]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/apt_signs.cxx
remove some warnings for MSVC
[simgear.git] / simgear / scene / tgdb / apt_signs.cxx
1 // apt_signs.cxx -- build airport signs on the fly
2 //
3 // Written by Curtis Olson, started July 2001.
4 //
5 // Copyright (C) 2001  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear_config.h>
25 #endif
26
27 #include <vector>
28 #include <boost/foreach.hpp>
29
30 #include <osg/Geode>
31 #include <osg/Geometry>
32 #include <osg/Group>
33 #include <osg/MatrixTransform>
34 #include <osg/StateSet>
35
36 #include <simgear/debug/logstream.hxx>
37 #include <simgear/math/sg_types.hxx>
38 #include <simgear/scene/material/Effect.hxx>
39 #include <simgear/scene/material/EffectGeode.hxx>
40 #include <simgear/scene/material/mat.hxx>
41 #include <simgear/scene/material/matlib.hxx>
42
43 #include "apt_signs.hxx"
44
45 #define SIGN "OBJECT_SIGN: "
46
47 using std::vector;
48 using namespace simgear;
49
50 // for temporary storage of sign elements
51 struct element_info {
52     element_info(SGMaterial *m, SGMaterialGlyph *g, double h, double c)
53         : material(m), glyph(g), height(h), coverwidth(c)
54     {
55         scale = h * m->get_xsize()
56                 / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize());
57         abswidth = c == 0 ? g->get_width() * scale : c;
58     }
59     SGMaterial *material;
60     SGMaterialGlyph *glyph;
61     double height;
62     double scale;
63     double abswidth;
64     double coverwidth;
65 };
66
67 typedef std::vector<element_info*> ElementVec;
68
69 const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760}; // standard panel height sizes
70 const double grounddist = 0.2;     // hard-code sign distance from surface for now
71 const double thick = 0.1;    // half the thickness of the 3D sign
72
73
74 // translation table for "command" to "glyph name"
75 struct pair {
76     const char *keyword;
77     const char *glyph_name;
78 } cmds[] = {
79     {"@u",       "^u"},
80     {"@d",       "^d"},
81     {"@l",       "^l"},
82     {"@lu",      "^lu"},
83     {"@ul",      "^lu"},
84     {"@ld",      "^ld"},
85     {"@dl",      "^ld"},
86     {"@r",       "^r"},
87     {"@ru",      "^ru"},
88     {"@ur",      "^ru"},
89     {"@rd",      "^rd"},
90     {"@dr",      "^rd"},
91     {0, 0},
92 };
93
94 struct GlyphGeometry
95 {
96   osg::DrawArrays* quads;
97   osg::Vec2Array* uvs;
98   osg::Vec3Array* vertices;
99   osg::Vec3Array* normals;
100   
101   void addGlyph(SGMaterialGlyph* glyph, double x, double y, double width, double height, const osg::Matrix& xform)
102   {
103     
104     vertices->push_back(xform.preMult(osg::Vec3(thick, x, y)));
105     vertices->push_back(xform.preMult(osg::Vec3(thick, x + width, y)));
106     vertices->push_back(xform.preMult(osg::Vec3(thick, x + width, y + height)));
107     vertices->push_back(xform.preMult(osg::Vec3(thick, x, y + height)));
108     
109     // texture coordinates
110     double xoffset = glyph->get_left();
111     double texWidth = glyph->get_width();
112     
113     uvs->push_back(osg::Vec2(xoffset,         0));
114     uvs->push_back(osg::Vec2(xoffset + texWidth, 0));
115     uvs->push_back(osg::Vec2(xoffset + texWidth, 1));
116     uvs->push_back(osg::Vec2(xoffset,         1));
117
118     // normals
119     for (int i=0; i<4; ++i)
120       normals->push_back(xform.preMult(osg::Vec3(0, -1, 0)));
121     
122     quads->setCount(vertices->size());
123   }
124   
125   void addSignCase(double caseWidth, double caseHeight, const osg::Matrix& xform)
126   {
127     int last = vertices->size();
128     double texsize = caseWidth / caseHeight;
129
130     //left
131     vertices->push_back(osg::Vec3(-thick, -caseWidth,  grounddist));
132     vertices->push_back(osg::Vec3(thick, -caseWidth,  grounddist));
133     vertices->push_back(osg::Vec3(thick, -caseWidth,  grounddist + caseHeight));
134     vertices->push_back(osg::Vec3(-thick, -caseWidth,  grounddist + caseHeight));
135
136     uvs->push_back(osg::Vec2(1,    1));
137     uvs->push_back(osg::Vec2(0.75, 1));
138     uvs->push_back(osg::Vec2(0.75, 0));
139     uvs->push_back(osg::Vec2(1,    0));
140     
141     for (int i=0; i<4; ++i)
142       normals->push_back(osg::Vec3(-1, 0.0, 0));
143
144     //top
145     vertices->push_back(osg::Vec3(-thick, -caseWidth,  grounddist + caseHeight));
146     vertices->push_back(osg::Vec3(thick,  -caseWidth,  grounddist + caseHeight));
147     vertices->push_back(osg::Vec3(thick,  caseWidth, grounddist + caseHeight));
148     vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight));
149     
150     uvs->push_back(osg::Vec2(1,    texsize));
151     uvs->push_back(osg::Vec2(0.75, texsize));
152     uvs->push_back(osg::Vec2(0.75, 0));
153     uvs->push_back(osg::Vec2(1,    0));
154
155     for (int i=0; i<4; ++i)
156       normals->push_back(osg::Vec3(0, 0, 1));
157
158     //right
159     vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight));
160     vertices->push_back(osg::Vec3(thick,  caseWidth, grounddist + caseHeight));
161     vertices->push_back(osg::Vec3(thick,  caseWidth, grounddist));
162     vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist));
163
164     uvs->push_back(osg::Vec2(1,    1));
165     uvs->push_back(osg::Vec2(0.75, 1));
166     uvs->push_back(osg::Vec2(0.75, 0));
167     uvs->push_back(osg::Vec2(1,    0));
168
169     for (int i=0; i<4; ++i)
170       normals->push_back(osg::Vec3(1, 0.0, 0));
171
172
173   // transform all the newly added vertices and normals by the matrix
174     for (unsigned int i=last; i<vertices->size(); ++i) {
175       (*vertices)[i]= xform.preMult((*vertices)[i]);
176       (*normals)[i] = xform.preMult((*normals)[i]);
177     }
178     
179     quads->setCount(vertices->size());
180   }
181 };
182
183 typedef std::map<Effect*, GlyphGeometry*> EffectGeometryMap;
184
185 GlyphGeometry* makeGeometry(Effect* eff, osg::Group* group)
186 {
187   GlyphGeometry* gg = new GlyphGeometry;
188   
189   EffectGeode* geode = new EffectGeode;
190   geode->setEffect(eff);
191  
192   gg->vertices = new osg::Vec3Array;
193   gg->normals = new osg::Vec3Array;
194   gg->uvs = new osg::Vec2Array;
195   
196   osg::Vec4Array* cl = new osg::Vec4Array;
197   cl->push_back(osg::Vec4(1, 1, 1, 1));
198   
199   osg::Geometry* geometry = new osg::Geometry;
200   geometry->setVertexArray(gg->vertices);
201   geometry->setNormalArray(gg->normals);
202   geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
203   geometry->setColorArray(cl);
204   geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
205   geometry->setTexCoordArray(0, gg->uvs);
206   
207   gg->quads = new osg::DrawArrays(GL_QUADS, 0, gg->vertices->size());
208   geometry->addPrimitiveSet(gg->quads);
209   geode->addDrawable(geometry);
210   group->addChild(geode);
211   return gg;
212 }
213
214 // see $FG_ROOT/Docs/README.scenery
215
216 namespace simgear
217 {
218
219 class AirportSignBuilder::AirportSignBuilderPrivate
220 {
221 public:
222     SGMaterialLib* materials;
223     EffectGeometryMap geometries;
224     osg::MatrixTransform* signsGroup;
225     GlyphGeometry* signCaseGeometry;
226     
227     GlyphGeometry* getGeometry(Effect* eff)
228     {
229         EffectGeometryMap::iterator it = geometries.find(eff);
230         if (it != geometries.end()) {
231             return it->second;
232         }
233         
234         GlyphGeometry* gg = makeGeometry(eff, signsGroup);
235         geometries[eff] = gg;
236         return gg;
237     }
238     
239     void makeFace(const ElementVec& elements, double hpos, const osg::Matrix& xform)
240     {
241         BOOST_FOREACH(element_info* element, elements) {
242             GlyphGeometry* gg = getGeometry(element->material->get_effect());
243             gg->addGlyph(element->glyph, hpos, grounddist, element->abswidth, element->height, xform);
244             hpos += element->abswidth;
245             delete element;
246         }
247     }
248     
249 };
250
251 AirportSignBuilder::AirportSignBuilder(SGMaterialLib* mats, const SGGeod& center) :
252     d(new AirportSignBuilderPrivate)
253 {
254     d->signsGroup = new osg::MatrixTransform;
255     d->signsGroup->setMatrix(makeZUpFrame(center));
256     
257     assert(mats);
258     d->materials = mats;
259     d->signCaseGeometry = d->getGeometry(d->materials->find("signcase")->get_effect());
260 }
261
262 osg::Node* AirportSignBuilder::getSignsGroup()
263 {
264     if (0 == d->signsGroup->getNumChildren())
265         return 0;
266     return d->signsGroup;
267 }
268
269 AirportSignBuilder::~AirportSignBuilder()
270 {
271     EffectGeometryMap::iterator it;
272     for (it = d->geometries.begin(); it != d->geometries.end(); ++it) {
273         delete it->second;
274     }
275 }
276
277 void AirportSignBuilder::addSign(const SGGeod& pos, double heading, const std::string& content)
278 {
279     double sign_height = 1.0;  // meter
280     string newmat = "BlackSign";
281     ElementVec elements1, elements2;
282     element_info *close1 = 0;
283     element_info *close2 = 0;
284     double total_width1 = 0.0;
285     double total_width2 = 0.0;
286     bool cmd = false;
287     bool isBackside = false;
288     int size = -1;
289     char oldtype = 0, newtype = 0;
290     SGMaterial *material = 0;
291
292     // Part I: parse & measure
293     for (const char *s = content.data(); *s; s++) {
294         string name;
295         string value;
296
297         if (*s == '{') {
298             if (cmd)
299                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '{' in '" << content << "'.");
300             cmd = true;
301             continue;
302
303         } else if (*s == '}') {
304             if (!cmd)
305                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '}' in '" << content << "'.");
306             cmd = false;
307             continue;
308
309         } else if (!cmd) {
310             name = *s;
311
312         } else {
313             if (*s == ',')
314                 continue;
315
316             for (; *s; s++) {
317                 name += *s;
318                 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
319                     break;
320             }
321             if (!*s) {
322                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
323             } else if (s[1] == '=') {
324                 for (s += 2; *s; s++) {
325                     value += *s;
326                     if (s[1] == ',' || s[1] == '}')
327                         break;
328                 }
329                 if (!*s)
330                     SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
331             }
332
333             if (name == "@@") {
334                 isBackside = true;
335                 continue;
336             }
337
338             if (name == "@size") {
339                 sign_height = strtod(value.data(), 0);
340                 continue;
341             }
342
343             if (name.size() == 2 || name.size() == 3) {
344                 string n = name;
345                 if (n.size() == 3 && n[2] >= '1' && n[2] <= '5') {
346                     size = n[2] - '1';
347                     n = n.substr(0, 2);
348                 }
349                 if (n == "@Y") {
350                     if (size < 3) {
351                         sign_height = HT[size < 0 ? 2 : size];
352                         newmat = "YellowSign";
353                         newtype = 'Y';
354                         continue;
355                     }
356
357                 } else if (n == "@R") {
358                     if (size < 3) {
359                         sign_height = HT[size < 0 ? 2 : size];
360                         newmat = "RedSign";
361                         newtype = 'R';
362                         continue;
363                     }
364
365                 } else if (n == "@L") {
366                     if (size < 3) {
367                         sign_height = HT[size < 0 ? 2 : size];
368                         newmat = "FramedSign";
369                         newtype = 'L';
370                         continue;
371                     }
372
373                 } else if (n == "@B") {
374                     if (size < 0 || size == 3 || size == 4) {
375                         sign_height = HT[size < 0 ? 3 : size];
376                         newmat = "BlackSign";
377                         newtype = 'B';
378                         continue;
379                     }
380                 }
381             }
382
383             for (int i = 0; cmds[i].keyword; i++) {
384                 if (name == cmds[i].keyword) {
385                     name = cmds[i].glyph_name;
386                     break;
387                 }
388             }
389
390             if (name[0] == '@') {
391                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown command `" << name << '\'');
392                 continue;
393             }
394         }
395
396         if (newmat.size()) {
397             material = d->materials->find(newmat);
398             newmat.clear();
399         }
400  
401         SGMaterialGlyph *glyph = material->get_glyph(name);
402         if (!glyph) {
403             SG_LOG( SG_TERRAIN, SG_ALERT, SIGN "unsupported glyph '" << *s << '\'');
404             continue;
405         }
406
407     // in managed mode push frame stop and frame start first
408         if (!isBackside) {
409             if (newtype && newtype != oldtype) {
410                 if (close1) {
411                     elements1.push_back(close1);
412                     total_width1 += close1->glyph->get_width() * close1->scale;
413                     close1 = 0;
414                 }
415                 oldtype = newtype;
416                 SGMaterialGlyph *g = material->get_glyph("stop-frame");
417                 if (g)
418                     close1 = new element_info(material, g, sign_height, 0);
419                 g = material->get_glyph("start-frame");
420                 if (g) {
421                     element_info* e1 = new element_info(material, g, sign_height, 0);
422                     elements1.push_back(e1);
423                     total_width1 += e1->glyph->get_width() * e1->scale;
424                 }
425             }
426             // now the actually requested glyph (front)
427             element_info* e1 = new element_info(material, glyph, sign_height, 0);
428             elements1.push_back(e1);
429             total_width1 += e1->glyph->get_width() * e1->scale;
430         } else {
431             if (newtype && newtype != oldtype) {
432                 if (close2) {
433                     elements2.push_back(close2);
434                     total_width2 += close2->glyph->get_width() * close2->scale;
435                     close2 = 0;
436                 }
437                 oldtype = newtype;
438                 SGMaterialGlyph *g = material->get_glyph("stop-frame");
439                 if (g)
440                     close2 = new element_info(material, g, sign_height, 0);
441                 g = material->get_glyph("start-frame");
442                 if (g) {
443                     element_info* e2 = new element_info(material, g, sign_height, 0);
444                     elements2.push_back(e2);
445                     total_width2 += e2->glyph->get_width() * e2->scale;
446                 }
447             }
448             // now the actually requested glyph (back)
449             element_info* e2 = new element_info(material, glyph, sign_height, 0);
450             elements2.push_back(e2);
451             total_width2 += e2->glyph->get_width() * e2->scale;
452         }
453     }
454
455     // in managed mode close frame
456     if (close1) {
457         elements1.push_back(close1);
458         total_width1 += close1->glyph->get_width() * close1->scale;
459         close1 = 0;
460     }
461
462     if (close2) {
463         elements2.push_back(close2);
464         total_width2 += close2->glyph->get_width() * close2->scale;
465         close2 = 0;
466     }
467
468   // Part II: typeset
469     double boxwidth = std::max(total_width1, total_width2) * 0.5;
470     double hpos = -boxwidth;
471     SGMaterial *mat = d->materials->find("signcase");
472   
473     double coverSize = fabs(total_width1 - total_width2) * 0.5;
474     element_info* s1 = new element_info(mat, mat->get_glyph("cover1"), sign_height, coverSize);
475     element_info* s2 = new element_info(mat, mat->get_glyph("cover2"), sign_height, coverSize);
476   
477     if (total_width1 < total_width2) {            
478         elements1.insert(elements1.begin(), s1);
479         elements1.push_back(s2);
480     } else if (total_width2 < total_width1) {
481         elements2.insert(elements2.begin(), s1);
482         elements2.push_back(s2);
483     } else {
484         delete s1;
485         delete s2;
486     }
487     
488 // position the sign
489     const osg::Vec3 Z_AXIS(0, 0, 1);
490     osg::Matrix m(makeZUpFrame(pos));
491     m.preMultRotate(osg::Quat(SGMiscd::deg2rad(heading), Z_AXIS));
492     
493     // apply the inverse of the group transform, so sign vertices
494     // are relative to the tile center, and hence have a magnitude which
495     // fits in a float with sufficent precision.
496     m.postMult(d->signsGroup->getInverseMatrix());
497     
498     d->makeFace(elements1, hpos, m);
499 // Create back side
500     osg::Matrix back(m);
501     back.preMultRotate(osg::Quat(M_PI, Z_AXIS));
502     d->makeFace(elements2, hpos, back);
503     
504     d->signCaseGeometry->addSignCase(boxwidth, sign_height, m);
505 }
506
507 } // of namespace simgear
508