2003-05-14 18:33:56 +00:00
|
|
|
|
// obj.cxx -- routines to handle loading scenery and building the plib
|
2003-05-06 14:20:30 +00:00
|
|
|
|
// scene graph.
|
1998-08-25 16:51:22 +00:00
|
|
|
|
//
|
|
|
|
|
// Written by Curtis Olson, started October 1997.
|
|
|
|
|
//
|
|
|
|
|
// Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com
|
|
|
|
|
//
|
|
|
|
|
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
//
|
|
|
|
|
// $Id$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
# include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2001-03-23 22:42:49 +00:00
|
|
|
|
#ifdef SG_MATH_EXCEPTION_CLASH
|
1999-05-08 02:33:13 +00:00
|
|
|
|
# include <math.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
1998-08-25 16:51:22 +00:00
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
2000-02-15 03:30:01 +00:00
|
|
|
|
#include <simgear/compiler.h>
|
2002-07-20 19:23:44 +00:00
|
|
|
|
#include <simgear/sg_inlines.h>
|
2001-01-05 01:07:10 +00:00
|
|
|
|
#include <simgear/io/sg_binobj.hxx>
|
1998-08-25 16:51:22 +00:00
|
|
|
|
|
1999-02-26 22:08:34 +00:00
|
|
|
|
#include STL_STRING
|
2002-08-05 20:14:28 +00:00
|
|
|
|
#include <map> // STL
|
|
|
|
|
#include <vector> // STL
|
|
|
|
|
#include <ctype.h> // isdigit()
|
1998-08-25 16:51:22 +00:00
|
|
|
|
|
2000-02-15 03:30:01 +00:00
|
|
|
|
#include <simgear/constants.h>
|
2000-02-16 23:01:03 +00:00
|
|
|
|
#include <simgear/debug/logstream.hxx>
|
|
|
|
|
#include <simgear/math/point3d.hxx>
|
|
|
|
|
#include <simgear/math/polar3d.hxx>
|
2000-09-27 20:16:22 +00:00
|
|
|
|
#include <simgear/math/sg_geodesy.hxx>
|
2000-12-04 05:23:06 +00:00
|
|
|
|
#include <simgear/math/sg_random.h>
|
2002-08-08 23:09:02 +00:00
|
|
|
|
#include <simgear/math/vector.hxx>
|
2001-03-25 14:20:12 +00:00
|
|
|
|
#include <simgear/misc/sgstream.hxx>
|
2000-02-16 23:01:03 +00:00
|
|
|
|
#include <simgear/misc/stopwatch.hxx>
|
|
|
|
|
#include <simgear/misc/texcoord.hxx>
|
2003-05-12 21:34:29 +00:00
|
|
|
|
#include <simgear/scene/material/mat.hxx>
|
|
|
|
|
#include <simgear/scene/material/matlib.hxx>
|
2003-05-14 19:22:24 +00:00
|
|
|
|
#include <simgear/scene/tgdb/leaf.hxx>
|
|
|
|
|
#include <simgear/scene/tgdb/pt_lights.hxx>
|
2000-02-15 03:30:01 +00:00
|
|
|
|
|
2000-10-19 21:24:43 +00:00
|
|
|
|
#include <Main/globals.hxx>
|
2001-01-13 22:06:39 +00:00
|
|
|
|
#include <Main/fg_props.hxx>
|
2002-07-15 18:16:20 +00:00
|
|
|
|
#include <Time/light.hxx>
|
1998-08-25 16:51:22 +00:00
|
|
|
|
|
2003-05-14 19:22:24 +00:00
|
|
|
|
|
1998-08-25 16:51:22 +00:00
|
|
|
|
#include "obj.hxx"
|
|
|
|
|
|
2001-03-23 22:59:18 +00:00
|
|
|
|
SG_USING_STD(string);
|
|
|
|
|
SG_USING_STD(vector);
|
1999-06-28 00:02:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef vector < int > int_list;
|
|
|
|
|
typedef int_list::iterator int_list_iterator;
|
|
|
|
|
typedef int_list::const_iterator int_point_list_iterator;
|
1999-03-02 01:02:31 +00:00
|
|
|
|
|
1998-08-25 16:51:22 +00:00
|
|
|
|
|
2002-10-21 16:26:48 +00:00
|
|
|
|
// not used because plib branches don't honor call backs.
|
2002-07-15 18:16:20 +00:00
|
|
|
|
static int
|
2002-11-16 11:56:15 +00:00
|
|
|
|
runway_lights_pretrav (ssgEntity * e, int mask)
|
2002-07-15 18:16:20 +00:00
|
|
|
|
{
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// Turn on lights only at night
|
2002-07-15 18:16:20 +00:00
|
|
|
|
float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
|
2002-11-25 21:42:07 +00:00
|
|
|
|
return int((sun_angle > 85.0) ||
|
2002-10-04 00:23:40 +00:00
|
|
|
|
(fgGetDouble("/environment/visibility-m") < 5000.0));
|
2002-07-15 18:16:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-08-25 16:51:22 +00:00
|
|
|
|
|
2002-03-03 20:29:31 +00:00
|
|
|
|
// Generate an ocean tile
|
|
|
|
|
bool fgGenTile( const string& path, SGBucket b,
|
2003-05-14 18:33:56 +00:00
|
|
|
|
Point3D *center, double *bounding_radius,
|
|
|
|
|
SGMaterialLib *matlib, ssgBranch* geometry )
|
2002-03-03 20:29:31 +00:00
|
|
|
|
{
|
1999-08-31 23:23:04 +00:00
|
|
|
|
ssgSimpleState *state = NULL;
|
|
|
|
|
|
2003-05-14 18:33:56 +00:00
|
|
|
|
geometry->setName( (char *)path.c_str() );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
|
2000-06-23 00:30:04 +00:00
|
|
|
|
double tex_width = 1000.0;
|
|
|
|
|
// double tex_height;
|
|
|
|
|
|
1999-08-31 23:23:04 +00:00
|
|
|
|
// find Ocean material in the properties list
|
2003-05-14 18:33:56 +00:00
|
|
|
|
SGMaterial *mat = matlib->find( "Ocean" );
|
|
|
|
|
if ( mat != NULL ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// set the texture width and height values for this
|
|
|
|
|
// material
|
2003-05-14 18:33:56 +00:00
|
|
|
|
tex_width = mat->get_xsize();
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// tex_height = newmat->get_ysize();
|
|
|
|
|
|
|
|
|
|
// set ssgState
|
2003-05-14 18:33:56 +00:00
|
|
|
|
state = mat->get_state();
|
2000-06-23 00:30:04 +00:00
|
|
|
|
} else {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
|
|
|
|
"Ack! unknown usemtl name = " << "Ocean"
|
|
|
|
|
<< " in " << path );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate center point
|
|
|
|
|
double clon = b.get_center_lon();
|
|
|
|
|
double clat = b.get_center_lat();
|
|
|
|
|
double height = b.get_height();
|
|
|
|
|
double width = b.get_width();
|
|
|
|
|
|
2002-03-03 20:29:31 +00:00
|
|
|
|
*center = sgGeodToCart( Point3D(clon*SGD_DEGREES_TO_RADIANS,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
clat*SGD_DEGREES_TO_RADIANS,
|
|
|
|
|
0.0) );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
// cout << "center = " << center << endl;;
|
|
|
|
|
|
|
|
|
|
// Caculate corner vertices
|
|
|
|
|
Point3D geod[4];
|
|
|
|
|
geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
|
|
|
|
|
geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
|
|
|
|
|
geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
|
|
|
|
|
geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
|
|
|
|
|
|
|
|
|
|
Point3D rad[4];
|
1999-09-28 22:44:08 +00:00
|
|
|
|
int i;
|
|
|
|
|
for ( i = 0; i < 4; ++i ) {
|
2002-02-28 00:10:35 +00:00
|
|
|
|
rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS,
|
|
|
|
|
geod[i].y() * SGD_DEGREES_TO_RADIANS,
|
|
|
|
|
geod[i].z() );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Point3D cart[4], rel[4];
|
1999-09-28 22:44:08 +00:00
|
|
|
|
for ( i = 0; i < 4; ++i ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
cart[i] = sgGeodToCart(rad[i]);
|
|
|
|
|
rel[i] = cart[i] - *center;
|
|
|
|
|
// cout << "corner " << i << " = " << cart[i] << endl;
|
1999-08-31 23:23:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate bounding radius
|
2002-03-03 20:29:31 +00:00
|
|
|
|
*bounding_radius = center->distance3D( cart[0] );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
// cout << "bounding radius = " << t->bounding_radius << endl;
|
|
|
|
|
|
|
|
|
|
// Calculate normals
|
|
|
|
|
Point3D normals[4];
|
1999-09-28 22:44:08 +00:00
|
|
|
|
for ( i = 0; i < 4; ++i ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
double length = cart[i].distance3D( Point3D(0.0) );
|
|
|
|
|
normals[i] = cart[i] / length;
|
|
|
|
|
// cout << "normal = " << normals[i] << endl;
|
1999-08-31 23:23:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate texture coordinates
|
1999-10-27 00:51:42 +00:00
|
|
|
|
point_list geod_nodes;
|
|
|
|
|
geod_nodes.clear();
|
2002-08-23 19:53:48 +00:00
|
|
|
|
geod_nodes.reserve(4);
|
1999-10-27 00:51:42 +00:00
|
|
|
|
int_list rectangle;
|
|
|
|
|
rectangle.clear();
|
2002-08-23 19:53:48 +00:00
|
|
|
|
rectangle.reserve(4);
|
1999-09-28 22:44:08 +00:00
|
|
|
|
for ( i = 0; i < 4; ++i ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
geod_nodes.push_back( geod[i] );
|
|
|
|
|
rectangle.push_back( i );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
}
|
1999-10-27 00:51:42 +00:00
|
|
|
|
point_list texs = calc_tex_coords( b, geod_nodes, rectangle,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
1000.0 / tex_width );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
|
1999-10-27 00:51:42 +00:00
|
|
|
|
// Allocate ssg structure
|
2000-02-20 05:19:15 +00:00
|
|
|
|
ssgVertexArray *vl = new ssgVertexArray( 4 );
|
|
|
|
|
ssgNormalArray *nl = new ssgNormalArray( 4 );
|
|
|
|
|
ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
|
2000-03-17 06:16:15 +00:00
|
|
|
|
ssgColourArray *cl = new ssgColourArray( 1 );
|
|
|
|
|
|
|
|
|
|
sgVec4 color;
|
|
|
|
|
sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
|
|
|
|
|
cl->add( color );
|
2000-02-20 05:19:15 +00:00
|
|
|
|
|
|
|
|
|
// sgVec3 *vtlist = new sgVec3 [ 4 ];
|
|
|
|
|
// t->vec3_ptrs.push_back( vtlist );
|
|
|
|
|
// sgVec3 *vnlist = new sgVec3 [ 4 ];
|
|
|
|
|
// t->vec3_ptrs.push_back( vnlist );
|
|
|
|
|
// sgVec2 *tclist = new sgVec2 [ 4 ];
|
|
|
|
|
// t->vec2_ptrs.push_back( tclist );
|
|
|
|
|
|
|
|
|
|
sgVec2 tmp2;
|
|
|
|
|
sgVec3 tmp3;
|
1999-09-28 22:44:08 +00:00
|
|
|
|
for ( i = 0; i < 4; ++i ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
sgSetVec3( tmp3,
|
|
|
|
|
rel[i].x(), rel[i].y(), rel[i].z() );
|
|
|
|
|
vl->add( tmp3 );
|
2000-02-20 05:19:15 +00:00
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
sgSetVec3( tmp3,
|
|
|
|
|
normals[i].x(), normals[i].y(), normals[i].z() );
|
|
|
|
|
nl->add( tmp3 );
|
2000-02-20 05:19:15 +00:00
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
sgSetVec2( tmp2, texs[i].x(), texs[i].y());
|
|
|
|
|
tl->add( tmp2 );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssgLeaf *leaf =
|
2002-08-05 20:14:28 +00:00
|
|
|
|
new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
|
2000-02-20 05:19:15 +00:00
|
|
|
|
|
1999-08-31 23:23:04 +00:00
|
|
|
|
leaf->setState( state );
|
|
|
|
|
|
2002-03-03 20:29:31 +00:00
|
|
|
|
geometry->addKid( leaf );
|
1999-08-31 23:23:04 +00:00
|
|
|
|
|
2002-03-03 20:29:31 +00:00
|
|
|
|
return true;
|
1999-08-31 23:23:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2000-12-04 22:35:38 +00:00
|
|
|
|
static void random_pt_inside_tri( float *res,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
float *n1, float *n2, float *n3 )
|
2000-12-04 22:35:38 +00:00
|
|
|
|
{
|
|
|
|
|
double a = sg_random();
|
|
|
|
|
double b = sg_random();
|
|
|
|
|
if ( a + b > 1.0 ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
a = 1.0 - a;
|
|
|
|
|
b = 1.0 - b;
|
2000-12-04 22:35:38 +00:00
|
|
|
|
}
|
|
|
|
|
double c = 1 - a - b;
|
2000-12-04 05:23:06 +00:00
|
|
|
|
|
2002-08-08 23:09:02 +00:00
|
|
|
|
res[0] = n1[0]*a + n2[0]*b + n3[0]*c;
|
|
|
|
|
res[1] = n1[1]*a + n2[1]*b + n3[1]*c;
|
|
|
|
|
res[2] = n1[2]*a + n2[2]*b + n3[2]*c;
|
2000-12-04 22:35:38 +00:00
|
|
|
|
}
|
2000-12-04 05:23:06 +00:00
|
|
|
|
|
|
|
|
|
|
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 <path>...</path> property.
Removed the <billboard> property and replaced it with <heading-type>,
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
<object>
<range-m>...</range-m>
...
</object>
<object>
<range-m>...</range-m>
...
</object>
...
we now have
<object-group>
<range-m>...</range-m>
<object>
...
</object>
<object>
...
</object>
...
</object-group>
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).
2002-07-20 14:56:37 +00:00
|
|
|
|
/**
|
2002-08-08 23:09:02 +00:00
|
|
|
|
* User data for populating leaves when they come in range.
|
2002-07-16 23:39:53 +00:00
|
|
|
|
*/
|
2002-08-08 23:09:02 +00:00
|
|
|
|
class LeafUserData : public ssgBase
|
2002-07-16 23:39:53 +00:00
|
|
|
|
{
|
2002-08-08 23:09:02 +00:00
|
|
|
|
public:
|
|
|
|
|
bool is_filled_in;
|
2003-05-14 18:33:56 +00:00
|
|
|
|
ssgLeaf *leaf;
|
|
|
|
|
SGMaterial *mat;
|
|
|
|
|
ssgBranch *branch;
|
2002-08-08 23:09:02 +00:00
|
|
|
|
float sin_lat;
|
|
|
|
|
float cos_lat;
|
|
|
|
|
float sin_lon;
|
|
|
|
|
float cos_lon;
|
|
|
|
|
|
|
|
|
|
void setup_triangle( int i );
|
|
|
|
|
};
|
2002-07-16 23:39:53 +00:00
|
|
|
|
|
2002-07-25 17:32:31 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* User data for populating triangles when they come in range.
|
|
|
|
|
*/
|
|
|
|
|
class TriUserData : public ssgBase
|
2002-07-18 01:04:08 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
bool is_filled_in;
|
|
|
|
|
float * p1;
|
|
|
|
|
float * p2;
|
|
|
|
|
float * p3;
|
2002-08-08 23:09:02 +00:00
|
|
|
|
sgVec3 center;
|
|
|
|
|
double area;
|
2003-05-13 03:18:42 +00:00
|
|
|
|
SGMaterial::ObjectGroup * object_group;
|
2002-07-18 01:04:08 +00:00
|
|
|
|
ssgBranch * branch;
|
2002-08-08 23:09:02 +00:00
|
|
|
|
LeafUserData * leafData;
|
2002-07-25 20:30:51 +00:00
|
|
|
|
unsigned int seed;
|
2002-08-08 23:09:02 +00:00
|
|
|
|
|
|
|
|
|
void fill_in_triangle();
|
2003-05-13 03:18:42 +00:00
|
|
|
|
void add_object_to_triangle(SGMaterial::Object * object);
|
2002-08-08 23:09:02 +00:00
|
|
|
|
void makeWorldMatrix (sgMat4 ROT, double hdg_deg );
|
2002-07-18 01:04:08 +00:00
|
|
|
|
};
|
|
|
|
|
|
2002-07-16 23:39:53 +00:00
|
|
|
|
|
2002-07-17 22:11:13 +00:00
|
|
|
|
/**
|
2002-07-18 01:04:08 +00:00
|
|
|
|
* Fill in a triangle with randomly-placed objects.
|
2002-07-17 22:11:13 +00:00
|
|
|
|
*
|
2002-07-18 01:04:08 +00:00
|
|
|
|
* This method is invoked by a callback when the triangle is in range
|
|
|
|
|
* but not yet populated.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2002-08-08 23:09:02 +00:00
|
|
|
|
|
|
|
|
|
void TriUserData::fill_in_triangle ()
|
2002-07-18 01:04:08 +00:00
|
|
|
|
{
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// generate a repeatable random seed
|
2002-07-25 20:30:51 +00:00
|
|
|
|
sg_srandom(seed);
|
2002-07-25 17:32:31 +00:00
|
|
|
|
|
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 <path>...</path> property.
Removed the <billboard> property and replaced it with <heading-type>,
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
<object>
<range-m>...</range-m>
...
</object>
<object>
<range-m>...</range-m>
...
</object>
...
we now have
<object-group>
<range-m>...</range-m>
<object>
...
</object>
<object>
...
</object>
...
</object-group>
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).
2002-07-20 14:56:37 +00:00
|
|
|
|
int nObjects = object_group->get_object_count();
|
2002-08-08 23:09:02 +00:00
|
|
|
|
|
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 <path>...</path> property.
Removed the <billboard> property and replaced it with <heading-type>,
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
<object>
<range-m>...</range-m>
...
</object>
<object>
<range-m>...</range-m>
...
</object>
...
we now have
<object-group>
<range-m>...</range-m>
<object>
...
</object>
<object>
...
</object>
...
</object-group>
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).
2002-07-20 14:56:37 +00:00
|
|
|
|
for (int i = 0; i < nObjects; i++) {
|
2003-05-13 03:18:42 +00:00
|
|
|
|
SGMaterial::Object * object = object_group->get_object(i);
|
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 <path>...</path> property.
Removed the <billboard> property and replaced it with <heading-type>,
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
<object>
<range-m>...</range-m>
...
</object>
<object>
<range-m>...</range-m>
...
</object>
...
we now have
<object-group>
<range-m>...</range-m>
<object>
...
</object>
<object>
...
</object>
...
</object-group>
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).
2002-07-20 14:56:37 +00:00
|
|
|
|
double num = area / object->get_coverage_m2();
|
|
|
|
|
|
|
|
|
|
// place an object each unit of area
|
|
|
|
|
while ( num > 1.0 ) {
|
2002-10-24 12:11:55 +00:00
|
|
|
|
add_object_to_triangle(object);
|
|
|
|
|
num -= 1.0;
|
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 <path>...</path> property.
Removed the <billboard> property and replaced it with <heading-type>,
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
<object>
<range-m>...</range-m>
...
</object>
<object>
<range-m>...</range-m>
...
</object>
...
we now have
<object-group>
<range-m>...</range-m>
<object>
...
</object>
<object>
...
</object>
...
</object-group>
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).
2002-07-20 14:56:37 +00:00
|
|
|
|
}
|
|
|
|
|
// 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 ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
if ( sg_random() <= num ) {
|
|
|
|
|
// a zombie made it through our door
|
2002-08-08 23:09:02 +00:00
|
|
|
|
add_object_to_triangle(object);
|
2002-08-05 20:14:28 +00:00
|
|
|
|
}
|
2002-07-18 01:04:08 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-05-13 03:18:42 +00:00
|
|
|
|
void TriUserData::add_object_to_triangle (SGMaterial::Object * object)
|
2002-08-08 23:09:02 +00:00
|
|
|
|
{
|
|
|
|
|
// Set up the random heading if required.
|
|
|
|
|
double hdg_deg = 0;
|
2003-05-13 03:18:42 +00:00
|
|
|
|
if (object->get_heading_type() == SGMaterial::Object::HEADING_RANDOM)
|
2002-08-08 23:09:02 +00:00
|
|
|
|
hdg_deg = sg_random() * 360;
|
|
|
|
|
|
|
|
|
|
sgMat4 mat;
|
|
|
|
|
makeWorldMatrix(mat, hdg_deg);
|
|
|
|
|
|
|
|
|
|
ssgTransform * pos = new ssgTransform;
|
|
|
|
|
pos->setTransform(mat);
|
2003-05-12 21:34:29 +00:00
|
|
|
|
pos->addKid( object->get_random_model( globals->get_model_loader(),
|
|
|
|
|
globals->get_fg_root(),
|
|
|
|
|
globals->get_props(),
|
|
|
|
|
globals->get_sim_time_sec() )
|
|
|
|
|
);
|
2002-08-08 23:09:02 +00:00
|
|
|
|
branch->addKid(pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TriUserData::makeWorldMatrix (sgMat4 mat, double hdg_deg )
|
|
|
|
|
{
|
|
|
|
|
if (hdg_deg == 0) {
|
|
|
|
|
mat[0][0] = leafData->sin_lat * leafData->cos_lon;
|
|
|
|
|
mat[0][1] = leafData->sin_lat * leafData->sin_lon;
|
|
|
|
|
mat[0][2] = -leafData->cos_lat;
|
|
|
|
|
mat[0][3] = SG_ZERO;
|
|
|
|
|
|
|
|
|
|
mat[1][0] = -leafData->sin_lon;
|
|
|
|
|
mat[1][1] = leafData->cos_lon;
|
|
|
|
|
mat[1][2] = SG_ZERO;
|
|
|
|
|
mat[1][3] = SG_ZERO;
|
|
|
|
|
} else {
|
|
|
|
|
float sin_hdg = sin( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
|
|
|
|
|
float cos_hdg = cos( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
|
|
|
|
|
mat[0][0] = cos_hdg * leafData->sin_lat * leafData->cos_lon - sin_hdg * leafData->sin_lon;
|
|
|
|
|
mat[0][1] = cos_hdg * leafData->sin_lat * leafData->sin_lon + sin_hdg * leafData->cos_lon;
|
|
|
|
|
mat[0][2] = -cos_hdg * leafData->cos_lat;
|
|
|
|
|
mat[0][3] = SG_ZERO;
|
|
|
|
|
|
|
|
|
|
mat[1][0] = -sin_hdg * leafData->sin_lat * leafData->cos_lon - cos_hdg * leafData->sin_lon;
|
|
|
|
|
mat[1][1] = -sin_hdg * leafData->sin_lat * leafData->sin_lon + cos_hdg * leafData->cos_lon;
|
|
|
|
|
mat[1][2] = sin_hdg * leafData->cos_lat;
|
|
|
|
|
mat[1][3] = SG_ZERO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mat[2][0] = leafData->cos_lat * leafData->cos_lon;
|
|
|
|
|
mat[2][1] = leafData->cos_lat * leafData->sin_lon;
|
|
|
|
|
mat[2][2] = leafData->sin_lat;
|
|
|
|
|
mat[2][3] = SG_ZERO;
|
|
|
|
|
|
|
|
|
|
// translate to random point in triangle
|
|
|
|
|
sgVec3 result;
|
|
|
|
|
random_pt_inside_tri(result, p1, p2, p3);
|
|
|
|
|
sgSubVec3(mat[3], result, center);
|
|
|
|
|
|
|
|
|
|
mat[3][3] = SG_ONE ;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-18 01:04:08 +00:00
|
|
|
|
/**
|
|
|
|
|
* SSG callback for an in-range triangle of randomly-placed objects.
|
|
|
|
|
*
|
|
|
|
|
* This pretraversal callback is attached to a branch that is traversed
|
|
|
|
|
* only when a triangle is in range. If the triangle is not currently
|
|
|
|
|
* populated with randomly-placed objects, this callback will populate
|
|
|
|
|
* it.
|
|
|
|
|
*
|
|
|
|
|
* @param entity The entity to which the callback is attached (not used).
|
|
|
|
|
* @param mask The entity's traversal mask (not used).
|
|
|
|
|
* @return Always 1, to allow traversal and culling to continue.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2002-07-25 17:32:31 +00:00
|
|
|
|
tri_in_range_callback (ssgEntity * entity, int mask)
|
2002-07-18 01:04:08 +00:00
|
|
|
|
{
|
2002-07-25 17:32:31 +00:00
|
|
|
|
TriUserData * data = (TriUserData *)entity->getUserData();
|
2002-07-18 01:04:08 +00:00
|
|
|
|
if (!data->is_filled_in) {
|
2002-08-08 23:09:02 +00:00
|
|
|
|
data->fill_in_triangle();
|
2002-07-18 01:04:08 +00:00
|
|
|
|
data->is_filled_in = true;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* SSG callback for an out-of-range triangle of randomly-placed objects.
|
|
|
|
|
*
|
|
|
|
|
* This pretraversal callback is attached to a branch that is traversed
|
|
|
|
|
* only when a triangle is out of range. If the triangle is currently
|
|
|
|
|
* populated with randomly-placed objects, the objects will be removed.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @param entity The entity to which the callback is attached (not used).
|
|
|
|
|
* @param mask The entity's traversal mask (not used).
|
|
|
|
|
* @return Always 0, to prevent any further traversal or culling.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2002-07-25 17:32:31 +00:00
|
|
|
|
tri_out_of_range_callback (ssgEntity * entity, int mask)
|
2002-07-18 01:04:08 +00:00
|
|
|
|
{
|
2002-07-25 17:32:31 +00:00
|
|
|
|
TriUserData * data = (TriUserData *)entity->getUserData();
|
2002-07-18 01:04:08 +00:00
|
|
|
|
if (data->is_filled_in) {
|
|
|
|
|
data->branch->removeAllKids();
|
|
|
|
|
data->is_filled_in = false;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2002-07-26 19:06:29 +00:00
|
|
|
|
* ssgEntity with a dummy bounding sphere, to fool culling.
|
2002-07-18 01:04:08 +00:00
|
|
|
|
*
|
2002-07-18 13:23:29 +00:00
|
|
|
|
* This forces the in-range and out-of-range branches to be visited
|
|
|
|
|
* when appropriate, even if they have no children. It's ugly, but
|
|
|
|
|
* it works and seems fairly efficient (since branches can still
|
|
|
|
|
* be culled when they're out of the view frustum).
|
|
|
|
|
*/
|
2002-12-11 21:49:25 +00:00
|
|
|
|
class DummyBSphereEntity : public ssgBranch
|
2002-07-18 13:23:29 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
2002-07-26 19:06:29 +00:00
|
|
|
|
DummyBSphereEntity (float radius)
|
|
|
|
|
{
|
|
|
|
|
bsphere.setCenter(0, 0, 0);
|
|
|
|
|
bsphere.setRadius(radius);
|
|
|
|
|
}
|
2002-07-18 13:23:29 +00:00
|
|
|
|
virtual ~DummyBSphereEntity () {}
|
|
|
|
|
virtual void recalcBSphere () { bsphere_is_invalid = false; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2002-07-18 19:16:47 +00:00
|
|
|
|
/**
|
|
|
|
|
* Calculate the bounding radius of a triangle from its center.
|
|
|
|
|
*
|
|
|
|
|
* @param center The triangle center.
|
|
|
|
|
* @param p1 The first point in the triangle.
|
|
|
|
|
* @param p2 The second point in the triangle.
|
|
|
|
|
* @param p3 The third point in the triangle.
|
|
|
|
|
* @return The greatest distance any point lies from the center.
|
|
|
|
|
*/
|
2002-07-20 19:23:44 +00:00
|
|
|
|
static inline float
|
|
|
|
|
get_bounding_radius( sgVec3 center, float *p1, float *p2, float *p3)
|
2002-07-18 19:16:47 +00:00
|
|
|
|
{
|
2002-07-20 19:23:44 +00:00
|
|
|
|
return sqrt( SG_MAX3( sgDistanceSquaredVec3(center, p1),
|
2002-08-05 20:14:28 +00:00
|
|
|
|
sgDistanceSquaredVec3(center, p2),
|
|
|
|
|
sgDistanceSquaredVec3(center, p3) ) );
|
2002-07-18 19:16:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-07-18 01:04:08 +00:00
|
|
|
|
/**
|
|
|
|
|
* Set up a triangle for randomly-placed objects.
|
|
|
|
|
*
|
|
|
|
|
* No objects will be added unless the triangle comes into range.
|
2002-07-17 22:11:13 +00:00
|
|
|
|
*
|
|
|
|
|
*/
|
2002-08-08 23:09:02 +00:00
|
|
|
|
|
|
|
|
|
void LeafUserData::setup_triangle (int i )
|
2002-07-17 22:11:13 +00:00
|
|
|
|
{
|
2002-08-08 23:09:02 +00:00
|
|
|
|
short n1, n2, n3;
|
|
|
|
|
leaf->getTriangle(i, &n1, &n2, &n3);
|
|
|
|
|
|
|
|
|
|
float * p1 = leaf->getVertex(n1);
|
|
|
|
|
float * p2 = leaf->getVertex(n2);
|
|
|
|
|
float * p3 = leaf->getVertex(n3);
|
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// Set up a single center point for LOD
|
2002-07-17 22:11:13 +00:00
|
|
|
|
sgVec3 center;
|
|
|
|
|
sgSetVec3(center,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
(p1[0] + p2[0] + p3[0]) / 3.0,
|
|
|
|
|
(p1[1] + p2[1] + p3[1]) / 3.0,
|
|
|
|
|
(p1[2] + p2[2] + p3[2]) / 3.0);
|
2002-08-08 23:09:02 +00:00
|
|
|
|
double area = sgTriArea(p1, p2, p3);
|
2002-07-17 22:11:13 +00:00
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// maximum radius of an object from center.
|
2002-07-18 19:16:47 +00:00
|
|
|
|
double bounding_radius = get_bounding_radius(center, p1, p2, p3);
|
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// Set up a transformation to the center
|
|
|
|
|
// point, so that everything else can
|
|
|
|
|
// be specified relative to it.
|
2002-07-17 22:11:13 +00:00
|
|
|
|
ssgTransform * location = new ssgTransform;
|
|
|
|
|
sgMat4 TRANS;
|
|
|
|
|
sgMakeTransMat4(TRANS, center);
|
|
|
|
|
location->setTransform(TRANS);
|
|
|
|
|
branch->addKid(location);
|
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// Iterate through all the object types.
|
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 <path>...</path> property.
Removed the <billboard> property and replaced it with <heading-type>,
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
<object>
<range-m>...</range-m>
...
</object>
<object>
<range-m>...</range-m>
...
</object>
...
we now have
<object-group>
<range-m>...</range-m>
<object>
...
</object>
<object>
...
</object>
...
</object-group>
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).
2002-07-20 14:56:37 +00:00
|
|
|
|
int num_groups = mat->get_object_group_count();
|
2002-08-08 23:09:02 +00:00
|
|
|
|
for (int j = 0; j < num_groups; j++) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// Look up the random object.
|
2003-05-13 03:18:42 +00:00
|
|
|
|
SGMaterial::ObjectGroup * group = mat->get_object_group(j);
|
2002-07-18 01:04:08 +00:00
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// 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.
|
2002-08-08 23:09:02 +00:00
|
|
|
|
float ranges[] = { 0,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
group->get_range_m() + bounding_radius,
|
2002-08-08 23:09:02 +00:00
|
|
|
|
SG_MAX };
|
2002-08-05 20:14:28 +00:00
|
|
|
|
ssgRangeSelector * lod = new ssgRangeSelector;
|
|
|
|
|
lod->setRanges(ranges, 3);
|
|
|
|
|
location->addKid(lod);
|
|
|
|
|
|
|
|
|
|
// Create the in-range and out-of-range
|
|
|
|
|
// branches.
|
|
|
|
|
ssgBranch * in_range = new ssgBranch;
|
|
|
|
|
ssgBranch * out_of_range = new ssgBranch;
|
|
|
|
|
|
|
|
|
|
// Set up the user data for if/when
|
|
|
|
|
// the random objects in this triangle
|
|
|
|
|
// are filled in.
|
|
|
|
|
TriUserData * data = new TriUserData;
|
|
|
|
|
data->is_filled_in = false;
|
|
|
|
|
data->p1 = p1;
|
|
|
|
|
data->p2 = p2;
|
|
|
|
|
data->p3 = p3;
|
2002-08-08 23:09:02 +00:00
|
|
|
|
sgCopyVec3 (data->center, center);
|
|
|
|
|
data->area = area;
|
2002-08-05 20:14:28 +00:00
|
|
|
|
data->object_group = group;
|
|
|
|
|
data->branch = in_range;
|
2002-08-08 23:09:02 +00:00
|
|
|
|
data->leafData = this;
|
|
|
|
|
data->seed = (unsigned int)(p1[0] * j);
|
2002-08-05 20:14:28 +00:00
|
|
|
|
|
|
|
|
|
// Set up the in-range node.
|
|
|
|
|
in_range->setUserData(data);
|
|
|
|
|
in_range->setTravCallback(SSG_CALLBACK_PRETRAV,
|
|
|
|
|
tri_in_range_callback);
|
|
|
|
|
lod->addKid(in_range);
|
|
|
|
|
|
|
|
|
|
// Set up the out-of-range node.
|
|
|
|
|
out_of_range->setUserData(data);
|
|
|
|
|
out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
|
|
|
|
|
tri_out_of_range_callback);
|
|
|
|
|
out_of_range->addKid(new DummyBSphereEntity(bounding_radius));
|
|
|
|
|
lod->addKid(out_of_range);
|
2002-07-17 22:11:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-25 17:32:31 +00:00
|
|
|
|
/**
|
2002-07-26 22:12:29 +00:00
|
|
|
|
* SSG callback for an in-range leaf of randomly-placed objects.
|
2002-07-25 17:32:31 +00:00
|
|
|
|
*
|
|
|
|
|
* This pretraversal callback is attached to a branch that is
|
2002-07-26 22:12:29 +00:00
|
|
|
|
* traversed only when a leaf is in range. If the leaf is not
|
2002-07-25 17:32:31 +00:00
|
|
|
|
* currently prepared to be populated with randomly-placed objects,
|
|
|
|
|
* this callback will prepare it (actual population is handled by
|
|
|
|
|
* the tri_in_range_callback for individual triangles).
|
|
|
|
|
*
|
|
|
|
|
* @param entity The entity to which the callback is attached (not used).
|
|
|
|
|
* @param mask The entity's traversal mask (not used).
|
|
|
|
|
* @return Always 1, to allow traversal and culling to continue.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2002-07-26 22:12:29 +00:00
|
|
|
|
leaf_in_range_callback (ssgEntity * entity, int mask)
|
2002-07-25 17:32:31 +00:00
|
|
|
|
{
|
2002-07-26 22:12:29 +00:00
|
|
|
|
LeafUserData * data = (LeafUserData *)entity->getUserData();
|
2002-07-25 17:32:31 +00:00
|
|
|
|
|
|
|
|
|
if (!data->is_filled_in) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// Iterate through all the triangles
|
|
|
|
|
// and populate them.
|
2002-07-25 17:32:31 +00:00
|
|
|
|
int num_tris = data->leaf->getNumTriangles();
|
|
|
|
|
for ( int i = 0; i < num_tris; ++i ) {
|
2002-08-08 23:09:02 +00:00
|
|
|
|
data->setup_triangle(i);
|
2002-07-25 17:32:31 +00:00
|
|
|
|
}
|
|
|
|
|
data->is_filled_in = true;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2002-07-26 22:12:29 +00:00
|
|
|
|
* SSG callback for an out-of-range leaf of randomly-placed objects.
|
2002-07-25 17:32:31 +00:00
|
|
|
|
*
|
|
|
|
|
* This pretraversal callback is attached to a branch that is
|
2002-07-26 22:12:29 +00:00
|
|
|
|
* traversed only when a leaf is out of range. If the leaf is
|
2002-07-25 17:32:31 +00:00
|
|
|
|
* currently prepared to be populated with randomly-placed objects (or
|
|
|
|
|
* is actually populated), the objects will be removed.
|
|
|
|
|
*
|
|
|
|
|
* @param entity The entity to which the callback is attached (not used).
|
|
|
|
|
* @param mask The entity's traversal mask (not used).
|
|
|
|
|
* @return Always 0, to prevent any further traversal or culling.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2002-07-26 22:12:29 +00:00
|
|
|
|
leaf_out_of_range_callback (ssgEntity * entity, int mask)
|
2002-07-25 17:32:31 +00:00
|
|
|
|
{
|
2002-07-26 22:12:29 +00:00
|
|
|
|
LeafUserData * data = (LeafUserData *)entity->getUserData();
|
2002-07-25 17:32:31 +00:00
|
|
|
|
if (data->is_filled_in) {
|
|
|
|
|
data->branch->removeAllKids();
|
|
|
|
|
data->is_filled_in = false;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-07-17 22:11:13 +00:00
|
|
|
|
/**
|
|
|
|
|
* Randomly place objects on a surface.
|
|
|
|
|
*
|
|
|
|
|
* The leaf node provides the geometry of the surface, while the
|
|
|
|
|
* material provides the objects and placement density. Latitude
|
|
|
|
|
* and longitude are required so that the objects can be rotated
|
2002-07-25 17:32:31 +00:00
|
|
|
|
* to the world-up vector. This function does not actually add
|
|
|
|
|
* any objects; instead, it attaches an ssgRangeSelector to the
|
|
|
|
|
* branch with callbacks to generate the objects when needed.
|
2002-07-17 22:11:13 +00:00
|
|
|
|
*
|
|
|
|
|
* @param leaf The surface where the objects should be placed.
|
|
|
|
|
* @param branch The branch that will hold the randomly-placed objects.
|
2002-07-26 22:12:29 +00:00
|
|
|
|
* @param center The center of the leaf in FlightGear coordinates.
|
2002-07-17 22:11:13 +00:00
|
|
|
|
* @param material_name The name of the surface's material.
|
|
|
|
|
*/
|
2002-07-15 18:16:20 +00:00
|
|
|
|
static void
|
|
|
|
|
gen_random_surface_objects (ssgLeaf *leaf,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
ssgBranch *branch,
|
2003-05-14 18:33:56 +00:00
|
|
|
|
Point3D *center,
|
|
|
|
|
SGMaterial *mat )
|
2002-07-15 18:16:20 +00:00
|
|
|
|
{
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// If the surface has no triangles, return
|
|
|
|
|
// now.
|
2002-07-25 17:32:31 +00:00
|
|
|
|
int num_tris = leaf->getNumTriangles();
|
|
|
|
|
if (num_tris < 1)
|
2003-05-14 18:33:56 +00:00
|
|
|
|
return;
|
2002-07-15 18:16:20 +00:00
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// If the material has no randomly-placed
|
|
|
|
|
// objects, return now.
|
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 <path>...</path> property.
Removed the <billboard> property and replaced it with <heading-type>,
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
<object>
<range-m>...</range-m>
...
</object>
<object>
<range-m>...</range-m>
...
</object>
...
we now have
<object-group>
<range-m>...</range-m>
<object>
...
</object>
<object>
...
</object>
...
</object-group>
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).
2002-07-20 14:56:37 +00:00
|
|
|
|
if (mat->get_object_group_count() < 1)
|
2003-05-14 18:33:56 +00:00
|
|
|
|
return;
|
2002-07-15 18:16:20 +00:00
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// Calculate the geodetic centre of
|
|
|
|
|
// the tile, for aligning automatic
|
|
|
|
|
// objects.
|
2002-07-25 17:32:31 +00:00
|
|
|
|
double lon_deg, lat_rad, lat_deg, alt_m, sl_radius_m;
|
|
|
|
|
Point3D geoc = sgCartToPolar3d(*center);
|
|
|
|
|
lon_deg = geoc.lon() * SGD_RADIANS_TO_DEGREES;
|
|
|
|
|
sgGeocToGeod(geoc.lat(), geoc.radius(),
|
2002-08-05 20:14:28 +00:00
|
|
|
|
&lat_rad, &alt_m, &sl_radius_m);
|
2002-07-25 17:32:31 +00:00
|
|
|
|
lat_deg = lat_rad * SGD_RADIANS_TO_DEGREES;
|
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// LOD for the leaf
|
|
|
|
|
// max random object range: 20000m
|
2002-08-08 23:09:02 +00:00
|
|
|
|
float ranges[] = { 0, 20000, 1000000 };
|
2002-07-25 17:32:31 +00:00
|
|
|
|
ssgRangeSelector * lod = new ssgRangeSelector;
|
|
|
|
|
lod->setRanges(ranges, 3);
|
|
|
|
|
branch->addKid(lod);
|
2002-07-15 18:16:20 +00:00
|
|
|
|
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// Create the in-range and out-of-range
|
|
|
|
|
// branches.
|
2002-07-25 17:32:31 +00:00
|
|
|
|
ssgBranch * in_range = new ssgBranch;
|
|
|
|
|
ssgBranch * out_of_range = new ssgBranch;
|
|
|
|
|
lod->addKid(in_range);
|
|
|
|
|
lod->addKid(out_of_range);
|
2002-07-17 21:00:54 +00:00
|
|
|
|
|
2002-07-26 22:12:29 +00:00
|
|
|
|
LeafUserData * data = new LeafUserData;
|
2002-07-25 17:32:31 +00:00
|
|
|
|
data->is_filled_in = false;
|
|
|
|
|
data->leaf = leaf;
|
|
|
|
|
data->mat = mat;
|
|
|
|
|
data->branch = in_range;
|
2002-08-08 23:09:02 +00:00
|
|
|
|
data->sin_lat = sin(lat_deg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
|
data->cos_lat = cos(lat_deg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
|
data->sin_lon = sin(lon_deg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
|
data->cos_lon = cos(lon_deg * SGD_DEGREES_TO_RADIANS);
|
2002-07-25 17:32:31 +00:00
|
|
|
|
|
|
|
|
|
in_range->setUserData(data);
|
2002-07-26 22:12:29 +00:00
|
|
|
|
in_range->setTravCallback(SSG_CALLBACK_PRETRAV, leaf_in_range_callback);
|
2002-07-25 17:32:31 +00:00
|
|
|
|
out_of_range->setUserData(data);
|
|
|
|
|
out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
leaf_out_of_range_callback);
|
2002-07-26 19:06:29 +00:00
|
|
|
|
out_of_range
|
|
|
|
|
->addKid(new DummyBSphereEntity(leaf->getBSphere()->getRadius()));
|
2002-07-15 18:16:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-07-16 23:39:53 +00:00
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Scenery loaders.
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2001-01-05 01:07:10 +00:00
|
|
|
|
// Load an Binary obj file
|
2002-03-03 20:29:31 +00:00
|
|
|
|
bool fgBinObjLoad( const string& path, const bool is_base,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
Point3D *center,
|
|
|
|
|
double *bounding_radius,
|
2003-05-14 18:33:56 +00:00
|
|
|
|
SGMaterialLib *matlib,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
ssgBranch* geometry,
|
|
|
|
|
ssgBranch* rwy_lights,
|
2002-11-01 21:56:48 +00:00
|
|
|
|
ssgBranch* taxi_lights,
|
2002-08-05 20:14:28 +00:00
|
|
|
|
ssgVertexArray *ground_lights )
|
2001-01-05 01:07:10 +00:00
|
|
|
|
{
|
2001-01-06 05:09:40 +00:00
|
|
|
|
SGBinObject obj;
|
2002-07-20 23:11:27 +00:00
|
|
|
|
bool use_random_objects =
|
|
|
|
|
fgGetBool("/sim/rendering/random-objects", true);
|
2001-01-05 01:07:10 +00:00
|
|
|
|
|
2002-03-11 22:50:23 +00:00
|
|
|
|
if ( ! obj.read_bin( path ) ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
return false;
|
2001-01-05 01:07:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-03-03 20:29:31 +00:00
|
|
|
|
geometry->setName( (char *)path.c_str() );
|
2002-07-27 14:29:57 +00:00
|
|
|
|
|
2002-10-06 03:53:19 +00:00
|
|
|
|
// reference point (center offset/bounding sphere)
|
|
|
|
|
*center = obj.get_gbs_center();
|
|
|
|
|
*bounding_radius = obj.get_gbs_radius();
|
2001-01-05 01:07:10 +00:00
|
|
|
|
|
2002-08-23 19:53:48 +00:00
|
|
|
|
point_list const& nodes = obj.get_wgs84_nodes();
|
2002-11-16 20:17:11 +00:00
|
|
|
|
// point_list const& colors = obj.get_colors();
|
2002-08-23 19:53:48 +00:00
|
|
|
|
point_list const& normals = obj.get_normals();
|
|
|
|
|
point_list const& texcoords = obj.get_texcoords();
|
2001-01-06 05:09:40 +00:00
|
|
|
|
|
2002-08-23 19:53:48 +00:00
|
|
|
|
string material;
|
2001-01-06 05:09:40 +00:00
|
|
|
|
int_list tex_index;
|
|
|
|
|
|
2002-08-23 19:53:48 +00:00
|
|
|
|
group_list::size_type i;
|
2002-03-03 20:29:31 +00:00
|
|
|
|
|
2002-02-28 00:10:35 +00:00
|
|
|
|
// generate points
|
2002-08-23 19:53:48 +00:00
|
|
|
|
string_list const& pt_materials = obj.get_pt_materials();
|
|
|
|
|
group_list const& pts_v = obj.get_pts_v();
|
|
|
|
|
group_list const& pts_n = obj.get_pts_n();
|
|
|
|
|
for ( i = 0; i < pts_v.size(); ++i ) {
|
2002-08-05 20:14:28 +00:00
|
|
|
|
// cout << "pts_v.size() = " << pts_v.size() << endl;
|
2002-08-23 19:53:48 +00:00
|
|
|
|
if ( pt_materials[i].substr(0, 3) == "RWY" ) {
|
2002-10-06 03:53:19 +00:00
|
|
|
|
sgVec3 up;
|
|
|
|
|
sgSetVec3( up, center->x(), center->y(), center->z() );
|
2002-10-21 19:45:21 +00:00
|
|
|
|
// returns a transform -> lod -> leaf structure
|
2002-10-06 03:53:19 +00:00
|
|
|
|
ssgBranch *branch = gen_directional_lights( nodes, normals,
|
|
|
|
|
pts_v[i], pts_n[i],
|
2003-05-14 18:33:56 +00:00
|
|
|
|
matlib, pt_materials[i],
|
2002-10-06 03:53:19 +00:00
|
|
|
|
up );
|
2002-10-21 16:26:48 +00:00
|
|
|
|
// branches don't honor callbacks as far as I know so I'm
|
|
|
|
|
// commenting this out to avoid a plib runtime warning.
|
2002-11-16 11:56:15 +00:00
|
|
|
|
branch->setTravCallback( SSG_CALLBACK_PRETRAV,
|
|
|
|
|
runway_lights_pretrav );
|
2002-11-01 21:56:48 +00:00
|
|
|
|
if ( pt_materials[i].substr(0, 16) == "RWY_BLUE_TAXIWAY" ) {
|
|
|
|
|
taxi_lights->addKid( branch );
|
|
|
|
|
} else {
|
|
|
|
|
rwy_lights->addKid( branch );
|
|
|
|
|
}
|
2002-10-06 04:27:24 +00:00
|
|
|
|
} else {
|
2002-08-23 19:53:48 +00:00
|
|
|
|
material = pt_materials[i];
|
2002-10-06 03:53:19 +00:00
|
|
|
|
tex_index.clear();
|
2003-05-14 19:22:24 +00:00
|
|
|
|
ssgLeaf *leaf = sgMakeLeaf( path, GL_POINTS, matlib, material,
|
|
|
|
|
nodes, normals, texcoords,
|
|
|
|
|
pts_v[i], pts_n[i], tex_index,
|
|
|
|
|
false, ground_lights );
|
2002-10-06 03:53:19 +00:00
|
|
|
|
geometry->addKid( leaf );
|
2002-08-05 20:14:28 +00:00
|
|
|
|
}
|
2002-02-28 00:10:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-07-27 14:29:57 +00:00
|
|
|
|
// Put all randomly-placed objects under a separate branch
|
|
|
|
|
// (actually an ssgRangeSelector) named "random-models".
|
|
|
|
|
ssgBranch * random_object_branch = 0;
|
|
|
|
|
if (use_random_objects) {
|
2002-08-08 23:09:02 +00:00
|
|
|
|
float ranges[] = { 0, 20000 }; // Maximum 20km range for random objects
|
2002-10-09 03:40:23 +00:00
|
|
|
|
ssgRangeSelector * object_lod = new ssgRangeSelector;
|
|
|
|
|
object_lod->setRanges(ranges, 2);
|
|
|
|
|
object_lod->setName("random-models");
|
|
|
|
|
geometry->addKid(object_lod);
|
|
|
|
|
random_object_branch = new ssgBranch;
|
|
|
|
|
object_lod->addKid(random_object_branch);
|
2002-07-27 14:29:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-05 01:07:10 +00:00
|
|
|
|
// generate triangles
|
2002-08-23 19:53:48 +00:00
|
|
|
|
string_list const& tri_materials = obj.get_tri_materials();
|
|
|
|
|
group_list const& tris_v = obj.get_tris_v();
|
|
|
|
|
group_list const& tris_n = obj.get_tris_n();
|
|
|
|
|
group_list const& tris_tc = obj.get_tris_tc();
|
|
|
|
|
for ( i = 0; i < tris_v.size(); ++i ) {
|
2003-05-14 19:22:24 +00:00
|
|
|
|
ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLES, matlib,
|
|
|
|
|
tri_materials[i],
|
|
|
|
|
nodes, normals, texcoords,
|
|
|
|
|
tris_v[i], tris_n[i], tris_tc[i],
|
|
|
|
|
is_base, ground_lights );
|
2002-08-05 20:14:28 +00:00
|
|
|
|
|
2003-05-14 18:33:56 +00:00
|
|
|
|
if ( use_random_objects ) {
|
|
|
|
|
SGMaterial *mat = matlib->find( tri_materials[i] );
|
|
|
|
|
if ( mat == NULL ) {
|
|
|
|
|
SG_LOG( SG_INPUT, SG_ALERT,
|
|
|
|
|
"Unknown material for random surface objects = "
|
|
|
|
|
<< tri_materials[i] );
|
|
|
|
|
}
|
|
|
|
|
gen_random_surface_objects( leaf, random_object_branch,
|
|
|
|
|
center, mat );
|
|
|
|
|
}
|
2002-08-05 20:14:28 +00:00
|
|
|
|
geometry->addKid( leaf );
|
2001-01-05 01:07:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generate strips
|
2002-08-23 19:53:48 +00:00
|
|
|
|
string_list const& strip_materials = obj.get_strip_materials();
|
|
|
|
|
group_list const& strips_v = obj.get_strips_v();
|
|
|
|
|
group_list const& strips_n = obj.get_strips_n();
|
|
|
|
|
group_list const& strips_tc = obj.get_strips_tc();
|
|
|
|
|
for ( i = 0; i < strips_v.size(); ++i ) {
|
2003-05-14 19:22:24 +00:00
|
|
|
|
ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_STRIP,
|
|
|
|
|
matlib, strip_materials[i],
|
|
|
|
|
nodes, normals, texcoords,
|
|
|
|
|
strips_v[i], strips_n[i], strips_tc[i],
|
|
|
|
|
is_base, ground_lights );
|
2002-08-05 20:14:28 +00:00
|
|
|
|
|
2003-05-14 18:33:56 +00:00
|
|
|
|
if ( use_random_objects ) {
|
|
|
|
|
SGMaterial *mat = matlib->find( strip_materials[i] );
|
|
|
|
|
if ( mat == NULL ) {
|
|
|
|
|
SG_LOG( SG_INPUT, SG_ALERT,
|
|
|
|
|
"Unknown material for random surface objects = "
|
|
|
|
|
<< strip_materials[i] );
|
|
|
|
|
}
|
|
|
|
|
gen_random_surface_objects( leaf, random_object_branch,
|
|
|
|
|
center, mat );
|
|
|
|
|
}
|
2002-08-05 20:14:28 +00:00
|
|
|
|
geometry->addKid( leaf );
|
2001-01-05 01:07:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generate fans
|
2002-08-23 19:53:48 +00:00
|
|
|
|
string_list const& fan_materials = obj.get_fan_materials();
|
|
|
|
|
group_list const& fans_v = obj.get_fans_v();
|
|
|
|
|
group_list const& fans_n = obj.get_fans_n();
|
|
|
|
|
group_list const& fans_tc = obj.get_fans_tc();
|
|
|
|
|
for ( i = 0; i < fans_v.size(); ++i ) {
|
2003-05-14 19:22:24 +00:00
|
|
|
|
ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_FAN,
|
|
|
|
|
matlib, fan_materials[i],
|
|
|
|
|
nodes, normals, texcoords,
|
|
|
|
|
fans_v[i], fans_n[i], fans_tc[i],
|
|
|
|
|
is_base, ground_lights );
|
2003-05-14 18:33:56 +00:00
|
|
|
|
if ( use_random_objects ) {
|
|
|
|
|
SGMaterial *mat = matlib->find( fan_materials[i] );
|
|
|
|
|
if ( mat == NULL ) {
|
|
|
|
|
SG_LOG( SG_INPUT, SG_ALERT,
|
|
|
|
|
"Unknown material for random surface objects = "
|
|
|
|
|
<< fan_materials[i] );
|
|
|
|
|
}
|
|
|
|
|
gen_random_surface_objects( leaf, random_object_branch,
|
|
|
|
|
center, mat );
|
|
|
|
|
}
|
2002-08-05 20:14:28 +00:00
|
|
|
|
geometry->addKid( leaf );
|
2001-01-05 01:07:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-03-03 20:29:31 +00:00
|
|
|
|
return true;
|
2001-01-05 01:07:10 +00:00
|
|
|
|
}
|