open_toontown_panda3d/panda/src/text/textGlyph.cxx

294 lines
8.2 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file textGlyph.cxx
* @author drose
* @date 2002-02-08
*/
#include "textGlyph.h"
#include "geomTextGlyph.h"
#include "geomTriangles.h"
#include "geomVertexReader.h"
#include "geomVertexWriter.h"
using std::max;
using std::min;
TypeHandle TextGlyph::_type_handle;
/**
*
*/
TextGlyph::
~TextGlyph() {
}
/**
* Returns true if this glyph represents invisible whitespace, or false if it
* corresponds to some visible character.
*/
bool TextGlyph::
is_whitespace() const {
// In a static font, there is no explicit glyph for whitespace, so all
// glyphs are non-whitespace.
return false;
}
/**
* Returns a Geom that renders the particular glyph. It will be generated if
* necessary.
*
* This method will always return a copy of the Geom, so the caller is free to
* modify it.
*/
PT(Geom) TextGlyph::
get_geom(Geom::UsageHint usage_hint) const {
if (_geom.is_null()) {
// Maybe we have yet to generate a suitable Geom.
if (_has_quad) {
((TextGlyph *)this)->make_quad_geom();
if (_geom.is_null()) {
return nullptr;
}
} else {
// Nope.
return nullptr;
}
}
// We always return a copy of the geom. That will allow the caller to
// modify its vertices without fear of stomping on other copies. It is also
// important that we store a reference to this glyph on the Geom, since the
// DynamicTextFont relies on counting references to determine whether a
// glyph is no longer used.
PT(Geom) new_geom = new GeomTextGlyph(*_geom, this);
new_geom->set_usage_hint(usage_hint);
const GeomVertexData *vdata = new_geom->get_vertex_data();
nassertr(vdata != nullptr, new_geom);
if (vdata->get_usage_hint() != usage_hint) {
new_geom->modify_vertex_data()->set_usage_hint(usage_hint);
}
return new_geom;
}
/**
* Expands min_point and max_point to include all of the vertices in the
* glyph, if any. found_any is set true if any points are found. It is the
* caller's responsibility to initialize min_point, max_point, and found_any
* before calling this function.
*/
void TextGlyph::
calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, Thread *current_thread) const {
if (_has_quad) {
found_any = true;
min_point.set(_quad_dimensions[0], 0, _quad_dimensions[1]);
max_point.set(_quad_dimensions[2], 0, _quad_dimensions[3]);
} else if (!_geom.is_null()) {
_geom->calc_tight_bounds(min_point, max_point, found_any, current_thread);
}
}
/**
* Sets the glyph using the given quad parameters. Any Geom assigned will be
* cleared. The order of the components is left, bottom, right, top.
*/
void TextGlyph::
set_quad(const LVecBase4 &dimensions, const LVecBase4 &texcoords,
const RenderState *state) {
_geom.clear();
_quad_dimensions = dimensions;
_quad_texcoords = texcoords;
_has_quad = true;
_state = state;
}
/**
* Sets the geom from a pre-built Geom object. Any quad parameters assigned
* will be cleared.
*/
void TextGlyph::
set_geom(GeomVertexData *vdata, GeomPrimitive *prim,
const RenderState *state) {
PT(Geom) geom = new GeomTextGlyph(this, vdata);
geom->add_primitive(prim);
_geom = geom;
_has_quad = false;
_state = state;
}
/**
* Checks if the geom that was passed in is actually a quad, and if so, sets
* the appropriate quad parameters.
*
* This is useful when loading static text fonts, so that they can still
* benefit from the fast text assembly that is done for quads.
*/
void TextGlyph::
check_quad_geom() {
// Currently it looks for rather specific signs that this glyph has been
// generated using egg-mkfont. For now, this is fine.
CPT(GeomVertexData) vdata = _geom->get_vertex_data();
if (vdata->get_num_rows() != 4) {
return;
}
// Does it appear to have a single primitive with two triangles?
if (_geom->get_num_primitives() != 1) {
return;
}
CPT(GeomPrimitive) prim = _geom->get_primitive(0);
if (!prim->is_indexed() ||
prim->get_primitive_type() != GeomPrimitive::PT_polygons) {
return;
}
if (!prim->is_of_type(GeomTriangles::get_class_type())) {
prim = prim->decompose();
}
if (prim->get_num_vertices() != 6) {
return;
}
// Check that it has a vertex column and optionally a texcoord column.
CPT(GeomVertexFormat) format = vdata->get_format();
if (format->get_num_columns() > 2 ||
!format->has_column(InternalName::get_vertex())) {
return;
}
if (format->has_column(InternalName::get_texcoord()) != (format->get_num_columns() == 2)) {
// The second column, if there is one, is not a texcoord.
return;
}
// Check that the vertices are arranged in a square.
GeomVertexReader vertex(vdata, InternalName::get_vertex());
GeomVertexReader texcoord(vdata, InternalName::get_texcoord());
LVecBase3 v = vertex.get_data3();
if (!IS_NEARLY_ZERO(v[1])) {
return;
}
LTexCoord uv(0);
if (texcoord.has_column()) {
uv = texcoord.get_data2();
}
PN_stdfloat minx = v[0];
PN_stdfloat maxx = v[0];
PN_stdfloat miny = v[2];
PN_stdfloat maxy = v[2];
PN_stdfloat minu = uv[0];
PN_stdfloat maxu = uv[0];
PN_stdfloat minv = uv[1];
PN_stdfloat maxv = uv[1];
for (int i = 1; i < 4; ++i) {
v = vertex.get_data3();
if (!IS_NEARLY_ZERO(v[1])) {
return;
}
if (texcoord.has_column()) {
uv = texcoord.get_data2();
}
if (!IS_NEARLY_EQUAL(v[0], minx) && !IS_NEARLY_EQUAL(v[0], maxx)) {
if (!IS_NEARLY_EQUAL(minx, maxx)) {
return;
}
if (v[0] < minx) {
minx = v[0];
minu = uv[0];
}
if (v[0] > maxx) {
maxx = v[0];
maxu = uv[0];
}
}
if (!IS_NEARLY_EQUAL(v[2], miny) && !IS_NEARLY_EQUAL(v[2], maxy)) {
if (!IS_NEARLY_EQUAL(miny, maxy)) {
return;
}
if (v[2] < miny) {
miny = v[2];
minv = uv[1];
}
if (v[2] > maxy) {
maxy = v[2];
maxv = uv[1];
}
}
if (!IS_NEARLY_EQUAL(uv[0], minu) && !IS_NEARLY_EQUAL(uv[0], maxu)) {
return;
}
if (!IS_NEARLY_EQUAL(uv[1], minv) && !IS_NEARLY_EQUAL(uv[1], maxv)) {
return;
}
}
_quad_dimensions.set(minx, miny, maxx, maxy);
_quad_texcoords.set(minu, minv, maxu, maxv);
_has_quad = true;
}
/**
* Generates a Geom representing this text glyph, if at all possible.
*/
void TextGlyph::
make_quad_geom() {
// The default implementation is to generate a Geom based on the get_quad()
// implementation, if any.
LVecBase4 dimensions, uvs;
if (!get_quad(dimensions, uvs)) {
return;
}
// Create a corresponding triangle pair. We use a pair of indexed triangles
// rather than a single triangle strip, to avoid the bad vertex duplication
// behavior with lots of two-triangle strips.
PT(GeomVertexData) vdata = new GeomVertexData
(std::string(), GeomVertexFormat::get_v3t2(), Geom::UH_static);
vdata->unclean_set_num_rows(4);
PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
{
GeomVertexWriter vertex(vdata, InternalName::get_vertex());
GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
PT(GeomVertexArrayData) indices = tris->modify_vertices();
indices->unclean_set_num_rows(6);
GeomVertexWriter index(indices, 0);
vertex.set_data3(dimensions[0], 0, dimensions[3]);
vertex.set_data3(dimensions[0], 0, dimensions[1]);
vertex.set_data3(dimensions[2], 0, dimensions[3]);
vertex.set_data3(dimensions[2], 0, dimensions[1]);
texcoord.set_data2(uvs[0], uvs[3]);
texcoord.set_data2(uvs[0], uvs[1]);
texcoord.set_data2(uvs[2], uvs[3]);
texcoord.set_data2(uvs[2], uvs[1]);
index.set_data1i(0);
index.set_data1i(1);
index.set_data1i(2);
index.set_data1i(2);
index.set_data1i(1);
index.set_data1i(3);
}
// We create a regular Geom here, not a GeomTextGlyph, since doing so would
// create a circular reference. When the get_geom method makes a copy, it
// will add in a pointer to this text glyph.
PT(Geom) geom = new Geom(vdata);
geom->add_primitive(tris);
_geom = geom;
}