#include <simgear/scene/util/SGUpdateVisitor.hxx>
#include <simgear/scene/util/SGNodeMasks.hxx>
#include <simgear/scene/util/QuadTreeBuilder.hxx>
-#include <simgear/threads/SGThread.hxx>
-#include <simgear/threads/SGGuard.hxx>
#include "SGTexturedTriangleBin.hxx"
#include "SGLightBin.hxx"
#include "SGModelBin.hxx"
+#include "TreeBin.hxx"
#include "SGDirectionalLightBin.hxx"
#include "GroundLightManager.hxx"
SGMaterialTriangleMap materialTriangleMap;
SGLightBin tileLights;
SGLightBin randomTileLights;
+ TreeBin randomForest;
SGDirectionalLightBin runwayLights;
SGDirectionalLightBin taxiLights;
SGDirectionalLightListBin vasiLights;
SGLightListBin odalLights;
SGDirectionalLightListBin reilLights;
SGMatModelBin randomModels;
-
+
static SGVec4f
getMaterialLightColor(const SGMaterial* material)
{
std::string materialName = obj.get_pt_materials()[grp];
SGMaterial* material = matlib->find(materialName);
SGVec4f color = getMaterialLightColor(material);
-
+
if (3 <= materialName.size() && materialName.substr(0, 3) != "RWY") {
// Just plain lights. Not something for the runway.
addPointGeometry(tileLights, obj.get_wgs84_nodes(), color,
void computeRandomSurfaceLights(SGMaterialLib* matlib)
{
- SGMaterialTriangleMap::const_iterator i;
+ SGMaterialTriangleMap::iterator i;
// generate a repeatable random seed
- mt* seed = new mt;
- mt_init(seed, unsigned(123));
+ mt seed;
+ mt_init(&seed, unsigned(123));
for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
SGMaterial *mat = matlib->find(i->first);
i->second.addRandomSurfacePoints(coverage, 3, randomPoints);
std::vector<SGVec3f>::iterator j;
for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
- float zombie = mt_rand(seed);
+ float zombie = mt_rand(&seed);
// factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
- float factor = mt_rand(seed);
+ float factor = mt_rand(&seed);
factor *= factor;
float bright = 1;
}
}
}
-
+
+ void computeRandomForest(SGMaterialLib* matlib)
+ {
+ SGMaterialTriangleMap::iterator i;
+
+ // generate a repeatable random seed
+ mt seed;
+ mt_init(&seed, unsigned(586));
+
+ for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+ SGMaterial *mat = matlib->find(i->first);
+ if (!mat)
+ continue;
+
+ float coverage = mat->get_tree_coverage();
+ if (coverage <= 0)
+ continue;
+
+ // Attributes that don't vary by tree
+ randomForest.texture = mat->get_tree_texture();
+ randomForest.range = mat->get_tree_range();
+ randomForest.width = mat->get_tree_width();
+ randomForest.height = mat->get_tree_height();
+ randomForest.texture_varieties = mat->get_tree_varieties();
+
+ std::vector<SGVec3f> randomPoints;
+ i->second.addRandomSurfacePoints(coverage, 0, randomPoints);
+ std::vector<SGVec3f>::iterator j;
+ for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
+
+ // Apply a random scaling factor and texture index.
+ float scale = (mt_rand(&seed) + mt_rand(&seed)) / 2.0f + 0.5f;
+ int v = (int) (mt_rand(&seed) * mat->get_tree_varieties());
+ if (v == mat->get_tree_varieties()) v--;
+ randomForest.insert(*j, v, scale);
+ }
+ }
+ }
+
void computeRandomObjects(SGMaterialLib* matlib)
{
- SGMaterialTriangleMap::const_iterator i;
+ SGMaterialTriangleMap::iterator i;
+
+ // generate a repeatable random seed
+ mt seed;
+ mt_init(&seed, unsigned(123));
+
for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
SGMaterial *mat = matlib->find(i->first);
if (!mat)
continue;
-
+
int group_count = mat->get_object_group_count();
-
+
if (group_count > 0)
- {
+ {
for (int j = 0; j < group_count; j++)
- {
+ {
SGMatModelGroup *object_group = mat->get_object_group(j);
int nObjects = object_group->get_object_count();
-
+
if (nObjects > 0)
{
// For each of the random models in the group, determine an appropriate
// number of random placements and insert them.
for (int k = 0; k < nObjects; k++) {
SGMatModel * object = object_group->get_object(k);
-
+
std::vector<SGVec3f> randomPoints;
i->second.addRandomPoints(object->get_coverage_m2(), randomPoints);
std::vector<SGVec3f>::iterator l;
for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
- randomModels.insert(*l, object);
+ randomModels.insert(*l, object, (int)object->get_randomized_range_m(&seed));
}
}
}
}
};
+typedef std::pair<osg::Node*, int> ModelLOD;
+struct MakeQuadLeaf {
+ osg::LOD* operator() () const { return new osg::LOD; }
+};
+struct AddModelLOD {
+ void operator() (osg::LOD* leaf, ModelLOD& mlod) const
+ {
+ leaf->addChild(mlod.first, 0, mlod.second);
+ }
+};
+struct GetModelLODCoord {
+ GetModelLODCoord(const osg::Matrix& transform) : _transform(transform) {}
+ GetModelLODCoord(const GetModelLODCoord& rhs) : _transform(rhs._transform)
+ {}
+ osg::Vec3 operator() (const ModelLOD& mlod) const
+ {
+ return mlod.first->getBound().center() * _transform;
+ }
+ osg::Matrix _transform;
+};
+
+typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
+ GetModelLODCoord> RandomObjectsQuadtree;
+
osg::Node*
-SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects)
+SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects, bool use_random_vegetation)
{
SGBinObject tile;
if (!tile.read_bin(path))
SGGeod geodPos = SGGeod::fromCart(center);
SGQuatd hlOr = SGQuatd::fromLonLat(geodPos);
SGVec3f up = toVec3f(hlOr.backTransform(SGVec3d(0, 0, -1)));
- osg::Matrix world2Tile(-hlOr.osg());
GroundLightManager* lightManager = GroundLightManager::instance();
osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
osg::ref_ptr<osg::Group> randomObjects;
+ osg::ref_ptr<osg::Group> randomForest;
osg::Group* terrainGroup = new osg::Group;
osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
if (node)
terrainGroup->addChild(node);
+
+ if (use_random_objects || use_random_vegetation) {
+
+ // Simple matrix for used for flipping models that have been oriented
+ // with the center of the tile but upside down.
+ static const osg::Matrix flip(1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, -1, 0,
+ 0, 0, 0, 1);
+ // Determine an rotation matrix for the models to place them
+ // perpendicular to the earth's surface. We use the same matrix,
+ // based on the centre of the tile, as the small angular differences
+ // between different points on the tile aren't worth worrying about
+ // for random objects. We also need to flip the orientation 180 degrees
+ osg::Matrix mAtt = flip * osg::Matrix::rotate(hlOr.osg());
+ // The inverse goes from world coordinates to Z up tile coordinates.
+ osg::Matrix world2Tile(osg::Matrix(hlOr.osg().conj()) * flip);
+
+ if (use_random_objects) {
+ tileGeometryBin.computeRandomObjects(matlib);
- if (use_random_objects) {
- tileGeometryBin.computeRandomObjects(matlib);
-
- if (tileGeometryBin.randomModels.getNumModels() > 0) {
- // Generate a repeatable random seed
- mt* seed = new mt;
- mt_init(seed, unsigned(123));
-
- // Determine an rotation matrix for the models to place them
- // perpendicular to the earth's surface. We use the same matrix,
- // based on the centre of the tile, as the small angular differences
- // between different points on the tile aren't worth worrying about
- // for random objects. We also need to flip the orientation 180 degrees
- static const osg::Matrix flip(1, 0, 0, 0,
- 0, -1, 0, 0,
- 0, 0, -1, 0,
- 0, 0, 0, 1);
- osg::Matrix mAtt = flip * osg::Matrix::rotate(hlOr.osg());
- std::vector<osg::ref_ptr<osg::Node> > models;
-
- for (unsigned int i = 0; i < tileGeometryBin.randomModels.getNumModels(); i++) {
- SGMatModelBin::MatModel obj = tileGeometryBin.randomModels.getMatModel(i);
- osg::Node* node = sgGetRandomModel(obj.model);
+ if (tileGeometryBin.randomModels.getNumModels() > 0) {
+ // Generate a repeatable random seed
+ mt seed;
+ mt_init(&seed, unsigned(123));
+
+ std::vector<ModelLOD> models;
+ for (unsigned int i = 0;
+ i < tileGeometryBin.randomModels.getNumModels(); i++) {
+ SGMatModelBin::MatModel obj
+ = tileGeometryBin.randomModels.getMatModel(i);
+ osg::Node* node = sgGetRandomModel(obj.model);
- // Create a matrix to place the object in the correct location, and then
- // apply the rotation matrix created above, with an additional random
- // heading rotation if appropriate.
- osg::Matrix mPos = osg::Matrix::translate(obj.position.osg());
- osg::MatrixTransform* position;
-
- if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
- // Rotate the object around the z axis.
- double hdg = mt_rand(seed) * M_PI * 2;
- osg::Matrix rot(cos(hdg), -sin(hdg), 0, 0,
- sin(hdg), cos(hdg), 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1);
- position = new osg::MatrixTransform(rot * mAtt * mPos);
- } else {
- position = new osg::MatrixTransform(mAtt * mPos);
+ // Create a matrix to place the object in the correct
+ // location, and then apply the rotation matrix created
+ // above, with an additional random heading rotation if appropriate.
+ osg::Matrix transformMat(mAtt);
+ transformMat.postMult(osg::Matrix::translate(obj.position.osg()));
+ if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
+ // Rotate the object around the z axis.
+ double hdg = mt_rand(&seed) * M_PI * 2;
+ transformMat.preMult(osg::Matrix::rotate(hdg,
+ osg::Vec3d(0.0, 0.0, 1.0)));
+ }
+ osg::MatrixTransform* position =
+ new osg::MatrixTransform(transformMat);
+ position->addChild(node);
+ models.push_back(ModelLOD(position, obj.lod));
}
-
- position->addChild(node);
- models.push_back(position);
- // Add to the leaf of the quadtree based on object location.
+ RandomObjectsQuadtree quadtree((GetModelLODCoord(world2Tile)),
+ (AddModelLOD()));
+ quadtree.buildQuadTree(models.begin(), models.end());
+ randomObjects = quadtree.getRoot();
+ randomObjects->setName("random objects");
}
- randomObjects = QuadTreeBuilder::makeQuadTree(models, world2Tile);
- randomObjects->setName("random objects");
}
+
+ if (use_random_vegetation) {
+ // Now add some random forest.
+ tileGeometryBin.computeRandomForest(matlib);
+
+ if (tileGeometryBin.randomForest.getNumTrees() > 0) {
+ randomForest = createForest(tileGeometryBin.randomForest, mAtt);
+ randomForest->setName("random trees");
+ }
+ }
}
if (calc_lights) {
}
if (!tileGeometryBin.vasiLights.empty()) {
+ osg::Geode* vasiGeode = new osg::Geode;
SGVec4f red(1, 0, 0, 1);
SGMaterial* mat = matlib->find("RWY_RED_LIGHTS");
if (mat)
if (mat)
white = mat->get_light_color();
- osg::Geode* geode = new osg::Geode;
SGDirectionalLightListBin::const_iterator i;
for (i = tileGeometryBin.vasiLights.begin();
i != tileGeometryBin.vasiLights.end(); ++i) {
- geode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
+ vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
}
- osg::Group* vasiLights = new osg::Group;
- vasiLights->setCullCallback(new SGPointSpriteLightCullCallback(osg::Vec3(1, 0.0001, 0.000001), 6));
- vasiLights->setStateSet(lightManager->getRunwayLightStateSet());
- vasiLights->addChild(geode);
- lightGroup->addChild(vasiLights);
+ vasiGeode->setCullCallback(new SGPointSpriteLightCullCallback(osg::Vec3(1, 0.0001, 0.000001), 6));
+ vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
+ lightGroup->addChild(vasiGeode);
}
if (tileGeometryBin.runwayLights.getNumLights() > 0
if (lightGroup->getNumChildren() > 0) {
osg::LOD* lightLOD = new osg::LOD;
lightLOD->addChild(lightGroup.get(), 0, 30000);
- unsigned nodeMask = ~0u;
- nodeMask &= ~SG_NODEMASK_CASTSHADOW_BIT;
- nodeMask &= ~SG_NODEMASK_RECIEVESHADOW_BIT;
- nodeMask &= ~SG_NODEMASK_PICK_BIT;
- nodeMask &= ~SG_NODEMASK_TERRAIN_BIT;
- lightLOD->setNodeMask(nodeMask);
+ // VASI is always on, so doesn't use light bits.
+ lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT);
transform->addChild(lightLOD);
}
- if (randomObjects.valid()) {
+ if (randomObjects.valid() || randomForest.valid()) {
// Add a LoD node, so we don't try to display anything when the tile center
// is more than 20km away.
osg::LOD* objectLOD = new osg::LOD;
- objectLOD->addChild(randomObjects.get(), 0, 20000);
- unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT;
+
+ if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
+ if (randomForest.valid()) objectLOD->addChild(randomForest.get(), 0, 20000);
+
+ unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
objectLOD->setNodeMask(nodeMask);
transform->addChild(objectLOD);
}