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_TAXI_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 *sgMakeTaxiSign( SGMaterialLib *matlib,
91 const string path, const string content )
93 double sign_height = 1.0; // meter
96 vector<element_info *> elements;
97 element_info *close = 0;
98 double total_width = 0.0;
100 char *newmat = "BlackSign";
102 char oldtype = 0, newtype = 0;
104 ssgBranch *object = new ssgBranch();
105 object->setName((char *)content.c_str());
106 SGMaterial *material = matlib->find(newmat);
108 // Part I: parse & measure
109 for (const char *s = content.data(); *s; s++) {
111 const char *newmat = 0;
115 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unexpected { in sign contents");
119 } else if (*s == '}') {
121 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unexpected } in sign contents");
135 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
139 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unclosed { in sign contents");
140 } else if (s[1] == '=') {
141 for (s += 2; *s; s++) {
143 if (s[1] == ',' || s[1] == '}')
147 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "unclosed { in sign contents");
150 if (name == "@size") {
151 sign_height = strtod(value.data(), 0);
155 if (name == "@light") {
156 lighted = (value != "0" && value != "no" && value != "off" && value != "false");
160 if (name == "@material")
161 newmat = value.data();
162 else if (name.size() == 2 || name.size() == 3) {
163 if (name.size() == 3 && name[2] >= '1' && name[2] <= '5') {
164 size = name[2] - '1';
165 name = name.substr(0, 2);
170 sign_height = HT[size < 0 ? 2 : size];
171 newmat = "YellowSign";
174 } else if (name == "@R") {
176 sign_height = HT[size < 0 ? 2 : size];
180 } else if (name == "@L") {
182 sign_height = HT[size < 0 ? 2 : size];
183 newmat = "FramedSign";
186 } else if (name == "@B") {
187 if (size < 0 || size == 3 || size == 4) {
188 sign_height = HT[size < 0 ? 3 : size];
189 newmat = "BlackSign";
196 material = matlib->find(newmat);
200 for (int i = 0; cmds[i].keyword; i++) {
201 if (name == cmds[i].keyword) {
202 name = cmds[i].glyph_name;
207 if (name[0] == '@') {
208 SG_LOG(SG_TERRAIN, SG_ALERT, TAXI "ignoring unknown command `" << name << '\'');
214 SG_LOG( SG_TERRAIN, SG_ALERT, TAXI "material doesn't exit");
218 SGMaterialGlyph *glyph = material->get_glyph(name);
220 SG_LOG( SG_TERRAIN, SG_ALERT, TAXI "unsupported glyph `" << *s << '\'');
224 // in managed mode push frame stop and frame start first
226 if (newtype && newtype != oldtype) {
228 elements.push_back(close);
229 total_width += close->glyph->get_width() * close->scale;
233 SGMaterialGlyph *g = material->get_glyph("stop-frame");
235 close = new element_info(material, g, sign_height, lighted);
236 g = material->get_glyph("start-frame");
238 e = new element_info(material, g, sign_height, lighted);
239 elements.push_back(e);
240 total_width += e->glyph->get_width() * e->scale;
243 // now the actually requested glyph
244 e = new element_info(material, glyph, sign_height, lighted);
245 elements.push_back(e);
246 total_width += e->glyph->get_width() * e->scale;
249 // in managed mode close frame
251 elements.push_back(close);
252 total_width += close->glyph->get_width() * close->scale;
258 double hpos = -total_width / 2;
259 const double dist = 0.25; // hard-code distance from surface for now
262 sgSetVec3(normal, 0, -1, 0);
265 sgSetVec4(color, 1.0, 1.0, 1.0, 1.0);
267 for (unsigned int i = 0; i < elements.size(); i++) {
268 element_info *element = elements[i];
270 double xoffset = element->glyph->get_left();
271 double height = element->height;
272 double width = element->glyph->get_width();
273 double abswidth = width * element->scale;
276 ssgVertexArray *vl = new ssgVertexArray(4);
277 vl->add(hpos, 0, dist);
278 vl->add(hpos + abswidth, 0, dist);
279 vl->add(hpos, 0, dist + height);
280 vl->add(hpos + abswidth, 0, dist + height);
282 // texture coordinates
283 ssgTexCoordArray *tl = new ssgTexCoordArray(4);
285 tl->add(xoffset + width, 0);
287 tl->add(xoffset + width, 1);
290 ssgNormalArray *nl = new ssgNormalArray(1);
294 ssgColourArray *cl = new ssgColourArray(1);
297 ssgLeaf *leaf = new ssgVtxTable(GL_TRIANGLE_STRIP, vl, nl, tl, cl);
298 ssgSimpleState *state = element->material->get_state();
299 if (!element->lighted) {
300 // FIXME: clone state for unlighted elements only once per material
301 state = (ssgSimpleState *)state->clone(SSG_CLONE_STATE);
302 state->setMaterial(GL_EMISSION, 0.3, 0.3, 0.3, 1);
304 leaf->setState(state);
306 object->addKid(leaf);
312 // minimalistic backside
313 ssgVertexArray *vl = new ssgVertexArray(4);
314 vl->add(hpos, 0, dist);
315 vl->add(hpos - total_width, 0, dist);
316 vl->add(hpos, 0, dist + sign_height);
317 vl->add(hpos - total_width, 0, dist + sign_height);
319 ssgNormalArray *nl = new ssgNormalArray(1);
322 ssgLeaf *leaf = new ssgVtxTable(GL_TRIANGLE_STRIP, vl, nl, 0, 0);
323 SGMaterial *mat = matlib->find("BlackSign");
325 leaf->setState(mat->get_state());
326 object->addKid(leaf);
335 ssgBranch *sgMakeRunwaySign( SGMaterialLib *matlib,
336 const string path, const string name )
338 // for demo purposes we assume each element (letter) is 1x1 meter.
339 // Sign is placed 0.25 meters above the ground
341 ssgBranch *object = new ssgBranch();
342 object->setName( (char *)name.c_str() );
344 double width = name.length() / 3.0;
346 string material = name;
350 point_list texcoords;
351 int_list vertex_index;
352 int_list normal_index;
355 nodes.push_back( Point3D( -width, 0, 0.25 ) );
356 nodes.push_back( Point3D( width + 1, 0, 0.25 ) );
357 nodes.push_back( Point3D( -width, 0, 1.25 ) );
358 nodes.push_back( Point3D( width + 1, 0, 1.25 ) );
360 normals.push_back( Point3D( 0, -1, 0 ) );
362 texcoords.push_back( Point3D( 0, 0, 0 ) );
363 texcoords.push_back( Point3D( 1, 0, 0 ) );
364 texcoords.push_back( Point3D( 0, 1, 0 ) );
365 texcoords.push_back( Point3D( 1, 1, 0 ) );
367 vertex_index.push_back( 0 );
368 vertex_index.push_back( 1 );
369 vertex_index.push_back( 2 );
370 vertex_index.push_back( 3 );
372 normal_index.push_back( 0 );
373 normal_index.push_back( 0 );
374 normal_index.push_back( 0 );
375 normal_index.push_back( 0 );
377 tex_index.push_back( 0 );
378 tex_index.push_back( 1 );
379 tex_index.push_back( 2 );
380 tex_index.push_back( 3 );
382 ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_STRIP, matlib, material,
383 nodes, normals, texcoords,
384 vertex_index, normal_index, tex_index,
387 object->addKid( leaf );