From 605125c0e7589ab027561227dcc2bb8e0a51f1ab Mon Sep 17 00:00:00 2001 From: timoore Date: Sun, 6 Jan 2008 15:04:43 +0000 Subject: [PATCH] Random object support from Stuart Buchanan In addition to Stuart's changes, there's an independent quad tree builder class for constructing loose quad trees from scene graph nodes. Stuart also implemented changes to the random number generator suggested by Andy Ross. --- simgear/math/sg_random.c | 156 ++++++------------- simgear/math/sg_random.h | 27 +++- simgear/scene/material/matmodel.cxx | 75 ++++++--- simgear/scene/material/matmodel.hxx | 4 +- simgear/scene/tgdb/SGModelBin.hxx | 88 +++++++++++ simgear/scene/tgdb/SGTexturedTriangleBin.hxx | 54 ++++++- simgear/scene/tgdb/obj.cxx | 130 ++++++++++++++-- simgear/scene/tgdb/userdata.cxx | 8 + simgear/scene/tgdb/userdata.hxx | 9 ++ simgear/scene/util/Makefile.am | 4 +- simgear/scene/util/QuadTreeBuilder.cxx | 76 +++++++++ simgear/scene/util/QuadTreeBuilder.hxx | 46 ++++++ 12 files changed, 534 insertions(+), 143 deletions(-) create mode 100755 simgear/scene/tgdb/SGModelBin.hxx create mode 100644 simgear/scene/util/QuadTreeBuilder.cxx create mode 100644 simgear/scene/util/QuadTreeBuilder.hxx diff --git a/simgear/math/sg_random.c b/simgear/math/sg_random.c index b58cf938..e9004323 100644 --- a/simgear/math/sg_random.c +++ b/simgear/math/sg_random.c @@ -21,50 +21,19 @@ // $Id$ -/* - A C-program for MT19937, with initialization improved 2002/2/10. - Coded by Takuji Nishimura and Makoto Matsumoto. - This is a faster version by taking Shawn Cokus's optimization, - Matthe Bellew's simplification, Isaku Wada's real version. - - Before using, initialize the state by using init_genrand(seed) - or init_by_array(init_key, key_length). - - Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - Any feedback is very welcome. - http://www.math.keio.ac.jp/matumoto/emt.html - email: matumoto@math.keio.ac.jp -*/ +/* + * "Cleaned up" and simplified Mersenne Twister implementation. + * Vastly smaller and more easily understood and embedded. Stores the + * state in a user-maintained structure instead of static memory, so + * you can have more than one, or save snapshots of the RNG state. + * Lacks the "init_by_array()" feature of the original code in favor + * of the simpler 32 bit seed initialization. Lacks the floating + * point generator, which is an orthogonal problem not related to + * random number generation. Verified to be identical to the original + * MT199367ar code through the first 10M generated numbers. + */ + + #ifdef HAVE_CONFIG_H # include @@ -76,94 +45,65 @@ #include "sg_random.h" -/* Period parameters */ -#define N 624 -#define M 397 -#define MATRIX_A 0x9908b0dfUL /* constant vector a */ -#define UMASK 0x80000000UL /* most significant w-r bits */ -#define LMASK 0x7fffffffUL /* least significant r bits */ -#define MIXBITS(u,v) ( ((u) & UMASK) | ((v) & LMASK) ) -#define TWIST(u,v) ((MIXBITS(u,v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL)) - -static unsigned long state[N]; /* the array for the state vector */ -static int left = 1; -static int initf = 0; -static unsigned long *next; - -/* initializes state[N] with a seed */ -void init_genrand(unsigned long s) +// Structure for the random number functions. +mt random_seed; + +#define MT(i) mt->array[i] + +void mt_init(mt *mt, unsigned int seed) { - int j; - state[0]= s & 0xffffffffUL; - for (j=1; j> 30)) + j); - /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ - /* In the previous versions, MSBs of the seed affect */ - /* only MSBs of the array state[]. */ - /* 2002/01/09 modified by Makoto Matsumoto */ - state[j] &= 0xffffffffUL; /* for >32 bit machines */ - } - left = 1; initf = 1; + int i; + MT(0)= seed; + for(i=1; i> 30)) + i); + mt->index = MT_N+1; } -static void next_state(void) +unsigned int mt_rand32(mt *mt) { - unsigned long *p=state; - int j; - - /* if init_genrand() has not been called, */ - /* a default initial seed is used */ - if (initf==0) init_genrand(5489UL); - - left = N; - next = state; - - for (j=N-M+1; --j; p++) - *p = p[M] ^ TWIST(p[0], p[1]); - - for (j=M; --j; p++) - *p = p[M-N] ^ TWIST(p[0], p[1]); + unsigned int i, y; + if(mt->index >= MT_N) { + for(i=0; i>1) ^ (y&1 ? 0x9908b0df : 0); + } + mt->index = 0; + } + y = MT(mt->index++); + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680; + y ^= (y << 15) & 0xefc60000; + y ^= (y >> 18); + return y; +} - *p = p[M-N] ^ TWIST(p[0], state[0]); +double mt_rand(mt *mt) +{ + /* divided by 2^32-1 */ + return (double)mt_rand32(mt) * (1.0/4294967295.0); } // Seed the random number generater with time() so we don't see the // same sequence every time void sg_srandom_time() { - init_genrand(time(NULL)); + mt_init(&random_seed, (unsigned int) time(NULL)); } // Seed the random number generater with time() in 10 minute intervals // so we get the same sequence within 10 minutes interval. // This is useful for synchronizing two display systems. void sg_srandom_time_10() { - init_genrand(time(NULL) / 600); + mt_init(&random_seed, (unsigned int) time(NULL) / 600); } - // Seed the random number generater with your own seed so can set up // repeatable randomization. void sg_srandom( unsigned int seed ) { - init_genrand( seed ); + mt_init(&random_seed, seed); } - // return a random number between [0.0, 1.0) double sg_random() { - unsigned long y; - - if (--left == 0) - next_state(); - y = *next++; - - /* Tempering */ - y ^= (y >> 11); - y ^= (y << 7) & 0x9d2c5680UL; - y ^= (y << 15) & 0xefc60000UL; - y ^= (y >> 18); - - return (double)y * (1.0/4294967295.0); - /* divided by 2^32-1 */ + return mt_rand(&random_seed); } - diff --git a/simgear/math/sg_random.h b/simgear/math/sg_random.h index e93a9420..1fed733d 100644 --- a/simgear/math/sg_random.h +++ b/simgear/math/sg_random.h @@ -33,6 +33,31 @@ extern "C" { #endif +#define MT_N 624 +#define MT_M 397 + +/** + * Structure to hold MT algorithm state to easily allow independant + * sets of random numbers with different seeds. + */ +struct {unsigned int array[MT_N]; int index; } typedef mt; + +/** + * Initialize a new MT state with a given seed. + */ +void mt_init(mt *mt, unsigned int seed); + +/** + * Generate a new 32-bit random number based on the given MT state. + */ +unsigned int mt_rand32( mt *mt); + +/** + * Generate a new random number between [0.0, 1.0) based + * on the given MT state. + */ +double mt_rand(mt *mt); + /** * Seed the random number generater with time() so we don't see the * same sequence every time. @@ -51,7 +76,7 @@ void sg_srandom_time_10(); * repeatable randomization. * @param seed random number generator seed */ -void sg_srandom( unsigned int seed ); +void sg_srandom(unsigned int seed ); /** * Return a random number between [0.0, 1.0) diff --git a/simgear/scene/material/matmodel.cxx b/simgear/scene/material/matmodel.cxx index 789f29ee..3d58d18e 100644 --- a/simgear/scene/material/matmodel.cxx +++ b/simgear/scene/material/matmodel.cxx @@ -137,15 +137,39 @@ SGMatModel::load_models ( SGModelLib *modellib, prop_root, sim_time_sec, /*cache_object*/ true ); if (entity != 0) { - // FIXME: this stuff can be handled - // in the XML wrapper as well (at least, - // the billboarding should be handled - // there). - osg::LOD * lod = new osg::LOD; - lod->setName("Model LOD"); - lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); - lod->setRange(0, 0, _range_m); - if (_heading_type == HEADING_BILLBOARD) { + // FIXME: this stuff can be handled + // in the XML wrapper as well (at least, + // the billboarding should be handled + // there). + + // Create multiple LoD nodes so instead of all objects + // of the same type appearing at once, some appear further + // away. + // + // Very basic hardcoded distribution: + // 4 at normal range + // 2 at 1.5 times normal range + // 1 at 2 time normal range. + // + // We achieve this by creating the three different LoD + // nodes and adding them to the _models list multiple times. + + osg::LOD * lod1 = new osg::LOD; + lod1->setName("Model LOD"); + lod1->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); + lod1->setRange(0, 0, _range_m); + + osg::LOD * lod15 = new osg::LOD; + lod15->setName("Model LOD - 1.5"); + lod15->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); + lod15->setRange(0, 0, 1.5 * _range_m); + + osg::LOD * lod2 = new osg::LOD; + lod2->setName("Model LOD - 2.0"); + lod2->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); + lod2->setRange(0, 0, 2.0 * _range_m); + + if (_heading_type == HEADING_BILLBOARD) { // if the model is a billboard, it is likely : // 1. a branch with only leaves, // 2. a tree or a non rectangular shape faked by transparency @@ -156,18 +180,29 @@ SGMatModel::load_models ( SGModelLib *modellib, stateSet->setAttributeAndModes(alphaFunc, osg::StateAttribute::OVERRIDE); stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + + lod1->addChild(entity); + lod15->addChild(entity); + lod2->addChild(entity); + } else { + lod1->addChild(entity); + lod15->addChild(entity); + lod2->addChild(entity); + } + + // Vary the distribution of LoDs by adding multiple times. + _models.push_back(lod1); + _models.push_back(lod1); + _models.push_back(lod1); + _models.push_back(lod1); + + _models.push_back(lod15); + _models.push_back(lod15); + + _models.push_back(lod2); - osg::Transform* transform = new osg::Transform; - transform->setName("Model Billboard Transform"); - transform->setReferenceFrame(osg::Transform::ABSOLUTE_RF); - transform->addChild(entity); - lod->addChild(transform); - } else { - lod->addChild(entity); - } - _models.push_back(lod); } else { - SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]); + SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]); } } } @@ -255,4 +290,6 @@ SGMatModelGroup::get_object (int index) const } + + // end of matmodel.cxx diff --git a/simgear/scene/material/matmodel.hxx b/simgear/scene/material/matmodel.hxx index 85363e80..f173f5c0 100644 --- a/simgear/scene/material/matmodel.hxx +++ b/simgear/scene/material/matmodel.hxx @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include @@ -119,6 +121,7 @@ public: HeadingType get_heading_type () const; virtual ~SGMatModel (); + protected: @@ -199,5 +202,4 @@ private: vector > _objects; }; - #endif // _SG_MAT_MODEL_HXX diff --git a/simgear/scene/tgdb/SGModelBin.hxx b/simgear/scene/tgdb/SGModelBin.hxx new file mode 100755 index 00000000..1c851418 --- /dev/null +++ b/simgear/scene/tgdb/SGModelBin.hxx @@ -0,0 +1,88 @@ +/* -*-c++-*- + * + * Copyright (C) 2007 Stuart Buchanan + * + * 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. + * + */ + +#ifndef SG_MAT_MODEL_BIN_HXX +#define SG_MAT_MODEL_BIN_HXX + +#include + +class SGMatModelBin { +public: + struct MatModel { + MatModel(const SGVec3f& p, SGMatModel *m) : + position(p), model(m) + { } + SGVec3f position; + SGMatModel *model; + }; + typedef std::vector MatModelList; + + void insert(const MatModel& model) + { + float x = model.position.x(); + float y = model.position.y(); + float z = model.position.z(); + + if (_models.size() == 0) + { + min_x = x; + max_x = x; + + min_y = y; + max_y = y; + + min_z = z; + max_z = z; + } + else + { + min_x = SGMisc::min(min_x, x); + max_x = SGMisc::max(max_x, x); + + min_y = SGMisc::min(min_y, y); + max_y = SGMisc::max(max_y, y); + + min_z = SGMisc::min(min_z, z); + max_z = SGMisc::max(max_z, z); + } + + _models.push_back(model); + } + + void insert(const SGVec3f& p, SGMatModel *m) + { insert(MatModel(p, m)); } + + unsigned getNumModels() const + { return _models.size(); } + const MatModel& getMatModel(unsigned i) const + { return _models[i]; } + +private: + MatModelList _models; + float min_x; + float max_x; + float min_y; + float max_y; + float min_z; + float max_z; +}; + +#endif diff --git a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx index dcf78e1c..bec81978 100644 --- a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx +++ b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx @@ -30,6 +30,8 @@ #include #include "SGTriangleBin.hxx" + + struct SGVertNormTex { SGVertNormTex() { } @@ -94,6 +96,11 @@ protected: class SGTexturedTriangleBin : public SGTriangleBin { public: + SGTexturedTriangleBin() + { + seed = new mt; + mt_init(seed, 123); + } // Computes and adds random surface points to the points list. // The random points are computed with a density of (coverage points)/1 @@ -118,13 +125,13 @@ public: // 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 + sg_random()*coverage; + 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 = sg_random(); - float b = sg_random(); + float a = mt_rand(seed); + float b = mt_rand(seed); if ( a + b > 1 ) { a = 1 - a; b = 1 - b; @@ -136,6 +143,43 @@ public: } } } + + void addRandomPoints(float coverage, + std::vector& points) const + { + unsigned num = getNumTriangles(); + for (unsigned i = 0; i < num; ++i) { + triangle_ref triangleRef = getTriangleRef(i); + SGVec3f v0 = getVertex(triangleRef[0]).vertex; + SGVec3f v1 = getVertex(triangleRef[1]).vertex; + SGVec3f v2 = getVertex(triangleRef[2]).vertex; + 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 an object being created + // for this triangle. + double num = area / coverage + mt_rand(seed); + + // place an object each unit of area + while ( num > 1.0 ) { + 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 = a*v0 + b*v1 + c*v2; + points.push_back(randomPoint); + num -= 1.0; + } + } + } osg::Geometry* buildGeometry(const TriangleVector& triangles) const { @@ -196,6 +240,10 @@ public: osg::Geometry* buildGeometry() const { return buildGeometry(getTriangles()); } + +private: + // Random seed for the triangle. + mt* seed; }; #endif diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index 1f6cfd27..4e904415 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -49,15 +49,18 @@ #include #include #include +#include #include #include #include "SGTexturedTriangleBin.hxx" #include "SGLightBin.hxx" +#include "SGModelBin.hxx" #include "SGDirectionalLightBin.hxx" #include "GroundLightManager.hxx" +#include "userdata.hxx" #include "pt_lights.hxx" using namespace simgear; @@ -76,7 +79,8 @@ struct SGTileGeometryBin { SGDirectionalLightListBin rabitLights; SGLightListBin odalLights; SGDirectionalLightListBin reilLights; - + SGMatModelBin randomModels; + static SGVec4f getMaterialLightColor(const SGMaterial* material) { @@ -397,6 +401,11 @@ struct SGTileGeometryBin { void computeRandomSurfaceLights(SGMaterialLib* matlib) { SGMaterialTriangleMap::const_iterator i; + + // generate a repeatable random seed + mt* seed = new mt; + mt_init(seed, unsigned(123)); + for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { SGMaterial *mat = matlib->find(i->first); if (!mat) @@ -411,16 +420,13 @@ struct SGTileGeometryBin { coverage = 10000; } - // generate a repeatable random seed - sg_srandom(unsigned(coverage)); - std::vector randomPoints; i->second.addRandomSurfacePoints(coverage, 3, randomPoints); std::vector::iterator j; for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { - float zombie = sg_random(); + float zombie = mt_rand(seed); // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0 - float factor = sg_random(); + float factor = mt_rand(seed); factor *= factor; float bright = 1; @@ -442,6 +448,44 @@ struct SGTileGeometryBin { } } } + + void computeRandomObjects(SGMaterialLib* matlib) + { + SGMaterialTriangleMap::const_iterator i; + for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { + SGMaterial *mat = matlib->find(i->first); + if (!mat) + continue; + + int group_count = mat->get_object_group_count(); + + if (group_count > 0) + { + for (int j = 0; j < group_count; j++) + { + SGMatModelGroup *object_group = mat->get_object_group(j); + int nObjects = object_group->get_object_count(); + + if (nObjects > 0) + { + // For each of the random models in the group, determine an appropriate + // number of random placements and insert them. + for (int k = 0; k < nObjects; k++) { + SGMatModel * object = object_group->get_object(k); + + std::vector randomPoints; + + i->second.addRandomPoints(object->get_coverage_m2(), randomPoints); + std::vector::iterator l; + for (l = randomPoints.begin(); l != randomPoints.end(); ++l) { + randomModels.insert(*l, object); + } + } + } + } + } + } + } bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib) { @@ -456,25 +500,79 @@ struct SGTileGeometryBin { osg::Node* SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects) { - SGBinObject obj; - if (!obj.read_bin(path)) + SGBinObject tile; + if (!tile.read_bin(path)) return false; SGTileGeometryBin tileGeometryBin; - if (!tileGeometryBin.insertBinObj(obj, matlib)) + if (!tileGeometryBin.insertBinObj(tile, matlib)) return false; - SGVec3d center = obj.get_gbs_center2(); + SGVec3d center = tile.get_gbs_center2(); SGGeod geodPos = SGGeod::fromCart(center); SGQuatd hlOr = SGQuatd::fromLonLat(geodPos); SGVec3f up = toVec3f(hlOr.backTransform(SGVec3d(0, 0, -1))); + osg::Matrix world2Tile(-hlOr.osg()); GroundLightManager* lightManager = GroundLightManager::instance(); osg::ref_ptr lightGroup = new SGOffsetTransform(0.94); + osg::ref_ptr randomObjects; osg::Group* terrainGroup = new osg::Group; + osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib); if (node) terrainGroup->addChild(node); + + if (use_random_objects) { + tileGeometryBin.computeRandomObjects(matlib); + + if (tileGeometryBin.randomModels.getNumModels() > 0) { + // Generate a repeatable random seed + mt* seed = new mt; + mt_init(seed, unsigned(123)); + + // Determine an rotation matrix for the models to place them + // perpendicular to the earth's surface. We use the same matrix, + // based on the centre of the tile, as the small angular differences + // between different points on the tile aren't worth worrying about + // for random objects. We also need to flip the orientation 180 degrees + static const osg::Matrix flip(1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1); + osg::Matrix mAtt = flip * osg::Matrix::rotate(hlOr.osg()); + std::vector > models; + + for (unsigned int i = 0; i < tileGeometryBin.randomModels.getNumModels(); i++) { + SGMatModelBin::MatModel obj = tileGeometryBin.randomModels.getMatModel(i); + osg::Node* node = sgGetRandomModel(obj.model); + + // Create a matrix to place the object in the correct location, and then + // apply the rotation matrix created above, with an additional random + // heading rotation if appropriate. + osg::Matrix mPos = osg::Matrix::translate(obj.position.osg()); + osg::MatrixTransform* position; + + if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) { + // Rotate the object around the z axis. + double hdg = mt_rand(seed) * M_PI * 2; + osg::Matrix rot(cos(hdg), -sin(hdg), 0, 0, + sin(hdg), cos(hdg), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + position = new osg::MatrixTransform(rot * mAtt * mPos); + } else { + position = new osg::MatrixTransform(mAtt * mPos); + } + + position->addChild(node); + models.push_back(position); + // Add to the leaf of the quadtree based on object location. + } + randomObjects = QuadTreeBuilder::makeQuadTree(models, world2Tile); + randomObjects->setName("random objects"); + } + } if (calc_lights) { // FIXME: ugly, has a side effect @@ -590,5 +688,17 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool lightLOD->setNodeMask(nodeMask); transform->addChild(lightLOD); } + + if (randomObjects.valid()) { + + // Add a LoD node, so we don't try to display anything when the tile center + // is more than 20km away. + osg::LOD* objectLOD = new osg::LOD; + objectLOD->addChild(randomObjects.get(), 0, 20000); + unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT; + objectLOD->setNodeMask(nodeMask); + transform->addChild(objectLOD); + } + return transform; } diff --git a/simgear/scene/tgdb/userdata.cxx b/simgear/scene/tgdb/userdata.cxx index 9e2b3f1f..c1956edd 100644 --- a/simgear/scene/tgdb/userdata.cxx +++ b/simgear/scene/tgdb/userdata.cxx @@ -34,6 +34,7 @@ #include #include +#include "SGModelBin.hxx" #include "userdata.hxx" #include "SGReaderWriterBTG.hxx" @@ -66,6 +67,13 @@ void sgUserDataInit( SGModelLib *m, const string &r, sim_time_sec = t; } + osg::Node* sgGetRandomModel(SGMatModel *obj) { + return obj->get_random_model(modellib, model_root, root_props, sim_time_sec); + } + +osg::Node* sgGetModel(int i, SGMatModel *obj) { + return obj->get_model(i, modellib, model_root, root_props, sim_time_sec); + } static void random_pt_inside_tri( float *res, float *n1, float *n2, float *n3 ) diff --git a/simgear/scene/tgdb/userdata.hxx b/simgear/scene/tgdb/userdata.hxx index 7001e995..d60d54a4 100644 --- a/simgear/scene/tgdb/userdata.hxx +++ b/simgear/scene/tgdb/userdata.hxx @@ -52,6 +52,15 @@ class SGPropertyNode; void sgUserDataInit( SGModelLib *m, const string &r, SGPropertyNode *p, double t ); +/** + * Get a random model. + */ +osg::Node* sgGetRandomModel(SGMatModel *obj); + +/** + * Get a specific model. + */ +osg::Node* sgGetModel(int i, SGMatModel *obj); /** * User data for populating leaves when they come in range. diff --git a/simgear/scene/util/Makefile.am b/simgear/scene/util/Makefile.am index 48218f07..121900a5 100644 --- a/simgear/scene/util/Makefile.am +++ b/simgear/scene/util/Makefile.am @@ -14,6 +14,7 @@ include_HEADERS = \ SGStateAttributeVisitor.hxx \ SGTextureStateAttributeVisitor.hxx \ SGUpdateVisitor.hxx \ + QuadTreeBuilder.hxx \ RenderConstants.hxx \ StateAttributeFactory.hxx \ VectorArrayAdapter.hxx @@ -25,6 +26,7 @@ libsgutil_a_SOURCES = \ SGSceneUserData.cxx \ SGStateAttributeVisitor.cxx \ SGTextureStateAttributeVisitor.cxx \ - StateAttributeFactory.cxx + StateAttributeFactory.cxx \ + QuadTreeBuilder.cxx INCLUDES = -I$(top_srcdir) diff --git a/simgear/scene/util/QuadTreeBuilder.cxx b/simgear/scene/util/QuadTreeBuilder.cxx new file mode 100644 index 00000000..4fe230c5 --- /dev/null +++ b/simgear/scene/util/QuadTreeBuilder.cxx @@ -0,0 +1,76 @@ +// Copyright (C) 2008 Tim Moore +// +// 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. + +#include +#include + +#include "QuadTreeBuilder.hxx" + +using namespace std; +using namespace osg; + +namespace simgear +{ +QuadTreeBuilder::QuadTreeBuilder(const Vec2& min, const Vec2& max) : + _root(new osg::Group), _min(min), _max(max) +{ + for (int i = 0; i < 4; ++i) { + Group* interior = new osg::Group; + _root->addChild(interior); + for (int j = 0; j < 4; ++j) { + Group* leaf = new osg::Group; + interior->addChild(leaf); + _leaves[i][j] = leaf; + } + } +} + +void QuadTreeBuilder::addNode(Node* node, const Matrix& transform) +{ + Vec3 center = node->getBound().center() * transform; + + int x = (int)(4.0 * (center.x() - _min.x()) / (_max.x() - _min.x())); + x = clampTo(x, 0, 3); + int y = (int)(4.0 * (center.y() - _min.y()) / (_max.y() - _min.y())); + y = clampTo(y, 0, 3); + _leaves[y][x]->addChild(node); +} + +osg::Group* QuadTreeBuilder::makeQuadTree(vector >& nodes, + const Matrix& transform) +{ + typedef vector > NodeList; + BoundingBox extents; + for (NodeList::iterator iter = nodes.begin(); iter != nodes.end(); ++iter) { + const Vec3 center = (*iter)->getBound().center() * transform; + extents.expandBy(center); + } + const Vec2 quadMin(extents.xMin(), extents.yMin()); + const Vec2 quadMax(extents.xMax(), extents.yMax()); + ref_ptr result; + { + QuadTreeBuilder quadTree(quadMin, quadMax); + for (NodeList::iterator iter = nodes.begin(); + iter != nodes.end(); + ++iter) { + quadTree.addNode(iter->get(), transform); + } + result = quadTree.getRoot(); + } + return result.release(); +} + +} diff --git a/simgear/scene/util/QuadTreeBuilder.hxx b/simgear/scene/util/QuadTreeBuilder.hxx new file mode 100644 index 00000000..6a0e2dd1 --- /dev/null +++ b/simgear/scene/util/QuadTreeBuilder.hxx @@ -0,0 +1,46 @@ +// Copyright (C) 2008 Tim Moore +// +// 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. +#ifndef SIMGEAR_QUADTREEBUILDER_HXX +#define SIMGEAR_QUADTREEBUILDER_HXX 1 + +#include +#include +#include +#include +#include +#include + +namespace simgear +{ +// Create a quad tree based on x, y extents +class QuadTreeBuilder { +public: + QuadTreeBuilder(const osg::Vec2& min, const osg::Vec2& max); + ~QuadTreeBuilder() {} + osg::Group* getRoot() { return _root.get(); } + // Add node to the quadtree using its x, y + void addNode(osg::Node* node, const osg::Matrix& transform); + // Make a quadtree of nodes from a vector of nodes + static osg::Group* makeQuadTree(std::vector >& nodes, + const osg::Matrix& transform); +protected: + osg::ref_ptr _root; + osg::Group* _leaves[4][4]; + osg::Vec2 _min; + osg::Vec2 _max; +}; +} +#endif -- 2.39.5