]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/tgdb/apt_signs.cxx
Phase 1 - single geometry per material
[simgear.git] / simgear / scene / tgdb / apt_signs.cxx
index 089369fb315b33b8b21ed0b7039135fad5939a2d..8341005189fb7f0940c829b99ae3f26748c2741d 100644 (file)
 #  include <simgear_config.h>
 #endif
 
+#include <vector>
+#include <boost/foreach.hpp>
+
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/MatrixTransform>
+#include <osg/StateSet>
+
 #include <simgear/debug/logstream.hxx>
 #include <simgear/math/sg_types.hxx>
-#include <simgear/scene/tgdb/leaf.hxx>
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/material/mat.hxx>
 #include <simgear/scene/material/matlib.hxx>
 
 #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<element_info*> 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<Effect*, GlyphGeometry*> 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<element_info *> 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;
 }