]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/apt_signs.cxx
Merge branch 'timoore/aptsign' into next
[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
29 #include <osg/StateSet>
30 #include <osg/Geode>
31 #include <osg/Geometry>
32 #include <osg/Group>
33
34 #include <simgear/debug/logstream.hxx>
35 #include <simgear/math/sg_types.hxx>
36 #include <simgear/scene/material/Effect.hxx>
37 #include <simgear/scene/material/EffectGeode.hxx>
38 #include <simgear/scene/material/mat.hxx>
39 #include <simgear/scene/material/matlib.hxx>
40
41 #include "apt_signs.hxx"
42
43 #define SIGN "OBJECT_SIGN: "
44 #define RWY "OBJECT_RUNWAY_SIGN: "
45
46 using std::vector;
47 using namespace simgear;
48
49 // for temporary storage of sign elements
50 struct element_info {
51     element_info(SGMaterial *m, Effect *s, SGMaterialGlyph *g, double h)
52         : material(m), state(s), glyph(g), height(h)
53     {
54         scale = h * m->get_xsize()
55                 / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize());
56     }
57     SGMaterial *material;
58     Effect *state;
59     SGMaterialGlyph *glyph;
60     double height;
61     double scale;
62 };
63
64
65 // standard panel height sizes
66 const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760};
67
68
69 // translation table for "command" to "glyph name"
70 struct pair {
71     const char *keyword;
72     const char *glyph_name;
73 } cmds[] = {
74     {"@u",       "^u"},
75     {"@d",       "^d"},
76     {"@l",       "^l"},
77     {"@lu",      "^lu"},
78     {"@ul",      "^lu"},
79     {"@ld",      "^ld"},
80     {"@dl",      "^ld"},
81     {"@r",       "^r"},
82     {"@ru",      "^ru"},
83     {"@ur",      "^ru"},
84     {"@rd",      "^rd"},
85     {"@dr",      "^rd"},
86     {0, 0},
87 };
88
89
90 // see $FG_ROOT/Docs/README.scenery
91 //
92 osg::Node*
93 SGMakeSign(SGMaterialLib *matlib, const string& path, const string& content)
94 {
95     double sign_height = 1.0;  // meter
96     bool lighted = true;
97     string newmat = "BlackSign";
98
99     vector<element_info *> elements;
100     element_info *close = 0;
101     double total_width = 0.0;
102     bool cmd = false;
103     int size = -1;
104     char oldtype = 0, newtype = 0;
105
106     osg::Group* object = new osg::Group;
107     object->setName(content);
108
109     SGMaterial *material = 0;
110     Effect *lighted_state = 0;
111     Effect *unlighted_state = 0;
112
113     // Part I: parse & measure
114     for (const char *s = content.data(); *s; s++) {
115         string name;
116         string value;
117
118         if (*s == '{') {
119             if (cmd)
120                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unexpected { in sign contents");
121             cmd = true;
122             continue;
123
124         } else if (*s == '}') {
125             if (!cmd)
126                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unexpected } in sign contents");
127             cmd = false;
128             continue;
129
130         } else if (!cmd) {
131             name = *s;
132
133         } else {
134             if (*s == ',')
135                 continue;
136
137             for (; *s; s++) {
138                 name += *s;
139                 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
140                     break;
141             }
142             if (!*s) {
143                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
144             } else if (s[1] == '=') {
145                 for (s += 2; *s; s++) {
146                     value += *s;
147                     if (s[1] == ',' || s[1] == '}')
148                         break;
149                 }
150                 if (!*s)
151                     SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
152             }
153
154             if (name == "@size") {
155                 sign_height = strtod(value.data(), 0);
156                 continue;
157             }
158
159             if (name == "@light") {
160                 lighted = (value != "0" && value != "no" && value != "off" && value != "false");
161                 continue;
162             }
163
164             if (name == "@material") {
165                 newmat = value.data();
166                 continue;
167
168             } else if (name.size() == 2 || name.size() == 3) {
169                 string n = name;
170                 if (n.size() == 3 && n[2] >= '1' && n[2] <= '5') {
171                     size = n[2] - '1';
172                     n = n.substr(0, 2);
173                 }
174
175                 if (n == "@Y") {
176                     if (size < 3) {
177                         sign_height = HT[size < 0 ? 2 : size];
178                         newmat = "YellowSign";
179                         newtype = 'Y';
180                         continue;
181                     }
182
183                 } else if (n == "@R") {
184                     if (size < 3) {
185                         sign_height = HT[size < 0 ? 2 : size];
186                         newmat = "RedSign";
187                         newtype = 'R';
188                         continue;
189                     }
190
191                 } else if (n == "@L") {
192                     if (size < 3) {
193                         sign_height = HT[size < 0 ? 2 : size];
194                         newmat = "FramedSign";
195                         newtype = 'L';
196                         continue;
197                     }
198
199                 } else if (n == "@B") {
200                     if (size < 0 || size == 3 || size == 4) {
201                         sign_height = HT[size < 0 ? 3 : size];
202                         newmat = "BlackSign";
203                         newtype = 'B';
204                         continue;
205                     }
206                 }
207             }
208
209             for (int i = 0; cmds[i].keyword; i++) {
210                 if (name == cmds[i].keyword) {
211                     name = cmds[i].glyph_name;
212                     break;
213                 }
214             }
215
216             if (name[0] == '@') {
217                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown command `" << name << '\'');
218                 continue;
219             }
220         }
221
222         if (newmat.size()) {
223             SGMaterial *m = matlib->find(newmat);
224             if (!m) {
225                 // log error, but keep using previous material to at least show something
226                 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown material `" << newmat << '\'');
227             } else {
228                 material = m;
229                 // set material states (lighted & unlighted)
230                 lighted_state = material->get_effect();
231                 newmat.append(".unlighted");
232
233                 m = matlib->find(newmat);
234                 if (m) {
235                     unlighted_state = m->get_effect();
236                 } else {
237                     SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown material `" << newmat << '\'');
238                     unlighted_state = lighted_state;
239                 }
240             }
241             newmat.clear();
242         }
243
244         // This can only happen if the default material is missing.
245         // Error has been already logged in the block above.
246         if (!material) continue;
247  
248         SGMaterialGlyph *glyph = material->get_glyph(name);
249         if (!glyph) {
250             SG_LOG( SG_TERRAIN, SG_ALERT, SIGN "unsupported glyph `" << *s << '\'');
251             continue;
252         }
253
254         // in managed mode push frame stop and frame start first
255         Effect *state = lighted ? lighted_state : unlighted_state;
256         element_info *e;
257         if (newtype && newtype != oldtype) {
258             if (close) {
259                 elements.push_back(close);
260                 total_width += close->glyph->get_width() * close->scale;
261                 close = 0;
262             }
263             oldtype = newtype;
264             SGMaterialGlyph *g = material->get_glyph("stop-frame");
265             if (g)
266                 close = new element_info(material, state, g, sign_height);
267             g = material->get_glyph("start-frame");
268             if (g) {
269                 e = new element_info(material, state, g, sign_height);
270                 elements.push_back(e);
271                 total_width += e->glyph->get_width() * e->scale;
272             }
273         }
274         // now the actually requested glyph
275         e = new element_info(material, state, glyph, sign_height);
276         elements.push_back(e);
277         total_width += e->glyph->get_width() * e->scale;
278     }
279
280     // in managed mode close frame
281     if (close) {
282         elements.push_back(close);
283         total_width += close->glyph->get_width() * close->scale;
284         close = 0;
285     }
286
287
288     // Part II: typeset
289     double hpos = -total_width / 2;
290     const double dist = 0.25;        // hard-code distance from surface for now
291
292     for (unsigned int i = 0; i < elements.size(); i++) {
293         element_info *element = elements[i];
294
295         double xoffset = element->glyph->get_left();
296         double height = element->height;
297         double width = element->glyph->get_width();
298         double abswidth = width * element->scale;
299
300         // vertices
301         osg::Vec3Array* vl = new osg::Vec3Array;
302         vl->push_back(osg::Vec3(0, hpos,            dist));
303         vl->push_back(osg::Vec3(0, hpos + abswidth, dist));
304         vl->push_back(osg::Vec3(0, hpos,            dist + height));
305         vl->push_back(osg::Vec3(0, hpos + abswidth, dist + height));
306
307         // texture coordinates
308         osg::Vec2Array* tl = new osg::Vec2Array;
309         tl->push_back(osg::Vec2(xoffset,         0));
310         tl->push_back(osg::Vec2(xoffset + width, 0));
311         tl->push_back(osg::Vec2(xoffset,         1));
312         tl->push_back(osg::Vec2(xoffset + width, 1));
313
314         // normals
315         osg::Vec3Array* nl = new osg::Vec3Array;
316         nl->push_back(osg::Vec3(0, -1, 0));
317
318         // colors
319         osg::Vec4Array* cl = new osg::Vec4Array;
320         cl->push_back(osg::Vec4(1, 1, 1, 1));
321
322         osg::Geometry* geometry = new osg::Geometry;
323         geometry->setVertexArray(vl);
324         geometry->setNormalArray(nl);
325         geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
326         geometry->setColorArray(cl);
327         geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
328         geometry->setTexCoordArray(0, tl);
329         geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl->size()));
330         EffectGeode* geode = new EffectGeode;
331         geode->addDrawable(geometry);
332         geode->setEffect(element->state);
333
334         object->addChild(geode);
335         hpos += abswidth;
336         delete element;
337     }
338
339
340     // minimalistic backside
341     osg::Vec3Array* vl = new osg::Vec3Array;
342     vl->push_back(osg::Vec3(0, hpos,               dist));
343     vl->push_back(osg::Vec3(0, hpos - total_width, dist));
344     vl->push_back(osg::Vec3(0, hpos,               dist + sign_height));
345     vl->push_back(osg::Vec3(0, hpos - total_width, dist + sign_height));
346
347     osg::Vec2Array* tl = new osg::Vec2Array;
348     for (int i = 0; i < 4; ++i)
349         tl->push_back(osg::Vec2(0.0, 0.0));
350     osg::Vec3Array* nl = new osg::Vec3Array;
351     nl->push_back(osg::Vec3(0, 1, 0));
352     osg::Vec4Array* cl = new osg::Vec4Array;
353     cl->push_back(osg::Vec4(0.0, 0.0, 0.0, 1.0));
354     osg::Geometry* geometry = new osg::Geometry;
355     geometry->setVertexArray(vl);
356     geometry->setNormalArray(nl);
357     geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
358     geometry->setTexCoordArray(0, tl);
359     geometry->setColorArray(cl);
360     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
361     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl->size()));
362     EffectGeode* geode = new EffectGeode;
363     geode->addDrawable(geometry);
364     SGMaterial *mat = matlib->find("BlackSign");
365     if (mat)
366       geode->setEffect(mat->get_effect());
367     object->addChild(geode);
368
369     return object;
370 }
371
372 osg::Node*
373 SGMakeRunwaySign(SGMaterialLib *matlib, const string& path, const string& name)
374 {
375     // for demo purposes we assume each element (letter) is 1x1 meter.
376     // Sign is placed 0.25 meters above the ground
377
378     float width = name.length() / 3.0;
379
380     osg::Vec3 corner(-width, 0, 0.25f);
381     osg::Vec3 widthVec(2*width + 1, 0, 0);
382     osg::Vec3 heightVec(0, 0, 1);
383     osg::Geometry* geometry;
384     geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
385     EffectGeode* geode = new EffectGeode;
386     geode->setName(name);
387     geode->addDrawable(geometry);
388     SGMaterial *mat = matlib->find(name);
389     if (mat)
390       geode->setEffect(mat->get_effect());
391
392     return geode;
393 }