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");
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<osg::Group> group = new osg::Group;
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;
}
// This is considered a real existing file.
// We still apply the search path algorithms for relative files.
osg::ref_ptr<osg::Group> 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())
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;
}
} 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 << "'" );
in >> ::skipeol;
}
+
+ group.addChild(signBuilder.getSignsGroup());
return has_base;
}
// 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;
typedef std::vector<element_info*> 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
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));
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));
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));
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));
uvs->push_back(osg::Vec2(1, 0));
}
+ // transform all the newly added vertices and normals by the matrix
+ for (unsigned int i=last; i<vertices->size(); ++i) {
+ (*vertices)[i]= xform.preMult((*vertices)[i]);
+ (*normals)[i] = xform.preMult((*normals)[i]);
+ }
+
quads->setCount(vertices->size());
}
};
typedef std::map<Effect*, GlyphGeometry*> 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;
}
// 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";
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
}
if (newmat.size()) {
- material = matlib->find(newmat);
+ material = d->materials->find(newmat);
newmat.clear();
}
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) {
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 {
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;
}
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;
-}