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/tgdb/leaf.hxx>
37 #include <simgear/scene/material/mat.hxx>
38 #include <simgear/scene/material/matlib.hxx>
40 #include "apt_signs.hxx"
42 #define SIGN "OBJECT_SIGN: "
43 #define RWY "OBJECT_RUNWAY_SIGN: "
47 // for temporary storage of sign elements
49 element_info(SGMaterial *m, osg::StateSet *s, SGMaterialGlyph *g, double h)
50 : material(m), state(s), glyph(g), height(h)
52 scale = h * m->get_xsize()
53 / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize());
57 SGMaterialGlyph *glyph;
63 // standard panel height sizes
64 const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760};
67 // translation table for "command" to "glyph name"
70 const char *glyph_name;
82 {"@rd", "right-down"},
83 {"@dr", "right-down"},
88 // see $FG_ROOT/Docs/README.scenery
91 SGMakeSign(SGMaterialLib *matlib, const string& path, const string& content)
93 double sign_height = 1.0; // meter
95 string newmat = "BlackSign";
97 vector<element_info *> elements;
98 element_info *close = 0;
99 double total_width = 0.0;
102 char oldtype = 0, newtype = 0;
104 osg::Group* object = new osg::Group;
105 object->setName(content);
107 SGMaterial *material;
108 osg::StateSet *lighted_state;
109 osg::StateSet *unlighted_state;
111 // Part I: parse & measure
112 for (const char *s = content.data(); *s; s++) {
118 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unexpected { in sign contents");
122 } else if (*s == '}') {
124 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unexpected } in sign contents");
137 if (s[1] == ',' || s[1] == '}' || s[1] == '=')
141 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
142 } else if (s[1] == '=') {
143 for (s += 2; *s; s++) {
145 if (s[1] == ',' || s[1] == '}')
149 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "unclosed { in sign contents");
152 if (name == "@size") {
153 sign_height = strtod(value.data(), 0);
157 if (name == "@light") {
158 lighted = (value != "0" && value != "no" && value != "off" && value != "false");
162 if (name == "@material") {
163 newmat = value.data();
166 } else if (name.size() == 2 || name.size() == 3) {
168 if (n.size() == 3 && n[2] >= '1' && n[2] <= '5') {
175 sign_height = HT[size < 0 ? 2 : size];
176 newmat = "YellowSign";
181 } else if (n == "@R") {
183 sign_height = HT[size < 0 ? 2 : size];
189 } else if (n == "@L") {
191 sign_height = HT[size < 0 ? 2 : size];
192 newmat = "FramedSign";
197 } else if (n == "@B") {
198 if (size < 0 || size == 3 || size == 4) {
199 sign_height = HT[size < 0 ? 3 : size];
200 newmat = "BlackSign";
207 for (int i = 0; cmds[i].keyword; i++) {
208 if (name == cmds[i].keyword) {
209 name = cmds[i].glyph_name;
214 if (name[0] == '@') {
215 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown command `" << name << '\'');
221 material = matlib->find(newmat);
223 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown material `" << newmat << '\'');
227 // set material states (lighted & unlighted)
228 lighted_state = material->get_state();
229 string u = newmat + ".unlighted";
231 SGMaterial *m = matlib->find(u);
233 unlighted_state = m->get_state();
235 SG_LOG(SG_TERRAIN, SG_ALERT, SIGN "ignoring unknown material `" << u << '\'');
236 unlighted_state = lighted_state;
241 SGMaterialGlyph *glyph = material->get_glyph(name);
243 SG_LOG( SG_TERRAIN, SG_ALERT, SIGN "unsupported glyph `" << *s << '\'');
247 // in managed mode push frame stop and frame start first
248 osg::StateSet *state = lighted ? lighted_state : unlighted_state;
250 if (newtype && newtype != oldtype) {
252 elements.push_back(close);
253 total_width += close->glyph->get_width() * close->scale;
257 SGMaterialGlyph *g = material->get_glyph("stop-frame");
259 close = new element_info(material, state, g, sign_height);
260 g = material->get_glyph("start-frame");
262 e = new element_info(material, state, g, sign_height);
263 elements.push_back(e);
264 total_width += e->glyph->get_width() * e->scale;
267 // now the actually requested glyph
268 e = new element_info(material, state, glyph, sign_height);
269 elements.push_back(e);
270 total_width += e->glyph->get_width() * e->scale;
273 // in managed mode close frame
275 elements.push_back(close);
276 total_width += close->glyph->get_width() * close->scale;
282 double hpos = -total_width / 2;
283 const double dist = 0.25; // hard-code distance from surface for now
285 for (unsigned int i = 0; i < elements.size(); i++) {
286 element_info *element = elements[i];
288 double xoffset = element->glyph->get_left();
289 double height = element->height;
290 double width = element->glyph->get_width();
291 double abswidth = width * element->scale;
294 osg::Vec3Array* vl = new osg::Vec3Array;
295 vl->push_back(osg::Vec3(0, hpos, dist));
296 vl->push_back(osg::Vec3(0, hpos + abswidth, dist));
297 vl->push_back(osg::Vec3(0, hpos, dist + height));
298 vl->push_back(osg::Vec3(0, hpos + abswidth, dist + height));
300 // texture coordinates
301 osg::Vec2Array* tl = new osg::Vec2Array;
302 tl->push_back(osg::Vec2(xoffset, 0));
303 tl->push_back(osg::Vec2(xoffset + width, 0));
304 tl->push_back(osg::Vec2(xoffset, 1));
305 tl->push_back(osg::Vec2(xoffset + width, 1));
308 osg::Vec3Array* nl = new osg::Vec3Array;
309 nl->push_back(osg::Vec3(0, -1, 0));
312 osg::Vec4Array* cl = new osg::Vec4Array;
313 cl->push_back(osg::Vec4(1, 1, 1, 1));
315 osg::Geometry* geometry = new osg::Geometry;
316 geometry->setVertexArray(vl);
317 geometry->setNormalArray(nl);
318 geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
319 geometry->setColorArray(cl);
320 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
321 geometry->setTexCoordArray(0, tl);
322 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl->size()));
323 osg::Geode* geode = new osg::Geode;
324 geode->addDrawable(geometry);
325 geode->setStateSet(element->state);
327 object->addChild(geode);
333 // minimalistic backside
334 osg::Vec3Array* vl = new osg::Vec3Array;
335 vl->push_back(osg::Vec3(0, hpos, dist));
336 vl->push_back(osg::Vec3(0, hpos - total_width, dist));
337 vl->push_back(osg::Vec3(0, hpos, dist + sign_height));
338 vl->push_back(osg::Vec3(0, hpos - total_width, dist + sign_height));
340 osg::Vec3Array* nl = new osg::Vec3Array;
341 nl->push_back(osg::Vec3(0, 1, 0));
343 osg::Geometry* geometry = new osg::Geometry;
344 geometry->setVertexArray(vl);
345 geometry->setNormalArray(nl);
346 geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
347 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl->size()));
348 osg::Geode* geode = new osg::Geode;
349 geode->addDrawable(geometry);
350 SGMaterial *mat = matlib->find("BlackSign");
352 geode->setStateSet(mat->get_state());
353 object->addChild(geode);
363 SGMakeRunwaySign(SGMaterialLib *matlib, const string& path, const string& name)
365 // for demo purposes we assume each element (letter) is 1x1 meter.
366 // Sign is placed 0.25 meters above the ground
368 osg::Group *object = new osg::Group;
369 object->setName(name);
371 double width = name.length() / 3.0;
373 string material = name;
377 point_list texcoords;
378 int_list vertex_index;
379 int_list normal_index;
382 nodes.push_back( Point3D( -width, 0, 0.25 ) );
383 nodes.push_back( Point3D( width + 1, 0, 0.25 ) );
384 nodes.push_back( Point3D( -width, 0, 1.25 ) );
385 nodes.push_back( Point3D( width + 1, 0, 1.25 ) );
387 normals.push_back( Point3D( 0, -1, 0 ) );
389 texcoords.push_back( Point3D( 0, 0, 0 ) );
390 texcoords.push_back( Point3D( 1, 0, 0 ) );
391 texcoords.push_back( Point3D( 0, 1, 0 ) );
392 texcoords.push_back( Point3D( 1, 1, 0 ) );
394 vertex_index.push_back( 0 );
395 vertex_index.push_back( 1 );
396 vertex_index.push_back( 2 );
397 vertex_index.push_back( 3 );
399 normal_index.push_back( 0 );
400 normal_index.push_back( 0 );
401 normal_index.push_back( 0 );
402 normal_index.push_back( 0 );
404 tex_index.push_back( 0 );
405 tex_index.push_back( 1 );
406 tex_index.push_back( 2 );
407 tex_index.push_back( 3 );
409 osg::Node* leaf = SGMakeLeaf( path, GL_TRIANGLE_STRIP, matlib, material,
410 nodes, normals, texcoords,
411 vertex_index, normal_index, tex_index,
414 object->addChild(leaf);