From 29268401b2bfcbcb6d85ea3dac8b8b244366ce1b Mon Sep 17 00:00:00 2001 From: david Date: Sat, 20 Jul 2002 14:56:37 +0000 Subject: [PATCH] Randomly-place object overhaul and enhancements ----------------------------------------------- Fixed a segfault on exit. Changed the radius of the dummy bounding sphere from 10m to 1000m to ensure that FOV culling doesn't leave anything out. Allow an object to have more than one variant model, which will be chosen randomly. Simply repeat the ... property. Removed the property and replaced it with , which can be set to "fixed" (leave the model oriented as it is), "random" (give the model a random heading between 0 and 359 deg), or "billboard" (always turn the model to face the camera). The default is "fixed". Models look much better when they are not all facing the same direction. Allow the user to group models with the same visual range, so that there can be *many* fewer nodes in the scene graph when the models are not visible. This causes an XML-format change, so that instead of ... ... ... ... ... we now have ... ... ... ... Every object in a group can still have its own model(s), coverage, and heading-type, but they all share the same range selector. This change should already help users with tight memory constraints, but it will matter much more when we add more object types -- for example, we can now add dozens of different urban building types without bloating the scene graph or slowing down the LOD tests for tris that are out of range (i.e. most of them). --- src/Objects/newmat.cxx | 156 ++++++++++++++++++++++-------- src/Objects/newmat.hxx | 47 ++++++--- src/Objects/obj.cxx | 214 +++++++++++++++++++++-------------------- 3 files changed, 262 insertions(+), 155 deletions(-) diff --git a/src/Objects/newmat.cxx b/src/Objects/newmat.cxx index 8a3da39e4..d40c6ccd3 100644 --- a/src/Objects/newmat.cxx +++ b/src/Objects/newmat.cxx @@ -37,6 +37,7 @@ SG_USING_STD(map); #endif #include +#include #include #include @@ -95,12 +96,17 @@ local_file_exists( const string& path ) { // Implementation of FGNewMat::Object. //////////////////////////////////////////////////////////////////////// -FGNewMat::Object::Object (const SGPropertyNode * node) - : _path(node->getStringValue("path")), - _model(0), +FGNewMat::Object::Object (const SGPropertyNode * node, double range_m) + : _models_loaded(false), _coverage_m2(node->getDoubleValue("coverage-m2", 100000)), - _range_m(node->getDoubleValue("range-m", 2000)) + _range_m(range_m) { + // Note all the model paths + vector path_nodes = node->getChildren("path"); + for (int i = 0; i < path_nodes.size(); i++) + _paths.push_back(path_nodes[i]->getStringValue()); + + // Note the heading type string hdg = node->getStringValue("heading-type", "fixed"); if (hdg == "fixed") { _heading_type = HEADING_FIXED; @@ -117,41 +123,67 @@ FGNewMat::Object::Object (const SGPropertyNode * node) FGNewMat::Object::~Object () { - _model->deRef(); + for (int i = 0; i < _models.size(); i++) { + if (_models[i] != 0) { + _models[i]->deRef(); + _models[i] = 0; + } + } } -const string & -FGNewMat::Object::get_path () const +int +FGNewMat::Object::get_model_count () const { - return _path; + return _models.size(); } -ssgEntity * -FGNewMat::Object::get_model () const +inline void +FGNewMat::Object::load_models () const { // Load model only on demand - if (_model == 0) { - SGPath path = globals->get_fg_root(); - path.append(_path); - ssgTexturePath((char *)path.dir().c_str()); - ssgEntity * entity = load_object((char *)path.c_str()); - if (entity != 0) { - float ranges[] = {0, _range_m}; - _model = new ssgRangeSelector; - ((ssgRangeSelector *)_model)->setRanges(ranges, 2); - if (_heading_type == HEADING_BILLBOARD) { - ssgCutout * cutout = new ssgCutout(false); - cutout->addKid(entity); - ((ssgBranch *)_model)->addKid(cutout); + if (!_models_loaded) { + for (int i = 0; i < _paths.size(); i++) { + SGPath path = globals->get_fg_root(); + path.append(_paths[i]); + ssgTexturePath((char *)path.dir().c_str()); + ssgEntity * entity = load_object((char *)path.c_str()); + if (entity != 0) { + float ranges[] = {0, _range_m}; + ssgRangeSelector * lod = new ssgRangeSelector; + lod->setRanges(ranges, 2); + if (_heading_type == HEADING_BILLBOARD) { + ssgCutout * cutout = new ssgCutout(false); + cutout->addKid(entity); + lod->addKid(cutout); + } else { + lod->addKid(entity); + } + lod->ref(); + _models.push_back(lod); } else { - ((ssgBranch *)_model)->addKid(entity); + SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << path.str()); } - _model->ref(); - } else { - SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << path.str()); } } - return _model; + _models_loaded = true; +} + +ssgEntity * +FGNewMat::Object::get_model (int index) const +{ + load_models(); + return _models[index]; +} + +ssgEntity * +FGNewMat::Object::get_random_model () const +{ + load_models(); + int nModels = _models.size(); + int index = int(sg_random() * nModels); + if (index >= nModels) + index = 0; + return _models[index]; } double @@ -160,12 +192,59 @@ FGNewMat::Object::get_coverage_m2 () const return _coverage_m2; } +FGNewMat::Object::HeadingType +FGNewMat::Object::get_heading_type () const +{ + return _heading_type; +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGNewMat::ObjectGroup. +//////////////////////////////////////////////////////////////////////// + +FGNewMat::ObjectGroup::ObjectGroup (SGPropertyNode * node) + : _range_m(node->getDoubleValue("range-m", 2000)) +{ + // Load the object subnodes + vector object_nodes = + ((SGPropertyNode *)node)->getChildren("object"); + for (unsigned int i = 0; i < object_nodes.size(); i++) { + const SGPropertyNode * object_node = object_nodes[i]; + if (object_node->hasChild("path")) + _objects.push_back(new Object(object_node, _range_m)); + else + SG_LOG(SG_INPUT, SG_ALERT, "No path supplied for object"); + } +} + +FGNewMat::ObjectGroup::~ObjectGroup () +{ + for (int i = 0; i < _objects.size(); i++) { + delete _objects[i]; + _objects[i] = 0; + } +} + double -FGNewMat::Object::get_range_m () const +FGNewMat::ObjectGroup::get_range_m () const { return _range_m; } +int +FGNewMat::ObjectGroup::get_object_count () const +{ + return _objects.size(); +} + +FGNewMat::Object * +FGNewMat::ObjectGroup::get_object (int index) const +{ + return _objects[index]; +} + //////////////////////////////////////////////////////////////////////// @@ -195,9 +274,9 @@ FGNewMat::FGNewMat (ssgSimpleState * s) FGNewMat::~FGNewMat (void) { - for (unsigned int i = 0; i < objects.size(); i++) { - delete objects[i]; - objects[i] = 0; + for (unsigned int i = 0; i < object_groups.size(); i++) { + delete object_groups[i]; + object_groups[i] = 0; } } @@ -249,15 +328,10 @@ FGNewMat::read_properties (const SGPropertyNode * props) emission[2] = props->getDoubleValue("emissive/b", 0.0); emission[3] = props->getDoubleValue("emissive/a", 0.0); - vector object_nodes = - ((SGPropertyNode *)props)->getChildren("object"); - for (unsigned int i = 0; i < object_nodes.size(); i++) { - const SGPropertyNode * object_node = object_nodes[i]; - if (object_node->hasChild("path")) - objects.push_back(new Object(object_node)); - else - SG_LOG(SG_INPUT, SG_ALERT, "No path supplied for object"); - } + vector object_group_nodes = + ((SGPropertyNode *)props)->getChildren("object-group"); + for (unsigned int i = 0; i < object_group_nodes.size(); i++) + object_groups.push_back(new ObjectGroup(object_group_nodes[i])); } diff --git a/src/Objects/newmat.hxx b/src/Objects/newmat.hxx index 506051788..c80382440 100644 --- a/src/Objects/newmat.hxx +++ b/src/Objects/newmat.hxx @@ -66,9 +66,10 @@ public: ////////////////////////////////////////////////////////////////////// - // Inner class. + // Inner classes. ////////////////////////////////////////////////////////////////////// + class ObjectGroup; /** * A randomly-placeable object. @@ -83,24 +84,46 @@ public: HEADING_RANDOM }; - const string &get_path () const; - ssgEntity * get_model () const; + int get_model_count () const; + ssgEntity * get_model (int index) const; + ssgEntity * get_random_model () const; double get_coverage_m2 () const; - double get_range_m () const; HeadingType get_heading_type () const; protected: - friend class FGNewMat; - Object (const SGPropertyNode * node); + friend class ObjectGroup; + Object (const SGPropertyNode * node, double range_m); virtual ~Object (); private: - string _path; - mutable ssgEntity * _model; + void load_models () const; + vector _paths; + mutable vector _models; + mutable bool _models_loaded; double _coverage_m2; double _range_m; HeadingType _heading_type; }; + /** + * A collection of related objects with the same visual range. + */ + class ObjectGroup + { + public: + virtual ~ObjectGroup (); + double get_range_m () const; + int get_object_count () const; + Object * get_object (int index) const; + protected: + friend class FGNewMat; + ObjectGroup (SGPropertyNode * node); + private: + double _range_m; + vector _objects; + }; + + + //////////////////////////////////////////////////////////////////// // Public Constructors. @@ -187,13 +210,15 @@ public: /** * Get the number of randomly-placed objects defined for this material. */ - virtual int get_object_count () const { return objects.size(); } + virtual int get_object_group_count () const { return object_groups.size(); } /** * Get a randomly-placed object for this material. */ - virtual Object * get_object (int index) const { return objects[index]; } + virtual ObjectGroup * get_object_group (int index) const { + return object_groups[index]; + } /** @@ -270,7 +295,7 @@ private: // true if texture loading deferred, and not yet loaded bool texture_loaded; - vector objects; + vector object_groups; // ref count so we can properly delete if we have multiple // pointers to this record diff --git a/src/Objects/obj.cxx b/src/Objects/obj.cxx index f0b2e6e24..88ad034ba 100644 --- a/src/Objects/obj.cxx +++ b/src/Objects/obj.cxx @@ -313,6 +313,54 @@ static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights, } +/** + * Create a rotation matrix to align an object for the current lat/lon. + * + * By default, objects are aligned for the north pole. This code + * calculates a matrix to rotate them for the surface of the earth in + * the current location. + * + * TODO: there should be a single version of this method somewhere + * for all of SimGear. + * + * @param ROT The resulting rotation matrix. + * @param hdg_deg The object heading in degrees. + * @param lon_deg The longitude in degrees. + * @param lat_deg The latitude in degrees. + */ +static void +makeWorldUpRotationMatrix (sgMat4 ROT, double hdg_deg, + double lon_deg, double lat_deg) +{ + SGfloat sin_lat = sin( lat_deg * SGD_DEGREES_TO_RADIANS ); + SGfloat cos_lat = cos( lat_deg * SGD_DEGREES_TO_RADIANS ); + SGfloat sin_lon = sin( lon_deg * SGD_DEGREES_TO_RADIANS ); + SGfloat cos_lon = cos( lon_deg * SGD_DEGREES_TO_RADIANS ); + SGfloat sin_hdg = sin( hdg_deg * SGD_DEGREES_TO_RADIANS ) ; + SGfloat cos_hdg = cos( hdg_deg * SGD_DEGREES_TO_RADIANS ) ; + + ROT[0][0] = cos_hdg * sin_lat * cos_lon - sin_hdg * sin_lon; + ROT[0][1] = cos_hdg * sin_lat * sin_lon + sin_hdg * cos_lon; + ROT[0][2] = -cos_hdg * cos_lat; + ROT[0][3] = SG_ZERO; + + ROT[1][0] = -sin_hdg * sin_lat * cos_lon - cos_hdg * sin_lon; + ROT[1][1] = -sin_hdg * sin_lat * sin_lon + cos_hdg * cos_lon; + ROT[1][2] = sin_hdg * cos_lat; + ROT[1][3] = SG_ZERO; + + ROT[2][0] = cos_lat * cos_lon; + ROT[2][1] = cos_lat * sin_lon; + ROT[2][2] = sin_lat; + ROT[2][3] = SG_ZERO; + + ROT[3][0] = SG_ZERO; + ROT[3][1] = SG_ZERO; + ROT[3][2] = SG_ZERO; + ROT[3][3] = SG_ONE ; +} + + /** * Add an object to a random location inside a triangle. * @@ -320,20 +368,27 @@ static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights, * @param p2 The second vertex of the triangle. * @param p3 The third vertex of the triangle. * @param center The center of the triangle. - * @param ROT The world-up rotation matrix. - * @param mat The material object. - * @param object_index The index of the dynamically-placed object in - * the material. + * @param lon_deg The longitude of the surface center, in degrees. + * @param lat_deg The latitude of the surface center, in degrees. + * @param object The randomly-placed object. * @param branch The branch where the object should be added to the * scene graph. */ static void add_object_to_triangle (sgVec3 p1, sgVec3 p2, sgVec3 p3, sgVec3 center, - sgMat4 ROT, FGNewMat::Object * object, - ssgBranch * branch) + double lon_deg, double lat_deg, + FGNewMat::Object * object, ssgBranch * branch) { + // Set up the random heading if required. + double hdg_deg = 0; + if (object->get_heading_type() == FGNewMat::Object::HEADING_RANDOM) + hdg_deg = sg_random() * 360; + sgVec3 result; + sgMat4 ROT; + makeWorldUpRotationMatrix(ROT, hdg_deg, lon_deg, lat_deg); + random_pt_inside_tri(result, p1, p2, p3); sgSubVec3(result, center); sgMat4 OBJ_pos, OBJ; @@ -342,7 +397,7 @@ add_object_to_triangle (sgVec3 p1, sgVec3 p2, sgVec3 p3, sgVec3 center, sgPostMultMat4(OBJ, OBJ_pos); ssgTransform * pos = new ssgTransform; pos->setTransform(OBJ); - pos->addKid(object->get_model()); + pos->addKid(object->get_random_model()); branch->addKid(pos); } @@ -353,9 +408,10 @@ public: float * p1; float * p2; float * p3; - FGNewMat::Object * object; + FGNewMat::ObjectGroup * object_group; ssgBranch * branch; - sgMat4 ROT; + double lon_deg; + double lat_deg; }; @@ -371,33 +427,40 @@ public: * @param mat The triangle's material. * @param object_index The index of the random object in the triangle. * @param branch The branch where the objects should be added. - * @param ROT The rotation matrix to align objects with the earth's - * surface. + * @param lon_deg The longitude of the surface center, in degrees. + * @param lat_deg The latitude of the surface center, in degrees. */ static void fill_in_triangle (float * p1, float * p2, float * p3, - FGNewMat::Object *object, ssgBranch * branch, sgMat4 ROT) + FGNewMat::ObjectGroup * object_group, ssgBranch * branch, + double lon_deg, double lat_deg) { - sgVec3 center; - sgSetVec3(center, - (p1[0] + p2[0] + p3[0]) / 3.0, - (p1[1] + p2[1] + p3[1]) / 3.0, - (p1[2] + p2[2] + p3[2]) / 3.0); - double area = sgTriArea(p1, p2, p3); - double num = area / object->get_coverage_m2(); - - // place an object each unit of area - while ( num > 1.0 ) { - add_object_to_triangle(p1, p2, p3, center, ROT, object, branch); - num -= 1.0; - } - // for partial units of area, use a zombie door method to - // create the proper random chance of an object being created - // for this triangle - if ( num > 0.0 ) { - if ( sg_random() <= num ) { - // a zombie made it through our door - add_object_to_triangle(p1, p2, p3, center, ROT, object, branch); + int nObjects = object_group->get_object_count(); + for (int i = 0; i < nObjects; i++) { + FGNewMat::Object * object = object_group->get_object(i); + sgVec3 center; + sgSetVec3(center, + (p1[0] + p2[0] + p3[0]) / 3.0, + (p1[1] + p2[1] + p3[1]) / 3.0, + (p1[2] + p2[2] + p3[2]) / 3.0); + double area = sgTriArea(p1, p2, p3); + double num = area / object->get_coverage_m2(); + + // place an object each unit of area + while ( num > 1.0 ) { + add_object_to_triangle(p1, p2, p3, center, lon_deg, lat_deg, + object, branch); + num -= 1.0; + } + // for partial units of area, use a zombie door method to + // create the proper random chance of an object being created + // for this triangle + if ( num > 0.0 ) { + if ( sg_random() <= num ) { + // a zombie made it through our door + add_object_to_triangle(p1, p2, p3, center, lon_deg, lat_deg, + object, branch); + } } } } @@ -420,8 +483,8 @@ in_range_callback (ssgEntity * entity, int mask) { RandomObjectUserData * data = (RandomObjectUserData *)entity->getUserData(); if (!data->is_filled_in) { - fill_in_triangle(data->p1, data->p2, data->p3, data->object, - data->branch, data->ROT); + fill_in_triangle(data->p1, data->p2, data->p3, data->object_group, + data->branch, data->lon_deg, data->lat_deg); data->is_filled_in = true; } return 1; @@ -474,7 +537,7 @@ private: DummyBSphereEntity () { bsphere.setCenter(0, 0, 0); - bsphere.setRadius(10); + bsphere.setRadius(1000); } static DummyBSphereEntity * entity; }; @@ -532,12 +595,13 @@ get_bounding_radius (sgVec3 center, float *p1, float *p2, float *p3) * @param mat The material data for the triangle. * @param branch The branch to which the randomly-placed objects * should be added. - * @param ROT A rotation matrix to align the objects with the earth's - * surface at the current lat/lon. + * @param lon_deg The longitude of the surface center, in degrees. + * @param lat_deg The latitude of the surface center, in degrees. */ static void setup_triangle (float * p1, float * p2, float * p3, - FGNewMat * mat, ssgBranch * branch, sgMat4 ROT) + FGNewMat * mat, ssgBranch * branch, + double lon_deg, double lat_deg) { // Set up a single center point for LOD sgVec3 center; @@ -559,17 +623,17 @@ setup_triangle (float * p1, float * p2, float * p3, branch->addKid(location); // Iterate through all the object types. - int num_objects = mat->get_object_count(); - for (int i = 0; i < num_objects; i++) { + int num_groups = mat->get_object_group_count(); + for (int i = 0; i < num_groups; i++) { // Look up the random object. - FGNewMat::Object * object = mat->get_object(i); + FGNewMat::ObjectGroup * group = mat->get_object_group(i); // Set up the range selector for the entire // triangle; note that we use the object // range plus the bounding radius here, to // allow for objects far from the center. float ranges[] = {0, - object->get_range_m() + bounding_radius, + group->get_range_m() + bounding_radius, 500000}; ssgRangeSelector * lod = new ssgRangeSelector; lod->setRanges(ranges, 3); @@ -588,9 +652,10 @@ setup_triangle (float * p1, float * p2, float * p3, data->p1 = p1; data->p2 = p2; data->p3 = p3; - data->object = object; + data->object_group = group; data->branch = in_range; - sgCopyMat4(data->ROT, ROT); + data->lon_deg = lon_deg; + data->lat_deg = lat_deg; // Set up the in-range node. in_range->setUserData(data); @@ -608,54 +673,6 @@ setup_triangle (float * p1, float * p2, float * p3, } -/** - * Create a rotation matrix to align an object for the current lat/lon. - * - * By default, objects are aligned for the north pole. This code - * calculates a matrix to rotate them for the surface of the earth in - * the current location. - * - * TODO: there should be a single version of this method somewhere - * for all of SimGear. - * - * @param ROT The resulting rotation matrix. - * @param hdg_deg The object heading in degrees. - * @param lon_deg The longitude in degrees. - * @param lat_deg The latitude in degrees. - */ -void -makeWorldUpRotationMatrix (sgMat4 ROT, double hdg_deg, - double lon_deg, double lat_deg) -{ - SGfloat sin_lat = sin( lat_deg * SGD_DEGREES_TO_RADIANS ); - SGfloat cos_lat = cos( lat_deg * SGD_DEGREES_TO_RADIANS ); - SGfloat sin_lon = sin( lon_deg * SGD_DEGREES_TO_RADIANS ); - SGfloat cos_lon = cos( lon_deg * SGD_DEGREES_TO_RADIANS ); - SGfloat sin_hdg = sin( hdg_deg * SGD_DEGREES_TO_RADIANS ) ; - SGfloat cos_hdg = cos( hdg_deg * SGD_DEGREES_TO_RADIANS ) ; - - ROT[0][0] = cos_hdg * sin_lat * cos_lon - sin_hdg * sin_lon; - ROT[0][1] = cos_hdg * sin_lat * sin_lon + sin_hdg * cos_lon; - ROT[0][2] = -cos_hdg * cos_lat; - ROT[0][3] = SG_ZERO; - - ROT[1][0] = -sin_hdg * sin_lat * cos_lon - cos_hdg * sin_lon; - ROT[1][1] = -sin_hdg * sin_lat * sin_lon + cos_hdg * cos_lon; - ROT[1][2] = sin_hdg * cos_lat; - ROT[1][3] = SG_ZERO; - - ROT[2][0] = cos_lat * cos_lon; - ROT[2][1] = cos_lat * sin_lon; - ROT[2][2] = sin_lat; - ROT[2][3] = SG_ZERO; - - ROT[3][0] = SG_ZERO; - ROT[3][1] = SG_ZERO; - ROT[3][2] = SG_ZERO; - ROT[3][3] = SG_ONE ; -} - - /** * Randomly place objects on a surface. * @@ -677,8 +694,6 @@ gen_random_surface_objects (ssgLeaf *leaf, float lat_deg, const string &material_name) { - float hdg_deg = 0.0; // do something here later - // First, look up the material // for this surface. FGNewMat * mat = material_lib.find(material_name); @@ -689,8 +704,7 @@ gen_random_surface_objects (ssgLeaf *leaf, // If the material has no randomly-placed // objects, return now. - int num_objects = mat->get_object_count(); - if (num_objects < 1) + if (mat->get_object_group_count() < 1) return; // If the surface has no triangles, return @@ -699,12 +713,6 @@ gen_random_surface_objects (ssgLeaf *leaf, if (num_tris < 1) return; - // Make a rotation matrix to align the - // object for this point on the earth's - // surface. - sgMat4 ROT; - makeWorldUpRotationMatrix(ROT, hdg_deg, lon_deg, lat_deg); - // generate a repeatable random seed sg_srandom((unsigned int)(leaf->getVertex(0)[0])); @@ -716,7 +724,7 @@ gen_random_surface_objects (ssgLeaf *leaf, setup_triangle(leaf->getVertex(n1), leaf->getVertex(n2), leaf->getVertex(n3), - mat, branch, ROT); + mat, branch, lon_deg, lat_deg); } } -- 2.39.5