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>
29 #include <osg/StateSet>
31 #include <osg/Geometry>
34 #include <simgear/debug/logstream.hxx>
35 #include <simgear/math/sg_types.hxx>
36 #include <simgear/scene/material/Effect.hxx>
37 #include <simgear/scene/material/EffectGeode.hxx>
38 #include <simgear/scene/material/mat.hxx>
39 #include <simgear/scene/material/matlib.hxx>
41 #include "apt_signs.hxx"
43 #define SIGN "OBJECT_SIGN: "
44 #define RWY "OBJECT_RUNWAY_SIGN: "
47 using namespace simgear;
49 // for temporary storage of sign elements
51 element_info(SGMaterial *m, Effect *s, SGMaterialGlyph *g, double h)
52 : material(m), state(s), glyph(g), height(h)
54 scale = h * m->get_xsize()
55 / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize());
59 SGMaterialGlyph *glyph;
65 // standard panel height sizes
66 const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760};
69 // translation table for "command" to "glyph name"
72 const char *glyph_name;
90 // see $FG_ROOT/Docs/README.scenery
93 SGMakeSign(SGMaterialLib *matlib, const string& path, const string& content)
95 double sign_height = 1.0; // meter
97 string newmat = "BlackSign";
99 vector<element_info *> elements;
100 element_info *close = 0;
101 double total_width = 0.0;
104 char oldtype = 0, newtype = 0;
106 osg::Group* object = new osg::Group;
107 object->setName(content);
109 SGMaterial *material = 0;
110 Effect *lighted_state = 0;
111 Effect *unlighted_state = 0;
113 // Part I: parse & measure
114 for (const char *s = content.data(); *s; s++) {
120 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unexpected { in sign contents");
124 } else if (*s == '}') {
126 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unexpected } in sign contents");
139 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
143 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
144 } else if (s[1] == '=') {
145 for (s += 2; *s; s++) {
147 if (s[1] == ',' || s[1] == '}')
151 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
154 if (name == "@size") {
155 sign_height = strtod(value.data(), 0);
159 if (name == "@light") {
160 lighted = (value != "0" && value != "no" && value != "off" && value != "false");
164 if (name == "@material") {
165 newmat = value.data();
168 } else if (name.size() == 2 || name.size() == 3) {
170 if (n.size() == 3 && n[2] >= '1' && n[2] <= '5') {
177 sign_height = HT[size < 0 ? 2 : size];
178 newmat = "YellowSign";
183 } else if (n == "@R") {
185 sign_height = HT[size < 0 ? 2 : size];
191 } else if (n == "@L") {
193 sign_height = HT[size < 0 ? 2 : size];
194 newmat = "FramedSign";
199 } else if (n == "@B") {
200 if (size < 0 || size == 3 || size == 4) {
201 sign_height = HT[size < 0 ? 3 : size];
202 newmat = "BlackSign";
209 for (int i = 0; cmds[i].keyword; i++) {
210 if (name == cmds[i].keyword) {
211 name = cmds[i].glyph_name;
216 if (name[0] == '@') {
217 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown command `" << name << '\'');
223 SGMaterial *m = matlib->find(newmat);
225 // log error, but keep using previous material to at least show something
226 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown material `" << newmat << '\'');
229 // set material states (lighted & unlighted)
230 lighted_state = material->get_effect();
231 newmat.append(".unlighted");
233 m = matlib->find(newmat);
235 unlighted_state = m->get_effect();
237 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown material `" << newmat << '\'');
238 unlighted_state = lighted_state;
244 // This can only happen if the default material is missing.
245 // Error has been already logged in the block above.
246 if (!material) continue;
248 SGMaterialGlyph *glyph = material->get_glyph(name);
250 SG_LOG( SG_TERRAIN, SG_ALERT, SIGN "unsupported glyph `" << *s << '\'');
254 // in managed mode push frame stop and frame start first
255 Effect *state = lighted ? lighted_state : unlighted_state;
257 if (newtype && newtype != oldtype) {
259 elements.push_back(close);
260 total_width += close->glyph->get_width() * close->scale;
264 SGMaterialGlyph *g = material->get_glyph("stop-frame");
266 close = new element_info(material, state, g, sign_height);
267 g = material->get_glyph("start-frame");
269 e = new element_info(material, state, g, sign_height);
270 elements.push_back(e);
271 total_width += e->glyph->get_width() * e->scale;
274 // now the actually requested glyph
275 e = new element_info(material, state, glyph, sign_height);
276 elements.push_back(e);
277 total_width += e->glyph->get_width() * e->scale;
280 // in managed mode close frame
282 elements.push_back(close);
283 total_width += close->glyph->get_width() * close->scale;
289 double hpos = -total_width / 2;
290 const double dist = 0.25; // hard-code distance from surface for now
292 for (unsigned int i = 0; i < elements.size(); i++) {
293 element_info *element = elements[i];
295 double xoffset = element->glyph->get_left();
296 double height = element->height;
297 double width = element->glyph->get_width();
298 double abswidth = width * element->scale;
301 osg::Vec3Array* vl = new osg::Vec3Array;
302 vl->push_back(osg::Vec3(0, hpos, dist));
303 vl->push_back(osg::Vec3(0, hpos + abswidth, dist));
304 vl->push_back(osg::Vec3(0, hpos, dist + height));
305 vl->push_back(osg::Vec3(0, hpos + abswidth, dist + height));
307 // texture coordinates
308 osg::Vec2Array* tl = new osg::Vec2Array;
309 tl->push_back(osg::Vec2(xoffset, 0));
310 tl->push_back(osg::Vec2(xoffset + width, 0));
311 tl->push_back(osg::Vec2(xoffset, 1));
312 tl->push_back(osg::Vec2(xoffset + width, 1));
315 osg::Vec3Array* nl = new osg::Vec3Array;
316 nl->push_back(osg::Vec3(0, -1, 0));
319 osg::Vec4Array* cl = new osg::Vec4Array;
320 cl->push_back(osg::Vec4(1, 1, 1, 1));
322 osg::Geometry* geometry = new osg::Geometry;
323 geometry->setVertexArray(vl);
324 geometry->setNormalArray(nl);
325 geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
326 geometry->setColorArray(cl);
327 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
328 geometry->setTexCoordArray(0, tl);
329 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl->size()));
330 EffectGeode* geode = new EffectGeode;
331 geode->addDrawable(geometry);
332 geode->setEffect(element->state);
334 object->addChild(geode);
340 // minimalistic backside
341 osg::Vec3Array* vl = new osg::Vec3Array;
342 vl->push_back(osg::Vec3(0, hpos, dist));
343 vl->push_back(osg::Vec3(0, hpos - total_width, dist));
344 vl->push_back(osg::Vec3(0, hpos, dist + sign_height));
345 vl->push_back(osg::Vec3(0, hpos - total_width, dist + sign_height));
347 osg::Vec2Array* tl = new osg::Vec2Array;
348 for (int i = 0; i < 4; ++i)
349 tl->push_back(osg::Vec2(0.0, 0.0));
350 osg::Vec3Array* nl = new osg::Vec3Array;
351 nl->push_back(osg::Vec3(0, 1, 0));
352 osg::Vec4Array* cl = new osg::Vec4Array;
353 cl->push_back(osg::Vec4(0.0, 0.0, 0.0, 1.0));
354 osg::Geometry* geometry = new osg::Geometry;
355 geometry->setVertexArray(vl);
356 geometry->setNormalArray(nl);
357 geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
358 geometry->setTexCoordArray(0, tl);
359 geometry->setColorArray(cl);
360 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
361 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl->size()));
362 EffectGeode* geode = new EffectGeode;
363 geode->addDrawable(geometry);
364 SGMaterial *mat = matlib->find("BlackSign");
366 geode->setEffect(mat->get_effect());
367 object->addChild(geode);
373 SGMakeRunwaySign(SGMaterialLib *matlib, const string& path, const string& name)
375 // for demo purposes we assume each element (letter) is 1x1 meter.
376 // Sign is placed 0.25 meters above the ground
378 float width = name.length() / 3.0;
380 osg::Vec3 corner(-width, 0, 0.25f);
381 osg::Vec3 widthVec(2*width + 1, 0, 0);
382 osg::Vec3 heightVec(0, 0, 1);
383 osg::Geometry* geometry;
384 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
385 EffectGeode* geode = new EffectGeode;
386 geode->setName(name);
387 geode->addDrawable(geometry);
388 SGMaterial *mat = matlib->find(name);
390 geode->setEffect(mat->get_effect());