1 // apt_signs.cxx -- build airport signs on the fly
3 // Written by Curtis Olson, started July 2001.
5 // Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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.
24 # include <simgear_config.h>
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>
33 #include "apt_signs.hxx"
35 #define TAXI "OBJECT_SIGN: "
36 #define RWY "OBJECT_RUNWAY_SIGN: "
39 // for temporary storage of sign elements
41 element_info(SGMaterial *m, SGMaterialGlyph *g, double h, bool l)
42 : material(m), glyph(g), height(h), lighted(l)
44 scale = h * m->get_xsize()
45 / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize());
48 SGMaterialGlyph *glyph;
55 // standard panel height sizes
56 const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760};
59 // translation table for "command" to "glyph name"
62 const char *glyph_name;
74 {"@rd", "right-down"},
75 {"@dr", "right-down"},
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
85 // u/d/l/r ... up/down/left/right arrow or two-letter combinations
86 // thereof (ur, dl, ...)
88 // Example: {l}E|{L}[T]|{Y,ur}L|E{r}
90 ssgBranch *sgMakeSign(SGMaterialLib *matlib, const string path, const string content)
92 double sign_height = 1.0; // meter
95 vector<element_info *> elements;
96 element_info *close = 0;
97 double total_width = 0.0;
99 char *newmat = "BlackSign";
101 char oldtype = 0, newtype = 0;
103 ssgBranch *object = new ssgBranch();
104 object->setName((char *)content.c_str());
105 SGMaterial *material = matlib->find(newmat);
107 // Part I: parse & measure
108 for (const char *s = content.data(); *s; s++) {
110 const char *newmat = 0;
114 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unexpected { in sign contents");
118 } else if (*s == '}') {
120 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unexpected } in sign contents");
134 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
138 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unclosed { in sign contents");
139 } else if (s[1] == '=') {
140 for (s += 2; *s; s++) {
142 if (s[1] == ',' || s[1] == '}')
146 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unclosed { in sign contents");
149 if (name == "@size") {
150 sign_height = strtod(value.data(), 0);
154 if (name == "@light") {
155 lighted = (value != "0" && value != "no" && value != "off" && value != "false");
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);
169 sign_height = HT[size < 0 ? 2 : size];
170 newmat = "YellowSign";
173 } else if (name == "@R") {
175 sign_height = HT[size < 0 ? 2 : size];
179 } else if (name == "@L") {
181 sign_height = HT[size < 0 ? 2 : size];
182 newmat = "FramedSign";
185 } else if (name == "@B") {
186 if (size < 0 || size == 3 || size == 4) {
187 sign_height = HT[size < 0 ? 3 : size];
188 newmat = "BlackSign";
195 material = matlib->find(newmat);
199 for (int i = 0; cmds[i].keyword; i++) {
200 if (name == cmds[i].keyword) {
201 name = cmds[i].glyph_name;
206 if (name[0] == '@') {
207 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "ignoring unknown command `" << name << '\'');
213 SG_LOG( SG_TERRAIN, SG_ALERT, TAXI "material doesn't exit");
217 SGMaterialGlyph *glyph = material->get_glyph(name);
219 SG_LOG( SG_TERRAIN, SG_ALERT, TAXI "unsupported glyph `" << *s << '\'');
223 // in managed mode push frame stop and frame start first
225 if (newtype && newtype != oldtype) {
227 elements.push_back(close);
228 total_width += close->glyph->get_width() * close->scale;
232 SGMaterialGlyph *g = material->get_glyph("stop-frame");
234 close = new element_info(material, g, sign_height, lighted);
235 g = material->get_glyph("start-frame");
237 e = new element_info(material, g, sign_height, lighted);
238 elements.push_back(e);
239 total_width += e->glyph->get_width() * e->scale;
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;
248 // in managed mode close frame
250 elements.push_back(close);
251 total_width += close->glyph->get_width() * close->scale;
257 double hpos = -total_width / 2;
258 const double dist = 0.25; // hard-code distance from surface for now
261 sgSetVec3(normal, 0, -1, 0);
264 sgSetVec4(color, 1.0, 1.0, 1.0, 1.0);
266 for (unsigned int i = 0; i < elements.size(); i++) {
267 element_info *element = elements[i];
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;
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);
281 // texture coordinates
282 ssgTexCoordArray *tl = new ssgTexCoordArray(4);
284 tl->add(xoffset + width, 0);
286 tl->add(xoffset + width, 1);
289 ssgNormalArray *nl = new ssgNormalArray(1);
293 ssgColourArray *cl = new ssgColourArray(1);
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);
303 leaf->setState(state);
305 object->addKid(leaf);
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);
318 ssgNormalArray *nl = new ssgNormalArray(1);
321 ssgLeaf *leaf = new ssgVtxTable(GL_TRIANGLE_STRIP, vl, nl, 0, 0);
322 SGMaterial *mat = matlib->find("BlackSign");
324 leaf->setState(mat->get_state());
325 object->addKid(leaf);
334 ssgBranch *sgMakeRunwaySign( SGMaterialLib *matlib,
335 const string path, const string name )
337 // for demo purposes we assume each element (letter) is 1x1 meter.
338 // Sign is placed 0.25 meters above the ground
340 ssgBranch *object = new ssgBranch();
341 object->setName( (char *)name.c_str() );
343 double width = name.length() / 3.0;
345 string material = name;
349 point_list texcoords;
350 int_list vertex_index;
351 int_list normal_index;
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 ) );
359 normals.push_back( Point3D( 0, -1, 0 ) );
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 ) );
366 vertex_index.push_back( 0 );
367 vertex_index.push_back( 1 );
368 vertex_index.push_back( 2 );
369 vertex_index.push_back( 3 );
371 normal_index.push_back( 0 );
372 normal_index.push_back( 0 );
373 normal_index.push_back( 0 );
374 normal_index.push_back( 0 );
376 tex_index.push_back( 0 );
377 tex_index.push_back( 1 );
378 tex_index.push_back( 2 );
379 tex_index.push_back( 3 );
381 ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_STRIP, matlib, material,
382 nodes, normals, texcoords,
383 vertex_index, normal_index, tex_index,
386 object->addKid( leaf );