]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/apt_signs.cxx
8341005189fb7f0940c829b99ae3f26748c2741d
[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, Effect *s, SGMaterialGlyph *g, double h, double c)
53         : material(m), state(s), 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     Effect *state;
61     SGMaterialGlyph *glyph;
62     double height;
63     double scale;
64     double abswidth;
65     double coverwidth;
66 };
67
68 typedef std::vector<element_info*> ElementVec;
69
70
71
72
73 const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760}; // standard panel height sizes
74 const double grounddist = 0.2;     // hard-code sign distance from surface for now
75 const double thick = 0.1;    // half the thickness of the 3D sign
76
77
78 // translation table for "command" to "glyph name"
79 struct pair {
80     const char *keyword;
81     const char *glyph_name;
82 } cmds[] = {
83     {"@u",       "^u"},
84     {"@d",       "^d"},
85     {"@l",       "^l"},
86     {"@lu",      "^lu"},
87     {"@ul",      "^lu"},
88     {"@ld",      "^ld"},
89     {"@dl",      "^ld"},
90     {"@r",       "^r"},
91     {"@ru",      "^ru"},
92     {"@ur",      "^ru"},
93     {"@rd",      "^rd"},
94     {"@dr",      "^rd"},
95     {0, 0},
96 };
97
98 struct GlyphGeometry
99 {
100   osg::DrawArrays* quads;
101   osg::Vec2Array* uvs;
102   osg::Vec3Array* vertices;
103   osg::Vec3Array* normals;
104   
105   void addGlyph(SGMaterialGlyph* glyph, double x, double y, double width, double height, const osg::Matrix& xform)
106   {
107     
108     vertices->push_back(xform.preMult(osg::Vec3(thick, x, y)));
109     vertices->push_back(xform.preMult(osg::Vec3(thick, x + width, y)));
110     vertices->push_back(xform.preMult(osg::Vec3(thick, x + width, y + height)));
111     vertices->push_back(xform.preMult(osg::Vec3(thick, x, y + height)));
112     
113     // texture coordinates
114     double xoffset = glyph->get_left();
115     double texWidth = glyph->get_width();
116     
117     uvs->push_back(osg::Vec2(xoffset,         0));
118     uvs->push_back(osg::Vec2(xoffset + texWidth, 0));
119     uvs->push_back(osg::Vec2(xoffset + texWidth, 1));
120     uvs->push_back(osg::Vec2(xoffset,         1));
121
122     // normals
123     for (int i=0; i<4; ++i)
124       normals->push_back(xform.preMult(osg::Vec3(0, -1, 0)));
125     
126     quads->setCount(vertices->size());
127   }
128   
129   void addSignCase(double caseWidth, double caseHeight, const osg::Matrix& xform)
130   {
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     
137     for (int i=0; i<4; ++i)
138       normals->push_back(xform.preMult(osg::Vec3(-1, 0.0, 0)));
139     
140     vertices->push_back(osg::Vec3(-thick, -caseWidth,  grounddist + caseHeight));
141     vertices->push_back(osg::Vec3(thick,  -caseWidth,  grounddist + caseHeight));
142     vertices->push_back(osg::Vec3(thick,  caseWidth, grounddist + caseHeight));
143     vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight));
144     
145     for (int i=0; i<4; ++i)
146       normals->push_back(xform.preMult(osg::Vec3(0, 0, 1)));
147     
148     vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight));
149     vertices->push_back(osg::Vec3(thick,  caseWidth, grounddist + caseHeight));
150     vertices->push_back(osg::Vec3(thick,  caseWidth, grounddist));
151     vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist));
152     
153     for (int i=0; i<4; ++i)
154       normals->push_back(xform.preMult(osg::Vec3(1, 0.0, 0)));
155     
156     for (int i = 0; i < 3; ++i) {
157       uvs->push_back(osg::Vec2(1,    1));
158       uvs->push_back(osg::Vec2(0.75, 1));
159       uvs->push_back(osg::Vec2(0.75, 0));
160       uvs->push_back(osg::Vec2(1,    0));
161     }
162     
163     quads->setCount(vertices->size());
164   }
165 };
166
167 typedef std::map<Effect*, GlyphGeometry*> EffectGeometryMap;
168
169 void SGMakeSignFace(EffectGeometryMap& glyphs, const ElementVec& elements, double hpos, const osg::Matrix& xform)
170 {
171     BOOST_FOREACH(element_info* element, elements) {
172         GlyphGeometry* gg = glyphs[element->state];
173         gg->addGlyph(element->glyph, hpos, grounddist, element->abswidth, element->height, xform);
174         hpos += element->abswidth;
175         delete element;
176     }
177 }
178
179 GlyphGeometry* makeGeometry(Effect* eff, osg::Group* group)
180 {
181   GlyphGeometry* gg = new GlyphGeometry;
182   
183   EffectGeode* geode = new EffectGeode;
184   geode->setEffect(eff);
185  
186   gg->vertices = new osg::Vec3Array;
187   gg->normals = new osg::Vec3Array;
188   gg->uvs = new osg::Vec2Array;
189   
190   osg::Vec4Array* cl = new osg::Vec4Array;
191   cl->push_back(osg::Vec4(1, 1, 1, 1));
192   
193   osg::Geometry* geometry = new osg::Geometry;
194   geometry->setVertexArray(gg->vertices);
195   geometry->setNormalArray(gg->normals);
196   geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
197   geometry->setColorArray(cl);
198   geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
199   geometry->setTexCoordArray(0, gg->uvs);
200   
201   gg->quads = new osg::DrawArrays(GL_QUADS, 0, gg->vertices->size());
202   geometry->addPrimitiveSet(gg->quads);
203   geode->addDrawable(geometry);
204   group->addChild(geode);
205   return gg;
206 }
207
208 // see $FG_ROOT/Docs/README.scenery
209 //
210 osg::Node*
211 SGMakeSign(SGMaterialLib *matlib, const string& content)
212 {
213     double sign_height = 1.0;  // meter
214     string newmat = "BlackSign";
215     ElementVec elements1, elements2;
216     element_info *close1 = 0;
217     element_info *close2 = 0;
218     double total_width1 = 0.0;
219     double total_width2 = 0.0;
220     bool cmd = false;
221     bool isBackside = false;
222     int size = -1;
223     char oldtype = 0, newtype = 0;
224
225     EffectGeometryMap geoms;
226   
227     osg::Group* object = new osg::Group;
228     object->setName(content);
229
230     SGMaterial *material = 0;
231
232     // Part I: parse & measure
233     for (const char *s = content.data(); *s; s++) {
234         string name;
235         string value;
236
237         if (*s == '{') {
238             if (cmd)
239                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '{' in '" << content << "'.");
240             cmd = true;
241             continue;
242
243         } else if (*s == '}') {
244             if (!cmd)
245                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '}' in '" << content << "'.");
246             cmd = false;
247             continue;
248
249         } else if (!cmd) {
250             name = *s;
251
252         } else {
253             if (*s == ',')
254                 continue;
255
256             for (; *s; s++) {
257                 name += *s;
258                 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
259                     break;
260             }
261             if (!*s) {
262                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
263             } else if (s[1] == '=') {
264                 for (s += 2; *s; s++) {
265                     value += *s;
266                     if (s[1] == ',' || s[1] == '}')
267                         break;
268                 }
269                 if (!*s)
270                     SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
271             }
272
273             if (name == "@@") {
274                 isBackside = true;
275                 continue;
276             }
277
278             if (name == "@size") {
279                 sign_height = strtod(value.data(), 0);
280                 continue;
281             }
282
283             if (name.size() == 2 || name.size() == 3) {
284                 string n = name;
285                 if (n.size() == 3 && n[2] >= '1' && n[2] <= '5') {
286                     size = n[2] - '1';
287                     n = n.substr(0, 2);
288                 }
289                 if (n == "@Y") {
290                     if (size < 3) {
291                         sign_height = HT[size < 0 ? 2 : size];
292                         newmat = "YellowSign";
293                         newtype = 'Y';
294                         continue;
295                     }
296
297                 } else if (n == "@R") {
298                     if (size < 3) {
299                         sign_height = HT[size < 0 ? 2 : size];
300                         newmat = "RedSign";
301                         newtype = 'R';
302                         continue;
303                     }
304
305                 } else if (n == "@L") {
306                     if (size < 3) {
307                         sign_height = HT[size < 0 ? 2 : size];
308                         newmat = "FramedSign";
309                         newtype = 'L';
310                         continue;
311                     }
312
313                 } else if (n == "@B") {
314                     if (size < 0 || size == 3 || size == 4) {
315                         sign_height = HT[size < 0 ? 3 : size];
316                         newmat = "BlackSign";
317                         newtype = 'B';
318                         continue;
319                     }
320                 }
321             }
322
323             for (int i = 0; cmds[i].keyword; i++) {
324                 if (name == cmds[i].keyword) {
325                     name = cmds[i].glyph_name;
326                     break;
327                 }
328             }
329
330             if (name[0] == '@') {
331                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown command `" << name << '\'');
332                 continue;
333             }
334         }
335
336         if (newmat.size()) {
337             material = matlib->find(newmat);
338             newmat.clear();
339         }
340  
341         SGMaterialGlyph *glyph = material->get_glyph(name);
342         if (!glyph) {
343             SG_LOG( SG_TERRAIN, SG_ALERT, SIGN "unsupported glyph '" << *s << '\'');
344             continue;
345         }
346
347         // in managed mode push frame stop and frame start first
348         Effect *state = material->get_effect();
349         if (geoms[state] == NULL) {
350           geoms[state] = makeGeometry(state, object);
351         }
352       
353         element_info *e1;
354         element_info *e2;
355         if (!isBackside) {
356             if (newtype && newtype != oldtype) {
357                 if (close1) {
358                     elements1.push_back(close1);
359                     total_width1 += close1->glyph->get_width() * close1->scale;
360                     close1 = 0;
361                 }
362                 oldtype = newtype;
363                 SGMaterialGlyph *g = material->get_glyph("stop-frame");
364                 if (g)
365                     close1 = new element_info(material, state, g, sign_height, 0);
366                 g = material->get_glyph("start-frame");
367                 if (g) {
368                     e1 = new element_info(material, state, g, sign_height, 0);
369                     elements1.push_back(e1);
370                     total_width1 += e1->glyph->get_width() * e1->scale;
371                 }
372             }
373             // now the actually requested glyph (front)
374             e1 = new element_info(material, state, glyph, sign_height, 0);
375             elements1.push_back(e1);
376             total_width1 += e1->glyph->get_width() * e1->scale;
377         } else {
378             if (newtype && newtype != oldtype) {
379                 if (close2) {
380                     elements2.push_back(close2);
381                     total_width2 += close2->glyph->get_width() * close2->scale;
382                     close2 = 0;
383                 }
384                 oldtype = newtype;
385                 SGMaterialGlyph *g = material->get_glyph("stop-frame");
386                 if (g)
387                     close2 = new element_info(material, state, g, sign_height, 0);
388                 g = material->get_glyph("start-frame");
389                 if (g) {
390                     e2 = new element_info(material, state, g, sign_height, 0);
391                     elements2.push_back(e2);
392                     total_width2 += e2->glyph->get_width() * e2->scale;
393                 }
394             }
395             // now the actually requested glyph (back)
396             e2 = new element_info(material, state, glyph, sign_height, 0);
397             elements2.push_back(e2);
398             total_width2 += e2->glyph->get_width() * e2->scale;
399         }
400     }
401
402     // in managed mode close frame
403     if (close1) {
404         elements1.push_back(close1);
405         total_width1 += close1->glyph->get_width() * close1->scale;
406         close1 = 0;
407     }
408
409     if (close2) {
410         elements2.push_back(close2);
411         total_width2 += close2->glyph->get_width() * close2->scale;
412         close2 = 0;
413     }
414
415
416   // Part II: typeset
417     double boxwidth = std::max(total_width1, total_width2) * 0.5;
418     double hpos = -boxwidth;
419     SGMaterial *mat = matlib->find("signcase");
420     geoms[mat->get_effect()] = makeGeometry(mat->get_effect(), object);
421   
422     double coverSize = fabs(total_width1 - total_width2) * 0.5;
423   
424     if (total_width1 < total_width2) {
425         if (mat) {
426             element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize);
427             element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize);
428             elements1.insert(elements1.begin(), s1);
429             elements1.push_back(s2);
430         }
431
432     } else if (total_width2 < total_width1) {
433         if (mat) {
434             element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize);
435             element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize);
436             elements2.insert(elements2.begin(), s1);
437             elements2.push_back(s2);
438         }
439     }
440
441     // Create front side
442     SGMakeSignFace(geoms, elements1, hpos, osg::Matrix::identity());
443
444     // Create back side
445     const osg::Vec3d axis(0, 0, 1);
446     SGMakeSignFace(geoms, elements2, hpos, osg::Matrix::rotate( M_PI, axis));
447     geoms[mat->get_effect()]->addSignCase(boxwidth, sign_height, osg::Matrix::identity());
448   
449     return object;
450 }