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