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