1999-06-12 21:15:27 +00:00
|
|
|
// tile.cxx -- routines to handle a scenery tile
|
|
|
|
//
|
|
|
|
// Written by Curtis Olson, started May 1998.
|
|
|
|
//
|
|
|
|
// Copyright (C) 1998, 1999 Curtis L. Olson - curt@flightgear.org
|
|
|
|
//
|
|
|
|
// 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$
|
|
|
|
|
|
|
|
|
2000-02-15 03:30:01 +00:00
|
|
|
#include <simgear/compiler.h>
|
1999-06-12 21:15:27 +00:00
|
|
|
|
2001-03-23 22:42:49 +00:00
|
|
|
#ifdef SG_MATH_EXCEPTION_CLASH
|
1999-06-12 21:15:27 +00:00
|
|
|
# include <math.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include STL_FUNCTIONAL
|
|
|
|
#include STL_ALGORITHM
|
2001-03-29 01:42:31 +00:00
|
|
|
#include STL_STRING
|
1999-06-12 21:15:27 +00:00
|
|
|
|
2000-02-16 23:01:03 +00:00
|
|
|
#include <simgear/bucket/newbucket.hxx>
|
|
|
|
#include <simgear/debug/logstream.hxx>
|
2001-03-29 01:42:31 +00:00
|
|
|
#include <simgear/math/sg_random.h>
|
|
|
|
#include <simgear/misc/sgstream.hxx>
|
1999-06-12 21:15:27 +00:00
|
|
|
|
2000-12-04 05:24:38 +00:00
|
|
|
#include <Aircraft/aircraft.hxx>
|
2000-12-05 14:27:27 +00:00
|
|
|
#include <Include/general.hxx>
|
2000-12-04 05:24:38 +00:00
|
|
|
#include <Main/globals.hxx>
|
|
|
|
#include <Scenery/scenery.hxx>
|
2000-12-04 23:25:05 +00:00
|
|
|
#include <Time/light.hxx>
|
2001-03-29 01:42:31 +00:00
|
|
|
#include <Objects/matlib.hxx>
|
|
|
|
#include <Objects/newmat.hxx>
|
|
|
|
#include <Objects/obj.hxx>
|
2000-12-04 05:24:38 +00:00
|
|
|
|
1999-06-12 21:15:27 +00:00
|
|
|
#include "tileentry.hxx"
|
|
|
|
|
2001-03-23 22:59:18 +00:00
|
|
|
SG_USING_STD(for_each);
|
|
|
|
SG_USING_STD(mem_fun_ref);
|
2001-03-29 01:42:31 +00:00
|
|
|
SG_USING_STD(string);
|
1999-06-12 21:15:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Constructor
|
2001-03-29 01:42:31 +00:00
|
|
|
FGTileEntry::FGTileEntry ( const SGBucket& b )
|
|
|
|
: ncount( 0 ),
|
|
|
|
center( Point3D( 0.0 ) ),
|
|
|
|
tile_bucket( b ),
|
|
|
|
terra_transform( new ssgTransform ),
|
|
|
|
terra_range( new ssgRangeSelector )
|
1999-06-12 21:15:27 +00:00
|
|
|
{
|
|
|
|
nodes.clear();
|
2001-03-29 01:42:31 +00:00
|
|
|
|
|
|
|
// update the contents
|
|
|
|
// if ( vec3_ptrs.size() || vec2_ptrs.size() || index_ptrs.size() ) {
|
|
|
|
// SG_LOG( SG_TERRAIN, SG_ALERT,
|
|
|
|
// "Attempting to overwrite existing or"
|
|
|
|
// << " not properly freed leaf data." );
|
|
|
|
// exit(-1);
|
|
|
|
// }
|
1999-06-12 21:15:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Destructor
|
2000-12-03 20:15:46 +00:00
|
|
|
FGTileEntry::~FGTileEntry () {
|
1999-06-12 21:15:27 +00:00
|
|
|
// cout << "nodes = " << nodes.size() << endl;;
|
|
|
|
// delete[] nodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-10-26 03:45:33 +00:00
|
|
|
// recurse an ssg tree and call removeKid() on every node from the
|
|
|
|
// bottom up. Leaves the original branch in existance, but empty so
|
|
|
|
// it can be removed by the calling routine.
|
|
|
|
static void my_remove_branch( ssgBranch * branch ) {
|
|
|
|
for ( ssgEntity *k = branch->getKid( 0 );
|
|
|
|
k != NULL;
|
|
|
|
k = branch->getNextKid() )
|
|
|
|
{
|
|
|
|
if ( k -> isAKindOf ( ssgTypeBranch() ) ) {
|
|
|
|
my_remove_branch( (ssgBranch *)k );
|
|
|
|
branch -> removeKid ( k );
|
|
|
|
} else if ( k -> isAKindOf ( ssgTypeLeaf() ) ) {
|
|
|
|
branch -> removeKid ( k ) ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-06-23 00:30:04 +00:00
|
|
|
// Clean up the memory used by this tile and delete the arrays used by
|
1999-06-29 14:57:00 +00:00
|
|
|
// ssg as well as the whole ssg branch
|
1999-10-27 00:52:25 +00:00
|
|
|
void FGTileEntry::free_tile() {
|
|
|
|
int i;
|
2001-03-24 06:03:11 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG,
|
1999-06-12 21:15:27 +00:00
|
|
|
"FREEING TILE = (" << tile_bucket << ")" );
|
1999-06-29 14:57:00 +00:00
|
|
|
|
2001-03-24 06:03:11 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG,
|
1999-09-01 18:52:31 +00:00
|
|
|
" deleting " << nodes.size() << " nodes" );
|
|
|
|
nodes.clear();
|
1999-06-29 14:57:00 +00:00
|
|
|
|
1999-09-01 18:52:31 +00:00
|
|
|
// delete the ssg structures
|
2001-03-24 06:03:11 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG,
|
1999-10-27 00:52:25 +00:00
|
|
|
" deleting (leaf data) vertex, normal, and "
|
|
|
|
<< " texture coordinate arrays" );
|
|
|
|
|
|
|
|
for ( i = 0; i < (int)vec3_ptrs.size(); ++i ) {
|
2001-01-11 05:04:17 +00:00
|
|
|
#if defined(macintosh) || defined(_MSC_VER)
|
|
|
|
delete [] vec3_ptrs[i]; //that's the correct version
|
2000-02-10 23:37:56 +00:00
|
|
|
#else
|
1999-10-27 00:52:25 +00:00
|
|
|
delete vec3_ptrs[i];
|
2000-02-10 23:37:56 +00:00
|
|
|
#endif
|
1999-06-30 00:28:20 +00:00
|
|
|
}
|
1999-10-27 00:52:25 +00:00
|
|
|
vec3_ptrs.clear();
|
|
|
|
|
|
|
|
for ( i = 0; i < (int)vec2_ptrs.size(); ++i ) {
|
2001-01-11 05:04:17 +00:00
|
|
|
#if defined(macintosh) || defined(_MSC_VER)
|
|
|
|
delete [] vec2_ptrs[i]; //that's the correct version
|
2000-02-10 23:37:56 +00:00
|
|
|
#else
|
1999-10-27 00:52:25 +00:00
|
|
|
delete vec2_ptrs[i];
|
2000-02-10 23:37:56 +00:00
|
|
|
#endif
|
1999-10-26 03:45:33 +00:00
|
|
|
}
|
1999-10-27 00:52:25 +00:00
|
|
|
vec2_ptrs.clear();
|
|
|
|
|
|
|
|
for ( i = 0; i < (int)index_ptrs.size(); ++i ) {
|
|
|
|
delete index_ptrs[i];
|
1999-06-30 00:28:20 +00:00
|
|
|
}
|
1999-10-27 00:52:25 +00:00
|
|
|
index_ptrs.clear();
|
1999-06-29 14:57:00 +00:00
|
|
|
|
2000-12-04 05:24:38 +00:00
|
|
|
// delete the terrain branch
|
|
|
|
int pcount = terra_transform->getNumParents();
|
1999-06-29 14:57:00 +00:00
|
|
|
if ( pcount > 0 ) {
|
|
|
|
// find the first parent (should only be one)
|
2000-12-04 05:24:38 +00:00
|
|
|
ssgBranch *parent = terra_transform->getParent( 0 ) ;
|
1999-10-06 20:59:52 +00:00
|
|
|
if( parent ) {
|
1999-10-30 02:37:56 +00:00
|
|
|
// my_remove_branch( select_ptr );
|
2000-12-04 05:24:38 +00:00
|
|
|
parent->removeKid( terra_transform );
|
|
|
|
terra_transform = NULL;
|
1999-10-06 20:59:52 +00:00
|
|
|
} else {
|
2001-03-24 06:03:11 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
1999-10-06 20:59:52 +00:00
|
|
|
"parent pointer is NULL! Dying" );
|
|
|
|
exit(-1);
|
|
|
|
}
|
1999-06-29 14:57:00 +00:00
|
|
|
} else {
|
2001-03-24 06:03:11 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
1999-06-29 14:57:00 +00:00
|
|
|
"Parent count is zero for an ssg tile! Dying" );
|
|
|
|
exit(-1);
|
1999-06-30 00:28:20 +00:00
|
|
|
}
|
2000-12-04 05:24:38 +00:00
|
|
|
|
|
|
|
if ( lights_transform ) {
|
|
|
|
// delete the terrain lighting branch
|
|
|
|
pcount = lights_transform->getNumParents();
|
|
|
|
if ( pcount > 0 ) {
|
|
|
|
// find the first parent (should only be one)
|
|
|
|
ssgBranch *parent = lights_transform->getParent( 0 ) ;
|
|
|
|
if( parent ) {
|
|
|
|
parent->removeKid( lights_transform );
|
|
|
|
lights_transform = NULL;
|
|
|
|
} else {
|
2001-03-24 06:03:11 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
2000-12-04 05:24:38 +00:00
|
|
|
"parent pointer is NULL! Dying" );
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
} else {
|
2001-03-24 06:03:11 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
2000-12-04 05:24:38 +00:00
|
|
|
"Parent count is zero for an ssg light tile! Dying" );
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
}
|
1999-06-12 21:15:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-12-04 05:24:38 +00:00
|
|
|
// Update the ssg transform node for this tile so it can be
|
|
|
|
// properly drawn relative to our (0,0,0) point
|
|
|
|
void FGTileEntry::prep_ssg_node( const Point3D& p, float vis) {
|
|
|
|
SetOffset( p );
|
|
|
|
|
|
|
|
// #define USE_UP_AND_COMING_PLIB_FEATURE
|
|
|
|
#ifdef USE_UP_AND_COMING_PLIB_FEATURE
|
|
|
|
terra_range->setRange( 0, SG_ZERO );
|
|
|
|
terra_range->setRange( 1, vis + bounding_radius );
|
|
|
|
lights_range->setRange( 0, SG_ZERO );
|
2000-12-04 22:36:18 +00:00
|
|
|
lights_range->setRange( 1, vis * 1.5 + bounding_radius );
|
2000-12-04 05:24:38 +00:00
|
|
|
#else
|
|
|
|
float ranges[2];
|
|
|
|
ranges[0] = SG_ZERO;
|
|
|
|
ranges[1] = vis + bounding_radius;
|
|
|
|
terra_range->setRanges( ranges, 2 );
|
2000-12-04 23:25:05 +00:00
|
|
|
if ( lights_range ) {
|
|
|
|
ranges[1] = vis * 1.5 + bounding_radius;
|
|
|
|
lights_range->setRanges( ranges, 2 );
|
|
|
|
}
|
2000-12-04 05:24:38 +00:00
|
|
|
#endif
|
|
|
|
sgVec3 sgTrans;
|
|
|
|
sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
|
|
|
|
terra_transform->setTransform( sgTrans );
|
|
|
|
|
|
|
|
if ( lights_transform ) {
|
|
|
|
// we need to lift the lights above the terrain to avoid
|
|
|
|
// z-buffer fighting. We do this based on our altitude and
|
|
|
|
// the distance this tile is away from scenery center.
|
|
|
|
|
|
|
|
sgVec3 up;
|
|
|
|
sgCopyVec3( up, globals->get_current_view()->get_world_up() );
|
|
|
|
|
|
|
|
double agl;
|
|
|
|
if ( current_aircraft.fdm_state ) {
|
2001-03-24 04:56:46 +00:00
|
|
|
agl = current_aircraft.fdm_state->get_Altitude() * SG_FEET_TO_METER
|
2000-12-04 05:24:38 +00:00
|
|
|
- scenery.cur_elev;
|
|
|
|
} else {
|
|
|
|
agl = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sgTrans just happens to be the
|
|
|
|
// vector from scenery center to the center of this tile which
|
|
|
|
// is what we want to calculate the distance of
|
|
|
|
sgVec3 to;
|
|
|
|
sgCopyVec3( to, sgTrans );
|
|
|
|
double dist = sgLengthVec3( to );
|
|
|
|
|
2000-12-05 14:27:27 +00:00
|
|
|
if ( general.get_glDepthBits() > 16 ) {
|
|
|
|
sgScaleVec3( up, 10.0 + agl / 100.0 + dist / 10000 );
|
|
|
|
} else {
|
|
|
|
sgScaleVec3( up, 10.0 + agl / 20.0 + dist / 5000 );
|
|
|
|
}
|
2000-12-04 05:24:38 +00:00
|
|
|
sgAddVec3( sgTrans, up );
|
|
|
|
lights_transform->setTransform( sgTrans );
|
2000-12-04 23:25:05 +00:00
|
|
|
|
|
|
|
// select which set of lights based on sun angle
|
2001-03-24 04:48:44 +00:00
|
|
|
float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
|
2000-12-06 22:16:12 +00:00
|
|
|
if ( sun_angle > 95 ) {
|
2000-12-04 23:25:05 +00:00
|
|
|
lights_brightness->select(0x04);
|
2000-12-06 22:16:12 +00:00
|
|
|
} else if ( sun_angle > 92 ) {
|
2000-12-04 23:25:05 +00:00
|
|
|
lights_brightness->select(0x02);
|
2000-12-05 18:58:05 +00:00
|
|
|
} else if ( sun_angle > 89 ) {
|
2000-12-04 23:25:05 +00:00
|
|
|
lights_brightness->select(0x01);
|
|
|
|
} else {
|
|
|
|
lights_brightness->select(0x00);
|
|
|
|
}
|
2000-12-04 05:24:38 +00:00
|
|
|
}
|
1999-07-04 07:37:30 +00:00
|
|
|
}
|
2001-03-29 01:42:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
ssgLeaf* FGTileEntry::gen_lights( ssgVertexArray *lights, int inc, float bright ) {
|
|
|
|
// generate a repeatable random seed
|
|
|
|
float *p1 = lights->get( 0 );
|
|
|
|
unsigned int *seed = (unsigned int *)p1;
|
|
|
|
sg_srandom( *seed );
|
|
|
|
|
|
|
|
int size = lights->getNum() / inc;
|
|
|
|
|
|
|
|
// Allocate ssg structure
|
|
|
|
ssgVertexArray *vl = new ssgVertexArray( size + 1 );
|
|
|
|
ssgNormalArray *nl = NULL;
|
|
|
|
ssgTexCoordArray *tl = NULL;
|
|
|
|
ssgColourArray *cl = new ssgColourArray( size + 1 );
|
|
|
|
|
|
|
|
sgVec4 color;
|
|
|
|
for ( int i = 0; i < lights->getNum(); ++i ) {
|
|
|
|
// this loop is slightly less efficient than it otherwise
|
|
|
|
// could be, but we want a red light to always be red, and a
|
|
|
|
// yellow light to always be yellow, etc. so we are trying to
|
|
|
|
// preserve the random sequence.
|
|
|
|
float zombie = sg_random();
|
|
|
|
if ( i % inc == 0 ) {
|
|
|
|
vl->add( lights->get(i) );
|
|
|
|
|
|
|
|
// factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
|
|
|
|
float factor = sg_random();
|
|
|
|
factor *= factor;
|
|
|
|
|
|
|
|
if ( zombie > 0.5 ) {
|
|
|
|
// 50% chance of yellowish
|
|
|
|
sgSetVec4( color, 0.9, 0.9, 0.3, bright - factor * 0.2 );
|
|
|
|
} else if ( zombie > 0.15 ) {
|
|
|
|
// 35% chance of whitish
|
|
|
|
sgSetVec4( color, 0.9, 0.9, 0.8, bright - factor * 0.2 );
|
|
|
|
} else if ( zombie > 0.05 ) {
|
|
|
|
// 10% chance of orangish
|
|
|
|
sgSetVec4( color, 0.9, 0.6, 0.2, bright - factor * 0.2 );
|
|
|
|
} else {
|
|
|
|
// 5% chance of redish
|
|
|
|
sgSetVec4( color, 0.9, 0.2, 0.2, bright - factor * 0.2 );
|
|
|
|
}
|
|
|
|
cl->add( color );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create ssg leaf
|
|
|
|
ssgLeaf *leaf =
|
|
|
|
new ssgVtxTable ( GL_POINTS, vl, nl, tl, cl );
|
|
|
|
|
|
|
|
// assign state
|
|
|
|
FGNewMat *newmat = material_lib.find( "LIGHTS" );
|
|
|
|
leaf->setState( newmat->get_state() );
|
|
|
|
|
|
|
|
return leaf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssgBranch*
|
|
|
|
FGTileEntry::obj_load( const std::string& path,
|
|
|
|
ssgVertexArray* lights, bool is_base )
|
|
|
|
{
|
|
|
|
ssgBranch* result = 0;
|
|
|
|
|
|
|
|
// try loading binary format
|
|
|
|
result = fgBinObjLoad( path, this, lights, is_base );
|
|
|
|
if ( result == NULL ) {
|
|
|
|
// next try the older ascii format
|
|
|
|
result = fgAsciiObjLoad( path, this, lights, is_base );
|
|
|
|
if ( result == NULL ) {
|
|
|
|
// default to an ocean tile
|
|
|
|
result = fgGenTile( path, this );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
FGTileEntry::load( SGPath& tile_path, bool is_base )
|
|
|
|
{
|
|
|
|
// a cheesy hack (to be fixed later)
|
|
|
|
extern ssgBranch *terrain;
|
|
|
|
extern ssgBranch *ground;
|
|
|
|
|
|
|
|
string index_str = tile_bucket.gen_index_str();
|
|
|
|
|
|
|
|
// Generate name of file to load.
|
|
|
|
tile_path.append( tile_bucket.gen_base_path() );
|
|
|
|
SGPath basename = tile_path;
|
|
|
|
basename.append( index_str );
|
|
|
|
string path = basename.str();
|
|
|
|
|
|
|
|
SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << path );
|
|
|
|
|
|
|
|
// fgObjLoad will generate ground lighting for us ...
|
|
|
|
ssgVertexArray *light_pts = new ssgVertexArray( 100 );
|
|
|
|
|
|
|
|
ssgBranch* new_tile = obj_load( path, light_pts, is_base );
|
|
|
|
if ( new_tile != NULL ) {
|
|
|
|
terra_range->addKid( new_tile );
|
|
|
|
}
|
|
|
|
|
|
|
|
// load custom objects
|
|
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "CUSTOM OBJECTS" );
|
|
|
|
|
|
|
|
SGPath index_path = tile_path;
|
|
|
|
index_path.append( index_str );
|
|
|
|
index_path.concat( ".ind" );
|
|
|
|
|
|
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "Looking in " << index_path.str() );
|
|
|
|
|
|
|
|
sg_gzifstream in( index_path.str() );
|
|
|
|
|
|
|
|
if ( in.is_open() ) {
|
|
|
|
string token, name;
|
|
|
|
|
|
|
|
while ( ! in.eof() ) {
|
|
|
|
in >> token >> name >> ::skipws;
|
|
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "token = " << token
|
|
|
|
<< " name = " << name );
|
|
|
|
|
|
|
|
SGPath custom_path = tile_path;
|
|
|
|
custom_path.append( name );
|
|
|
|
ssgBranch *custom_obj = obj_load( custom_path.str(), NULL, false );
|
|
|
|
if ( (new_tile != NULL) && (custom_obj != NULL) ) {
|
|
|
|
new_tile -> addKid( custom_obj );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
terra_transform->addKid( terra_range );
|
|
|
|
|
|
|
|
// calculate initial tile offset
|
|
|
|
SetOffset( scenery.center );
|
|
|
|
sgCoord sgcoord;
|
|
|
|
sgSetCoord( &sgcoord,
|
|
|
|
offset.x(), offset.y(), offset.z(),
|
|
|
|
0.0, 0.0, 0.0 );
|
|
|
|
terra_transform->setTransform( &sgcoord );
|
|
|
|
terrain->addKid( terra_transform );
|
|
|
|
|
|
|
|
lights_transform = NULL;
|
|
|
|
lights_range = NULL;
|
|
|
|
/* uncomment this section for testing ground lights */
|
|
|
|
if ( light_pts->getNum() ) {
|
|
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "generating lights" );
|
|
|
|
lights_transform = new ssgTransform;
|
|
|
|
lights_range = new ssgRangeSelector;
|
|
|
|
lights_brightness = new ssgSelector;
|
|
|
|
ssgLeaf *lights;
|
|
|
|
|
|
|
|
lights = gen_lights( light_pts, 4, 0.7 );
|
|
|
|
lights_brightness->addKid( lights );
|
|
|
|
|
|
|
|
lights = gen_lights( light_pts, 2, 0.85 );
|
|
|
|
lights_brightness->addKid( lights );
|
|
|
|
|
|
|
|
lights = gen_lights( light_pts, 1, 1.0 );
|
|
|
|
lights_brightness->addKid( lights );
|
|
|
|
|
|
|
|
lights_range->addKid( lights_brightness );
|
|
|
|
lights_transform->addKid( lights_range );
|
|
|
|
lights_transform->setTransform( &sgcoord );
|
|
|
|
ground->addKid( lights_transform );
|
|
|
|
}
|
|
|
|
/* end of ground light section */
|
|
|
|
}
|