build signs for an STG into single geometry.

This commit is contained in:
James Turner
2012-05-08 13:29:34 +01:00
parent 71443d1c9a
commit a1f74729ab
4 changed files with 162 additions and 91 deletions

View File

@@ -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<osg::Group> 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<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())
@@ -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;
}

View File

@@ -24,6 +24,8 @@
#include <osgDB/ReaderWriter>
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;
};
}

View File

@@ -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<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
@@ -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; 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;
@@ -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;
}
// Create front side
SGMakeSignFace(geoms, elements1, hpos, osg::Matrix::identity());
// 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;
// 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);
}
} // of namespace simgear

View File

@@ -24,23 +24,36 @@
#ifndef _SG_APT_SIGNS_HXX
#define _SG_APT_SIGNS_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/compiler.h>
#include <string>
#include <memory> // for auto-ptr
#include <osg/Node>
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<AirportSignBuilderPrivate> d;
};
} // of namespace simgear
#endif // _SG_APT_SIGNS_HXX