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>
30 #include <osg/Geometry>
32 #include <osg/MatrixTransform>
33 #include <osg/StateSet>
35 #include <simgear/debug/logstream.hxx>
36 #include <simgear/math/sg_types.hxx>
37 #include <simgear/scene/material/Effect.hxx>
38 #include <simgear/scene/material/EffectGeode.hxx>
39 #include <simgear/scene/material/mat.hxx>
40 #include <simgear/scene/material/matlib.hxx>
42 #include "apt_signs.hxx"
44 #define SIGN "OBJECT_SIGN: "
47 using namespace simgear;
49 // for temporary storage of sign elements
51 element_info(SGMaterial *m, Effect *s, SGMaterialGlyph *g, double h, double c)
52 : material(m), state(s), glyph(g), height(h), coverwidth(c)
54 scale = h * m->get_xsize()
55 / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize());
56 abswidth = c == 0 ? g->get_width() * scale : c;
60 SGMaterialGlyph *glyph;
67 typedef std::vector<element_info*> ElementVec;
69 const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760}; // standard panel height sizes
70 const double grounddist = 0.2; // hard-code sign distance from surface for now
71 const double thick = 0.1; // half the thickness of the 3D sign
74 // translation table for "command" to "glyph name"
77 const char *glyph_name;
94 void SGMakeSignFace(osg::Group* object, const ElementVec& elements, double hpos, bool isBackside)
96 std::map<Effect*, EffectGeode*> geodesByEffect;
98 for (unsigned int i = 0; i < elements.size(); i++) {
99 element_info *element = elements[i];
101 double xoffset = element->glyph->get_left();
102 double height = element->height;
103 double width = element->glyph->get_width();
105 osg::Vec3Array* vl = new osg::Vec3Array;
106 osg::Vec2Array* tl = new osg::Vec2Array;
109 vl->push_back(osg::Vec3(thick, hpos, grounddist));
110 vl->push_back(osg::Vec3(thick, hpos + element->abswidth, grounddist));
111 vl->push_back(osg::Vec3(thick, hpos, grounddist + height));
112 vl->push_back(osg::Vec3(thick, hpos + element->abswidth, grounddist + height));
114 // texture coordinates
115 tl->push_back(osg::Vec2(xoffset, 0));
116 tl->push_back(osg::Vec2(xoffset + width, 0));
117 tl->push_back(osg::Vec2(xoffset, 1));
118 tl->push_back(osg::Vec2(xoffset + width, 1));
121 osg::Vec3Array* nl = new osg::Vec3Array;
122 nl->push_back(osg::Vec3(0, -1, 0));
125 osg::Vec4Array* cl = new osg::Vec4Array;
126 cl->push_back(osg::Vec4(1, 1, 1, 1));
128 osg::Geometry* geometry = new osg::Geometry;
129 geometry->setVertexArray(vl);
130 geometry->setNormalArray(nl);
131 geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
132 geometry->setColorArray(cl);
133 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
134 geometry->setTexCoordArray(0, tl);
135 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl->size()));
137 EffectGeode* geode = geodesByEffect[element->state];
139 geode = new EffectGeode();
140 geode->setEffect(element->state);
141 geodesByEffect[element->state] = geode;
144 geode->addDrawable(geometry);
147 osg::MatrixTransform* transform = new osg::MatrixTransform();
148 const double angle = 180;
149 const osg::Vec3d axis(0, 0, 1);
150 transform->setMatrix(osg::Matrix::rotate( osg::DegreesToRadians(angle), axis));
151 transform->addChild(geode);
152 object->addChild(transform);
154 object->addChild(geode);
156 hpos += element->abswidth;
162 // see $FG_ROOT/Docs/README.scenery
165 SGMakeSign(SGMaterialLib *matlib, const string& content)
167 double sign_height = 1.0; // meter
168 string newmat = "BlackSign";
169 ElementVec elements1, elements2;
170 element_info *close1 = 0;
171 element_info *close2 = 0;
172 double total_width1 = 0.0;
173 double total_width2 = 0.0;
175 bool isBackside = false;
177 char oldtype = 0, newtype = 0;
179 osg::Group* object = new osg::Group;
180 object->setName(content);
182 SGMaterial *material = 0;
184 // Part I: parse & measure
185 for (const char *s = content.data(); *s; s++) {
191 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '{' in '" << content << "'.");
195 } else if (*s == '}') {
197 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '}' in '" << content << "'.");
210 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
214 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
215 } else if (s[1] == '=') {
216 for (s += 2; *s; s++) {
218 if (s[1] == ',' || s[1] == '}')
222 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
230 if (name == "@size") {
231 sign_height = strtod(value.data(), 0);
235 if (name.size() == 2 || name.size() == 3) {
237 if (n.size() == 3 && n[2] >= '1' && n[2] <= '5') {
243 sign_height = HT[size < 0 ? 2 : size];
244 newmat = "YellowSign";
249 } else if (n == "@R") {
251 sign_height = HT[size < 0 ? 2 : size];
257 } else if (n == "@L") {
259 sign_height = HT[size < 0 ? 2 : size];
260 newmat = "FramedSign";
265 } else if (n == "@B") {
266 if (size < 0 || size == 3 || size == 4) {
267 sign_height = HT[size < 0 ? 3 : size];
268 newmat = "BlackSign";
275 for (int i = 0; cmds[i].keyword; i++) {
276 if (name == cmds[i].keyword) {
277 name = cmds[i].glyph_name;
282 if (name[0] == '@') {
283 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown command `" << name << '\'');
289 material = matlib->find(newmat);
293 SGMaterialGlyph *glyph = material->get_glyph(name);
295 SG_LOG( SG_TERRAIN, SG_ALERT, SIGN "unsupported glyph '" << *s << '\'');
299 // in managed mode push frame stop and frame start first
300 Effect *state = material->get_effect();
304 if (newtype && newtype != oldtype) {
306 elements1.push_back(close1);
307 total_width1 += close1->glyph->get_width() * close1->scale;
311 SGMaterialGlyph *g = material->get_glyph("stop-frame");
313 close1 = new element_info(material, state, g, sign_height, 0);
314 g = material->get_glyph("start-frame");
316 e1 = new element_info(material, state, g, sign_height, 0);
317 elements1.push_back(e1);
318 total_width1 += e1->glyph->get_width() * e1->scale;
321 // now the actually requested glyph (front)
322 e1 = new element_info(material, state, glyph, sign_height, 0);
323 elements1.push_back(e1);
324 total_width1 += e1->glyph->get_width() * e1->scale;
326 if (newtype && newtype != oldtype) {
328 elements2.push_back(close2);
329 total_width2 += close2->glyph->get_width() * close2->scale;
333 SGMaterialGlyph *g = material->get_glyph("stop-frame");
335 close2 = new element_info(material, state, g, sign_height, 0);
336 g = material->get_glyph("start-frame");
338 e2 = new element_info(material, state, g, sign_height, 0);
339 elements2.push_back(e2);
340 total_width2 += e2->glyph->get_width() * e2->scale;
343 // now the actually requested glyph (back)
344 e2 = new element_info(material, state, glyph, sign_height, 0);
345 elements2.push_back(e2);
346 total_width2 += e2->glyph->get_width() * e2->scale;
350 // in managed mode close frame
352 elements1.push_back(close1);
353 total_width1 += close1->glyph->get_width() * close1->scale;
358 elements2.push_back(close2);
359 total_width2 += close2->glyph->get_width() * close2->scale;
365 double hpos = -total_width1 / 2;
366 double boxwidth = total_width1 > total_width2 ? total_width1/2 : total_width2/2;
368 if (total_width1 < total_width2) {
369 double ssize = (total_width2 - total_width1)/2;
370 SGMaterial *mat = matlib->find("signcase");
372 element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, ssize);
373 element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, ssize);
374 elements1.insert(elements1.begin(), s1);
375 elements1.push_back(s2);
377 hpos = -total_width2 / 2;
379 } else if (total_width2 < total_width1) {
380 double ssize = (total_width1 - total_width2)/2;
381 SGMaterial *mat = matlib->find("signcase");
383 element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, ssize);
384 element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, ssize);
385 elements2.insert(elements2.begin(), s1);
386 elements2.push_back(s2);
388 hpos = -total_width1 / 2;
392 SGMakeSignFace(object, elements1, hpos, false);
395 SGMakeSignFace(object, elements2, hpos, true);
398 // Add 3D case for signs
399 osg::Vec3Array* vl = new osg::Vec3Array;
400 osg::Vec2Array* tl = new osg::Vec2Array;
401 osg::Vec3Array* nl = new osg::Vec3Array;
404 vl->push_back(osg::Vec3(-thick, -boxwidth, grounddist));
405 vl->push_back(osg::Vec3(thick, -boxwidth, grounddist));
406 vl->push_back(osg::Vec3(-thick, -boxwidth, grounddist + sign_height));
407 vl->push_back(osg::Vec3(thick, -boxwidth, grounddist + sign_height));
409 nl->push_back(osg::Vec3(-1, 0, 0));
412 vl->push_back(osg::Vec3(-thick, -boxwidth, grounddist + sign_height));
413 vl->push_back(osg::Vec3(thick, -boxwidth, grounddist + sign_height));
414 vl->push_back(osg::Vec3(-thick, boxwidth, grounddist + sign_height));
415 vl->push_back(osg::Vec3(thick, boxwidth, grounddist + sign_height));
417 nl->push_back(osg::Vec3(0, 0, 1));
420 vl->push_back(osg::Vec3(-thick, boxwidth, grounddist + sign_height));
421 vl->push_back(osg::Vec3(thick, boxwidth, grounddist + sign_height));
422 vl->push_back(osg::Vec3(-thick, boxwidth, grounddist));
423 vl->push_back(osg::Vec3(thick, boxwidth, grounddist));
425 for (int i = 0; i < 4; ++i) {
426 tl->push_back(osg::Vec2(1, 1));
427 tl->push_back(osg::Vec2(0.75, 1));
428 tl->push_back(osg::Vec2(1, 0));
429 tl->push_back(osg::Vec2(0.75, 0));
432 nl->push_back(osg::Vec3(1, 0, 0));
434 osg::Vec4Array* cl = new osg::Vec4Array;
435 cl->push_back(osg::Vec4(1.0, 1.0, 1.0, 1.0));
436 osg::Geometry* geometry = new osg::Geometry;
437 geometry->setVertexArray(vl);
438 geometry->setNormalArray(nl);
439 geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
440 geometry->setTexCoordArray(0, tl);
441 geometry->setColorArray(cl);
442 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
443 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl->size()));
444 EffectGeode* geode = new EffectGeode;
445 geode->addDrawable(geometry);
446 SGMaterial *mat = matlib->find("signcase");
448 geode->setEffect(mat->get_effect());
449 object->addChild(geode);