From: Peter Sadrozinski Date: Sat, 10 Jan 2015 12:58:21 +0000 (-0500) Subject: memory reduced tile loading. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=148640be3450e34416c77111103f637c451362c8;p=simgear.git memory reduced tile loading. - do not save the TileGeometryBin and matcach in the randomObjectCallback - recreate matcache, and get TileGeometry from scenegraph - split obj.cxx into three distinct files - loadBTG, load surface geometry, and load tile details - includes fix for sceneries that have missing materials --- diff --git a/simgear/scene/material/EffectGeode.hxx b/simgear/scene/material/EffectGeode.hxx index 34b87e3a..e4b024b7 100644 --- a/simgear/scene/material/EffectGeode.hxx +++ b/simgear/scene/material/EffectGeode.hxx @@ -23,6 +23,7 @@ #include #include "Effect.hxx" +#include "mat.hxx" namespace simgear { @@ -69,6 +70,8 @@ class EffectGeode : public osg::Geode META_Node(simgear,EffectGeode); Effect* getEffect() const { return _effect.get(); } void setEffect(Effect* effect); + SGMaterial* getMaterial() const { return _material; } + void setMaterial(SGMaterial* mat) { _material = mat; } virtual void resizeGLObjectBuffers(unsigned int maxSize); virtual void releaseGLObjects(osg::State* = 0) const; @@ -83,6 +86,7 @@ class EffectGeode : public osg::Geode void runGenerators(osg::Geometry *geometry); private: osg::ref_ptr _effect; + SGMaterial* _material; }; } #endif diff --git a/simgear/scene/material/mat.cxx b/simgear/scene/material/mat.cxx index 546b807e..a39c71c1 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -441,7 +441,7 @@ Effect* SGMaterial::get_effect(int i) return _status[i].effect.get(); } -Effect* SGMaterial::get_effect(const SGTexturedTriangleBin& triangleBin) +Effect* SGMaterial::get_one_effect(int texIndex) { SGGuard g(_lock); if (_status.empty()) { @@ -449,7 +449,7 @@ Effect* SGMaterial::get_effect(const SGTexturedTriangleBin& triangleBin) return 0; } - int i = triangleBin.getTextureIndex() % _status.size(); + int i = texIndex % _status.size(); return get_effect(i); } @@ -460,7 +460,7 @@ Effect* SGMaterial::get_effect() } -osg::Texture2D* SGMaterial::get_object_mask(const SGTexturedTriangleBin& triangleBin) +osg::Texture2D* SGMaterial::get_one_object_mask(int texIndex) { if (_status.empty()) { SG_LOG( SG_GENERAL, SG_WARN, "No mask available."); @@ -469,7 +469,7 @@ osg::Texture2D* SGMaterial::get_object_mask(const SGTexturedTriangleBin& triangl // Note that the object mask is closely linked to the texture/effect // so we index based on the texture index, - unsigned int i = triangleBin.getTextureIndex() % _status.size(); + unsigned int i = texIndex % _status.size(); if (i < _masks.size()) { return _masks[i].get(); } else { diff --git a/simgear/scene/material/mat.hxx b/simgear/scene/material/mat.hxx index 5a37b756..ef420d94 100644 --- a/simgear/scene/material/mat.hxx +++ b/simgear/scene/material/mat.hxx @@ -119,13 +119,13 @@ public: /** * Get the textured state. */ - simgear::Effect* get_effect(const SGTexturedTriangleBin& triangleBin); + simgear::Effect* get_one_effect(int texIndex); simgear::Effect* get_effect(); /** * Get the textured state. */ - osg::Texture2D* get_object_mask(const SGTexturedTriangleBin& triangleBin); + osg::Texture2D* get_one_object_mask(int texIndex); /** diff --git a/simgear/scene/tgdb/CMakeLists.txt b/simgear/scene/tgdb/CMakeLists.txt index cdf6a3de..590f2270 100644 --- a/simgear/scene/tgdb/CMakeLists.txt +++ b/simgear/scene/tgdb/CMakeLists.txt @@ -8,9 +8,12 @@ set(HEADERS SGDirectionalLightBin.hxx SGLightBin.hxx SGModelBin.hxx + SGNodeTriangles.hxx SGOceanTile.hxx SGReaderWriterBTG.hxx SGTexturedTriangleBin.hxx + SGTileDetailsCallback.hxx + SGTileGeometryBin.hxx SGTriangleBin.hxx SGVasiDrawable.hxx SGVertexArrayBin.hxx diff --git a/simgear/scene/tgdb/SGNodeTriangles.hxx b/simgear/scene/tgdb/SGNodeTriangles.hxx new file mode 100644 index 00000000..9ec37658 --- /dev/null +++ b/simgear/scene/tgdb/SGNodeTriangles.hxx @@ -0,0 +1,482 @@ +// future API - just run through once to convert from OSG to SG +// then we can use these triangle lists for random +// trees/lights/buildings/objects +struct SGTexturedTriangle +{ +public: + std::vector vertices; + std::vector texcoords; +}; + +struct SGBorderContour +{ +public: + SGVec3d start; + SGVec3d end; +}; + +class SGTriangleInfo +{ +public: + SGTriangleInfo( const SGVec3d& center ) { + gbs_center = center; + mt_init(&seed, 123); + } + + // API used to build the Info by the visitor + void addGeometry( osg::Geometry* g ) { + geometries.push_back(g); + } + + void setMaterial( SGMaterial* m ) { + mat = m; + } + + SGMaterial* getMaterial( void ) const { + return mat; + } + + // API used to get a specific texture or effect from a material. Materials can have + // multiple textures - use the floor of the x coordinate of the first vertes to select it. + // This will be constant, and give the same result each time to select one effect/texture per drawable. + int getTextureIndex( void ) const { + int texInfo = 0; + const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); + if ( vertices ) { + const osg::Vec3 *v0 = &vertices->operator[](0); + texInfo = floor(v0->x()); + } + return texInfo; + } + + // new API - TODO + void getTriangles( std::vector& tris ) + { + const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); + const osg::Vec2Array* texcoords = dynamic_cast(geometries[0]->getTexCoordArray(0)); + + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); + if ( numPrimitiveSets > 0 ) { + const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); + unsigned int numIndices = ps->getNumIndices(); + + for ( unsigned int i=2; ioperator[](ps->index(i-2))) ); + tri.vertices.push_back( toSG(vertices->operator[](ps->index(i-1))) ); + tri.vertices.push_back( toSG(vertices->operator[](ps->index(i-0))) ); + + tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-2))) ); + tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-1))) ); + tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-0))) ); + } + } + } + + void getBorderContours( std::vector& border ) + { + // each structure contains a list of target indexes and a count + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); + if ( numPrimitiveSets > 0 ) { + const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); + + const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); + unsigned int numTriangles = ps->getNumIndices()/3; + + // use a map for fast lookup map the segment as a 64 bit int + std::map segCounter; + uint32_t idx1, idx2; + uint64_t key; + + for ( unsigned int i=0; iindex(i+0) < ps->index(i+1) ) { + idx1 = ps->index(i+0); + idx2 = ps->index(i+1); + } else { + idx1 = ps->index(i+1); + idx2 = ps->index(i+0); + } + + key=( (uint64_t)idx1<<32) | (uint64_t)idx2; + SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] ); + segCounter[key]++; + SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] ); + + // second seg + if ( ps->index(i+1) < ps->index(i+2) ) { + idx1 = ps->index(i+1); + idx2 = ps->index(i+2); + } else { + idx1 = ps->index(i+2); + idx2 = ps->index(i+1); + } + + key=( (uint64_t)idx1<<32) | (uint64_t)idx2; + SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] ); + segCounter[key]++; + SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] ); + + // third seg + if ( ps->index(i+2) < ps->index(i+0) ) { + idx1 = ps->index(i+2); + idx2 = ps->index(i+0); + } else { + idx1 = ps->index(i+0); + idx2 = ps->index(i+2); + } + + key=( (uint64_t)idx1<<32) | (uint64_t)idx2; + SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] ); + segCounter[key]++; + SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] ); + } + + // return all segments with count = 1 ( border ) + std::map::iterator segIt = segCounter.begin(); + while ( segIt != segCounter.end() ) { + if ( segIt->second == 1 ) { + SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << segIt->first << std::dec << " count is " << segIt->second ); + + unsigned int iStart = segIt->first >> 32; + unsigned int iEnd = segIt->first & 0x00000000FFFFFFFF; + + SGBorderContour bc; + + bc.start = toVec3d(toSG(vertices->operator[](iStart))); + bc.end = toVec3d(toSG(vertices->operator[](iEnd))); + border.push_back( bc ); + } + segIt++; + } + +#if 0 + // debug out - requires GDAL + // + // + // + SGGeod geodPos = SGGeod::fromCart(gbs_center); + SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180); + + for ( unsigned int i=0; iget_names()[0], "border" ); + } +#endif + } + } + + // Random buildings API - get num triangles, then get a triangle at index + unsigned int getNumTriangles( void ) const { + unsigned int num_triangles = 0; + + if ( !geometries.empty() ) { + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); + if ( numPrimitiveSets > 0 ) { + const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); + unsigned int numIndices = ps->getNumIndices(); + num_triangles = numIndices/3; + } + } + + return num_triangles; + } + + void getTriangle(unsigned int i, std::vector& triVerts, std::vector& triTCs) const { + const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); + const osg::Vec2Array* texcoords = dynamic_cast(geometries[0]->getTexCoordArray(0)); + + if ( !geometries.empty() ) { + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); + if ( numPrimitiveSets > 0 ) { + const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); + int idxStart = i*3; + + triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+0))) ); + triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+1))) ); + triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+2))) ); + + triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+0))) ); + triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+1))) ); + triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+2))) ); + } + } + } + + // random lights and trees - just get a list of points on where to add the light / tree + // TODO move this out - and handle in the random light / tree code + // just use generic triangle API. + void addRandomSurfacePoints(float coverage, float offset, + osg::Texture2D* object_mask, + std::vector& points) + { + if ( !geometries.empty() ) { + const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); + const osg::Vec2Array* texcoords = dynamic_cast(geometries[0]->getTexCoordArray(0)); + + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); + if ( numPrimitiveSets > 0 ) { + const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); + unsigned int numIndices = ps->getNumIndices(); + + for ( unsigned int i=2; ioperator[](ps->index(i-2))); + SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1))); + SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0))); + + SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2))); + SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1))); + SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0))); + + SGVec3f normal = cross(v1 - v0, v2 - v0); + + // Compute the area + float area = 0.5f*length(normal); + if (area <= SGLimitsf::min()) + continue; + + // For partial units of area, use a zombie door method to + // create the proper random chance of a light being created + // for this triangle + float unit = area + mt_rand(&seed)*coverage; + + SGVec3f offsetVector = offset*normalize(normal); + // generate a light point for each unit of area + + while ( coverage < unit ) { + float a = mt_rand(&seed); + float b = mt_rand(&seed); + + if ( a + b > 1 ) { + a = 1 - a; + b = 1 - b; + } + float c = 1 - a - b; + SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2; + + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // red channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); + unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).r()) { + points.push_back(randomPoint); + } + } else { + // No object mask, so simply place the object + points.push_back(randomPoint); + } + unit -= coverage; + } + } + } + } + } + + void addRandomTreePoints(float wood_coverage, + osg::Texture2D* object_mask, + float vegetation_density, + float cos_max_density_angle, + float cos_zero_density_angle, + std::vector& points) + { + if ( !geometries.empty() ) { + const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); + const osg::Vec2Array* texcoords = dynamic_cast(geometries[0]->getTexCoordArray(0)); + + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); + if ( numPrimitiveSets > 0 ) { + const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); + unsigned int numIndices = ps->getNumIndices(); + + for ( unsigned int i=2; ioperator[](ps->index(i-2))); + SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1))); + SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0))); + + SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2))); + SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1))); + SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0))); + + SGVec3f normal = cross(v1 - v0, v2 - v0); + + // Ensure the slope isn't too steep by checking the + // cos of the angle between the slope normal and the + // vertical (conveniently the z-component of the normalized + // normal) and values passed in. + float alpha = normalize(normal).z(); + float slope_density = 1.0; + + if (alpha < cos_zero_density_angle) + continue; // Too steep for any vegetation + + if (alpha < cos_max_density_angle) { + slope_density = + (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle); + } + + // Compute the area + float area = 0.5f*length(normal); + if (area <= SGLimitsf::min()) + continue; + + // Determine the number of trees, taking into account vegetation + // density (which is linear) and the slope density factor. + // Use a zombie door method to create the proper random chance + // of a tree being created for partial values. + int woodcount = (int) (vegetation_density * vegetation_density * + slope_density * + area / wood_coverage + mt_rand(&seed)); + + for (int j = 0; j < woodcount; j++) { + float a = mt_rand(&seed); + float b = mt_rand(&seed); + + if ( a + b > 1.0f ) { + a = 1.0f - a; + b = 1.0f - b; + } + + float c = 1.0f - a - b; + + SGVec3f randomPoint = a*v0 + b*v1 + c*v2; + + if (object_mask != NULL) { + SGVec2f texCoord = a*t0 + b*t1 + c*t2; + + // Check this random point against the object mask + // green (for trees) channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); + unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).g()) { + // The red channel contains the rotation for this object + points.push_back(randomPoint); + } + } else { + points.push_back(randomPoint); + } + } + } + } + } + } + +#if 0 + // debug : this will save the tile as a shapefile that can be viewed in QGIS. + // NOTE: this is really slow.... + // remember - we need to de-rotate the tile, then translate back to gbs_center. + void dumpBorder() { + //dump the first triangle only of the first geometry, for now... + SG_LOG(SG_TERRAIN, SG_ALERT, "effect geode has " << geometries.size() << " geometries" ); + + const osg::Vec3Array* vertices = dynamic_cast(geometries[0]->getVertexArray()); + if ( vertices ) { + SG_LOG(SG_TERRAIN, SG_ALERT, " geometry has " << vertices->getNumElements() << " vertices" ); + } + + if ( !geometries.empty() ) { + int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); + SG_LOG(SG_TERRAIN, SG_ALERT, " geometry has " << numPrimitiveSets << " primitive sets" ); + + if ( numPrimitiveSets > 0 ) { + const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); + unsigned int numIndices = ps->getNumIndices(); + + // create the same quat we used to rotate here + // - use backTransform to go back to original node location + SGGeod geodPos = SGGeod::fromCart(gbs_center); + SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180); + + SG_LOG(SG_TERRAIN, SG_ALERT, " primitive set has has " << numIndices << " indices" ); + for ( unsigned int i=2; i= 3 ) { + unsigned int v0i = ps->index(i-2); + unsigned int v1i = ps->index(i-1); + unsigned int v2i = ps->index(i-0); + + const osg::Vec3 *v0 = &vertices->operator[](v0i); + const osg::Vec3 *v1 = &vertices->operator[](v1i); + const osg::Vec3 *v2 = &vertices->operator[](v2i); + + // de-rotate and translate : todo - create a paralell vertex list so we just do this + // once per vertex, not for every triangle's use of the vertex + SGVec3d vec0 = hlOr.backTransform( toVec3d(toSG(*v0))) + gbs_center; + SGVec3d vec1 = hlOr.backTransform( toVec3d(toSG(*v1))) + gbs_center; + SGVec3d vec2 = hlOr.backTransform( toVec3d(toSG(*v2))) + gbs_center; + + // convert from cartesian to Geodetic, and save as a list of Geods for output + std::vector triangle; + triangle.push_back( SGGeod::fromCart(vec0) ); + triangle.push_back( SGGeod::fromCart(vec1) ); + triangle.push_back( SGGeod::fromCart(vec2) ); + + SGShapefile::FromGeodList( triangle, true, "./triangles", mat->get_names()[0], "tri" ); + } + } + } + + } + } +#endif + +private: + mt seed; + SGMaterial* mat; + SGVec3d gbs_center; + std::vector geometries; + std::vector polygon_border; // TODO +}; + +// This visitor will generate an SGTriangleInfo. +// currently, it looks like it could save multiple lists, which could be the case +// if multiple osg::geods are found with osg::Geometry. +// But right now, we store a single PrimitiveSet under a single EffectGeod. +// so the traversal should only find a single EffectGeod - building a single SGTriangleInfo +class GetNodeTriangles : public osg::NodeVisitor +{ +public: + GetNodeTriangles(const SGVec3d& c, std::vector* nt) : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), center(c), nodeTris(nt) {} + + // This method gets called for every node in the scene + // graph. Check each node to see if it has user + // out target. If so, save the node's address. + virtual void apply( osg::Node& node ) + { + EffectGeode* eg = dynamic_cast(&node); + if ( eg ) { + // get the material from the user info + SGTriangleInfo triInfo( center ); + triInfo.setMaterial( eg->getMaterial() ); + + // let's find the drawables for this node + int numDrawables = eg->getNumDrawables(); + for ( int i=0; igetDrawable(i)->asGeometry() ); + } + + nodeTris->push_back( triInfo ); + } + + // Keep traversing the rest of the scene graph. + traverse( node ); + } + +protected: + SGVec3d center; + std::vector* nodeTris; +}; diff --git a/simgear/scene/tgdb/SGTileDetailsCallback.hxx b/simgear/scene/tgdb/SGTileDetailsCallback.hxx new file mode 100644 index 00000000..d7ffb5b1 --- /dev/null +++ b/simgear/scene/tgdb/SGTileDetailsCallback.hxx @@ -0,0 +1,1140 @@ +// obj.cxx -- routines to handle loading scenery and building the plib +// scene graph. +// +// Written by Curtis Olson, started October 1997. +// +// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "SGNodeTriangles.hxx" +#include "GroundLightManager.hxx" +#include "SGLightBin.hxx" +#include "SGDirectionalLightBin.hxx" +#include "SGModelBin.hxx" +#include "SGBuildingBin.hxx" +#include "TreeBin.hxx" + +#include "pt_lights.hxx" + + +typedef std::list SGLightListBin; +typedef std::list SGDirectionalLightListBin; + +#define SG_SIMPLIFIER_RATIO (0.001) +#define SG_SIMPLIFIER_MAX_LENGTH (1000.0) +#define SG_SIMPLIFIER_MAX_ERROR (2000.0) +#define SG_OBJECT_RANGE (9000.0) +#define SG_TILE_RADIUS (14000.0) +#define SG_TILE_MIN_EXPIRY (180.0) + +using namespace simgear; + +// QuadTreeBuilder is used by Random Objects Generator +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() {} + GetModelLODCoord(const GetModelLODCoord& rhs) + {} + osg::Vec3 operator() (const ModelLOD& mlod) const + { + return mlod.first->getBound().center(); + } +}; +typedef QuadTreeBuilder RandomObjectsQuadtree; + + +// needs constructor +static unsigned int num_tdcb = 0; +class SGTileDetailsCallback : public OptionsReadFileCallback { +public: + SGTileDetailsCallback() + { + num_tdcb++; + } + + virtual ~SGTileDetailsCallback() + { + num_tdcb--; + SG_LOG( SG_GENERAL, SG_INFO, "SGTileDetailsCallback::~SGTileDetailsCallback() num cbs left " << num_tdcb ); + } + + virtual osgDB::ReaderWriter::ReadResult readNode( + const std::string&, const osgDB::Options*) + { + SGMaterialLibPtr matlib; + osg::ref_ptr matcache; + + osg::ref_ptr group = new osg::Group; + group->setDataVariance(osg::Object::STATIC); + + // generate textured triangle list + std::vector matTris; + GetNodeTriangles nodeTris(_gbs_center, &matTris); + _rootNode->accept( nodeTris ); + + // build matcache + matlib = _options->getMaterialLib(); + if (matlib) { + SGGeod geodPos = SGGeod::fromCart(_gbs_center); + matcache = matlib->generateMatCache(geodPos); + } + +#if 0 + // TEST : See if we can regenerate landclass shapes from node + for ( unsigned int i=0; iaddChild(node); + } + + osg::LOD* lightLOD = generateLightingTileObjects(matTris, matcache); + if (lightLOD) { + group->addChild(lightLOD); + } + + osg::LOD* objectLOD = generateRandomTileObjects(matTris, matcache); + if (objectLOD) { + group->addChild(objectLOD); + } + + return group.release(); + } + + static SGVec4f getMaterialLightColor(const SGMaterial* material) + { + if (!material) { + return SGVec4f(1, 1, 1, 0.8); + } + + return material->get_light_color(); + } + + static void + addPointGeometry(SGLightBin& lights, + const std::vector& vertices, + const SGVec4f& color, + const int_list& pts_v) + { + for (unsigned i = 0; i < pts_v.size(); ++i) + lights.insert(toVec3f(vertices[pts_v[i]]), color); + } + + static void + addPointGeometry(SGDirectionalLightBin& lights, + const std::vector& vertices, + const std::vector& normals, + const SGVec4f& color, + const int_list& pts_v, + const int_list& pts_n) + { + // If the normal indices match the vertex indices, use seperate + // normal indices. Else reuse the vertex indices for the normals. + if (pts_v.size() == pts_n.size()) { + for (unsigned i = 0; i < pts_v.size(); ++i) + lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_n[i]], color); + } else { + for (unsigned i = 0; i < pts_v.size(); ++i) + lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_v[i]], color); + } + } + + bool insertPtGeometry(const SGBinObject& obj, SGMaterialCache* matcache) + { + if (obj.get_pts_v().size() != obj.get_pts_n().size()) { + SG_LOG(SG_TERRAIN, SG_ALERT, + "Group list sizes for points do not match!"); + return false; + } + + for (unsigned grp = 0; grp < obj.get_pts_v().size(); ++grp) { + std::string materialName = obj.get_pt_materials()[grp]; + SGMaterial* material = matcache->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, + obj.get_pts_v()[grp]); + } else if (materialName == "RWY_BLUE_TAXIWAY_LIGHTS" + || materialName == "RWY_GREEN_TAXIWAY_LIGHTS") { + addPointGeometry(taxiLights, obj.get_wgs84_nodes(), obj.get_normals(), + color, obj.get_pts_v()[grp], obj.get_pts_n()[grp]); + } else if (materialName == "RWY_VASI_LIGHTS") { + vasiLights.push_back(SGDirectionalLightBin()); + addPointGeometry(vasiLights.back(), obj.get_wgs84_nodes(), + obj.get_normals(), color, obj.get_pts_v()[grp], + obj.get_pts_n()[grp]); + } else if (materialName == "RWY_SEQUENCED_LIGHTS") { + rabitLights.push_back(SGDirectionalLightBin()); + addPointGeometry(rabitLights.back(), obj.get_wgs84_nodes(), + obj.get_normals(), color, obj.get_pts_v()[grp], + obj.get_pts_n()[grp]); + } else if (materialName == "RWY_ODALS_LIGHTS") { + odalLights.push_back(SGLightBin()); + addPointGeometry(odalLights.back(), obj.get_wgs84_nodes(), + color, obj.get_pts_v()[grp]); + } else if (materialName == "RWY_YELLOW_PULSE_LIGHTS") { + holdshortLights.push_back(SGDirectionalLightBin()); + addPointGeometry(holdshortLights.back(), obj.get_wgs84_nodes(), + obj.get_normals(), color, obj.get_pts_v()[grp], + obj.get_pts_n()[grp]); + } else if (materialName == "RWY_GUARD_LIGHTS") { + guardLights.push_back(SGDirectionalLightBin()); + addPointGeometry(guardLights.back(), obj.get_wgs84_nodes(), + obj.get_normals(), color, obj.get_pts_v()[grp], + obj.get_pts_n()[grp]); + } else if (materialName == "RWY_REIL_LIGHTS") { + reilLights.push_back(SGDirectionalLightBin()); + addPointGeometry(reilLights.back(), obj.get_wgs84_nodes(), + obj.get_normals(), color, obj.get_pts_v()[grp], + obj.get_pts_n()[grp]); + } else { + // what is left must be runway lights + addPointGeometry(runwayLights, obj.get_wgs84_nodes(), + obj.get_normals(), color, obj.get_pts_v()[grp], + obj.get_pts_n()[grp]); + } + } + + return true; + } + + + + // Load terrain if required + // todo - this is the same code as when we load a btg from the .STG - can we combine? + osg::Node* loadTerrain() + { + if (! _loadterrain) + return NULL; + + SGBinObject tile; + if (!tile.read_bin(_path)) + return NULL; + + SGMaterialLibPtr matlib; + SGMaterialCache* matcache = 0; + bool useVBOs = false; + bool simplifyNear = false; + double ratio = SG_SIMPLIFIER_RATIO; + double maxLength = SG_SIMPLIFIER_MAX_LENGTH; + double maxError = SG_SIMPLIFIER_MAX_ERROR; + + if (_options) { + matlib = _options->getMaterialLib(); + useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON"); + SGPropertyNode* propertyNode = _options->getPropertyNode().get(); + simplifyNear = propertyNode->getBoolValue("/sim/rendering/terrain/simplifier/enabled-near", simplifyNear); + ratio = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/ratio", ratio); + maxLength = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-length", maxLength); + maxError = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-error", maxError); + } + + // PSADRO TODO : we can do this in terragear + // - why not add a bitmask of flags to the btg so we can precompute this? + // and only do it if it hasn't been done already + SGVec3d center = tile.get_gbs_center(); + SGGeod geodPos = SGGeod::fromCart(center); + SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180); + + // Generate a materials cache + if (matlib) { + matcache = matlib->generateMatCache(geodPos); + } + + // rotate the tiles so that the bounding boxes get nearly axis aligned. + // this will help the collision tree's bounding boxes a bit ... + std::vector nodes = tile.get_wgs84_nodes(); + for (unsigned i = 0; i < nodes.size(); ++i) { + nodes[i] = hlOr.transform(nodes[i]); + } + tile.set_wgs84_nodes(nodes); + + SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]); + std::vector normals = tile.get_normals(); + for (unsigned i = 0; i < normals.size(); ++i) { + normals[i] = hlOrf.transform(normals[i]); + } + tile.set_normals(normals); + + osg::ref_ptr tileGeometryBin = new SGTileGeometryBin; + + if (!tileGeometryBin->insertSurfaceGeometry(tile, matcache)) { + return NULL; + } + + osg::Node* node = tileGeometryBin->getSurfaceGeometry(matcache, useVBOs); + if (node && simplifyNear) { + osgUtil::Simplifier simplifier(ratio, maxError, maxLength); + node->accept(simplifier); + } + + return node; + } + + float min_dist_to_seg_squared( const SGVec3f p, const SGVec3d& a, const SGVec3d& b ) + { + const float l2 = distSqr(a, b); + SGVec3d pd = toVec3d( p ); + if (l2 == 0.0) { + return distSqr(pd, a); // if a == b, just return distance to A + } + + // Consider the line extending the segment, parameterized as a + t (b - a). + // We find projection of pt onto the line. + // It falls where t = [(p-a) . (b-a)] / |b-a|^2 + const float t = dot(pd-a, b-a) / l2; + + if (t < 0.0) { + return distSqr(pd, a); + } else if (t > 1.0) { + return distSqr(pd, b); + } else { + const SGVec3d proj = a + t * (b-a); + return distSqr(pd, proj); + } + } + + float min_dist_from_borders( SGVec3f p, const std::vector& bsegs ) + { + // calc min dist to each line + // calc distance squared to keep this as fast as we can + // first, we must be able to project the point onto the segment + std::vector distances; + for ( unsigned int b=0; b& matTris, + float building_density, + bool use_random_objects, + bool use_random_buildings, + bool useVBOs, + SGMatModelBin& randomModels, + SGBuildingBinList& randomBuildings ) + { + unsigned int m; + + // Only compute the random objects if we haven't already done so + if (_tileRandomObjectsComputed) { + return; + } + _tileRandomObjectsComputed = true; + + // generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(123)); + + for ( m=0; mget_one_object_mask(matTris[m].getTextureIndex()); + + int group_count = mat->get_object_group_count(); + float building_coverage = mat->get_building_coverage(); + float cos_zero_density_angle = mat->get_cos_object_zero_density_slope_angle(); + float cos_max_density_angle = mat->get_cos_object_max_density_slope_angle(); + + if (building_coverage == 0) + continue; + + SGBuildingBin* bin = NULL; + + if (building_coverage > 0) { + bin = new SGBuildingBin(mat, useVBOs); + randomBuildings.push_back(bin); + } + + unsigned num = matTris[m].getNumTriangles(); + int random_dropped = 0; + int mask_dropped = 0; + int building_dropped = 0; + int triangle_dropped = 0; + + // get the polygon border segments +// std::vector borderSegs; +// matTris[m].getBorderContours( borderSegs ); + + for (unsigned i = 0; i < num; ++i) { + std::vector triVerts; + std::vector triTCs; + matTris[m].getTriangle(i, triVerts, triTCs); + + SGVec3f vorigin = triVerts[0]; + SGVec3f v0 = triVerts[1] - vorigin; + SGVec3f v1 = triVerts[2] - vorigin; + SGVec2f torigin = triTCs[0]; + SGVec2f t0 = triTCs[1] - torigin; + SGVec2f t1 = triTCs[2] - torigin; + SGVec3f normal = cross(v0, v1); + + // Ensure the slope isn't too steep by checking the + // cos of the angle between the slope normal and the + // vertical (conveniently the z-component of the normalized + // normal) and values passed in. + float cos = normalize(normal).z(); + float slope_density = 1.0; + if (cos < cos_zero_density_angle) continue; // Too steep for any objects + if (cos < cos_max_density_angle) { + slope_density = + (cos - cos_zero_density_angle) / + (cos_max_density_angle - cos_zero_density_angle); + } + + // Containers to hold the random buildings and objects generated + // for this triangle for collision detection purposes. + std::vector< std::pair< SGVec3f, float> > triangleObjectsList; + std::vector< std::pair< SGVec3f, float> > triangleBuildingList; + + // Compute the area : todo - we only want to stop if the area of the POLY + // is too small + // so we need to know area of each poly.... + float area = 0.5f*length(normal); + if (area <= SGLimitsf::min()) + continue; + + // Generate any random objects + if (use_random_objects && (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) continue; + + // 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); + + // Determine the number of objecst to place, taking into account + // the slope density factor. + double n = slope_density * area / object->get_coverage_m2(); + + // Use the zombie door method to determine fractional object placement. + n = n + mt_rand(&seed); + + // place an object each unit of area + while ( n > 1.0 ) { + n -= 1.0; + + float a = mt_rand(&seed); + float b = mt_rand(&seed); + if ( a + b > 1 ) { + a = 1 - a; + b = 1 - b; + } + + SGVec3f randomPoint = vorigin + a*v0 + b*v1; + float rotation = static_cast(mt_rand(&seed)); + + // Check that the point is sufficiently far from + // the edge of the triangle by measuring the distance + // from the three lines that make up the triangle. + float spacing = object->get_spacing_m(); + + SGVec3f p = randomPoint - vorigin; +#if 1 + float edges[] = { + length(cross(p , p - v0)) / length(v0), + length(cross(p - v0, p - v1)) / length(v1 - v0), + length(cross(p - v1, p )) / length(v1) }; + float edge_dist = *std::min_element(edges, edges + 3); +#else + float edge_dist = min_dist_from_borders( randomPoint, borderSegs ); +#endif + if (edge_dist < spacing) { + continue; + } + + if (object_mask != NULL) { + SGVec2f texCoord = torigin + a*t0 + b*t1; + + // Check this random point against the object mask + // blue (for buildings) channel. + osg::Image* img = object_mask->getImage(); + unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); + unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); + + if (mt_rand(&seed) > img->getColor(x, y).b()) { + // Failed object mask check + continue; + } + + rotation = img->getColor(x,y).r(); + } + + bool close = false; + + // Check it isn't too close to any other random objects in the triangle + std::vector >::iterator l; + for (l = triangleObjectsList.begin(); l != triangleObjectsList.end(); ++l) { + float min_dist2 = (l->second + object->get_spacing_m()) * + (l->second + object->get_spacing_m()); + + if (distSqr(l->first, randomPoint) < min_dist2) { + close = true; + continue; + } + } + + if (!close) { + triangleObjectsList.push_back(std::make_pair(randomPoint, object->get_spacing_m())); + randomModels.insert(randomPoint, + object, + (int)object->get_randomized_range_m(&seed), + rotation); + } + } + } + } + } + + // Random objects now generated. Now generate the random buildings (if any); + if (use_random_buildings && (building_coverage > 0) && (building_density > 0)) { + + // Calculate the number of buildings, taking into account building density (which is linear) + // and the slope density factor. + double num = building_density * building_density * slope_density * area / building_coverage; + + // For partial units of area, use a zombie door method to + // create the proper random chance of an object being created + // for this triangle. + num = num + mt_rand(&seed); + + if (num < 1.0f) { + continue; + } + + // Cosine of the angle between the two vectors. + float cosine = (dot(v0, v1) / (length(v0) * length(v1))); + + // Determine a grid spacing in each vector such that the correct + // coverage will result. + float stepv0 = (sqrtf(building_coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine); + float stepv1 = (sqrtf(building_coverage) / building_density) / length(v1); + + stepv0 = std::min(stepv0, 1.0f); + stepv1 = std::min(stepv1, 1.0f); + + // Start at a random point. a will be immediately incremented below. + float a = -mt_rand(&seed) * stepv0; + float b = mt_rand(&seed) * stepv1; + + // Place an object each unit of area + while (num > 1.0) { + num -= 1.0; + + // Set the next location to place a building + a += stepv0; + + if ((a + b) > 1.0f) { + // Reached the end of the scan-line on v0. Reset and increment + // scan-line on v1 + a = mt_rand(&seed) * stepv0; + b += stepv1; + } + + if (b > 1.0f) { + // In a degenerate case of a single point, we might be outside the + // scanline. Note that we need to still ensure that a+b < 1. + b = mt_rand(&seed) * stepv1 * (1.0f - a); + } + + if ((a + b) > 1.0f ) { + // Truly degenerate case - simply choose a random point guaranteed + // to fulfil the constraing of a+b < 1. + a = mt_rand(&seed); + b = mt_rand(&seed) * (1.0f - a); + } + + SGVec3f randomPoint = vorigin + a*v0 + b*v1; + float rotation = mt_rand(&seed); + + if (object_mask != NULL) { + SGVec2f texCoord = torigin + a*t0 + b*t1; + osg::Image* img = object_mask->getImage(); + int x = (int) (img->s() * texCoord.x()) % img->s(); + int y = (int) (img->t() * texCoord.y()) % img->t(); + + // In some degenerate cases x or y can be < 1, in which case the mod operand fails + while (x < 0) x += img->s(); + while (y < 0) y += img->t(); + + if (mt_rand(&seed) < img->getColor(x, y).b()) { + // Object passes mask. Rotation is taken from the red channel + rotation = img->getColor(x,y).r(); + } else { + // Fails mask test - try again. + mask_dropped++; + continue; + } + } + + // Check building isn't too close to the triangle edge. + float type_roll = mt_rand(&seed); + SGBuildingBin::BuildingType buildingtype = bin->getBuildingType(type_roll); + float radius = bin->getBuildingMaxRadius(buildingtype); + + // Determine the actual center of the building, by shifting from the + // center of the front face to the true center. + osg::Matrix rotationMat = osg::Matrix::rotate(- rotation * M_PI * 2, + osg::Vec3f(0.0, 0.0, 1.0)); + SGVec3f buildingCenter = randomPoint + toSG(osg::Vec3f(-0.5 * bin->getBuildingMaxDepth(buildingtype), 0.0, 0.0) * rotationMat); + + SGVec3f p = buildingCenter - vorigin; +#if 1 + float edges[] = { length(cross(p , p - v0)) / length(v0), + length(cross(p - v0, p - v1)) / length(v1 - v0), + length(cross(p - v1, p )) / length(v1) }; + float edge_dist = *std::min_element(edges, edges + 3); +#else + float edge_dist = min_dist_from_borders(randomPoint, borderSegs); +#endif + if (edge_dist < radius) { + triangle_dropped++; + continue; + } + + // Check building isn't too close to random objects and other buildings. + bool close = false; + std::vector >::iterator iter; + + for (iter = triangleBuildingList.begin(); iter != triangleBuildingList.end(); ++iter) { + float min_dist = iter->second + radius; + if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) { + close = true; + continue; + } + } + + if (close) { + building_dropped++; + continue; + } + + for (iter = triangleObjectsList.begin(); iter != triangleObjectsList.end(); ++iter) { + float min_dist = iter->second + radius; + if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) { + close = true; + continue; + } + } + + if (close) { + random_dropped++; + continue; + } + + std::pair pt = std::make_pair(buildingCenter, radius); + triangleBuildingList.push_back(pt); + bin->insert(randomPoint, rotation, buildingtype); + } + } + + triangleObjectsList.clear(); + triangleBuildingList.clear(); + } + + SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << ((bin) ? bin->getNumBuildings() : 0)); + SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to mask: " << mask_dropped); + SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to random object: " << random_dropped); + SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to other buildings: " << building_dropped); + } + } + + void computeRandomForest(std::vector& matTris, float vegetation_density, SGTreeBinList& randomForest) + { + unsigned int i; + + // generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(586)); + + for ( i=0; iget_wood_coverage(); + if ((wood_coverage <= 0) || (vegetation_density <= 0)) + continue; + + // Attributes that don't vary by tree but do vary by material + bool found = false; + TreeBin* bin = NULL; + + BOOST_FOREACH(bin, randomForest) + { + if ((bin->texture == mat->get_tree_texture() ) && + (bin->texture_varieties == mat->get_tree_varieties()) && + (bin->range == mat->get_tree_range() ) && + (bin->width == mat->get_tree_width() ) && + (bin->height == mat->get_tree_height() ) ) { + found = true; + break; + } + } + + if (!found) { + bin = new TreeBin(); + bin->texture = mat->get_tree_texture(); + SG_LOG(SG_INPUT, SG_DEBUG, "Tree texture " << bin->texture); + bin->range = mat->get_tree_range(); + bin->width = mat->get_tree_width(); + bin->height = mat->get_tree_height(); + bin->texture_varieties = mat->get_tree_varieties(); + randomForest.push_back(bin); + } + + std::vector randomPoints; + matTris[i].addRandomTreePoints(wood_coverage, + mat->get_one_object_mask(matTris[i].getTextureIndex()), + vegetation_density, + mat->get_cos_tree_max_density_slope_angle(), + mat->get_cos_tree_zero_density_slope_angle(), + randomPoints); + + std::vector::iterator k; + for (k = randomPoints.begin(); k != randomPoints.end(); ++k) { + bin->insert(*k); + } + } + } + + void computeRandomSurfaceLights(std::vector& matTris, SGLightBin& randomTileLights ) + { + unsigned int i; + + // Only compute the lights if we haven't already done so. + // For example, the light data will still exist if the + // PagedLOD expires. + if ( _randomSurfaceLightsComputed ) + { + return; + } + _randomSurfaceLightsComputed = true; + + // generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(123)); + + for ( i=0; iget_light_coverage(); + if (coverage <= 0) + continue; + if (coverage < 10000.0) { + SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is " + << coverage << ", pushing up to 10000"); + coverage = 10000; + } + + int texIndex = matTris[i].getTextureIndex(); + + std::vector randomPoints; + matTris[i].addRandomSurfacePoints(coverage, 3, mat->get_one_object_mask(texIndex), randomPoints); + std::vector::iterator j; + for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { + float zombie = mt_rand(&seed); + // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0 + float factor = mt_rand(&seed); + factor *= factor; + + float bright = 1; + SGVec4f color; + if ( zombie > 0.5 ) { + // 50% chance of yellowish + color = SGVec4f(0.9f, 0.9f, 0.3f, bright - factor * 0.2f); + } else if (zombie > 0.15f) { + // 35% chance of whitish + color = SGVec4f(0.9, 0.9f, 0.8f, bright - factor * 0.2f); + } else if (zombie > 0.05f) { + // 10% chance of orangish + color = SGVec4f(0.9f, 0.6f, 0.2f, bright - factor * 0.2f); + } else { + // 5% chance of redish + color = SGVec4f(0.9f, 0.2f, 0.2f, bright - factor * 0.2f); + } + randomTileLights.insert(*j, color); + } + } + } + + // Generate all the lighting objects for the tile. + osg::LOD* generateLightingTileObjects(std::vector& matTris, const SGMaterialCache* matcache) + { + SGLightBin randomTileLights; + computeRandomSurfaceLights(matTris, randomTileLights); + + GroundLightManager* lightManager = GroundLightManager::instance(); + osg::ref_ptr lightGroup = new SGOffsetTransform(0.94); + SGVec3f up(0, 0, 1); + + if (tileLights.getNumLights() > 0 || randomTileLights.getNumLights() > 0) { + osg::ref_ptr groundLights0 = new osg::Group; + + groundLights0->setStateSet(lightManager->getGroundLightStateSet()); + groundLights0->setNodeMask(GROUNDLIGHTS0_BIT); + + osg::ref_ptr geode = new EffectGeode; + osg::ref_ptr lightEffect = getLightEffect(24, osg::Vec3(1, 0.001, 0.00001), 1, 8, false, _options); + + geode->setEffect(lightEffect); + geode->addDrawable(SGLightFactory::getLights(tileLights)); + geode->addDrawable(SGLightFactory::getLights(randomTileLights, 4, -0.3f)); + groundLights0->addChild(geode); + lightGroup->addChild(groundLights0); + } + + if (randomTileLights.getNumLights() > 0) { + osg::ref_ptr groundLights1 = new osg::Group; + groundLights1->setStateSet(lightManager->getGroundLightStateSet()); + groundLights1->setNodeMask(GROUNDLIGHTS1_BIT); + + osg::ref_ptr groundLights2 = new osg::Group; + groundLights2->setStateSet(lightManager->getGroundLightStateSet()); + groundLights2->setNodeMask(GROUNDLIGHTS2_BIT); + + osg::ref_ptr geode1 = new EffectGeode; + + osg::ref_ptr lightEffect = getLightEffect(24, osg::Vec3(1, 0.001, 0.00001), 1, 8, false, _options); + geode1->setEffect(lightEffect); + geode1->addDrawable(SGLightFactory::getLights(randomTileLights, 2, -0.15f)); + groundLights1->addChild(geode1); + lightGroup->addChild(groundLights1); + + osg::ref_ptr geode2 = new EffectGeode; + + geode2->setEffect(lightEffect); + geode2->addDrawable(SGLightFactory::getLights(randomTileLights)); + groundLights2->addChild(geode2); + lightGroup->addChild(groundLights2); + } + + if (vasiLights.empty()) { + EffectGeode* vasiGeode = new EffectGeode; + Effect* vasiEffect = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, true, _options); + vasiGeode->setEffect(vasiEffect); + SGVec4f red(1, 0, 0, 1); + SGMaterial* mat = 0; + if (matcache) + mat = matcache->find("RWY_RED_LIGHTS"); + if (mat) { + red = mat->get_light_color(); + } + + SGVec4f white(1, 1, 1, 1); + mat = 0; + if (matcache) + mat = matcache->find("RWY_WHITE_LIGHTS"); + if (mat) { + white = mat->get_light_color(); + } + SGDirectionalLightListBin::const_iterator i; + for (i = vasiLights.begin(); + i != vasiLights.end(); ++i) { + osg::Drawable* vasiDraw = SGLightFactory::getVasi(up, *i, red, white); + vasiGeode->addDrawable( vasiDraw ); + } + osg::StateSet* ss = lightManager->getRunwayLightStateSet(); + vasiGeode->setStateSet( ss ); + lightGroup->addChild(vasiGeode); + } + + Effect* runwayEffect = 0; + if (runwayLights.getNumLights() > 0 + || !rabitLights.empty() + || !reilLights.empty() + || !odalLights.empty() + || taxiLights.getNumLights() > 0) { + + runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true, _options); + } + + if (runwayLights.getNumLights() > 0 + || !rabitLights.empty() + || !reilLights.empty() + || !odalLights.empty() + || !holdshortLights.empty() + || !guardLights.empty()) { + osg::Group* rwyLights = new osg::Group; + + osg::StateSet* ss = lightManager->getRunwayLightStateSet(); + rwyLights->setStateSet(ss); + rwyLights->setNodeMask(RUNWAYLIGHTS_BIT); + + if (runwayLights.getNumLights() != 0) { + EffectGeode* geode = new EffectGeode; + geode->setEffect(runwayEffect); + + osg::Drawable* rldraw = SGLightFactory::getLights(runwayLights); + geode->addDrawable( rldraw ); + + rwyLights->addChild(geode); + } + SGDirectionalLightListBin::const_iterator i; + for (i = rabitLights.begin(); + i != rabitLights.end(); ++i) { + osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options); + rwyLights->addChild( seqNode ); + } + for (i = reilLights.begin(); + i != reilLights.end(); ++i) { + osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options); + rwyLights->addChild(seqNode); + } + for (i = holdshortLights.begin(); + i != holdshortLights.end(); ++i) { + osg::Node* seqNode = SGLightFactory::getHoldShort(*i, _options); + rwyLights->addChild(seqNode); + } + for (i = guardLights.begin(); + i != guardLights.end(); ++i) { + osg::Node* seqNode = SGLightFactory::getGuard(*i, _options); + rwyLights->addChild(seqNode); + } + SGLightListBin::const_iterator j; + for (j = odalLights.begin(); + j != odalLights.end(); ++j) { + osg::Node* seqNode = SGLightFactory::getOdal(*j, _options); + rwyLights->addChild(seqNode); + } + lightGroup->addChild(rwyLights); + } + + if (taxiLights.getNumLights() > 0) { + osg::Group* taxiLightsGroup = new osg::Group; + taxiLightsGroup->setStateSet(lightManager->getTaxiLightStateSet()); + taxiLightsGroup->setNodeMask(RUNWAYLIGHTS_BIT); + EffectGeode* geode = new EffectGeode; + geode->setEffect(runwayEffect); + geode->addDrawable(SGLightFactory::getLights(taxiLights)); + taxiLightsGroup->addChild(geode); + lightGroup->addChild(taxiLightsGroup); + } + + osg::LOD* lightLOD = NULL; + + if (lightGroup->getNumChildren() > 0) { + lightLOD = new osg::LOD; + lightLOD->addChild(lightGroup.get(), 0, 60000); + // VASI is always on, so doesn't use light bits. + lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT | PERMANENTLIGHT_BIT); + } + + return lightLOD; + } + + // Generate all the random forest, objects and buildings for the tile + osg::LOD* generateRandomTileObjects(std::vector& matTris, const SGMaterialCache* matcache) + { + SGMaterialLibPtr matlib; + bool use_random_objects = false; + bool use_random_vegetation = false; + bool use_random_buildings = false; + float vegetation_density = 1.0f; + float building_density = 1.0f; + bool useVBOs = false; + + osg::ref_ptr randomObjects; + osg::ref_ptr forestNode; + osg::ref_ptr buildingNode; + + if (_options) { + matlib = _options->getMaterialLib(); + SGPropertyNode* propertyNode = _options->getPropertyNode().get(); + if (propertyNode) { + use_random_objects + = propertyNode->getBoolValue("/sim/rendering/random-objects", + use_random_objects); + use_random_vegetation + = propertyNode->getBoolValue("/sim/rendering/random-vegetation", + use_random_vegetation); + vegetation_density + = propertyNode->getFloatValue("/sim/rendering/vegetation-density", + vegetation_density); + use_random_buildings + = propertyNode->getBoolValue("/sim/rendering/random-buildings", + use_random_buildings); + building_density + = propertyNode->getFloatValue("/sim/rendering/building-density", + building_density); + } + + useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON"); + } + + SGMatModelBin randomModels; + + SGBuildingBinList randomBuildings; + + if (matlib && (use_random_objects || use_random_buildings)) { + computeRandomObjectsAndBuildings( matTris, + building_density, + use_random_objects, + use_random_buildings, + useVBOs, + randomModels, + randomBuildings + ); + } + + if (randomModels.getNumModels() > 0) { + // Generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(123)); + + std::vector models; + for (unsigned int i = 0; i < randomModels.getNumModels(); i++) { + SGMatModelBin::MatModel obj = randomModels.getMatModel(i); + + SGPropertyNode* root = _options->getPropertyNode()->getRootNode(); + osg::Node* node = obj.model->get_random_model(root, &seed); + + // Create a matrix to place the object in the correct + // location, and then apply the rotation matrix created + // above, with an additional random (or taken from + // the object mask) heading rotation if appropriate. + osg::Matrix transformMat; + transformMat = osg::Matrix::translate(toOsg(obj.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; + transformMat.preMult(osg::Matrix::rotate(hdg, + osg::Vec3d(0.0, 0.0, 1.0))); + } + + if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) { + // Rotate the object around the z axis. + double hdg = - obj.rotation * 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->setName("positionRandomModel"); + position->addChild(node); + models.push_back(ModelLOD(position, obj.lod)); + } + RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD())); + quadtree.buildQuadTree(models.begin(), models.end()); + randomObjects = quadtree.getRoot(); + randomObjects->setName("Random objects"); + } + + if (!randomBuildings.empty()) { + buildingNode = createRandomBuildings(randomBuildings, osg::Matrix::identity(), _options); + buildingNode->setName("Random buildings"); + randomBuildings.clear(); + } + + if (use_random_vegetation && matlib) { + // Now add some random forest. + SGTreeBinList randomForest; + computeRandomForest(matTris, vegetation_density, randomForest); + + if (!randomForest.empty()) { + forestNode = createForest(randomForest, osg::Matrix::identity(),_options); + forestNode->setName("Random trees"); + } + } + + osg::LOD* objectLOD = NULL; + + if (randomObjects.valid() || forestNode.valid() || buildingNode.valid()) { + objectLOD = new osg::LOD; + + if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000); + if (forestNode.valid()) objectLOD->addChild(forestNode.get(), 0, 20000); + if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000); + + unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT; + objectLOD->setNodeMask(nodeMask); + } + + return objectLOD; + } + + /// The original options to use for this bunch of models + osg::ref_ptr _options; + string _path; + bool _loadterrain; + osg::ref_ptr _rootNode; + SGVec3d _gbs_center; + bool _randomSurfaceLightsComputed; + bool _tileRandomObjectsComputed; + + // most of these are just point and color arrays - extracted from the + // .BTG PointGeometry at tile load time. + // It shouldn't be too much to keep this in memory even if we don't use it. + SGLightBin tileLights; + SGDirectionalLightBin runwayLights; + SGDirectionalLightBin taxiLights; + SGDirectionalLightListBin vasiLights; + SGDirectionalLightListBin rabitLights; + SGLightListBin odalLights; + SGDirectionalLightListBin holdshortLights; + SGDirectionalLightListBin guardLights; + SGDirectionalLightListBin reilLights; +}; \ No newline at end of file diff --git a/simgear/scene/tgdb/SGTileGeometryBin.hxx b/simgear/scene/tgdb/SGTileGeometryBin.hxx new file mode 100644 index 00000000..409ded6c --- /dev/null +++ b/simgear/scene/tgdb/SGTileGeometryBin.hxx @@ -0,0 +1,304 @@ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "obj.hxx" + +#include +#include +#include + +#include "SGTexturedTriangleBin.hxx" + +using namespace simgear; + +typedef std::map SGMaterialTriangleMap; + +// Class handling the initial BTG loading : should probably be in its own file +// it is very closely coupled with SGTexturedTriangleBin.hxx +// it was used to load fans, strips, and triangles. +// WS2.0 no longer uses fans or strips, but people still use ws1.0, so we need +// to keep this functionality. +class SGTileGeometryBin : public osg::Referenced { +public: + SGMaterialTriangleMap materialTriangleMap; + + SGTileGeometryBin() {} + + static SGVec2f + getTexCoord(const std::vector& texCoords, const int_list& tc, + const SGVec2f& tcScale, unsigned i) + { + if (tc.empty()) + return tcScale; + else if (tc.size() == 1) + return mult(texCoords[tc[0]], tcScale); + else + return mult(texCoords[tc[i]], tcScale); + } + + SGVec2f getTexCoordScale(const std::string& name, SGMaterialCache* matcache) + { + if (!matcache) + return SGVec2f(1, 1); + SGMaterial* material = matcache->find(name); + if (!material) + return SGVec2f(1, 1); + + return material->get_tex_coord_scale(); + } + + static void + addTriangleGeometry(SGTexturedTriangleBin& triangles, + const SGBinObject& obj, unsigned grp, + const SGVec2f& tc0Scale, + const SGVec2f& tc1Scale) + { + const std::vector& vertices(obj.get_wgs84_nodes()); + const std::vector& normals(obj.get_normals()); + const std::vector& texCoords(obj.get_texcoords()); + const int_list& tris_v(obj.get_tris_v()[grp]); + const int_list& tris_n(obj.get_tris_n()[grp]); + const tci_list& tris_tc(obj.get_tris_tcs()[grp]); + bool num_norms_is_num_verts = true; + + if (tris_v.size() != tris_n.size()) { + // If the normal indices do not match, they should be inmplicitly + // the same than the vertex indices. + num_norms_is_num_verts = false; + } + + if ( !tris_tc[1].empty() ) { + triangles.hasSecondaryTexCoord(true); + } + + for (unsigned i = 2; i < tris_v.size(); i += 3) { + SGVertNormTex v0; + v0.SetVertex( toVec3f(vertices[tris_v[i-2]]) ); + v0.SetNormal( num_norms_is_num_verts ? normals[tris_n[i-2]] : + normals[tris_v[i-2]] ); + v0.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i-2) ); + if (!tris_tc[1].empty()) { + v0.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i-2) ); + } + SGVertNormTex v1; + v1.SetVertex( toVec3f(vertices[tris_v[i-1]]) ); + v1.SetNormal( num_norms_is_num_verts ? normals[tris_n[i-1]] : + normals[tris_v[i-1]] ); + v1.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i-1) ); + if (!tris_tc[1].empty()) { + v1.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i-1) ); + } + SGVertNormTex v2; + v2.SetVertex( toVec3f(vertices[tris_v[i]]) ); + v2.SetNormal( num_norms_is_num_verts ? normals[tris_n[i]] : + normals[tris_v[i]] ); + v2.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i) ); + if (!tris_tc[1].empty()) { + v2.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i) ); + } + + triangles.insert(v0, v1, v2); + } + } + + static void + addStripGeometry(SGTexturedTriangleBin& triangles, + const SGBinObject& obj, unsigned grp, + const SGVec2f& tc0Scale, + const SGVec2f& tc1Scale) + { + const std::vector& vertices(obj.get_wgs84_nodes()); + const std::vector& normals(obj.get_normals()); + const std::vector& texCoords(obj.get_texcoords()); + const int_list& strips_v(obj.get_strips_v()[grp]); + const int_list& strips_n(obj.get_strips_n()[grp]); + const tci_list& strips_tc(obj.get_strips_tcs()[grp]); + bool num_norms_is_num_verts = true; + + if (strips_v.size() != strips_n.size()) { + // If the normal indices do not match, they should be inmplicitly + // the same than the vertex indices. + num_norms_is_num_verts = false; + } + + if ( !strips_tc[1].empty() ) { + triangles.hasSecondaryTexCoord(true); + } + + for (unsigned i = 2; i < strips_v.size(); ++i) { + SGVertNormTex v0; + v0.SetVertex( toVec3f(vertices[strips_v[i-2]]) ); + v0.SetNormal( num_norms_is_num_verts ? normals[strips_n[i-2]] : + normals[strips_v[i-2]] ); + v0.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[0], tc0Scale, i-2) ); + if (!strips_tc[1].empty()) { + v0.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i-2) ); + } + SGVertNormTex v1; + v1.SetVertex( toVec3f(vertices[strips_v[i-1]]) ); + v1.SetNormal( num_norms_is_num_verts ? normals[strips_n[i-1]] : + normals[strips_v[i-1]] ); + v1.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[1], tc0Scale, i-1) ); + if (!strips_tc[1].empty()) { + v1.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i-1) ); + } + SGVertNormTex v2; + v2.SetVertex( toVec3f(vertices[strips_v[i]]) ); + v2.SetNormal( num_norms_is_num_verts ? normals[strips_n[i]] : + normals[strips_v[i]] ); + v2.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[0], tc0Scale, i) ); + if (!strips_tc[1].empty()) { + v2.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i) ); + } + if (i%2) + triangles.insert(v1, v0, v2); + else + triangles.insert(v0, v1, v2); + } + } + + static void + addFanGeometry(SGTexturedTriangleBin& triangles, + const SGBinObject& obj, unsigned grp, + const SGVec2f& tc0Scale, + const SGVec2f& tc1Scale) + { + const std::vector& vertices(obj.get_wgs84_nodes()); + const std::vector& normals(obj.get_normals()); + const std::vector& texCoords(obj.get_texcoords()); + const int_list& fans_v(obj.get_fans_v()[grp]); + const int_list& fans_n(obj.get_fans_n()[grp]); + const tci_list& fans_tc(obj.get_fans_tcs()[grp]); + bool num_norms_is_num_verts = true; + + if (fans_v.size() != fans_n.size()) { + // If the normal indices do not match, they should be inmplicitly + // the same than the vertex indices. + num_norms_is_num_verts = false; + } + + if ( !fans_tc[1].empty() ) { + triangles.hasSecondaryTexCoord(true); + } + + SGVertNormTex v0; + v0.SetVertex( toVec3f(vertices[fans_v[0]]) ); + v0.SetNormal( num_norms_is_num_verts ? normals[fans_n[0]] : + normals[fans_v[0]] ); + v0.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, 0) ); + if (!fans_tc[1].empty()) { + v0.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, 0) ); + } + SGVertNormTex v1; + v1.SetVertex( toVec3f(vertices[fans_v[1]]) ); + v1.SetNormal( num_norms_is_num_verts ? normals[fans_n[1]] : + normals[fans_v[1]] ); + v1.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, 1) ); + if (!fans_tc[1].empty()) { + v1.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, 1) ); + } + for (unsigned i = 2; i < fans_v.size(); ++i) { + SGVertNormTex v2; + v2.SetVertex( toVec3f(vertices[fans_v[i]]) ); + v2.SetNormal( num_norms_is_num_verts ? normals[fans_n[i]] : + normals[fans_v[i]] ); + v2.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, i) ); + if (!fans_tc[1].empty()) { + v2.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, i) ); + } + triangles.insert(v0, v1, v2); + v1 = v2; + } + } + + bool + insertSurfaceGeometry(const SGBinObject& obj, SGMaterialCache* matcache) + { + if (obj.get_tris_n().size() < obj.get_tris_v().size() || + obj.get_tris_tcs().size() < obj.get_tris_v().size()) { + SG_LOG(SG_TERRAIN, SG_ALERT, + "Group list sizes for triangles do not match!"); + return false; + } + + for (unsigned grp = 0; grp < obj.get_tris_v().size(); ++grp) { + std::string materialName = obj.get_tri_materials()[grp]; + SGVec2f tc0Scale = getTexCoordScale(materialName, matcache); + SGVec2f tc1Scale(1.0, 1.0); + addTriangleGeometry(materialTriangleMap[materialName], + obj, grp, tc0Scale, tc1Scale ); + } + + if (obj.get_strips_n().size() < obj.get_strips_v().size() || + obj.get_strips_tcs().size() < obj.get_strips_v().size()) { + SG_LOG(SG_TERRAIN, SG_ALERT, + "Group list sizes for strips do not match!"); + return false; + } + for (unsigned grp = 0; grp < obj.get_strips_v().size(); ++grp) { + std::string materialName = obj.get_strip_materials()[grp]; + SGVec2f tc0Scale = getTexCoordScale(materialName, matcache); + SGVec2f tc1Scale(1.0, 1.0); + addStripGeometry(materialTriangleMap[materialName], + obj, grp, tc0Scale, tc1Scale); + } + + if (obj.get_fans_n().size() < obj.get_fans_v().size() || + obj.get_fans_tcs().size() < obj.get_fans_v().size()) { + SG_LOG(SG_TERRAIN, SG_ALERT, + "Group list sizes for fans do not match!"); + return false; + } + for (unsigned grp = 0; grp < obj.get_fans_v().size(); ++grp) { + std::string materialName = obj.get_fan_materials()[grp]; + SGVec2f tc0Scale = getTexCoordScale(materialName, matcache); + SGVec2f tc1Scale(1.0, 1.0); + addFanGeometry(materialTriangleMap[materialName], + obj, grp, tc0Scale, tc1Scale ); + } + return true; + } + + osg::Node* getSurfaceGeometry(SGMaterialCache* matcache, bool useVBOs) const + { + if (materialTriangleMap.empty()) + return 0; + + EffectGeode* eg = NULL; + osg::Group* group = (materialTriangleMap.size() > 1 ? new osg::Group : NULL); + if (group) { + group->setName("surfaceGeometryGroup"); + } + + //osg::Geode* geode = new osg::Geode; + SGMaterialTriangleMap::const_iterator i; + for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { + osg::Geometry* geometry = i->second.buildGeometry(useVBOs); + SGMaterial *mat = NULL; + if (matcache) { + mat = matcache->find(i->first); + } + eg = new EffectGeode; + eg->setName("EffectGeode"); + if (mat) { + eg->setMaterial(mat); + eg->setEffect(mat->get_one_effect(i->second.getTextureIndex())); + } else { + eg->setMaterial(NULL); + } + eg->addDrawable(geometry); + eg->runGenerators(geometry); // Generate extra data needed by effect + if (group) { + group->addChild(eg); + } + } + + if (group) { + return group; + } else { + return eg; + } + } +}; diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index 1fd3a2f3..3ff0bed4 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -28,1307 +28,15 @@ #include "obj.hxx" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "SGTexturedTriangleBin.hxx" -#include "SGLightBin.hxx" -#include "SGModelBin.hxx" -#include "SGBuildingBin.hxx" -#include "TreeBin.hxx" -#include "SGDirectionalLightBin.hxx" -#include "GroundLightManager.hxx" -#include "pt_lights.hxx" +#include "SGTileGeometryBin.hxx" // for original tile loading +#include "SGTileDetailsCallback.hxx" // for tile details ( random objects, and lighting ) -#define SG_SIMPLIFIER_RATIO 0.001 -#define SG_SIMPLIFIER_MAX_LENGTH 1000.0 -#define SG_SIMPLIFIER_MAX_ERROR 2000.0 -#define SG_OBJECT_RANGE 9000.0 -#define SG_TILE_RADIUS 14000.0 -#define SG_TILE_MIN_EXPIRY 180.0 using namespace simgear; -typedef std::map SGMaterialTriangleMap; -typedef std::list SGLightListBin; -typedef std::list SGDirectionalLightListBin; - -class SGTileGeometryBin : public osg::Referenced { -public: - SGMaterialTriangleMap materialTriangleMap; - SGLightBin tileLights; - SGLightBin randomTileLights; - SGTreeBinList randomForest; - SGDirectionalLightBin runwayLights; - SGDirectionalLightBin taxiLights; - SGDirectionalLightListBin vasiLights; - SGDirectionalLightListBin rabitLights; - SGLightListBin odalLights; - SGDirectionalLightListBin holdshortLights; - SGDirectionalLightListBin guardLights; - SGDirectionalLightListBin reilLights; - SGMatModelBin randomModels; - SGBuildingBinList randomBuildings; - bool tileRandomSurfaceLightsComputed; - bool tileRandomObjectsComputed; - - SGTileGeometryBin() { - tileRandomSurfaceLightsComputed = false; - tileRandomObjectsComputed = false; - } - - static SGVec4f - getMaterialLightColor(const SGMaterial* material) - { - if (!material) - return SGVec4f(1, 1, 1, 0.8); - return material->get_light_color(); - } - - static void - addPointGeometry(SGLightBin& lights, - const std::vector& vertices, - const SGVec4f& color, - const int_list& pts_v) - { - for (unsigned i = 0; i < pts_v.size(); ++i) - lights.insert(toVec3f(vertices[pts_v[i]]), color); - } - - static void - addPointGeometry(SGDirectionalLightBin& lights, - const std::vector& vertices, - const std::vector& normals, - const SGVec4f& color, - const int_list& pts_v, - const int_list& pts_n) - { - // If the normal indices match the vertex indices, use seperate - // normal indices. Else reuse the vertex indices for the normals. - if (pts_v.size() == pts_n.size()) { - for (unsigned i = 0; i < pts_v.size(); ++i) - lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_n[i]], color); - } else { - for (unsigned i = 0; i < pts_v.size(); ++i) - lights.insert(toVec3f(vertices[pts_v[i]]), normals[pts_v[i]], color); - } - } - - bool - insertPtGeometry(const SGBinObject& obj, SGMaterialCache* matcache) - { - if (obj.get_pts_v().size() != obj.get_pts_n().size()) { - SG_LOG(SG_TERRAIN, SG_ALERT, - "Group list sizes for points do not match!"); - return false; - } - - for (unsigned grp = 0; grp < obj.get_pts_v().size(); ++grp) { - std::string materialName = obj.get_pt_materials()[grp]; - SGMaterial* material = matcache->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, - obj.get_pts_v()[grp]); - } else if (materialName == "RWY_BLUE_TAXIWAY_LIGHTS" - || materialName == "RWY_GREEN_TAXIWAY_LIGHTS") { - addPointGeometry(taxiLights, obj.get_wgs84_nodes(), obj.get_normals(), - color, obj.get_pts_v()[grp], obj.get_pts_n()[grp]); - } else if (materialName == "RWY_VASI_LIGHTS") { - vasiLights.push_back(SGDirectionalLightBin()); - addPointGeometry(vasiLights.back(), obj.get_wgs84_nodes(), - obj.get_normals(), color, obj.get_pts_v()[grp], - obj.get_pts_n()[grp]); - } else if (materialName == "RWY_SEQUENCED_LIGHTS") { - rabitLights.push_back(SGDirectionalLightBin()); - addPointGeometry(rabitLights.back(), obj.get_wgs84_nodes(), - obj.get_normals(), color, obj.get_pts_v()[grp], - obj.get_pts_n()[grp]); - } else if (materialName == "RWY_ODALS_LIGHTS") { - odalLights.push_back(SGLightBin()); - addPointGeometry(odalLights.back(), obj.get_wgs84_nodes(), - color, obj.get_pts_v()[grp]); - } else if (materialName == "RWY_YELLOW_PULSE_LIGHTS") { - holdshortLights.push_back(SGDirectionalLightBin()); - addPointGeometry(holdshortLights.back(), obj.get_wgs84_nodes(), - obj.get_normals(), color, obj.get_pts_v()[grp], - obj.get_pts_n()[grp]); - } else if (materialName == "RWY_GUARD_LIGHTS") { - guardLights.push_back(SGDirectionalLightBin()); - addPointGeometry(guardLights.back(), obj.get_wgs84_nodes(), - obj.get_normals(), color, obj.get_pts_v()[grp], - obj.get_pts_n()[grp]); - } else if (materialName == "RWY_REIL_LIGHTS") { - reilLights.push_back(SGDirectionalLightBin()); - addPointGeometry(reilLights.back(), obj.get_wgs84_nodes(), - obj.get_normals(), color, obj.get_pts_v()[grp], - obj.get_pts_n()[grp]); - } else { - // what is left must be runway lights - addPointGeometry(runwayLights, obj.get_wgs84_nodes(), - obj.get_normals(), color, obj.get_pts_v()[grp], - obj.get_pts_n()[grp]); - } - } - - return true; - } - - - static SGVec2f - getTexCoord(const std::vector& texCoords, const int_list& tc, - const SGVec2f& tcScale, unsigned i) - { - if (tc.empty()) - return tcScale; - else if (tc.size() == 1) - return mult(texCoords[tc[0]], tcScale); - else - return mult(texCoords[tc[i]], tcScale); - } - - static void - addTriangleGeometry(SGTexturedTriangleBin& triangles, - const SGBinObject& obj, unsigned grp, - const SGVec2f& tc0Scale, - const SGVec2f& tc1Scale) - { - const std::vector& vertices(obj.get_wgs84_nodes()); - const std::vector& normals(obj.get_normals()); - const std::vector& texCoords(obj.get_texcoords()); - const int_list& tris_v(obj.get_tris_v()[grp]); - const int_list& tris_n(obj.get_tris_n()[grp]); - const tci_list& tris_tc(obj.get_tris_tcs()[grp]); - bool num_norms_is_num_verts = true; - - if (tris_v.size() != tris_n.size()) { - // If the normal indices do not match, they should be inmplicitly - // the same than the vertex indices. - num_norms_is_num_verts = false; - } - - if ( !tris_tc[1].empty() ) { - triangles.hasSecondaryTexCoord(true); - } - - for (unsigned i = 2; i < tris_v.size(); i += 3) { - SGVertNormTex v0; - v0.SetVertex( toVec3f(vertices[tris_v[i-2]]) ); - v0.SetNormal( num_norms_is_num_verts ? normals[tris_n[i-2]] : - normals[tris_v[i-2]] ); - v0.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i-2) ); - if (!tris_tc[1].empty()) { - v0.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i-2) ); - } - SGVertNormTex v1; - v1.SetVertex( toVec3f(vertices[tris_v[i-1]]) ); - v1.SetNormal( num_norms_is_num_verts ? normals[tris_n[i-1]] : - normals[tris_v[i-1]] ); - v1.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i-1) ); - if (!tris_tc[1].empty()) { - v1.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i-1) ); - } - SGVertNormTex v2; - v2.SetVertex( toVec3f(vertices[tris_v[i]]) ); - v2.SetNormal( num_norms_is_num_verts ? normals[tris_n[i]] : - normals[tris_v[i]] ); - v2.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i) ); - if (!tris_tc[1].empty()) { - v2.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i) ); - } - - triangles.insert(v0, v1, v2); - } - } - - static void - addStripGeometry(SGTexturedTriangleBin& triangles, - const SGBinObject& obj, unsigned grp, - const SGVec2f& tc0Scale, - const SGVec2f& tc1Scale) - { - const std::vector& vertices(obj.get_wgs84_nodes()); - const std::vector& normals(obj.get_normals()); - const std::vector& texCoords(obj.get_texcoords()); - const int_list& strips_v(obj.get_strips_v()[grp]); - const int_list& strips_n(obj.get_strips_n()[grp]); - const tci_list& strips_tc(obj.get_strips_tcs()[grp]); - bool num_norms_is_num_verts = true; - - if (strips_v.size() != strips_n.size()) { - // If the normal indices do not match, they should be inmplicitly - // the same than the vertex indices. - num_norms_is_num_verts = false; - } - - if ( !strips_tc[1].empty() ) { - triangles.hasSecondaryTexCoord(true); - } - - for (unsigned i = 2; i < strips_v.size(); ++i) { - SGVertNormTex v0; - v0.SetVertex( toVec3f(vertices[strips_v[i-2]]) ); - v0.SetNormal( num_norms_is_num_verts ? normals[strips_n[i-2]] : - normals[strips_v[i-2]] ); - v0.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[0], tc0Scale, i-2) ); - if (!strips_tc[1].empty()) { - v0.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i-2) ); - } - SGVertNormTex v1; - v1.SetVertex( toVec3f(vertices[strips_v[i-1]]) ); - v1.SetNormal( num_norms_is_num_verts ? normals[strips_n[i-1]] : - normals[strips_v[i-1]] ); - v1.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[1], tc0Scale, i-1) ); - if (!strips_tc[1].empty()) { - v1.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i-1) ); - } - SGVertNormTex v2; - v2.SetVertex( toVec3f(vertices[strips_v[i]]) ); - v2.SetNormal( num_norms_is_num_verts ? normals[strips_n[i]] : - normals[strips_v[i]] ); - v2.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[0], tc0Scale, i) ); - if (!strips_tc[1].empty()) { - v2.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i) ); - } - if (i%2) - triangles.insert(v1, v0, v2); - else - triangles.insert(v0, v1, v2); - } - } - - static void - addFanGeometry(SGTexturedTriangleBin& triangles, - const SGBinObject& obj, unsigned grp, - const SGVec2f& tc0Scale, - const SGVec2f& tc1Scale) - { - const std::vector& vertices(obj.get_wgs84_nodes()); - const std::vector& normals(obj.get_normals()); - const std::vector& texCoords(obj.get_texcoords()); - const int_list& fans_v(obj.get_fans_v()[grp]); - const int_list& fans_n(obj.get_fans_n()[grp]); - const tci_list& fans_tc(obj.get_fans_tcs()[grp]); - bool num_norms_is_num_verts = true; - - if (fans_v.size() != fans_n.size()) { - // If the normal indices do not match, they should be inmplicitly - // the same than the vertex indices. - num_norms_is_num_verts = false; - } - - if ( !fans_tc[1].empty() ) { - triangles.hasSecondaryTexCoord(true); - } - - SGVertNormTex v0; - v0.SetVertex( toVec3f(vertices[fans_v[0]]) ); - v0.SetNormal( num_norms_is_num_verts ? normals[fans_n[0]] : - normals[fans_v[0]] ); - v0.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, 0) ); - if (!fans_tc[1].empty()) { - v0.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, 0) ); - } - SGVertNormTex v1; - v1.SetVertex( toVec3f(vertices[fans_v[1]]) ); - v1.SetNormal( num_norms_is_num_verts ? normals[fans_n[1]] : - normals[fans_v[1]] ); - v1.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, 1) ); - if (!fans_tc[1].empty()) { - v1.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, 1) ); - } - for (unsigned i = 2; i < fans_v.size(); ++i) { - SGVertNormTex v2; - v2.SetVertex( toVec3f(vertices[fans_v[i]]) ); - v2.SetNormal( num_norms_is_num_verts ? normals[fans_n[i]] : - normals[fans_v[i]] ); - v2.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, i) ); - if (!fans_tc[1].empty()) { - v2.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, i) ); - } - triangles.insert(v0, v1, v2); - v1 = v2; - } - } - - SGVec2f getTexCoordScale(const std::string& name, SGMaterialCache* matcache) - { - if (!matcache) - return SGVec2f(1, 1); - SGMaterial* material = matcache->find(name); - if (!material) - return SGVec2f(1, 1); - - return material->get_tex_coord_scale(); - } - - bool - insertSurfaceGeometry(const SGBinObject& obj, SGMaterialCache* matcache) - { - if (obj.get_tris_n().size() < obj.get_tris_v().size() || - obj.get_tris_tcs().size() < obj.get_tris_v().size()) { - SG_LOG(SG_TERRAIN, SG_ALERT, - "Group list sizes for triangles do not match!"); - return false; - } - - for (unsigned grp = 0; grp < obj.get_tris_v().size(); ++grp) { - std::string materialName = obj.get_tri_materials()[grp]; - SGVec2f tc0Scale = getTexCoordScale(materialName, matcache); - SGVec2f tc1Scale(1.0, 1.0); - addTriangleGeometry(materialTriangleMap[materialName], - obj, grp, tc0Scale, tc1Scale ); - } - - if (obj.get_strips_n().size() < obj.get_strips_v().size() || - obj.get_strips_tcs().size() < obj.get_strips_v().size()) { - SG_LOG(SG_TERRAIN, SG_ALERT, - "Group list sizes for strips do not match!"); - return false; - } - for (unsigned grp = 0; grp < obj.get_strips_v().size(); ++grp) { - std::string materialName = obj.get_strip_materials()[grp]; - SGVec2f tc0Scale = getTexCoordScale(materialName, matcache); - SGVec2f tc1Scale(1.0, 1.0); - addStripGeometry(materialTriangleMap[materialName], - obj, grp, tc0Scale, tc1Scale); - } - - if (obj.get_fans_n().size() < obj.get_fans_v().size() || - obj.get_fans_tcs().size() < obj.get_fans_v().size()) { - SG_LOG(SG_TERRAIN, SG_ALERT, - "Group list sizes for fans do not match!"); - return false; - } - for (unsigned grp = 0; grp < obj.get_fans_v().size(); ++grp) { - std::string materialName = obj.get_fan_materials()[grp]; - SGVec2f tc0Scale = getTexCoordScale(materialName, matcache); - SGVec2f tc1Scale(1.0, 1.0); - addFanGeometry(materialTriangleMap[materialName], - obj, grp, tc0Scale, tc1Scale ); - } - return true; - } - - osg::Node* getSurfaceGeometry(SGMaterialCache* matcache, bool useVBOs) const - { - if (materialTriangleMap.empty()) - return 0; - - EffectGeode* eg = NULL; - osg::Group* group = (materialTriangleMap.size() > 1 ? new osg::Group : NULL); - if (group) { - group->setName("surfaceGeometryGroup"); - } - - //osg::Geode* geode = new osg::Geode; - SGMaterialTriangleMap::const_iterator i; - for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { - osg::Geometry* geometry = i->second.buildGeometry(useVBOs); - SGMaterial *mat = NULL; - if (matcache) { - mat = matcache->find(i->first); - } - eg = new EffectGeode; - eg->setName("EffectGeode"); - if (mat) { - eg->setEffect(mat->get_effect(i->second)); - } - eg->addDrawable(geometry); - eg->runGenerators(geometry); // Generate extra data needed by effect - if (group) { - group->addChild(eg); - } - } - - if (group) { - return group; - } else { - return eg; - } - } - - void computeRandomSurfaceLights(SGMaterialCache* matcache) - { - SGMaterialTriangleMap::iterator i; - - // Only compute the lights if we haven't already done so. - // For example, the light data will still exist if the - // PagedLOD expires. - if (tileRandomSurfaceLightsComputed) return; - tileRandomSurfaceLightsComputed = true; - - // generate a repeatable random seed - mt seed; - mt_init(&seed, unsigned(123)); - - for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { - SGMaterial *mat = matcache->find(i->first); - if (!mat) - continue; - - float coverage = mat->get_light_coverage(); - if (coverage <= 0) - continue; - if (coverage < 10000.0) { - SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is " - << coverage << ", pushing up to 10000"); - coverage = 10000; - } - - std::vector randomPoints; - i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints); - std::vector::iterator j; - for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { - float zombie = mt_rand(&seed); - // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0 - float factor = mt_rand(&seed); - factor *= factor; - - float bright = 1; - SGVec4f color; - if ( zombie > 0.5 ) { - // 50% chance of yellowish - color = SGVec4f(0.9f, 0.9f, 0.3f, bright - factor * 0.2f); - } else if (zombie > 0.15f) { - // 35% chance of whitish - color = SGVec4f(0.9, 0.9f, 0.8f, bright - factor * 0.2f); - } else if (zombie > 0.05f) { - // 10% chance of orangish - color = SGVec4f(0.9f, 0.6f, 0.2f, bright - factor * 0.2f); - } else { - // 5% chance of redish - color = SGVec4f(0.9f, 0.2f, 0.2f, bright - factor * 0.2f); - } - randomTileLights.insert(*j, color); - } - } - } - - void computeRandomObjectsAndBuildings( - SGMaterialCache* matcache, - float building_density, - bool use_random_objects, - bool use_random_buildings, - bool useVBOs) - { - SGMaterialTriangleMap::iterator i; - - // Only compute the random objects if we haven't already done so - if (tileRandomObjectsComputed) return; - tileRandomObjectsComputed = true; - - // generate a repeatable random seed - mt seed; - mt_init(&seed, unsigned(123)); - - for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { - SGMaterial *mat = matcache->find(i->first); - SGTexturedTriangleBin triangleBin = i->second; - - if (!mat) - continue; - - osg::Texture2D* object_mask = mat->get_object_mask(triangleBin); - - int group_count = mat->get_object_group_count(); - float building_coverage = mat->get_building_coverage(); - float cos_zero_density_angle = mat->get_cos_object_zero_density_slope_angle(); - float cos_max_density_angle = mat->get_cos_object_max_density_slope_angle(); - - if (building_coverage == 0) - continue; - - SGBuildingBin* bin = NULL; - - if (building_coverage > 0) { - bin = new SGBuildingBin(mat, useVBOs); - randomBuildings.push_back(bin); - } - - unsigned num = i->second.getNumTriangles(); - int random_dropped = 0; - int mask_dropped = 0; - int building_dropped = 0; - int triangle_dropped = 0; - - for (unsigned i = 0; i < num; ++i) { - SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i); - - SGVec3f vorigin = triangleBin.getVertex(triangleRef[0]).GetVertex(); - SGVec3f v0 = triangleBin.getVertex(triangleRef[1]).GetVertex() - vorigin; - SGVec3f v1 = triangleBin.getVertex(triangleRef[2]).GetVertex() - vorigin; - SGVec2f torigin = triangleBin.getVertex(triangleRef[0]).GetTexCoord(0); - SGVec2f t0 = triangleBin.getVertex(triangleRef[1]).GetTexCoord(0) - torigin; - SGVec2f t1 = triangleBin.getVertex(triangleRef[2]).GetTexCoord(0) - torigin; - SGVec3f normal = cross(v0, v1); - - // Ensure the slope isn't too steep by checking the - // cos of the angle between the slope normal and the - // vertical (conveniently the z-component of the normalized - // normal) and values passed in. - float cos = normalize(normal).z(); - float slope_density = 1.0; - if (cos < cos_zero_density_angle) continue; // Too steep for any objects - if (cos < cos_max_density_angle) { - slope_density = - (cos - cos_zero_density_angle) / - (cos_max_density_angle - cos_zero_density_angle); - } - - // Containers to hold the random buildings and objects generated - // for this triangle for collision detection purposes. - std::vector< std::pair< SGVec3f, float> > triangleObjectsList; - std::vector< std::pair< SGVec3f, float> > triangleBuildingList; - - // Compute the area - float area = 0.5f*length(normal); - if (area <= SGLimitsf::min()) - continue; - - // Generate any random objects - if (use_random_objects && (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) continue; - - // 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); - - // Determine the number of objecst to place, taking into account - // the slope density factor. - double n = slope_density * area / object->get_coverage_m2(); - - // Use the zombie door method to determine fractional object placement. - n = n + mt_rand(&seed); - - // place an object each unit of area - while ( n > 1.0 ) { - n -= 1.0; - - float a = mt_rand(&seed); - float b = mt_rand(&seed); - if ( a + b > 1 ) { - a = 1 - a; - b = 1 - b; - } - - SGVec3f randomPoint = vorigin + a*v0 + b*v1; - float rotation = static_cast(mt_rand(&seed)); - - // Check that the point is sufficiently far from - // the edge of the triangle by measuring the distance - // from the three lines that make up the triangle. - float spacing = object->get_spacing_m(); - - SGVec3f p = randomPoint - vorigin; - float edges[] = { length(cross(p , p - v0)) / length(v0), - length(cross(p - v0, p - v1)) / length(v1 - v0), - length(cross(p - v1, p )) / length(v1) }; - float edge_dist = *std::min_element(edges, edges + 3); - - if (edge_dist < spacing) { - continue; - } - - if (object_mask != NULL) { - SGVec2f texCoord = torigin + a*t0 + b*t1; - - // Check this random point against the object mask - // blue (for buildings) channel. - osg::Image* img = object_mask->getImage(); - unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); - unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); - - if (mt_rand(&seed) > img->getColor(x, y).b()) { - // Failed object mask check - continue; - } - - rotation = img->getColor(x,y).r(); - } - - bool close = false; - - // Check it isn't too close to any other random objects in the triangle - std::vector >::iterator l; - for (l = triangleObjectsList.begin(); l != triangleObjectsList.end(); ++l) { - float min_dist2 = (l->second + object->get_spacing_m()) * - (l->second + object->get_spacing_m()); - - if (distSqr(l->first, randomPoint) < min_dist2) { - close = true; - continue; - } - } - - if (!close) { - triangleObjectsList.push_back(std::make_pair(randomPoint, object->get_spacing_m())); - randomModels.insert(randomPoint, - object, - (int)object->get_randomized_range_m(&seed), - rotation); - } - } - } - } - } - - // Random objects now generated. Now generate the random buildings (if any); - if (use_random_buildings && (building_coverage > 0) && (building_density > 0)) { - - // Calculate the number of buildings, taking into account building density (which is linear) - // and the slope density factor. - double num = building_density * building_density * slope_density * area / building_coverage; - - // For partial units of area, use a zombie door method to - // create the proper random chance of an object being created - // for this triangle. - num = num + mt_rand(&seed); - - if (num < 1.0f) { - continue; - } - - // Cosine of the angle between the two vectors. - float cosine = (dot(v0, v1) / (length(v0) * length(v1))); - - // Determine a grid spacing in each vector such that the correct - // coverage will result. - float stepv0 = (sqrtf(building_coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine); - float stepv1 = (sqrtf(building_coverage) / building_density) / length(v1); - - stepv0 = std::min(stepv0, 1.0f); - stepv1 = std::min(stepv1, 1.0f); - - // Start at a random point. a will be immediately incremented below. - float a = -mt_rand(&seed) * stepv0; - float b = mt_rand(&seed) * stepv1; - - // Place an object each unit of area - while (num > 1.0) { - num -= 1.0; - - // Set the next location to place a building - a += stepv0; - - if ((a + b) > 1.0f) { - // Reached the end of the scan-line on v0. Reset and increment - // scan-line on v1 - a = mt_rand(&seed) * stepv0; - b += stepv1; - } - - if (b > 1.0f) { - // In a degenerate case of a single point, we might be outside the - // scanline. Note that we need to still ensure that a+b < 1. - b = mt_rand(&seed) * stepv1 * (1.0f - a); - } - - if ((a + b) > 1.0f ) { - // Truly degenerate case - simply choose a random point guaranteed - // to fulfil the constraing of a+b < 1. - a = mt_rand(&seed); - b = mt_rand(&seed) * (1.0f - a); - } - - SGVec3f randomPoint = vorigin + a*v0 + b*v1; - float rotation = mt_rand(&seed); - - if (object_mask != NULL) { - SGVec2f texCoord = torigin + a*t0 + b*t1; - osg::Image* img = object_mask->getImage(); - int x = (int) (img->s() * texCoord.x()) % img->s(); - int y = (int) (img->t() * texCoord.y()) % img->t(); - - // In some degenerate cases x or y can be < 1, in which case the mod operand fails - while (x < 0) x += img->s(); - while (y < 0) y += img->t(); - - if (mt_rand(&seed) < img->getColor(x, y).b()) { - // Object passes mask. Rotation is taken from the red channel - rotation = img->getColor(x,y).r(); - } else { - // Fails mask test - try again. - mask_dropped++; - continue; - } - } - - // Check building isn't too close to the triangle edge. - float type_roll = mt_rand(&seed); - SGBuildingBin::BuildingType buildingtype = bin->getBuildingType(type_roll); - float radius = bin->getBuildingMaxRadius(buildingtype); - - // Determine the actual center of the building, by shifting from the - // center of the front face to the true center. - osg::Matrix rotationMat = osg::Matrix::rotate(- rotation * M_PI * 2, - osg::Vec3f(0.0, 0.0, 1.0)); - SGVec3f buildingCenter = randomPoint + toSG(osg::Vec3f(-0.5 * bin->getBuildingMaxDepth(buildingtype), 0.0, 0.0) * rotationMat); - - SGVec3f p = buildingCenter - vorigin; - float edges[] = { length(cross(p , p - v0)) / length(v0), - length(cross(p - v0, p - v1)) / length(v1 - v0), - length(cross(p - v1, p )) / length(v1) }; - float edge_dist = *std::min_element(edges, edges + 3); - - if (edge_dist < radius) { - triangle_dropped++; - continue; - } - - // Check building isn't too close to random objects and other buildings. - bool close = false; - std::vector >::iterator iter; - - for (iter = triangleBuildingList.begin(); iter != triangleBuildingList.end(); ++iter) { - float min_dist = iter->second + radius; - if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) { - close = true; - continue; - } - } - - if (close) { - building_dropped++; - continue; - } - - for (iter = triangleObjectsList.begin(); iter != triangleObjectsList.end(); ++iter) { - float min_dist = iter->second + radius; - if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) { - close = true; - continue; - } - } - - if (close) { - random_dropped++; - continue; - } - - std::pair pt = std::make_pair(buildingCenter, radius); - triangleBuildingList.push_back(pt); - bin->insert(randomPoint, rotation, buildingtype); - } - } - - triangleObjectsList.clear(); - triangleBuildingList.clear(); - } - - SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << ((bin) ? bin->getNumBuildings() : 0)); - SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to mask: " << mask_dropped); - SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to random object: " << random_dropped); - SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to other buildings: " << building_dropped); - } - } - - void computeRandomForest(SGMaterialCache* matcache, float vegetation_density) - { - 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 = matcache->find(i->first); - if (!mat) - continue; - - float wood_coverage = mat->get_wood_coverage(); - if ((wood_coverage <= 0) || (vegetation_density <= 0)) - continue; - - // Attributes that don't vary by tree but do vary by material - bool found = false; - TreeBin* bin = NULL; - - BOOST_FOREACH(bin, randomForest) - { - if ((bin->texture == mat->get_tree_texture() ) && - (bin->texture_varieties == mat->get_tree_varieties()) && - (bin->range == mat->get_tree_range() ) && - (bin->width == mat->get_tree_width() ) && - (bin->height == mat->get_tree_height() ) ) { - found = true; - break; - } - } - - if (!found) { - bin = new TreeBin(); - bin->texture = mat->get_tree_texture(); - SG_LOG(SG_INPUT, SG_DEBUG, "Tree texture " << bin->texture); - bin->range = mat->get_tree_range(); - bin->width = mat->get_tree_width(); - bin->height = mat->get_tree_height(); - bin->texture_varieties = mat->get_tree_varieties(); - randomForest.push_back(bin); - } - - std::vector randomPoints; - i->second.addRandomTreePoints(wood_coverage, - mat->get_object_mask(i->second), - vegetation_density, - mat->get_cos_tree_max_density_slope_angle(), - mat->get_cos_tree_zero_density_slope_angle(), - randomPoints); - - std::vector::iterator k; - for (k = randomPoints.begin(); k != randomPoints.end(); ++k) { - bin->insert(*k); - } - } - } - - bool insertBinObj(const SGBinObject& obj, SGMaterialCache* matcache) - { - if (!insertPtGeometry(obj, matcache)) - return false; - if (!insertSurfaceGeometry(obj, matcache)) - return false; - return true; - } -}; - -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() {} - GetModelLODCoord(const GetModelLODCoord& rhs) - {} - osg::Vec3 operator() (const ModelLOD& mlod) const - { - return mlod.first->getBound().center(); - } -}; - -typedef QuadTreeBuilder RandomObjectsQuadtree; - -class RandomObjectCallback : public OptionsReadFileCallback { -public: - virtual osgDB::ReaderWriter::ReadResult - readNode(const std::string&, const osgDB::Options*) - { - osg::ref_ptr group = new osg::Group; - group->setName("Random Object and Lighting Group"); - group->setDataVariance(osg::Object::STATIC); - - osg::Node* node = loadTerrain(); - if (node) - group->addChild(node); - - osg::LOD* lightLOD = generateLightingTileObjects(); - if (lightLOD) - group->addChild(lightLOD); - - osg::LOD* objectLOD = generateRandomTileObjects(); - if (objectLOD) - group->addChild(objectLOD); - - return group.release(); - } - - // Load terrain if required - osg::Node* loadTerrain() - { - if (! _loadterrain) - return NULL; - - SGBinObject tile; - if (!tile.read_bin(_path)) - return NULL; - - SGMaterialLibPtr matlib; - SGMaterialCache* matcache = 0; - bool useVBOs = false; - bool simplifyNear = false; - double ratio = SG_SIMPLIFIER_RATIO; - double maxLength = SG_SIMPLIFIER_MAX_LENGTH; - double maxError = SG_SIMPLIFIER_MAX_ERROR; - - if (_options) { - matlib = _options->getMaterialLib(); - useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON"); - SGPropertyNode* propertyNode = _options->getPropertyNode().get(); - simplifyNear = propertyNode->getBoolValue("/sim/rendering/terrain/simplifier/enabled-near", simplifyNear); - ratio = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/ratio", ratio); - maxLength = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-length", maxLength); - maxError = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-error", maxError); - } - - // PSADRO TODO : we can do this in terragear - // - why not add a bitmask of flags to the btg so we can precompute this? - // and only do it if it hasn't been done already - SGVec3d center = tile.get_gbs_center(); - SGGeod geodPos = SGGeod::fromCart(center); - SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180); - - // Generate a materials cache - if (matlib) matcache = matlib->generateMatCache(geodPos); - - // rotate the tiles so that the bounding boxes get nearly axis aligned. - // this will help the collision tree's bounding boxes a bit ... - std::vector nodes = tile.get_wgs84_nodes(); - for (unsigned i = 0; i < nodes.size(); ++i) - nodes[i] = hlOr.transform(nodes[i]); - tile.set_wgs84_nodes(nodes); - - SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]); - std::vector normals = tile.get_normals(); - for (unsigned i = 0; i < normals.size(); ++i) - normals[i] = hlOrf.transform(normals[i]); - tile.set_normals(normals); - - osg::ref_ptr tileGeometryBin = new SGTileGeometryBin; - - if (!tileGeometryBin->insertBinObj(tile, matcache)) - return NULL; - - osg::Node* node = tileGeometryBin->getSurfaceGeometry(matcache, useVBOs); - if (node && simplifyNear) { - osgUtil::Simplifier simplifier(ratio, maxError, maxLength); - node->accept(simplifier); - } - - return node; - } - - // Generate all the lighting objects for the tile. - osg::LOD* generateLightingTileObjects() - { - if (_matcache) - _tileGeometryBin->computeRandomSurfaceLights(_matcache); - - GroundLightManager* lightManager = GroundLightManager::instance(); - osg::ref_ptr lightGroup = new SGOffsetTransform(0.94); - SGVec3f up(0, 0, 1); - - if (_tileGeometryBin->tileLights.getNumLights() > 0 - || _tileGeometryBin->randomTileLights.getNumLights() > 0) { - osg::Group* groundLights0 = new osg::Group; - groundLights0->setStateSet(lightManager->getGroundLightStateSet()); - groundLights0->setNodeMask(GROUNDLIGHTS0_BIT); - - EffectGeode* geode = new EffectGeode; - Effect* lightEffect = getLightEffect(24, osg::Vec3(1, 0.001, 0.00001), 1, 8, false, _options); - - geode->setEffect(lightEffect); - 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); - EffectGeode* geode = new EffectGeode; - Effect* lightEffect = getLightEffect(24, osg::Vec3(1, 0.001, 0.00001), 1, 8, false, _options); - geode->setEffect(lightEffect); - geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->randomTileLights, 2, -0.15f)); - groundLights1->addChild(geode); - lightGroup->addChild(groundLights1); - geode = new EffectGeode; - geode->setEffect(lightEffect); - geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->randomTileLights)); - groundLights2->addChild(geode); - lightGroup->addChild(groundLights2); - } - - if (!_tileGeometryBin->vasiLights.empty()) { - EffectGeode* vasiGeode = new EffectGeode; - Effect* vasiEffect - = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, true, _options); - vasiGeode->setEffect(vasiEffect); - SGVec4f red(1, 0, 0, 1); - SGMaterial* mat = 0; - if (_matcache) - mat = _matcache->find("RWY_RED_LIGHTS"); - if (mat) - red = mat->get_light_color(); - SGVec4f white(1, 1, 1, 1); - mat = 0; - if (_matcache) - mat = _matcache->find("RWY_WHITE_LIGHTS"); - if (mat) - white = mat->get_light_color(); - SGDirectionalLightListBin::const_iterator i; - for (i = _tileGeometryBin->vasiLights.begin(); - i != _tileGeometryBin->vasiLights.end(); ++i) { - vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white)); - } - vasiGeode->setStateSet(lightManager->getRunwayLightStateSet()); - lightGroup->addChild(vasiGeode); - } - - Effect* runwayEffect = 0; - if (_tileGeometryBin->runwayLights.getNumLights() > 0 - || !_tileGeometryBin->rabitLights.empty() - || !_tileGeometryBin->reilLights.empty() - || !_tileGeometryBin->odalLights.empty() - || _tileGeometryBin->taxiLights.getNumLights() > 0) - runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true, _options); - if (_tileGeometryBin->runwayLights.getNumLights() > 0 - || !_tileGeometryBin->rabitLights.empty() - || !_tileGeometryBin->reilLights.empty() - || !_tileGeometryBin->odalLights.empty() - || !_tileGeometryBin->holdshortLights.empty() - || !_tileGeometryBin->guardLights.empty()) { - osg::Group* rwyLights = new osg::Group; - rwyLights->setStateSet(lightManager->getRunwayLightStateSet()); - rwyLights->setNodeMask(RUNWAYLIGHTS_BIT); - if (_tileGeometryBin->runwayLights.getNumLights() != 0) { - EffectGeode* geode = new EffectGeode; - geode->setEffect(runwayEffect); - 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, _options)); - } - for (i = _tileGeometryBin->reilLights.begin(); - i != _tileGeometryBin->reilLights.end(); ++i) { - rwyLights->addChild(SGLightFactory::getSequenced(*i, _options)); - } - for (i = _tileGeometryBin->holdshortLights.begin(); - i != _tileGeometryBin->holdshortLights.end(); ++i) { - rwyLights->addChild(SGLightFactory::getHoldShort(*i, _options)); - } - for (i = _tileGeometryBin->guardLights.begin(); - i != _tileGeometryBin->guardLights.end(); ++i) { - rwyLights->addChild(SGLightFactory::getGuard(*i, _options)); - } - SGLightListBin::const_iterator j; - for (j = _tileGeometryBin->odalLights.begin(); - j != _tileGeometryBin->odalLights.end(); ++j) { - rwyLights->addChild(SGLightFactory::getOdal(*j, _options)); - } - lightGroup->addChild(rwyLights); - } - - if (_tileGeometryBin->taxiLights.getNumLights() > 0) { - osg::Group* taxiLights = new osg::Group; - taxiLights->setStateSet(lightManager->getTaxiLightStateSet()); - taxiLights->setNodeMask(RUNWAYLIGHTS_BIT); - EffectGeode* geode = new EffectGeode; - geode->setEffect(runwayEffect); - geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->taxiLights)); - taxiLights->addChild(geode); - lightGroup->addChild(taxiLights); - } - - osg::LOD* lightLOD = NULL; - - if (lightGroup->getNumChildren() > 0) { - lightLOD = new osg::LOD; - lightLOD->addChild(lightGroup.get(), 0, 60000); - // VASI is always on, so doesn't use light bits. - lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT | PERMANENTLIGHT_BIT); - } - - return lightLOD; - } - - // Generate all the random forest, objects and buildings for the tile - osg::LOD* generateRandomTileObjects() - { - SGMaterialLibPtr matlib; - bool use_random_objects = false; - bool use_random_vegetation = false; - bool use_random_buildings = false; - float vegetation_density = 1.0f; - float building_density = 1.0f; - bool useVBOs = false; - - osg::ref_ptr randomObjects; - osg::ref_ptr forestNode; - osg::ref_ptr buildingNode; - - if (_options) { - matlib = _options->getMaterialLib(); - SGPropertyNode* propertyNode = _options->getPropertyNode().get(); - if (propertyNode) { - use_random_objects - = propertyNode->getBoolValue("/sim/rendering/random-objects", - use_random_objects); - use_random_vegetation - = propertyNode->getBoolValue("/sim/rendering/random-vegetation", - use_random_vegetation); - vegetation_density - = propertyNode->getFloatValue("/sim/rendering/vegetation-density", - vegetation_density); - use_random_buildings - = propertyNode->getBoolValue("/sim/rendering/random-buildings", - use_random_buildings); - building_density - = propertyNode->getFloatValue("/sim/rendering/building-density", - building_density); - } - - useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON"); - } - - - - if (matlib && (use_random_objects || use_random_buildings)) { - _tileGeometryBin->computeRandomObjectsAndBuildings(_matcache, - building_density, - use_random_objects, - use_random_buildings, - useVBOs); - } - - - 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); - - SGPropertyNode* root = _options->getPropertyNode()->getRootNode(); - osg::Node* node = obj.model->get_random_model(root, &seed); - - // Create a matrix to place the object in the correct - // location, and then apply the rotation matrix created - // above, with an additional random (or taken from - // the object mask) heading rotation if appropriate. - osg::Matrix transformMat; - transformMat = osg::Matrix::translate(toOsg(obj.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; - transformMat.preMult(osg::Matrix::rotate(hdg, - osg::Vec3d(0.0, 0.0, 1.0))); - } - - if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) { - // Rotate the object around the z axis. - double hdg = - obj.rotation * 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->setName("positionRandomModel"); - position->addChild(node); - models.push_back(ModelLOD(position, obj.lod)); - } - RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD())); - quadtree.buildQuadTree(models.begin(), models.end()); - randomObjects = quadtree.getRoot(); - randomObjects->setName("Random objects"); - } - - if (! _tileGeometryBin->randomBuildings.empty()) { - buildingNode = createRandomBuildings(_tileGeometryBin->randomBuildings, osg::Matrix::identity(), - _options); - buildingNode->setName("Random buildings"); - _tileGeometryBin->randomBuildings.clear(); - } - - if (use_random_vegetation && matlib) { - // Now add some random forest. - _tileGeometryBin->computeRandomForest(_matcache, vegetation_density); - - if (! _tileGeometryBin->randomForest.empty()) { - forestNode = createForest(_tileGeometryBin->randomForest, osg::Matrix::identity(), - _options); - forestNode->setName("Random trees"); - } - } - - osg::LOD* objectLOD = NULL; - - if (randomObjects.valid() || forestNode.valid() || buildingNode.valid()) { - objectLOD = new osg::LOD; - - if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000); - if (forestNode.valid()) objectLOD->addChild(forestNode.get(), 0, 20000); - if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000); - - unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT; - objectLOD->setNodeMask(nodeMask); - } - - return objectLOD; - } - - /// The original options to use for this bunch of models - osg::ref_ptr _options; - osg::ref_ptr _matcache; - osg::ref_ptr _tileGeometryBin; - string _path; - bool _loadterrain; -}; - osg::Node* SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options) { @@ -1382,9 +90,10 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options normals[i] = hlOrf.transform(normals[i]); tile.set_normals(normals); + // tile surface osg::ref_ptr tileGeometryBin = new SGTileGeometryBin(); - if (!tileGeometryBin->insertBinObj(tile, matcache)) + if (!tileGeometryBin->insertSurfaceGeometry(tile, matcache)) return NULL; osg::Node* node = tileGeometryBin->getSurfaceGeometry(matcache, useVBOs); @@ -1399,13 +108,17 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))* osg::Matrix::translate(toOsg(center))); - // PagedLOD for the random objects so we don't need to generate - // them all on tile loading. - osg::PagedLOD* pagedLOD = new osg::PagedLOD; - pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER); - pagedLOD->setName("pagedObjectLOD"); - if (node) { + // tile points + SGTileDetailsCallback* tileDetailsCallback = new SGTileDetailsCallback; + tileDetailsCallback->insertPtGeometry( tile, matcache ); + + // PagedLOD for the random objects so we don't need to generate + // them all on tile loading. + osg::PagedLOD* pagedLOD = new osg::PagedLOD; + pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER); + pagedLOD->setName("pagedObjectLOD"); + if (simplifyNear == simplifyDistant) { // Same terrain type is used for both near and far distances, // so add it to the main group. @@ -1418,30 +131,31 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options // call-back below will re-generate the closer version pagedLOD->addChild(node, object_range + SG_TILE_RADIUS, FLT_MAX); } - } - osg::ref_ptr opt; - opt = SGReaderWriterOptions::copyOrCreate(options); + osg::ref_ptr opt; + opt = SGReaderWriterOptions::copyOrCreate(options); - // we just need to know about the read file callback that itself holds the data - RandomObjectCallback* randomObjectCallback = new RandomObjectCallback; - randomObjectCallback->_options = opt; - randomObjectCallback->_tileGeometryBin = tileGeometryBin; - randomObjectCallback->_path = std::string(path); - randomObjectCallback->_loadterrain = ! (simplifyNear == simplifyDistant); - randomObjectCallback->_matcache = matcache; + // we just need to know about the read file callback that itself holds the data + tileDetailsCallback->_options = opt; + tileDetailsCallback->_path = std::string(path); + tileDetailsCallback->_loadterrain = ! (simplifyNear == simplifyDistant); + tileDetailsCallback->_gbs_center = center; + tileDetailsCallback->_rootNode = node; + tileDetailsCallback->_randomSurfaceLightsComputed = false; + tileDetailsCallback->_tileRandomObjectsComputed = false; + + osg::ref_ptr callbackOptions = new osgDB::Options; + callbackOptions->setObjectCacheHint(osgDB::Options::CACHE_ALL); + callbackOptions->setReadFileCallback(tileDetailsCallback); + pagedLOD->setDatabaseOptions(callbackOptions.get()); - osg::ref_ptr callbackOptions = new osgDB::Options; - callbackOptions->setObjectCacheHint(osgDB::Options::CACHE_ALL); - callbackOptions->setReadFileCallback(randomObjectCallback); - pagedLOD->setDatabaseOptions(callbackOptions.get()); + // Ensure that the random objects aren't expired too quickly + pagedLOD->setMinimumExpiryTime(pagedLOD->getNumChildren(), tile_min_expiry); + pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy filename for random objects callback"); + pagedLOD->setRange(pagedLOD->getNumChildren(), 0, object_range + SG_TILE_RADIUS); + transform->addChild(pagedLOD); + } - // Ensure that the random objects aren't expired too quickly - pagedLOD->setMinimumExpiryTime(pagedLOD->getNumChildren(), tile_min_expiry); - pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy filename for random objects callback"); - pagedLOD->setRange(pagedLOD->getNumChildren(), 0, object_range + SG_TILE_RADIUS); - transform->addChild(pagedLOD); transform->setNodeMask( ~simgear::MODELLIGHT_BIT ); - return transform; }