X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftgdb%2Fobj.cxx;h=da21d9f16f05f77572b62ed71fae913753693a3f;hb=d4c7e950927b1e19a7a7622a7919f32233a6b7a8;hp=89f19fc15a67aef45958943c6972f6d59df886ff;hpb=cafcecf03ddbdcc2b9bfb57fd60f702bee469b12;p=simgear.git diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index 89f19fc1..da21d9f1 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -49,15 +49,21 @@ #include #include #include -#include -#include +#include #include "SGTexturedTriangleBin.hxx" #include "SGLightBin.hxx" +#include "SGModelBin.hxx" +#include "TreeBin.hxx" #include "SGDirectionalLightBin.hxx" +#include "GroundLightManager.hxx" + +#include "userdata.hxx" #include "pt_lights.hxx" +using namespace simgear; + typedef std::map SGMaterialTriangleMap; typedef std::list SGLightListBin; typedef std::list SGDirectionalLightListBin; @@ -66,12 +72,14 @@ struct SGTileGeometryBin { SGMaterialTriangleMap materialTriangleMap; SGLightBin tileLights; SGLightBin randomTileLights; + TreeBin randomForest; SGDirectionalLightBin runwayLights; SGDirectionalLightBin taxiLights; SGDirectionalLightListBin vasiLights; SGDirectionalLightListBin rabitLights; SGLightListBin odalLights; SGDirectionalLightListBin reilLights; + SGMatModelBin randomModels; static SGVec4f getMaterialLightColor(const SGMaterial* material) @@ -123,7 +131,7 @@ struct SGTileGeometryBin { 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, @@ -392,7 +400,12 @@ struct SGTileGeometryBin { void computeRandomSurfaceLights(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) @@ -407,16 +420,13 @@ struct SGTileGeometryBin { coverage = 10000; } - // generate a repeatable random seed - sg_srandom(unsigned(coverage)); - std::vector randomPoints; i->second.addRandomSurfacePoints(coverage, 3, randomPoints); std::vector::iterator j; for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { - float zombie = sg_random(); + float zombie = mt_rand(&seed); // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0 - float factor = sg_random(); + float factor = mt_rand(&seed); factor *= factor; float bright = 1; @@ -439,202 +449,249 @@ struct SGTileGeometryBin { } } - bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib) + void computeRandomForest(SGMaterialLib* matlib) { - if (!insertPtGeometry(obj, matlib)) - return false; - if (!insertSurfaceGeometry(obj, matlib)) - return false; - return true; - } -}; + SGMaterialTriangleMap::iterator i; + // generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(586)); -class SGTileUpdateCallback : public osg::NodeCallback { -public: - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - assert(dynamic_cast(node)); - assert(dynamic_cast(nv)); + for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { + SGMaterial *mat = matlib->find(i->first); + if (!mat) + continue; - osg::Switch* lightSwitch = static_cast(node); - SGUpdateVisitor* updateVisitor = static_cast(nv); + float coverage = mat->get_tree_coverage(); + if (coverage <= 0) + continue; - // The current sun angle in degree - float sun_angle = updateVisitor->getSunAngleDeg(); + // 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(); - // vasi is always on - lightSwitch->setValue(0, true); - if (sun_angle > 85 || updateVisitor->getVisibility() < 5000) { - // runway and taxi - lightSwitch->setValue(1, true); - lightSwitch->setValue(2, true); - } else { - // runway and taxi - lightSwitch->setValue(1, false); - lightSwitch->setValue(2, false); - } - - // ground lights - if ( sun_angle > 95 ) - lightSwitch->setValue(5, true); - else - lightSwitch->setValue(5, false); - if ( sun_angle > 92 ) - lightSwitch->setValue(4, true); - else - lightSwitch->setValue(4, false); - if ( sun_angle > 89 ) - lightSwitch->setValue(3, true); - else - lightSwitch->setValue(3, false); + std::vector randomPoints; + i->second.addRandomSurfacePoints(coverage, 0, randomPoints); + std::vector::iterator j; + for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { - traverse(node, nv); + // 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); + } + } } -}; -class SGRunwayLightFogUpdateCallback : public osg::StateAttribute::Callback { -public: - virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv) + void computeRandomObjects(SGMaterialLib* matlib) { - assert(dynamic_cast(nv)); - assert(dynamic_cast(sa)); - SGUpdateVisitor* updateVisitor = static_cast(nv); - osg::Fog* fog = static_cast(sa); - fog->setMode(osg::Fog::EXP2); - fog->setColor(updateVisitor->getFogColor().osg()); - fog->setDensity(updateVisitor->getRunwayFogExp2Density()); + 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 randomPoints; + + i->second.addRandomPoints(object->get_coverage_m2(), randomPoints); + std::vector::iterator l; + for (l = randomPoints.begin(); l != randomPoints.end(); ++l) { + randomModels.insert(*l, object, (int)object->get_randomized_range_m(&seed)); + } + } + } + } + } + } } -}; -class SGTaxiLightFogUpdateCallback : public osg::StateAttribute::Callback { -public: - virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv) + bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib) { - assert(dynamic_cast(nv)); - assert(dynamic_cast(sa)); - SGUpdateVisitor* updateVisitor = static_cast(nv); - osg::Fog* fog = static_cast(sa); - fog->setMode(osg::Fog::EXP2); - fog->setColor(updateVisitor->getFogColor().osg()); - fog->setDensity(updateVisitor->getTaxiFogExp2Density()); + if (!insertPtGeometry(obj, matlib)) + return false; + if (!insertSurfaceGeometry(obj, matlib)) + return false; + return true; } }; -class SGGroundLightFogUpdateCallback : public osg::StateAttribute::Callback { -public: - virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv) - { - assert(dynamic_cast(nv)); - assert(dynamic_cast(sa)); - SGUpdateVisitor* updateVisitor = static_cast(nv); - osg::Fog* fog = static_cast(sa); - fog->setMode(osg::Fog::EXP2); - fog->setColor(updateVisitor->getFogColor().osg()); - fog->setDensity(updateVisitor->getGroundLightsFogExp2Density()); - } +typedef std::pair 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 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 obj; - if (!obj.read_bin(path)) + SGBinObject tile; + if (!tile.read_bin(path)) return false; SGTileGeometryBin tileGeometryBin; - if (!tileGeometryBin.insertBinObj(obj, matlib)) + if (!tileGeometryBin.insertBinObj(tile, matlib)) return false; - SGVec3d center = obj.get_gbs_center2(); + SGVec3d center = tile.get_gbs_center2(); SGGeod geodPos = SGGeod::fromCart(center); SGQuatd hlOr = SGQuatd::fromLonLat(geodPos); SGVec3f up = toVec3f(hlOr.backTransform(SGVec3d(0, 0, -1))); + GroundLightManager* lightManager = GroundLightManager::instance(); - osg::Group* vasiLights = new osg::Group; - vasiLights->setCullCallback(new SGPointSpriteLightCullCallback(osg::Vec3(1, 0.0001, 0.000001), 6)); - osg::StateSet* stateSet = vasiLights->getOrCreateStateSet(); - osg::Fog* fog = new osg::Fog; - fog->setUpdateCallback(new SGRunwayLightFogUpdateCallback); - stateSet->setAttribute(fog); - - osg::Group* rwyLights = new osg::Group; - rwyLights->setCullCallback(new SGPointSpriteLightCullCallback); - stateSet = rwyLights->getOrCreateStateSet(); - fog = new osg::Fog; - fog->setUpdateCallback(new SGRunwayLightFogUpdateCallback); - stateSet->setAttribute(fog); - - osg::Group* taxiLights = new osg::Group; - taxiLights->setCullCallback(new SGPointSpriteLightCullCallback); - stateSet = taxiLights->getOrCreateStateSet(); - fog = new osg::Fog; - fog->setUpdateCallback(new SGTaxiLightFogUpdateCallback); - stateSet->setAttribute(fog); - - osg::Group* groundLights0 = new osg::Group; - stateSet = groundLights0->getOrCreateStateSet(); - fog = new osg::Fog; - fog->setUpdateCallback(new SGGroundLightFogUpdateCallback); - stateSet->setAttribute(fog); - - osg::Group* groundLights1 = new osg::Group; - stateSet = groundLights1->getOrCreateStateSet(); - fog = new osg::Fog; - fog->setUpdateCallback(new SGGroundLightFogUpdateCallback); - stateSet->setAttribute(fog); - - osg::Group* groundLights2 = new osg::Group; - stateSet = groundLights2->getOrCreateStateSet(); - fog = new osg::Fog; - fog->setUpdateCallback(new SGGroundLightFogUpdateCallback); - stateSet->setAttribute(fog); - - osg::Switch* lightSwitch = new osg::Switch; - lightSwitch->setUpdateCallback(new SGTileUpdateCallback); - lightSwitch->addChild(vasiLights, true); - lightSwitch->addChild(rwyLights, true); - lightSwitch->addChild(taxiLights, true); - lightSwitch->addChild(groundLights0, true); - lightSwitch->addChild(groundLights1, true); - lightSwitch->addChild(groundLights2, true); - - osg::Group* lightGroup = new SGOffsetTransform(0.94); - lightGroup->addChild(lightSwitch); - - osg::LOD* lightLOD = new osg::LOD; - lightLOD->addChild(lightGroup, 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); - + osg::ref_ptr lightGroup = new SGOffsetTransform(0.94); + osg::ref_ptr randomObjects; + osg::ref_ptr 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 (tileGeometryBin.randomModels.getNumModels() > 0) { + // Generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(123)); + + std::vector 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 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)); + } + RandomObjectsQuadtree quadtree((GetModelLODCoord(world2Tile)), + (AddModelLOD())); + quadtree.buildQuadTree(models.begin(), models.end()); + randomObjects = quadtree.getRoot(); + 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) { // FIXME: ugly, has a side effect tileGeometryBin.computeRandomSurfaceLights(matlib); - osg::Geode* geode = new osg::Geode; - groundLights0->addChild(geode); - geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.tileLights)); - geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 4, -0.3f)); - - geode = new osg::Geode; - groundLights1->addChild(geode); - geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 2, -0.15f)); - - geode = new osg::Geode; - groundLights2->addChild(geode); - geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights)); + if (tileGeometryBin.tileLights.getNumLights() > 0 + || tileGeometryBin.randomTileLights.getNumLights() > 0) { + osg::Group* groundLights0 = new osg::Group; + groundLights0->setStateSet(lightManager->getGroundLightStateSet()); + groundLights0->setNodeMask(GROUNDLIGHTS0_BIT); + osg::Geode* geode = new osg::Geode; + geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.tileLights)); + geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 4, -0.3f)); + groundLights0->addChild(geode); + lightGroup->addChild(groundLights0); + } + if (tileGeometryBin.randomTileLights.getNumLights() > 0) { + osg::Group* groundLights1 = new osg::Group; + groundLights1->setStateSet(lightManager->getGroundLightStateSet()); + groundLights1->setNodeMask(GROUNDLIGHTS1_BIT); + osg::Group* groundLights2 = new osg::Group; + groundLights2->setStateSet(lightManager->getGroundLightStateSet()); + groundLights2->setNodeMask(GROUNDLIGHTS2_BIT); + osg::Geode* geode = new osg::Geode; + geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 2, -0.15f)); + groundLights1->addChild(geode); + lightGroup->addChild(groundLights1); + geode = new osg::Geode; + geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights)); + groundLights2->addChild(geode); + lightGroup->addChild(groundLights2); + } } - { + 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) @@ -643,18 +700,32 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool mat = matlib->find("RWY_WHITE_LIGHTS"); if (mat) white = mat->get_light_color(); - osg::Geode* geode; - geode = new osg::Geode; - vasiLights->addChild(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)); } - - geode = new osg::Geode; - rwyLights->addChild(geode); - geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.runwayLights)); + vasiGeode->setCullCallback(new SGPointSpriteLightCullCallback(osg::Vec3(1, 0.0001, 0.000001), 6)); + vasiGeode->setStateSet(lightManager->getRunwayLightStateSet()); + lightGroup->addChild(vasiGeode); + } + + if (tileGeometryBin.runwayLights.getNumLights() > 0 + || !tileGeometryBin.rabitLights.empty() + || !tileGeometryBin.reilLights.empty() + || !tileGeometryBin.odalLights.empty()) { + osg::Group* rwyLights = new osg::Group; + rwyLights->setCullCallback(new SGPointSpriteLightCullCallback); + rwyLights->setStateSet(lightManager->getRunwayLightStateSet()); + rwyLights->setNodeMask(RUNWAYLIGHTS_BIT); + if (tileGeometryBin.runwayLights.getNumLights() != 0) { + osg::Geode* geode = new osg::Geode; + geode->addDrawable(SGLightFactory::getLights(tileGeometryBin + .runwayLights)); + rwyLights->addChild(geode); + } + SGDirectionalLightListBin::const_iterator i; for (i = tileGeometryBin.rabitLights.begin(); i != tileGeometryBin.rabitLights.end(); ++i) { rwyLights->addChild(SGLightFactory::getSequenced(*i)); @@ -663,16 +734,23 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool i != tileGeometryBin.reilLights.end(); ++i) { rwyLights->addChild(SGLightFactory::getSequenced(*i)); } - SGLightListBin::const_iterator j; for (j = tileGeometryBin.odalLights.begin(); j != tileGeometryBin.odalLights.end(); ++j) { rwyLights->addChild(SGLightFactory::getOdal(*j)); } + lightGroup->addChild(rwyLights); + } - geode = new osg::Geode; - taxiLights->addChild(geode); + if (tileGeometryBin.taxiLights.getNumLights() > 0) { + osg::Group* taxiLights = new osg::Group; + taxiLights->setCullCallback(new SGPointSpriteLightCullCallback); + taxiLights->setStateSet(lightManager->getTaxiLightStateSet()); + taxiLights->setNodeMask(RUNWAYLIGHTS_BIT); + osg::Geode* geode = new osg::Geode; geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.taxiLights)); + taxiLights->addChild(geode); + lightGroup->addChild(taxiLights); } // The toplevel transform for that tile. @@ -680,7 +758,27 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool transform->setName(path); transform->setMatrix(osg::Matrix::translate(center.osg())); transform->addChild(terrainGroup); - transform->addChild(lightLOD); - + if (lightGroup->getNumChildren() > 0) { + osg::LOD* lightLOD = new osg::LOD; + lightLOD->addChild(lightGroup.get(), 0, 30000); + // VASI is always on, so doesn't use light bits. + lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT); + transform->addChild(lightLOD); + } + + 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; + + 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); + } + return transform; }