From a1f74729ab87a3f35c61bf4d779311596521290c Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 8 May 2012 13:29:34 +0100 Subject: [PATCH] build signs for an STG into single geometry. --- simgear/scene/tgdb/ReaderWriterSTG.cxx | 38 ++++-- simgear/scene/tgdb/ReaderWriterSTG.hxx | 4 +- simgear/scene/tgdb/apt_signs.cxx | 182 +++++++++++++++---------- simgear/scene/tgdb/apt_signs.hxx | 27 +++- 4 files changed, 161 insertions(+), 90 deletions(-) diff --git a/simgear/scene/tgdb/ReaderWriterSTG.cxx b/simgear/scene/tgdb/ReaderWriterSTG.cxx index 8dc917d0..21a47ba6 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.cxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.cxx @@ -47,6 +47,18 @@ using namespace simgear; +static SGBucket bucketIndexFromFileName(const std::string& fileName) +{ + // Extract the bucket from the filename + std::istringstream ss(osgDB::getNameLessExtension(fileName)); + long index; + ss >> index; + if (ss.fail()) + return SGBucket(); + + return SGBucket(index); +} + ReaderWriterSTG::ReaderWriterSTG() { supportsExtension("stg", "SimGear stg database format"); @@ -76,16 +88,11 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt if (!options) return ReadResult::FILE_NOT_FOUND; - // Extract the bucket from the filename - std::istringstream ss(osgDB::getNameLessExtension(fileName)); - long index; - ss >> index; - if (ss.fail()) - return ReadResult::FILE_NOT_FOUND; + SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName); - SGBucket bucket(index); + SGBucket bucket(bucketIndexFromFileName(fileName)); std::string basePath = bucket.gen_base_path(); osg::ref_ptr group = new osg::Group; @@ -100,14 +107,14 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt objects.append("Objects"); objects.append(basePath); objects.append(fileName); - if (readStgFile(objects.str(), *group, options)) + if (readStgFile(objects.str(), bucket, *group, options)) foundBase = true; SGPath terrain(*i); terrain.append("Terrain"); terrain.append(basePath); terrain.append(fileName); - if (readStgFile(terrain.str(), *group, options)) + if (readStgFile(terrain.str(), bucket, *group, options)) foundBase = true; } @@ -135,14 +142,15 @@ ReaderWriterSTG::readStgFile(const std::string& fileName, const osgDB::Options* // This is considered a real existing file. // We still apply the search path algorithms for relative files. osg::ref_ptr group = new osg::Group; - - readStgFile(osgDB::findDataFile(fileName, options), *group, options); + std::string path = osgDB::findDataFile(fileName, options); + readStgFile(path, bucketIndexFromFileName(path), *group, options); return group.get(); } bool ReaderWriterSTG::readStgFile(const std::string& absoluteFileName, + const SGBucket& bucket, osg::Group& group, const osgDB::Options* options) const { if (absoluteFileName.empty()) @@ -172,6 +180,8 @@ ReaderWriterSTG::readStgFile(const std::string& absoluteFileName, std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT"); sharedOptions->getDatabasePathList().push_back(fg_root); + simgear::AirportSignBuilder signBuilder(staticOptions->getMaterialLib(), bucket.get_center()); + bool has_base = false; while ( ! in.eof() ) { std::string token; @@ -259,8 +269,8 @@ ReaderWriterSTG::readStgFile(const std::string& absoluteFileName, } } else if ( token == "OBJECT_SIGN" ) { - node = SGMakeSign(staticOptions->getMaterialLib(), name); - + //node = SGMakeSign(staticOptions->getMaterialLib(), name); + signBuilder.addSign(SGGeod::fromDegM(lon, lat, elev), hdg, name); } else { SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName << ": Unknown token '" << token << "'" ); @@ -285,6 +295,8 @@ ReaderWriterSTG::readStgFile(const std::string& absoluteFileName, in >> ::skipeol; } + + group.addChild(signBuilder.getSignsGroup()); return has_base; } diff --git a/simgear/scene/tgdb/ReaderWriterSTG.hxx b/simgear/scene/tgdb/ReaderWriterSTG.hxx index 0d5e0b30..63c3eb72 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.hxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.hxx @@ -24,6 +24,8 @@ #include +class SGBucket; + namespace simgear { class ReaderWriterSTG : public osgDB::ReaderWriter { @@ -44,7 +46,7 @@ private: /// Read an real existing stg file that exists on disk and return true /// if a BASE_OBJECT is found. bool - readStgFile(const std::string&, osg::Group&, const osgDB::Options*) const; + readStgFile(const std::string&, const SGBucket& bucket, osg::Group&, const osgDB::Options*) const; }; } diff --git a/simgear/scene/tgdb/apt_signs.cxx b/simgear/scene/tgdb/apt_signs.cxx index 83410051..288ee398 100644 --- a/simgear/scene/tgdb/apt_signs.cxx +++ b/simgear/scene/tgdb/apt_signs.cxx @@ -49,15 +49,14 @@ using namespace simgear; // for temporary storage of sign elements struct element_info { - element_info(SGMaterial *m, Effect *s, SGMaterialGlyph *g, double h, double c) - : material(m), state(s), glyph(g), height(h), coverwidth(c) + element_info(SGMaterial *m, SGMaterialGlyph *g, double h, double c) + : material(m), glyph(g), height(h), coverwidth(c) { scale = h * m->get_xsize() / (m->get_ysize() < 0.001 ? 1.0 : m->get_ysize()); abswidth = c == 0 ? g->get_width() * scale : c; } SGMaterial *material; - Effect *state; SGMaterialGlyph *glyph; double height; double scale; @@ -67,9 +66,6 @@ struct element_info { typedef std::vector ElementVec; - - - const double HT[5] = {0.460, 0.610, 0.760, 1.220, 0.760}; // standard panel height sizes const double grounddist = 0.2; // hard-code sign distance from surface for now const double thick = 0.1; // half the thickness of the 3D sign @@ -128,6 +124,8 @@ struct GlyphGeometry void addSignCase(double caseWidth, double caseHeight, const osg::Matrix& xform) { + int last = vertices->size(); + vertices->push_back(osg::Vec3(-thick, -caseWidth, grounddist)); vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist)); vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist + caseHeight)); @@ -135,7 +133,7 @@ struct GlyphGeometry for (int i=0; i<4; ++i) - normals->push_back(xform.preMult(osg::Vec3(-1, 0.0, 0))); + normals->push_back(osg::Vec3(-1, 0.0, 0)); vertices->push_back(osg::Vec3(-thick, -caseWidth, grounddist + caseHeight)); vertices->push_back(osg::Vec3(thick, -caseWidth, grounddist + caseHeight)); @@ -143,7 +141,7 @@ struct GlyphGeometry vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight)); for (int i=0; i<4; ++i) - normals->push_back(xform.preMult(osg::Vec3(0, 0, 1))); + normals->push_back(osg::Vec3(0, 0, 1)); vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist + caseHeight)); vertices->push_back(osg::Vec3(thick, caseWidth, grounddist + caseHeight)); @@ -151,7 +149,7 @@ struct GlyphGeometry vertices->push_back(osg::Vec3(-thick, caseWidth, grounddist)); for (int i=0; i<4; ++i) - normals->push_back(xform.preMult(osg::Vec3(1, 0.0, 0))); + normals->push_back(osg::Vec3(1, 0.0, 0)); for (int i = 0; i < 3; ++i) { uvs->push_back(osg::Vec2(1, 1)); @@ -160,22 +158,18 @@ struct GlyphGeometry uvs->push_back(osg::Vec2(1, 0)); } + // transform all the newly added vertices and normals by the matrix + for (unsigned int i=last; isize(); ++i) { + (*vertices)[i]= xform.preMult((*vertices)[i]); + (*normals)[i] = xform.preMult((*normals)[i]); + } + quads->setCount(vertices->size()); } }; typedef std::map EffectGeometryMap; -void SGMakeSignFace(EffectGeometryMap& glyphs, const ElementVec& elements, double hpos, const osg::Matrix& xform) -{ - BOOST_FOREACH(element_info* element, elements) { - GlyphGeometry* gg = glyphs[element->state]; - gg->addGlyph(element->glyph, hpos, grounddist, element->abswidth, element->height, xform); - hpos += element->abswidth; - delete element; - } -} - GlyphGeometry* makeGeometry(Effect* eff, osg::Group* group) { GlyphGeometry* gg = new GlyphGeometry; @@ -206,9 +200,67 @@ GlyphGeometry* makeGeometry(Effect* eff, osg::Group* group) } // see $FG_ROOT/Docs/README.scenery -// -osg::Node* -SGMakeSign(SGMaterialLib *matlib, const string& content) + +namespace simgear +{ + +class AirportSignBuilder::AirportSignBuilderPrivate +{ +public: + SGMaterialLib* materials; + EffectGeometryMap geometries; + osg::MatrixTransform* signsGroup; + GlyphGeometry* signCaseGeometry; + + GlyphGeometry* getGeometry(Effect* eff) + { + EffectGeometryMap::iterator it = geometries.find(eff); + if (it != geometries.end()) { + return it->second; + } + + GlyphGeometry* gg = makeGeometry(eff, signsGroup); + geometries[eff] = gg; + return gg; + } + + void makeFace(const ElementVec& elements, double hpos, const osg::Matrix& xform) + { + BOOST_FOREACH(element_info* element, elements) { + GlyphGeometry* gg = getGeometry(element->material->get_effect()); + gg->addGlyph(element->glyph, hpos, grounddist, element->abswidth, element->height, xform); + hpos += element->abswidth; + delete element; + } + } + +}; + +AirportSignBuilder::AirportSignBuilder(SGMaterialLib* mats, const SGGeod& center) : + d(new AirportSignBuilderPrivate) +{ + d->signsGroup = new osg::MatrixTransform; + d->signsGroup->setMatrix(makeZUpFrame(center)); + + assert(mats); + d->materials = mats; + d->signCaseGeometry = d->getGeometry(d->materials->find("signcase")->get_effect()); +} + +osg::Node* AirportSignBuilder::getSignsGroup() +{ + return d->signsGroup; +} + +AirportSignBuilder::~AirportSignBuilder() +{ + EffectGeometryMap::iterator it; + for (it = d->geometries.begin(); it != d->geometries.end(); ++it) { + delete it->second; + } +} + +void AirportSignBuilder::addSign(const SGGeod& pos, double heading, const std::string& content) { double sign_height = 1.0; // meter string newmat = "BlackSign"; @@ -221,12 +273,6 @@ SGMakeSign(SGMaterialLib *matlib, const string& content) bool isBackside = false; int size = -1; char oldtype = 0, newtype = 0; - - EffectGeometryMap geoms; - - osg::Group* object = new osg::Group; - object->setName(content); - SGMaterial *material = 0; // Part I: parse & measure @@ -334,7 +380,7 @@ SGMakeSign(SGMaterialLib *matlib, const string& content) } if (newmat.size()) { - material = matlib->find(newmat); + material = d->materials->find(newmat); newmat.clear(); } @@ -344,14 +390,7 @@ SGMakeSign(SGMaterialLib *matlib, const string& content) continue; } - // in managed mode push frame stop and frame start first - Effect *state = material->get_effect(); - if (geoms[state] == NULL) { - geoms[state] = makeGeometry(state, object); - } - - element_info *e1; - element_info *e2; + // in managed mode push frame stop and frame start first if (!isBackside) { if (newtype && newtype != oldtype) { if (close1) { @@ -362,16 +401,16 @@ SGMakeSign(SGMaterialLib *matlib, const string& content) oldtype = newtype; SGMaterialGlyph *g = material->get_glyph("stop-frame"); if (g) - close1 = new element_info(material, state, g, sign_height, 0); + close1 = new element_info(material, g, sign_height, 0); g = material->get_glyph("start-frame"); if (g) { - e1 = new element_info(material, state, g, sign_height, 0); + element_info* e1 = new element_info(material, g, sign_height, 0); elements1.push_back(e1); total_width1 += e1->glyph->get_width() * e1->scale; } } // now the actually requested glyph (front) - e1 = new element_info(material, state, glyph, sign_height, 0); + element_info* e1 = new element_info(material, glyph, sign_height, 0); elements1.push_back(e1); total_width1 += e1->glyph->get_width() * e1->scale; } else { @@ -384,16 +423,16 @@ SGMakeSign(SGMaterialLib *matlib, const string& content) oldtype = newtype; SGMaterialGlyph *g = material->get_glyph("stop-frame"); if (g) - close2 = new element_info(material, state, g, sign_height, 0); + close2 = new element_info(material, g, sign_height, 0); g = material->get_glyph("start-frame"); if (g) { - e2 = new element_info(material, state, g, sign_height, 0); + element_info* e2 = new element_info(material, g, sign_height, 0); elements2.push_back(e2); total_width2 += e2->glyph->get_width() * e2->scale; } } // now the actually requested glyph (back) - e2 = new element_info(material, state, glyph, sign_height, 0); + element_info* e2 = new element_info(material, glyph, sign_height, 0); elements2.push_back(e2); total_width2 += e2->glyph->get_width() * e2->scale; } @@ -412,39 +451,44 @@ SGMakeSign(SGMaterialLib *matlib, const string& content) close2 = 0; } - // Part II: typeset double boxwidth = std::max(total_width1, total_width2) * 0.5; double hpos = -boxwidth; - SGMaterial *mat = matlib->find("signcase"); - geoms[mat->get_effect()] = makeGeometry(mat->get_effect(), object); + SGMaterial *mat = d->materials->find("signcase"); double coverSize = fabs(total_width1 - total_width2) * 0.5; + element_info* s1 = new element_info(mat, mat->get_glyph("cover"), sign_height, coverSize); + element_info* s2 = new element_info(mat, mat->get_glyph("cover"), sign_height, coverSize); - if (total_width1 < total_width2) { - if (mat) { - element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize); - element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize); - elements1.insert(elements1.begin(), s1); - elements1.push_back(s2); - } - + if (total_width1 < total_width2) { + elements1.insert(elements1.begin(), s1); + elements1.push_back(s2); } else if (total_width2 < total_width1) { - if (mat) { - element_info* s1 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize); - element_info* s2 = new element_info(mat, mat->get_effect(), mat->get_glyph("cover"), sign_height, coverSize); - elements2.insert(elements2.begin(), s1); - elements2.push_back(s2); - } + elements2.insert(elements2.begin(), s1); + elements2.push_back(s2); + } else { + delete s1; + delete s2; } + +// position the sign + const osg::Vec3 Z_AXIS(0, 0, 1); + osg::Matrix m(makeZUpFrame(pos)); + m.preMultRotate(osg::Quat(SGMiscd::deg2rad(heading), Z_AXIS)); + + // apply the inverse of the group transform, so sign vertices + // are relative to the tile center, and hence have a magnitude which + // fits in a float with sufficent precision. + m.postMult(d->signsGroup->getInverseMatrix()); + + d->makeFace(elements1, hpos, m); +// Create back side + osg::Matrix back(m); + back.preMultRotate(osg::Quat(M_PI, Z_AXIS)); + d->makeFace(elements2, hpos, back); + + d->signCaseGeometry->addSignCase(boxwidth, sign_height, m); +} - // Create front side - SGMakeSignFace(geoms, elements1, hpos, osg::Matrix::identity()); +} // of namespace simgear - // Create back side - const osg::Vec3d axis(0, 0, 1); - SGMakeSignFace(geoms, elements2, hpos, osg::Matrix::rotate( M_PI, axis)); - geoms[mat->get_effect()]->addSignCase(boxwidth, sign_height, osg::Matrix::identity()); - - return object; -} diff --git a/simgear/scene/tgdb/apt_signs.hxx b/simgear/scene/tgdb/apt_signs.hxx index 01339401..31334e4a 100644 --- a/simgear/scene/tgdb/apt_signs.hxx +++ b/simgear/scene/tgdb/apt_signs.hxx @@ -24,23 +24,36 @@ #ifndef _SG_APT_SIGNS_HXX #define _SG_APT_SIGNS_HXX - -#ifndef __cplusplus -# error This library requires C++ -#endif - - #include #include +#include // for auto-ptr #include class SGMaterialLib; // forward declaration - +class SGGeod; // Generate a generic sign osg::Node* SGMakeSign( SGMaterialLib *matlib, const std::string& content ); +namespace simgear +{ + +class AirportSignBuilder +{ +public: + AirportSignBuilder(SGMaterialLib* mats, const SGGeod& center); + ~AirportSignBuilder(); + + void addSign(const SGGeod& pos, double heading, const std::string& content); + + osg::Node* getSignsGroup(); +private: + class AirportSignBuilderPrivate; + std::auto_ptr d; +}; + +} // of namespace simgear #endif // _SG_APT_SIGNS_HXX -- 2.39.5