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>
28 #include <boost/foreach.hpp>
31 #include <osg/Geometry>
33 #include <osg/MatrixTransform>
34 #include <osg/StateSet>
36 #include <simgear/debug/logstream.hxx>
37 #include <simgear/math/sg_types.hxx>
38 #include <simgear/scene/material/Effect.hxx>
39 #include <simgear/scene/material/EffectGeode.hxx>
40 #include <simgear/scene/material/mat.hxx>
41 #include <simgear/scene/material/matlib.hxx>
43 #include "apt_signs.hxx"
45 #define SIGN "OBJECT_SIGN: "
48 using namespace simgear;
50 // for temporary storage of sign elements
52 element_info(SGMaterial *m, Effect *s, SGMaterialGlyph *g, double h, double c)
53 : material(m), state(s), glyph(g), height(h), coverwidth(c)
55 scale = h * m->get_xsize()
56 / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize());
57 abswidth = c == 0 ? g->get_width() * scale : c;
61 SGMaterialGlyph *glyph;
68 typedef std::vector<element_info*> ElementVec;
73 const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760}; // standard panel height sizes
74 const double grounddist = 0.2; // hard-code sign distance from surface for now
75 const double thick = 0.1; // half the thickness of the 3D sign
78 // translation table for "command" to "glyph name"
81 const char *glyph_name;
100 osg::DrawArrays* quads;
102 osg::Vec3Array* vertices;
103 osg::Vec3Array* normals;
105 void addGlyph(SGMaterialGlyph* glyph, double x, double y, double width, double height, const osg::Matrix& xform)
108 vertices->push_back(xform.preMult(osg::Vec3(thick, x, y)));
109 vertices->push_back(xform.preMult(osg::Vec3(thick, x + width, y)));
110 vertices->push_back(xform.preMult(osg::Vec3(thick, x + width, y + height)));
111 vertices->push_back(xform.preMult(osg::Vec3(thick, x, y + height)));
113 // texture coordinates
114 double xoffset = glyph->get_left();
115 double texWidth = glyph->get_width();
117 uvs->push_back(osg::Vec2(xoffset, 0));
118 uvs->push_back(osg::Vec2(xoffset + texWidth, 0));
119 uvs->push_back(osg::Vec2(xoffset + texWidth, 1));
120 uvs->push_back(osg::Vec2(xoffset, 1));
123 for (int i=0; i<4; ++i)
124 normals->push_back(xform.preMult(osg::Vec3(0, -1, 0)));
126 quads->setCount(vertices->size());
129 void addSignCase(double caseWidth, double caseHeight, const osg::Matrix& xform)
131 vertices->push_back(osg::Vec3(-thick, -caseWidth, grounddist));
132 vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist));
133 vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist + caseHeight));
134 vertices->push_back(osg::Vec3(-thick, -caseWidth, grounddist + caseHeight));
137 for (int i=0; i<4; ++i)
138 normals->push_back(xform.preMult(osg::Vec3(-1, 0.0, 0)));
140 vertices->push_back(osg::Vec3(-thick, -caseWidth, grounddist + caseHeight));
141 vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist + caseHeight));
142 vertices->push_back(osg::Vec3(thick, caseWidth, grounddist + caseHeight));
143 vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight));
145 for (int i=0; i<4; ++i)
146 normals->push_back(xform.preMult(osg::Vec3(0, 0, 1)));
148 vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight));
149 vertices->push_back(osg::Vec3(thick, caseWidth, grounddist + caseHeight));
150 vertices->push_back(osg::Vec3(thick, caseWidth, grounddist));
151 vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist));
153 for (int i=0; i<4; ++i)
154 normals->push_back(xform.preMult(osg::Vec3(1, 0.0, 0)));
156 for (int i = 0; i < 3; ++i) {
157 uvs->push_back(osg::Vec2(1, 1));
158 uvs->push_back(osg::Vec2(0.75, 1));
159 uvs->push_back(osg::Vec2(0.75, 0));
160 uvs->push_back(osg::Vec2(1, 0));
163 quads->setCount(vertices->size());
167 typedef std::map<Effect*, GlyphGeometry*> EffectGeometryMap;
169 void SGMakeSignFace(EffectGeometryMap& glyphs, const ElementVec& elements, double hpos, const osg::Matrix& xform)
171 BOOST_FOREACH(element_info* element, elements) {
172 GlyphGeometry* gg = glyphs[element->state];
173 gg->addGlyph(element->glyph, hpos, grounddist, element->abswidth, element->height, xform);
174 hpos += element->abswidth;
179 GlyphGeometry* makeGeometry(Effect* eff, osg::Group* group)
181 GlyphGeometry* gg = new GlyphGeometry;
183 EffectGeode* geode = new EffectGeode;
184 geode->setEffect(eff);
186 gg->vertices = new osg::Vec3Array;
187 gg->normals = new osg::Vec3Array;
188 gg->uvs = new osg::Vec2Array;
190 osg::Vec4Array* cl = new osg::Vec4Array;
191 cl->push_back(osg::Vec4(1, 1, 1, 1));
193 osg::Geometry* geometry = new osg::Geometry;
194 geometry->setVertexArray(gg->vertices);
195 geometry->setNormalArray(gg->normals);
196 geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
197 geometry->setColorArray(cl);
198 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
199 geometry->setTexCoordArray(0, gg->uvs);
201 gg->quads = new osg::DrawArrays(GL_QUADS, 0, gg->vertices->size());
202 geometry->addPrimitiveSet(gg->quads);
203 geode->addDrawable(geometry);
204 group->addChild(geode);
208 // see $FG_ROOT/Docs/README.scenery
211 SGMakeSign(SGMaterialLib *matlib, const string& content)
213 double sign_height = 1.0; // meter
214 string newmat = "BlackSign";
215 ElementVec elements1, elements2;
216 element_info *close1 = 0;
217 element_info *close2 = 0;
218 double total_width1 = 0.0;
219 double total_width2 = 0.0;
221 bool isBackside = false;
223 char oldtype = 0, newtype = 0;
225 EffectGeometryMap geoms;
227 osg::Group* object = new osg::Group;
228 object->setName(content);
230 SGMaterial *material = 0;
232 // Part I: parse & measure
233 for (const char *s = content.data(); *s; s++) {
239 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '{' in '" << content << "'.");
243 } else if (*s == '}') {
245 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "Illegal taxiway sign syntax. Unexpected '}' in '" << content << "'.");
258 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
262 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
263 } else if (s[1] == '=') {
264 for (s += 2; *s; s++) {
266 if (s[1] == ',' || s[1] == '}')
270 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
278 if (name == "@size") {
279 sign_height = strtod(value.data(), 0);
283 if (name.size() == 2 || name.size() == 3) {
285 if (n.size() == 3 && n[2] >= '1' && n[2] <= '5') {
291 sign_height = HT[size < 0 ? 2 : size];
292 newmat = "YellowSign";
297 } else if (n == "@R") {
299 sign_height = HT[size < 0 ? 2 : size];
305 } else if (n == "@L") {
307 sign_height = HT[size < 0 ? 2 : size];
308 newmat = "FramedSign";
313 } else if (n == "@B") {
314 if (size < 0 || size == 3 || size == 4) {
315 sign_height = HT[size < 0 ? 3 : size];
316 newmat = "BlackSign";
323 for (int i = 0; cmds[i].keyword; i++) {
324 if (name == cmds[i].keyword) {
325 name = cmds[i].glyph_name;
330 if (name[0] == '@') {
331 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown command `" << name << '\'');
337 material = matlib->find(newmat);
341 SGMaterialGlyph *glyph = material->get_glyph(name);
343 SG_LOG( SG_TERRAIN, SG_ALERT, SIGN "unsupported glyph '" << *s << '\'');
347 // in managed mode push frame stop and frame start first
348 Effect *state = material->get_effect();
349 if (geoms[state] == NULL) {
350 geoms[state] = makeGeometry(state, object);
356 if (newtype && newtype != oldtype) {
358 elements1.push_back(close1);
359 total_width1 += close1->glyph->get_width() * close1->scale;
363 SGMaterialGlyph *g = material->get_glyph("stop-frame");
365 close1 = new element_info(material, state, g, sign_height, 0);
366 g = material->get_glyph("start-frame");
368 e1 = new element_info(material, state, g, sign_height, 0);
369 elements1.push_back(e1);
370 total_width1 += e1->glyph->get_width() * e1->scale;
373 // now the actually requested glyph (front)
374 e1 = new element_info(material, state, glyph, sign_height, 0);
375 elements1.push_back(e1);
376 total_width1 += e1->glyph->get_width() * e1->scale;
378 if (newtype && newtype != oldtype) {
380 elements2.push_back(close2);
381 total_width2 += close2->glyph->get_width() * close2->scale;
385 SGMaterialGlyph *g = material->get_glyph("stop-frame");
387 close2 = new element_info(material, state, g, sign_height, 0);
388 g = material->get_glyph("start-frame");
390 e2 = new element_info(material, state, g, sign_height, 0);
391 elements2.push_back(e2);
392 total_width2 += e2->glyph->get_width() * e2->scale;
395 // now the actually requested glyph (back)
396 e2 = new element_info(material, state, glyph, sign_height, 0);
397 elements2.push_back(e2);
398 total_width2 += e2->glyph->get_width() * e2->scale;
402 // in managed mode close frame
404 elements1.push_back(close1);
405 total_width1 += close1->glyph->get_width() * close1->scale;
410 elements2.push_back(close2);
411 total_width2 += close2->glyph->get_width() * close2->scale;
417 double boxwidth = std::max(total_width1, total_width2) * 0.5;
418 double hpos = -boxwidth;
419 SGMaterial *mat = matlib->find("signcase");
420 geoms[mat->get_effect()] = makeGeometry(mat->get_effect(), object);
422 double coverSize = fabs(total_width1 - total_width2) * 0.5;
424 if (total_width1 < total_width2) {
426 element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize);
427 element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize);
428 elements1.insert(elements1.begin(), s1);
429 elements1.push_back(s2);
432 } else if (total_width2 < total_width1) {
434 element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize);
435 element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize);
436 elements2.insert(elements2.begin(), s1);
437 elements2.push_back(s2);
442 SGMakeSignFace(geoms, elements1, hpos, osg::Matrix::identity());
445 const osg::Vec3d axis(0, 0, 1);
446 SGMakeSignFace(geoms, elements2, hpos, osg::Matrix::rotate( M_PI, axis));
447 geoms[mat->get_effect()]->addSignCase(boxwidth, sign_height, osg::Matrix::identity());