X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftgdb%2Fapt_signs.cxx;h=8341005189fb7f0940c829b99ae3f26748c2741d;hb=71443d1c9abb0ef3aa5b1ac5ed32c0624c958c50;hp=089369fb315b33b8b21ed0b7039135fad5939a2d;hpb=73c0ef59c1848b81d4c381f6c450e4a945d0ee1e;p=simgear.git diff --git a/simgear/scene/tgdb/apt_signs.cxx b/simgear/scene/tgdb/apt_signs.cxx index 089369fb..83410051 100644 --- a/simgear/scene/tgdb/apt_signs.cxx +++ b/simgear/scene/tgdb/apt_signs.cxx @@ -24,36 +24,55 @@ # include #endif +#include +#include + +#include +#include +#include +#include +#include + #include #include -#include +#include +#include #include #include #include "apt_signs.hxx" -#define TAXI "OBJECT_SIGN: " -#define RWY "OBJECT_RUNWAY_SIGN: " +#define SIGN "OBJECT_SIGN: " +using std::vector; +using namespace simgear; // for temporary storage of sign elements struct element_info { - element_info(SGMaterial *m, SGMaterialGlyph *g, double h, bool l) - : material(m), glyph(g), height(h), lighted(l) + element_info(SGMaterial *m, Effect *s, SGMaterialGlyph *g, double h, double c) + : material(m), state(s), glyph(g), height(h), coverwidth(c) { scale = h * m->get_xsize() / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize()); + abswidth = c == 0 ? g->get_width() * scale : c; } SGMaterial *material; + Effect *state; SGMaterialGlyph *glyph; double height; double scale; - bool lighted; + double abswidth; + double coverwidth; }; +typedef std::vector ElementVec; + + + -// standard panel height sizes -const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760}; +const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760}; // standard panel height sizes +const double grounddist = 0.2; // hard-code sign distance from surface for now +const double thick = 0.1; // half the thickness of the 3D sign // translation table for "command" to "glyph name" @@ -61,63 +80,169 @@ struct pair { const char *keyword; const char *glyph_name; } cmds[] = { - {"@u", "up"}, - {"@d", "down"}, - {"@l", "left"}, - {"@lu", "left-up"}, - {"@ul", "left-up"}, - {"@ld", "left-down"}, - {"@dl", "left-down"}, - {"@r", "right"}, - {"@ru", "right-up"}, - {"@ur", "right-up"}, - {"@rd", "right-down"}, - {"@dr", "right-down"}, + {"@u", "^u"}, + {"@d", "^d"}, + {"@l", "^l"}, + {"@lu", "^lu"}, + {"@ul", "^lu"}, + {"@ld", "^ld"}, + {"@dl", "^ld"}, + {"@r", "^r"}, + {"@ru", "^ru"}, + {"@ur", "^ru"}, + {"@rd", "^rd"}, + {"@dr", "^rd"}, {0, 0}, }; +struct GlyphGeometry +{ + osg::DrawArrays* quads; + osg::Vec2Array* uvs; + osg::Vec3Array* vertices; + osg::Vec3Array* normals; + + void addGlyph(SGMaterialGlyph* glyph, double x, double y, double width, double height, const osg::Matrix& xform) + { + + vertices->push_back(xform.preMult(osg::Vec3(thick, x, y))); + vertices->push_back(xform.preMult(osg::Vec3(thick, x + width, y))); + vertices->push_back(xform.preMult(osg::Vec3(thick, x + width, y + height))); + vertices->push_back(xform.preMult(osg::Vec3(thick, x, y + height))); + + // texture coordinates + double xoffset = glyph->get_left(); + double texWidth = glyph->get_width(); + + uvs->push_back(osg::Vec2(xoffset, 0)); + uvs->push_back(osg::Vec2(xoffset + texWidth, 0)); + uvs->push_back(osg::Vec2(xoffset + texWidth, 1)); + uvs->push_back(osg::Vec2(xoffset, 1)); + + // normals + for (int i=0; i<4; ++i) + normals->push_back(xform.preMult(osg::Vec3(0, -1, 0))); + + quads->setCount(vertices->size()); + } + + void addSignCase(double caseWidth, double caseHeight, const osg::Matrix& xform) + { + vertices->push_back(osg::Vec3(-thick, -caseWidth, grounddist)); + vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist)); + vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist + caseHeight)); + vertices->push_back(osg::Vec3(-thick, -caseWidth, grounddist + caseHeight)); + + + for (int i=0; i<4; ++i) + normals->push_back(xform.preMult(osg::Vec3(-1, 0.0, 0))); + + vertices->push_back(osg::Vec3(-thick, -caseWidth, grounddist + caseHeight)); + vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist + caseHeight)); + vertices->push_back(osg::Vec3(thick, caseWidth, grounddist + caseHeight)); + vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight)); + + for (int i=0; i<4; ++i) + normals->push_back(xform.preMult(osg::Vec3(0, 0, 1))); + + vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight)); + vertices->push_back(osg::Vec3(thick, caseWidth, grounddist + caseHeight)); + vertices->push_back(osg::Vec3(thick, caseWidth, grounddist)); + vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist)); + + for (int i=0; i<4; ++i) + normals->push_back(xform.preMult(osg::Vec3(1, 0.0, 0))); + + for (int i = 0; i < 3; ++i) { + uvs->push_back(osg::Vec2(1, 1)); + uvs->push_back(osg::Vec2(0.75, 1)); + uvs->push_back(osg::Vec2(0.75, 0)); + uvs->push_back(osg::Vec2(1, 0)); + } + + quads->setCount(vertices->size()); + } +}; -// Y ... black on yellow -> direction, destination, boundary -// R ... white on red -> mandatory instruction -// L ... yellow on black with frame -> runway/taxiway location -// B ... white on black -> runway distance remaining -// -// u/d/l/r ... up/down/left/right arrow or two-letter combinations -// thereof (ur, dl, ...) -// -// Example: {l}E|{L}[T]|{Y,ur}L|E{r} +typedef std::map EffectGeometryMap; + +void SGMakeSignFace(EffectGeometryMap& glyphs, const ElementVec& elements, double hpos, const osg::Matrix& xform) +{ + BOOST_FOREACH(element_info* element, elements) { + GlyphGeometry* gg = glyphs[element->state]; + gg->addGlyph(element->glyph, hpos, grounddist, element->abswidth, element->height, xform); + hpos += element->abswidth; + delete element; + } +} + +GlyphGeometry* makeGeometry(Effect* eff, osg::Group* group) +{ + GlyphGeometry* gg = new GlyphGeometry; + + EffectGeode* geode = new EffectGeode; + geode->setEffect(eff); + + gg->vertices = new osg::Vec3Array; + gg->normals = new osg::Vec3Array; + gg->uvs = new osg::Vec2Array; + + osg::Vec4Array* cl = new osg::Vec4Array; + cl->push_back(osg::Vec4(1, 1, 1, 1)); + + osg::Geometry* geometry = new osg::Geometry; + geometry->setVertexArray(gg->vertices); + geometry->setNormalArray(gg->normals); + geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); + geometry->setColorArray(cl); + geometry->setColorBinding(osg::Geometry::BIND_OVERALL); + geometry->setTexCoordArray(0, gg->uvs); + + gg->quads = new osg::DrawArrays(GL_QUADS, 0, gg->vertices->size()); + geometry->addPrimitiveSet(gg->quads); + geode->addDrawable(geometry); + group->addChild(geode); + return gg; +} + +// see $FG_ROOT/Docs/README.scenery // -ssgBranch *sgMakeSign(SGMaterialLib *matlib, const string path, const string content) +osg::Node* +SGMakeSign(SGMaterialLib *matlib, const string& content) { double sign_height = 1.0; // meter - bool lighted = true; - - vector elements; - element_info *close = 0; - double total_width = 0.0; + string newmat = "BlackSign"; + ElementVec elements1, elements2; + element_info *close1 = 0; + element_info *close2 = 0; + double total_width1 = 0.0; + double total_width2 = 0.0; bool cmd = false; - char *newmat = "BlackSign"; + bool isBackside = false; int size = -1; char oldtype = 0, newtype = 0; - ssgBranch *object = new ssgBranch(); - object->setName((char *)content.c_str()); - SGMaterial *material = matlib->find(newmat); + EffectGeometryMap geoms; + + osg::Group* object = new osg::Group; + object->setName(content); + + SGMaterial *material = 0; // Part I: parse & measure for (const char *s = content.data(); *s; s++) { string name; - const char *newmat = 0; + string value; if (*s == '{') { if (cmd) - SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unexpected { in sign contents"); + SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '{' in '" << content << "'."); cmd = true; continue; } else if (*s == '}') { if (!cmd) - SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unexpected } in sign contents"); + SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '}' in '" << content << "'."); cmd = false; continue; @@ -128,14 +253,13 @@ ssgBranch *sgMakeSign(SGMaterialLib *matlib, const string path, const string con if (*s == ',') continue; - string value; for (; *s; s++) { name += *s; if (s[1] == ',' || s[1] == '}' || s[1] == '=') break; } if (!*s) { - SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unclosed { in sign contents"); + SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents"); } else if (s[1] == '=') { for (s += 2; *s; s++) { value += *s; @@ -143,59 +267,59 @@ ssgBranch *sgMakeSign(SGMaterialLib *matlib, const string path, const string con break; } if (!*s) - SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unclosed { in sign contents"); + SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents"); } - if (name == "@size") { - sign_height = strtod(value.data(), 0); + if (name == "@@") { + isBackside = true; continue; } - if (name == "@light") { - lighted = (value != "0" && value != "no" && value != "off" && value != "false"); + if (name == "@size") { + sign_height = strtod(value.data(), 0); continue; } - if (name == "@material") - newmat = value.data(); - else if (name.size() == 2 || name.size() == 3) { - if (name.size() == 3 && name[2] >= '1' && name[2] <= '5') { - size = name[2] - '1'; - name = name.substr(0, 2); + if (name.size() == 2 || name.size() == 3) { + string n = name; + if (n.size() == 3 && n[2] >= '1' && n[2] <= '5') { + size = n[2] - '1'; + n = n.substr(0, 2); } - - if (name == "@Y") { + if (n == "@Y") { if (size < 3) { sign_height = HT[size < 0 ? 2 : size]; newmat = "YellowSign"; newtype = 'Y'; + continue; } - } else if (name == "@R") { + + } else if (n == "@R") { if (size < 3) { sign_height = HT[size < 0 ? 2 : size]; newmat = "RedSign"; newtype = 'R'; + continue; } - } else if (name == "@L") { + + } else if (n == "@L") { if (size < 3) { sign_height = HT[size < 0 ? 2 : size]; newmat = "FramedSign"; newtype = 'L'; + continue; } - } else if (name == "@B") { + + } else if (n == "@B") { if (size < 0 || size == 3 || size == 4) { sign_height = HT[size < 0 ? 3 : size]; newmat = "BlackSign"; newtype = 'B'; + continue; } } } - if (newmat) { - material = matlib->find(newmat); - continue; - } - for (int i = 0; cmds[i].keyword; i++) { if (name == cmds[i].keyword) { name = cmds[i].glyph_name; @@ -204,186 +328,123 @@ ssgBranch *sgMakeSign(SGMaterialLib *matlib, const string path, const string con } if (name[0] == '@') { - SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "ignoring unknown command `" << name << '\''); + SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown command `" << name << '\''); continue; } } - if (!material) { - SG_LOG( SG_TERRAIN, SG_ALERT, TAXI "material doesn't exit"); - continue; + if (newmat.size()) { + material = matlib->find(newmat); + newmat.clear(); } - + SGMaterialGlyph *glyph = material->get_glyph(name); if (!glyph) { - SG_LOG( SG_TERRAIN, SG_ALERT, TAXI "unsupported glyph `" << *s << '\''); + SG_LOG( SG_TERRAIN, SG_ALERT, SIGN "unsupported glyph '" << *s << '\''); continue; } // in managed mode push frame stop and frame start first - element_info *e; - if (newtype && newtype != oldtype) { - if (close) { - elements.push_back(close); - total_width += close->glyph->get_width() * close->scale; - close = 0; + Effect *state = material->get_effect(); + if (geoms[state] == NULL) { + geoms[state] = makeGeometry(state, object); + } + + element_info *e1; + element_info *e2; + if (!isBackside) { + if (newtype && newtype != oldtype) { + if (close1) { + elements1.push_back(close1); + total_width1 += close1->glyph->get_width() * close1->scale; + close1 = 0; + } + oldtype = newtype; + SGMaterialGlyph *g = material->get_glyph("stop-frame"); + if (g) + close1 = new element_info(material, state, g, sign_height, 0); + g = material->get_glyph("start-frame"); + if (g) { + e1 = new element_info(material, state, g, sign_height, 0); + elements1.push_back(e1); + total_width1 += e1->glyph->get_width() * e1->scale; + } } - oldtype = newtype; - SGMaterialGlyph *g = material->get_glyph("stop-frame"); - if (g) - close = new element_info(material, g, sign_height, lighted); - g = material->get_glyph("start-frame"); - if (g) { - e = new element_info(material, g, sign_height, lighted); - elements.push_back(e); - total_width += e->glyph->get_width() * e->scale; + // now the actually requested glyph (front) + e1 = new element_info(material, state, glyph, sign_height, 0); + elements1.push_back(e1); + total_width1 += e1->glyph->get_width() * e1->scale; + } else { + if (newtype && newtype != oldtype) { + if (close2) { + elements2.push_back(close2); + total_width2 += close2->glyph->get_width() * close2->scale; + close2 = 0; + } + oldtype = newtype; + SGMaterialGlyph *g = material->get_glyph("stop-frame"); + if (g) + close2 = new element_info(material, state, g, sign_height, 0); + g = material->get_glyph("start-frame"); + if (g) { + e2 = new element_info(material, state, g, sign_height, 0); + elements2.push_back(e2); + total_width2 += e2->glyph->get_width() * e2->scale; + } } + // now the actually requested glyph (back) + e2 = new element_info(material, state, glyph, sign_height, 0); + elements2.push_back(e2); + total_width2 += e2->glyph->get_width() * e2->scale; } - // now the actually requested glyph - e = new element_info(material, glyph, sign_height, lighted); - elements.push_back(e); - total_width += e->glyph->get_width() * e->scale; } // in managed mode close frame - if (close) { - elements.push_back(close); - total_width += close->glyph->get_width() * close->scale; - close = 0; + if (close1) { + elements1.push_back(close1); + total_width1 += close1->glyph->get_width() * close1->scale; + close1 = 0; } - - // Part II: typeset - double hpos = -total_width / 2; - const double dist = 0.25; // hard-code distance from surface for now - - sgVec3 normal; - sgSetVec3(normal, 0, -1, 0); - - sgVec4 color; - sgSetVec4(color, 1.0, 1.0, 1.0, 1.0); - - for (unsigned int i = 0; i < elements.size(); i++) { - element_info *element = elements[i]; - - double xoffset = element->glyph->get_left(); - double height = element->height; - double width = element->glyph->get_width(); - double abswidth = width * element->scale; - - // vertices - ssgVertexArray *vl = new ssgVertexArray(4); - vl->add(0, hpos, dist); - vl->add(0, hpos + abswidth, dist); - vl->add(0, hpos, dist + height); - vl->add(0, hpos + abswidth, dist + height); - - // texture coordinates - ssgTexCoordArray *tl = new ssgTexCoordArray(4); - tl->add(xoffset, 0); - tl->add(xoffset + width, 0); - tl->add(xoffset, 1); - tl->add(xoffset + width, 1); - - // normals - ssgNormalArray *nl = new ssgNormalArray(1); - nl->add(normal); - - // colors - ssgColourArray *cl = new ssgColourArray(1); - cl->add(color); - - ssgLeaf *leaf = new ssgVtxTable(GL_TRIANGLE_STRIP, vl, nl, tl, cl); - ssgSimpleState *state = element->material->get_state(); - if (!element->lighted) { - // FIXME: clone state for unlighted elements only once per material - state = (ssgSimpleState *)state->clone(SSG_CLONE_STATE); - state->setMaterial(GL_EMISSION, 0.3, 0.3, 0.3, 1); - } - leaf->setState(state); - - object->addKid(leaf); - hpos += abswidth; - delete element; + if (close2) { + elements2.push_back(close2); + total_width2 += close2->glyph->get_width() * close2->scale; + close2 = 0; } - // minimalistic backside - ssgVertexArray *vl = new ssgVertexArray(4); - vl->add(0, hpos, dist); - vl->add(0, hpos - total_width, dist); - vl->add(0, hpos, dist + sign_height); - vl->add(0, hpos - total_width, dist + sign_height); - - ssgNormalArray *nl = new ssgNormalArray(1); - nl->add(0, 1, 0); - - ssgLeaf *leaf = new ssgVtxTable(GL_TRIANGLE_STRIP, vl, nl, 0, 0); - SGMaterial *mat = matlib->find("BlackSign"); - if (mat) - leaf->setState(mat->get_state()); - object->addKid(leaf); - - return object; -} - - - - - -ssgBranch *sgMakeRunwaySign( SGMaterialLib *matlib, - const string path, const string name ) -{ - // for demo purposes we assume each element (letter) is 1x1 meter. - // Sign is placed 0.25 meters above the ground - - ssgBranch *object = new ssgBranch(); - object->setName( (char *)name.c_str() ); - - double width = name.length() / 3.0; - - string material = name; - - point_list nodes; - point_list normals; - point_list texcoords; - int_list vertex_index; - int_list normal_index; - int_list tex_index; - - nodes.push_back( Point3D( -width, 0, 0.25 ) ); - nodes.push_back( Point3D( width + 1, 0, 0.25 ) ); - nodes.push_back( Point3D( -width, 0, 1.25 ) ); - nodes.push_back( Point3D( width + 1, 0, 1.25 ) ); - - normals.push_back( Point3D( 0, -1, 0 ) ); - - texcoords.push_back( Point3D( 0, 0, 0 ) ); - texcoords.push_back( Point3D( 1, 0, 0 ) ); - texcoords.push_back( Point3D( 0, 1, 0 ) ); - texcoords.push_back( Point3D( 1, 1, 0 ) ); - - vertex_index.push_back( 0 ); - vertex_index.push_back( 1 ); - vertex_index.push_back( 2 ); - vertex_index.push_back( 3 ); - - normal_index.push_back( 0 ); - normal_index.push_back( 0 ); - normal_index.push_back( 0 ); - normal_index.push_back( 0 ); - - tex_index.push_back( 0 ); - tex_index.push_back( 1 ); - tex_index.push_back( 2 ); - tex_index.push_back( 3 ); + // Part II: typeset + double boxwidth = std::max(total_width1, total_width2) * 0.5; + double hpos = -boxwidth; + SGMaterial *mat = matlib->find("signcase"); + geoms[mat->get_effect()] = makeGeometry(mat->get_effect(), object); + + double coverSize = fabs(total_width1 - total_width2) * 0.5; + + if (total_width1 < total_width2) { + if (mat) { + element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize); + element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize); + elements1.insert(elements1.begin(), s1); + elements1.push_back(s2); + } - ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_STRIP, matlib, material, - nodes, normals, texcoords, - vertex_index, normal_index, tex_index, - false, NULL ); + } else if (total_width2 < total_width1) { + if (mat) { + element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize); + element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize); + elements2.insert(elements2.begin(), s1); + elements2.push_back(s2); + } + } - object->addKid( leaf ); + // Create front side + SGMakeSignFace(geoms, elements1, hpos, osg::Matrix::identity()); + // Create back side + const osg::Vec3d axis(0, 0, 1); + SGMakeSignFace(geoms, elements2, hpos, osg::Matrix::rotate( M_PI, axis)); + geoms[mat->get_effect()]->addSignCase(boxwidth, sign_height, osg::Matrix::identity()); + return object; }