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