mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-15 00:24:40 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
102
Engine/source/scene/zones/scenePolyhedralZone.cpp
Normal file
102
Engine/source/scene/zones/scenePolyhedralZone.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "scene/zones/scenePolyhedralZone.h"
|
||||
|
||||
#include "scene/mixin/scenePolyhedralObject.impl.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ScenePolyhedralZone::ScenePolyhedralZone( const PolyhedronType& polyhedron )
|
||||
: Parent( polyhedron )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool ScenePolyhedralZone::onAdd()
|
||||
{
|
||||
if( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
// Precompute polyhedron/AABB intersection data, if not
|
||||
// a trivial box polyhedron.
|
||||
|
||||
if( !mIsBox )
|
||||
{
|
||||
_updateIntersector();
|
||||
|
||||
// Also need to update OBB.
|
||||
_updateOrientedWorldBox();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ScenePolyhedralZone::setTransform( const MatrixF& mat )
|
||||
{
|
||||
Parent::setTransform( mat );
|
||||
|
||||
// Recompute intersection data.
|
||||
|
||||
if( !mIsBox )
|
||||
_updateIntersector();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ScenePolyhedralZone::_updateOrientedWorldBox()
|
||||
{
|
||||
if( mIsBox )
|
||||
Parent::_updateOrientedWorldBox();
|
||||
else
|
||||
mOrientedWorldBox.set( getTransform(), Point3F( mObjBox.len_x(), mObjBox.len_y(), mObjBox.len_z() ) );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool ScenePolyhedralZone::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones )
|
||||
{
|
||||
// If a trivial box, let parent handle this.
|
||||
|
||||
if( mIsBox )
|
||||
return Parent::getOverlappingZones( aabb, outZones, outNumZones );
|
||||
|
||||
// Otherwise, use the intersector.
|
||||
|
||||
OverlapTestResult overlap = mIntersector.test( aabb );
|
||||
|
||||
if( overlap == GeometryOutside )
|
||||
{
|
||||
outNumZones = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
outZones[ 0 ] = getZoneRangeStart();
|
||||
outNumZones = 1;
|
||||
|
||||
return ( overlap != GeometryInside );
|
||||
}
|
||||
81
Engine/source/scene/zones/scenePolyhedralZone.h
Normal file
81
Engine/source/scene/zones/scenePolyhedralZone.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SCENEPOLYHEDRALZONE_H_
|
||||
#define _SCENEPOLYHEDRALZONE_H_
|
||||
|
||||
#ifndef _SCENESIMPLEZONE_H_
|
||||
#include "scene/zones/sceneSimpleZone.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENEPOLYHEDRALOBJECT_H_
|
||||
#include "scene/mixin/scenePolyhedralObject.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MINTERSECTOR_H_
|
||||
#include "math/mIntersector.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// A simple zone space that is described by a polyhedron.
|
||||
///
|
||||
/// By default, if no other polyhedron is assigned to a polyhedral zone, the
|
||||
/// polyhedron is initialized from the zone's object box.
|
||||
class ScenePolyhedralZone : public ScenePolyhedralObject< SceneSimpleZone >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ScenePolyhedralObject< SceneSimpleZone > Parent;
|
||||
|
||||
protected:
|
||||
|
||||
typedef PolyhedronBoxIntersector< PolyhedronType > IntersectorType;
|
||||
|
||||
/// Fast polyhedron/AABB intersector used for testing SceneObject AABBs
|
||||
/// for overlap with the zone.
|
||||
IntersectorType mIntersector;
|
||||
|
||||
/// Precompute polyhedron/AABB intersection data.
|
||||
void _updateIntersector()
|
||||
{
|
||||
mIntersector = IntersectorType( getPolyhedron(), getTransform(), getScale(), getWorldBox() );
|
||||
}
|
||||
|
||||
// SceneSimpleZone.
|
||||
virtual void _updateOrientedWorldBox();
|
||||
|
||||
public:
|
||||
|
||||
ScenePolyhedralZone() {}
|
||||
ScenePolyhedralZone( const PolyhedronType& polyhedron );
|
||||
|
||||
// SimObject.
|
||||
virtual bool onAdd();
|
||||
|
||||
// SceneObject.
|
||||
virtual void setTransform( const MatrixF& mat );
|
||||
|
||||
// SceneZoneSpace.
|
||||
virtual bool getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones );
|
||||
};
|
||||
|
||||
#endif // !_SCENEPOLYHEDRALZONE_H_
|
||||
121
Engine/source/scene/zones/sceneRootZone.cpp
Normal file
121
Engine/source/scene/zones/sceneRootZone.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "scene/zones/sceneRootZone.h"
|
||||
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
|
||||
|
||||
|
||||
//RD: for the SceneRootZone, it may be worthwhile to put an optimized path in place that
|
||||
// does some spatially aware testing of portals rather than just blindly going through
|
||||
// the list of connected zone managers
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneRootZone::SceneRootZone()
|
||||
{
|
||||
setGlobalBounds();
|
||||
resetWorldBox();
|
||||
|
||||
// Clear netflags we have inherited. We don't
|
||||
// network scene roots.
|
||||
mNetFlags.clear( Ghostable | ScopeAlways );
|
||||
|
||||
// Clear type flags we have inherited.
|
||||
mTypeMask &= ~StaticObjectType;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
String SceneRootZone::describeSelf() const
|
||||
{
|
||||
String str = Parent::describeSelf();
|
||||
|
||||
str += "|SceneRootZone";
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneRootZone::onSceneAdd()
|
||||
{
|
||||
if( !Parent::onSceneAdd() )
|
||||
return false;
|
||||
|
||||
AssertFatal( getZoneRangeStart() == SceneZoneSpaceManager::RootZoneId, "SceneRootZone::onSceneAdd - SceneRootZone must be first scene object zone manager!" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneRootZone::onSceneRemove()
|
||||
{
|
||||
AssertFatal( getZoneRangeStart() == SceneZoneSpaceManager::RootZoneId, "SceneRootZone::onSceneRemove - SceneRootZone must be first scene object zone manager!");
|
||||
Parent::onSceneRemove();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneRootZone::onAdd()
|
||||
{
|
||||
AssertFatal( false, "SceneRootZone::onAdd - Must not register SceneRootZone!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneRootZone::onRemove()
|
||||
{
|
||||
AssertFatal( false, "SceneRootZone::onRemove - Must not be called!" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 SceneRootZone::getPointZone( const Point3F &p ) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneRootZone::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones )
|
||||
{
|
||||
// Everything overlaps the outside zone.
|
||||
|
||||
outZones[ 0 ] = SceneZoneSpaceManager::RootZoneId;
|
||||
outNumZones = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneRootZone::containsPoint( const Point3F &point ) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
69
Engine/source/scene/zones/sceneRootZone.h
Normal file
69
Engine/source/scene/zones/sceneRootZone.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SCENEROOTZONE_H_
|
||||
#define _SCENEROOTZONE_H_
|
||||
|
||||
#ifndef _SCENESIMPLEZONE_H_
|
||||
#include "scene/zones/sceneSimpleZone.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/// Root zone in a scene.
|
||||
///
|
||||
/// The root zone is an infinite zone that contains all others zones and
|
||||
/// objects in the scene.
|
||||
///
|
||||
/// This class is not declared as a ConsoleObject and is not visible
|
||||
/// from TorqueScript. SceneRootZone objects are not registered with the
|
||||
/// Sim system.
|
||||
class SceneRootZone : public SceneSimpleZone
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SceneSimpleZone Parent;
|
||||
|
||||
protected:
|
||||
|
||||
// SceneObject.
|
||||
virtual bool onSceneAdd();
|
||||
virtual void onSceneRemove();
|
||||
|
||||
public:
|
||||
|
||||
SceneRootZone();
|
||||
|
||||
// SimObject.
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
virtual String describeSelf() const;
|
||||
|
||||
// SceneObject.
|
||||
virtual bool containsPoint( const Point3F &point ) const;
|
||||
|
||||
// SceneZoneSpace.
|
||||
virtual U32 getPointZone( const Point3F &p ) const;
|
||||
virtual bool getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones );
|
||||
};
|
||||
|
||||
#endif //_SCENEROOTZONE_H_
|
||||
361
Engine/source/scene/zones/sceneSimpleZone.cpp
Normal file
361
Engine/source/scene/zones/sceneSimpleZone.cpp
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "scene/zones/sceneSimpleZone.h"
|
||||
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/zones/sceneTraversalState.h"
|
||||
#include "scene/culling/sceneCullingVolume.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "platform/profiler.h"
|
||||
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneSimpleZone::SceneSimpleZone()
|
||||
: mUseAmbientLightColor( false ),
|
||||
mAmbientLightColor( 0.1f, 0.1f, 0.1f, 1.f ),
|
||||
mIsRotated( false )
|
||||
{
|
||||
// Box zones are unit cubes that are scaled to fit.
|
||||
|
||||
mObjScale.set( 10, 10, 10 );
|
||||
mObjBox.set(
|
||||
Point3F( -0.5f, -0.5f, -0.5f ),
|
||||
Point3F( 0.5f, 0.5f, 0.5f )
|
||||
);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSimpleZone::initPersistFields()
|
||||
{
|
||||
addGroup( "Lighting" );
|
||||
|
||||
addProtectedField( "useAmbientLightColor", TypeBool, Offset( mUseAmbientLightColor, SceneSimpleZone ),
|
||||
&_setUseAmbientLightColor, &defaultProtectedGetFn,
|
||||
"Whether to use #ambientLightColor for ambient lighting in this zone or the global ambient color." );
|
||||
addProtectedField( "ambientLightColor", TypeColorF, Offset( mAmbientLightColor, SceneSimpleZone ),
|
||||
&_setAmbientLightColor, &defaultProtectedGetFn,
|
||||
"Color of ambient lighting in this zone.\n\n"
|
||||
"Only used if #useAmbientLightColor is true." );
|
||||
|
||||
endGroup( "Lighting" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
String SceneSimpleZone::describeSelf() const
|
||||
{
|
||||
String str = Parent::describeSelf();
|
||||
str += String::ToString( "|zoneid: %i", getZoneRangeStart() );
|
||||
return str;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneSimpleZone::onSceneAdd()
|
||||
{
|
||||
if( !Parent::onSceneAdd() )
|
||||
return false;
|
||||
|
||||
// Register our zone.
|
||||
|
||||
SceneZoneSpaceManager* manager = getSceneManager()->getZoneManager();
|
||||
if( manager )
|
||||
manager->registerZones( this, 1 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 SceneSimpleZone::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
|
||||
{
|
||||
U32 retMask = Parent::packUpdate( connection, mask, stream );
|
||||
|
||||
if( stream->writeFlag( mask & AmbientMask ) )
|
||||
{
|
||||
stream->writeFlag( mUseAmbientLightColor );
|
||||
stream->writeFloat( mAmbientLightColor.red, 7 );
|
||||
stream->writeFloat( mAmbientLightColor.green, 7 );
|
||||
stream->writeFloat( mAmbientLightColor.blue, 7 );
|
||||
stream->writeFloat( mAmbientLightColor.alpha, 7 );
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSimpleZone::unpackUpdate( NetConnection* connection, BitStream* stream )
|
||||
{
|
||||
Parent::unpackUpdate( connection, stream );
|
||||
|
||||
if( stream->readFlag() ) // AmbientMask
|
||||
{
|
||||
mUseAmbientLightColor = stream->readFlag();
|
||||
mAmbientLightColor.red = stream->readFloat( 7 );
|
||||
mAmbientLightColor.green = stream->readFloat( 7 );
|
||||
mAmbientLightColor.blue = stream->readFloat( 7 );
|
||||
mAmbientLightColor.alpha = stream->readFloat( 7 );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSimpleZone::setUseAmbientLightColor( bool value )
|
||||
{
|
||||
if( mUseAmbientLightColor == value )
|
||||
return;
|
||||
|
||||
mUseAmbientLightColor = value;
|
||||
if( isServerObject() )
|
||||
setMaskBits( AmbientMask );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSimpleZone::setAmbientLightColor( const ColorF& color )
|
||||
{
|
||||
mAmbientLightColor = color;
|
||||
if( isServerObject() )
|
||||
setMaskBits( AmbientMask );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneSimpleZone::getZoneAmbientLightColor( U32 zone, ColorF& outColor ) const
|
||||
{
|
||||
AssertFatal( zone == getZoneRangeStart(), "SceneSimpleZone::getZoneAmbientLightColor - Invalid zone ID!" );
|
||||
|
||||
if( !mUseAmbientLightColor )
|
||||
return false;
|
||||
|
||||
outColor = mAmbientLightColor;
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 SceneSimpleZone::getPointZone( const Point3F& p )
|
||||
{
|
||||
if( !containsPoint( p ) )
|
||||
return SceneZoneSpaceManager::InvalidZoneId;
|
||||
|
||||
return getZoneRangeStart();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneSimpleZone::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones )
|
||||
{
|
||||
PROFILE_SCOPE( SceneBoxZone_getOverlappingZones );
|
||||
|
||||
bool isOverlapped = false;
|
||||
bool isContained = false;
|
||||
|
||||
// If the zone has not been rotated, we can simply use straightforward
|
||||
// AABB/AABB intersection based on the world boxes of the zone and the
|
||||
// object.
|
||||
//
|
||||
// If, however, the zone has been rotated, then we must use the zone's
|
||||
// OBB and test that against the object's AABB.
|
||||
|
||||
if( !mIsRotated )
|
||||
{
|
||||
isOverlapped = mWorldBox.isOverlapped( aabb );
|
||||
isContained = isOverlapped && mWorldBox.isContained( aabb );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the zone's OBB intersects the object's AABB.
|
||||
|
||||
isOverlapped = aabb.collideOrientedBox(
|
||||
getScale() / 2.f,
|
||||
getTransform()
|
||||
);
|
||||
|
||||
// If so, check whether the object's AABB is fully contained
|
||||
// inside the zone's OBB.
|
||||
|
||||
if( isOverlapped )
|
||||
{
|
||||
isContained = true;
|
||||
|
||||
for( U32 i = 0; i < Box3F::NUM_POINTS; ++ i )
|
||||
{
|
||||
Point3F cornerPoint = aabb.computeVertex( i );
|
||||
if( !mOrientedWorldBox.isContained( cornerPoint ) )
|
||||
{
|
||||
isContained = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( isOverlapped )
|
||||
{
|
||||
outNumZones = 1;
|
||||
outZones[ 0 ] = getZoneRangeStart();
|
||||
}
|
||||
else
|
||||
outNumZones = 0;
|
||||
|
||||
return !isContained;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSimpleZone::setTransform( const MatrixF& mat )
|
||||
{
|
||||
Parent::setTransform( mat );
|
||||
|
||||
// Find out whether the zone has been rotated.
|
||||
|
||||
EulerF rotation = getTransform().toEuler();
|
||||
mIsRotated = !mIsZero( rotation.x ) ||
|
||||
!mIsZero( rotation.y ) ||
|
||||
!mIsZero( rotation.z );
|
||||
|
||||
// Update the OBB.
|
||||
|
||||
_updateOrientedWorldBox();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSimpleZone::prepRenderImage( SceneRenderState* state )
|
||||
{
|
||||
if( isRootZone() )
|
||||
return;
|
||||
|
||||
Parent::prepRenderImage( state );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSimpleZone::traverseZones( SceneTraversalState* state )
|
||||
{
|
||||
traverseZones( state, getZoneRangeStart() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSimpleZone::traverseZones( SceneTraversalState* state, U32 startZoneId )
|
||||
{
|
||||
PROFILE_SCOPE( SceneSimpleZone_traverseZones );
|
||||
AssertFatal( startZoneId == getZoneRangeStart(), "SceneSimpleZone::traverseZones - Invalid start zone ID!" );
|
||||
|
||||
// If we aren't the root of the traversal, do a number of checks
|
||||
// to see if we can early out of the traversal here. The primary reason
|
||||
// we don't do the checks if we are the root is because of the frustum
|
||||
// near plane. The start zone of a traversal is selected based on the
|
||||
// current viewpoint. However, that point still has some space in between
|
||||
// it and the near plane so if we end up with a case where that's all the
|
||||
// space that is needed to cull away our starting zone, we won't see any
|
||||
// traversal at all and get a blank scene back even if the starting zone
|
||||
// would actually hand the traversal over to other zones and eventually
|
||||
// discover visible space.
|
||||
|
||||
if( state->getTraversalDepth() > 0 )
|
||||
{
|
||||
// If we have already visited this zone in this traversal chain,
|
||||
// exit out. This can happen when zones are grouped together.
|
||||
// Note that this can also happen with the outdoor zone since it isn't
|
||||
// convex. However, in that case, this acts as a nice side optimization
|
||||
// we get since if the outdoor zone is already on the stack, our culling
|
||||
// culling volume can only be smaller than the one we started with and thus
|
||||
// by earlying out here, we prevent adding a pointless culling volume
|
||||
// to the zone.
|
||||
|
||||
//TODO: would be nice to catch this via "visibility changed?" checks but
|
||||
// that's non-trivial
|
||||
|
||||
if( state->haveVisitedZone( getZoneRangeStart() ) )
|
||||
return;
|
||||
|
||||
// First check whether we even intersect the given frustum at all.
|
||||
|
||||
if( mIsRotated )
|
||||
{
|
||||
// Space has been rotated, so do a frustum/OBB check.
|
||||
if( !state->getCurrentCullingVolume().test( _getOrientedWorldBox() ) )
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Space has not been rotated, so we can do a faster frustum/ABB check.
|
||||
if( !state->getCurrentCullingVolume().test( getWorldBox() ) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the current culling volume to the culling state for this zone.
|
||||
// If that doesn't result in new space becoming visible, we can terminate the traversal
|
||||
// here.
|
||||
|
||||
if( !state->getCullingState()->addCullingVolumeToZone( startZoneId, state->getCurrentCullingVolume() ) )
|
||||
return;
|
||||
|
||||
// Add our occluders to the rendering state.
|
||||
|
||||
_addOccludersToCullingState( state->getCullingState() );
|
||||
|
||||
// Merge the zone into the traversal area.
|
||||
|
||||
state->addToTraversedArea( getWorldBox() );
|
||||
|
||||
// Push our zone ID on the traversal stack and traverse into our
|
||||
// connected zone managers.
|
||||
|
||||
state->pushZone( startZoneId );
|
||||
_traverseConnectedZoneSpaces( state );
|
||||
state->popZone();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneSimpleZone::_setUseAmbientLightColor( void* object, const char* index, const char* data )
|
||||
{
|
||||
SceneSimpleZone* zone = reinterpret_cast< SceneSimpleZone* >( object );
|
||||
zone->setUseAmbientLightColor( EngineUnmarshallData< bool >()( data ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneSimpleZone::_setAmbientLightColor( void* object, const char* index, const char* data )
|
||||
{
|
||||
SceneSimpleZone* zone = reinterpret_cast< SceneSimpleZone* >( object );
|
||||
zone->setAmbientLightColor( EngineUnmarshallData< ColorF >()( data ) );
|
||||
return false;
|
||||
}
|
||||
145
Engine/source/scene/zones/sceneSimpleZone.h
Normal file
145
Engine/source/scene/zones/sceneSimpleZone.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SCENESIMPLEZONE_H_
|
||||
#define _SCENESIMPLEZONE_H_
|
||||
|
||||
#ifndef _SCENEZONESPACE_H_
|
||||
#include "scene/zones/sceneZoneSpace.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MORIENTEDBOX_H_
|
||||
#include "math/mOrientedBox.h"
|
||||
#endif
|
||||
|
||||
|
||||
class SceneRenderState;
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
/// Abstract base class for a zone space that contains only a single zone.
|
||||
///
|
||||
/// Simple zones are required to be convex.
|
||||
class SceneSimpleZone : public SceneZoneSpace
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SceneZoneSpace Parent;
|
||||
|
||||
protected:
|
||||
|
||||
enum
|
||||
{
|
||||
AmbientMask = Parent::NextFreeMask << 0, ///< Ambient light color setting has changed.
|
||||
NextFreeMask = Parent::NextFreeMask << 1,
|
||||
};
|
||||
|
||||
/// @name Ambient Lighting
|
||||
/// @{
|
||||
|
||||
/// If this is true, then the zone defines its own ambient
|
||||
/// light color in #mAmbientColor.
|
||||
bool mUseAmbientLightColor;
|
||||
|
||||
/// Ambient light color in this zone.
|
||||
ColorF mAmbientLightColor;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Transforms
|
||||
/// @{
|
||||
|
||||
/// Whether the zone has been rotated. This is used to fasttrack overlap
|
||||
/// tests to avoid doing an OBB/AABB intersection test when we can simply
|
||||
/// use a much quicker AABB/AABB intersection test on the world boxes of
|
||||
/// both objects.
|
||||
bool mIsRotated;
|
||||
|
||||
/// The OBB for the zone.
|
||||
OrientedBox3F mOrientedWorldBox;
|
||||
|
||||
/// @}
|
||||
|
||||
/// Return the oriented bounding box for the zone.
|
||||
const OrientedBox3F& _getOrientedWorldBox() const { return mOrientedWorldBox; }
|
||||
|
||||
/// Update the OBB for the zone.
|
||||
virtual void _updateOrientedWorldBox() { mOrientedWorldBox.set( getTransform(), getScale() ); }
|
||||
|
||||
// SceneObject.
|
||||
virtual bool onSceneAdd();
|
||||
|
||||
public:
|
||||
|
||||
SceneSimpleZone();
|
||||
|
||||
/// @name Ambient Lighting
|
||||
/// @{
|
||||
|
||||
/// Return true if the zone defines its own ambient light color.
|
||||
bool useAmbientLightColor() const { return mUseAmbientLightColor; }
|
||||
|
||||
/// Set whether a custom ambient light color is active in this zone.
|
||||
void setUseAmbientLightColor( bool value );
|
||||
|
||||
/// Return the ambient light color for this zone.
|
||||
ColorF getAmbientLightColor() const { return mAmbientLightColor; }
|
||||
|
||||
/// Set the ambient light color for the zone.
|
||||
/// @note This only takes effect if useAmbientLightColor() return true.
|
||||
/// @see setUseAmbientLightColor
|
||||
void setAmbientLightColor( const ColorF& color );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Inherited
|
||||
/// @{
|
||||
|
||||
// SimObject.
|
||||
virtual String describeSelf() const;
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
// NetObject
|
||||
virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream );
|
||||
virtual void unpackUpdate( NetConnection *conn, BitStream *stream );
|
||||
|
||||
// SceneObject
|
||||
virtual void prepRenderImage( SceneRenderState* state );
|
||||
virtual void setTransform( const MatrixF& mat );
|
||||
|
||||
// SceneZoneSpace.
|
||||
virtual U32 getPointZone( const Point3F &p );
|
||||
virtual bool getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones );
|
||||
virtual void traverseZones( SceneTraversalState* state );
|
||||
virtual bool getZoneAmbientLightColor( U32 zone, ColorF& outColor ) const;
|
||||
virtual void traverseZones( SceneTraversalState* state, U32 startZoneId );
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
|
||||
static bool _setUseAmbientLightColor( void* object, const char* index, const char* data );
|
||||
static bool _setAmbientLightColor( void* object, const char* index, const char* data );
|
||||
};
|
||||
|
||||
#endif // !_SCENESIMPLEZONE_H_
|
||||
70
Engine/source/scene/zones/sceneTraversalState.cpp
Normal file
70
Engine/source/scene/zones/sceneTraversalState.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "scene/zones/sceneTraversalState.h"
|
||||
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/culling/sceneCullingState.h"
|
||||
#include "scene/culling/sceneCullingVolume.h"
|
||||
#include "scene/zones/sceneZoneSpaceManager.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneTraversalState::SceneTraversalState( SceneCullingState* cullingState )
|
||||
: mCullingState( cullingState ),
|
||||
mTraversedArea( 0.f )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mZoneStack );
|
||||
VECTOR_SET_ASSOCIATION( mCullingVolumeStack );
|
||||
|
||||
// Push the polyhedron of the root frustum onto the traversal
|
||||
// stack as the current culling volume.
|
||||
|
||||
pushCullingVolume( cullingState->getRootVolume() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneZoneSpace* SceneTraversalState::getZoneFromStack( U32 depth )
|
||||
{
|
||||
SceneZoneSpaceManager* zoneManager = getCullingState()->getSceneManager()->getZoneManager();
|
||||
U32 zoneId = getZoneIdFromStack( depth );
|
||||
|
||||
return zoneManager->getZoneOwner( zoneId );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneTraversalState::pushCullingVolume( const SceneCullingVolume& volume )
|
||||
{
|
||||
mCullingVolumeStack.push_back( volume );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneTraversalState::popCullingVolume()
|
||||
{
|
||||
AssertFatal( mCullingVolumeStack.size() > 1, "SceneTraversalState::popCullingVolume - Must not pop root volume" );
|
||||
mCullingVolumeStack.pop_back();
|
||||
}
|
||||
119
Engine/source/scene/zones/sceneTraversalState.h
Normal file
119
Engine/source/scene/zones/sceneTraversalState.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SCENETRAVERSALSTATE_H_
|
||||
#define _SCENETRAVERSALSTATE_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MBOX_H_
|
||||
#include "math/mBox.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MPLANESET_H_
|
||||
#include "math/mPlaneSet.h"
|
||||
#endif
|
||||
|
||||
|
||||
class SceneCullingState;
|
||||
class SceneCullingVolume;
|
||||
class SceneZoneSpace;
|
||||
|
||||
|
||||
/// Temporary state for zone traversals. Keeps track of the zones
|
||||
/// that were visited in the current traversal chain as well as of
|
||||
/// the total area that so far has been visited in the traversal.
|
||||
class SceneTraversalState
|
||||
{
|
||||
protected:
|
||||
|
||||
/// Scene culling state.
|
||||
SceneCullingState* mCullingState;
|
||||
|
||||
/// Stack of zones visited in current traversal chain.
|
||||
Vector< U32 > mZoneStack;
|
||||
|
||||
/// Stack of culling volumes. Topmost is current volume.
|
||||
Vector< SceneCullingVolume > mCullingVolumeStack;
|
||||
|
||||
/// Area of scene visited in traversal.
|
||||
Box3F mTraversedArea;
|
||||
|
||||
public:
|
||||
|
||||
/// Construct an empty scene traversal state.
|
||||
/// @param cullingState Scene culling state.
|
||||
SceneTraversalState( SceneCullingState* cullingState );
|
||||
|
||||
/// Return the scene culling state for this traversal.
|
||||
SceneCullingState* getCullingState() const { return mCullingState; }
|
||||
|
||||
/// Get the scene area that has been visited so far in the traversal.
|
||||
const Box3F& getTraversedArea() const { return mTraversedArea; }
|
||||
|
||||
/// Add an area to what has been visited so far in the traversal.
|
||||
void addToTraversedArea( const Box3F& area ) { mTraversedArea.intersect( area ); }
|
||||
|
||||
/// @name Zone Stack
|
||||
/// @{
|
||||
|
||||
/// Return the number of zones that are currently on the traversal stack.
|
||||
U32 getTraversalDepth() const { return mZoneStack.size(); }
|
||||
|
||||
/// Push a zone onto the traversal stack.
|
||||
void pushZone( U32 zoneId ) { mZoneStack.push_back( zoneId ); }
|
||||
|
||||
/// Pop the topmost zone from the traversal stack.
|
||||
void popZone() { mZoneStack.pop_back(); }
|
||||
|
||||
/// Return true if the given zone has already been visited in the
|
||||
/// current traversal chain, i.e. if it is currently on the traversal
|
||||
/// stack.
|
||||
bool haveVisitedZone( U32 zoneId ) const { return mZoneStack.contains( zoneId ); }
|
||||
|
||||
/// Return the zone ID of the topmost zone on the traversal stack.
|
||||
U32 getZoneIdFromStack( U32 depth = 0 ) { return mZoneStack[ mZoneStack.size() - 1 - depth ]; }
|
||||
|
||||
/// Return the zone space that owns the topmost zone on the traversal stack.
|
||||
SceneZoneSpace* getZoneFromStack( U32 depth = 0 );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Culling Volume Stack
|
||||
/// @{
|
||||
|
||||
/// Push a culling volume onto the stack so that it becomes the current culling
|
||||
/// volume.
|
||||
void pushCullingVolume( const SceneCullingVolume& volume );
|
||||
|
||||
/// Pop the current culling volume from the stack.
|
||||
void popCullingVolume();
|
||||
|
||||
///
|
||||
const SceneCullingVolume& getCurrentCullingVolume() const { return mCullingVolumeStack.last(); }
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // !_SCENETRAVERSALSTATE_H_
|
||||
362
Engine/source/scene/zones/sceneZoneSpace.cpp
Normal file
362
Engine/source/scene/zones/sceneZoneSpace.cpp
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "scene/zones/sceneZoneSpace.h"
|
||||
|
||||
#include "scene/zones/sceneTraversalState.h"
|
||||
#include "scene/zones/sceneZoneSpaceManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
ClassChunker< SceneZoneSpace::ZoneSpaceRef > SceneZoneSpace::smZoneSpaceRefChunker;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneZoneSpace::SceneZoneSpace()
|
||||
: mManager( NULL ),
|
||||
mZoneGroup( InvalidZoneGroup ),
|
||||
mZoneRangeStart( SceneZoneSpaceManager::InvalidZoneId ),
|
||||
mZoneFlags( ZoneFlag_IsClosedOffSpace ),
|
||||
mNumZones( 0 ),
|
||||
mConnectedZoneSpaces( NULL )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mOccluders );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneZoneSpace::~SceneZoneSpace()
|
||||
{
|
||||
AssertFatal( mConnectedZoneSpaces == NULL, "SceneZoneSpace::~SceneZoneSpace - Still have connected zone spaces!" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::onSceneRemove()
|
||||
{
|
||||
_disconnectAllZoneSpaces();
|
||||
Parent::onSceneRemove();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::initPersistFields()
|
||||
{
|
||||
addGroup( "Zoning" );
|
||||
|
||||
addProtectedField( "zoneGroup", TypeS32, Offset( mZoneGroup, SceneZoneSpace ),
|
||||
&_setZoneGroup, &defaultProtectedGetFn,
|
||||
"ID of group the zone is part of." );
|
||||
|
||||
endGroup( "Zoning" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneZoneSpace::writeField( StringTableEntry fieldName, const char* value )
|
||||
{
|
||||
// Don't write zoneGroup field if at default.
|
||||
static StringTableEntry sZoneGroup = StringTable->insert( "zoneGroup" );
|
||||
if( fieldName == sZoneGroup && getZoneGroup() == InvalidZoneGroup )
|
||||
return false;
|
||||
|
||||
return Parent::writeField( fieldName, value );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::setZoneGroup( U32 group )
|
||||
{
|
||||
if( mZoneGroup == group )
|
||||
return;
|
||||
|
||||
mZoneGroup = group;
|
||||
setMaskBits( ZoneGroupMask );
|
||||
|
||||
// Rezone to establish new connectivity.
|
||||
|
||||
if( mManager )
|
||||
mManager->notifyObjectChanged( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 SceneZoneSpace::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
|
||||
{
|
||||
U32 retMask = Parent::packUpdate( connection, mask, stream );
|
||||
|
||||
if( stream->writeFlag( mask & ZoneGroupMask ) )
|
||||
stream->write( mZoneGroup );
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::unpackUpdate( NetConnection* connection, BitStream* stream )
|
||||
{
|
||||
Parent::unpackUpdate( connection, stream );
|
||||
|
||||
if( stream->readFlag() ) // ZoneGroupMask
|
||||
{
|
||||
U32 zoneGroup;
|
||||
stream->read( &zoneGroup );
|
||||
setZoneGroup( zoneGroup );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneZoneSpace::getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones )
|
||||
{
|
||||
return getOverlappingZones( obj->getWorldBox(), outZones, outNumZones );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::_onZoneAddObject( SceneObject* object, const U32* zoneIDs, U32 numZones )
|
||||
{
|
||||
if( object->isVisualOccluder() )
|
||||
_addOccluder( object );
|
||||
|
||||
// If this isn't the root zone and the object is zone space,
|
||||
// see if we should automatically connect the two.
|
||||
|
||||
if( !isRootZone() && object->getTypeMask() & ZoneObjectType )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
|
||||
|
||||
// Don't connect a zone space that has the same closed off status
|
||||
// that we have except it is assigned to the same zone group.
|
||||
|
||||
if( zoneSpace &&
|
||||
( zoneSpace->mZoneFlags.test( ZoneFlag_IsClosedOffSpace ) != mZoneFlags.test( ZoneFlag_IsClosedOffSpace ) ||
|
||||
( zoneSpace->getZoneGroup() == getZoneGroup() &&
|
||||
zoneSpace->getZoneGroup() != InvalidZoneGroup ) ) &&
|
||||
_automaticallyConnectZoneSpace( zoneSpace ) )
|
||||
{
|
||||
connectZoneSpace( zoneSpace );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::_onZoneRemoveObject( SceneObject* object )
|
||||
{
|
||||
if( object->isVisualOccluder() )
|
||||
_removeOccluder( object );
|
||||
|
||||
if( !isRootZone() && object->getTypeMask() & ZoneObjectType )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
|
||||
if( zoneSpace )
|
||||
disconnectZoneSpace( zoneSpace );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneZoneSpace::_automaticallyConnectZoneSpace( SceneZoneSpace* zoneSpace ) const
|
||||
{
|
||||
//TODO: This is suboptimal. While it prevents the most blatantly wrong automatic connections,
|
||||
// we need a true polyhedron/polyhedron intersection to accurately determine zone intersection
|
||||
// when it comes to automatic connections.
|
||||
|
||||
U32 numZones = 0;
|
||||
U32 zones[ SceneObject::MaxObjectZones ];
|
||||
|
||||
zoneSpace->getOverlappingZones( getWorldBox(), zones, numZones );
|
||||
|
||||
return ( numZones > 0 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::connectZoneSpace( SceneZoneSpace* zoneSpace )
|
||||
{
|
||||
// If the zone space is already in the list, do nothing.
|
||||
|
||||
for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
|
||||
if( ref->mZoneSpace == zoneSpace )
|
||||
return;
|
||||
|
||||
// Link the zone space to the zone space refs.
|
||||
|
||||
ZoneSpaceRef* ref = smZoneSpaceRefChunker.alloc();
|
||||
|
||||
ref->mZoneSpace = zoneSpace;
|
||||
ref->mNext = mConnectedZoneSpaces;
|
||||
|
||||
mConnectedZoneSpaces = ref;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SceneZoneSpace] Connecting %i-%i to %i-%i",
|
||||
getZoneRangeStart(), getZoneRangeStart() + getZoneRange(),
|
||||
zoneSpace->getZoneRangeStart(), zoneSpace->getZoneRangeStart() + zoneSpace->getZoneRange()
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::disconnectZoneSpace( SceneZoneSpace* zoneSpace )
|
||||
{
|
||||
ZoneSpaceRef* prev = NULL;
|
||||
for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; prev = ref, ref = ref->mNext )
|
||||
if( ref->mZoneSpace == zoneSpace )
|
||||
{
|
||||
if( prev )
|
||||
prev->mNext = ref->mNext;
|
||||
else
|
||||
mConnectedZoneSpaces = ref->mNext;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SceneZoneSpace] Disconnecting %i-%i from %i-%i",
|
||||
getZoneRangeStart(), getZoneRangeStart() + getZoneRange(),
|
||||
zoneSpace->getZoneRangeStart(), zoneSpace->getZoneRangeStart() + zoneSpace->getZoneRange()
|
||||
);
|
||||
#endif
|
||||
|
||||
smZoneSpaceRefChunker.free( ref );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::_disconnectAllZoneSpaces()
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
if( mConnectedZoneSpaces != NULL )
|
||||
Platform::outputDebugString( "[SceneZoneSpace] Disconnecting all from %i-%i",
|
||||
getZoneRangeStart(), getZoneRangeStart() + getZoneRange()
|
||||
);
|
||||
#endif
|
||||
|
||||
for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; )
|
||||
{
|
||||
ZoneSpaceRef* next = ref->mNext;
|
||||
smZoneSpaceRefChunker.free( ref );
|
||||
ref = next;
|
||||
}
|
||||
mConnectedZoneSpaces = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::_addOccluder( SceneObject* object )
|
||||
{
|
||||
AssertFatal( !mOccluders.contains( object ), "SceneZoneSpace::_addOccluder - Occluder already added to this zone space!" );
|
||||
mOccluders.push_back( object );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::_removeOccluder( SceneObject* object )
|
||||
{
|
||||
const U32 numOccluders = mOccluders.size();
|
||||
for( U32 i = 0; i < numOccluders; ++ i )
|
||||
if( mOccluders[ i ] == object )
|
||||
{
|
||||
mOccluders.erase_fast( i );
|
||||
break;
|
||||
}
|
||||
|
||||
AssertFatal( !mOccluders.contains( object ), "SceneZoneSpace::_removeOccluder - Occluder still added to this zone space!" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::_addOccludersToCullingState( SceneCullingState* state ) const
|
||||
{
|
||||
const U32 numOccluders = mOccluders.size();
|
||||
for( U32 i = 0; i < numOccluders; ++ i )
|
||||
state->addOccluder( mOccluders[ i ] );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::_traverseConnectedZoneSpaces( SceneTraversalState* state )
|
||||
{
|
||||
// Hand the traversal over to all connected zone spaces.
|
||||
|
||||
for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = ref->mZoneSpace;
|
||||
zoneSpace->traverseZones( state );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpace::dumpZoneState( bool update )
|
||||
{
|
||||
// Nothing to dump if not registered.
|
||||
|
||||
if( !mManager )
|
||||
return;
|
||||
|
||||
// If we should update, trigger rezoning for the space
|
||||
// we occupy.
|
||||
|
||||
if( update )
|
||||
mManager->_rezoneObjects( getWorldBox() );
|
||||
|
||||
Con::printf( "====== Zones in: %s =====", describeSelf().c_str() );
|
||||
|
||||
// Dump connections.
|
||||
|
||||
for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
|
||||
Con::printf( "Connected to: %s", ref->mZoneSpace->describeSelf().c_str() );
|
||||
|
||||
// Dump objects.
|
||||
|
||||
for( U32 i = 0; i < getZoneRange(); ++ i )
|
||||
{
|
||||
U32 zoneId = getZoneRangeStart() + i;
|
||||
|
||||
Con::printf( "--- Zone %i", zoneId );
|
||||
|
||||
for( SceneZoneSpaceManager::ZoneContentIterator iter( mManager, zoneId, false ); iter.isValid(); ++ iter )
|
||||
Con::printf( iter->describeSelf() );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneZoneSpace::_setZoneGroup( void* object, const char* index, const char* data )
|
||||
{
|
||||
SceneZoneSpace* zone = reinterpret_cast< SceneZoneSpace* >( object );
|
||||
zone->setZoneGroup( EngineUnmarshallData< S32 >()( data ) );
|
||||
return false;
|
||||
}
|
||||
298
Engine/source/scene/zones/sceneZoneSpace.h
Normal file
298
Engine/source/scene/zones/sceneZoneSpace.h
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SCENEZONESPACE_H_
|
||||
#define _SCENEZONESPACE_H_
|
||||
|
||||
#ifndef _SCENESPACE_H_
|
||||
#include "scene/sceneSpace.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
|
||||
class SceneZoneSpaceManager;
|
||||
class SceneCullingState;
|
||||
|
||||
|
||||
/// Abstract base class for an object that manages zones in a scene.
|
||||
///
|
||||
/// This class adds the ability to SceneSpace to define and manage zones within the object's
|
||||
/// space. Zones are used to determine visibility in a scene.
|
||||
///
|
||||
/// Each zone space manages one or more zones in a scene. All the zones must be within the
|
||||
/// AABB of the zone space but within that AABB, the zone space is free to distribute and
|
||||
/// manage zones in arbitrary fashion.
|
||||
///
|
||||
/// For scene traversal, zone spaces are interconnected. By default, zone spaces get a chance
|
||||
/// to connect to each other when being moved into each other's zones. An exception to this
|
||||
/// is the root zone since it is both immobile and without position and (limited) extents. If
|
||||
/// a zone space wants to connect to the root zone, it must do so manually (same goes for
|
||||
/// disconnecting).
|
||||
class SceneZoneSpace : public SceneSpace
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SceneSpace Parent;
|
||||
|
||||
friend class SceneZoneSpaceManager;
|
||||
friend class SceneRootZone; // mZoneFlags, connectZoneSpace, disconnectZoneSpace
|
||||
|
||||
enum
|
||||
{
|
||||
InvalidZoneGroup = 0
|
||||
};
|
||||
|
||||
enum ZoneFlags
|
||||
{
|
||||
/// Whether this zoning space is "closed off" or not. When connections between
|
||||
/// zoning spaces are established, by default only spaces that are closed off are
|
||||
/// connected to those that are *not* and vice versa. This defines a natural progression
|
||||
/// where spaces that explicitly want to propagate traversals are connected to those
|
||||
/// that want to contain it.
|
||||
///
|
||||
/// This flag is set by default.
|
||||
ZoneFlag_IsClosedOffSpace = BIT( 0 ),
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
enum
|
||||
{
|
||||
ZoneGroupMask = Parent::NextFreeMask << 0,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
/// The manager to which this zone space is registered.
|
||||
SceneZoneSpaceManager* mManager;
|
||||
|
||||
/// ID of first zone defined by object.
|
||||
U32 mZoneRangeStart;
|
||||
|
||||
/// Number of zones managed by #obj. IDs are consecutive
|
||||
/// starting with #mZoneRangeStart.
|
||||
U32 mNumZones;
|
||||
|
||||
/// Group which the zone is assigned to. Zone spaces that would normally not connect
|
||||
/// do connect if they are assigned the same zone group. O to disable (default).
|
||||
U32 mZoneGroup;
|
||||
|
||||
///
|
||||
BitSet32 mZoneFlags;
|
||||
|
||||
/// @name Occluders
|
||||
///
|
||||
/// Zone spaces keep track of the occluders that get added to them so that during
|
||||
/// traversal, they can be taken on board as early as possible. This allows traversals
|
||||
/// themselves to take occlusion into account.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Occluders in this space. Every object that is assigned to this space
|
||||
/// and has the OccluderObjectType flag set, is added to this list.
|
||||
Vector< SceneObject* > mOccluders;
|
||||
|
||||
/// Add the given object to the list of occluders in this zone space.
|
||||
void _addOccluder( SceneObject* object );
|
||||
|
||||
/// Remove the given object from the list of occluders in this zone space.
|
||||
void _removeOccluder( SceneObject* object );
|
||||
|
||||
/// Register the occluders in this zone with the given culling state.
|
||||
void _addOccludersToCullingState( SceneCullingState* state ) const;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Zone Space Connectivity
|
||||
/// @{
|
||||
|
||||
//TODO: we should have both automatic and manual connections; only automatic connections
|
||||
// should get reset when a zone space moves
|
||||
|
||||
/// Link to another zone space.
|
||||
struct ZoneSpaceRef
|
||||
{
|
||||
// We could be storing zone IDs here to connect individual zones rather than just
|
||||
// the managers but since no one requires this at the moment, the code doesn't do it.
|
||||
// However, it's trivial to add.
|
||||
|
||||
SceneZoneSpace* mZoneSpace;
|
||||
ZoneSpaceRef* mNext;
|
||||
};
|
||||
|
||||
/// List of zone spaces that this space is connected to. This is used
|
||||
/// for traversals.
|
||||
ZoneSpaceRef* mConnectedZoneSpaces;
|
||||
|
||||
/// Allocator for ZoneSpaceRefs.
|
||||
static ClassChunker< ZoneSpaceRef > smZoneSpaceRefChunker;
|
||||
|
||||
/// Disconnect all zone spaces currently connected to this space.
|
||||
virtual void _disconnectAllZoneSpaces();
|
||||
|
||||
/// Hand the traversal over to connected zone spaces.
|
||||
virtual void _traverseConnectedZoneSpaces( SceneTraversalState* state );
|
||||
|
||||
/// Return true if the given zone space, which has crossed into this space, should
|
||||
/// be automatically connected. Note that event
|
||||
virtual bool _automaticallyConnectZoneSpace( SceneZoneSpace* zoneSpace ) const;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name SceneManager Notifications
|
||||
///
|
||||
/// These methods are called when SceneManager assigns or removes objects to/from our zones.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Called by the SceneManager when an object is added to one or more zones that
|
||||
/// are managed by this object.
|
||||
virtual void _onZoneAddObject( SceneObject* object, const U32* zoneIDs, U32 numZones );
|
||||
|
||||
/// Called by the SceneManager when an object that was previously added to one or more
|
||||
/// zones managed by this object is now removed.
|
||||
virtual void _onZoneRemoveObject( SceneObject* object );
|
||||
|
||||
/// @}
|
||||
|
||||
// SceneObject.
|
||||
virtual void onSceneRemove();
|
||||
|
||||
public:
|
||||
|
||||
SceneZoneSpace();
|
||||
virtual ~SceneZoneSpace();
|
||||
|
||||
/// Return true if this is the outdoor zone.
|
||||
bool isRootZone() const { return ( getZoneRangeStart() == 0 ); }
|
||||
|
||||
/// Gets the index of the first zone this object manages in the collection of zones or 0xFFFFFFFF if the
|
||||
/// object is not managing zones.
|
||||
U32 getZoneRangeStart() const { return mZoneRangeStart; }
|
||||
|
||||
/// Return the number of zones that are managed by this object.
|
||||
U32 getZoneRange() const { return mNumZones; }
|
||||
|
||||
/// Return the zone group that this zone space belongs to. 0 by default which means the
|
||||
/// zone space is not allocated to a specific zone group.
|
||||
U32 getZoneGroup() const { return mZoneGroup; }
|
||||
|
||||
/// Set the zone group of this zone space. Zone spaces in the same group will connect even if
|
||||
/// not connecting by default. Set to 0 to deactivate.
|
||||
void setZoneGroup( U32 group );
|
||||
|
||||
/// Dump a listing of all objects assigned to this zone space to the console as well
|
||||
/// as a list of all connected spaces.
|
||||
///
|
||||
/// @param update Whether to update the zone contents before dumping. Since the zoning states of
|
||||
/// SceneObjects are updated lazily, the contents of a zone can be outdated.
|
||||
void dumpZoneState( bool update = true );
|
||||
|
||||
/// Get the ambient light color of the given zone in this space or return false if the
|
||||
/// given zone does not have an ambient light color assigned to it.
|
||||
virtual bool getZoneAmbientLightColor( U32 zone, ColorF& outColor ) const { return false; }
|
||||
|
||||
/// @name Containment Tests
|
||||
/// @{
|
||||
|
||||
///
|
||||
virtual bool getOverlappingZones( const Box3F& aabb, U32* zones, U32& numZones ) = 0;
|
||||
|
||||
/// Find the zones in this object that @a obj is part of.
|
||||
///
|
||||
/// @param obj Object in question.
|
||||
/// @param outZones Indices of zones containing the object. Must have at least as many entries
|
||||
/// as there as zones in this object or SceneObject::MaxObjectZones, whichever is smaller.
|
||||
/// Note that implementations should never write more than SceneObject::MaxObjectZones entries.
|
||||
/// @param outNumZones Number of elements in the returned array.
|
||||
///
|
||||
/// @return Return true if the world box of @a obj is fully contained within the zones of this object or
|
||||
/// false if it is at least partially outside of them.
|
||||
virtual bool getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones );
|
||||
|
||||
/// Returns the ID of the zone that are managed by this object that contains @a p.
|
||||
/// @param p Point to test.
|
||||
/// @return ID of the zone containing @a p or InvalidZoneId if none of the zones defined by this
|
||||
/// object contain the point.
|
||||
virtual U32 getPointZone( const Point3F& p ) = 0;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Connectivity
|
||||
/// @{
|
||||
|
||||
/// Connect this zone space to the given zone space.
|
||||
///
|
||||
/// @param zoneSpace A zone space.
|
||||
///
|
||||
/// @note Connectivity is reset when a zone space is moved!
|
||||
virtual void connectZoneSpace( SceneZoneSpace* zoneSpace );
|
||||
|
||||
/// If the object is a zone space, then this method is called to instruct the object
|
||||
/// to remove any zone connectivity to @a zoneSpace.
|
||||
///
|
||||
/// @param zoneSpace A zone space which had previously been passed to connectZoneSpace().
|
||||
virtual void disconnectZoneSpace( SceneZoneSpace* zoneSpace );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Traversals
|
||||
/// @{
|
||||
|
||||
/// Traverse into the zones of this space. Set the render states of those zones and add the frustums
|
||||
/// that lead into them.
|
||||
///
|
||||
/// The traversal stack is expected to not be empty and the topmost entry on the stack should be the
|
||||
/// zone of another manager from which traversal is handed over to this manager.
|
||||
///
|
||||
/// @param state Scene traversal state.
|
||||
///
|
||||
/// @note If the zone's of a zone space are reached via different traversal paths, this method
|
||||
/// will be called multiple times on the same space.
|
||||
virtual void traverseZones( SceneTraversalState* state ) = 0;
|
||||
|
||||
/// Traverse the zones in this space starting with the given zone. Where appropriate, traversal
|
||||
/// should be handed off to connected spaces.
|
||||
///
|
||||
/// @param state State which should be updated by the traversal.
|
||||
/// @param startZoneId ID of zone in this manager in which to start traversal.
|
||||
virtual void traverseZones( SceneTraversalState* state, U32 startZoneId ) = 0;
|
||||
|
||||
/// @}
|
||||
|
||||
// SimObject.
|
||||
virtual bool writeField( StringTableEntry fieldName, const char* value );
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
// NetObject.
|
||||
virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream );
|
||||
virtual void unpackUpdate( NetConnection* connection, BitStream* stream );
|
||||
|
||||
private:
|
||||
|
||||
static bool _setZoneGroup( void* object, const char* index, const char* data );
|
||||
};
|
||||
|
||||
#endif // !_SCENEZONESPACE_H_
|
||||
923
Engine/source/scene/zones/sceneZoneSpaceManager.cpp
Normal file
923
Engine/source/scene/zones/sceneZoneSpaceManager.cpp
Normal file
|
|
@ -0,0 +1,923 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "scene/zones/sceneZoneSpaceManager.h"
|
||||
|
||||
#include "platform/profiler.h"
|
||||
#include "platform/platformMemory.h"
|
||||
#include "scene/sceneContainer.h"
|
||||
#include "scene/zones/sceneRootZone.h"
|
||||
#include "scene/zones/sceneZoneSpace.h"
|
||||
|
||||
|
||||
// Uncomment to enable verification code for debugging. This slows the
|
||||
// manager down significantly but will allow to find zoning state corruption
|
||||
// much quicker.
|
||||
//#define DEBUG_VERIFY
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
ClassChunker< SceneObject::ZoneRef > SceneZoneSpaceManager::smZoneRefChunker;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneZoneSpaceManager::SceneZoneSpaceManager( SceneContainer* container )
|
||||
: mContainer( container ),
|
||||
mRootZone( new SceneRootZone() ),
|
||||
mNumTotalAllocatedZones( 0 ),
|
||||
mNumActiveZones( 0 ),
|
||||
mDirtyArea( Box3F::Invalid )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mZoneSpaces );
|
||||
VECTOR_SET_ASSOCIATION( mZoneLists );
|
||||
VECTOR_SET_ASSOCIATION( mZoneSpacesQueryList );
|
||||
VECTOR_SET_ASSOCIATION( mDirtyObjects );
|
||||
VECTOR_SET_ASSOCIATION( mDirtyZoneSpaces );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneZoneSpaceManager::~SceneZoneSpaceManager()
|
||||
{
|
||||
// Delete root zone.
|
||||
SAFE_DELETE( mRootZone );
|
||||
|
||||
mNumTotalAllocatedZones = 0;
|
||||
mNumActiveZones = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::registerZones( SceneZoneSpace* object, U32 numZones )
|
||||
{
|
||||
AssertFatal( _getZoneSpaceIndex( object ) == -1, "SceneZoneSpaceManager::registerZones - Object already registered" );
|
||||
_compactZonesCheck();
|
||||
|
||||
const U32 zoneRangeStart = mNumTotalAllocatedZones;
|
||||
|
||||
mNumTotalAllocatedZones += numZones;
|
||||
mNumActiveZones += numZones;
|
||||
|
||||
object->mNumZones = numZones;
|
||||
object->mZoneRangeStart = zoneRangeStart;
|
||||
|
||||
// Allocate zone lists for all of the zones managed by the object.
|
||||
// Add an entry to each list that points back to the zone space.
|
||||
|
||||
mZoneLists.increment( numZones );
|
||||
for( U32 i = zoneRangeStart; i < mNumTotalAllocatedZones; ++ i )
|
||||
{
|
||||
SceneObject::ZoneRef* zoneRef = smZoneRefChunker.alloc();
|
||||
|
||||
zoneRef->object = object;
|
||||
zoneRef->nextInBin = NULL;
|
||||
zoneRef->prevInBin = NULL;
|
||||
zoneRef->nextInObj = NULL;
|
||||
zoneRef->zone = i;
|
||||
|
||||
mZoneLists[ i ] = zoneRef;
|
||||
}
|
||||
|
||||
// Add space to list.
|
||||
|
||||
mZoneSpaces.push_back( object );
|
||||
object->mManager = this;
|
||||
|
||||
// Set ZoneObjectType.
|
||||
|
||||
object->mTypeMask |= ZoneObjectType;
|
||||
|
||||
// Put the object on the dirty list.
|
||||
|
||||
if( !object->isRootZone() )
|
||||
{
|
||||
// Make sure the object gets on the zone space list even
|
||||
// if it is already on the object dirty list.
|
||||
object->mZoneRefDirty = false;
|
||||
|
||||
notifyObjectChanged( object );
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i allocated to: %s",
|
||||
zoneRangeStart, numZones, object->describeSelf().c_str() );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::unregisterZones( SceneZoneSpace* object )
|
||||
{
|
||||
S32 zoneSpaceIndex = _getZoneSpaceIndex( object );
|
||||
|
||||
AssertFatal( zoneSpaceIndex != -1, "SceneZoneSpaceManager::unregisterZones - Object not registered as zone space" );
|
||||
AssertFatal( mNumActiveZones >= object->mNumZones, "SceneZoneSpaceManager::unregisterZones - Too many zones removed");
|
||||
|
||||
const U32 zoneRangeStart = object->getZoneRangeStart();
|
||||
const U32 numZones = object->getZoneRange();
|
||||
|
||||
// Destroy the zone lists for the zones registered
|
||||
// by the object.
|
||||
|
||||
for( U32 j = zoneRangeStart; j < zoneRangeStart + numZones; j ++ )
|
||||
{
|
||||
// Delete all object links.
|
||||
|
||||
_clearZoneList( j );
|
||||
|
||||
// Delete the first link which refers to the zone itself.
|
||||
|
||||
smZoneRefChunker.free( mZoneLists[ j ] );
|
||||
mZoneLists[ j ] = NULL;
|
||||
}
|
||||
|
||||
// Destroy the connections the zone space has.
|
||||
|
||||
object->_disconnectAllZoneSpaces();
|
||||
|
||||
// Remove the zone manager entry.
|
||||
|
||||
mNumActiveZones -= numZones;
|
||||
mZoneSpaces.erase( zoneSpaceIndex );
|
||||
|
||||
// Clear ZoneObjectType.
|
||||
|
||||
object->mTypeMask &= ~ZoneObjectType;
|
||||
|
||||
// Clear zone assignments.
|
||||
|
||||
object->mZoneRangeStart = InvalidZoneId;
|
||||
object->mNumZones = 0;
|
||||
object->mManager = NULL;
|
||||
|
||||
// Mark the zone space's area as dirty.
|
||||
|
||||
mDirtyArea.intersect( object->getWorldBox() );
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i released from: %s",
|
||||
zoneRangeStart, numZones, object->describeSelf().c_str() );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_rezoneObjects( const Box3F& area )
|
||||
{
|
||||
static Vector< SceneObject* > sObjects( __FILE__, __LINE__ );
|
||||
|
||||
// Find all objects in the area. We cannot use the callback
|
||||
// version here and directly trigger rezoning since the rezoning
|
||||
// itself does a container query.
|
||||
|
||||
sObjects.clear();
|
||||
mContainer->findObjectList( area, 0xFFFFFFFF, &sObjects );
|
||||
|
||||
// Rezone the objects.
|
||||
|
||||
const U32 numObjects = sObjects.size();
|
||||
for( U32 i = 0; i < numObjects; ++ i )
|
||||
{
|
||||
SceneObject* object = sObjects[ i ];
|
||||
if( object != getRootZone() )
|
||||
_rezoneObject( object );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_compactZonesCheck()
|
||||
{
|
||||
if( mNumActiveZones > ( mNumTotalAllocatedZones / 2 ) )
|
||||
return;
|
||||
|
||||
// Redistribute the zone IDs among the current zone spaces
|
||||
// so that the range of IDs is consecutive.
|
||||
|
||||
const U32 numZoneSpaces = mZoneSpaces.size();
|
||||
U32 nextZoneId = 0;
|
||||
|
||||
Vector< SceneObject::ZoneRef* > newZoneLists;
|
||||
newZoneLists.setSize( mNumActiveZones );
|
||||
|
||||
for( U32 i = 0; i < numZoneSpaces; ++ i )
|
||||
{
|
||||
SceneZoneSpace* space = mZoneSpaces[ i ];
|
||||
|
||||
const U32 oldZoneRangeStart = space->getZoneRangeStart();
|
||||
const U32 newZoneRangeStart = nextZoneId;
|
||||
const U32 numZones = space->getZoneRange();
|
||||
|
||||
// Assign the new zone range start.
|
||||
|
||||
space->mZoneRangeStart = newZoneRangeStart;
|
||||
nextZoneId += numZones;
|
||||
|
||||
// Relocate the zone lists to match the new zone IDs and update
|
||||
// the contents of the zone lists to match the new IDs.
|
||||
|
||||
for( U32 n = 0; n < numZones; ++ n )
|
||||
{
|
||||
const U32 newZoneId = newZoneRangeStart + n;
|
||||
const U32 oldZoneId = oldZoneRangeStart + n;
|
||||
|
||||
// Relocate list.
|
||||
|
||||
newZoneLists[ newZoneId ] = mZoneLists[ oldZoneId ];
|
||||
|
||||
// Update entries.
|
||||
|
||||
for( SceneObject::ZoneRef* ref = newZoneLists[ newZoneId ]; ref != NULL; ref = ref->nextInBin )
|
||||
ref->zone = newZoneId;
|
||||
}
|
||||
}
|
||||
|
||||
mNumTotalAllocatedZones = nextZoneId;
|
||||
mZoneLists = newZoneLists;
|
||||
|
||||
AssertFatal( mNumTotalAllocatedZones == mNumActiveZones, "SceneZoneSpaceManager::_compactZonesCheck - Error during compact; mismatch between active and allocated zones" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
S32 SceneZoneSpaceManager::_getZoneSpaceIndex( SceneZoneSpace* object ) const
|
||||
{
|
||||
const U32 numZoneSpaces = getNumZoneSpaces();
|
||||
for( U32 i = 0; i < numZoneSpaces; ++ i )
|
||||
if( mZoneSpaces[ i ] == object )
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::findZone( const Point3F& p, SceneZoneSpace*& owner, U32& zone ) const
|
||||
{
|
||||
AssertFatal( mNumActiveZones >= 1, "SceneZoneSpaceManager::findZone - Must have at least one active zone in scene (outdoor zone)" );
|
||||
|
||||
// If there are no zones in the level other than the outdoor
|
||||
// zone, just return that.
|
||||
|
||||
if( mNumActiveZones == 1 )
|
||||
{
|
||||
owner = getRootZone();
|
||||
zone = RootZoneId;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PROFILE_SCOPE( SceneZoneSpaceManager_findZone );
|
||||
|
||||
// Query the scene container for zones with a query
|
||||
// box that tightly fits around the point.
|
||||
|
||||
Box3F queryBox( p.x - 0.1f, p.y - 0.1f, p.z - 0.1f,
|
||||
p.x + 0.1f, p.y + 0.1f, p.z + 0.1f );
|
||||
|
||||
_queryZoneSpaces( queryBox );
|
||||
|
||||
// Go through the zones and look for the first one that
|
||||
// contains the given point.
|
||||
|
||||
const U32 numZones = mZoneSpacesQueryList.size();
|
||||
for( U32 i = 0; i < numZones; ++ i )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
|
||||
if( !zoneSpace )
|
||||
continue;
|
||||
|
||||
AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZone - SceneRootZone returned by zone manager query" );
|
||||
|
||||
// If the point is in one of the zones of this manager,
|
||||
// then make this the result.
|
||||
|
||||
U32 inZone = zoneSpace->getPointZone( p );
|
||||
if( inZone != InvalidZoneId )
|
||||
{
|
||||
owner = zoneSpace;
|
||||
zone = inZone;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No other zone matched so return the outdoor zone.
|
||||
|
||||
owner = getRootZone();
|
||||
zone = RootZoneId;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 SceneZoneSpaceManager::findZones( const Box3F& area, Vector< U32 >& outZones ) const
|
||||
{
|
||||
// Query all zone spaces in the area.
|
||||
|
||||
_queryZoneSpaces( area );
|
||||
|
||||
// Query each zone space for overlaps with the given
|
||||
// area and add the zones to outZones.
|
||||
|
||||
bool outsideIncluded = false;
|
||||
U32 numTotalZones = 0;
|
||||
|
||||
const U32 numZoneSpaces = mZoneSpacesQueryList.size();
|
||||
for( U32 i = 0; i < numZoneSpaces; ++ i )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
|
||||
if( !zoneSpace )
|
||||
continue;
|
||||
|
||||
AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZones - SceneRootZone returned by zone manager query" );
|
||||
|
||||
// Query manager.
|
||||
|
||||
U32 zones[ SceneObject::MaxObjectZones ];
|
||||
U32 numZones = 0;
|
||||
|
||||
outsideIncluded |= zoneSpace->getOverlappingZones( area, zones, numZones );
|
||||
|
||||
// Add overlapped zones.
|
||||
|
||||
for( U32 n = 0; n < numZones; n ++ )
|
||||
{
|
||||
outZones.push_back( zones[ n ] );
|
||||
numTotalZones ++;
|
||||
}
|
||||
}
|
||||
|
||||
// If the area box wasn't fully enclosed by the zones of the
|
||||
// manager(s) or the query only returned the outside zone,
|
||||
// add the outside zone to the list.
|
||||
|
||||
if( outsideIncluded || numTotalZones == 0 )
|
||||
{
|
||||
outZones.push_back( RootZoneId );
|
||||
numTotalZones ++;
|
||||
}
|
||||
|
||||
return numTotalZones;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_rezoneObject( SceneObject* object )
|
||||
{
|
||||
PROFILE_SCOPE( SceneZoneSpaceManager_rezoneObject );
|
||||
|
||||
AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_rezoneObject - Cannot rezone the SceneRootZone!" );
|
||||
|
||||
// If the object is not yet assigned to zones,
|
||||
// do so now and return.
|
||||
|
||||
if( !object->mNumCurrZones )
|
||||
{
|
||||
_zoneInsert( object );
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have no zones in the scene other than the outdoor zone or if the
|
||||
// object has global bounds on (and thus is always in the outdoor zone) or
|
||||
// is an object that is restricted to the outdoor zone, leave the object's
|
||||
// zoning state untouched.
|
||||
|
||||
if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK )
|
||||
{
|
||||
object->mZoneRefDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// First, find out whether there's even a chance of the zoning to have changed
|
||||
// for the object.
|
||||
|
||||
_queryZoneSpaces( object->getWorldBox() );
|
||||
|
||||
const U32 numZoneSpaces = mZoneSpacesQueryList.size();
|
||||
if( !numZoneSpaces )
|
||||
{
|
||||
// There is no zone in the object's area. If it is already assigned to the
|
||||
// root zone, then we don't need an update. Otherwise, we do.
|
||||
|
||||
if( object->mNumCurrZones == 1 &&
|
||||
object->mZoneRefHead &&
|
||||
object->mZoneRefHead->zone == RootZoneId )
|
||||
{
|
||||
object->mZoneRefDirty = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the object's zoning information by removing and recomputing
|
||||
// its zoning information.
|
||||
|
||||
_zoneRemove( object );
|
||||
_zoneInsert( object, true ); // Query already in place.
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::registerObject( SceneObject* object )
|
||||
{
|
||||
// Just put it on the dirty list.
|
||||
|
||||
notifyObjectChanged( object );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::unregisterObject( SceneObject* object )
|
||||
{
|
||||
// Remove from dirty list.
|
||||
|
||||
mDirtyObjects.remove( object );
|
||||
|
||||
// Remove from zone lists.
|
||||
|
||||
_zoneRemove( object );
|
||||
|
||||
// If it's a zone space, unregister it.
|
||||
|
||||
if( object->getTypeMask() & ZoneObjectType && dynamic_cast< SceneZoneSpace* >( object ) )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = static_cast< SceneZoneSpace* >( object );
|
||||
unregisterZones( zoneSpace );
|
||||
mDirtyZoneSpaces.remove( zoneSpace );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::updateObject( SceneObject* object )
|
||||
{
|
||||
// If no zone spaces have changed and the object's zoning
|
||||
// state is clean, then there's nothing to do for this object.
|
||||
|
||||
if( mDirtyZoneSpaces.empty() && !object->mZoneRefDirty )
|
||||
return;
|
||||
|
||||
// Otherwise update all the dirty zoning state.
|
||||
|
||||
updateZoningState();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::notifyObjectChanged( SceneObject* object )
|
||||
{
|
||||
AssertFatal( object != getRootZone(), "SceneZoneSpaceManager::notifyObjectChanged - Cannot dirty root zone!" );
|
||||
|
||||
// Ignore if object is already on the dirty list.
|
||||
|
||||
if( object->mZoneRefDirty )
|
||||
return;
|
||||
|
||||
// Put the object on the respective dirty list.
|
||||
|
||||
if( object->getTypeMask() & ZoneObjectType )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object );
|
||||
AssertFatal( zoneSpace != NULL, "SceneZoneSpaceManager::notifyObjectChanged - ZoneObjectType is not a SceneZoneSpace!" );
|
||||
|
||||
if( zoneSpace )
|
||||
mDirtyZoneSpaces.push_back( zoneSpace );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDirtyObjects.push_back( object );
|
||||
}
|
||||
|
||||
// Mark object as dirty.
|
||||
|
||||
object->mZoneRefDirty = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::updateZoningState()
|
||||
{
|
||||
// If there are no dirty objects, there's nothing to do.
|
||||
|
||||
if( mDirtyObjects.empty() &&
|
||||
mDirtyZoneSpaces.empty() &&
|
||||
mDirtyArea == Box3F::Invalid )
|
||||
return;
|
||||
|
||||
// Otherwise, first update the zone spaces. Do this in two passes:
|
||||
// first take all the dirty zone spaces out of the zoning state and
|
||||
// then rezone the combined area of all dirty zone spaces.
|
||||
//
|
||||
// Note that this path here is pretty much only relevant during loading
|
||||
// or editing and thus can be less performant than the path for individual
|
||||
// objects below.
|
||||
|
||||
while( !mDirtyZoneSpaces.empty() )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = mDirtyZoneSpaces.last();
|
||||
mDirtyZoneSpaces.decrement();
|
||||
|
||||
// Remove the zoning state of the object.
|
||||
|
||||
_zoneRemove( zoneSpace );
|
||||
|
||||
// Destroy all connections that this zone space has to
|
||||
// other zone spaces.
|
||||
|
||||
zoneSpace->_disconnectAllZoneSpaces();
|
||||
|
||||
// Nuke its zone lists.
|
||||
|
||||
const U32 numZones = zoneSpace->getZoneRange();
|
||||
for( U32 n = 0; n < numZones; ++ n )
|
||||
_clearZoneList( zoneSpace->getZoneRangeStart() + n );
|
||||
|
||||
// Merge into dirty region.
|
||||
|
||||
mDirtyArea.intersect( zoneSpace->getWorldBox() );
|
||||
}
|
||||
|
||||
if( mDirtyArea != Box3F::Invalid )
|
||||
{
|
||||
// Rezone everything in the dirty region.
|
||||
|
||||
_rezoneObjects( mDirtyArea );
|
||||
mDirtyArea = Box3F::Invalid;
|
||||
|
||||
// Verify zoning state.
|
||||
|
||||
#ifdef DEBUG_VERIFY
|
||||
verifyState();
|
||||
#endif
|
||||
|
||||
// Fire the zoning changed signal to let interested parties
|
||||
// know that the zoning setup of the scene has changed.
|
||||
|
||||
getZoningChangedSignal().trigger( this );
|
||||
}
|
||||
|
||||
// And finally, update objects that have changed state.
|
||||
|
||||
while( !mDirtyObjects.empty() )
|
||||
{
|
||||
SceneObject* object = mDirtyObjects.last();
|
||||
mDirtyObjects.decrement();
|
||||
|
||||
if( object->mZoneRefDirty )
|
||||
_rezoneObject( object );
|
||||
|
||||
AssertFatal( !object->mZoneRefDirty, "SceneZoneSpaceManager::updateZoningState - Object still dirty!" );
|
||||
}
|
||||
|
||||
AssertFatal( mDirtyObjects.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty objects!" );
|
||||
AssertFatal( mDirtyZoneSpaces.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty zones!" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_zoneInsert( SceneObject* object, bool queryListInitialized )
|
||||
{
|
||||
PROFILE_SCOPE( SceneZoneSpaceManager_zoneInsert );
|
||||
|
||||
AssertFatal( object->mNumCurrZones == 0, "SceneZoneSpaceManager::_zoneInsert - Object already in zone list" );
|
||||
AssertFatal( object->getContainer() != NULL, "SceneZoneSpaceManager::_zoneInsert - Object must be in scene" );
|
||||
AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_zoneInsert - Must not be called on SceneRootZone" );
|
||||
|
||||
// If all we have is a single zone in the scene, then it must
|
||||
// be the outdoor zone. Simply assign the object to it. Also do this
|
||||
// if the object has global bounds on since we always assign these to
|
||||
// just the outdoor zone. Finally, also do it for all object types that
|
||||
// we want to restrict to the outdoor zone.
|
||||
|
||||
if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK )
|
||||
_addToOutdoorZone( object );
|
||||
else
|
||||
{
|
||||
// Otherwise find all zones spaces that intersect with the object's
|
||||
// world box.
|
||||
|
||||
if( !queryListInitialized )
|
||||
_queryZoneSpaces( object->getWorldBox() );
|
||||
|
||||
// Go through the zone spaces and link all zones that the object
|
||||
// overlaps.
|
||||
|
||||
bool outsideIncluded = true;
|
||||
const U32 numZoneSpaces = mZoneSpacesQueryList.size();
|
||||
for( U32 i = 0; i < numZoneSpaces; ++ i )
|
||||
{
|
||||
SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] );
|
||||
if( !zoneSpace )
|
||||
continue;
|
||||
|
||||
AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::_zoneInsert - SceneRootZone returned by zone space query" );
|
||||
|
||||
// If we are inserting a zone space, then the query will turn up
|
||||
// the object itself at some point. Skip it.
|
||||
|
||||
if( zoneSpace == object )
|
||||
continue;
|
||||
|
||||
// Find the zones that the object overlaps within
|
||||
// the zone space.
|
||||
|
||||
U32 numZones = 0;
|
||||
U32 zones[ SceneObject::MaxObjectZones ];
|
||||
|
||||
bool overlapsOutside = zoneSpace->getOverlappingZones( object, zones, numZones );
|
||||
AssertFatal( numZones != 0 || overlapsOutside,
|
||||
"SceneZoneSpaceManager::_zoneInsert - Object must be fully contained in one or more zones or intersect the outside zone" );
|
||||
|
||||
outsideIncluded &= overlapsOutside; // Only include outside if *none* of the zones fully contains the object.
|
||||
|
||||
// Link the object to the zones.
|
||||
|
||||
for( U32 n = 0; n < numZones; ++ n )
|
||||
_addToZoneList( zones[ n ], object );
|
||||
|
||||
// Let the zone manager know we have added objects to its
|
||||
// zones.
|
||||
|
||||
if( numZones > 0 )
|
||||
zoneSpace->_onZoneAddObject( object, zones, numZones );
|
||||
}
|
||||
|
||||
// If the object crosses into the outside zone or hasn't been
|
||||
// added to any zone above, add it to the outside zone.
|
||||
|
||||
if( outsideIncluded )
|
||||
_addToOutdoorZone( object );
|
||||
}
|
||||
|
||||
// Mark the zoning state of the object as current.
|
||||
|
||||
object->mZoneRefDirty = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_zoneRemove( SceneObject* obj )
|
||||
{
|
||||
PROFILE_SCOPE( SceneZoneSpaceManager_zoneRemove );
|
||||
|
||||
// Remove the object from the zone lists.
|
||||
|
||||
for( SceneObject::ZoneRef* walk = obj->mZoneRefHead; walk != NULL; )
|
||||
{
|
||||
// Let the zone owner know we are removing an object
|
||||
// from its zones.
|
||||
|
||||
getZoneOwner( walk->zone )->_onZoneRemoveObject( walk->object );
|
||||
|
||||
// Now remove the ZoneRef link this object has in the
|
||||
// zone list of the current zone.
|
||||
|
||||
SceneObject::ZoneRef* remove = walk;
|
||||
walk = walk->nextInObj;
|
||||
|
||||
remove->prevInBin->nextInBin = remove->nextInBin;
|
||||
if( remove->nextInBin )
|
||||
remove->nextInBin->prevInBin = remove->prevInBin;
|
||||
|
||||
smZoneRefChunker.free( remove );
|
||||
}
|
||||
|
||||
// Clear the object's zoning state.
|
||||
|
||||
obj->mZoneRefHead = NULL;
|
||||
obj->mZoneRefDirty = false;
|
||||
obj->mNumCurrZones = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_addToZoneList( U32 zoneId, SceneObject* object )
|
||||
{
|
||||
SceneObject::ZoneRef* zoneList = mZoneLists[ zoneId ];
|
||||
|
||||
AssertFatal( zoneList != NULL, "SceneZoneSpaceManager::_addToZoneList - Zone list not initialized" );
|
||||
AssertFatal( object != zoneList->object, "SCene::_addToZoneList - Cannot add zone to itself" );
|
||||
|
||||
SceneObject::ZoneRef* newRef = smZoneRefChunker.alloc();
|
||||
|
||||
// Add the object to the zone list.
|
||||
|
||||
newRef->zone = zoneId;
|
||||
newRef->object = object;
|
||||
newRef->nextInBin = zoneList->nextInBin;
|
||||
newRef->prevInBin = zoneList;
|
||||
|
||||
if( zoneList->nextInBin )
|
||||
zoneList->nextInBin->prevInBin = newRef;
|
||||
|
||||
zoneList->nextInBin = newRef;
|
||||
|
||||
// Add the zone to the object list.
|
||||
|
||||
newRef->nextInObj = object->mZoneRefHead;
|
||||
object->mZoneRefHead = newRef;
|
||||
object->mNumCurrZones ++;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_clearZoneList( U32 zoneId )
|
||||
{
|
||||
AssertFatal( zoneId < getNumZones(), "SceneZoneSpaceManager::_clearZoneList - Zone ID out of range" );
|
||||
|
||||
SceneObject::ZoneRef* list = mZoneLists[ zoneId ];
|
||||
SceneZoneSpace* zoneSpace = getZoneOwner( zoneId );
|
||||
|
||||
// Go through the objects in the zone list and unlink and
|
||||
// delete their zone entries.
|
||||
|
||||
for( SceneObject::ZoneRef* walk = list->nextInBin; walk != NULL; walk = walk->nextInBin )
|
||||
{
|
||||
SceneObject* object = walk->object;
|
||||
AssertFatal( object != NULL, "SceneZoneSpaceManager::_clearZoneList - Object field not set on link" );
|
||||
|
||||
// The zone entry links on the objects are singly-linked lists
|
||||
// linked through nextInObject so we need to find where in the
|
||||
// objects zone entry list the node for the current zone is.
|
||||
|
||||
SceneObject::ZoneRef** ptrNext = &object->mZoneRefHead;
|
||||
while( *ptrNext && *ptrNext != walk )
|
||||
ptrNext = &( *ptrNext )->nextInObj;
|
||||
|
||||
AssertFatal( *ptrNext == walk, "SceneZoneSpaceManager::_clearZoneList - Zone entry not found on object in zone list!");
|
||||
|
||||
// Unlink and delete the entry.
|
||||
|
||||
*ptrNext = ( *ptrNext )->nextInObj;
|
||||
smZoneRefChunker.free( walk );
|
||||
|
||||
object->mNumCurrZones --;
|
||||
|
||||
// If this is the only zone the object was in, mark
|
||||
// its zoning state as dirty so it will get assigned
|
||||
// to the outdoor zone on the next update.
|
||||
|
||||
if( !object->mZoneRefHead )
|
||||
object->mZoneRefDirty = true;
|
||||
|
||||
// Let the zone know we have removed the object.
|
||||
|
||||
zoneSpace->_onZoneRemoveObject( object );
|
||||
}
|
||||
|
||||
list->nextInBin = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneObject::ZoneRef* SceneZoneSpaceManager::_findInZoneList( U32 zoneId, SceneObject* object ) const
|
||||
{
|
||||
for( SceneObject::ZoneRef* ref = object->mZoneRefHead; ref != NULL; ref = ref->nextInObj )
|
||||
if( ref->zone == zoneId )
|
||||
return ref;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_addToOutdoorZone( SceneObject* object )
|
||||
{
|
||||
AssertFatal( !object->mZoneRefHead || !_findInZoneList( RootZoneId, object ),
|
||||
"SceneZoneSpaceManager::_addToOutdoorZone - Object already added to outdoor zone" );
|
||||
|
||||
// Add the object to the outside's zone list. This method is always called
|
||||
// *last* after the object has already been assigned to any other zone it
|
||||
// intersects. Since we always prepend to the zoning lists, this means that
|
||||
// the outdoor zone will always be *first* in the list of zones that an object
|
||||
// is assigned to which generally is a good order.
|
||||
|
||||
_addToZoneList( RootZoneId, object );
|
||||
|
||||
// Let the zone know we added an object to it.
|
||||
|
||||
const U32 zoneId = RootZoneId;
|
||||
static_cast< SceneZoneSpace* >( getRootZone() )->_onZoneAddObject( object, &zoneId, 1 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::_queryZoneSpaces( const Box3F& area ) const
|
||||
{
|
||||
mZoneSpacesQueryList.clear();
|
||||
mContainer->findObjectList( area, ZoneObjectType, &mZoneSpacesQueryList );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::dumpZoneStates( bool update )
|
||||
{
|
||||
if( update )
|
||||
_rezoneObjects( getRootZone()->getWorldBox() );
|
||||
|
||||
const U32 numZoneSpaces = mZoneSpaces.size();
|
||||
for( U32 i = 0; i < numZoneSpaces; ++ i )
|
||||
mZoneSpaces[ i ]->dumpZoneState( false );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneSpaceManager::verifyState()
|
||||
{
|
||||
AssertFatal( mZoneSpaces.size() <= mNumActiveZones,
|
||||
"SceneZoneSpaceManager::verifyState - More zone spaces than active zones!" );
|
||||
AssertFatal( mNumTotalAllocatedZones >= mNumActiveZones,
|
||||
"SceneZoneSpaceManager::verifyState - Fewer allocated than active zones!" );
|
||||
AssertFatal( mRootZone->getZoneRangeStart() == 0,
|
||||
"SceneZoneSpaceManager::verifyState - Invalid ID on root zone!" );
|
||||
AssertFatal( mRootZone->getZoneRange() == 1,
|
||||
"SceneZoneSpaceManager::verifyState - Invalid zone range on root zone!" );
|
||||
|
||||
// First validate the zone spaces themselves.
|
||||
|
||||
const U32 numZoneSpaces = mZoneSpaces.size();
|
||||
for( U32 i = 0; i < numZoneSpaces; ++ i )
|
||||
{
|
||||
SceneZoneSpace* space = mZoneSpaces[ i ];
|
||||
|
||||
#ifndef TORQUE_DISABLE_MEMORY_MANAGER
|
||||
Memory::checkPtr( space );
|
||||
#endif
|
||||
|
||||
AssertFatal( space->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" );
|
||||
|
||||
const U32 zoneRangeStart = space->getZoneRangeStart();
|
||||
const U32 numZones = space->getZoneRange();
|
||||
|
||||
// Verify each of the allocated zones in this space.
|
||||
|
||||
for( U32 n = 0; n < numZones; ++ n )
|
||||
{
|
||||
const U32 zoneId = zoneRangeStart + n;
|
||||
|
||||
// Simple validation of zone ID.
|
||||
AssertFatal( isValidZoneId( zoneId ), "SceneZoneSpaceManager::verifyState - Zone space is assigned in invalid zone ID!" );
|
||||
|
||||
AssertFatal( mZoneLists[ zoneId ] != NULL, "SceneZoneSpaceManager::verifyState - Zone list missing for zone!" );
|
||||
AssertFatal( mZoneLists[ zoneId ]->object == space, "SceneZoneSpaceManager::verifyState - Zone list entry #0 is not referring back to zone!" );
|
||||
|
||||
for( SceneObject::ZoneRef* ref = mZoneLists[ zoneId ]; ref != NULL; ref = ref->nextInBin )
|
||||
{
|
||||
AssertFatal( ref->zone == zoneId, "SceneZoneSpaceManager::verifyState - Incorrect ID in zone list!" );
|
||||
AssertFatal( ref->object != NULL, "SceneZoneSpaceManager::verifyState - Null object pointer in zone list!" );
|
||||
|
||||
#ifndef TORQUE_DISABLE_MEMORY_MANAGER
|
||||
Memory::checkPtr( ref->object );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure no other zone space owns any of the same IDs.
|
||||
|
||||
for( U32 n = 0; n < numZoneSpaces; ++ n )
|
||||
{
|
||||
if( n == i )
|
||||
continue;
|
||||
|
||||
SceneZoneSpace* otherSpace = mZoneSpaces[ n ];
|
||||
AssertFatal( otherSpace->getZoneRangeStart() >= zoneRangeStart + numZones ||
|
||||
otherSpace->getZoneRangeStart() + otherSpace->getZoneRange() <= zoneRangeStart,
|
||||
"SceneZoneSpaceManager::verifyState - Overlap between zone ID ranges of zone spaces!" );
|
||||
}
|
||||
|
||||
// Make sure that all zone connections appear to be valid.
|
||||
|
||||
for( SceneZoneSpace::ZoneSpaceRef* ref = space->mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
|
||||
{
|
||||
#ifndef TORQUE_DISABLE_MEMORY_MANAGER
|
||||
Memory::checkPtr( ref->mZoneSpace );
|
||||
#endif
|
||||
|
||||
AssertFatal( _getZoneSpaceIndex( ref->mZoneSpace ) != -1, "SceneZoneSpaceManager::verifyState - Zone connected to invalid zone!" );
|
||||
AssertFatal( ref->mZoneSpace->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" );
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: can do a lot more validation here
|
||||
}
|
||||
331
Engine/source/scene/zones/sceneZoneSpaceManager.h
Normal file
331
Engine/source/scene/zones/sceneZoneSpaceManager.h
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SCENEZONESPACEMANAGER_H_
|
||||
#define _SCENEZONESPACEMANAGER_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TSIGNAL_H_
|
||||
#include "core/util/tSignal.h"
|
||||
#endif
|
||||
|
||||
#ifndef _DATACHUNKER_H_
|
||||
#include "core/dataChunker.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
class SceneContainer;
|
||||
class SceneRootZone;
|
||||
class SceneZoneSpace;
|
||||
|
||||
|
||||
/// Object that manages zone spaces in a scene.
|
||||
class SceneZoneSpaceManager
|
||||
{
|
||||
public:
|
||||
|
||||
class ZoneContentIterator;
|
||||
|
||||
friend class SceneZoneSpace; // mZoneLists
|
||||
friend class ZoneContentIterator; // mZoneLists
|
||||
|
||||
/// A signal used to notify that the zone setup of the scene has changed.
|
||||
///
|
||||
/// @note If you use this signal to maintain state that depends on the zoning
|
||||
/// setup, it's best to not immediately update the sate in response to this
|
||||
/// signal. The reason is that during loading and editing, the signal may
|
||||
/// be fired a lot and continuously updating dependent data may waste a lot
|
||||
/// of time.
|
||||
typedef Signal< void( SceneZoneSpaceManager* ) > ZoningChangedSignal;
|
||||
|
||||
enum
|
||||
{
|
||||
/// Zone ID of the exterior zone.
|
||||
RootZoneId = 0,
|
||||
|
||||
/// Constant to indicate an invalid zone ID.
|
||||
InvalidZoneId = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
/// Iterator for the contents of a given zone.
|
||||
class ZoneContentIterator
|
||||
{
|
||||
public:
|
||||
|
||||
ZoneContentIterator( SceneZoneSpaceManager* manager, int zoneId, bool upToDate = true )
|
||||
{
|
||||
AssertFatal( zoneId < manager->getNumZones(), "SceneZoneSpaceManager::ZoneContentIterator - Zone ID out of range" );
|
||||
|
||||
if( upToDate )
|
||||
{
|
||||
// Since zoning is updated lazily, the zone contents may actually
|
||||
// be out of date. Force an update by triggering rezoning on the
|
||||
// zone object. This is brute-force but this iterator is not meant
|
||||
// to be used for high-frequency code anyway.
|
||||
//
|
||||
// Use the area-based rezoning so that we can also properly iterate
|
||||
// over the contents of SceneRootZone.
|
||||
manager->_rezoneObjects( ( ( SceneObject* ) manager->getZoneOwner( zoneId ) )->getWorldBox() );
|
||||
}
|
||||
|
||||
mCurrent = manager->mZoneLists[ zoneId ]->nextInBin; // Skip zone object entry.
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return ( mCurrent != NULL );
|
||||
}
|
||||
bool operator !() const
|
||||
{
|
||||
return ( mCurrent == NULL );
|
||||
}
|
||||
ZoneContentIterator& operator ++()
|
||||
{
|
||||
if( mCurrent )
|
||||
mCurrent = mCurrent->nextInBin;
|
||||
return *this;
|
||||
}
|
||||
ZoneContentIterator& operator --()
|
||||
{
|
||||
if( mCurrent )
|
||||
mCurrent = mCurrent->prevInBin;
|
||||
return *this;
|
||||
}
|
||||
SceneObject* operator *() const
|
||||
{
|
||||
AssertFatal( mCurrent != NULL, "SceneManager::ZoneContentIterator::operator* - Invalid iterator" );
|
||||
return mCurrent->object;
|
||||
}
|
||||
SceneObject* operator ->() const
|
||||
{
|
||||
AssertFatal( mCurrent != NULL, "SceneManager::ZoneContentIterator::operator-> - Invalid iterator" );
|
||||
return mCurrent->object;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
SceneObject::ZoneRef* mCurrent;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
/// The root and outdoor zone of the scene.
|
||||
SceneRootZone* mRootZone;
|
||||
|
||||
/// Scene container that holds the zone spaces we are managing.
|
||||
SceneContainer* mContainer;
|
||||
|
||||
/// Collection of objects that manage zones.
|
||||
Vector< SceneZoneSpace* > mZoneSpaces;
|
||||
|
||||
/// Total number of zones that have been allocated in the scene.
|
||||
U32 mNumTotalAllocatedZones;
|
||||
|
||||
/// Number of zone IDs that are in active use.
|
||||
U32 mNumActiveZones;
|
||||
|
||||
/// Object list for each zone in the scene.
|
||||
/// First entry in the list points back to the zone manager.
|
||||
Vector< SceneObject::ZoneRef* > mZoneLists;
|
||||
|
||||
/// Vector used repeatedly for zone space queries on the container.
|
||||
mutable Vector< SceneObject* > mZoneSpacesQueryList;
|
||||
|
||||
/// Allocator for ZoneRefs.
|
||||
static ClassChunker< SceneObject::ZoneRef > smZoneRefChunker;
|
||||
|
||||
/// @name Dirty Lists
|
||||
/// Updating the zoning state of a scene is done en block rather than
|
||||
/// individually for each object as it changes transforms or size.
|
||||
/// @{
|
||||
|
||||
/// Area of the scene that needs to be rezoned.
|
||||
Box3F mDirtyArea;
|
||||
|
||||
/// List of zone spaces that have changed state and need updating.
|
||||
Vector< SceneZoneSpace* > mDirtyZoneSpaces;
|
||||
|
||||
/// List of objects (non-zone spaces) that have changed state and need
|
||||
/// updating.
|
||||
Vector< SceneObject* > mDirtyObjects;
|
||||
|
||||
/// @}
|
||||
|
||||
/// Check to see if we have accumulated a lot of unallocate zone IDs and if so,
|
||||
/// compact the zoning lists by reassigning IDs.
|
||||
///
|
||||
/// @warn This method may alter all zone IDs in the scene!
|
||||
void _compactZonesCheck();
|
||||
|
||||
/// Return the index into #mZoneSpaces for the given object or -1 if
|
||||
/// @object is not a zone manager.
|
||||
S32 _getZoneSpaceIndex( SceneZoneSpace* object ) const;
|
||||
|
||||
/// Attach zoning state to the given object.
|
||||
void _zoneInsert( SceneObject* object, bool queryListInitialized = false );
|
||||
|
||||
/// Detach zoning state from the given object.
|
||||
void _zoneRemove( SceneObject* object );
|
||||
|
||||
/// Add to given object to the zone list of the given zone.
|
||||
void _addToZoneList( U32 zoneId, SceneObject* object );
|
||||
|
||||
/// Clear all objects assigned to the given zone.
|
||||
/// @note This does not remove the first link in the zone list which is the link
|
||||
/// back to the zone manager.
|
||||
void _clearZoneList( U32 zoneId );
|
||||
|
||||
/// Find the given object in the zone list of the given zone.
|
||||
SceneObject::ZoneRef* _findInZoneList( U32 zoneId, SceneObject* object ) const;
|
||||
|
||||
/// Assign the given object to the outdoor zone.
|
||||
void _addToOutdoorZone( SceneObject* object );
|
||||
|
||||
/// Rezone all objects in the given area.
|
||||
void _rezoneObjects( const Box3F& area );
|
||||
|
||||
/// Update the zoning state of the given object.
|
||||
void _rezoneObject( SceneObject* object );
|
||||
|
||||
/// Fill #mZoneSpacesQueryList with all ZoneObjectType objects in the given area.
|
||||
void _queryZoneSpaces( const Box3F& area ) const;
|
||||
|
||||
public:
|
||||
|
||||
SceneZoneSpaceManager( SceneContainer* container );
|
||||
~SceneZoneSpaceManager();
|
||||
|
||||
/// Bring the zoning state of the scene up to date. This will cause objects
|
||||
/// that have moved or have been resized to be rezoned and will updated regions
|
||||
/// of the scene that had their zoning setup changed.
|
||||
///
|
||||
/// @note This method depends on proper use of notifyObjectChanged().
|
||||
void updateZoningState();
|
||||
|
||||
/// @name Objects
|
||||
/// @{
|
||||
|
||||
/// Add zoning state to the given object.
|
||||
void registerObject( SceneObject* object );
|
||||
|
||||
/// Remove the given object from the zoning state.
|
||||
void unregisterObject( SceneObject* object );
|
||||
|
||||
/// Let the manager know that state relevant to zoning of the given
|
||||
/// object has changed.
|
||||
void notifyObjectChanged( SceneObject* object );
|
||||
|
||||
/// Update the zoning state of the given object.
|
||||
void updateObject( SceneObject* object );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Zones
|
||||
/// @{
|
||||
|
||||
/// Return the root zone of the scene.
|
||||
SceneRootZone* getRootZone() const { return mRootZone; }
|
||||
|
||||
/// Register a zone manager.
|
||||
///
|
||||
/// @param object SceneZoneSpace object that contains zones.
|
||||
/// @param numZones Number of zones that @a object contains.
|
||||
void registerZones( SceneZoneSpace* object, U32 numZones );
|
||||
|
||||
/// Unregister a zone manager.
|
||||
///
|
||||
/// @param object Object that contains zones.
|
||||
void unregisterZones( SceneZoneSpace* object );
|
||||
|
||||
/// Return true if the given ID belongs to a currently registered zone.
|
||||
bool isValidZoneId( const U32 zoneId ) const
|
||||
{
|
||||
return ( zoneId < mNumTotalAllocatedZones && mZoneLists[ zoneId ] );
|
||||
}
|
||||
|
||||
/// Get the scene object that contains the zone with the given ID.
|
||||
///
|
||||
/// @param zoneId ID of the zone. Must be valid.
|
||||
/// @return The zone space that has registered the given zone.
|
||||
SceneZoneSpace* getZoneOwner( const U32 zoneId ) const
|
||||
{
|
||||
AssertFatal( isValidZoneId( zoneId ), "SceneManager::getZoneOwner - Invalid zone ID!");
|
||||
return ( SceneZoneSpace* ) mZoneLists[ zoneId ]->object;
|
||||
}
|
||||
|
||||
/// Return the total number of zones in the scene.
|
||||
U32 getNumZones() const { return mNumTotalAllocatedZones; }
|
||||
|
||||
/// Return the effective amount of used zone IDs in the scene.
|
||||
U32 getNumActiveZones() const { return mNumActiveZones; }
|
||||
|
||||
/// Return the total number of objects in the scene that manage zones.
|
||||
U32 getNumZoneSpaces() const { return mZoneSpaces.size(); }
|
||||
|
||||
/// Find the zone that contains the given point.
|
||||
///
|
||||
/// Note that the result can be <em>any</em> zone containing the given
|
||||
/// point.
|
||||
void findZone( const Point3F& point, SceneZoneSpace*& outZoneSpace, U32& outZoneID ) const;
|
||||
|
||||
/// Collect the IDs of all zones that overlap the given area.
|
||||
///
|
||||
/// @param area AABB of scene space to query.
|
||||
/// @param outZones IDs of all overlapped zones are added to this vector.
|
||||
///
|
||||
/// @return Number of zones that have been added to @a outZones. Always at least
|
||||
/// 1 as at least the outdoor zone always overlaps the given area (though if another zone
|
||||
/// manager fully contains @a area, the outdoor zone will not be added to the list).
|
||||
U32 findZones( const Box3F& area, Vector< U32 >& outZones ) const;
|
||||
|
||||
static ZoningChangedSignal& getZoningChangedSignal()
|
||||
{
|
||||
static ZoningChangedSignal sSignal;
|
||||
return sSignal;
|
||||
}
|
||||
|
||||
/// @name Debugging
|
||||
/// @{
|
||||
|
||||
/// Verify the current zoning state. This makes sure all the connectivity
|
||||
/// information and all the zone assignments appear to be correct.
|
||||
void verifyState();
|
||||
|
||||
/// Dump the current state of all zone spaces in the scene to the console.
|
||||
/// @param update If true, zoning state states are updated first; if false, zoning is
|
||||
/// dumped as is.
|
||||
void dumpZoneStates( bool update = true );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // !_SCENEZONESPACEMANAGER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue