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. +/* + * "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. + */ - Any feedback is very welcome. - http://www.math.keio.ac.jp/matumoto/emt.html - email: matumoto@math.keio.ac.jp -*/ #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)) +// Structure for the random number functions. +mt random_seed; -static unsigned long state[N]; /* the array for the state vector */ -static int left = 1; -static int initf = 0; -static unsigned long *next; +#define MT(i) mt->array[i] -/* initializes state[N] with a seed */ -void init_genrand(unsigned long s) +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; + 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; +} - /* 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]); - - *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