294 lines
8.2 KiB
C++
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;
|
|
}
|