mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 07:04:36 +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
934
Engine/source/scene/culling/sceneCullingState.cpp
Normal file
934
Engine/source/scene/culling/sceneCullingState.cpp
Normal file
|
|
@ -0,0 +1,934 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/culling/sceneCullingState.h"
|
||||
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/sceneObject.h"
|
||||
#include "scene/zones/sceneZoneSpace.h"
|
||||
#include "math/mathUtils.h"
|
||||
#include "platform/profiler.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "util/tempAlloc.h"
|
||||
#include "gfx/sim/debugDraw.h"
|
||||
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
|
||||
bool SceneCullingState::smDisableTerrainOcclusion = false;
|
||||
bool SceneCullingState::smDisableZoneCulling = false;
|
||||
U32 SceneCullingState::smMaxOccludersPerZone = 4;
|
||||
F32 SceneCullingState::smOccluderMinWidthPercentage = 0.1f;
|
||||
F32 SceneCullingState::smOccluderMinHeightPercentage = 0.1f;
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneCullingState::SceneCullingState( SceneManager* sceneManager, const SceneCameraState& viewState )
|
||||
: mSceneManager( sceneManager ),
|
||||
mCameraState( viewState ),
|
||||
mDisableZoneCulling( smDisableZoneCulling ),
|
||||
mDisableTerrainOcclusion( smDisableTerrainOcclusion )
|
||||
{
|
||||
AssertFatal( sceneManager->getZoneManager(), "SceneCullingState::SceneCullingState - SceneManager must have a zone manager!" );
|
||||
|
||||
VECTOR_SET_ASSOCIATION( mZoneStates );
|
||||
VECTOR_SET_ASSOCIATION( mAddedOccluderObjects );
|
||||
|
||||
// Allocate zone states.
|
||||
|
||||
const U32 numZones = sceneManager->getZoneManager()->getNumZones();
|
||||
mZoneStates.setSize( numZones );
|
||||
dMemset( mZoneStates.address(), 0, sizeof( SceneZoneCullingState ) * numZones );
|
||||
|
||||
// Allocate the zone visibility flags.
|
||||
|
||||
mZoneVisibilityFlags.setSize( numZones );
|
||||
mZoneVisibilityFlags.clear();
|
||||
|
||||
// Construct the root culling volume from
|
||||
// the camera's view frustum. Omit the frustum's
|
||||
// near and far plane so we don't test it repeatedly.
|
||||
|
||||
const Frustum& frustum = mCameraState.getFrustum();
|
||||
PlaneF* planes = allocateData< PlaneF >( 4 );
|
||||
|
||||
planes[ 0 ] = frustum.getPlanes()[ Frustum::PlaneLeft ];
|
||||
planes[ 1 ] = frustum.getPlanes()[ Frustum::PlaneRight ];
|
||||
planes[ 2 ] = frustum.getPlanes()[ Frustum::PlaneTop];
|
||||
planes[ 3 ] = frustum.getPlanes()[ Frustum::PlaneBottom ];
|
||||
|
||||
mRootVolume = SceneCullingVolume(
|
||||
SceneCullingVolume::Includer,
|
||||
PlaneSetF( planes, 4 )
|
||||
);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isWithinVisibleZone( SceneObject* object ) const
|
||||
{
|
||||
for( SceneObject::ZoneRef* ref = object->_getZoneRefHead();
|
||||
ref != NULL; ref = ref->nextInObj )
|
||||
if( mZoneVisibilityFlags.test( ref->zone ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneCullingState::addOccluder( SceneObject* object )
|
||||
{
|
||||
PROFILE_SCOPE( SceneCullingState_addOccluder );
|
||||
|
||||
// If the occluder is itself occluded, don't add it.
|
||||
//
|
||||
// NOTE: We do allow near plane intersections here. Silhouette extraction
|
||||
// should take that into account.
|
||||
|
||||
if( cullObjects( &object, 1, DontCullRenderDisabled ) != 1 )
|
||||
return;
|
||||
|
||||
// If the occluder has already been added, do nothing. Check this
|
||||
// after the culling check since the same occluder can be added by
|
||||
// two separate zones and not be visible in one yet be visible in the
|
||||
// other.
|
||||
|
||||
if( mAddedOccluderObjects.contains( object ) )
|
||||
return;
|
||||
mAddedOccluderObjects.push_back( object );
|
||||
|
||||
// Let the object build a silhouette. If it doesn't
|
||||
// return one, abort.
|
||||
|
||||
Vector< Point3F > silhouette;
|
||||
object->buildSilhouette( getCameraState(), silhouette );
|
||||
|
||||
if( silhouette.empty() || silhouette.size() < 3 )
|
||||
return;
|
||||
|
||||
// Generate the culling volume.
|
||||
|
||||
SceneCullingVolume volume;
|
||||
if( !createCullingVolume(
|
||||
silhouette.address(),
|
||||
silhouette.size(),
|
||||
SceneCullingVolume::Occluder,
|
||||
volume ) )
|
||||
return;
|
||||
|
||||
// Add the frustum to all zones that the object is assigned to.
|
||||
|
||||
for( SceneObject::ZoneRef* ref = object->_getZoneRefHead(); ref != NULL; ref = ref->nextInObj )
|
||||
addCullingVolumeToZone( ref->zone, volume );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::addCullingVolumeToZone( U32 zoneId, const SceneCullingVolume& volume )
|
||||
{
|
||||
PROFILE_SCOPE( SceneCullingState_addCullingVolumeToZone );
|
||||
|
||||
AssertFatal( zoneId < mZoneStates.size(), "SceneCullingState::addCullingVolumeToZone - Zone ID out of range" );
|
||||
SceneZoneCullingState& zoneState = mZoneStates[ zoneId ];
|
||||
|
||||
// [rene, 07-Apr-10] I previously used to attempt to merge things here and detect whether
|
||||
// the visibility state of the zone has changed at all. Since we allow polyhedra to be
|
||||
// degenerate here and since polyhedra cannot be merged easily like frustums, I have opted
|
||||
// to remove this for now. I'm also convinced that with the current traversal system it
|
||||
// adds little benefit.
|
||||
|
||||
// Link the volume to the zone state.
|
||||
|
||||
typedef SceneZoneCullingState::CullingVolumeLink LinkType;
|
||||
|
||||
LinkType* link = reinterpret_cast< LinkType* >( allocateData( sizeof( LinkType ) ) );
|
||||
|
||||
link->mVolume = volume;
|
||||
link->mNext = zoneState.mCullingVolumes;
|
||||
zoneState.mCullingVolumes = link;
|
||||
|
||||
if( volume.isOccluder() )
|
||||
zoneState.mHaveOccluders = true;
|
||||
else
|
||||
zoneState.mHaveIncluders = true;
|
||||
|
||||
// Mark sorting state as dirty.
|
||||
|
||||
zoneState.mHaveSortedVolumes = false;
|
||||
|
||||
// Set the visibility flag for the zone.
|
||||
|
||||
if( volume.isIncluder() )
|
||||
mZoneVisibilityFlags.set( zoneId );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::addCullingVolumeToZone( U32 zoneId, SceneCullingVolume::Type type, const AnyPolyhedron& polyhedron )
|
||||
{
|
||||
// Allocate space on our chunker.
|
||||
|
||||
const U32 numPlanes = polyhedron.getNumPlanes();
|
||||
PlaneF* planes = allocateData< PlaneF >( numPlanes );
|
||||
|
||||
// Copy the planes over.
|
||||
|
||||
dMemcpy( planes, polyhedron.getPlanes(), numPlanes * sizeof( planes[ 0 ] ) );
|
||||
|
||||
// Create a culling volume.
|
||||
|
||||
SceneCullingVolume volume(
|
||||
type,
|
||||
PlaneSetF( planes, numPlanes )
|
||||
);
|
||||
|
||||
// And add it.
|
||||
|
||||
return addCullingVolumeToZone( zoneId, volume );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::createCullingVolume( const Point3F* vertices, U32 numVertices, SceneCullingVolume::Type type, SceneCullingVolume& outVolume )
|
||||
{
|
||||
const Point3F& viewPos = getCameraState().getViewPosition();
|
||||
const Point3F& viewDir = getCameraState().getViewDirection();
|
||||
const bool isOrtho = getFrustum().isOrtho();
|
||||
|
||||
//TODO: check if we need to handle penetration of the near plane for occluders specially
|
||||
|
||||
// Allocate space for the clipping planes we generate. Assume the worst case
|
||||
// of every edge generating a plane and, for includers, all edges meeting at
|
||||
// steep angles so we need to insert extra planes (the latter is not possible,
|
||||
// of course, but it makes things less complicated here). For occluders, add
|
||||
// an extra plane for the near cap.
|
||||
|
||||
const U32 maxPlanes = ( type == SceneCullingVolume::Occluder ? numVertices + 1 : numVertices * 2 );
|
||||
PlaneF* planes = allocateData< PlaneF >( maxPlanes );
|
||||
|
||||
// Keep track of the world-space bounds of the polygon. We use this later
|
||||
// to derive some metrics.
|
||||
|
||||
Box3F wsPolyBounds;
|
||||
|
||||
wsPolyBounds.minExtents = Point3F( TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX );
|
||||
wsPolyBounds.maxExtents = Point3F( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN );
|
||||
|
||||
// For occluders, also keep track of the nearest, and two farthest silhouette points. We use
|
||||
// this later to construct a near capping plane.
|
||||
F32 minVertexDistanceSquared = TypeTraits< F32 >::MAX;
|
||||
U32 leastDistantVert = 0;
|
||||
|
||||
F32 maxVertexDistancesSquared[ 2 ] = { TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN };
|
||||
U32 mostDistantVertices[ 2 ] = { 0, 0 };
|
||||
|
||||
// Generate the extrusion volume. For orthographic projections, extrude
|
||||
// parallel to the view direction whereas for parallel projections, extrude
|
||||
// from the viewpoint.
|
||||
|
||||
U32 numPlanes = 0;
|
||||
U32 lastVertex = numVertices - 1;
|
||||
bool invert = false;
|
||||
|
||||
for( U32 i = 0; i < numVertices; lastVertex = i, ++ i )
|
||||
{
|
||||
AssertFatal( numPlanes < maxPlanes, "SceneCullingState::createCullingVolume - Did not allocate enough planes!" );
|
||||
|
||||
const Point3F& v1 = vertices[ i ];
|
||||
const Point3F& v2 = vertices[ lastVertex ];
|
||||
|
||||
// Keep track of bounds.
|
||||
|
||||
wsPolyBounds.minExtents.setMin( v1 );
|
||||
wsPolyBounds.maxExtents.setMax( v1 );
|
||||
|
||||
// Skip the edge if it's length is really short.
|
||||
|
||||
const Point3F edgeVector = v2 - v1;
|
||||
const F32 edgeVectorLenSquared = edgeVector.lenSquared();
|
||||
if( edgeVectorLenSquared < 0.025f )
|
||||
continue;
|
||||
|
||||
//TODO: might need to do additional checks here for non-planar polygons used by occluders
|
||||
//TODO: test for colinearity of edge vector with view vector (occluders only)
|
||||
|
||||
// Create a plane for the edge.
|
||||
|
||||
if( isOrtho )
|
||||
{
|
||||
// Compute a plane through the two edge vertices and one
|
||||
// of the vertices extended along the view direction.
|
||||
|
||||
if( !invert )
|
||||
planes[ numPlanes ] = PlaneF( v1, v1 + viewDir, v2 );
|
||||
else
|
||||
planes[ numPlanes ] = PlaneF( v2, v1 + viewDir, v1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute a plane going through the viewpoint and the two
|
||||
// edge vertices.
|
||||
|
||||
if( !invert )
|
||||
planes[ numPlanes ] = PlaneF( v1, viewPos, v2 );
|
||||
else
|
||||
planes[ numPlanes ] = PlaneF( v2, viewPos, v1 );
|
||||
}
|
||||
|
||||
numPlanes ++;
|
||||
|
||||
// If this is the first plane that we have created, find out whether
|
||||
// the vertex ordering is giving us the plane orientations that we want
|
||||
// (facing inside). If not, invert vertex order from now on.
|
||||
|
||||
if( numPlanes == 1 )
|
||||
{
|
||||
Point3F center( 0, 0, 0 );
|
||||
for( U32 n = 0; n < numVertices; ++ n )
|
||||
center += vertices[n];
|
||||
center /= numVertices;
|
||||
|
||||
if( planes[numPlanes - 1].whichSide( center ) == PlaneF::Back )
|
||||
{
|
||||
invert = true;
|
||||
planes[ numPlanes - 1 ].invert();
|
||||
}
|
||||
}
|
||||
|
||||
// For occluders, keep tabs of the nearest, and two farthest vertices.
|
||||
|
||||
if( type == SceneCullingVolume::Occluder )
|
||||
{
|
||||
const F32 distSquared = ( v1 - viewPos ).lenSquared();
|
||||
if( distSquared < minVertexDistanceSquared )
|
||||
{
|
||||
minVertexDistanceSquared = distSquared;
|
||||
leastDistantVert = i;
|
||||
}
|
||||
if( distSquared > maxVertexDistancesSquared[ 0 ] )
|
||||
{
|
||||
// Move 0 to 1.
|
||||
maxVertexDistancesSquared[ 1 ] = maxVertexDistancesSquared[ 0 ];
|
||||
mostDistantVertices[ 1 ] = mostDistantVertices[ 0 ];
|
||||
|
||||
// Replace 0.
|
||||
maxVertexDistancesSquared[ 0 ] = distSquared;
|
||||
mostDistantVertices[ 0 ] = i;
|
||||
}
|
||||
else if( distSquared > maxVertexDistancesSquared[ 1 ] )
|
||||
{
|
||||
// Replace 1.
|
||||
maxVertexDistancesSquared[ 1 ] = distSquared;
|
||||
mostDistantVertices[ 1 ] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the extrusion produced no useful result, abort.
|
||||
|
||||
if( numPlanes < 3 )
|
||||
return false;
|
||||
|
||||
// For includers, test the angle of the edges at the current vertex.
|
||||
// If too steep, add an extra plane to improve culling efficiency.
|
||||
|
||||
if( false )//type == SceneCullingVolume::Includer )
|
||||
{
|
||||
const U32 numOriginalPlanes = numPlanes;
|
||||
U32 lastPlaneIndex = numPlanes - 1;
|
||||
|
||||
for( U32 i = 0; i < numOriginalPlanes; lastPlaneIndex = i, ++ i )
|
||||
{
|
||||
const PlaneF& currentPlane = planes[ i ];
|
||||
const PlaneF& lastPlane = planes[ lastPlaneIndex ];
|
||||
|
||||
// Compute the cosine of the angle between the two plane normals.
|
||||
|
||||
const F32 cosAngle = mFabs( mDot( currentPlane, lastPlane ) );
|
||||
|
||||
// The planes meet at increasingly steep angles the more they point
|
||||
// in opposite directions, i.e the closer the angle of their normals
|
||||
// is to 180 degrees. Skip any two planes that don't get near that.
|
||||
|
||||
if( cosAngle > 0.1f )
|
||||
continue;
|
||||
|
||||
//TODO
|
||||
|
||||
const Point3F addNormals = currentPlane + lastPlane;
|
||||
const Point3F crossNormals = mCross( currentPlane, lastPlane );
|
||||
|
||||
Point3F newNormal = currentPlane + lastPlane;//addNormals - mDot( addNormals, crossNormals ) * crossNormals;
|
||||
|
||||
//
|
||||
|
||||
planes[ numPlanes ] = PlaneF( currentPlane.getPosition(), newNormal );
|
||||
numPlanes ++;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the metrics of the culling volume in relation to the view frustum.
|
||||
//
|
||||
// For this, we are short-circuiting things slightly. The correct way (other than doing
|
||||
// full screen projections) would be to transform all the polygon points into camera
|
||||
// space, lay an AABB around those points, and then find the X and Z extents on the near plane.
|
||||
//
|
||||
// However, while not as accurate, a faster way is to just project the axial vectors
|
||||
// of the bounding box onto both the camera right and up vector. This gives us a rough
|
||||
// estimate of the camera-space size of the polygon we're looking at.
|
||||
|
||||
const MatrixF& cameraTransform = getCameraState().getViewWorldMatrix();
|
||||
const Point3F cameraRight = cameraTransform.getRightVector();
|
||||
const Point3F cameraUp = cameraTransform.getUpVector();
|
||||
|
||||
const Point3F wsPolyBoundsExtents = wsPolyBounds.getExtents();
|
||||
|
||||
F32 widthEstimate =
|
||||
getMax( mFabs( wsPolyBoundsExtents.x * cameraRight.x ),
|
||||
getMax( mFabs( wsPolyBoundsExtents.y * cameraRight.y ),
|
||||
mFabs( wsPolyBoundsExtents.z * cameraRight.z ) ) );
|
||||
|
||||
F32 heightEstimate =
|
||||
getMax( mFabs( wsPolyBoundsExtents.x * cameraUp.x ),
|
||||
getMax( mFabs( wsPolyBoundsExtents.y * cameraUp.y ),
|
||||
mFabs( wsPolyBoundsExtents.z * cameraUp.z ) ) );
|
||||
|
||||
// If the current camera is a perspective one, divide the two estimates
|
||||
// by the distance of the nearest bounding box vertex to the camera
|
||||
// to account for perspective distortion.
|
||||
|
||||
if( !isOrtho )
|
||||
{
|
||||
const Point3F nearestVertex = wsPolyBounds.computeVertex(
|
||||
Box3F::getPointIndexFromOctant( - viewDir )
|
||||
);
|
||||
|
||||
const F32 distance = ( nearestVertex - viewPos ).len();
|
||||
|
||||
widthEstimate /= distance;
|
||||
heightEstimate /= distance;
|
||||
}
|
||||
|
||||
// If we are creating an occluder, check to see if the estimates fit
|
||||
// our minimum requirements.
|
||||
|
||||
if( type == SceneCullingVolume::Occluder )
|
||||
{
|
||||
const F32 widthEstimatePercentage = widthEstimate / getFrustum().getWidth();
|
||||
const F32 heightEstimatePercentage = heightEstimate / getFrustum().getHeight();
|
||||
|
||||
if( widthEstimatePercentage < smOccluderMinWidthPercentage ||
|
||||
heightEstimatePercentage < smOccluderMinHeightPercentage )
|
||||
return false; // Reject.
|
||||
}
|
||||
|
||||
// Use the area estimate as the volume's sort point.
|
||||
|
||||
const F32 sortPoint = widthEstimate * heightEstimate;
|
||||
|
||||
// Finally, if it's an occluder, compute a near cap. The near cap prevents objects
|
||||
// in front of the occluder from testing positive. The same could be achieved by
|
||||
// manually comparing distances before testing objects but since that would amount
|
||||
// to the same checks the plane/AABB tests do, it's easier to just add another plane.
|
||||
// Additionally, it gives the benefit of being able to create more precise culling
|
||||
// results by angling the plane.
|
||||
|
||||
//NOTE: Could consider adding a near cap for includers too when generating a volume
|
||||
// for the outdoor zone as that may prevent quite a bit of space from being included.
|
||||
// However, given that this space will most likely just be filled with interior
|
||||
// stuff anyway, it's probably not worth it.
|
||||
|
||||
if( type == SceneCullingVolume::Occluder )
|
||||
{
|
||||
const U32 nearCapIndex = numPlanes;
|
||||
planes[ nearCapIndex ] = PlaneF(
|
||||
vertices[ mostDistantVertices[ 0 ] ],
|
||||
vertices[ mostDistantVertices[ 1 ] ],
|
||||
vertices[ leastDistantVert ] );
|
||||
|
||||
// Invert the plane, if necessary.
|
||||
if( planes[ nearCapIndex ].whichSide( viewPos ) == PlaneF::Front )
|
||||
planes[ nearCapIndex ].invert();
|
||||
|
||||
numPlanes ++;
|
||||
}
|
||||
|
||||
// Create the volume from the planes.
|
||||
|
||||
outVolume = SceneCullingVolume(
|
||||
type,
|
||||
PlaneSetF( planes, numPlanes )
|
||||
);
|
||||
outVolume.setSortPoint( sortPoint );
|
||||
|
||||
// Done.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
struct ZoneArrayIterator
|
||||
{
|
||||
U32 mCurrent;
|
||||
U32 mNumZones;
|
||||
const U32* mZones;
|
||||
|
||||
ZoneArrayIterator( const U32* zones, U32 numZones )
|
||||
: mCurrent( 0 ),
|
||||
mNumZones( numZones ),
|
||||
mZones( zones ) {}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return ( mCurrent < mNumZones );
|
||||
}
|
||||
ZoneArrayIterator& operator ++()
|
||||
{
|
||||
mCurrent ++;
|
||||
return *this;
|
||||
}
|
||||
U32 operator *() const
|
||||
{
|
||||
return mZones[ mCurrent ];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template< typename T, typename Iter >
|
||||
inline SceneZoneCullingState::CullingTestResult SceneCullingState::_testOccludersOnly( const T& bounds, Iter zoneIter ) const
|
||||
{
|
||||
// Test the culling states of all zones that the object
|
||||
// is assigned to.
|
||||
|
||||
for( ; zoneIter.isValid(); ++ zoneIter )
|
||||
{
|
||||
const SceneZoneCullingState& zoneState = getZoneState( *zoneIter );
|
||||
|
||||
// Skip zone if there are no occluders.
|
||||
|
||||
if( !zoneState.hasOccluders() )
|
||||
continue;
|
||||
|
||||
// If the object's world bounds overlaps any of the volumes
|
||||
// for this zone, it's rendered.
|
||||
|
||||
if( zoneState.testVolumes( bounds, true ) == SceneZoneCullingState::CullingTestPositiveByOcclusion )
|
||||
return SceneZoneCullingState::CullingTestPositiveByOcclusion;
|
||||
}
|
||||
|
||||
return SceneZoneCullingState::CullingTestNegative;
|
||||
}
|
||||
|
||||
template< typename T, typename Iter >
|
||||
inline SceneZoneCullingState::CullingTestResult SceneCullingState::_test( const T& bounds, Iter zoneIter,
|
||||
const PlaneF& nearPlane, const PlaneF& farPlane ) const
|
||||
{
|
||||
// Defer test of near and far plane until we've hit a zone
|
||||
// which actually has visible space. This prevents us from
|
||||
// doing near/far tests on objects that were included in the
|
||||
// potential render list but aren't actually in any visible
|
||||
// zone.
|
||||
bool haveTestedNearAndFar = false;
|
||||
|
||||
// Test the culling states of all zones that the object
|
||||
// is assigned to.
|
||||
|
||||
for( ; zoneIter.isValid(); ++ zoneIter )
|
||||
{
|
||||
const SceneZoneCullingState& zoneState = getZoneState( *zoneIter );
|
||||
|
||||
// Skip zone if there are no positive culling volumes.
|
||||
|
||||
if( !zoneState.hasIncluders() )
|
||||
continue;
|
||||
|
||||
// If we haven't tested the near and far plane yet, do so
|
||||
// now.
|
||||
|
||||
if( !haveTestedNearAndFar )
|
||||
{
|
||||
// Test near plane.
|
||||
|
||||
PlaneF::Side nearSide = nearPlane.whichSide( bounds );
|
||||
if( nearSide == PlaneF::Back )
|
||||
return SceneZoneCullingState::CullingTestNegative;
|
||||
|
||||
// Test far plane.
|
||||
|
||||
PlaneF::Side farSide = farPlane.whichSide( bounds );
|
||||
if( farSide == PlaneF::Back )
|
||||
return SceneZoneCullingState::CullingTestNegative;
|
||||
|
||||
haveTestedNearAndFar = true;
|
||||
}
|
||||
|
||||
// If the object's world bounds overlaps any of the volumes
|
||||
// for this zone, it's rendered.
|
||||
|
||||
SceneZoneCullingState::CullingTestResult result = zoneState.testVolumes( bounds );
|
||||
|
||||
if( result == SceneZoneCullingState::CullingTestPositiveByInclusion )
|
||||
return result;
|
||||
else if( result == SceneZoneCullingState::CullingTestPositiveByOcclusion )
|
||||
return result;
|
||||
}
|
||||
|
||||
return SceneZoneCullingState::CullingTestNegative;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< bool OCCLUDERS_ONLY, typename T >
|
||||
inline SceneZoneCullingState::CullingTestResult SceneCullingState::_test( const T& bounds, const U32* zones, U32 numZones ) const
|
||||
{
|
||||
// If zone culling is disabled, only test against
|
||||
// the root frustum.
|
||||
|
||||
if( disableZoneCulling() )
|
||||
{
|
||||
if( !OCCLUDERS_ONLY && !getFrustum().isCulled( bounds ) )
|
||||
return SceneZoneCullingState::CullingTestPositiveByInclusion;
|
||||
|
||||
return SceneZoneCullingState::CullingTestNegative;
|
||||
}
|
||||
|
||||
// Otherwise test each of the zones.
|
||||
|
||||
if( OCCLUDERS_ONLY )
|
||||
{
|
||||
return _testOccludersOnly(
|
||||
bounds,
|
||||
ZoneArrayIterator( zones, numZones )
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
const PlaneF* frustumPlanes = getFrustum().getPlanes();
|
||||
|
||||
return _test(
|
||||
bounds,
|
||||
ZoneArrayIterator( zones, numZones ),
|
||||
frustumPlanes[ Frustum::PlaneNear ],
|
||||
frustumPlanes[ Frustum::PlaneFar ]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isCulled( const Box3F& aabb, const U32* zones, U32 numZones ) const
|
||||
{
|
||||
SceneZoneCullingState::CullingTestResult result = _test< false >( aabb, zones, numZones );
|
||||
return ( result == SceneZoneCullingState::CullingTestNegative ||
|
||||
result == SceneZoneCullingState::CullingTestPositiveByOcclusion );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isCulled( const OrientedBox3F& obb, const U32* zones, U32 numZones ) const
|
||||
{
|
||||
SceneZoneCullingState::CullingTestResult result = _test< false >( obb, zones, numZones );
|
||||
return ( result == SceneZoneCullingState::CullingTestNegative ||
|
||||
result == SceneZoneCullingState::CullingTestPositiveByOcclusion );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isCulled( const SphereF& sphere, const U32* zones, U32 numZones ) const
|
||||
{
|
||||
SceneZoneCullingState::CullingTestResult result = _test< false >( sphere, zones, numZones );
|
||||
return ( result == SceneZoneCullingState::CullingTestNegative ||
|
||||
result == SceneZoneCullingState::CullingTestPositiveByOcclusion );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isOccluded( SceneObject* object ) const
|
||||
{
|
||||
if( disableZoneCulling() )
|
||||
return false;
|
||||
|
||||
CullingTestResult result = _testOccludersOnly(
|
||||
object->getWorldBox(),
|
||||
SceneObject::ObjectZonesIterator( object )
|
||||
);
|
||||
|
||||
return ( result == SceneZoneCullingState::CullingTestPositiveByOcclusion );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isOccluded( const Box3F& aabb, const U32* zones, U32 numZones ) const
|
||||
{
|
||||
return ( _test< true >( aabb, zones, numZones ) == SceneZoneCullingState::CullingTestPositiveByOcclusion );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isOccluded( const OrientedBox3F& obb, const U32* zones, U32 numZones ) const
|
||||
{
|
||||
return ( _test< true >( obb, zones, numZones ) == SceneZoneCullingState::CullingTestPositiveByOcclusion );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isOccluded( const SphereF& sphere, const U32* zones, U32 numZones ) const
|
||||
{
|
||||
return ( _test< true >( sphere, zones, numZones ) == SceneZoneCullingState::CullingTestPositiveByOcclusion );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 SceneCullingState::cullObjects( SceneObject** objects, U32 numObjects, U32 cullOptions ) const
|
||||
{
|
||||
PROFILE_SCOPE( SceneCullingState_cullObjects );
|
||||
|
||||
U32 numRemainingObjects = 0;
|
||||
|
||||
// We test near and far planes separately in order to not do the tests
|
||||
// repeatedly, so fetch the planes now.
|
||||
const PlaneF& nearPlane = getFrustum().getPlanes()[ Frustum::PlaneNear ];
|
||||
const PlaneF& farPlane = getFrustum().getPlanes()[ Frustum::PlaneFar ];
|
||||
|
||||
for( U32 i = 0; i < numObjects; ++ i )
|
||||
{
|
||||
SceneObject* object = objects[ i ];
|
||||
bool isCulled = true;
|
||||
|
||||
// If we should respect editor overrides, test that now.
|
||||
|
||||
if( !( cullOptions & CullEditorOverrides ) &&
|
||||
gEditingMission &&
|
||||
( ( object->isCullingDisabledInEditor() && object->isRenderEnabled() ) || object->isSelected() ) )
|
||||
{
|
||||
isCulled = false;
|
||||
}
|
||||
|
||||
// If the object is render-disabled, it gets culled. The only
|
||||
// way around this is the editor override above.
|
||||
|
||||
else if( !( cullOptions & DontCullRenderDisabled ) &&
|
||||
!object->isRenderEnabled() )
|
||||
{
|
||||
isCulled = true;
|
||||
}
|
||||
|
||||
// Global bounds objects are never culled. Note that this means
|
||||
// that if these objects are to respect zoning, they need to manually
|
||||
// trigger the respective culling checks for whatever they want to
|
||||
// batch.
|
||||
|
||||
else if( object->isGlobalBounds() )
|
||||
isCulled = false;
|
||||
|
||||
// If terrain occlusion checks are enabled, run them now.
|
||||
|
||||
else if( !mDisableTerrainOcclusion &&
|
||||
object->getWorldBox().minExtents.x > -1e5 &&
|
||||
isOccludedByTerrain( object ) )
|
||||
{
|
||||
// Occluded by terrain.
|
||||
isCulled = true;
|
||||
}
|
||||
|
||||
// If the object shouldn't be subjected to more fine-grained culling
|
||||
// or if zone culling is disabled, just test against the root frustum.
|
||||
|
||||
else if( !( object->getTypeMask() & CULLING_INCLUDE_TYPEMASK ) ||
|
||||
( object->getTypeMask() & CULLING_EXCLUDE_TYPEMASK ) ||
|
||||
disableZoneCulling() )
|
||||
{
|
||||
isCulled = getFrustum().isCulled( object->getWorldBox() );
|
||||
}
|
||||
|
||||
// Go through the zones that the object is assigned to and
|
||||
// test the object against the frustums of each of the zones.
|
||||
|
||||
else
|
||||
{
|
||||
CullingTestResult result = _test(
|
||||
object->getWorldBox(),
|
||||
SceneObject::ObjectZonesIterator( object ),
|
||||
nearPlane,
|
||||
farPlane
|
||||
);
|
||||
|
||||
isCulled = ( result == SceneZoneCullingState::CullingTestNegative ||
|
||||
result == SceneZoneCullingState::CullingTestPositiveByOcclusion );
|
||||
}
|
||||
|
||||
if( !isCulled )
|
||||
objects[ numRemainingObjects ++ ] = object;
|
||||
}
|
||||
|
||||
return numRemainingObjects;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneCullingState::isOccludedByTerrain( SceneObject* object ) const
|
||||
{
|
||||
PROFILE_SCOPE( SceneCullingState_isOccludedByTerrain );
|
||||
|
||||
// Don't try to occlude globally bounded objects.
|
||||
if( object->isGlobalBounds() )
|
||||
return false;
|
||||
|
||||
const Vector< SceneObject* >& terrains = getSceneManager()->getContainer()->getTerrains();
|
||||
const U32 numTerrains = terrains.size();
|
||||
|
||||
for( U32 terrainIdx = 0; terrainIdx < numTerrains; ++ terrainIdx )
|
||||
{
|
||||
TerrainBlock* terrain = dynamic_cast< TerrainBlock* >( terrains[ terrainIdx ] );
|
||||
if( !terrain )
|
||||
continue;
|
||||
|
||||
Point3F localCamPos = getCameraState().getViewPosition();
|
||||
terrain->getWorldTransform().mulP( localCamPos );
|
||||
F32 height;
|
||||
terrain->getHeight( Point2F( localCamPos.x, localCamPos.y ), &height );
|
||||
bool aboveTerrain = ( height <= localCamPos.z );
|
||||
|
||||
// Don't occlude if we're below the terrain. This prevents problems when
|
||||
// looking out from underground bases...
|
||||
if( !aboveTerrain )
|
||||
continue;
|
||||
|
||||
const Box3F& oBox = object->getObjBox();
|
||||
F32 minSide = getMin(oBox.len_x(), oBox.len_y());
|
||||
if (minSide > 85.0f)
|
||||
continue;
|
||||
|
||||
const Box3F& rBox = object->getWorldBox();
|
||||
Point3F ul(rBox.minExtents.x, rBox.minExtents.y, rBox.maxExtents.z);
|
||||
Point3F ur(rBox.minExtents.x, rBox.maxExtents.y, rBox.maxExtents.z);
|
||||
Point3F ll(rBox.maxExtents.x, rBox.minExtents.y, rBox.maxExtents.z);
|
||||
Point3F lr(rBox.maxExtents.x, rBox.maxExtents.y, rBox.maxExtents.z);
|
||||
|
||||
terrain->getWorldTransform().mulP(ul);
|
||||
terrain->getWorldTransform().mulP(ur);
|
||||
terrain->getWorldTransform().mulP(ll);
|
||||
terrain->getWorldTransform().mulP(lr);
|
||||
|
||||
Point3F xBaseL0_s = ul - localCamPos;
|
||||
Point3F xBaseL0_e = lr - localCamPos;
|
||||
Point3F xBaseL1_s = ur - localCamPos;
|
||||
Point3F xBaseL1_e = ll - localCamPos;
|
||||
|
||||
static F32 checkPoints[3] = {0.75, 0.5, 0.25};
|
||||
RayInfo rinfo;
|
||||
for( U32 i = 0; i < 3; i ++ )
|
||||
{
|
||||
Point3F start = (xBaseL0_s * checkPoints[i]) + localCamPos;
|
||||
Point3F end = (xBaseL0_e * checkPoints[i]) + localCamPos;
|
||||
|
||||
if (terrain->castRay(start, end, &rinfo))
|
||||
continue;
|
||||
|
||||
terrain->getHeight(Point2F(start.x, start.y), &height);
|
||||
if ((height <= start.z) == aboveTerrain)
|
||||
continue;
|
||||
|
||||
start = (xBaseL1_s * checkPoints[i]) + localCamPos;
|
||||
end = (xBaseL1_e * checkPoints[i]) + localCamPos;
|
||||
|
||||
if (terrain->castRay(start, end, &rinfo))
|
||||
continue;
|
||||
|
||||
Point3F test = (start + end) * 0.5;
|
||||
if (terrain->castRay(localCamPos, test, &rinfo) == false)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneCullingState::debugRenderCullingVolumes() const
|
||||
{
|
||||
const ColorI occluderColor( 255, 0, 0, 255 );
|
||||
const ColorI includerColor( 0, 255, 0, 255 );
|
||||
|
||||
const PlaneF& nearPlane = getFrustum().getPlanes()[ Frustum::PlaneNear ];
|
||||
const PlaneF& farPlane = getFrustum().getPlanes()[ Frustum::PlaneFar ];
|
||||
|
||||
DebugDrawer* drawer = DebugDrawer::get();
|
||||
const SceneZoneSpaceManager* zoneManager = mSceneManager->getZoneManager();
|
||||
|
||||
bool haveDebugZone = false;
|
||||
const U32 numZones = mZoneStates.size();
|
||||
for( S32 zoneId = numZones - 1; zoneId >= 0; -- zoneId )
|
||||
{
|
||||
if( !zoneManager->isValidZoneId( zoneId ) )
|
||||
continue;
|
||||
|
||||
const SceneZoneCullingState& zoneState = mZoneStates[ zoneId ];
|
||||
if( !zoneManager->getZoneOwner( zoneId )->isSelected() && ( zoneId != SceneZoneSpaceManager::RootZoneId || haveDebugZone ) )
|
||||
continue;
|
||||
|
||||
haveDebugZone = true;
|
||||
|
||||
for( SceneZoneCullingState::CullingVolumeIterator iter( zoneState );
|
||||
iter.isValid(); ++ iter )
|
||||
{
|
||||
// Temporarily add near and far plane to culling volume so that
|
||||
// no matter how it is defined, it has a chance of being properly
|
||||
// capped.
|
||||
|
||||
const U32 numPlanes = iter->getPlanes().getNumPlanes();
|
||||
const PlaneF* planes = iter->getPlanes().getPlanes();
|
||||
|
||||
TempAlloc< PlaneF > tempPlanes( numPlanes + 2 );
|
||||
|
||||
tempPlanes[ 0 ] = nearPlane;
|
||||
tempPlanes[ 1 ] = farPlane;
|
||||
|
||||
dMemcpy( &tempPlanes[ 2 ], planes, numPlanes * sizeof( PlaneF ) );
|
||||
|
||||
// Build a polyhedron from the plane set.
|
||||
|
||||
Polyhedron polyhedron;
|
||||
polyhedron.buildFromPlanes(
|
||||
PlaneSetF( tempPlanes, numPlanes + 2 )
|
||||
);
|
||||
|
||||
// If the polyhedron has any renderable data,
|
||||
// hand it over to the debug drawer.
|
||||
|
||||
if( polyhedron.getNumEdges() )
|
||||
drawer->drawPolyhedron( polyhedron, iter->isOccluder() ? occluderColor : includerColor );
|
||||
}
|
||||
}
|
||||
}
|
||||
310
Engine/source/scene/culling/sceneCullingState.h
Normal file
310
Engine/source/scene/culling/sceneCullingState.h
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENECULLINGSTATE_H_
|
||||
#define _SCENECULLINGSTATE_H_
|
||||
|
||||
#ifndef _SCENEZONECULLINGSTATE_H_
|
||||
#include "scene/culling/sceneZoneCullingState.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MATHUTIL_FRUSTUM_H_
|
||||
#include "math/util/frustum.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENECAMERASTATE_H_
|
||||
#include "scene/sceneCameraState.h"
|
||||
#endif
|
||||
|
||||
#ifndef _DATACHUNKER_H_
|
||||
#include "core/dataChunker.h"
|
||||
#endif
|
||||
|
||||
#ifndef _BITVECTOR_H_
|
||||
#include "core/bitVector.h"
|
||||
#endif
|
||||
|
||||
|
||||
class SceneObject;
|
||||
class SceneManager;
|
||||
|
||||
|
||||
/// An object that gathers the culling state for a scene.
|
||||
class SceneCullingState
|
||||
{
|
||||
public:
|
||||
|
||||
/// Used to disable the somewhat expensive terrain occlusion testing
|
||||
/// done in during scene culling.
|
||||
static bool smDisableTerrainOcclusion;
|
||||
|
||||
/// Whether to force zone culling to off by default.
|
||||
static bool smDisableZoneCulling;
|
||||
|
||||
/// @name Occluder Restrictions
|
||||
/// Size restrictions on occlusion culling volumes. Any occlusion volume
|
||||
/// that does not meet these minimum requirements is not accepted into the
|
||||
/// rendering state.
|
||||
///
|
||||
/// Having independent restrictions on both width and height allows filtering
|
||||
/// out occluders that might have a lot of area but only by covering very thin
|
||||
/// stretches of the screen.
|
||||
/// @{
|
||||
|
||||
/// If more than this number of occlusion volumes are added to a ZoneState,
|
||||
/// then the occlusions volumes corresponding to the smallest amount of screen
|
||||
/// real estate get dropped such as to never exceed this total number of occlusion
|
||||
/// volumes.
|
||||
static U32 smMaxOccludersPerZone;
|
||||
|
||||
/// Percentage of camera-space frustum near plane height that an occlusion culler must
|
||||
/// at least fill in order to not be rejected.
|
||||
/// @note The height computed for occluders is only an estimate.
|
||||
static F32 smOccluderMinHeightPercentage;
|
||||
|
||||
/// Percentage of camera-space frustum near plane width that an occlusion culler must
|
||||
/// at least fill in order to not be rejected.
|
||||
/// @note The width computed for occluders is only an estimate.
|
||||
static F32 smOccluderMinWidthPercentage;
|
||||
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
|
||||
/// Scene which is being culled.
|
||||
SceneManager* mSceneManager;
|
||||
|
||||
/// The viewing state that defines how the scene is being viewed.
|
||||
SceneCameraState mCameraState;
|
||||
|
||||
/// The root culling volume corresponding to the camera frustum.
|
||||
SceneCullingVolume mRootVolume;
|
||||
|
||||
/// Occluders that have been added to this render state. Adding an occluder does not
|
||||
/// necessarily result in an occluder volume being added. To not repeatedly try to
|
||||
/// process the same occluder object, all objects that are added are recorded here.
|
||||
Vector< SceneObject* > mAddedOccluderObjects;
|
||||
|
||||
///
|
||||
BitVector mZoneVisibilityFlags;
|
||||
|
||||
/// ZoneState entries for all zones in the scene.
|
||||
Vector< SceneZoneCullingState > mZoneStates;
|
||||
|
||||
/// Allocator for culling data that can be freed in one go when
|
||||
/// the culling state is freed.
|
||||
DataChunker mDataChunker;
|
||||
|
||||
/// If true, occlusion checks will not be done against the terrains
|
||||
/// in the scene.
|
||||
bool mDisableTerrainOcclusion;
|
||||
|
||||
/// If true, all objects will only be tested against the root
|
||||
/// frustum.
|
||||
bool mDisableZoneCulling;
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
SceneCullingState( SceneManager* sceneManager,
|
||||
const SceneCameraState& cameraState );
|
||||
|
||||
/// Return the scene which is being culled in this state.
|
||||
SceneManager* getSceneManager() const { return mSceneManager; }
|
||||
|
||||
/// Return the root frustum which is used to set up scene visibility.
|
||||
const Frustum& getFrustum() const { return getCameraState().getFrustum(); }
|
||||
|
||||
/// Return the viewing state that defines how the scene is being viewed.
|
||||
const SceneCameraState& getCameraState() const { return mCameraState; }
|
||||
|
||||
/// Return the root culling volume that corresponds to the camera frustum.
|
||||
/// @note This volume omits the near and far plane of the frustum's polyhedron
|
||||
/// as these will be tested separately during culling. Testing them repeatedly
|
||||
/// just wastes time.
|
||||
const SceneCullingVolume& getRootVolume() const { return mRootVolume; }
|
||||
|
||||
/// @name Visibility and Occlusion
|
||||
/// @{
|
||||
|
||||
enum CullOptions
|
||||
{
|
||||
/// Cull objects that have their SceneObject::DisableCullingInEditorFlag set.
|
||||
/// By default, these objects will not get culled if the editor is active.
|
||||
CullEditorOverrides = BIT( 0 ),
|
||||
|
||||
/// Do not cull objects that are render-disabled.
|
||||
/// @see SceneObject::isRenderEnabled()
|
||||
DontCullRenderDisabled = BIT( 1 )
|
||||
};
|
||||
|
||||
/// Cull the given list of objects according to the current culling state.
|
||||
///
|
||||
/// @param object Array of objects. This array will be modified in place.
|
||||
/// @param numObjects Number of objects in @a objects.
|
||||
/// @param cullOptions Combination of CullOptions.
|
||||
///
|
||||
/// @return Number of objects remaining in the list.
|
||||
U32 cullObjects( SceneObject** objects, U32 numObjects, U32 cullOptions = 0 ) const;
|
||||
|
||||
/// Return true if the given object is culled according to the current culling state.
|
||||
bool isCulled( SceneObject* object ) const { return ( cullObjects( &object, 1 ) == 0 ); }
|
||||
|
||||
/// Return true if the given AABB is culled in any of the given zones.
|
||||
bool isCulled( const Box3F& aabb, const U32* zones, U32 numZones ) const;
|
||||
|
||||
/// Return true if the given OBB is culled in any of the given zones.
|
||||
bool isCulled( const OrientedBox3F& obb, const U32* zones, U32 numZones ) const;
|
||||
|
||||
/// Return true if the given sphere is culled in any of the given zones.
|
||||
bool isCulled( const SphereF& sphere, const U32* zones, U32 numZones ) const;
|
||||
|
||||
/// Return true if the given object is occluded according to the current culling state.
|
||||
bool isOccluded( SceneObject* object ) const;
|
||||
|
||||
/// Return true if the given AABB is occluded according to the current culling state.
|
||||
bool isOccluded( const Box3F& aabb, const U32* zones, U32 numZones ) const;
|
||||
|
||||
/// Return true if the given OBB is occluded according to the current culling state.
|
||||
bool isOccluded( const OrientedBox3F& obb, const U32* zones, U32 numZones ) const;
|
||||
|
||||
/// Return true if the given sphere is occluded according to the current culling state.
|
||||
bool isOccluded( const SphereF& sphere, const U32* zones, U32 numZones ) const;
|
||||
|
||||
/// Add the occlusion information contained in the given object.
|
||||
///
|
||||
/// @note This should only be called after all positive frustums have been added
|
||||
/// to the zone state.
|
||||
void addOccluder( SceneObject* object );
|
||||
|
||||
/// Test whether the given object is occluded by any of the terrains
|
||||
/// in the scene.
|
||||
bool isOccludedByTerrain( SceneObject* object ) const;
|
||||
|
||||
/// Set whether isCulled() should do terrain occlusion checks or not.
|
||||
void setDisableTerrainOcclusion( bool value ) { mDisableTerrainOcclusion = value; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Zones
|
||||
/// @{
|
||||
|
||||
/// If true, culling will only be performed against the root frustum
|
||||
/// and not against frustums of individual zones.
|
||||
///
|
||||
/// @note This also disables occluders as these are added to the zone frustums.
|
||||
bool disableZoneCulling() const { return mDisableZoneCulling; }
|
||||
void disableZoneCulling( bool value ) { mDisableZoneCulling = value; }
|
||||
|
||||
/// Return true if any of the zones that the object is currently are
|
||||
/// visible.
|
||||
bool isWithinVisibleZone( SceneObject* object ) const;
|
||||
|
||||
/// Return a bit vector with one bit for each zone in the scene. If the bit is set,
|
||||
/// the zone has includer culling volumes attached to it and thus is visible.
|
||||
const BitVector& getZoneVisibilityFlags() const { return mZoneVisibilityFlags; }
|
||||
|
||||
/// Return the culling state for a particular zone.
|
||||
/// @param zoneId Numeric ID of zone.
|
||||
const SceneZoneCullingState& getZoneState( U32 zoneId ) const
|
||||
{
|
||||
AssertFatal( zoneId < ( U32 ) mZoneStates.size(), "SceneCullingState::getZoneState - Index out of bounds" );
|
||||
return mZoneStates[ zoneId ];
|
||||
}
|
||||
|
||||
/// Returns the culling state for a particular zone.
|
||||
/// @param zoneId Numeric ID of zone.
|
||||
SceneZoneCullingState& getZoneState( U32 zoneId )
|
||||
{
|
||||
return const_cast< SceneZoneCullingState& >( static_cast< const SceneCullingState* >( this )->getZoneState( zoneId ) );
|
||||
}
|
||||
|
||||
/// Add a culling volume to the visibility state of the given zone.
|
||||
///
|
||||
/// @param zoneId ID of zone to which to add the given frustum's visibility information.
|
||||
/// @param volume A culling volume. Note that the data in the volume must have
|
||||
/// a lifetime at least as long as the culling state.
|
||||
///
|
||||
/// @return True if the visibility state of the zone has changed, i.e. if the volume
|
||||
/// was either added in whole or merged with an existing set of planes. If the visibility
|
||||
/// state of the zone has not changed, returns false.
|
||||
bool addCullingVolumeToZone( U32 zoneId, const SceneCullingVolume& volume );
|
||||
|
||||
/// Copy the data from the given polyhedron to the culling state, create
|
||||
/// a new culling volume it and add it to the current culling state of the given zone.
|
||||
///
|
||||
/// @param zoneId ID of zone to which to add the given frustum's visibility information.
|
||||
/// @param type Which type of culling volume to add.
|
||||
/// @param polyhedron Polyhedron describing the space of the culling volume.
|
||||
bool addCullingVolumeToZone( U32 zoneId, SceneCullingVolume::Type type, const AnyPolyhedron& polyhedron );
|
||||
|
||||
/// Create a new culling volume by extruding the given polygon away from the viewpoint.
|
||||
///
|
||||
/// @param vertices Array of polygon vertices.
|
||||
/// @param numVertices Number of vertices in @a vertices.
|
||||
/// @param type Type of culling volume to create.
|
||||
/// @param outVolume (out) Receives the generated volume, if successful.
|
||||
///
|
||||
/// @return True if a volume could be generated from the given polygon or false if not.
|
||||
bool createCullingVolume( const Point3F* vertices, U32 numVertices, SceneCullingVolume::Type type, SceneCullingVolume& outVolume );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Memory Management
|
||||
///
|
||||
/// Rather than allocating a lot of individual point and plane data for the culling volumes,
|
||||
/// it is more efficient to batch allocate chunks of memory and then release all the memory
|
||||
/// for all culling volumes in one go. This is facilitated by this interface.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Allocate memory from this culling state. The memory is freed when the
|
||||
/// culling state is destroyed.
|
||||
void* allocateData( U32 size ) { return mDataChunker.alloc( size ); }
|
||||
|
||||
/// Allocate memory for @a num instances of T from this culling state.
|
||||
template< typename T >
|
||||
T* allocateData( U32 num ) { return reinterpret_cast< T* >( allocateData( sizeof( T ) * num ) ); }
|
||||
|
||||
/// @}
|
||||
|
||||
/// Queue debug visualizations of the culling volumes of all currently selected zones
|
||||
/// (or, if no zone is selected, all volumes in the outdoor zone) to the debug drawer.
|
||||
void debugRenderCullingVolumes() const;
|
||||
|
||||
private:
|
||||
|
||||
typedef SceneZoneCullingState::CullingTestResult CullingTestResult;
|
||||
|
||||
// Helper methods to avoid code duplication.
|
||||
|
||||
template< bool OCCLUDERS_ONLY, typename T > CullingTestResult _test( const T& bounds, const U32* zones, U32 numZones ) const;
|
||||
template< typename T, typename Iter > CullingTestResult _test
|
||||
( const T& bounds, Iter iter, const PlaneF& nearPlane, const PlaneF& farPlane ) const;
|
||||
template< typename T, typename Iter > CullingTestResult _testOccludersOnly( const T& bounds, Iter iter ) const;
|
||||
};
|
||||
|
||||
#endif // !_SCENECULLINGSTATE_H_
|
||||
24
Engine/source/scene/culling/sceneCullingVolume.cpp
Normal file
24
Engine/source/scene/culling/sceneCullingVolume.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/culling/sceneCullingVolume.h"
|
||||
130
Engine/source/scene/culling/sceneCullingVolume.h
Normal file
130
Engine/source/scene/culling/sceneCullingVolume.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENECULLINGVOLUME_H_
|
||||
#define _SCENECULLINGVOLUME_H_
|
||||
|
||||
#ifndef _MPLANESET_H_
|
||||
#include "math/mPlaneSet.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// A volume used to include or exclude space in a scene.
|
||||
///
|
||||
/// Culling volumes are represented as sets of clipping planes.
|
||||
///
|
||||
/// @note Culling is performed in world space so the plane data for culling volumes
|
||||
/// must be in world space too.
|
||||
class SceneCullingVolume
|
||||
{
|
||||
public:
|
||||
|
||||
/// Type of culling.
|
||||
enum Type
|
||||
{
|
||||
Includer,
|
||||
Occluder,
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
/// What type of culling volume this is.
|
||||
Type mType;
|
||||
|
||||
///
|
||||
F32 mSortPoint;
|
||||
|
||||
/// The set of clipping planes that defines the clipping volume for this culler.
|
||||
PlaneSetF mClippingPlanes;
|
||||
|
||||
/// Test the given bounds against this culling volume.
|
||||
///
|
||||
/// Note that we allow false positives here for includers. This will only cause an
|
||||
/// occasional object to be classified as intersecting when in fact it is outside.
|
||||
/// This is still better though than requiring the expensive intersection tests for
|
||||
/// all intersecting objects.
|
||||
///
|
||||
/// @return True if the culling volume accepts the given bounds.
|
||||
template< typename B > bool _testBounds( const B& bounds ) const
|
||||
{
|
||||
if( isOccluder() )
|
||||
return getPlanes().isContained( bounds );
|
||||
else
|
||||
return ( getPlanes().testPotentialIntersection( bounds ) != GeometryOutside );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Create an *uninitialized* culling volume.
|
||||
SceneCullingVolume() {}
|
||||
|
||||
///
|
||||
SceneCullingVolume( Type type, const PlaneSetF& planes )
|
||||
: mType( type ), mClippingPlanes( planes ), mSortPoint( 1.f ) {}
|
||||
|
||||
/// Return the type of volume defined by this culling volume, i.e. whether it includes
|
||||
/// or excludes space.
|
||||
Type getType() const { return mType; }
|
||||
|
||||
/// Return true if this is an inclusion volume.
|
||||
bool isIncluder() const { return ( getType() == Includer ); }
|
||||
|
||||
/// Return true if this is an occlusion volume.
|
||||
bool isOccluder() const { return ( getType() == Occluder ); }
|
||||
|
||||
/// Return the set of clipping planes that defines the culling volume.
|
||||
const PlaneSetF& getPlanes() const { return mClippingPlanes; }
|
||||
|
||||
/// @name Sorting
|
||||
///
|
||||
/// Before testing, culling volumes will be sorted by decreasing probability of causing
|
||||
/// test positives. Thus, the sort point of a volume should be a rough metric of the amount
|
||||
/// of scene/screen space it covers.
|
||||
///
|
||||
/// Note that sort points for occluders are independent of sort points for includers.
|
||||
/// @{
|
||||
|
||||
/// Return the sort point value of the volume. The larger the value, the more likely the
|
||||
/// volume is to cause positive test results with bounding volumes.
|
||||
F32 getSortPoint() const { return mSortPoint; }
|
||||
|
||||
///
|
||||
void setSortPoint( F32 value ) { mSortPoint = value; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Testing
|
||||
/// @{
|
||||
|
||||
/// Return true if the volume accepts the given AABB.
|
||||
bool test( const Box3F& aabb ) const { return _testBounds( aabb ); }
|
||||
|
||||
/// Return true if the volume accepts the given OBB.
|
||||
bool test( const OrientedBox3F& obb ) const { return _testBounds( obb ); }
|
||||
|
||||
/// Return true if the volume accepts the given sphere.
|
||||
bool test( const SphereF& sphere ) const { return _testBounds( sphere ); }
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // !_SCENECULLINGVOLUME_H_
|
||||
235
Engine/source/scene/culling/sceneZoneCullingState.cpp
Normal file
235
Engine/source/scene/culling/sceneZoneCullingState.cpp
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/culling/sceneZoneCullingState.h"
|
||||
|
||||
#include "scene/culling/sceneCullingState.h"
|
||||
#include "platform/profiler.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T >
|
||||
inline SceneZoneCullingState::CullingTestResult SceneZoneCullingState::_testVolumes( T bounds, bool occludersOnly ) const
|
||||
{
|
||||
// If we are testing for occlusion only and we don't have any
|
||||
// occlusion volumes, we can early out here.
|
||||
|
||||
if( occludersOnly && !mHaveOccluders )
|
||||
return CullingTestNegative;
|
||||
|
||||
// If we haven't sorted the volumes on this zone state yet,
|
||||
// do so now.
|
||||
|
||||
if( !mHaveSortedVolumes )
|
||||
_sortVolumes();
|
||||
|
||||
// Now go through the volumes in this zone and test them
|
||||
// against the bounds.
|
||||
|
||||
for( CullingVolumeLink* link = mCullingVolumes; link != NULL; link = link->mNext )
|
||||
{
|
||||
const SceneCullingVolume& volume = link->mVolume;
|
||||
|
||||
if( volume.isOccluder() )
|
||||
{
|
||||
if( volume.test( bounds ) )
|
||||
return CullingTestPositiveByOcclusion;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are testing for occlusion only, we can early out as soon
|
||||
// as we have reached the first non-inverted volume.
|
||||
if( occludersOnly )
|
||||
return CullingTestNegative;
|
||||
|
||||
if( volume.test( bounds ) )
|
||||
return CullingTestPositiveByInclusion;
|
||||
}
|
||||
}
|
||||
|
||||
return CullingTestNegative;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneZoneCullingState::CullingTestResult SceneZoneCullingState::testVolumes( const Box3F& aabb, bool invertedOnly ) const
|
||||
{
|
||||
PROFILE_SCOPE( SceneZoneCullingState_testVolumes );
|
||||
return _testVolumes( aabb, invertedOnly );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneZoneCullingState::CullingTestResult SceneZoneCullingState::testVolumes( const OrientedBox3F& obb, bool invertedOnly ) const
|
||||
{
|
||||
PROFILE_SCOPE( SceneZoneCullingState_testVolumes_OBB );
|
||||
return _testVolumes( obb, invertedOnly );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneZoneCullingState::CullingTestResult SceneZoneCullingState::testVolumes( const SphereF& sphere, bool invertedOnly ) const
|
||||
{
|
||||
PROFILE_SCOPE( SceneZoneCullingState_testVolumes_Sphere );
|
||||
return _testVolumes( sphere, invertedOnly );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneCullingState::_sortVolumes() const
|
||||
{
|
||||
// First do a pass to gather all occlusion volumes. These must be put on the
|
||||
// list in front of all inclusion volumes. Otherwise, an inclusion volume
|
||||
// may test positive when in fact an occlusion volume would reject the object.
|
||||
|
||||
CullingVolumeLink* occluderHead = NULL;
|
||||
CullingVolumeLink* occluderTail = NULL;
|
||||
|
||||
if( mHaveOccluders )
|
||||
{
|
||||
U32 numOccluders = 0;
|
||||
|
||||
for( CullingVolumeLink* current = mCullingVolumes, *prev = NULL; current != NULL; )
|
||||
{
|
||||
CullingVolumeLink* next = current->mNext;
|
||||
|
||||
if( !current->mVolume.isOccluder() )
|
||||
prev = current;
|
||||
else
|
||||
{
|
||||
// Unlink from list.
|
||||
|
||||
if( prev )
|
||||
prev->mNext = next;
|
||||
else
|
||||
mCullingVolumes = next;
|
||||
|
||||
// Sort into list.
|
||||
|
||||
_insertSorted( occluderHead, occluderTail, current );
|
||||
++ numOccluders;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
// If we ended up with more inverted (occlusion) volumes than we want,
|
||||
// chop off any but the first N volumes. Since we have sorted the volumes
|
||||
// by screen coverage, this will get rid of smallest occlusion volumes.
|
||||
|
||||
if( numOccluders > SceneCullingState::smMaxOccludersPerZone )
|
||||
{
|
||||
CullingVolumeLink* last = occluderHead;
|
||||
for( U32 i = 0; i < ( SceneCullingState::smMaxOccludersPerZone - 1 ); ++ i )
|
||||
last = last->mNext;
|
||||
|
||||
// Chop off rest. The links are allocated on the chunker
|
||||
// and thus will simply disappear when the state gets deleted.
|
||||
|
||||
last->mNext = NULL;
|
||||
occluderTail = last;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, do a second pass to sort all includer volumes by decreasing screen
|
||||
// real estate so that when testing against them, we test the larger volumes first
|
||||
// and the smaller ones later.
|
||||
|
||||
CullingVolumeLink* includerHead = NULL;
|
||||
CullingVolumeLink* includerTail = NULL;
|
||||
|
||||
while( mCullingVolumes )
|
||||
{
|
||||
CullingVolumeLink* current = mCullingVolumes;
|
||||
|
||||
AssertFatal( !current->mVolume.isOccluder(),
|
||||
"SceneCullingState::ZoneState::_sortFrustums - Occluders must have been filtered out at this point" );
|
||||
|
||||
// Unlink from list.
|
||||
|
||||
mCullingVolumes = current->mNext;
|
||||
|
||||
// Sort into list.
|
||||
|
||||
_insertSorted( includerHead, includerTail, current );
|
||||
}
|
||||
|
||||
// Merge the two lists. Put inverted volumes first and
|
||||
// non-inverted volumes second.
|
||||
|
||||
if( occluderHead != NULL )
|
||||
{
|
||||
mCullingVolumes = occluderHead;
|
||||
occluderTail->mNext = includerHead;
|
||||
}
|
||||
else
|
||||
mCullingVolumes = includerHead;
|
||||
|
||||
// Done.
|
||||
|
||||
mHaveSortedVolumes = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneZoneCullingState::_insertSorted( CullingVolumeLink*& head, CullingVolumeLink*& tail, CullingVolumeLink* link )
|
||||
{
|
||||
// If first element, just put it in the list
|
||||
// and return.
|
||||
|
||||
if( !head )
|
||||
{
|
||||
head = link;
|
||||
tail = link;
|
||||
link->mNext = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, search for where to put it.
|
||||
|
||||
F32 sortPoint = link->mVolume.getSortPoint();
|
||||
|
||||
for( CullingVolumeLink* current = head, *prev = NULL; current != NULL; prev = current, current = current->mNext )
|
||||
{
|
||||
F32 currentSortPoint = current->mVolume.getSortPoint();
|
||||
if( currentSortPoint > sortPoint )
|
||||
continue;
|
||||
|
||||
if( !prev )
|
||||
head = link;
|
||||
else
|
||||
prev->mNext = link;
|
||||
|
||||
link->mNext = current;
|
||||
return;
|
||||
}
|
||||
|
||||
// Smallest frustum in list. Append to end.
|
||||
|
||||
tail->mNext = link;
|
||||
link->mNext = NULL;
|
||||
|
||||
tail = link;
|
||||
}
|
||||
171
Engine/source/scene/culling/sceneZoneCullingState.h
Normal file
171
Engine/source/scene/culling/sceneZoneCullingState.h
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENEZONECULLINGSTATE_H_
|
||||
#define _SCENEZONECULLINGSTATE_H_
|
||||
|
||||
#ifndef _SCENECULLINGVOLUME_H_
|
||||
#include "scene/culling/sceneCullingVolume.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// Culling state for a zone.
|
||||
///
|
||||
/// Zone states keep track of the culling volumes that are generated during traversal
|
||||
/// for a particular zone in a scene.
|
||||
///
|
||||
/// @note This class has no meaningful constructor; the memory for all zone states is
|
||||
/// cleared en bloc.
|
||||
class SceneZoneCullingState
|
||||
{
|
||||
public:
|
||||
|
||||
friend class SceneCullingState; // mCullingVolumes
|
||||
|
||||
/// Result of a culling test in a zone.
|
||||
enum CullingTestResult
|
||||
{
|
||||
/// An includer tested positive on the bounding volume.
|
||||
CullingTestPositiveByInclusion,
|
||||
|
||||
/// An occluder tested positive on the bounding volume.
|
||||
CullingTestPositiveByOcclusion,
|
||||
|
||||
/// None of the culling volumes included or excluded the bounding volume.
|
||||
CullingTestNegative
|
||||
};
|
||||
|
||||
/// A culling volume linked to a zone.
|
||||
///
|
||||
/// @note Memory for CullingVolumeLink instances is maintained by SceneCullingState.
|
||||
struct CullingVolumeLink
|
||||
{
|
||||
/// Culling volume.
|
||||
SceneCullingVolume mVolume;
|
||||
|
||||
/// Next culling volume linked to the zone.
|
||||
CullingVolumeLink* mNext;
|
||||
|
||||
CullingVolumeLink( const SceneCullingVolume& volume )
|
||||
: mVolume( volume ) {}
|
||||
};
|
||||
|
||||
/// Iterator over the culling volumes assigned to a zone.
|
||||
struct CullingVolumeIterator
|
||||
{
|
||||
CullingVolumeIterator( const SceneZoneCullingState& state )
|
||||
: mCurrent( state.getCullingVolumes() ) {}
|
||||
|
||||
bool isValid() const { return mCurrent != NULL; }
|
||||
const SceneCullingVolume& operator *() const
|
||||
{
|
||||
AssertFatal( isValid(), "SceneCullingState::ZoneState::CullingVolumeIterator::operator* - Invalid iterator" );
|
||||
return mCurrent->mVolume;
|
||||
}
|
||||
const SceneCullingVolume* operator ->() const
|
||||
{
|
||||
AssertFatal( isValid(), "SceneCullingState::ZoneState::CullingVolumeIterator::operator-> - Invalid iterator" );
|
||||
return &mCurrent->mVolume;
|
||||
}
|
||||
CullingVolumeIterator& operator ++()
|
||||
{
|
||||
AssertFatal( isValid(), "SceneCullingState::ZoneState::CullingVolumeIterator::operator++ - Invalid iterator" );
|
||||
mCurrent = mCurrent->mNext;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
CullingVolumeLink* mCurrent;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
/// Whether tests can be short-circuited, i.e. the first culler that rejects or accepts
|
||||
/// will cause the test to terminate. This is the case if there are no includers inside
|
||||
/// occluders, i.e. if occluders can be trusted to fully exclude any space they cover.
|
||||
bool mCanShortcuit;//RDTODO: implement this
|
||||
|
||||
/// Link of culling volumes defining the visibility state of the zone. Since there may be
|
||||
/// multiple portals leading into a zone or multiple occluders inside a zone, we may have multiple
|
||||
/// culling volumes.
|
||||
mutable CullingVolumeLink* mCullingVolumes;
|
||||
|
||||
/// Whether culling volumes for this zone state have already been sorted.
|
||||
mutable bool mHaveSortedVolumes;
|
||||
|
||||
/// Whether there are inclusion volumes on this state.
|
||||
bool mHaveIncluders;
|
||||
|
||||
/// Whether there are occlusion volumes on this state.
|
||||
bool mHaveOccluders;
|
||||
|
||||
/// Culling volume test abstracted over bounding volume type.
|
||||
template< typename T > CullingTestResult _testVolumes( T bounds, bool occludersOnly ) const;
|
||||
|
||||
/// Sort the culling volumes such that the volumes with the highest probability
|
||||
/// of rejecting objects come first in the list. Also, make sure that all
|
||||
/// occluders come before all includers so that occlusion is handled correctly.
|
||||
void _sortVolumes() const;
|
||||
|
||||
/// Insert the volume in @a link at the proper position in the list represented
|
||||
/// by @a head and @a tail.
|
||||
static void _insertSorted( CullingVolumeLink*& head, CullingVolumeLink*& tail, CullingVolumeLink* link );
|
||||
|
||||
public:
|
||||
|
||||
/// Zone states are constructed by SceneCullingState. This constructor should not
|
||||
/// be used otherwise. It is public due to the use through Vector in SceneCullingState.
|
||||
SceneZoneCullingState() {}
|
||||
|
||||
/// Return true if the zone is visible. This is the case if any
|
||||
/// includers have been added to the zone's rendering state.
|
||||
bool isZoneVisible() const { return mHaveIncluders; }
|
||||
|
||||
/// Return the list of culling volumes attached to the zone.
|
||||
CullingVolumeLink* getCullingVolumes() const { _sortVolumes(); return mCullingVolumes; }
|
||||
|
||||
/// Test whether the culling volumes added to the zone test positive on the
|
||||
/// given AABB, i.e. whether they include or exclude the given AABB.
|
||||
CullingTestResult testVolumes( const Box3F& aabb, bool occludersOnly = false ) const;
|
||||
|
||||
/// Test whether the culling volumes added to the zone test positive on the
|
||||
/// given OBB, i.e. whether they include or exclude the given OBB.
|
||||
///
|
||||
/// @param obb An OBB described by 8 points.
|
||||
/// @param invertedOnly If true, only inverted cullers are tested.
|
||||
CullingTestResult testVolumes( const OrientedBox3F& obb, bool occludersOnly = false ) const;
|
||||
|
||||
/// Test whether the culling volumes added to the zone test positive on the
|
||||
/// given sphere, i.e. whether they include or exclude the given sphere.
|
||||
CullingTestResult testVolumes( const SphereF& sphere, bool occludersOnly = false ) const;
|
||||
|
||||
/// Return true if the zone has more than one culling volume assigned to it.
|
||||
bool hasMultipleVolumes() const { return ( mCullingVolumes && mCullingVolumes->mNext ); }
|
||||
|
||||
/// Return true if the zone has inclusion volumes assigned to it.
|
||||
bool hasIncluders() const { return mHaveIncluders; }
|
||||
|
||||
/// Return true if the zone has occlusion volumes assigned to it.
|
||||
bool hasOccluders() const { return mHaveOccluders; }
|
||||
};
|
||||
|
||||
#endif // !_SCENEZONECULLINGSTATE_H_
|
||||
67
Engine/source/scene/fogStructs.h
Normal file
67
Engine/source/scene/fogStructs.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _FOGSTRUCTS_H_
|
||||
#define _FOGSTRUCTS_H_
|
||||
|
||||
/// The aerial fog settings.
|
||||
struct FogData
|
||||
{
|
||||
F32 density;
|
||||
F32 densityOffset;
|
||||
F32 atmosphereHeight;
|
||||
ColorF color;
|
||||
|
||||
FogData()
|
||||
{
|
||||
density = 0.0f;
|
||||
densityOffset = 0.0f;
|
||||
atmosphereHeight = 0.0f;
|
||||
color.set( 0.5f, 0.5f, 0.5f, 1.0f );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// The water fog settings.
|
||||
struct WaterFogData
|
||||
{
|
||||
F32 density;
|
||||
F32 densityOffset;
|
||||
F32 wetDepth;
|
||||
F32 wetDarkening;
|
||||
ColorI color;
|
||||
PlaneF plane;
|
||||
F32 depthGradMax;
|
||||
|
||||
WaterFogData()
|
||||
{
|
||||
density = 0.0f;
|
||||
densityOffset = 0.0f;
|
||||
wetDepth = 0.0f;
|
||||
wetDarkening = 0.0f;
|
||||
color.set( 0.5f, 0.5f, 0.5f, 1.0f );
|
||||
plane.set( 0.0f, 0.0f, 1.0f );
|
||||
depthGradMax = 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _FOGSTRUCTS_H_
|
||||
73
Engine/source/scene/mixin/sceneAmbientSoundObject.h
Normal file
73
Engine/source/scene/mixin/sceneAmbientSoundObject.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENEAMBIENTSOUNDOBJECT_H_
|
||||
#define _SCENEAMBIENTSOUNDOBJECT_H_
|
||||
|
||||
|
||||
class SFXAmbience;
|
||||
|
||||
|
||||
/// Template mixin to add ability to hold a custom SFXAmbience to
|
||||
/// a SceneObject.
|
||||
template< typename Base >
|
||||
class SceneAmbientSoundObject : public Base
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Base Parent;
|
||||
|
||||
protected:
|
||||
|
||||
enum
|
||||
{
|
||||
SoundMask = Parent::NextFreeMask << 0, ///< Ambient sound properties have changed.
|
||||
NextFreeMask = Parent::NextFreeMask << 1,
|
||||
};
|
||||
|
||||
/// Ambient sound properties for this space.
|
||||
SFXAmbience* mSoundAmbience;
|
||||
|
||||
public:
|
||||
|
||||
SceneAmbientSoundObject();
|
||||
|
||||
/// Set the ambient sound properties for the space.
|
||||
void setSoundAmbience( SFXAmbience* ambience );
|
||||
|
||||
// SimObject.
|
||||
static void initPersistFields();
|
||||
|
||||
// NetObject.
|
||||
virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream );
|
||||
virtual void unpackUpdate( NetConnection* connection, BitStream* stream );
|
||||
|
||||
// SceneObject.
|
||||
virtual SFXAmbience* getSoundAmbience() const { return mSoundAmbience; }
|
||||
|
||||
private:
|
||||
|
||||
// Console field getters/setters.
|
||||
static bool _setSoundAmbience( void* object, const char* index, const char* data );
|
||||
};
|
||||
|
||||
#endif // !_SCENEAMBIENTSOUNDOBJECT_H_
|
||||
114
Engine/source/scene/mixin/sceneAmbientSoundObject.impl.h
Normal file
114
Engine/source/scene/mixin/sceneAmbientSoundObject.impl.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/mixin/sceneAmbientSoundObject.h"
|
||||
|
||||
#include "T3D/sfx/sfx3DWorld.h"
|
||||
#include "sfx/sfxTypes.h"
|
||||
#include "sfx/sfxAmbience.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base >
|
||||
SceneAmbientSoundObject< Base >::SceneAmbientSoundObject()
|
||||
: mSoundAmbience( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base >
|
||||
void SceneAmbientSoundObject< Base >::initPersistFields()
|
||||
{
|
||||
Parent::addGroup( "Sound" );
|
||||
Parent::addProtectedField( "soundAmbience", TypeSFXAmbienceName, Offset( mSoundAmbience, SceneAmbientSoundObject ),
|
||||
&_setSoundAmbience, &defaultProtectedGetFn,
|
||||
"Ambient sound environment for the space." );
|
||||
Parent::endGroup( "Sound" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base >
|
||||
U32 SceneAmbientSoundObject< Base >::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
|
||||
{
|
||||
U32 retMask = Parent::packUpdate( connection, mask, stream );
|
||||
|
||||
if( stream->writeFlag( mask & SoundMask ) )
|
||||
sfxWrite( stream, mSoundAmbience );
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base >
|
||||
void SceneAmbientSoundObject< Base >::unpackUpdate( NetConnection* connection, BitStream* stream )
|
||||
{
|
||||
Parent::unpackUpdate( connection, stream );
|
||||
|
||||
if( stream->readFlag() ) // SoundMask
|
||||
{
|
||||
SFXAmbience* ambience;
|
||||
|
||||
String errorStr;
|
||||
if( !sfxReadAndResolve( stream, &ambience, errorStr ) )
|
||||
Con::errorf( "SceneAmbientSoundObject::unpackUpdate - bad packet: %s", errorStr.c_str() );
|
||||
else
|
||||
setSoundAmbience( ambience );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base >
|
||||
void SceneAmbientSoundObject< Base >::setSoundAmbience( SFXAmbience* ambience )
|
||||
{
|
||||
if( mSoundAmbience == ambience )
|
||||
return;
|
||||
|
||||
mSoundAmbience = ambience;
|
||||
|
||||
if( this->isServerObject() )
|
||||
this->setMaskBits( SoundMask );
|
||||
else if( this->isProperlyAdded() && gSFX3DWorld )
|
||||
gSFX3DWorld->notifyChanged( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base >
|
||||
bool SceneAmbientSoundObject< Base >::_setSoundAmbience( void* object, const char* index, const char* data )
|
||||
{
|
||||
SceneAmbientSoundObject* p = reinterpret_cast< SceneAmbientSoundObject* >( object );
|
||||
SFXAmbience* ambience = EngineUnmarshallData< SFXAmbience* >()( data );
|
||||
p->setSoundAmbience( ambience );
|
||||
return false;
|
||||
}
|
||||
116
Engine/source/scene/mixin/scenePolyhedralObject.h
Normal file
116
Engine/source/scene/mixin/scenePolyhedralObject.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENEPOLYHEDRALOBJECT_H_
|
||||
#define _SCENEPOLYHEDRALOBJECT_H_
|
||||
|
||||
#ifndef _MPOLYHEDRON_H_
|
||||
#include "math/mPolyhedron.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// Shared interface for polyhedral objects.
|
||||
struct IScenePolyhedralObject
|
||||
{
|
||||
/// Convert the polyhedral object to a raw polyhedron.
|
||||
virtual AnyPolyhedron ToAnyPolyhedron() const = 0;
|
||||
};
|
||||
|
||||
|
||||
/// Helper template for mixing a polyhedral volume definition into
|
||||
/// the superclass hierarchy of a SceneSpace-derived class.
|
||||
template< typename Base, typename P = Polyhedron >
|
||||
class ScenePolyhedralObject : public Base, public IScenePolyhedralObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Base Parent;
|
||||
typedef P PolyhedronType;
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_PLANES = 256,
|
||||
MAX_POINTS = 256,
|
||||
MAX_EDGES = 256
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
enum
|
||||
{
|
||||
PolyMask = Parent::NextFreeMask << 0,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
/// Whether the polyhedron corresponds to the object box. If so,
|
||||
/// several things can be fast-tracked. For example, serializing the
|
||||
/// polyhedron is pointless as it can be easily reconstructed from the
|
||||
/// object box on load time. Also, certain operations like containment
|
||||
/// tests have significantly faster formulations for AABBs (given that the
|
||||
/// input data is transformed into object space) than for general
|
||||
/// polyhedrons.
|
||||
bool mIsBox;
|
||||
|
||||
/// The polyhedron that defines the volume of the object.
|
||||
/// @note Defined in object space by default.
|
||||
PolyhedronType mPolyhedron;
|
||||
|
||||
///
|
||||
virtual void _renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat );
|
||||
|
||||
public:
|
||||
|
||||
ScenePolyhedralObject()
|
||||
: mIsBox( true ) {}
|
||||
|
||||
ScenePolyhedralObject( const PolyhedronType& polyhedron )
|
||||
: mIsBox( false ),
|
||||
mPolyhedron( polyhedron ) {}
|
||||
|
||||
/// Return the polyhedron that describes the space.
|
||||
const PolyhedronType& getPolyhedron() const { return mPolyhedron; }
|
||||
|
||||
// SimObject.
|
||||
virtual bool onAdd();
|
||||
virtual void writeFields( Stream& stream, U32 tabStop );
|
||||
virtual bool writeField( StringTableEntry name, const char* value );
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
// NetObject.
|
||||
virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream );
|
||||
virtual void unpackUpdate( NetConnection* connection, BitStream* stream );
|
||||
|
||||
// SceneObject.
|
||||
virtual bool containsPoint( const Point3F& point );
|
||||
|
||||
// IScenePolyhedralObject.
|
||||
virtual AnyPolyhedron ToAnyPolyhedron() const { return getPolyhedron(); }
|
||||
|
||||
private:
|
||||
|
||||
static bool _setPlane( void* object, const char* index, const char* data );
|
||||
static bool _setPoint( void* object, const char* index, const char* data );
|
||||
static bool _setEdge( void* object, const char* index, const char* data );
|
||||
};
|
||||
|
||||
#endif // !_SCENEPOLYHEDRALOBJECT_H_
|
||||
395
Engine/source/scene/mixin/scenePolyhedralObject.impl.h
Normal file
395
Engine/source/scene/mixin/scenePolyhedralObject.impl.h
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/mixin/scenePolyhedralObject.h"
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "math/mathIO.h"
|
||||
|
||||
#if 0 // Enable when enabling debug rendering below.
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "gfx/sim/debugDraw.h"
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
void ScenePolyhedralObject< Base, P >::initPersistFields()
|
||||
{
|
||||
Parent::addGroup( "Internal" );
|
||||
|
||||
Parent::addProtectedField( "plane", TypeRealString, NULL,
|
||||
&_setPlane, &defaultProtectedGetFn,
|
||||
"For internal use only.",
|
||||
AbstractClassRep::FIELD_HideInInspectors );
|
||||
Parent::addProtectedField( "point", TypeRealString, NULL,
|
||||
&_setPoint, &defaultProtectedGetFn,
|
||||
"For internal use only.",
|
||||
AbstractClassRep::FIELD_HideInInspectors );
|
||||
Parent::addProtectedField( "edge", TypeRealString, NULL,
|
||||
&_setEdge, &defaultProtectedGetFn,
|
||||
"For internal use only.",
|
||||
AbstractClassRep::FIELD_HideInInspectors );
|
||||
|
||||
Parent::endGroup( "Internal" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
bool ScenePolyhedralObject< Base, P >::onAdd()
|
||||
{
|
||||
// If no polyhedron has been initialized for the zone, default
|
||||
// to object box. Do this before calling the parent's onAdd()
|
||||
// so that we set the object box correctly.
|
||||
|
||||
if( mPolyhedron.getNumPlanes() == 0 )
|
||||
{
|
||||
mPolyhedron.buildBox( MatrixF::Identity, this->getObjBox() );
|
||||
mIsBox = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsBox = false;
|
||||
|
||||
// Compute object-space bounds from polyhedron.
|
||||
this->mObjBox = mPolyhedron.getBounds();
|
||||
}
|
||||
|
||||
if( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
bool ScenePolyhedralObject< Base, P >::containsPoint( const Point3F& point )
|
||||
{
|
||||
// If our shape is the OBB, use the default implementation
|
||||
// inherited from SceneObject.
|
||||
|
||||
if( this->mIsBox )
|
||||
return Parent::containsPoint( point );
|
||||
|
||||
// Take the point into our local object space.
|
||||
|
||||
Point3F p = point;
|
||||
this->getWorldTransform().mulP( p );
|
||||
p.convolveInverse( this->getScale() );
|
||||
|
||||
// See if the polyhedron contains the point.
|
||||
|
||||
return mPolyhedron.isContained( p );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
void ScenePolyhedralObject< Base, P >::_renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat )
|
||||
{
|
||||
if( overrideMat )
|
||||
return;
|
||||
|
||||
if( this->mIsBox )
|
||||
Parent::_renderObject( ri, state, overrideMat );
|
||||
else if( !this->mEditorRenderMaterial )
|
||||
{
|
||||
GFXTransformSaver saver;
|
||||
|
||||
MatrixF mat = this->getRenderTransform();
|
||||
mat.scale( this->getScale() );
|
||||
|
||||
GFX->multWorld( mat );
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setZReadWrite( true, false );
|
||||
desc.setBlend( true );
|
||||
desc.setCullMode( GFXCullNone );
|
||||
|
||||
GFX->getDrawUtil()->drawPolyhedron( desc, mPolyhedron, this->_getDefaultEditorSolidColor() );
|
||||
|
||||
// Render black wireframe.
|
||||
|
||||
desc.setFillModeWireframe();
|
||||
GFX->getDrawUtil()->drawPolyhedron( desc, mPolyhedron, this->_getDefaultEditorWireframeColor() );
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: render polyhedron with material
|
||||
}
|
||||
|
||||
// Debug rendering.
|
||||
|
||||
#if 0
|
||||
if( state->isDiffusePass() )
|
||||
DebugDrawer::get()->drawPolyhedronDebugInfo( mPolyhedron, this->getTransform(), this->getScale() );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
U32 ScenePolyhedralObject< Base, P >::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
|
||||
{
|
||||
U32 retMask = Parent::packUpdate( connection, mask, stream );
|
||||
|
||||
if( stream->writeFlag( !mIsBox && ( mask & PolyMask ) ) )
|
||||
{
|
||||
// Write planes.
|
||||
|
||||
const U32 numPlanes = mPolyhedron.getNumPlanes();
|
||||
const typename PolyhedronType::PlaneType* planes = mPolyhedron.getPlanes();
|
||||
|
||||
stream->writeInt( numPlanes, 8 );
|
||||
for( U32 i = 0; i < numPlanes; ++ i )
|
||||
mathWrite( *stream, planes[ i ] );
|
||||
|
||||
// Write points.
|
||||
|
||||
const U32 numPoints = mPolyhedron.getNumPoints();
|
||||
const typename PolyhedronType::PointType* points = mPolyhedron.getPoints();
|
||||
|
||||
stream->writeInt( numPoints, 8 );
|
||||
for( U32 i = 0; i < numPoints; ++ i )
|
||||
mathWrite( *stream, points[ i ] );
|
||||
|
||||
// Write edges.
|
||||
|
||||
const U32 numEdges = mPolyhedron.getNumEdges();
|
||||
const typename PolyhedronType::EdgeType* edges = mPolyhedron.getEdges();
|
||||
|
||||
stream->writeInt( numEdges, 8 );
|
||||
for( U32 i = 0; i < numEdges; ++ i )
|
||||
{
|
||||
const typename PolyhedronType::EdgeType& edge = edges[ i ];
|
||||
|
||||
stream->writeInt( edge.face[ 0 ], 8 );
|
||||
stream->writeInt( edge.face[ 1 ], 8 );
|
||||
stream->writeInt( edge.vertex[ 0 ], 8 );
|
||||
stream->writeInt( edge.vertex[ 1 ], 8 );
|
||||
}
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
void ScenePolyhedralObject< Base, P >::unpackUpdate( NetConnection* connection, BitStream* stream )
|
||||
{
|
||||
Parent::unpackUpdate( connection, stream );
|
||||
|
||||
if( stream->readFlag() ) // PolyMask
|
||||
{
|
||||
// Read planes.
|
||||
|
||||
const U32 numPlanes = stream->readInt( 8 );
|
||||
mPolyhedron.planeList.setSize( numPlanes );
|
||||
|
||||
for( U32 i = 0; i < numPlanes; ++ i )
|
||||
mathRead( *stream, &mPolyhedron.planeList[ i ] );
|
||||
|
||||
// Read points.
|
||||
|
||||
const U32 numPoints = stream->readInt( 8 );
|
||||
mPolyhedron.pointList.setSize( numPoints );
|
||||
|
||||
for( U32 i = 0; i < numPoints; ++ i )
|
||||
mathRead( *stream, &mPolyhedron.pointList[ i ] );
|
||||
|
||||
// Read edges.
|
||||
|
||||
const U32 numEdges = stream->readInt( 8 );
|
||||
mPolyhedron.edgeList.setSize( numEdges );
|
||||
|
||||
for( U32 i = 0; i < numEdges; ++ i )
|
||||
{
|
||||
typename PolyhedronType::EdgeType& edge = mPolyhedron.edgeList[ i ];
|
||||
|
||||
edge.face[ 0 ] = stream->readInt( 8 );
|
||||
edge.face[ 1 ] = stream->readInt( 8 );
|
||||
edge.vertex[ 0 ] = stream->readInt( 8 );
|
||||
edge.vertex[ 1 ] = stream->readInt( 8 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
bool ScenePolyhedralObject< Base, P >::writeField( StringTableEntry name, const char* value )
|
||||
{
|
||||
StringTableEntry sPlane = StringTable->insert( "plane" );
|
||||
StringTableEntry sPoint = StringTable->insert( "point" );
|
||||
StringTableEntry sEdge = StringTable->insert( "edge" );
|
||||
|
||||
if( name == sPlane || name == sPoint || name == sEdge )
|
||||
return false;
|
||||
|
||||
return Parent::writeField( name, value );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
void ScenePolyhedralObject< Base, P >::writeFields( Stream& stream, U32 tabStop )
|
||||
{
|
||||
Parent::writeFields( stream, tabStop );
|
||||
|
||||
// If the polyhedron is the same as our object box,
|
||||
// don't bother writing out the planes and points.
|
||||
|
||||
if( mIsBox )
|
||||
return;
|
||||
|
||||
stream.write( 2, "\r\n" );
|
||||
|
||||
// Write all planes.
|
||||
|
||||
const U32 numPlanes = mPolyhedron.getNumPlanes();
|
||||
for( U32 i = 0; i < numPlanes; ++ i )
|
||||
{
|
||||
const PlaneF& plane = mPolyhedron.getPlanes()[ i ];
|
||||
|
||||
stream.writeTabs( tabStop );
|
||||
|
||||
char buffer[ 1024 ];
|
||||
dSprintf( buffer, sizeof( buffer ), "plane = \"%g %g %g %g\";",
|
||||
plane.x, plane.y, plane.z, plane.d
|
||||
);
|
||||
|
||||
stream.writeLine( reinterpret_cast< const U8* >( buffer ) );
|
||||
}
|
||||
|
||||
// Write all points.
|
||||
|
||||
const U32 numPoints = mPolyhedron.getNumPoints();
|
||||
for( U32 i = 0; i < numPoints; ++ i )
|
||||
{
|
||||
const Point3F& point = mPolyhedron.getPoints()[ i ];
|
||||
|
||||
stream.writeTabs( tabStop );
|
||||
|
||||
char buffer[ 1024 ];
|
||||
dSprintf( buffer, sizeof( buffer ), "point = \"%g %g %g\";",
|
||||
point.x, point.y, point.z
|
||||
);
|
||||
|
||||
stream.writeLine( reinterpret_cast< const U8* >( buffer ) );
|
||||
}
|
||||
|
||||
// Write all edges.
|
||||
|
||||
const U32 numEdges = mPolyhedron.getNumEdges();
|
||||
for( U32 i = 0; i < numEdges; ++ i )
|
||||
{
|
||||
const PolyhedronData::Edge& edge = mPolyhedron.getEdges()[ i ];
|
||||
|
||||
stream.writeTabs( tabStop );
|
||||
|
||||
char buffer[ 1024 ];
|
||||
dSprintf( buffer, sizeof( buffer ), "edge = \"%i %i %i %i\";",
|
||||
edge.face[ 0 ], edge.face[ 1 ],
|
||||
edge.vertex[ 0 ], edge.vertex[ 1 ]
|
||||
);
|
||||
|
||||
stream.writeLine( reinterpret_cast< const U8* >( buffer ) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
bool ScenePolyhedralObject< Base, P >::_setPlane( void* object, const char* index, const char* data )
|
||||
{
|
||||
ScenePolyhedralObject* obj = reinterpret_cast< ScenePolyhedralObject* >( object );
|
||||
|
||||
PlaneF plane;
|
||||
|
||||
dSscanf( data, "%g %g %g %g",
|
||||
&plane.x,
|
||||
&plane.y,
|
||||
&plane.z,
|
||||
&plane.d
|
||||
);
|
||||
|
||||
obj->mPolyhedron.planeList.push_back( plane );
|
||||
obj->setMaskBits( PolyMask );
|
||||
obj->mIsBox = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
bool ScenePolyhedralObject< Base, P >::_setPoint( void* object, const char* index, const char* data )
|
||||
{
|
||||
ScenePolyhedralObject* obj = reinterpret_cast< ScenePolyhedralObject* >( object );
|
||||
|
||||
Point3F point;
|
||||
|
||||
dSscanf( data, "%g %g %g %g",
|
||||
&point[ 0 ],
|
||||
&point[ 1 ],
|
||||
&point[ 2 ]
|
||||
);
|
||||
|
||||
obj->mPolyhedron.pointList.push_back( point );
|
||||
obj->setMaskBits( PolyMask );
|
||||
obj->mIsBox = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename Base, typename P >
|
||||
bool ScenePolyhedralObject< Base, P >::_setEdge( void* object, const char* index, const char* data )
|
||||
{
|
||||
ScenePolyhedralObject* obj = reinterpret_cast< ScenePolyhedralObject* >( object );
|
||||
|
||||
PolyhedronData::Edge edge;
|
||||
|
||||
dSscanf( data, "%i %i %i %i",
|
||||
&edge.face[ 0 ],
|
||||
&edge.face[ 1 ],
|
||||
&edge.vertex[ 0 ],
|
||||
&edge.vertex[ 1 ]
|
||||
);
|
||||
|
||||
obj->mPolyhedron.edgeList.push_back( edge );
|
||||
obj->setMaskBits( PolyMask );
|
||||
obj->mIsBox = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
434
Engine/source/scene/pathManager.cpp
Normal file
434
Engine/source/scene/pathManager.cpp
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "gfx/gfxDevice.h"
|
||||
#include "scene/pathManager.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "scene/simPath.h"
|
||||
#include "interior/interiorInstance.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "platform/profiler.h"
|
||||
#include "core/module.h"
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
U32 countNumBits(U32 n)
|
||||
{
|
||||
U32 count = 0;
|
||||
while (n != 0) {
|
||||
n >>= 1;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count ? count : 1;
|
||||
}
|
||||
|
||||
} // namespace {}
|
||||
|
||||
|
||||
MODULE_BEGIN( PathManager )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
AssertFatal(gClientPathManager == NULL && gServerPathManager == NULL, "Error, already initialized the path manager!");
|
||||
|
||||
gClientPathManager = new PathManager(false);
|
||||
gServerPathManager = new PathManager(true);
|
||||
}
|
||||
|
||||
MODULE_SHUTDOWN
|
||||
{
|
||||
AssertFatal(gClientPathManager != NULL && gServerPathManager != NULL, "Error, path manager not initialized!");
|
||||
|
||||
delete gClientPathManager;
|
||||
gClientPathManager = NULL;
|
||||
delete gServerPathManager;
|
||||
gServerPathManager = NULL;
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-------------------------------------- PathManagerEvent
|
||||
//
|
||||
class PathManagerEvent : public NetEvent
|
||||
{
|
||||
public:
|
||||
U32 modifiedPath;
|
||||
bool clearPaths;
|
||||
PathManager::PathEntry path;
|
||||
|
||||
public:
|
||||
typedef NetEvent Parent;
|
||||
PathManagerEvent() { }
|
||||
|
||||
void pack(NetConnection*, BitStream*);
|
||||
void write(NetConnection*, BitStream*);
|
||||
void unpack(NetConnection*, BitStream*);
|
||||
void process(NetConnection*);
|
||||
|
||||
DECLARE_CONOBJECT(PathManagerEvent);
|
||||
};
|
||||
|
||||
void PathManagerEvent::pack(NetConnection*, BitStream* stream)
|
||||
{
|
||||
// Write out the modified path...
|
||||
stream->write(modifiedPath);
|
||||
stream->writeFlag(clearPaths);
|
||||
stream->write(path.totalTime);
|
||||
stream->write(path.positions.size());
|
||||
|
||||
|
||||
// This is here for safety. You can remove it if you want to try your luck at bigger sizes. -- BJG
|
||||
AssertWarn(path.positions.size() < 1500/40, "Warning! Path size is pretty big - may cause packet overrun!");
|
||||
|
||||
// Each one of these is about 8 floats and 2 ints
|
||||
// so we'll say it's about 40 bytes in size, which is where the 40 in the above calc comes from.
|
||||
for (U32 j = 0; j < path.positions.size(); j++)
|
||||
{
|
||||
mathWrite(*stream, path.positions[j]);
|
||||
mathWrite(*stream, path.rotations[j]);
|
||||
stream->write(path.msToNext[j]);
|
||||
stream->write(path.smoothingType[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void PathManagerEvent::write(NetConnection*nc, BitStream *stream)
|
||||
{
|
||||
pack(nc, stream);
|
||||
}
|
||||
|
||||
void PathManagerEvent::unpack(NetConnection*, BitStream* stream)
|
||||
{
|
||||
// Read in the modified path...
|
||||
|
||||
stream->read(&modifiedPath);
|
||||
clearPaths = stream->readFlag();
|
||||
stream->read(&path.totalTime);
|
||||
|
||||
U32 numPoints;
|
||||
stream->read(&numPoints);
|
||||
path.positions.setSize(numPoints);
|
||||
path.rotations.setSize(numPoints);
|
||||
path.msToNext.setSize(numPoints);
|
||||
path.smoothingType.setSize(numPoints);
|
||||
for (U32 j = 0; j < path.positions.size(); j++)
|
||||
{
|
||||
mathRead(*stream, &path.positions[j]);
|
||||
mathRead(*stream, &path.rotations[j]);
|
||||
stream->read(&path.msToNext[j]);
|
||||
stream->read(&path.smoothingType[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void PathManagerEvent::process(NetConnection*)
|
||||
{
|
||||
if (clearPaths)
|
||||
{
|
||||
// Clear out all the client's paths...
|
||||
gClientPathManager->clearPaths();
|
||||
}
|
||||
AssertFatal(modifiedPath <= gClientPathManager->mPaths.size(), "Error out of bounds path!");
|
||||
if (modifiedPath == gClientPathManager->mPaths.size()) {
|
||||
PathManager::PathEntry *pe = new PathManager::PathEntry;
|
||||
*pe = path;
|
||||
gClientPathManager->mPaths.push_back(pe);
|
||||
}
|
||||
else
|
||||
*(gClientPathManager->mPaths[modifiedPath]) = path;
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETEVENT_V1(PathManagerEvent);
|
||||
|
||||
// Will be internalized once the @internal tag is working
|
||||
ConsoleDocClass( PathManagerEvent,
|
||||
"@brief Class responsible for the registration, transmission, and management "
|
||||
"of paths on client and server.\n\n"
|
||||
|
||||
"For internal use only, not intended for use in TorqueScript or game development\n\n"
|
||||
|
||||
"@internal\n"
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-------------------------------------- PathManager Implementation
|
||||
//
|
||||
PathManager* gClientPathManager = NULL;
|
||||
PathManager* gServerPathManager = NULL;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
PathManager::PathManager(const bool isServer)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mPaths);
|
||||
|
||||
mIsServer = isServer;
|
||||
}
|
||||
|
||||
PathManager::~PathManager()
|
||||
{
|
||||
clearPaths();
|
||||
}
|
||||
|
||||
void PathManager::clearPaths()
|
||||
{
|
||||
for (U32 i = 0; i < mPaths.size(); i++)
|
||||
delete mPaths[i];
|
||||
mPaths.setSize(0);
|
||||
#ifdef TORQUE_DEBUG
|
||||
// This gets rid of the memory used by the vector.
|
||||
// Prevents it from showing up in memory leak logs.
|
||||
mPaths.compact();
|
||||
#endif
|
||||
}
|
||||
|
||||
ConsoleFunction(clearServerPaths, void, 1, 1, "")
|
||||
{
|
||||
gServerPathManager->clearPaths();
|
||||
}
|
||||
|
||||
ConsoleFunction(clearClientPaths, void, 1, 1, "")
|
||||
{
|
||||
gClientPathManager->clearPaths();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
U32 PathManager::allocatePathId()
|
||||
{
|
||||
mPaths.increment();
|
||||
mPaths.last() = new PathEntry;
|
||||
|
||||
return (mPaths.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
void PathManager::updatePath(const U32 id,
|
||||
const Vector<Point3F>& positions,
|
||||
const Vector<QuatF>& rotations,
|
||||
const Vector<U32>& times,
|
||||
const Vector<U32>& smoothingTypes)
|
||||
{
|
||||
AssertFatal(mIsServer == true, "PathManager::updatePath: Error, must be called on the server side");
|
||||
AssertFatal(id < mPaths.size(), "PathManager::updatePath: error, id out of range");
|
||||
AssertFatal(positions.size() == times.size() && positions.size() == smoothingTypes.size(), "Error, times and positions must match!");
|
||||
|
||||
PathEntry& rEntry = *mPaths[id];
|
||||
|
||||
rEntry.positions = positions;
|
||||
rEntry.rotations = rotations;
|
||||
rEntry.msToNext = times;
|
||||
rEntry.smoothingType = smoothingTypes;
|
||||
|
||||
rEntry.totalTime = 0;
|
||||
for (S32 i = 0; i < S32(rEntry.msToNext.size()); i++)
|
||||
rEntry.totalTime += rEntry.msToNext[i];
|
||||
|
||||
transmitPath(id);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void PathManager::transmitPaths(NetConnection* nc)
|
||||
{
|
||||
AssertFatal(mIsServer, "Error, cannot call transmitPaths on client path manager!");
|
||||
|
||||
// Send over paths
|
||||
for(S32 i = 0; i < mPaths.size(); i++)
|
||||
{
|
||||
PathManagerEvent* event = new PathManagerEvent;
|
||||
event->clearPaths = (i == 0);
|
||||
event->modifiedPath = i;
|
||||
event->path = *(mPaths[i]);
|
||||
nc->postNetEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void PathManager::transmitPath(const U32 id)
|
||||
{
|
||||
AssertFatal(mIsServer, "Error, cannot call transmitNewPath on client path manager!");
|
||||
|
||||
// Post to all active clients that have already received their paths...
|
||||
//
|
||||
SimGroup* pClientGroup = Sim::getClientGroup();
|
||||
for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) {
|
||||
NetConnection* nc = dynamic_cast<NetConnection*>(*itr);
|
||||
if (nc && nc->missionPathsSent())
|
||||
{
|
||||
// Transmit the updated path...
|
||||
PathManagerEvent* event = new PathManagerEvent;
|
||||
event->modifiedPath = id;
|
||||
event->clearPaths = false;
|
||||
event->path = *(mPaths[id]);
|
||||
nc->postNetEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PathManager::getPathPosition(const U32 id,
|
||||
const F64 msPosition,
|
||||
Point3F& rPosition,
|
||||
QuatF &rotation)
|
||||
{
|
||||
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
|
||||
PROFILE_START(PathManGetPos);
|
||||
|
||||
// Ok, query holds our path information...
|
||||
F64 ms = msPosition;
|
||||
if (ms > mPaths[id]->totalTime)
|
||||
ms = mPaths[id]->totalTime;
|
||||
|
||||
S32 startNode = 0;
|
||||
while (ms > mPaths[id]->msToNext[startNode]) {
|
||||
ms -= mPaths[id]->msToNext[startNode];
|
||||
startNode++;
|
||||
}
|
||||
S32 endNode = (startNode + 1) % mPaths[id]->positions.size();
|
||||
|
||||
Point3F& rStart = mPaths[id]->positions[startNode];
|
||||
Point3F& rEnd = mPaths[id]->positions[endNode];
|
||||
|
||||
F64 interp = ms / F32(mPaths[id]->msToNext[startNode]);
|
||||
if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeLinear)
|
||||
{
|
||||
rPosition = (rStart * (1.0 - interp)) + (rEnd * interp);
|
||||
}
|
||||
else if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeAccelerate)
|
||||
{
|
||||
interp = mSin(interp * M_PI - (M_PI / 2)) * 0.5 + 0.5;
|
||||
rPosition = (rStart * (1.0 - interp)) + (rEnd * interp);
|
||||
}
|
||||
else if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeSpline)
|
||||
{
|
||||
S32 preStart = startNode - 1;
|
||||
S32 postEnd = endNode + 1;
|
||||
if(postEnd >= mPaths[id]->positions.size())
|
||||
postEnd = 0;
|
||||
if(preStart < 0)
|
||||
preStart = mPaths[id]->positions.size() - 1;
|
||||
Point3F p0 = mPaths[id]->positions[preStart];
|
||||
Point3F p1 = rStart;
|
||||
Point3F p2 = rEnd;
|
||||
Point3F p3 = mPaths[id]->positions[postEnd];
|
||||
rPosition.x = mCatmullrom(interp, p0.x, p1.x, p2.x, p3.x);
|
||||
rPosition.y = mCatmullrom(interp, p0.y, p1.y, p2.y, p3.y);
|
||||
rPosition.z = mCatmullrom(interp, p0.z, p1.z, p2.z, p3.z);
|
||||
}
|
||||
rotation.interpolate( mPaths[id]->rotations[startNode], mPaths[id]->rotations[endNode], interp );
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
U32 PathManager::getPathTotalTime(const U32 id) const
|
||||
{
|
||||
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
|
||||
|
||||
return mPaths[id]->totalTime;
|
||||
}
|
||||
|
||||
U32 PathManager::getPathNumWaypoints(const U32 id) const
|
||||
{
|
||||
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
|
||||
|
||||
return mPaths[id]->positions.size();
|
||||
}
|
||||
|
||||
U32 PathManager::getWaypointTime(const U32 id, const U32 wayPoint) const
|
||||
{
|
||||
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
|
||||
AssertFatal(wayPoint < getPathNumWaypoints(id), "Invalid waypoint!");
|
||||
|
||||
U32 time = 0;
|
||||
for (U32 i = 0; i < wayPoint; i++)
|
||||
time += mPaths[id]->msToNext[i];
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
U32 PathManager::getPathTimeBits(const U32 id)
|
||||
{
|
||||
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
|
||||
|
||||
return countNumBits(mPaths[id]->totalTime);
|
||||
}
|
||||
|
||||
U32 PathManager::getPathWaypointBits(const U32 id)
|
||||
{
|
||||
AssertFatal(isValidPath(id), "Error, this is not a valid path!");
|
||||
|
||||
return countNumBits(mPaths[id]->positions.size());
|
||||
}
|
||||
|
||||
|
||||
bool PathManager::dumpState(BitStream* stream) const
|
||||
{
|
||||
stream->write(mPaths.size());
|
||||
|
||||
for (U32 i = 0; i < mPaths.size(); i++) {
|
||||
const PathEntry& rEntry = *mPaths[i];
|
||||
stream->write(rEntry.totalTime);
|
||||
|
||||
stream->write(rEntry.positions.size());
|
||||
for (U32 j = 0; j < rEntry.positions.size(); j++) {
|
||||
mathWrite(*stream, rEntry.positions[j]);
|
||||
stream->write(rEntry.msToNext[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return stream->getStatus() == Stream::Ok;
|
||||
}
|
||||
|
||||
bool PathManager::readState(BitStream* stream)
|
||||
{
|
||||
U32 i;
|
||||
for (i = 0; i < mPaths.size(); i++)
|
||||
delete mPaths[i];
|
||||
|
||||
U32 numPaths;
|
||||
stream->read(&numPaths);
|
||||
mPaths.setSize(numPaths);
|
||||
|
||||
for (i = 0; i < mPaths.size(); i++) {
|
||||
mPaths[i] = new PathEntry;
|
||||
PathEntry& rEntry = *mPaths[i];
|
||||
|
||||
stream->read(&rEntry.totalTime);
|
||||
|
||||
U32 numPositions;
|
||||
stream->read(&numPositions);
|
||||
rEntry.positions.setSize(numPositions);
|
||||
rEntry.msToNext.setSize(numPositions);
|
||||
for (U32 j = 0; j < rEntry.positions.size(); j++) {
|
||||
mathRead(*stream, &rEntry.positions[j]);
|
||||
stream->read(&rEntry.msToNext[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return stream->getStatus() == Stream::Ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
129
Engine/source/scene/pathManager.h
Normal file
129
Engine/source/scene/pathManager.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _PATHMANAGER_H_
|
||||
#define _PATHMANAGER_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _MPOINT3_H_
|
||||
#include "math/mPoint3.h"
|
||||
#endif
|
||||
#ifndef _MQUAT_H_
|
||||
#include "math/mQuat.h"
|
||||
#endif
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
|
||||
class NetConnection;
|
||||
class BitStream;
|
||||
|
||||
class PathManager
|
||||
{
|
||||
friend class PathManagerEvent;
|
||||
|
||||
private:
|
||||
struct PathEntry {
|
||||
U32 totalTime;
|
||||
|
||||
Vector<Point3F> positions;
|
||||
Vector<QuatF> rotations;
|
||||
Vector<U32> smoothingType;
|
||||
Vector<U32> msToNext;
|
||||
|
||||
PathEntry() {
|
||||
VECTOR_SET_ASSOCIATION(positions);
|
||||
VECTOR_SET_ASSOCIATION(rotations);
|
||||
VECTOR_SET_ASSOCIATION(smoothingType);
|
||||
VECTOR_SET_ASSOCIATION(msToNext);
|
||||
}
|
||||
};
|
||||
|
||||
Vector<PathEntry*> mPaths;
|
||||
|
||||
public:
|
||||
enum PathType {
|
||||
BackAndForth,
|
||||
Looping
|
||||
};
|
||||
|
||||
public:
|
||||
PathManager(const bool isServer);
|
||||
~PathManager();
|
||||
|
||||
void clearPaths();
|
||||
|
||||
//-------------------------------------- Path querying
|
||||
public:
|
||||
bool isValidPath(const U32 id) const;
|
||||
void getPathPosition(const U32 id, const F64 msPosition, Point3F& rPosition, QuatF &rotation);
|
||||
U32 getPathTotalTime(const U32 id) const;
|
||||
U32 getPathNumWaypoints(const U32 id) const;
|
||||
U32 getWaypointTime(const U32 id, const U32 wayPoint) const;
|
||||
|
||||
U32 getPathTimeBits(const U32 id);
|
||||
U32 getPathWaypointBits(const U32 id);
|
||||
|
||||
//-------------------------------------- Path Registration/Transmission/Management
|
||||
public:
|
||||
// Called after mission load to clear out the paths on the client, and to transmit
|
||||
// the information for the current mission's paths.
|
||||
void transmitPaths(NetConnection*);
|
||||
void transmitPath(U32);
|
||||
|
||||
U32 allocatePathId();
|
||||
void updatePath(const U32 id, const Vector<Point3F>&, const Vector<QuatF>&, const Vector<U32> &, const Vector<U32>&);
|
||||
|
||||
//-------------------------------------- State dumping/reading
|
||||
public:
|
||||
bool dumpState(BitStream*) const;
|
||||
bool readState(BitStream*);
|
||||
|
||||
private:
|
||||
bool mIsServer;
|
||||
bool mPathsSent;
|
||||
};
|
||||
|
||||
struct PathNode {
|
||||
Point3F position;
|
||||
QuatF rotation;
|
||||
U32 smoothingType;
|
||||
U32 msToNext;
|
||||
};
|
||||
|
||||
extern PathManager* gClientPathManager;
|
||||
extern PathManager* gServerPathManager;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool PathManager::isValidPath(const U32 id) const
|
||||
{
|
||||
return (id < U32(mPaths.size())) && mPaths[id]->positions.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // _H_PATHMANAGER
|
||||
315
Engine/source/scene/reflectionManager.cpp
Normal file
315
Engine/source/scene/reflectionManager.cpp
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/reflectionManager.h"
|
||||
|
||||
#include "platform/profiler.h"
|
||||
#include "platform/platformTimer.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/tAlgorithm.h"
|
||||
#include "math/mMathFn.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "gui/3d/guiTSControl.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "gfx/gfxDebugEvent.h"
|
||||
#include "gfx/gfxStringEnumTranslate.h"
|
||||
#include "gfx/screenshot.h"
|
||||
#include "core/module.h"
|
||||
#include "scene/reflectionMatHook.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
|
||||
MODULE_BEGIN( ReflectionManager )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
ManagedSingleton< ReflectionManager >::createSingleton();
|
||||
ReflectionManager::initConsole();
|
||||
}
|
||||
|
||||
MODULE_SHUTDOWN
|
||||
{
|
||||
ManagedSingleton< ReflectionManager >::deleteSingleton();
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
|
||||
GFX_ImplementTextureProfile( ReflectRenderTargetProfile,
|
||||
GFXTextureProfile::DiffuseMap,
|
||||
GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap | GFXTextureProfile::RenderTarget | GFXTextureProfile::Pooled,
|
||||
GFXTextureProfile::None );
|
||||
|
||||
GFX_ImplementTextureProfile( RefractTextureProfile,
|
||||
GFXTextureProfile::DiffuseMap,
|
||||
GFXTextureProfile::PreserveSize |
|
||||
GFXTextureProfile::RenderTarget |
|
||||
GFXTextureProfile::Pooled,
|
||||
GFXTextureProfile::None );
|
||||
|
||||
static S32 QSORT_CALLBACK compareReflectors( const void *a, const void *b )
|
||||
{
|
||||
const ReflectorBase *A = *((ReflectorBase**)a);
|
||||
const ReflectorBase *B = *((ReflectorBase**)b);
|
||||
|
||||
F32 dif = B->score - A->score;
|
||||
return (S32)mFloor( dif );
|
||||
}
|
||||
|
||||
|
||||
U32 ReflectionManager::smFrameReflectionMS = 10;
|
||||
F32 ReflectionManager::smRefractTexScale = 0.5f;
|
||||
|
||||
ReflectionManager::ReflectionManager()
|
||||
: mUpdateRefract( true ),
|
||||
mReflectFormat( GFXFormatR8G8B8A8 )
|
||||
{
|
||||
mTimer = PlatformTimer::create();
|
||||
|
||||
GFXDevice::getDeviceEventSignal().notify( this, &ReflectionManager::_handleDeviceEvent );
|
||||
}
|
||||
|
||||
void ReflectionManager::initConsole()
|
||||
{
|
||||
Con::addVariable( "$pref::Reflect::refractTexScale", TypeF32, &ReflectionManager::smRefractTexScale, "RefractTex has dimensions equal to the active render target scaled in both x and y by this float.\n"
|
||||
"@ingroup Rendering");
|
||||
Con::addVariable( "$pref::Reflect::frameLimitMS", TypeS32, &ReflectionManager::smFrameReflectionMS, "ReflectionManager tries not to spend more than this amount of time updating reflections per frame.\n"
|
||||
"@ingroup Rendering");
|
||||
}
|
||||
|
||||
ReflectionManager::~ReflectionManager()
|
||||
{
|
||||
SAFE_DELETE( mTimer );
|
||||
AssertFatal( mReflectors.size() == 0, "ReflectionManager, some reflectors were left nregistered!" );
|
||||
|
||||
GFXDevice::getDeviceEventSignal().remove( this, &ReflectionManager::_handleDeviceEvent );
|
||||
}
|
||||
|
||||
void ReflectionManager::registerReflector( ReflectorBase *reflector )
|
||||
{
|
||||
mReflectors.push_back_unique( reflector );
|
||||
}
|
||||
|
||||
void ReflectionManager::unregisterReflector( ReflectorBase *reflector )
|
||||
{
|
||||
mReflectors.remove( reflector );
|
||||
}
|
||||
|
||||
void ReflectionManager::update( F32 timeSlice,
|
||||
const Point2I &resolution,
|
||||
const CameraQuery &query )
|
||||
{
|
||||
GFXDEBUGEVENT_SCOPE( UpdateReflections, ColorI::WHITE );
|
||||
|
||||
if ( mReflectors.empty() )
|
||||
return;
|
||||
|
||||
PROFILE_SCOPE( ReflectionManager_Update );
|
||||
|
||||
// Calculate our target time from the slice.
|
||||
U32 targetMs = timeSlice * smFrameReflectionMS;
|
||||
|
||||
// Setup a culler for testing the
|
||||
// visibility of reflectors.
|
||||
Frustum culler;
|
||||
culler.set( false,
|
||||
query.fov,
|
||||
(F32)resolution.x / (F32)resolution.y,
|
||||
query.nearPlane,
|
||||
query.farPlane,
|
||||
query.cameraMatrix );
|
||||
|
||||
// Manipulate the frustum for tiled screenshots
|
||||
const bool screenShotMode = gScreenShot && gScreenShot->isPending();
|
||||
if ( screenShotMode )
|
||||
gScreenShot->tileFrustum( culler );
|
||||
|
||||
// We use the frame time and not real time
|
||||
// here as this may be called multiple times
|
||||
// within a frame.
|
||||
U32 startOfUpdateMs = Platform::getVirtualMilliseconds();
|
||||
|
||||
// Save this for interested parties.
|
||||
mLastUpdateMs = startOfUpdateMs;
|
||||
|
||||
ReflectParams refparams;
|
||||
refparams.query = &query;
|
||||
refparams.viewportExtent = resolution;
|
||||
refparams.culler = culler;
|
||||
refparams.startOfUpdateMs = startOfUpdateMs;
|
||||
|
||||
// Update the reflection score.
|
||||
ReflectorList::iterator reflectorIter = mReflectors.begin();
|
||||
for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
|
||||
(*reflectorIter)->calcScore( refparams );
|
||||
|
||||
// Sort them by the score.
|
||||
dQsort( mReflectors.address(), mReflectors.size(), sizeof(ReflectorBase*), compareReflectors );
|
||||
|
||||
// Update as many reflections as we can
|
||||
// within the target time limit.
|
||||
mTimer->getElapsedMs();
|
||||
mTimer->reset();
|
||||
U32 numUpdated = 0;
|
||||
reflectorIter = mReflectors.begin();
|
||||
for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
|
||||
{
|
||||
// We're sorted by score... so once we reach
|
||||
// a zero score we have nothing more to update.
|
||||
if ( (*reflectorIter)->score <= 0.0f && !screenShotMode )
|
||||
break;
|
||||
|
||||
(*reflectorIter)->updateReflection( refparams );
|
||||
(*reflectorIter)->lastUpdateMs = startOfUpdateMs;
|
||||
numUpdated++;
|
||||
|
||||
// If we run out of update time then stop.
|
||||
if ( mTimer->getElapsedMs() > targetMs && !screenShotMode && (*reflectorIter)->score < 1000.0f )
|
||||
break;
|
||||
}
|
||||
|
||||
U32 totalElapsed = mTimer->getElapsedMs();
|
||||
|
||||
// Set metric/debug related script variables...
|
||||
|
||||
U32 numEnabled = mReflectors.size();
|
||||
U32 numVisible = 0;
|
||||
U32 numOccluded = 0;
|
||||
|
||||
reflectorIter = mReflectors.begin();
|
||||
for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
|
||||
{
|
||||
ReflectorBase *pReflector = (*reflectorIter);
|
||||
if ( pReflector->isOccluded() )
|
||||
numOccluded++;
|
||||
else
|
||||
numVisible++;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_GATHER_METRICS
|
||||
const GFXTextureProfileStats &stats = ReflectRenderTargetProfile.getStats();
|
||||
|
||||
F32 mb = ( stats.activeBytes / 1024.0f ) / 1024.0f;
|
||||
char temp[256];
|
||||
|
||||
dSprintf( temp, 256, "%s %d %0.2f\n",
|
||||
ReflectRenderTargetProfile.getName().c_str(),
|
||||
stats.activeCount,
|
||||
mb );
|
||||
|
||||
Con::setVariable( "$Reflect::textureStats", temp );
|
||||
Con::setIntVariable( "$Reflect::renderTargetsAllocated", stats.allocatedTextures );
|
||||
Con::setIntVariable( "$Reflect::poolSize", stats.activeCount );
|
||||
Con::setIntVariable( "$Reflect::numObjects", numEnabled );
|
||||
Con::setIntVariable( "$Reflect::numVisible", numVisible );
|
||||
Con::setIntVariable( "$Reflect::numOccluded", numOccluded );
|
||||
Con::setIntVariable( "$Reflect::numUpdated", numUpdated );
|
||||
Con::setIntVariable( "$Reflect::elapsed", totalElapsed );
|
||||
#endif
|
||||
}
|
||||
|
||||
GFXTexHandle ReflectionManager::allocRenderTarget( const Point2I &size )
|
||||
{
|
||||
return GFXTexHandle( size.x, size.y, mReflectFormat,
|
||||
&ReflectRenderTargetProfile,
|
||||
avar("%s() - mReflectTex (line %d)", __FUNCTION__, __LINE__) );
|
||||
}
|
||||
|
||||
GFXTextureObject* ReflectionManager::getRefractTex()
|
||||
{
|
||||
GFXTarget *target = GFX->getActiveRenderTarget();
|
||||
GFXFormat targetFormat = target->getFormat();
|
||||
const Point2I &targetSize = target->getSize();
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
// On the Xbox360, it needs to do a resolveTo from the active target, so this
|
||||
// may as well be the full size of the active target
|
||||
const U32 desWidth = targetSize.x;
|
||||
const U32 desHeight = targetSize.y;
|
||||
#else
|
||||
const U32 desWidth = mFloor( (F32)targetSize.x * smRefractTexScale );
|
||||
const U32 desHeight = mFloor( ( F32)targetSize.y * smRefractTexScale );
|
||||
#endif
|
||||
|
||||
if ( mRefractTex.isNull() ||
|
||||
mRefractTex->getWidth() != desWidth ||
|
||||
mRefractTex->getHeight() != desHeight ||
|
||||
mRefractTex->getFormat() != targetFormat )
|
||||
{
|
||||
mRefractTex.set( desWidth, desHeight, targetFormat, &RefractTextureProfile, "mRefractTex" );
|
||||
mUpdateRefract = true;
|
||||
}
|
||||
|
||||
if ( mUpdateRefract )
|
||||
{
|
||||
target->resolveTo( mRefractTex );
|
||||
mUpdateRefract = false;
|
||||
}
|
||||
|
||||
return mRefractTex;
|
||||
}
|
||||
|
||||
BaseMatInstance* ReflectionManager::getReflectionMaterial( BaseMatInstance *inMat ) const
|
||||
{
|
||||
// See if we have an existing material hook.
|
||||
ReflectionMaterialHook *hook = static_cast<ReflectionMaterialHook*>( inMat->getHook( ReflectionMaterialHook::Type ) );
|
||||
if ( !hook )
|
||||
{
|
||||
// Create a hook and initialize it using the incoming material.
|
||||
hook = new ReflectionMaterialHook;
|
||||
hook->init( inMat );
|
||||
inMat->addHook( hook );
|
||||
}
|
||||
|
||||
return hook->getReflectMat();
|
||||
}
|
||||
|
||||
bool ReflectionManager::_handleDeviceEvent( GFXDevice::GFXDeviceEventType evt )
|
||||
{
|
||||
switch( evt )
|
||||
{
|
||||
case GFXDevice::deStartOfFrame:
|
||||
|
||||
mUpdateRefract = true;
|
||||
break;
|
||||
|
||||
case GFXDevice::deDestroy:
|
||||
|
||||
mRefractTex = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DefineEngineFunction( setReflectFormat, void, ( GFXFormat format ),,
|
||||
"Set the reflection texture format.\n"
|
||||
"@ingroup GFX\n" )
|
||||
{
|
||||
REFLECTMGR->setReflectFormat( format );
|
||||
}
|
||||
|
||||
154
Engine/source/scene/reflectionManager.h
Normal file
154
Engine/source/scene/reflectionManager.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _REFLECTIONMANAGER_H_
|
||||
#define _REFLECTIONMANAGER_H_
|
||||
|
||||
#ifndef _GFXDEVICE_H_
|
||||
#include "gfx/gfxDevice.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _UTIL_DELEGATE_H_
|
||||
#include "core/util/delegate.h"
|
||||
#endif
|
||||
#ifndef _GFXTEXTUREHANDLE_H_
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
#endif
|
||||
#ifndef _TSINGLETON_H_
|
||||
#include "core/util/tSingleton.h"
|
||||
#endif
|
||||
#ifndef _REFLECTOR_H_
|
||||
#include "scene/reflector.h"
|
||||
#endif
|
||||
|
||||
class PlatformTimer;
|
||||
class BaseMatInstance;
|
||||
|
||||
enum ReflectMode
|
||||
{
|
||||
ReflectNever = 0,
|
||||
ReflectDynamic,
|
||||
ReflectAlways
|
||||
};
|
||||
|
||||
typedef Delegate<bool(bool)> ReflectDelegate;
|
||||
class SceneObject;
|
||||
|
||||
struct Reflector
|
||||
{
|
||||
SceneObject *object;
|
||||
ReflectDelegate updateFn;
|
||||
F32 priority;
|
||||
U32 maxRateMs;
|
||||
F32 maxDist;
|
||||
U32 lastUpdateMs;
|
||||
F32 score;
|
||||
bool updated;
|
||||
bool tried;
|
||||
bool hasTexture;
|
||||
};
|
||||
|
||||
typedef Vector<Reflector> ReflectorVec;
|
||||
|
||||
GFX_DeclareTextureProfile( ReflectRenderTargetProfile );
|
||||
GFX_DeclareTextureProfile( RefractTextureProfile );
|
||||
|
||||
class ReflectionManager
|
||||
{
|
||||
public:
|
||||
|
||||
ReflectionManager();
|
||||
virtual ~ReflectionManager();
|
||||
|
||||
static void initConsole();
|
||||
|
||||
/// Called to change the reflection texture format.
|
||||
void setReflectFormat( GFXFormat format ) { mReflectFormat = format; }
|
||||
|
||||
/// Returns the current reflection format.
|
||||
GFXFormat getReflectFormat() const { return mReflectFormat; }
|
||||
|
||||
/// Doll out callbacks to registered objects based on
|
||||
/// scoring and elapsed time. This should be called
|
||||
/// once for each viewport that renders.
|
||||
void update( F32 timeSlice,
|
||||
const Point2I &resolution,
|
||||
const CameraQuery &query );
|
||||
|
||||
void registerReflector( ReflectorBase *reflector );
|
||||
void unregisterReflector( ReflectorBase *reflector );
|
||||
|
||||
GFXTexHandle allocRenderTarget( const Point2I &size );
|
||||
|
||||
GFXTextureObject* getRefractTex();
|
||||
|
||||
BaseMatInstance* getReflectionMaterial( BaseMatInstance *inMat ) const;
|
||||
|
||||
const U32& getLastUpdateMs() const { return mLastUpdateMs; }
|
||||
|
||||
protected:
|
||||
|
||||
bool _handleDeviceEvent( GFXDevice::GFXDeviceEventType evt );
|
||||
|
||||
protected:
|
||||
|
||||
/// ReflectionManager tries not to spend more than this amount of time
|
||||
/// updating reflections per frame.
|
||||
static U32 smFrameReflectionMS;
|
||||
|
||||
/// RefractTex has dimensions equal to the active render target scaled in
|
||||
/// both x and y by this float.
|
||||
static F32 smRefractTexScale;
|
||||
|
||||
/// A timer used for tracking update time.
|
||||
PlatformTimer *mTimer;
|
||||
|
||||
/// All registered reflections which we handle updating.
|
||||
ReflectorList mReflectors;
|
||||
|
||||
/// Refraction texture copied from the backbuffer once per frame that
|
||||
/// gets used by all WaterObjects.
|
||||
GFXTexHandle mRefractTex;
|
||||
|
||||
/// The texture format to use for reflection and
|
||||
/// refraction texture sources.
|
||||
GFXFormat mReflectFormat;
|
||||
|
||||
/// Set when the refraction texture is dirty
|
||||
/// and requires an update.
|
||||
bool mUpdateRefract;
|
||||
|
||||
/// Platform time in milliseconds of the last update.
|
||||
U32 mLastUpdateMs;
|
||||
|
||||
public:
|
||||
// For ManagedSingleton.
|
||||
static const char* getSingletonName() { return "ReflectionManager"; }
|
||||
};
|
||||
|
||||
|
||||
/// Returns the ReflectionManager singleton.
|
||||
#define REFLECTMGR ManagedSingleton<ReflectionManager>::instance()
|
||||
|
||||
#endif // _REFLECTIONMANAGER_H_
|
||||
108
Engine/source/scene/reflectionMatHook.cpp
Normal file
108
Engine/source/scene/reflectionMatHook.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/reflectionMatHook.h"
|
||||
|
||||
#include "materials/materialManager.h"
|
||||
#include "materials/customMaterialDefinition.h"
|
||||
#include "materials/materialFeatureTypes.h"
|
||||
#include "materials/materialFeatureData.h"
|
||||
#include "shaderGen/featureType.h"
|
||||
#include "shaderGen/featureMgr.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
|
||||
|
||||
const MatInstanceHookType ReflectionMaterialHook::Type( "Reflection" );
|
||||
|
||||
ReflectionMaterialHook::ReflectionMaterialHook() :
|
||||
mReflectMat(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ReflectionMaterialHook::~ReflectionMaterialHook()
|
||||
{
|
||||
SAFE_DELETE(mReflectMat);
|
||||
}
|
||||
|
||||
void ReflectionMaterialHook::init( BaseMatInstance *inMat )
|
||||
{
|
||||
if( !inMat->isValid() )
|
||||
return;
|
||||
|
||||
Material *reflectMat = (Material*)inMat->getMaterial();
|
||||
if ( inMat->isCustomMaterial() )
|
||||
{
|
||||
// This is a custom material... who knows what it really does...do something
|
||||
// smart here later.
|
||||
}
|
||||
|
||||
// We may want to disable some states that the material might enable for us.
|
||||
GFXStateBlockDesc refractState = inMat->getUserStateBlock();
|
||||
|
||||
// Always z-read, and z-write if the material isn't translucent
|
||||
refractState.setZReadWrite( true, reflectMat->isTranslucent() ? false : true );
|
||||
|
||||
// Create reflection material instance.
|
||||
BaseMatInstance *newMat = new ReflectionMatInstance( reflectMat );
|
||||
newMat->setUserObject( inMat->getUserObject() );
|
||||
newMat->getFeaturesDelegate().bind( &ReflectionMaterialHook::_overrideFeatures );
|
||||
newMat->addStateBlockDesc( refractState );
|
||||
if( !newMat->init( inMat->getFeatures(), inMat->getVertexFormat() ) )
|
||||
{
|
||||
SAFE_DELETE( newMat );
|
||||
newMat = MATMGR->createWarningMatInstance();
|
||||
}
|
||||
|
||||
mReflectMat = newMat;
|
||||
}
|
||||
|
||||
void ReflectionMaterialHook::_overrideFeatures( ProcessedMaterial *mat,
|
||||
U32 stageNum,
|
||||
MaterialFeatureData &fd,
|
||||
const FeatureSet &features )
|
||||
{
|
||||
// First stage only in reflections
|
||||
if( stageNum != 0 )
|
||||
{
|
||||
fd.features.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Forward shading on materials in reflections
|
||||
fd.features.addFeature( MFT_ForwardShading );
|
||||
fd.features.addFeature( MFT_Fog );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ReflectionMatInstance::ReflectionMatInstance( Material *mat )
|
||||
: MatInstance( *mat )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ReflectionMatInstance::setupPass( SceneRenderState *state, const SceneData &sgData )
|
||||
{
|
||||
return Parent::setupPass(state, sgData);
|
||||
}
|
||||
74
Engine/source/scene/reflectionMatHook.h
Normal file
74
Engine/source/scene/reflectionMatHook.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _REFLECTIONMATHOOK_H_
|
||||
#define _REFLECTIONMATHOOK_H_
|
||||
|
||||
#ifndef _MATINSTANCEHOOK_H_
|
||||
#include "materials/matInstanceHook.h"
|
||||
#endif
|
||||
#ifndef _MATINSTANCE_H_
|
||||
#include "materials/matInstance.h"
|
||||
#endif
|
||||
|
||||
class ReflectionMatInstance : public MatInstance
|
||||
{
|
||||
typedef MatInstance Parent;
|
||||
|
||||
public:
|
||||
ReflectionMatInstance( Material *mat );
|
||||
virtual ~ReflectionMatInstance() {}
|
||||
|
||||
virtual bool setupPass( SceneRenderState *state, const SceneData &sgData );
|
||||
};
|
||||
|
||||
class ReflectionMaterialHook : public MatInstanceHook
|
||||
{
|
||||
public:
|
||||
|
||||
ReflectionMaterialHook();
|
||||
|
||||
// MatInstanceHook
|
||||
virtual ~ReflectionMaterialHook();
|
||||
virtual const MatInstanceHookType& getType() const { return Type; }
|
||||
|
||||
/// The material hook type.
|
||||
static const MatInstanceHookType Type;
|
||||
|
||||
BaseMatInstance* getReflectMat() const { return mReflectMat; }
|
||||
|
||||
void init( BaseMatInstance *mat );
|
||||
|
||||
protected:
|
||||
|
||||
static void _overrideFeatures( ProcessedMaterial *mat,
|
||||
U32 stageNum,
|
||||
MaterialFeatureData &fd,
|
||||
const FeatureSet &features );
|
||||
|
||||
///
|
||||
BaseMatInstance* mReflectMat;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // _SHADOWMATHOOK_H_
|
||||
743
Engine/source/scene/reflector.cpp
Normal file
743
Engine/source/scene/reflector.cpp
Normal file
|
|
@ -0,0 +1,743 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/reflector.h"
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "gfx/gfxCubemap.h"
|
||||
#include "gfx/gfxDebugEvent.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "scene/reflectionManager.h"
|
||||
#include "gui/3d/guiTSControl.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "gfx/gfxOcclusionQuery.h"
|
||||
#include "lighting/lightManager.h"
|
||||
#include "lighting/shadowMap/lightShadowMap.h"
|
||||
#include "math/mathUtils.h"
|
||||
#include "math/util/frustum.h"
|
||||
#include "gfx/screenshot.h"
|
||||
|
||||
extern ColorI gCanvasClearColor;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ReflectorDesc
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1( ReflectorDesc );
|
||||
|
||||
ConsoleDocClass( ReflectorDesc,
|
||||
"@brief A datablock which defines performance and quality properties for "
|
||||
"dynamic reflections.\n\n"
|
||||
|
||||
"ReflectorDesc is not itself a reflection and does not render reflections. "
|
||||
"It is a dummy class for holding and exposing to the user a set of "
|
||||
"reflection related properties. Objects which support dynamic reflections "
|
||||
"may then reference a ReflectorDesc.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"datablock ReflectorDesc( ExampleReflectorDesc )\n"
|
||||
"{\n"
|
||||
" texSize = 256;\n"
|
||||
" nearDist = 0.1;\n"
|
||||
" farDist = 500;\n"
|
||||
" objectTypeMask = 0xFFFFFFFF;\n"
|
||||
" detailAdjust = 1.0;\n"
|
||||
" priority = 1.0;\n"
|
||||
" maxRateMs = 0;\n"
|
||||
" useOcclusionQuery = true;\n"
|
||||
"};\n"
|
||||
"@endtsexample\n"
|
||||
|
||||
"@see ShapeBaseData::cubeReflectorDesc\n"
|
||||
"@ingroup enviroMisc"
|
||||
);
|
||||
|
||||
ReflectorDesc::ReflectorDesc()
|
||||
{
|
||||
texSize = 256;
|
||||
nearDist = 0.1f;
|
||||
farDist = 1000.0f;
|
||||
objectTypeMask = 0xFFFFFFFF;
|
||||
detailAdjust = 1.0f;
|
||||
priority = 1.0f;
|
||||
maxRateMs = 15;
|
||||
useOcclusionQuery = true;
|
||||
}
|
||||
|
||||
ReflectorDesc::~ReflectorDesc()
|
||||
{
|
||||
}
|
||||
|
||||
void ReflectorDesc::initPersistFields()
|
||||
{
|
||||
addGroup( "ReflectorDesc" );
|
||||
|
||||
addField( "texSize", TypeS32, Offset( texSize, ReflectorDesc ),
|
||||
"Size in pixels of the (square) reflection texture. For a cubemap "
|
||||
"this value is interpreted as size of each face." );
|
||||
|
||||
addField( "nearDist", TypeF32, Offset( nearDist, ReflectorDesc ),
|
||||
"Near plane distance to use when rendering this reflection. Adjust "
|
||||
"this to limit self-occlusion artifacts." );
|
||||
|
||||
addField( "farDist", TypeF32, Offset( farDist, ReflectorDesc ),
|
||||
"Far plane distance to use when rendering reflections." );
|
||||
|
||||
addField( "objectTypeMask", TypeS32, Offset( objectTypeMask, ReflectorDesc ),
|
||||
"Object types which render into this reflection." );
|
||||
|
||||
addField( "detailAdjust", TypeF32, Offset( detailAdjust, ReflectorDesc ),
|
||||
"Scale applied to lod calculation of objects rendering into "
|
||||
"this reflection ( modulates $pref::TS::detailAdjust )." );
|
||||
|
||||
addField( "priority", TypeF32, Offset( priority, ReflectorDesc ),
|
||||
"Priority for updating this reflection, relative to others." );
|
||||
|
||||
addField( "maxRateMs", TypeS32, Offset( maxRateMs, ReflectorDesc ),
|
||||
"If less than maxRateMs has elapsed since this relfection was last "
|
||||
"updated, then do not update it again. This 'skip' can be disabled by "
|
||||
"setting maxRateMs to zero." );
|
||||
|
||||
addField( "useOcclusionQuery", TypeBool, Offset( useOcclusionQuery, ReflectorDesc ),
|
||||
"If available on the device use HOQs to determine if the reflective object "
|
||||
"is visible before updating its reflection." );
|
||||
|
||||
endGroup( "ReflectorDesc" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void ReflectorDesc::packData( BitStream *stream )
|
||||
{
|
||||
Parent::packData( stream );
|
||||
|
||||
stream->write( texSize );
|
||||
stream->write( nearDist );
|
||||
stream->write( farDist );
|
||||
stream->write( objectTypeMask );
|
||||
stream->write( detailAdjust );
|
||||
stream->write( priority );
|
||||
stream->write( maxRateMs );
|
||||
stream->writeFlag( useOcclusionQuery );
|
||||
}
|
||||
|
||||
void ReflectorDesc::unpackData( BitStream *stream )
|
||||
{
|
||||
Parent::unpackData( stream );
|
||||
|
||||
stream->read( &texSize );
|
||||
stream->read( &nearDist );
|
||||
stream->read( &farDist );
|
||||
stream->read( &objectTypeMask );
|
||||
stream->read( &detailAdjust );
|
||||
stream->read( &priority );
|
||||
stream->read( &maxRateMs );
|
||||
useOcclusionQuery = stream->readFlag();
|
||||
}
|
||||
|
||||
bool ReflectorDesc::preload( bool server, String &errorStr )
|
||||
{
|
||||
if ( !Parent::preload( server, errorStr ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ReflectorBase
|
||||
//-------------------------------------------------------------------------
|
||||
ReflectorBase::ReflectorBase()
|
||||
{
|
||||
mEnabled = false;
|
||||
mOccluded = false;
|
||||
mIsRendering = false;
|
||||
mDesc = NULL;
|
||||
mObject = NULL;
|
||||
mOcclusionQuery = GFX->createOcclusionQuery();
|
||||
mQueryPending = false;
|
||||
}
|
||||
|
||||
ReflectorBase::~ReflectorBase()
|
||||
{
|
||||
delete mOcclusionQuery;
|
||||
}
|
||||
|
||||
void ReflectorBase::unregisterReflector()
|
||||
{
|
||||
if ( mEnabled )
|
||||
{
|
||||
REFLECTMGR->unregisterReflector( this );
|
||||
mEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
F32 ReflectorBase::calcScore( const ReflectParams ¶ms )
|
||||
{
|
||||
PROFILE_SCOPE( ReflectorBase_calcScore );
|
||||
|
||||
// First check the occlusion query to see if we're hidden.
|
||||
if ( mDesc->useOcclusionQuery &&
|
||||
mOcclusionQuery )
|
||||
{
|
||||
GFXOcclusionQuery::OcclusionQueryStatus status = mOcclusionQuery->getStatus( false );
|
||||
|
||||
if ( status == GFXOcclusionQuery::Waiting )
|
||||
{
|
||||
mQueryPending = true;
|
||||
// Don't change mOccluded since we don't know yet, use the value
|
||||
// from last frame.
|
||||
}
|
||||
else
|
||||
{
|
||||
mQueryPending = false;
|
||||
|
||||
if ( status == GFXOcclusionQuery::Occluded )
|
||||
mOccluded = true;
|
||||
else if ( status == GFXOcclusionQuery::NotOccluded )
|
||||
mOccluded = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're disabled for any reason then there
|
||||
// is nothing more left to do.
|
||||
if ( !mEnabled ||
|
||||
mOccluded ||
|
||||
params.culler.isCulled( mObject->getWorldBox() ) )
|
||||
{
|
||||
score = 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
// This mess is calculating a score based on LOD.
|
||||
|
||||
/*
|
||||
F32 sizeWS = getMax( object->getWorldBox().len_z(), 0.001f );
|
||||
Point3F cameraOffset = params.culler.getPosition() - object->getPosition();
|
||||
F32 dist = getMax( cameraOffset.len(), 0.01f );
|
||||
F32 worldToScreenScaleY = ( params.culler.getNearDist() * params.viewportExtent.y ) /
|
||||
( params.culler.getNearTop() - params.culler.getNearBottom() );
|
||||
F32 sizeSS = sizeWS / dist * worldToScreenScaleY;
|
||||
*/
|
||||
|
||||
if ( mDesc->priority == -1.0f )
|
||||
{
|
||||
score = 1000.0f;
|
||||
return score;
|
||||
}
|
||||
|
||||
F32 lodFactor = 1.0f; //sizeSS;
|
||||
|
||||
F32 maxRate = getMax( (F32)mDesc->maxRateMs, 1.0f );
|
||||
U32 delta = params.startOfUpdateMs - lastUpdateMs;
|
||||
F32 timeFactor = getMax( (F32)delta / maxRate - 1.0f, 0.0f );
|
||||
|
||||
score = mDesc->priority * timeFactor * lodFactor;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// CubeReflector
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
CubeReflector::CubeReflector()
|
||||
: mLastTexSize( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
void CubeReflector::registerReflector( SceneObject *object,
|
||||
ReflectorDesc *desc )
|
||||
{
|
||||
if ( mEnabled )
|
||||
return;
|
||||
|
||||
mEnabled = true;
|
||||
mObject = object;
|
||||
mDesc = desc;
|
||||
REFLECTMGR->registerReflector( this );
|
||||
}
|
||||
|
||||
void CubeReflector::unregisterReflector()
|
||||
{
|
||||
if ( !mEnabled )
|
||||
return;
|
||||
|
||||
REFLECTMGR->unregisterReflector( this );
|
||||
|
||||
mEnabled = false;
|
||||
}
|
||||
|
||||
void CubeReflector::updateReflection( const ReflectParams ¶ms )
|
||||
{
|
||||
GFXDEBUGEVENT_SCOPE( CubeReflector_UpdateReflection, ColorI::WHITE );
|
||||
|
||||
mIsRendering = true;
|
||||
|
||||
// Setup textures and targets...
|
||||
S32 texDim = mDesc->texSize;
|
||||
texDim = getMax( texDim, 32 );
|
||||
|
||||
// Protect against the reflection texture being bigger
|
||||
// than the current game back buffer.
|
||||
texDim = getMin( texDim, params.viewportExtent.x );
|
||||
texDim = getMin( texDim, params.viewportExtent.y );
|
||||
|
||||
bool texResize = ( texDim != mLastTexSize );
|
||||
|
||||
const GFXFormat reflectFormat = REFLECTMGR->getReflectFormat();
|
||||
|
||||
if ( texResize ||
|
||||
cubemap.isNull() ||
|
||||
cubemap->getFormat() != reflectFormat )
|
||||
{
|
||||
cubemap = GFX->createCubemap();
|
||||
cubemap->initDynamic( texDim, reflectFormat );
|
||||
}
|
||||
|
||||
GFXTexHandle depthBuff = LightShadowMap::_getDepthTarget( texDim, texDim );
|
||||
|
||||
if ( renderTarget.isNull() )
|
||||
renderTarget = GFX->allocRenderToTextureTarget();
|
||||
|
||||
GFX->pushActiveRenderTarget();
|
||||
renderTarget->attachTexture( GFXTextureTarget::DepthStencil, depthBuff );
|
||||
|
||||
|
||||
F32 oldVisibleDist = gClientSceneGraph->getVisibleDistance();
|
||||
gClientSceneGraph->setVisibleDistance( mDesc->farDist );
|
||||
|
||||
|
||||
for ( U32 i = 0; i < 6; i++ )
|
||||
updateFace( params, i );
|
||||
|
||||
|
||||
GFX->popActiveRenderTarget();
|
||||
|
||||
gClientSceneGraph->setVisibleDistance(oldVisibleDist);
|
||||
|
||||
mIsRendering = false;
|
||||
mLastTexSize = texDim;
|
||||
}
|
||||
|
||||
void CubeReflector::updateFace( const ReflectParams ¶ms, U32 faceidx )
|
||||
{
|
||||
GFXDEBUGEVENT_SCOPE( CubeReflector_UpdateFace, ColorI::WHITE );
|
||||
|
||||
// store current matrices
|
||||
GFXTransformSaver saver;
|
||||
|
||||
// set projection to 90 degrees vertical and horizontal
|
||||
F32 left, right, top, bottom;
|
||||
MathUtils::makeFrustum( &left, &right, &top, &bottom, M_HALFPI_F, 1.0f, mDesc->nearDist );
|
||||
GFX->setFrustum( left, right, bottom, top, mDesc->nearDist, mDesc->farDist );
|
||||
|
||||
// We don't use a special clipping projection, but still need to initialize
|
||||
// this for objects like SkyBox which will use it during a reflect pass.
|
||||
gClientSceneGraph->setNonClipProjection( GFX->getProjectionMatrix() );
|
||||
|
||||
// Standard view that will be overridden below.
|
||||
VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
|
||||
|
||||
switch( faceidx )
|
||||
{
|
||||
case 0 : // D3DCUBEMAP_FACE_POSITIVE_X:
|
||||
vLookatPt = VectorF( 1.0f, 0.0f, 0.0f );
|
||||
vUpVec = VectorF( 0.0f, 1.0f, 0.0f );
|
||||
break;
|
||||
case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X:
|
||||
vLookatPt = VectorF( -1.0f, 0.0f, 0.0f );
|
||||
vUpVec = VectorF( 0.0f, 1.0f, 0.0f );
|
||||
break;
|
||||
case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y:
|
||||
vLookatPt = VectorF( 0.0f, 1.0f, 0.0f );
|
||||
vUpVec = VectorF( 0.0f, 0.0f,-1.0f );
|
||||
break;
|
||||
case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y:
|
||||
vLookatPt = VectorF( 0.0f, -1.0f, 0.0f );
|
||||
vUpVec = VectorF( 0.0f, 0.0f, 1.0f );
|
||||
break;
|
||||
case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z:
|
||||
vLookatPt = VectorF( 0.0f, 0.0f, 1.0f );
|
||||
vUpVec = VectorF( 0.0f, 1.0f, 0.0f );
|
||||
break;
|
||||
case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
|
||||
vLookatPt = VectorF( 0.0f, 0.0f, -1.0f );
|
||||
vUpVec = VectorF( 0.0f, 1.0f, 0.0f );
|
||||
break;
|
||||
}
|
||||
|
||||
// create camera matrix
|
||||
VectorF cross = mCross( vUpVec, vLookatPt );
|
||||
cross.normalizeSafe();
|
||||
|
||||
MatrixF matView(true);
|
||||
matView.setColumn( 0, cross );
|
||||
matView.setColumn( 1, vLookatPt );
|
||||
matView.setColumn( 2, vUpVec );
|
||||
matView.setPosition( mObject->getPosition() );
|
||||
matView.inverse();
|
||||
|
||||
GFX->setWorldMatrix(matView);
|
||||
|
||||
renderTarget->attachTexture( GFXTextureTarget::Color0, cubemap, faceidx );
|
||||
GFX->setActiveRenderTarget( renderTarget );
|
||||
GFX->clear( GFXClearStencil | GFXClearTarget | GFXClearZBuffer, gCanvasClearColor, 1.0f, 0 );
|
||||
|
||||
SceneRenderState reflectRenderState
|
||||
(
|
||||
gClientSceneGraph,
|
||||
SPT_Reflect,
|
||||
SceneCameraState::fromGFX()
|
||||
);
|
||||
|
||||
reflectRenderState.getMaterialDelegate().bind( REFLECTMGR, &ReflectionManager::getReflectionMaterial );
|
||||
reflectRenderState.setDiffuseCameraTransform( params.query->cameraMatrix );
|
||||
reflectRenderState.disableAdvancedLightingBins(true);
|
||||
|
||||
// render scene
|
||||
LIGHTMGR->registerGlobalLights( &reflectRenderState.getFrustum(), false );
|
||||
gClientSceneGraph->renderSceneNoLights( &reflectRenderState, mDesc->objectTypeMask );
|
||||
LIGHTMGR->unregisterAllLights();
|
||||
|
||||
// Clean up.
|
||||
renderTarget->resolve();
|
||||
}
|
||||
|
||||
F32 CubeReflector::calcFaceScore( const ReflectParams ¶ms, U32 faceidx )
|
||||
{
|
||||
if ( Parent::calcScore( params ) <= 0.0f )
|
||||
return score;
|
||||
|
||||
VectorF vLookatPt(0.0f, 0.0f, 0.0f);
|
||||
|
||||
switch( faceidx )
|
||||
{
|
||||
case 0 : // D3DCUBEMAP_FACE_POSITIVE_X:
|
||||
vLookatPt = VectorF( 1.0f, 0.0f, 0.0f );
|
||||
break;
|
||||
case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X:
|
||||
vLookatPt = VectorF( -1.0f, 0.0f, 0.0f );
|
||||
break;
|
||||
case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y:
|
||||
vLookatPt = VectorF( 0.0f, 1.0f, 0.0f );
|
||||
break;
|
||||
case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y:
|
||||
vLookatPt = VectorF( 0.0f, -1.0f, 0.0f );
|
||||
break;
|
||||
case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z:
|
||||
vLookatPt = VectorF( 0.0f, 0.0f, 1.0f );
|
||||
break;
|
||||
case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
|
||||
vLookatPt = VectorF( 0.0f, 0.0f, -1.0f );
|
||||
break;
|
||||
}
|
||||
|
||||
VectorF cameraDir;
|
||||
params.query->cameraMatrix.getColumn( 1, &cameraDir );
|
||||
|
||||
F32 dot = mDot( cameraDir, -vLookatPt );
|
||||
|
||||
dot = getMax( ( dot + 1.0f ) / 2.0f, 0.1f );
|
||||
|
||||
score *= dot;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
F32 CubeReflector::CubeFaceReflector::calcScore( const ReflectParams ¶ms )
|
||||
{
|
||||
score = cube->calcFaceScore( params, faceIdx );
|
||||
mOccluded = cube->isOccluded();
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// PlaneReflector
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void PlaneReflector::registerReflector( SceneObject *object,
|
||||
ReflectorDesc *desc )
|
||||
{
|
||||
mEnabled = true;
|
||||
mObject = object;
|
||||
mDesc = desc;
|
||||
mLastDir = Point3F::One;
|
||||
mLastPos = Point3F::Max;
|
||||
|
||||
REFLECTMGR->registerReflector( this );
|
||||
}
|
||||
|
||||
F32 PlaneReflector::calcScore( const ReflectParams ¶ms )
|
||||
{
|
||||
if ( Parent::calcScore( params ) <= 0.0f || score >= 1000.0f )
|
||||
return score;
|
||||
|
||||
// The planar reflection is view dependent to score it
|
||||
// higher if the view direction and/or position has changed.
|
||||
|
||||
// Get the current camera info.
|
||||
VectorF camDir = params.query->cameraMatrix.getForwardVector();
|
||||
Point3F camPos = params.query->cameraMatrix.getPosition();
|
||||
|
||||
// Scale up the score based on the view direction change.
|
||||
F32 dot = mDot( camDir, mLastDir );
|
||||
dot = ( 1.0f - dot ) * 1000.0f;
|
||||
score += dot * mDesc->priority;
|
||||
|
||||
// Also account for the camera movement.
|
||||
score += ( camPos - mLastPos ).lenSquared() * mDesc->priority;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
void PlaneReflector::updateReflection( const ReflectParams ¶ms )
|
||||
{
|
||||
PROFILE_SCOPE(PlaneReflector_updateReflection);
|
||||
GFXDEBUGEVENT_SCOPE( PlaneReflector_updateReflection, ColorI::WHITE );
|
||||
|
||||
mIsRendering = true;
|
||||
|
||||
S32 texDim = mDesc->texSize;
|
||||
texDim = getMax( texDim, 32 );
|
||||
|
||||
// Protect against the reflection texture being bigger
|
||||
// than the current game back buffer.
|
||||
texDim = getMin( texDim, params.viewportExtent.x );
|
||||
texDim = getMin( texDim, params.viewportExtent.y );
|
||||
|
||||
bool texResize = ( texDim != mLastTexSize );
|
||||
mLastTexSize = texDim;
|
||||
|
||||
const Point2I texSize( texDim, texDim );
|
||||
|
||||
if ( texResize ||
|
||||
reflectTex.isNull() ||
|
||||
reflectTex->getFormat() != REFLECTMGR->getReflectFormat() )
|
||||
reflectTex = REFLECTMGR->allocRenderTarget( texSize );
|
||||
|
||||
GFXTexHandle depthBuff = LightShadowMap::_getDepthTarget( texSize.x, texSize.y );
|
||||
|
||||
// store current matrices
|
||||
GFXTransformSaver saver;
|
||||
|
||||
F32 aspectRatio = F32( params.viewportExtent.x ) / F32( params.viewportExtent.y );
|
||||
|
||||
Frustum frustum;
|
||||
frustum.set(false, params.query->fov, aspectRatio, params.query->nearPlane, params.query->farPlane);
|
||||
|
||||
// Manipulate the frustum for tiled screenshots
|
||||
const bool screenShotMode = gScreenShot && gScreenShot->isPending();
|
||||
if ( screenShotMode )
|
||||
gScreenShot->tileFrustum( frustum );
|
||||
|
||||
GFX->setFrustum( frustum );
|
||||
|
||||
// Store the last view info for scoring.
|
||||
mLastDir = params.query->cameraMatrix.getForwardVector();
|
||||
mLastPos = params.query->cameraMatrix.getPosition();
|
||||
|
||||
if ( objectSpace )
|
||||
{
|
||||
// set up camera transform relative to object
|
||||
MatrixF invObjTrans = mObject->getRenderTransform();
|
||||
invObjTrans.inverse();
|
||||
MatrixF relCamTrans = invObjTrans * params.query->cameraMatrix;
|
||||
|
||||
MatrixF camReflectTrans = getCameraReflection( relCamTrans );
|
||||
MatrixF camTrans = mObject->getRenderTransform() * camReflectTrans;
|
||||
camTrans.inverse();
|
||||
|
||||
GFX->setWorldMatrix( camTrans );
|
||||
|
||||
// use relative reflect transform for modelview since clip plane is in object space
|
||||
camTrans = camReflectTrans;
|
||||
camTrans.inverse();
|
||||
|
||||
// set new projection matrix
|
||||
gClientSceneGraph->setNonClipProjection( (MatrixF&) GFX->getProjectionMatrix() );
|
||||
MatrixF clipProj = getFrustumClipProj( camTrans );
|
||||
GFX->setProjectionMatrix( clipProj );
|
||||
}
|
||||
else
|
||||
{
|
||||
MatrixF camTrans = params.query->cameraMatrix;
|
||||
|
||||
// set world mat from new camera view
|
||||
MatrixF camReflectTrans = getCameraReflection( camTrans );
|
||||
camReflectTrans.inverse();
|
||||
GFX->setWorldMatrix( camReflectTrans );
|
||||
|
||||
// set new projection matrix
|
||||
gClientSceneGraph->setNonClipProjection( (MatrixF&) GFX->getProjectionMatrix() );
|
||||
MatrixF clipProj = getFrustumClipProj( camReflectTrans );
|
||||
GFX->setProjectionMatrix( clipProj );
|
||||
}
|
||||
|
||||
// Adjust the detail amount
|
||||
F32 detailAdjustBackup = TSShapeInstance::smDetailAdjust;
|
||||
TSShapeInstance::smDetailAdjust *= mDesc->detailAdjust;
|
||||
|
||||
|
||||
if(reflectTarget.isNull())
|
||||
reflectTarget = GFX->allocRenderToTextureTarget();
|
||||
reflectTarget->attachTexture( GFXTextureTarget::Color0, reflectTex );
|
||||
reflectTarget->attachTexture( GFXTextureTarget::DepthStencil, depthBuff );
|
||||
GFX->pushActiveRenderTarget();
|
||||
GFX->setActiveRenderTarget( reflectTarget );
|
||||
|
||||
SceneRenderState reflectRenderState
|
||||
(
|
||||
gClientSceneGraph,
|
||||
SPT_Reflect,
|
||||
SceneCameraState::fromGFX()
|
||||
);
|
||||
|
||||
reflectRenderState.getMaterialDelegate().bind( REFLECTMGR, &ReflectionManager::getReflectionMaterial );
|
||||
reflectRenderState.setDiffuseCameraTransform( params.query->cameraMatrix );
|
||||
reflectRenderState.disableAdvancedLightingBins(true);
|
||||
|
||||
U32 objTypeFlag = -1;
|
||||
LIGHTMGR->registerGlobalLights( &reflectRenderState.getFrustum(), false );
|
||||
|
||||
// Since we can sometime be rendering a reflection for 1 or 2 frames before
|
||||
// it gets updated do to the lag associated with getting the results from
|
||||
// a HOQ we can sometimes see into parts of the reflection texture that
|
||||
// have nothing but clear color ( eg. under the water ).
|
||||
// To make this look less crappy use the ambient color of the sun.
|
||||
//
|
||||
// In the future we may want to fix this instead by having the scatterSky
|
||||
// render a skirt or something in its lower half.
|
||||
//
|
||||
ColorF clearColor = reflectRenderState.getAmbientLightColor();
|
||||
GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, clearColor, 1.0f, 0 );
|
||||
|
||||
gClientSceneGraph->renderSceneNoLights( &reflectRenderState, objTypeFlag );
|
||||
|
||||
LIGHTMGR->unregisterAllLights();
|
||||
|
||||
// Clean up.
|
||||
reflectTarget->resolve();
|
||||
GFX->popActiveRenderTarget();
|
||||
|
||||
// Restore detail adjust amount.
|
||||
TSShapeInstance::smDetailAdjust = detailAdjustBackup;
|
||||
|
||||
mIsRendering = false;
|
||||
}
|
||||
|
||||
MatrixF PlaneReflector::getCameraReflection( MatrixF &camTrans )
|
||||
{
|
||||
Point3F normal = refplane;
|
||||
|
||||
// Figure out new cam position
|
||||
Point3F camPos = camTrans.getPosition();
|
||||
F32 dist = refplane.distToPlane( camPos );
|
||||
Point3F newCamPos = camPos - normal * dist * 2.0;
|
||||
|
||||
// Figure out new look direction
|
||||
Point3F i, j, k;
|
||||
camTrans.getColumn( 0, &i );
|
||||
camTrans.getColumn( 1, &j );
|
||||
camTrans.getColumn( 2, &k );
|
||||
|
||||
i = MathUtils::reflect( i, normal );
|
||||
j = MathUtils::reflect( j, normal );
|
||||
k = MathUtils::reflect( k, normal );
|
||||
//mCross( i, j, &k );
|
||||
|
||||
|
||||
MatrixF newTrans(true);
|
||||
newTrans.setColumn( 0, i );
|
||||
newTrans.setColumn( 1, j );
|
||||
newTrans.setColumn( 2, k );
|
||||
|
||||
newTrans.setPosition( newCamPos );
|
||||
|
||||
return newTrans;
|
||||
}
|
||||
|
||||
inline float sgn(float a)
|
||||
{
|
||||
if (a > 0.0F) return (1.0F);
|
||||
if (a < 0.0F) return (-1.0F);
|
||||
return (0.0F);
|
||||
}
|
||||
|
||||
MatrixF PlaneReflector::getFrustumClipProj( MatrixF &modelview )
|
||||
{
|
||||
static MatrixF rotMat(EulerF( static_cast<F32>(M_PI / 2.f), 0.0, 0.0));
|
||||
static MatrixF invRotMat(EulerF( -static_cast<F32>(M_PI / 2.f), 0.0, 0.0));
|
||||
|
||||
|
||||
MatrixF revModelview = modelview;
|
||||
revModelview = rotMat * revModelview; // add rotation to modelview because it needs to be removed from projection
|
||||
|
||||
// rotate clip plane into modelview space
|
||||
Point4F clipPlane;
|
||||
Point3F pnt = refplane * -(refplane.d + 0.0 );
|
||||
Point3F norm = refplane;
|
||||
|
||||
revModelview.mulP( pnt );
|
||||
revModelview.mulV( norm );
|
||||
norm.normalize();
|
||||
|
||||
clipPlane.set( norm.x, norm.y, norm.z, -mDot( pnt, norm ) );
|
||||
|
||||
|
||||
// Manipulate projection matrix
|
||||
//------------------------------------------------------------------------
|
||||
MatrixF proj = GFX->getProjectionMatrix();
|
||||
proj.mul( invRotMat ); // reverse rotation imposed by Torque
|
||||
proj.transpose(); // switch to row-major order
|
||||
|
||||
// Calculate the clip-space corner point opposite the clipping plane
|
||||
// as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
|
||||
// transform it into camera space by multiplying it
|
||||
// by the inverse of the projection matrix
|
||||
Vector4F q;
|
||||
q.x = sgn(clipPlane.x) / proj(0,0);
|
||||
q.y = sgn(clipPlane.y) / proj(1,1);
|
||||
q.z = -1.0F;
|
||||
q.w = ( 1.0F - proj(2,2) ) / proj(3,2);
|
||||
|
||||
F32 a = 1.0 / (clipPlane.x * q.x + clipPlane.y * q.y + clipPlane.z * q.z + clipPlane.w * q.w);
|
||||
|
||||
Vector4F c = clipPlane * a;
|
||||
|
||||
// CodeReview [ags 1/23/08] Come up with a better way to deal with this.
|
||||
if(GFX->getAdapterType() == OpenGL)
|
||||
c.z += 1.0f;
|
||||
|
||||
// Replace the third column of the projection matrix
|
||||
proj.setColumn( 2, c );
|
||||
proj.transpose(); // convert back to column major order
|
||||
proj.mul( rotMat ); // restore Torque rotation
|
||||
|
||||
return proj;
|
||||
}
|
||||
229
Engine/source/scene/reflector.h
Normal file
229
Engine/source/scene/reflector.h
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _REFLECTOR_H_
|
||||
#define _REFLECTOR_H_
|
||||
|
||||
#ifndef _GFXCUBEMAP_H_
|
||||
#include "gfx/gfxCubemap.h"
|
||||
#endif
|
||||
#ifndef _GFXTARGET_H_
|
||||
#include "gfx/gfxTarget.h"
|
||||
#endif
|
||||
#ifndef _SIMDATABLOCK_H_
|
||||
#include "console/simDatablock.h"
|
||||
#endif
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _MATHUTIL_FRUSTUM_H_
|
||||
#include "math/util/frustum.h"
|
||||
#endif
|
||||
|
||||
struct CameraQuery;
|
||||
class Point2I;
|
||||
class Frustum;
|
||||
class SceneManager;
|
||||
class SceneObject;
|
||||
class GFXOcclusionQuery;
|
||||
|
||||
|
||||
struct ReflectParams
|
||||
{
|
||||
const CameraQuery *query;
|
||||
Point2I viewportExtent;
|
||||
Frustum culler;
|
||||
U32 startOfUpdateMs;
|
||||
};
|
||||
|
||||
|
||||
class ReflectorDesc : public SimDataBlock
|
||||
{
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
|
||||
ReflectorDesc();
|
||||
virtual ~ReflectorDesc();
|
||||
|
||||
DECLARE_CONOBJECT( ReflectorDesc );
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void packData( BitStream *stream );
|
||||
virtual void unpackData( BitStream* stream );
|
||||
virtual bool preload( bool server, String &errorStr );
|
||||
|
||||
U32 texSize;
|
||||
F32 nearDist;
|
||||
F32 farDist;
|
||||
U32 objectTypeMask;
|
||||
F32 detailAdjust;
|
||||
F32 priority;
|
||||
U32 maxRateMs;
|
||||
bool useOcclusionQuery;
|
||||
//U32 lastLodSize;
|
||||
};
|
||||
|
||||
|
||||
class ReflectorBase
|
||||
{
|
||||
public:
|
||||
|
||||
ReflectorBase();
|
||||
virtual ~ReflectorBase();
|
||||
|
||||
bool isEnabled() const { return mEnabled; }
|
||||
|
||||
virtual void unregisterReflector();
|
||||
virtual F32 calcScore( const ReflectParams ¶ms );
|
||||
virtual void updateReflection( const ReflectParams ¶ms ) {}
|
||||
|
||||
GFXOcclusionQuery* getOcclusionQuery() const { return mOcclusionQuery; }
|
||||
|
||||
bool isOccluded() const { return mOccluded; }
|
||||
|
||||
/// Returns true if this reflector is in the process of rendering.
|
||||
bool isRendering() const { return mIsRendering; }
|
||||
|
||||
/// Signifies that the query has not finished yet and a new query
|
||||
/// does not need to be submitted.
|
||||
bool mQueryPending;
|
||||
|
||||
protected:
|
||||
|
||||
bool mEnabled;
|
||||
|
||||
bool mIsRendering;
|
||||
|
||||
GFXOcclusionQuery *mOcclusionQuery;
|
||||
|
||||
bool mOccluded;
|
||||
|
||||
SceneObject *mObject;
|
||||
|
||||
ReflectorDesc *mDesc;
|
||||
|
||||
public:
|
||||
|
||||
// These are public because some of them
|
||||
// are exposed as fields.
|
||||
|
||||
F32 score;
|
||||
U32 lastUpdateMs;
|
||||
|
||||
|
||||
};
|
||||
|
||||
typedef Vector<ReflectorBase*> ReflectorList;
|
||||
|
||||
|
||||
class CubeReflector : public ReflectorBase
|
||||
{
|
||||
typedef ReflectorBase Parent;
|
||||
|
||||
public:
|
||||
|
||||
CubeReflector();
|
||||
virtual ~CubeReflector() {}
|
||||
|
||||
void registerReflector( SceneObject *inObject,
|
||||
ReflectorDesc *inDesc );
|
||||
|
||||
virtual void unregisterReflector();
|
||||
virtual void updateReflection( const ReflectParams ¶ms );
|
||||
|
||||
GFXCubemap* getCubemap() const { return cubemap; }
|
||||
|
||||
void updateFace( const ReflectParams ¶ms, U32 faceidx );
|
||||
F32 calcFaceScore( const ReflectParams ¶ms, U32 faceidx );
|
||||
|
||||
protected:
|
||||
|
||||
GFXTexHandle depthBuff;
|
||||
GFXTextureTargetRef renderTarget;
|
||||
GFXCubemapHandle cubemap;
|
||||
U32 mLastTexSize;
|
||||
|
||||
class CubeFaceReflector : public ReflectorBase
|
||||
{
|
||||
typedef ReflectorBase Parent;
|
||||
friend class CubeReflector;
|
||||
|
||||
public:
|
||||
U32 faceIdx;
|
||||
CubeReflector *cube;
|
||||
|
||||
virtual void updateReflection( const ReflectParams ¶ms ) { cube->updateFace( params, faceIdx ); }
|
||||
virtual F32 calcScore( const ReflectParams ¶ms );
|
||||
};
|
||||
|
||||
CubeFaceReflector mFaces[6];
|
||||
};
|
||||
|
||||
|
||||
class PlaneReflector : public ReflectorBase
|
||||
{
|
||||
typedef ReflectorBase Parent;
|
||||
|
||||
public:
|
||||
|
||||
PlaneReflector()
|
||||
{
|
||||
refplane.set( Point3F(0,0,0), Point3F(0,0,1) );
|
||||
objectSpace = false;
|
||||
mLastTexSize = 0;
|
||||
}
|
||||
|
||||
virtual ~PlaneReflector() {}
|
||||
|
||||
void registerReflector( SceneObject *inObject,
|
||||
ReflectorDesc *inDesc );
|
||||
|
||||
virtual F32 calcScore( const ReflectParams ¶ms );
|
||||
virtual void updateReflection( const ReflectParams ¶ms );
|
||||
|
||||
/// Set up camera matrix for a reflection on the plane
|
||||
MatrixF getCameraReflection( MatrixF &camTrans );
|
||||
|
||||
/// Oblique frustum clipping - use near plane of zbuffer as a clip plane
|
||||
MatrixF getFrustumClipProj( MatrixF &modelview );
|
||||
|
||||
protected:
|
||||
|
||||
U32 mLastTexSize;
|
||||
|
||||
// The camera position at the last update.
|
||||
Point3F mLastPos;
|
||||
|
||||
// The camera direction at the last update.
|
||||
VectorF mLastDir;
|
||||
|
||||
public:
|
||||
|
||||
GFXTextureTargetRef reflectTarget;
|
||||
GFXTexHandle reflectTex;
|
||||
PlaneF refplane;
|
||||
bool objectSpace;
|
||||
};
|
||||
|
||||
#endif // _REFLECTOR_H_
|
||||
65
Engine/source/scene/sceneCameraState.cpp
Normal file
65
Engine/source/scene/sceneCameraState.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/sceneCameraState.h"
|
||||
|
||||
#include "gfx/gfxDevice.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneCameraState::SceneCameraState( const RectI& viewport, const Frustum& frustum, const MatrixF& worldView, const MatrixF& projection )
|
||||
: mViewport( viewport ),
|
||||
mFrustum( frustum ),
|
||||
mWorldViewMatrix( worldView ),
|
||||
mProjectionMatrix( projection )
|
||||
{
|
||||
mViewDirection = frustum.getTransform().getForwardVector();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneCameraState SceneCameraState::fromGFX()
|
||||
{
|
||||
return fromGFXWithViewport( GFX->getViewport() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneCameraState SceneCameraState::fromGFXWithViewport( const RectI& viewport )
|
||||
{
|
||||
const MatrixF& world = GFX->getWorldMatrix();
|
||||
|
||||
MatrixF camera = world;
|
||||
camera.inverse();
|
||||
|
||||
Frustum frustum = GFX->getFrustum();
|
||||
frustum.setTransform( camera );
|
||||
|
||||
return SceneCameraState(
|
||||
viewport,
|
||||
frustum,
|
||||
world,
|
||||
GFX->getProjectionMatrix()
|
||||
);
|
||||
}
|
||||
101
Engine/source/scene/sceneCameraState.h
Normal file
101
Engine/source/scene/sceneCameraState.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENECAMERASTATE_H_
|
||||
#define _SCENECAMERASTATE_H_
|
||||
|
||||
#ifndef _MATHUTIL_FRUSTUM_H_
|
||||
#include "math/util/frustum.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MRECT_H_
|
||||
#include "math/mRect.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MMATRIX_H_
|
||||
#include "math/mMatrix.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// An object that combines all the state that is relevant to looking into the
|
||||
/// scene from a particular point of view.
|
||||
class SceneCameraState
|
||||
{
|
||||
protected:
|
||||
|
||||
/// The screen-space viewport rectangle.
|
||||
RectI mViewport;
|
||||
|
||||
/// The viewing frustum.
|
||||
Frustum mFrustum;
|
||||
|
||||
/// The inverse of the frustum's transform stored here for caching.
|
||||
MatrixF mWorldViewMatrix;
|
||||
|
||||
/// The projection matrix.
|
||||
MatrixF mProjectionMatrix;
|
||||
|
||||
/// World-space vector representing the view direction.
|
||||
Point3F mViewDirection;
|
||||
|
||||
/// Internal constructor.
|
||||
SceneCameraState() {}
|
||||
|
||||
public:
|
||||
|
||||
/// Freeze the given viewing state.
|
||||
///
|
||||
/// @param viewport Screen-space viewport rectangle.
|
||||
/// @param frustum Camera frustum.
|
||||
/// @param worldView World->view matrix.
|
||||
/// @param projection Projection matrix.
|
||||
SceneCameraState( const RectI& viewport, const Frustum& frustum, const MatrixF& worldView, const MatrixF& projection );
|
||||
|
||||
/// Capture the view state from the current GFX state.
|
||||
static SceneCameraState fromGFX();
|
||||
|
||||
///
|
||||
static SceneCameraState fromGFXWithViewport( const RectI& viewport );
|
||||
|
||||
/// Return the screen-space viewport rectangle.
|
||||
const RectI& getViewport() const { return mViewport; }
|
||||
|
||||
/// Return the camera frustum.
|
||||
const Frustum& getFrustum() const { return mFrustum; }
|
||||
|
||||
/// Return the view position. This is a shortcut for getFrustum().getPosition().
|
||||
const Point3F& getViewPosition() const { return mFrustum.getPosition(); }
|
||||
|
||||
/// Return the world-space view vector.
|
||||
const Point3F& getViewDirection() const { return mViewDirection; }
|
||||
|
||||
/// Return the view->world transform. This is a shortcut for getFrustum().getTransform().
|
||||
const MatrixF& getViewWorldMatrix() const { return mFrustum.getTransform(); }
|
||||
|
||||
/// Return the world->view transform.
|
||||
const MatrixF& getWorldViewMatrix() const { return mWorldViewMatrix; }
|
||||
|
||||
/// Return the projection transform.
|
||||
const MatrixF& getProjectionMatrix() const { return mProjectionMatrix; }
|
||||
};
|
||||
|
||||
#endif // !_SCENECAMERASTATE_H_
|
||||
1649
Engine/source/scene/sceneContainer.cpp
Normal file
1649
Engine/source/scene/sceneContainer.cpp
Normal file
File diff suppressed because it is too large
Load diff
335
Engine/source/scene/sceneContainer.h
Normal file
335
Engine/source/scene/sceneContainer.h
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENECONTAINER_H_
|
||||
#define _SCENECONTAINER_H_
|
||||
|
||||
#ifndef _MBOX_H_
|
||||
#include "math/mBox.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MSPHERE_H_
|
||||
#include "math/mSphere.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MPOLYHEDRON_H_
|
||||
#include "math/mPolyhedron.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SIMOBJECT_H_
|
||||
#include "console/simObject.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// @file
|
||||
/// SceneObject database.
|
||||
|
||||
|
||||
class SceneObject;
|
||||
class AbstractPolyList;
|
||||
class OptimizedPolyList;
|
||||
class Frustum;
|
||||
class Point3F;
|
||||
|
||||
struct RayInfo;
|
||||
|
||||
|
||||
template< typename T >
|
||||
class SceneObjectRefBase
|
||||
{
|
||||
public:
|
||||
|
||||
/// Object that is referenced in the link.
|
||||
SceneObject* object;
|
||||
|
||||
/// Next link in chain of container.
|
||||
T* nextInBin;
|
||||
|
||||
/// Previous link in chain of container.
|
||||
T* prevInBin;
|
||||
|
||||
/// Next link in chain that is associated with #object.
|
||||
T* nextInObj;
|
||||
};
|
||||
|
||||
|
||||
/// Reference to a scene object.
|
||||
class SceneObjectRef : public SceneObjectRefBase< SceneObjectRef > {};
|
||||
|
||||
|
||||
/// A contextual hint passed to the polylist methods which
|
||||
/// allows it to return the appropriate geometry.
|
||||
enum PolyListContext
|
||||
{
|
||||
/// A hint that the polyist is intended
|
||||
/// for collision testing.
|
||||
PLC_Collision,
|
||||
|
||||
/// A hint that the polyist is for decal
|
||||
/// geometry generation.
|
||||
PLC_Decal,
|
||||
|
||||
/// A hint that the polyist is used for
|
||||
/// selection from an editor or other tool.
|
||||
PLC_Selection,
|
||||
|
||||
/// A hint that the polyist will be used
|
||||
/// to export geometry and would like to have
|
||||
/// texture coords and materials.
|
||||
PLC_Export
|
||||
};
|
||||
|
||||
|
||||
/// For simple queries. Simply creates a vector of the objects
|
||||
class SimpleQueryList
|
||||
{
|
||||
public:
|
||||
|
||||
Vector< SceneObject* > mList;
|
||||
|
||||
SimpleQueryList()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mList );
|
||||
}
|
||||
|
||||
void insertObject( SceneObject* obj ) { mList.push_back(obj); }
|
||||
static void insertionCallback( SceneObject* obj, void* key )
|
||||
{
|
||||
SimpleQueryList* pList = reinterpret_cast< SimpleQueryList* >( key );
|
||||
pList->insertObject( obj );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// Database for SceneObjects.
|
||||
///
|
||||
/// ScenceContainer implements a grid-based spatial subdivision for the contents of a scene.
|
||||
class SceneContainer
|
||||
{
|
||||
enum CastRayType
|
||||
{
|
||||
CollisionGeometry,
|
||||
RenderedGeometry,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
struct Link
|
||||
{
|
||||
Link* next;
|
||||
Link* prev;
|
||||
Link();
|
||||
void unlink();
|
||||
void linkAfter(Link* ptr);
|
||||
};
|
||||
|
||||
struct CallbackInfo
|
||||
{
|
||||
PolyListContext context;
|
||||
AbstractPolyList* polyList;
|
||||
Box3F boundingBox;
|
||||
SphereF boundingSphere;
|
||||
void *key;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Link mStart;
|
||||
Link mEnd;
|
||||
|
||||
/// Container queries based on #mCurrSeqKey are are not re-entrant;
|
||||
/// this is used to detect when it happens.
|
||||
bool mSearchInProgress;
|
||||
|
||||
/// Current sequence key.
|
||||
U32 mCurrSeqKey;
|
||||
|
||||
SceneObjectRef* mFreeRefPool;
|
||||
Vector< SceneObjectRef* > mRefPoolBlocks;
|
||||
|
||||
SceneObjectRef* mBinArray;
|
||||
SceneObjectRef mOverflowBin;
|
||||
|
||||
/// A vector that contains just the water and physical zone
|
||||
/// object types which is used to optimize searches.
|
||||
Vector< SceneObject* > mWaterAndZones;
|
||||
|
||||
/// Vector that contains just the terrain objects in the container.
|
||||
Vector< SceneObject* > mTerrains;
|
||||
|
||||
static const U32 csmNumBins;
|
||||
static const F32 csmBinSize;
|
||||
static const F32 csmTotalBinSize;
|
||||
static const U32 csmRefPoolBlockSize;
|
||||
|
||||
public:
|
||||
|
||||
SceneContainer();
|
||||
~SceneContainer();
|
||||
|
||||
/// Return a vector containing all the water and physical zone objects in this container.
|
||||
const Vector< SceneObject* >& getWaterAndPhysicalZones() const { return mWaterAndZones; }
|
||||
|
||||
/// Return a vector containing all terrain objects in this container.
|
||||
const Vector< SceneObject* >& getTerrains() const { return mTerrains; }
|
||||
|
||||
/// @name Basic database operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
typedef void ( *FindCallback )( SceneObject* object, void* key );
|
||||
|
||||
/// Find all objects of the given type(s) and invoke the given callback for each
|
||||
/// of them.
|
||||
/// @param mask Object type mask (@see SimObjectTypes).
|
||||
/// @param callback Pointer to function to invoke for each object.
|
||||
/// @param key User data to pass to the "key" argument of @a callback.
|
||||
void findObjects( U32 mask, FindCallback callback, void* key = NULL );
|
||||
|
||||
void findObjects( const Box3F& box, U32 mask, FindCallback, void *key = NULL );
|
||||
void findObjects( const Frustum& frustum, U32 mask, FindCallback, void *key = NULL );
|
||||
|
||||
void polyhedronFindObjects( const Polyhedron& polyhedron, U32 mask, FindCallback, void *key = NULL );
|
||||
|
||||
/// Find all objects of the given type(s) and add them to the given vector.
|
||||
/// @param mask Object type mask (@see SimObjectTypes).
|
||||
/// @param outFound Vector to add found objects to.
|
||||
void findObjectList( U32 mask, Vector< SceneObject* >* outFound );
|
||||
|
||||
///
|
||||
void findObjectList( const Box3F& box, U32 mask, Vector< SceneObject* >* outFound );
|
||||
|
||||
///
|
||||
void findObjectList( const Frustum& frustum, U32 mask, Vector< SceneObject* >* outFound );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Line intersection
|
||||
/// @{
|
||||
|
||||
typedef bool ( *CastRayCallback )( RayInfo* ri );
|
||||
|
||||
/// Test against collision geometry -- fast.
|
||||
bool castRay( const Point3F &start, const Point3F &end, U32 mask, RayInfo* info, CastRayCallback callback = NULL );
|
||||
|
||||
/// Test against rendered geometry -- slow.
|
||||
bool castRayRendered( const Point3F &start, const Point3F &end, U32 mask, RayInfo* info, CastRayCallback callback = NULL );
|
||||
|
||||
bool collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Poly list
|
||||
/// @{
|
||||
|
||||
///
|
||||
bool buildPolyList( PolyListContext context,
|
||||
const Box3F &box,
|
||||
U32 typeMask,
|
||||
AbstractPolyList *polylist );
|
||||
|
||||
/// @}
|
||||
|
||||
/// Add an object to the database.
|
||||
/// @param object A SceneObject.
|
||||
bool addObject( SceneObject* object );
|
||||
|
||||
/// Remove an object from the database.
|
||||
/// @param object A SceneObject.
|
||||
bool removeObject( SceneObject* object );
|
||||
|
||||
void addRefPoolBlock();
|
||||
SceneObjectRef* allocateObjectRef();
|
||||
void freeObjectRef(SceneObjectRef*);
|
||||
void insertIntoBins( SceneObject* object );
|
||||
void removeFromBins( SceneObject* object );
|
||||
|
||||
/// Make sure that we're not just sticking the object right back
|
||||
/// where it came from. The overloaded insertInto is so we don't calculate
|
||||
/// the ranges twice.
|
||||
void checkBins( SceneObject* object );
|
||||
void insertIntoBins(SceneObject*, U32, U32, U32, U32);
|
||||
|
||||
void initRadiusSearch(const Point3F& searchPoint,
|
||||
const F32 searchRadius,
|
||||
const U32 searchMask);
|
||||
void initTypeSearch(const U32 searchMask);
|
||||
SceneObject* containerSearchNextObject();
|
||||
U32 containerSearchNext();
|
||||
F32 containerSearchCurrDist();
|
||||
F32 containerSearchCurrRadiusDist();
|
||||
|
||||
private:
|
||||
|
||||
Vector<SimObjectPtr<SceneObject>*> mSearchList;///< Object searches to support console querying of the database. ONLY WORKS ON SERVER
|
||||
S32 mCurrSearchPos;
|
||||
Point3F mSearchReferencePoint;
|
||||
|
||||
void cleanupSearchVectors();
|
||||
|
||||
/// Base cast ray code
|
||||
bool _castRay( U32 type, const Point3F &start, const Point3F &end, U32 mask, RayInfo* info, CastRayCallback callback );
|
||||
|
||||
void _findSpecialObjects( const Vector< SceneObject* >& vector, U32 mask, FindCallback, void *key = NULL );
|
||||
void _findSpecialObjects( const Vector< SceneObject* >& vector, const Box3F &box, U32 mask, FindCallback callback, void *key = NULL );
|
||||
|
||||
static void getBinRange( const F32 min, const F32 max, U32& minBin, U32& maxBin );
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern SceneContainer gServerContainer;
|
||||
extern SceneContainer gClientContainer;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void SceneContainer::freeObjectRef(SceneObjectRef* trash)
|
||||
{
|
||||
trash->object = NULL;
|
||||
trash->nextInBin = NULL;
|
||||
trash->prevInBin = NULL;
|
||||
trash->nextInObj = mFreeRefPool;
|
||||
mFreeRefPool = trash;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline SceneObjectRef* SceneContainer::allocateObjectRef()
|
||||
{
|
||||
if( mFreeRefPool == NULL )
|
||||
addRefPoolBlock();
|
||||
AssertFatal( mFreeRefPool!=NULL, "Error, should always have a free reference here!" );
|
||||
|
||||
SceneObjectRef* ret = mFreeRefPool;
|
||||
mFreeRefPool = mFreeRefPool->nextInObj;
|
||||
|
||||
ret->nextInObj = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // !_SCENECONTAINER_H_
|
||||
692
Engine/source/scene/sceneManager.cpp
Normal file
692
Engine/source/scene/sceneManager.cpp
Normal file
|
|
@ -0,0 +1,692 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/sceneManager.h"
|
||||
|
||||
#include "scene/sceneObject.h"
|
||||
#include "scene/zones/sceneTraversalState.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "scene/zones/sceneRootZone.h"
|
||||
#include "scene/zones/sceneZoneSpace.h"
|
||||
#include "lighting/lightManager.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "gfx/gfxDebugEvent.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
|
||||
// For player object bounds workaround.
|
||||
#include "T3D/player.h"
|
||||
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
|
||||
MODULE_BEGIN( Scene )
|
||||
|
||||
MODULE_INIT_AFTER( Sim )
|
||||
MODULE_SHUTDOWN_BEFORE( Sim )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
// Client scene.
|
||||
gClientSceneGraph = new SceneManager( true );
|
||||
|
||||
// Server scene.
|
||||
gServerSceneGraph = new SceneManager( false );
|
||||
|
||||
Con::addVariable( "$Scene::lockCull", TypeBool, &SceneManager::smLockDiffuseFrustum,
|
||||
"Debug tool which locks the frustum culling to the current camera location.\n"
|
||||
"@ingroup Rendering\n" );
|
||||
|
||||
Con::addVariable( "$Scene::disableTerrainOcclusion", TypeBool, &SceneCullingState::smDisableTerrainOcclusion,
|
||||
"Used to disable the somewhat expensive terrain occlusion testing.\n"
|
||||
"@ingroup Rendering\n" );
|
||||
|
||||
Con::addVariable( "$Scene::disableZoneCulling", TypeBool, &SceneCullingState::smDisableZoneCulling,
|
||||
"If true, zone culling will be disabled and the scene contents will only be culled against the root frustum.\n\n"
|
||||
"@ingroup Rendering\n" );
|
||||
|
||||
Con::addVariable( "$Scene::renderBoundingBoxes", TypeBool, &SceneManager::smRenderBoundingBoxes,
|
||||
"If true, the bounding boxes of objects will be displayed.\n\n"
|
||||
"@ingroup Rendering" );
|
||||
|
||||
Con::addVariable( "$Scene::maxOccludersPerZone", TypeS32, &SceneCullingState::smMaxOccludersPerZone,
|
||||
"Maximum number of occluders that will be concurrently allowed into the scene culling state of any given zone.\n\n"
|
||||
"@ingroup Rendering" );
|
||||
|
||||
Con::addVariable( "$Scene::occluderMinWidthPercentage", TypeF32, &SceneCullingState::smOccluderMinWidthPercentage,
|
||||
"TODO\n\n"
|
||||
"@ingroup Rendering" );
|
||||
|
||||
Con::addVariable( "$Scene::occluderMinHeightPercentage", TypeF32, &SceneCullingState::smOccluderMinHeightPercentage,
|
||||
"TODO\n\n"
|
||||
"@ingroup Rendering" );
|
||||
}
|
||||
|
||||
MODULE_SHUTDOWN
|
||||
{
|
||||
SAFE_DELETE( gClientSceneGraph );
|
||||
SAFE_DELETE( gServerSceneGraph );
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
|
||||
bool SceneManager::smRenderBoundingBoxes;
|
||||
bool SceneManager::smLockDiffuseFrustum = false;
|
||||
SceneCameraState SceneManager::smLockedDiffuseCamera = SceneCameraState( RectI(), Frustum(), MatrixF(), MatrixF() );
|
||||
|
||||
SceneManager* gClientSceneGraph = NULL;
|
||||
SceneManager* gServerSceneGraph = NULL;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneManager::SceneManager( bool isClient )
|
||||
: mLightManager( NULL ),
|
||||
mCurrentRenderState( NULL ),
|
||||
mIsClient( isClient ),
|
||||
mUsePostEffectFog( true ),
|
||||
mDisplayTargetResolution( 0, 0 ),
|
||||
mDefaultRenderPass( NULL ),
|
||||
mVisibleDistance( 500.f ),
|
||||
mNearClip( 0.1f ),
|
||||
mAmbientLightColor( ColorF( 0.1f, 0.1f, 0.1f, 1.0f ) ),
|
||||
mZoneManager( NULL )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mBatchQueryList );
|
||||
|
||||
// For the client, create a zone manager.
|
||||
|
||||
if( isClient )
|
||||
{
|
||||
mZoneManager = new SceneZoneSpaceManager( getContainer() );
|
||||
|
||||
// Add the root zone to the scene.
|
||||
|
||||
addObjectToScene( mZoneManager->getRootZone() );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneManager::~SceneManager()
|
||||
{
|
||||
SAFE_DELETE( mZoneManager );
|
||||
|
||||
if( mLightManager )
|
||||
mLightManager->deactivate();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneManager::renderScene( ScenePassType passType, U32 objectMask )
|
||||
{
|
||||
SceneCameraState cameraState = SceneCameraState::fromGFX();
|
||||
|
||||
// Handle frustum locking.
|
||||
|
||||
const bool lockedFrustum = ( smLockDiffuseFrustum && passType == SPT_Diffuse );
|
||||
if( lockedFrustum )
|
||||
cameraState = smLockedDiffuseCamera;
|
||||
else if( passType == SPT_Diffuse )
|
||||
{
|
||||
// Store the camera state so if we lock, this will become the
|
||||
// locked state.
|
||||
|
||||
if( passType == SPT_Diffuse )
|
||||
smLockedDiffuseCamera = cameraState;
|
||||
}
|
||||
|
||||
// Create the render state.
|
||||
|
||||
SceneRenderState renderState( this, passType, cameraState );
|
||||
|
||||
// If we have locked the frustum, reset the view transform
|
||||
// on the render pass which the render state has just set
|
||||
// to the view matrix corresponding to the locked frustum. For
|
||||
// rendering, however, we need the true view matrix from the
|
||||
// GFX state.
|
||||
|
||||
if( lockedFrustum )
|
||||
{
|
||||
RenderPassManager* rpm = renderState.getRenderPass();
|
||||
rpm->assignSharedXform( RenderPassManager::View, GFX->getWorldMatrix() );
|
||||
}
|
||||
|
||||
// Render.
|
||||
|
||||
renderScene( &renderState, objectMask );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone )
|
||||
{
|
||||
PROFILE_SCOPE( SceneGraph_renderScene );
|
||||
|
||||
// Get the lights for rendering the scene.
|
||||
|
||||
PROFILE_START( SceneGraph_registerLights );
|
||||
LIGHTMGR->registerGlobalLights( &renderState->getFrustum(), false );
|
||||
PROFILE_END();
|
||||
|
||||
// If its a diffuse pass, update the current ambient light level.
|
||||
// To do that find the starting zone and determine whether it has a custom
|
||||
// ambient light color. If so, pass it on to the ambient light manager.
|
||||
// If not, use the ambient light color of the sunlight.
|
||||
//
|
||||
// Note that we retain the starting zone information here and pass it
|
||||
// on to renderSceneNoLights so that we don't need to look it up twice.
|
||||
|
||||
if( renderState->isDiffusePass() )
|
||||
{
|
||||
if( !baseObject && getZoneManager() )
|
||||
{
|
||||
getZoneManager()->findZone( renderState->getCameraPosition(), baseObject, baseZone );
|
||||
AssertFatal( baseObject != NULL, "SceneManager::renderScene - findZone() did not return an object" );
|
||||
}
|
||||
|
||||
ColorF zoneAmbient;
|
||||
if( baseObject && baseObject->getZoneAmbientLightColor( baseZone, zoneAmbient ) )
|
||||
mAmbientLightColor.setTargetValue( zoneAmbient );
|
||||
else
|
||||
{
|
||||
const LightInfo* sunlight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType );
|
||||
if( sunlight )
|
||||
mAmbientLightColor.setTargetValue( sunlight->getAmbient() );
|
||||
}
|
||||
|
||||
renderState->setAmbientLightColor( mAmbientLightColor.getCurrentValue() );
|
||||
}
|
||||
|
||||
// Trigger the pre-render signal.
|
||||
|
||||
PROFILE_START( SceneGraph_preRenderSignal);
|
||||
mCurrentRenderState = renderState;
|
||||
getPreRenderSignal().trigger( this, renderState );
|
||||
mCurrentRenderState = NULL;
|
||||
PROFILE_END();
|
||||
|
||||
// Render the scene.
|
||||
|
||||
renderSceneNoLights( renderState, objectMask, baseObject, baseZone );
|
||||
|
||||
// Trigger the post-render signal.
|
||||
|
||||
PROFILE_START( SceneGraphRender_postRenderSignal );
|
||||
mCurrentRenderState = renderState;
|
||||
getPostRenderSignal().trigger( this, renderState );
|
||||
mCurrentRenderState = NULL;
|
||||
PROFILE_END();
|
||||
|
||||
// Remove the previously registered lights.
|
||||
|
||||
PROFILE_START( SceneGraph_unregisterLights);
|
||||
LIGHTMGR->unregisterAllLights();
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneManager::renderSceneNoLights( SceneRenderState* renderState, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone )
|
||||
{
|
||||
// Set the current state.
|
||||
|
||||
mCurrentRenderState = renderState;
|
||||
|
||||
// Render.
|
||||
|
||||
_renderScene( mCurrentRenderState, objectMask, baseObject, baseZone );
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
|
||||
// If frustum is locked and this is a diffuse pass, render the culling volumes of
|
||||
// zones that are selected (or the volumes of the outdoor zone if no zone is
|
||||
// selected).
|
||||
|
||||
if( gEditingMission && renderState->isDiffusePass() && smLockDiffuseFrustum )
|
||||
renderState->getCullingState().debugRenderCullingVolumes();
|
||||
|
||||
#endif
|
||||
|
||||
mCurrentRenderState = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneManager::_renderScene( SceneRenderState* state, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone )
|
||||
{
|
||||
AssertFatal( this == gClientSceneGraph, "SceneManager::_buildSceneGraph - Only the client scenegraph can support this call!" );
|
||||
|
||||
PROFILE_SCOPE( SceneGraph_batchRenderImages );
|
||||
|
||||
// In the editor, override the type mask for diffuse passes.
|
||||
|
||||
if( gEditingMission && state->isDiffusePass() )
|
||||
objectMask = EDITOR_RENDER_TYPEMASK;
|
||||
|
||||
// Update the zoning state and traverse zones.
|
||||
|
||||
if( getZoneManager() )
|
||||
{
|
||||
// Update.
|
||||
|
||||
getZoneManager()->updateZoningState();
|
||||
|
||||
// If zone culling isn't disabled, traverse the
|
||||
// zones now.
|
||||
|
||||
if( !state->getCullingState().disableZoneCulling() )
|
||||
{
|
||||
// Find the start zone if we haven't already.
|
||||
|
||||
if( !baseObject )
|
||||
{
|
||||
getZoneManager()->findZone( state->getCameraPosition(), baseObject, baseZone );
|
||||
AssertFatal( baseObject != NULL, "SceneManager::_renderScene - findZone() did not return an object" );
|
||||
}
|
||||
|
||||
// Traverse zones starting in base object.
|
||||
|
||||
SceneTraversalState traversalState( &state->getCullingState() );
|
||||
PROFILE_START( Scene_traverseZones );
|
||||
baseObject->traverseZones( &traversalState, baseZone );
|
||||
PROFILE_END();
|
||||
|
||||
// Set the scene render box to the area we have traversed.
|
||||
|
||||
state->setRenderArea( traversalState.getTraversedArea() );
|
||||
}
|
||||
}
|
||||
|
||||
// Set the query box for the container query. Never
|
||||
// make it larger than the frustum's AABB. In the editor,
|
||||
// always query the full frustum as that gives objects
|
||||
// the opportunity to render editor visualizations even if
|
||||
// they are otherwise not in view.
|
||||
|
||||
if( !state->getFrustum().getBounds().isOverlapped( state->getRenderArea() ) )
|
||||
{
|
||||
// This handles fringe cases like flying backwards into a zone where you
|
||||
// end up pretty much standing on a zone border and looking directly into
|
||||
// its "walls". In that case the traversal area will be behind the frustum
|
||||
// (remember that the camera isn't where visibility starts, it's the near
|
||||
// distance).
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Box3F queryBox = state->getFrustum().getBounds();
|
||||
if( !gEditingMission )
|
||||
{
|
||||
queryBox.minExtents.setMax( state->getRenderArea().minExtents );
|
||||
queryBox.maxExtents.setMin( state->getRenderArea().maxExtents );
|
||||
}
|
||||
|
||||
PROFILE_START( Scene_cullObjects );
|
||||
|
||||
//TODO: We should split the codepaths here based on whether the outdoor zone has visible space.
|
||||
// If it has, we should use the container query-based path.
|
||||
// If it hasn't, we should fill the object list directly from the zone lists which will usually
|
||||
// include way fewer objects.
|
||||
|
||||
// Gather all objects that intersect the scene render box.
|
||||
|
||||
mBatchQueryList.clear();
|
||||
getContainer()->findObjectList( queryBox, objectMask, &mBatchQueryList );
|
||||
|
||||
// Cull the list.
|
||||
|
||||
U32 numRenderObjects = state->getCullingState().cullObjects(
|
||||
mBatchQueryList.address(),
|
||||
mBatchQueryList.size(),
|
||||
!state->isDiffusePass() ? SceneCullingState::CullEditorOverrides : 0 // Keep forced editor stuff out of non-diffuse passes.
|
||||
);
|
||||
|
||||
//HACK: If the control object is a Player and it is not in the render list, force
|
||||
// it into it. This really should be solved by collision bounds being separate from
|
||||
// object bounds; only because the Player class is using bounds not encompassing
|
||||
// the actual player object is it that we have this problem in the first place.
|
||||
// Note that we are forcing the player object into ALL passes here but such
|
||||
// is the power of proliferation of things done wrong.
|
||||
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
if( connection )
|
||||
{
|
||||
Player* player = dynamic_cast< Player* >( connection->getControlObject() );
|
||||
if( player )
|
||||
{
|
||||
mBatchQueryList.setSize( numRenderObjects );
|
||||
if( !mBatchQueryList.contains( player ) )
|
||||
{
|
||||
mBatchQueryList.push_back( player );
|
||||
numRenderObjects ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PROFILE_END();
|
||||
|
||||
// Render the remaining objects.
|
||||
|
||||
PROFILE_START( Scene_renderObjects );
|
||||
state->renderObjects( mBatchQueryList.address(), numRenderObjects );
|
||||
PROFILE_END();
|
||||
|
||||
// Render bounding boxes, if enabled.
|
||||
|
||||
if( smRenderBoundingBoxes && state->isDiffusePass() )
|
||||
{
|
||||
GFXDEBUGEVENT_SCOPE( Scene_renderBoundingBoxes, ColorI::WHITE );
|
||||
|
||||
GameBase* cameraObject = 0;
|
||||
if( connection )
|
||||
cameraObject = connection->getCameraObject();
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setFillModeWireframe();
|
||||
desc.setZReadWrite( true, false );
|
||||
|
||||
for( U32 i = 0; i < numRenderObjects; ++ i )
|
||||
{
|
||||
SceneObject* object = mBatchQueryList[ i ];
|
||||
|
||||
// Skip global bounds object.
|
||||
if( object->isGlobalBounds() )
|
||||
continue;
|
||||
|
||||
// Skip camera object as we're viewing the scene from it.
|
||||
if( object == cameraObject )
|
||||
continue;
|
||||
|
||||
const Box3F& worldBox = object->getWorldBox();
|
||||
GFX->getDrawUtil()->drawObjectBox(
|
||||
desc,
|
||||
Point3F( worldBox.len_x(), worldBox.len_y(), worldBox.len_z() ),
|
||||
worldBox.getCenter(),
|
||||
MatrixF::Identity,
|
||||
ColorI::WHITE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct ScopingInfo
|
||||
{
|
||||
Point3F scopePoint;
|
||||
F32 scopeDist;
|
||||
F32 scopeDistSquared;
|
||||
NetConnection* connection;
|
||||
};
|
||||
|
||||
static void _scopeCallback( SceneObject* object, void* data )
|
||||
{
|
||||
if( !object->isScopeable() )
|
||||
return;
|
||||
|
||||
ScopingInfo* info = reinterpret_cast< ScopingInfo* >( data );
|
||||
NetConnection* connection = info->connection;
|
||||
|
||||
F32 difSq = ( object->getWorldSphere().center - info->scopePoint ).lenSquared();
|
||||
if( difSq < info->scopeDistSquared )
|
||||
{
|
||||
// Not even close, it's in...
|
||||
connection->objectInScope( object );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check a little more closely...
|
||||
F32 realDif = mSqrt( difSq );
|
||||
if( realDif - object->getWorldSphere().radius < info->scopeDist)
|
||||
connection->objectInScope( object );
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::scopeScene( CameraScopeQuery* query, NetConnection* netConnection )
|
||||
{
|
||||
PROFILE_SCOPE( SceneGraph_scopeScene );
|
||||
|
||||
// Note that this method does not use the zoning information in the scene
|
||||
// to scope objects. The reason is that with the way that scoping is implemented
|
||||
// in the networking layer--i.e. by killing off ghosts of objects that are out
|
||||
// of scope--, it doesn't make sense to let, for example, all objects in the outdoor
|
||||
// zone go out of scope, just because there is no exterior portal that is visible from
|
||||
// the current camera viewpoint (in any direction).
|
||||
//
|
||||
// So, we perform a simple box query on the area covered by the camera query
|
||||
// and then scope in everything that is in range.
|
||||
|
||||
// Set up scoping info.
|
||||
|
||||
ScopingInfo info;
|
||||
|
||||
info.scopePoint = query->pos;
|
||||
info.scopeDist = query->visibleDistance;
|
||||
info.scopeDistSquared = info.scopeDist * info.scopeDist;
|
||||
info.connection = netConnection;
|
||||
|
||||
// Scope all objects in the query area.
|
||||
|
||||
Box3F area( query->visibleDistance );
|
||||
area.setCenter( query->pos );
|
||||
|
||||
getContainer()->findObjects( area, 0xFFFFFFFF, _scopeCallback, &info );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneManager::addObjectToScene( SceneObject* object )
|
||||
{
|
||||
AssertFatal( !object->mSceneManager, "SceneManager::addObjectToScene - Object already part of a scene" );
|
||||
|
||||
// Mark the object as belonging to us.
|
||||
|
||||
object->mSceneManager = this;
|
||||
|
||||
// Register with managers except its the root zone.
|
||||
|
||||
if( !dynamic_cast< SceneRootZone* >( object ) )
|
||||
{
|
||||
// Add to container.
|
||||
|
||||
getContainer()->addObject( object );
|
||||
|
||||
// Register the object with the zone manager.
|
||||
|
||||
if( getZoneManager() )
|
||||
getZoneManager()->registerObject( object );
|
||||
}
|
||||
|
||||
// Notify the object.
|
||||
|
||||
return object->onSceneAdd();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneManager::removeObjectFromScene( SceneObject* obj )
|
||||
{
|
||||
AssertFatal( obj->getSceneManager() == this, "SceneManager::removeObjectFromScene - Object not part of SceneManager" );
|
||||
|
||||
// Notify the object.
|
||||
|
||||
obj->onSceneRemove();
|
||||
|
||||
// Remove the object from the container.
|
||||
|
||||
getContainer()->removeObject( obj );
|
||||
|
||||
// Remove the object from the zoning system.
|
||||
|
||||
if( getZoneManager() )
|
||||
getZoneManager()->unregisterObject( obj );
|
||||
|
||||
// Clear out the reference to us.
|
||||
|
||||
obj->mSceneManager = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneManager::notifyObjectDirty( SceneObject* object )
|
||||
{
|
||||
// Update container state.
|
||||
|
||||
if( object->mContainer )
|
||||
object->mContainer->checkBins( object );
|
||||
|
||||
// Mark zoning state as dirty.
|
||||
|
||||
if( getZoneManager() )
|
||||
getZoneManager()->notifyObjectChanged( object );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneManager::setDisplayTargetResolution( const Point2I &size )
|
||||
{
|
||||
mDisplayTargetResolution = size;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const Point2I & SceneManager::getDisplayTargetResolution() const
|
||||
{
|
||||
return mDisplayTargetResolution;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneManager::setLightManager( const char* lmName )
|
||||
{
|
||||
LightManager *lm = LightManager::findByName( lmName );
|
||||
if ( !lm )
|
||||
return false;
|
||||
|
||||
return _setLightManager( lm );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneManager::_setLightManager( LightManager* lm )
|
||||
{
|
||||
// Avoid unnecessary work reinitializing materials.
|
||||
if ( lm == mLightManager )
|
||||
return true;
|
||||
|
||||
// Make sure its valid... else fail!
|
||||
if ( !lm->isCompatible() )
|
||||
return false;
|
||||
|
||||
// We only deactivate it... all light managers are singletons
|
||||
// and will manager their own lifetime.
|
||||
if ( mLightManager )
|
||||
mLightManager->deactivate();
|
||||
|
||||
mLightManager = lm;
|
||||
|
||||
if ( mLightManager )
|
||||
mLightManager->activate( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
RenderPassManager* SceneManager::getDefaultRenderPass() const
|
||||
{
|
||||
if( !mDefaultRenderPass )
|
||||
{
|
||||
Sim::findObject( "DiffuseRenderPassManager", mDefaultRenderPass );
|
||||
AssertISV( mDefaultRenderPass, "SceneManager::_setDefaultRenderPass - No DiffuseRenderPassManager defined! Must be set up in script!" );
|
||||
}
|
||||
|
||||
return mDefaultRenderPass;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Console API.
|
||||
//=============================================================================
|
||||
// MARK: ---- Console API ----
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineConsoleFunction( sceneDumpZoneStates, void, ( bool updateFirst ), ( true ),
|
||||
"Dump the current zoning states of all zone spaces in the scene to the console.\n\n"
|
||||
"@param updateFirst If true, zoning states are brought up to date first; if false, the zoning states "
|
||||
"are dumped as is.\n\n"
|
||||
"@note Only valid on the client.\n"
|
||||
"@ingroup Game" )
|
||||
{
|
||||
if( !gClientSceneGraph )
|
||||
{
|
||||
Con::errorf( "sceneDumpZoneStates - Only valid on client!" );
|
||||
return;
|
||||
}
|
||||
|
||||
SceneZoneSpaceManager* manager = gClientSceneGraph->getZoneManager();
|
||||
if( !manager )
|
||||
{
|
||||
Con::errorf( "sceneDumpZoneStates - Scene is not using zones!" );
|
||||
return;
|
||||
}
|
||||
|
||||
manager->dumpZoneStates( updateFirst );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineConsoleFunction( sceneGetZoneOwner, SceneObject*, ( U32 zoneId ), ( true ),
|
||||
"Return the SceneObject that contains the given zone.\n\n"
|
||||
"@param zoneId ID of zone.\n"
|
||||
"@return A SceneObject or NULL if the given @a zoneId is invalid.\n\n"
|
||||
"@note Only valid on the client.\n"
|
||||
"@ingroup Game" )
|
||||
{
|
||||
if( !gClientSceneGraph )
|
||||
{
|
||||
Con::errorf( "sceneGetZoneOwner - Only valid on client!" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SceneZoneSpaceManager* manager = gClientSceneGraph->getZoneManager();
|
||||
if( !manager )
|
||||
{
|
||||
Con::errorf( "sceneGetZoneOwner - Scene is not using zones!" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( !manager->isValidZoneId( zoneId ) )
|
||||
{
|
||||
Con::errorf( "sceneGetZoneOwner - Invalid zone ID: %i", zoneId );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return manager->getZoneOwner( zoneId );
|
||||
}
|
||||
354
Engine/source/scene/sceneManager.h
Normal file
354
Engine/source/scene/sceneManager.h
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENEMANAGER_H_
|
||||
#define _SCENEMANAGER_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENEZONESPACEMANAGER_H_
|
||||
#include "scene/zones/sceneZoneSpaceManager.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MRECT_H_
|
||||
#include "math/mRect.h"
|
||||
#endif
|
||||
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
#ifndef _INTERPOLATEDCHANGEPROPERTY_H_
|
||||
#include "util/interpolatedChangeProperty.h"
|
||||
#endif
|
||||
|
||||
#ifndef _GFXTEXTUREHANDLE_H_
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
#endif
|
||||
|
||||
#ifndef _FOGSTRUCTS_H_
|
||||
#include "scene/fogStructs.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TSIGNAL_H_
|
||||
#include "core/util/tSignal.h"
|
||||
#endif
|
||||
|
||||
|
||||
class LightManager;
|
||||
class SceneRootZone;
|
||||
class SceneRenderState;
|
||||
class SceneCameraState;
|
||||
class SceneZoneSpace;
|
||||
class NetConnection;
|
||||
class RenderPassManager;
|
||||
|
||||
|
||||
/// The type of scene pass.
|
||||
/// @see SceneManager
|
||||
/// @see SceneRenderState
|
||||
enum ScenePassType
|
||||
{
|
||||
/// The regular diffuse scene pass.
|
||||
SPT_Diffuse,
|
||||
|
||||
/// The scene pass made for reflection rendering.
|
||||
SPT_Reflect,
|
||||
|
||||
/// The scene pass made for shadow map rendering.
|
||||
SPT_Shadow,
|
||||
|
||||
/// A scene pass that isn't one of the other
|
||||
/// predefined scene pass types.
|
||||
SPT_Other,
|
||||
};
|
||||
|
||||
|
||||
/// An object that manages the SceneObjects belonging to a scene.
|
||||
class SceneManager
|
||||
{
|
||||
public:
|
||||
|
||||
/// A signal used to notify of render passes.
|
||||
typedef Signal< void( SceneManager*, const SceneRenderState* ) > RenderSignal;
|
||||
|
||||
/// If true use the last stored locked frustum for culling
|
||||
/// the diffuse render pass.
|
||||
/// @see smLockedDiffuseFrustum
|
||||
static bool smLockDiffuseFrustum;
|
||||
|
||||
/// If true, render the AABBs of objects for debugging.
|
||||
static bool smRenderBoundingBoxes;
|
||||
|
||||
protected:
|
||||
|
||||
/// Whether this is the client-side scene.
|
||||
bool mIsClient;
|
||||
|
||||
/// Manager for the zones in this scene.
|
||||
SceneZoneSpaceManager* mZoneManager;
|
||||
|
||||
// NonClipProjection is the projection matrix without oblique frustum clipping
|
||||
// applied to it (in reflections)
|
||||
MatrixF mNonClipProj;
|
||||
|
||||
///
|
||||
bool mUsePostEffectFog;
|
||||
|
||||
/// @see setDisplayTargetResolution
|
||||
Point2I mDisplayTargetResolution;
|
||||
|
||||
/// The currently active render state or NULL if we're
|
||||
/// not in the process of rendering.
|
||||
SceneRenderState* mCurrentRenderState;
|
||||
|
||||
F32 mVisibleDistance;
|
||||
|
||||
F32 mNearClip;
|
||||
|
||||
FogData mFogData;
|
||||
|
||||
WaterFogData mWaterFogData;
|
||||
|
||||
/// The stored last diffuse pass frustum for locking the cull.
|
||||
static SceneCameraState smLockedDiffuseCamera;
|
||||
|
||||
/// @name Lighting
|
||||
/// @{
|
||||
|
||||
typedef InterpolatedChangeProperty< ColorF > AmbientLightInterpolator;
|
||||
|
||||
/// Light manager that is active for the scene.
|
||||
LightManager* mLightManager;
|
||||
|
||||
/// Global ambient light level in the scene.
|
||||
AmbientLightInterpolator mAmbientLightColor;
|
||||
|
||||
/// Deactivates the previous light manager and activates the new one.
|
||||
bool _setLightManager( LightManager *lm );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Rendering
|
||||
/// @{
|
||||
|
||||
/// RenderPassManager for the default render pass. This is set up
|
||||
/// in script and looked up by getDefaultRenderPass().
|
||||
mutable RenderPassManager* mDefaultRenderPass;
|
||||
|
||||
///
|
||||
Vector< SceneObject* > mBatchQueryList;
|
||||
|
||||
/// Render scene using the given state.
|
||||
///
|
||||
/// @param state SceneManager render state.
|
||||
/// @param objectMask Object type mask with which to filter scene objects.
|
||||
/// @param baseObject Zone manager to start traversal in. If null, the zone manager
|
||||
/// that contains @a state's camera position will be used.
|
||||
/// @param baseZone Zone in @a zone manager in which to start traversal. Ignored if
|
||||
/// @a baseObject is NULL.
|
||||
void _renderScene( SceneRenderState* state,
|
||||
U32 objectMask = ( U32 ) -1,
|
||||
SceneZoneSpace* baseObject = NULL,
|
||||
U32 baseZone = 0 );
|
||||
|
||||
/// Callback for the container query.
|
||||
static void _batchObjectCallback( SceneObject* object, void* key );
|
||||
|
||||
/// @}
|
||||
|
||||
public:
|
||||
|
||||
SceneManager( bool isClient );
|
||||
~SceneManager();
|
||||
|
||||
/// Return the SceneContainer for this scene.
|
||||
SceneContainer* getContainer() const { return mIsClient ? &gClientContainer : &gServerContainer; }
|
||||
|
||||
/// Return the manager for the zones in this scene.
|
||||
/// @note Only client scenes have a zone manager as for the server, no zoning data is kept.
|
||||
const SceneZoneSpaceManager* getZoneManager() const { return mZoneManager; }
|
||||
SceneZoneSpaceManager* getZoneManager() { return mZoneManager; }
|
||||
|
||||
/// @name SceneObject Management
|
||||
/// @{
|
||||
|
||||
/// Add the given object to the scene.
|
||||
bool addObjectToScene( SceneObject* object );
|
||||
|
||||
/// Remove the given object from the scene.
|
||||
void removeObjectFromScene( SceneObject* object );
|
||||
|
||||
/// Let the scene manager know that the given object has changed its transform or
|
||||
/// sizing state.
|
||||
void notifyObjectDirty( SceneObject* object );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Rendering
|
||||
/// @{
|
||||
|
||||
/// Return the default RenderPassManager for the scene.
|
||||
RenderPassManager* getDefaultRenderPass() const;
|
||||
|
||||
/// Set the default render pass for the scene.
|
||||
void setDefaultRenderPass( RenderPassManager* rpm ) { mDefaultRenderPass = rpm; }
|
||||
|
||||
/// Render the scene with the default render pass.
|
||||
/// @note This uses the current GFX state (transforms, viewport, frustum) to initialize
|
||||
/// the render state.
|
||||
void renderScene( ScenePassType passType, U32 objectMask = DEFAULT_RENDER_TYPEMASK );
|
||||
|
||||
/// Render the scene with a custom rendering pass.
|
||||
void renderScene( SceneRenderState *state, U32 objectMask = DEFAULT_RENDER_TYPEMASK, SceneZoneSpace* baseObject = NULL, U32 baseZone = 0 );
|
||||
|
||||
/// Render the scene with a custom rendering pass and no lighting set up.
|
||||
void renderSceneNoLights( SceneRenderState *state, U32 objectMask = DEFAULT_RENDER_TYPEMASK, SceneZoneSpace* baseObject = NULL, U32 baseZone = 0 );
|
||||
|
||||
/// Returns the currently active scene state or NULL if no state is currently active.
|
||||
SceneRenderState* getCurrentRenderState() const { return mCurrentRenderState; }
|
||||
|
||||
static RenderSignal& getPreRenderSignal()
|
||||
{
|
||||
static RenderSignal theSignal;
|
||||
return theSignal;
|
||||
}
|
||||
|
||||
static RenderSignal& getPostRenderSignal()
|
||||
{
|
||||
static RenderSignal theSignal;
|
||||
return theSignal;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Lighting
|
||||
/// @{
|
||||
|
||||
/// Finds the light manager by name and activates it.
|
||||
bool setLightManager( const char *lmName );
|
||||
|
||||
/// Return the current global ambient light color.
|
||||
const ColorF& getAmbientLightColor() const { return mAmbientLightColor.getCurrentValue(); }
|
||||
|
||||
/// Set the time it takes for a new ambient light color to take full effect.
|
||||
void setAmbientLightTransitionTime( SimTime time ) { mAmbientLightColor.setTransitionTime( time ); }
|
||||
|
||||
/// Set the interpolation curve to use for blending from one global ambient light
|
||||
/// color to a different one.
|
||||
void setAmbientLightTransitionCurve( const EaseF& ease ) { mAmbientLightColor.setTransitionCurve( ease ); }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Networking
|
||||
/// @{
|
||||
|
||||
/// Set the scoping states of the objects in the scene.
|
||||
void scopeScene( CameraScopeQuery* query, NetConnection* netConnection );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Fog/Visibility Management
|
||||
/// @{
|
||||
|
||||
void setPostEffectFog( bool enable ) { mUsePostEffectFog = enable; }
|
||||
bool usePostEffectFog() const { return mUsePostEffectFog; }
|
||||
|
||||
/// Accessor for the FogData structure.
|
||||
const FogData& getFogData() { return mFogData; }
|
||||
|
||||
/// Sets the FogData structure.
|
||||
void setFogData( const FogData &data ) { mFogData = data; }
|
||||
|
||||
/// Accessor for the WaterFogData structure.
|
||||
const WaterFogData& getWaterFogData() { return mWaterFogData; }
|
||||
|
||||
/// Sets the WaterFogData structure.
|
||||
void setWaterFogData( const WaterFogData &data ) { mWaterFogData = data; }
|
||||
|
||||
/// Used by LevelInfo to set the default visible distance for
|
||||
/// rendering the scene.
|
||||
///
|
||||
/// Note this should not be used to alter culling which is
|
||||
/// controlled by the active frustum when a SceneRenderState is created.
|
||||
///
|
||||
/// @see SceneRenderState
|
||||
/// @see GameProcessCameraQuery
|
||||
/// @see LevelInfo
|
||||
void setVisibleDistance( F32 dist ) { mVisibleDistance = dist; }
|
||||
|
||||
/// Returns the default visible distance for the scene.
|
||||
F32 getVisibleDistance() { return mVisibleDistance; }
|
||||
|
||||
/// Used by LevelInfo to set the default near clip plane
|
||||
/// for rendering the scene.
|
||||
///
|
||||
/// @see GameProcessCameraQuery
|
||||
/// @see LevelInfo
|
||||
void setNearClip( F32 nearClip ) { mNearClip = nearClip; }
|
||||
|
||||
/// Returns the default near clip distance for the scene.
|
||||
F32 getNearClip() { return mNearClip; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name dtr Display Target Resolution
|
||||
///
|
||||
/// Some rendering must be targeted at a specific display resolution.
|
||||
/// This display resolution is distinct from the current RT's size
|
||||
/// (such as when rendering a reflection to a texture, for instance)
|
||||
/// so we store the size at which we're going to display the results of
|
||||
/// the current render.
|
||||
///
|
||||
/// @{
|
||||
|
||||
///
|
||||
void setDisplayTargetResolution(const Point2I &size);
|
||||
const Point2I &getDisplayTargetResolution() const;
|
||||
|
||||
/// @}
|
||||
|
||||
// NonClipProjection is the projection matrix without oblique frustum clipping
|
||||
// applied to it (in reflections)
|
||||
void setNonClipProjection( const MatrixF &proj ) { mNonClipProj = proj; }
|
||||
const MatrixF& getNonClipProjection() const { return mNonClipProj; }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//TODO: these two need to go
|
||||
|
||||
/// The client-side scene graph. Not used if the engine is running
|
||||
/// as a dedicated server.
|
||||
extern SceneManager* gClientSceneGraph;
|
||||
|
||||
/// The server-side scene graph. Not used if the engine is running
|
||||
/// as a pure client.
|
||||
extern SceneManager* gServerSceneGraph;
|
||||
|
||||
#endif //_SCENEMANAGER_H_
|
||||
1434
Engine/source/scene/sceneObject.cpp
Normal file
1434
Engine/source/scene/sceneObject.cpp
Normal file
File diff suppressed because it is too large
Load diff
773
Engine/source/scene/sceneObject.h
Normal file
773
Engine/source/scene/sceneObject.h
Normal file
|
|
@ -0,0 +1,773 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENEOBJECT_H_
|
||||
#define _SCENEOBJECT_H_
|
||||
|
||||
#ifndef _NETOBJECT_H_
|
||||
#include "sim/netObject.h"
|
||||
#endif
|
||||
|
||||
#ifndef _COLLISION_H_
|
||||
#include "collision/collision.h"
|
||||
#endif
|
||||
|
||||
#ifndef _OBJECTTYPES_H_
|
||||
#include "T3D/objectTypes.h"
|
||||
#endif
|
||||
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
#ifndef _BITSET_H_
|
||||
#include "core/bitSet.h"
|
||||
#endif
|
||||
|
||||
#ifndef _PROCESSLIST_H_
|
||||
#include "T3D/gameBase/processList.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENECONTAINER_H_
|
||||
#include "scene/sceneContainer.h"
|
||||
#endif
|
||||
|
||||
|
||||
class SceneManager;
|
||||
class SceneRenderState;
|
||||
class SceneTraversalState;
|
||||
class SceneCameraState;
|
||||
class SceneObjectLink;
|
||||
class SceneObjectLightingPlugin;
|
||||
|
||||
class Convex;
|
||||
class LightInfo;
|
||||
class SFXAmbience;
|
||||
|
||||
struct ObjectRenderInst;
|
||||
struct Move;
|
||||
|
||||
|
||||
/// A 3D object.
|
||||
///
|
||||
/// @section SceneObject_intro Introduction
|
||||
///
|
||||
/// SceneObject exists as a foundation for 3D objects in Torque. It provides the
|
||||
/// basic functionality for:
|
||||
/// - A scene graph (in the Zones and Portals sections), allowing efficient
|
||||
/// and robust rendering of the game scene.
|
||||
/// - Various helper functions, including functions to get bounding information
|
||||
/// and momentum/velocity.
|
||||
/// - Collision detection, as well as ray casting.
|
||||
/// - Lighting. SceneObjects can register lights both at lightmap generation time,
|
||||
/// and dynamic lights at runtime (for special effects, such as from flame or
|
||||
/// a projectile, or from an explosion).
|
||||
/// - Manipulating scene objects, for instance varying scale.
|
||||
///
|
||||
/// @section SceneObject_example An Example
|
||||
///
|
||||
/// Melv May has written a most marvelous example object deriving from SceneObject.
|
||||
/// Unfortunately this page is too small to contain it.
|
||||
///
|
||||
/// @see http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=3217
|
||||
/// for a copy of Melv's example.
|
||||
class SceneObject : public NetObject, private SceneContainer::Link, public ProcessObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef NetObject Parent;
|
||||
|
||||
friend class SceneManager;
|
||||
friend class SceneContainer;
|
||||
friend class SceneZoneSpaceManager;
|
||||
friend class SceneCullingState; // _getZoneRefHead
|
||||
friend class SceneObjectLink; // mSceneObjectLinks
|
||||
|
||||
enum
|
||||
{
|
||||
/// Maximum number of zones that an object can concurrently be assigned to.
|
||||
MaxObjectZones = 128,
|
||||
|
||||
NumMountPoints = 32,
|
||||
NumMountPointBits = 5,
|
||||
};
|
||||
|
||||
/// Networking dirty mask.
|
||||
enum SceneObjectMasks
|
||||
{
|
||||
InitialUpdateMask = BIT( 0 ),
|
||||
ScaleMask = BIT( 1 ),
|
||||
FlagMask = BIT( 2 ),
|
||||
MountedMask = BIT( 3 ),
|
||||
NextFreeMask = BIT( 4 )
|
||||
};
|
||||
|
||||
/// Bit-flags stored in mObjectFlags.
|
||||
/// If a derived class adds more flags they must overload
|
||||
/// getObjectFlagMax to ensure those flags will be transmitted over
|
||||
/// the network.
|
||||
/// @see getObjectFlagMax
|
||||
enum SceneObjectFlags
|
||||
{
|
||||
/// If set, the object can be rendered.
|
||||
/// @note The per-class render disable flag can override the per-object flag.
|
||||
RenderEnabledFlag = BIT( 0 ),
|
||||
|
||||
/// If set, the object can be selected in the editor.
|
||||
/// @note The per-class selection disable flag can override the per-object flag.
|
||||
SelectionEnabledFlag = BIT( 1 ),
|
||||
|
||||
/// If set, object will not be subjected to culling when in the editor.
|
||||
/// This is useful to bypass zone culling and always render certain editor-only
|
||||
/// visual elements (like the zones themselves).
|
||||
DisableCullingInEditorFlag = BIT( 2 ),
|
||||
|
||||
/// If set, object will be used as a visual occluder. In this case,
|
||||
/// the object should implement buildSilhouette() and return a
|
||||
/// *convex* silhouette polygon.
|
||||
VisualOccluderFlag = BIT( 3 ),
|
||||
|
||||
/// If set, object will be used as a sound occluder.
|
||||
SoundOccluderFlag = BIT( 4 ),
|
||||
|
||||
NextFreeFlag = BIT( 5 )
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
/// Combination of SceneObjectFlags.
|
||||
BitSet32 mObjectFlags;
|
||||
|
||||
/// SceneManager to which this SceneObject belongs.
|
||||
SceneManager* mSceneManager;
|
||||
|
||||
/// Links installed by SceneTrackers attached to this object.
|
||||
SceneObjectLink* mSceneObjectLinks;
|
||||
|
||||
/// SceneObjectLightingPlugin attached to this object.
|
||||
SceneObjectLightingPlugin* mLightPlugin;
|
||||
|
||||
/// Object type mask.
|
||||
/// @see SimObjectTypes
|
||||
U32 mTypeMask;
|
||||
|
||||
/// @name Mounting
|
||||
/// @{
|
||||
|
||||
/// Mounted object.
|
||||
struct MountInfo
|
||||
{
|
||||
SceneObject* list; ///< Linked-list of objects mounted on this object
|
||||
SceneObject* object; ///< Object this object is mounted on.
|
||||
SceneObject* link; ///< Link to next object mounted to this object's mount
|
||||
S32 node; ///< Node point we are mounted to.
|
||||
MatrixF xfm;
|
||||
};
|
||||
|
||||
///
|
||||
MountInfo mMount;
|
||||
|
||||
///
|
||||
SimPersistID* mMountPID;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Zoning
|
||||
/// @{
|
||||
|
||||
/// Bidirectional link between a zone manager and its objects.
|
||||
struct ZoneRef : public SceneObjectRefBase< ZoneRef >
|
||||
{
|
||||
/// ID of zone.
|
||||
U32 zone;
|
||||
};
|
||||
|
||||
/// Iterator over the zones that the object is assigned to.
|
||||
/// @note This iterator expects a clean zoning state. It will not update the
|
||||
/// zoning state in case it is dirty.
|
||||
struct ObjectZonesIterator
|
||||
{
|
||||
ObjectZonesIterator( SceneObject* object )
|
||||
: mCurrent( object->_getZoneRefHead() ) {}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return ( mCurrent != NULL );
|
||||
}
|
||||
ObjectZonesIterator& operator ++()
|
||||
{
|
||||
AssertFatal( isValid(), "SceneObject::ObjectZonesIterator::operator++ - Invalid iterator!" );
|
||||
mCurrent = mCurrent->nextInObj;
|
||||
return *this;
|
||||
}
|
||||
U32 operator *() const
|
||||
{
|
||||
AssertFatal( isValid(), "SceneObject::ObjectZonesIterator::operator* - Invalid iterator!" );
|
||||
return mCurrent->zone;
|
||||
}
|
||||
|
||||
private:
|
||||
ZoneRef* mCurrent;
|
||||
};
|
||||
|
||||
friend struct ObjectZonesIterator;
|
||||
|
||||
/// If an object moves, its zoning state needs to be updated. This is deferred
|
||||
/// to when the state is actually needed and this flag indicates a refresh
|
||||
/// is necessary.
|
||||
mutable bool mZoneRefDirty;
|
||||
|
||||
/// Number of zones this object is assigned to.
|
||||
/// @note If #mZoneRefDirty is set, this might be outdated.
|
||||
mutable U32 mNumCurrZones;
|
||||
|
||||
/// List of zones that this object is part of.
|
||||
/// @note If #mZoneRefDirty is set, this might be outdated.
|
||||
mutable ZoneRef* mZoneRefHead;
|
||||
|
||||
/// Refresh the zoning state of this object, if it isn't up-to-date anymore.
|
||||
void _updateZoningState() const;
|
||||
|
||||
/// Return the first link in the zone list of this object. Each link represents
|
||||
/// a single zone that the object is assigned to.
|
||||
///
|
||||
/// @note This method will return the zoning list as is. In case the zoning state
|
||||
/// of the object is dirty, the list contents may be outdated.
|
||||
ZoneRef* _getZoneRefHead() const { return mZoneRefHead; }
|
||||
|
||||
/// Gets the number of zones containing this object.
|
||||
U32 _getNumCurrZones() const { return mNumCurrZones; }
|
||||
|
||||
/// Returns the nth zone containing this object.
|
||||
U32 _getCurrZone( const U32 index ) const;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Transform and Collision Members
|
||||
/// @{
|
||||
|
||||
/// Transform from object space to world space.
|
||||
MatrixF mObjToWorld;
|
||||
|
||||
/// Transform from world space to object space (inverse).
|
||||
MatrixF mWorldToObj;
|
||||
|
||||
/// Object scale.
|
||||
Point3F mObjScale;
|
||||
|
||||
/// Bounding box in object space.
|
||||
Box3F mObjBox;
|
||||
|
||||
/// Bounding box (AABB) in world space.
|
||||
Box3F mWorldBox;
|
||||
|
||||
/// Bounding sphere in world space.
|
||||
SphereF mWorldSphere;
|
||||
|
||||
/// Render matrix to transform object space to world space.
|
||||
MatrixF mRenderObjToWorld;
|
||||
|
||||
/// Render matrix to transform world space to object space.
|
||||
MatrixF mRenderWorldToObj;
|
||||
|
||||
/// Render bounding box in world space.
|
||||
Box3F mRenderWorldBox;
|
||||
|
||||
/// Render bounding sphere in world space.
|
||||
SphereF mRenderWorldSphere;
|
||||
|
||||
/// Whether this object is considered to have an infinite bounding box.
|
||||
bool mGlobalBounds;
|
||||
|
||||
///
|
||||
S32 mCollisionCount;
|
||||
|
||||
/// Regenerates the world-space bounding box and bounding sphere.
|
||||
void resetWorldBox();
|
||||
|
||||
/// Regenerates the render-world-space bounding box and sphere.
|
||||
void resetRenderWorldBox();
|
||||
|
||||
/// Regenerates the object-space bounding box from the world-space
|
||||
/// bounding box, the world space to object space transform, and
|
||||
/// the object scale.
|
||||
void resetObjectBox();
|
||||
|
||||
/// Called when the size of the object changes.
|
||||
virtual void onScaleChanged() {}
|
||||
|
||||
/// @}
|
||||
|
||||
/// Object which must be ticked before this object.
|
||||
SimObjectPtr< SceneObject > mAfterObject;
|
||||
|
||||
/// @name SceneContainer Interface
|
||||
///
|
||||
/// When objects are searched, we go through all the zones and ask them for
|
||||
/// all of their objects. Because an object can exist in multiple zones, the
|
||||
/// container sequence key is set to the id of the current search. Then, while
|
||||
/// searching, we check to see if an object's sequence key is the same as the
|
||||
/// current search key. If it is, it will NOT be added to the list of returns
|
||||
/// since it has already been processed.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Container database that the object is assigned to.
|
||||
SceneContainer* mContainer;
|
||||
|
||||
/// SceneContainer sequence key.
|
||||
U32 mContainerSeqKey;
|
||||
|
||||
///
|
||||
SceneObjectRef* mBinRefHead;
|
||||
|
||||
U32 mBinMinX;
|
||||
U32 mBinMaxX;
|
||||
U32 mBinMinY;
|
||||
U32 mBinMaxY;
|
||||
|
||||
/// Returns the container sequence key.
|
||||
U32 getContainerSeqKey() const { return mContainerSeqKey; }
|
||||
|
||||
/// Sets the container sequence key.
|
||||
void setContainerSeqKey( const U32 key ) { mContainerSeqKey = key; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// Called when this is added to a SceneManager.
|
||||
virtual bool onSceneAdd() { return true; }
|
||||
|
||||
/// Called when this is removed from its current SceneManager.
|
||||
virtual void onSceneRemove() {}
|
||||
|
||||
/// Returns the greatest object flag bit defined.
|
||||
/// Only bits within this range will be transmitted over the network.
|
||||
virtual U32 getObjectFlagMax() const { return NextFreeFlag - 1; }
|
||||
|
||||
public:
|
||||
|
||||
SceneObject();
|
||||
virtual ~SceneObject();
|
||||
|
||||
/// Triggered when a SceneObject onAdd is called.
|
||||
static Signal< void( SceneObject* ) > smSceneObjectAdd;
|
||||
|
||||
/// Triggered when a SceneObject onRemove is called.
|
||||
static Signal< void( SceneObject* ) > smSceneObjectRemove;
|
||||
|
||||
/// Return the type mask that indicates to which broad object categories
|
||||
/// this object belongs.
|
||||
U32 getTypeMask() const { return mTypeMask; }
|
||||
|
||||
/// @name SceneManager Functionality
|
||||
/// @{
|
||||
|
||||
/// Return the SceneManager that this SceneObject belongs to.
|
||||
SceneManager* getSceneManager() const { return mSceneManager; }
|
||||
|
||||
/// Adds object to the client or server container depending on the object
|
||||
void addToScene();
|
||||
|
||||
/// Removes the object from the client/server container
|
||||
void removeFromScene();
|
||||
|
||||
/// Returns a pointer to the container that contains this object
|
||||
SceneContainer* getContainer() { return mContainer; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Flags
|
||||
/// @{
|
||||
|
||||
/// Return true if this object is rendered.
|
||||
bool isRenderEnabled() const;
|
||||
|
||||
/// Set whether the object gets rendered.
|
||||
void setRenderEnabled( bool value );
|
||||
|
||||
/// Return true if this object can be selected in the editor.
|
||||
bool isSelectionEnabled() const;
|
||||
|
||||
/// Set whether the object can be selected in the editor.
|
||||
void setSelectionEnabled( bool value );
|
||||
|
||||
/// Return true if the object doesn't want to be subjected to culling
|
||||
/// when in the editor.
|
||||
bool isCullingDisabledInEditor() const { return mObjectFlags.test( DisableCullingInEditorFlag ); }
|
||||
|
||||
/// Return true if the object should be taken into account for visual occlusion.
|
||||
bool isVisualOccluder() const { return mObjectFlags.test( VisualOccluderFlag ); }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Collision and transform related interface
|
||||
///
|
||||
/// The Render Transform is the interpolated transform with respect to the
|
||||
/// frame rate. The Render Transform will differ from the object transform
|
||||
/// because the simulation is updated in fixed intervals, which controls the
|
||||
/// object transform. The framerate is, most likely, higher than this rate,
|
||||
/// so that is why the render transform is interpolated and will differ slightly
|
||||
/// from the object transform.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Disables collisions for this object including raycasts
|
||||
virtual void disableCollision();
|
||||
|
||||
/// Enables collisions for this object
|
||||
virtual void enableCollision();
|
||||
|
||||
/// Returns true if collisions are enabled
|
||||
bool isCollisionEnabled() const { return mCollisionCount == 0; }
|
||||
|
||||
/// This gets called when an object collides with this object.
|
||||
/// @param object Object colliding with this object
|
||||
/// @param vec Vector along which collision occurred
|
||||
virtual void onCollision( SceneObject *object, const VectorF &vec ) {}
|
||||
|
||||
/// Returns true if this object allows itself to be displaced
|
||||
/// @see displaceObject
|
||||
virtual bool isDisplacable() const { return false; }
|
||||
|
||||
/// Returns the momentum of this object
|
||||
virtual Point3F getMomentum() const { return Point3F( 0, 0, 0 ); }
|
||||
|
||||
/// Sets the momentum of this object
|
||||
/// @param momentum Momentum
|
||||
virtual void setMomentum( const Point3F& momentum ) {}
|
||||
|
||||
/// Returns the mass of this object
|
||||
virtual F32 getMass() const { return 1.f; }
|
||||
|
||||
/// Displaces this object by a vector
|
||||
/// @param displaceVector Displacement vector
|
||||
virtual bool displaceObject( const Point3F& displaceVector ) { return false; }
|
||||
|
||||
/// Returns the transform which can be used to convert object space
|
||||
/// to world space
|
||||
virtual const MatrixF& getTransform() const { return mObjToWorld; }
|
||||
|
||||
/// Returns the transform which can be used to convert world space
|
||||
/// into object space
|
||||
const MatrixF& getWorldTransform() const { return mWorldToObj; }
|
||||
|
||||
/// Returns the scale of the object
|
||||
const VectorF& getScale() const { return mObjScale; }
|
||||
|
||||
/// Returns the bounding box for this object in local coordinates.
|
||||
const Box3F& getObjBox() const { return mObjBox; }
|
||||
|
||||
/// Returns the bounding box for this object in world coordinates.
|
||||
const Box3F& getWorldBox() const { return mWorldBox; }
|
||||
|
||||
/// Returns the bounding sphere for this object in world coordinates.
|
||||
const SphereF& getWorldSphere() const { return mWorldSphere; }
|
||||
|
||||
/// Returns the center of the bounding box in world coordinates
|
||||
Point3F getBoxCenter() const { return ( mWorldBox.minExtents + mWorldBox.maxExtents ) * 0.5f; }
|
||||
|
||||
/// Sets the Object -> World transform
|
||||
///
|
||||
/// @param mat New transform matrix
|
||||
virtual void setTransform( const MatrixF &mat );
|
||||
|
||||
/// Sets the scale for the object
|
||||
/// @param scale Scaling values
|
||||
virtual void setScale( const VectorF &scale );
|
||||
|
||||
/// This sets the render transform for this object
|
||||
/// @param mat New render transform
|
||||
virtual void setRenderTransform(const MatrixF &mat);
|
||||
|
||||
/// Returns the render transform
|
||||
const MatrixF& getRenderTransform() const { return mRenderObjToWorld; }
|
||||
|
||||
/// Returns the render transform to convert world to local coordinates
|
||||
const MatrixF& getRenderWorldTransform() const { return mRenderWorldToObj; }
|
||||
|
||||
/// Returns the render world box
|
||||
const Box3F& getRenderWorldBox() const { return mRenderWorldBox; }
|
||||
|
||||
/// Sets the state of this object as hidden or not. If an object is hidden
|
||||
/// it is removed entirely from collisions, it is not ghosted and is
|
||||
/// essentially "non existant" as far as simulation is concerned.
|
||||
/// @param hidden True if object is to be hidden
|
||||
virtual void setHidden( bool hidden );
|
||||
|
||||
/// Builds a convex hull for this object.
|
||||
///
|
||||
/// Think of a convex hull as a low-res mesh which covers, as tightly as
|
||||
/// possible, the object mesh, and is used as a collision mesh.
|
||||
/// @param box
|
||||
/// @param convex Convex mesh generated (out)
|
||||
virtual void buildConvex( const Box3F& box,Convex* convex ) {}
|
||||
|
||||
/// Builds a list of polygons which intersect a bounding volume.
|
||||
///
|
||||
/// This will use either the sphere or the box, not both, the
|
||||
/// SceneObject implementation ignores sphere.
|
||||
///
|
||||
/// @see AbstractPolyList
|
||||
/// @param context A contentual hint as to the type of polylist to build.
|
||||
/// @param polyList Poly list build (out)
|
||||
/// @param box Box bounding volume
|
||||
/// @param sphere Sphere bounding volume
|
||||
///
|
||||
virtual bool buildPolyList( PolyListContext context,
|
||||
AbstractPolyList* polyList,
|
||||
const Box3F& box,
|
||||
const SphereF& sphere ) { return false; }
|
||||
|
||||
/// Casts a ray and obtain collision information, returns true if RayInfo is modified.
|
||||
///
|
||||
/// @param start Start point of ray
|
||||
/// @param end End point of ray
|
||||
/// @param info Collision information obtained (out)
|
||||
virtual bool castRay( const Point3F& start, const Point3F& end, RayInfo* info ) { return false; }
|
||||
|
||||
/// Casts a ray against rendered geometry, returns true if RayInfo is modified.
|
||||
///
|
||||
/// @param start Start point of ray
|
||||
/// @param end End point of ray
|
||||
/// @param info Collision information obtained (out)
|
||||
virtual bool castRayRendered( const Point3F& start, const Point3F& end, RayInfo* info );
|
||||
|
||||
/// Build a world-space silhouette polygon for the object for the given camera settings.
|
||||
/// This is used for occlusion.
|
||||
///
|
||||
/// @param cameraState Camera view parameters.
|
||||
/// @param outPoints Vector to store the resulting polygon points in. Leave untouched
|
||||
/// if method is not implemented.
|
||||
virtual void buildSilhouette( const SceneCameraState& cameraState, Vector< Point3F >& outPoints ) {}
|
||||
|
||||
/// Return true if the given point is contained by the object's (collision) shape.
|
||||
///
|
||||
/// The default implementation will return true if the point is within the object's
|
||||
/// bounding box. Subclasses should implement more precise tests.
|
||||
virtual bool containsPoint( const Point3F &point );
|
||||
|
||||
virtual bool collideBox( const Point3F& start, const Point3F& end, RayInfo* info );
|
||||
|
||||
/// Returns the position of the object.
|
||||
virtual Point3F getPosition() const;
|
||||
|
||||
/// Returns the render-position of the object.
|
||||
///
|
||||
/// @see getRenderTransform
|
||||
Point3F getRenderPosition() const;
|
||||
|
||||
/// Sets the position of the object
|
||||
void setPosition ( const Point3F& pos );
|
||||
|
||||
/// Gets the velocity of the object.
|
||||
virtual Point3F getVelocity() const { return Point3F::Zero; }
|
||||
|
||||
/// Sets the velocity of the object
|
||||
/// @param v Velocity
|
||||
virtual void setVelocity( const Point3F &v ) {}
|
||||
|
||||
/// Applies an impulse force to this object
|
||||
/// @param pos Position where impulse came from in world space
|
||||
/// @param vec Velocity vector (Impulse force F = m * v)
|
||||
virtual void applyImpulse( const Point3F &pos, const VectorF &vec ) {}
|
||||
|
||||
/// Applies a radial impulse to the object
|
||||
/// using the impulse origin and force.
|
||||
/// @param origin Point of origin of the radial impulse.
|
||||
/// @param radius The radius of the impulse area.
|
||||
/// @param magnitude The strength of the impulse.
|
||||
virtual void applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ) {}
|
||||
|
||||
/// Returns the distance from this object to a point
|
||||
/// @param pnt World space point to measure to
|
||||
virtual F32 distanceTo( const Point3F &pnt ) const;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Mounting
|
||||
/// @{
|
||||
|
||||
/// ex: Mount B to A at A's node N
|
||||
/// A.mountObject( B, N )
|
||||
///
|
||||
/// @param obj Object to mount
|
||||
/// @param node Mount node ID
|
||||
virtual void mountObject( SceneObject *obj, S32 node, const MatrixF &xfm = MatrixF::Identity );
|
||||
|
||||
/// Remove an object mounting
|
||||
/// @param obj Object to unmount
|
||||
virtual void unmountObject( SceneObject *obj );
|
||||
|
||||
/// Unmount this object from it's mount
|
||||
virtual void unmount();
|
||||
|
||||
/// Callback when this object is mounted.
|
||||
/// @param obj Object we are mounting to.
|
||||
/// @param node Node we are unmounting from.
|
||||
virtual void onMount( SceneObject *obj, S32 node );
|
||||
|
||||
/// Callback when this object is unmounted. This should be overridden to
|
||||
/// set maskbits or do other object type specific work.
|
||||
/// @param obj Object we are unmounting from.
|
||||
/// @param node Node we are unmounting from.
|
||||
virtual void onUnmount( SceneObject *obj, S32 node );
|
||||
|
||||
// Returns mount point to world space transform at tick time.
|
||||
virtual void getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat );
|
||||
|
||||
// Returns mount point to world space transform at render time.
|
||||
// Note this will only be correct if called after this object has interpolated.
|
||||
virtual void getRenderMountTransform( F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat );
|
||||
|
||||
/// Return the object that this object is mounted to.
|
||||
virtual SceneObject* getObjectMount() { return mMount.object; }
|
||||
|
||||
/// Return object link of next object mounted to this object's mount
|
||||
virtual SceneObject* getMountLink() { return mMount.link; }
|
||||
|
||||
/// Returns object list of objects mounted to this object.
|
||||
virtual SceneObject* getMountList() { return mMount.list; }
|
||||
|
||||
/// Returns the mount id that this is mounted to.
|
||||
virtual U32 getMountNode() { return mMount.node; }
|
||||
|
||||
/// Returns true if this object is mounted to anything at all
|
||||
/// Also try to resolve the PID to objectId here if it is pending.
|
||||
virtual bool isMounted();
|
||||
|
||||
/// Returns the number of object mounted along with this
|
||||
virtual S32 getMountedObjectCount();
|
||||
|
||||
/// Returns the object mounted at a position in the mount list
|
||||
/// @param idx Position on the mount list
|
||||
virtual SceneObject* getMountedObject( S32 idx );
|
||||
|
||||
/// Returns the node the object at idx is mounted to
|
||||
/// @param idx Index
|
||||
virtual S32 getMountedObjectNode( S32 idx );
|
||||
|
||||
/// Returns the object a object on the mount list is mounted to
|
||||
/// @param node
|
||||
virtual SceneObject* getMountNodeObject( S32 node );
|
||||
|
||||
void resolveMountPID();
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Sound
|
||||
/// @{
|
||||
|
||||
/// Return whether the object's collision shape is blocking sound.
|
||||
bool isOccludingSound() const { return mObjectFlags.test( SoundOccluderFlag ); }
|
||||
|
||||
/// Return the ambient sound space active inside the volume of this object or NULL if the object does
|
||||
/// not have its own ambient space.
|
||||
virtual SFXAmbience* getSoundAmbience() const { return NULL; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Rendering
|
||||
/// @{
|
||||
|
||||
/// Called when the SceneManager is ready for the registration of render instances.
|
||||
/// @param state Rendering state.
|
||||
virtual void prepRenderImage( SceneRenderState* state ) {}
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Lighting
|
||||
/// @{
|
||||
|
||||
void setLightingPlugin( SceneObjectLightingPlugin* plugin ) { mLightPlugin = plugin; }
|
||||
SceneObjectLightingPlugin* getLightingPlugin() { return mLightPlugin; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Global Bounds
|
||||
/// @{
|
||||
|
||||
const bool isGlobalBounds() const
|
||||
{
|
||||
return mGlobalBounds;
|
||||
}
|
||||
|
||||
/// If global bounds are set to be true, then the object is assumed to
|
||||
/// have an infinitely large bounding box for collision and rendering
|
||||
/// purposes.
|
||||
///
|
||||
/// They can't be toggled currently.
|
||||
void setGlobalBounds();
|
||||
|
||||
/// @}
|
||||
|
||||
/// Return the ProcessList for this object to use.
|
||||
ProcessList* getProcessList() const;
|
||||
|
||||
// ProcessObject,
|
||||
virtual void processAfter( ProcessObject *obj );
|
||||
virtual void clearProcessAfter();
|
||||
virtual ProcessObject* getAfterObject() const { return mAfterObject; }
|
||||
virtual void setProcessTick( bool t );
|
||||
|
||||
// NetObject.
|
||||
virtual U32 packUpdate( NetConnection* conn, U32 mask, BitStream* stream );
|
||||
virtual void unpackUpdate( NetConnection* conn, BitStream* stream );
|
||||
virtual void onCameraScopeQuery( NetConnection* connection, CameraScopeQuery* query );
|
||||
|
||||
// SimObject.
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
virtual void onDeleteNotify( SimObject *object );
|
||||
virtual void inspectPostApply();
|
||||
virtual bool writeField( StringTableEntry fieldName, const char* value );
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
DECLARE_CONOBJECT( SceneObject );
|
||||
|
||||
private:
|
||||
|
||||
SceneObject( const SceneObject& ); ///< @deprecated disallowed
|
||||
|
||||
/// For ScopeAlways objects to be able to properly implement setHidden(), they
|
||||
/// need to temporarily give up ScopeAlways status while being hidden. Otherwise
|
||||
/// the client-side ghost will not disappear as the server-side object will be
|
||||
/// forced to stay in scope.
|
||||
bool mIsScopeAlways;
|
||||
|
||||
/// @name Protected field getters/setters
|
||||
/// @{
|
||||
|
||||
static const char* _getRenderEnabled( void *object, const char *data );
|
||||
static bool _setRenderEnabled( void *object, const char *index, const char *data );
|
||||
static const char* _getSelectionEnabled( void *object, const char *data );
|
||||
static bool _setSelectionEnabled( void *object, const char *index, const char *data );
|
||||
static bool _setFieldPosition( void *object, const char *index, const char *data );
|
||||
static bool _setFieldRotation( void *object, const char *index, const char *data );
|
||||
static bool _setFieldScale( void *object, const char *index, const char *data );
|
||||
static bool _setMountPID( void* object, const char* index, const char* data );
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // _SCENEOBJECT_H_
|
||||
|
||||
44
Engine/source/scene/sceneObjectLightingPlugin.h
Normal file
44
Engine/source/scene/sceneObjectLightingPlugin.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENEOBJECTLIGHTINGPLUGIN_H_
|
||||
#define _SCENEOBJECTLIGHTINGPLUGIN_H_
|
||||
|
||||
class SceneObject;
|
||||
class NetConnection;
|
||||
class BitStream;
|
||||
|
||||
class SceneObjectLightingPlugin
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~SceneObjectLightingPlugin() {}
|
||||
|
||||
/// Reset light plugin to clean state.
|
||||
virtual void reset() {}
|
||||
|
||||
// Called by statics
|
||||
virtual U32 packUpdate( SceneObject* obj, U32 checkMask, NetConnection* conn, U32 mask, BitStream* stream ) = 0;
|
||||
virtual void unpackUpdate( SceneObject* obj, NetConnection* conn, BitStream* stream ) = 0;
|
||||
};
|
||||
|
||||
#endif // !_SCENEOBJECTLIGHTINGPLUGIN_H_
|
||||
26
Engine/source/scene/scenePolyhedralSpace.cpp
Normal file
26
Engine/source/scene/scenePolyhedralSpace.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/scenePolyhedralSpace.h"
|
||||
|
||||
#include "scene/mixin/scenePolyhedralObject.impl.h"
|
||||
47
Engine/source/scene/scenePolyhedralSpace.h
Normal file
47
Engine/source/scene/scenePolyhedralSpace.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENEPOLYHEDRALSPACE_H_
|
||||
#define _SCENEPOLYHEDRALSPACE_H_
|
||||
|
||||
#ifndef _SCENESPACE_H_
|
||||
#include "scene/sceneSpace.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENEPOLYHEDRALOBJECT_H_
|
||||
#include "scene/mixin/scenePolyhedralObject.h"
|
||||
#endif
|
||||
|
||||
|
||||
///
|
||||
class ScenePolyhedralSpace : public ScenePolyhedralObject< SceneSpace >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ScenePolyhedralObject< SceneSpace > Parent;
|
||||
|
||||
ScenePolyhedralSpace() {}
|
||||
ScenePolyhedralSpace( const PolyhedronType& polyhedron )
|
||||
: Parent( polyhedron ) {}
|
||||
};
|
||||
|
||||
#endif // !_SCENEPOLYHEDRALSPACE_H_
|
||||
110
Engine/source/scene/sceneRenderState.cpp
Normal file
110
Engine/source/scene/sceneRenderState.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/sceneRenderState.h"
|
||||
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "math/util/matrixSet.h"
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneRenderState::SceneRenderState( SceneManager* sceneManager,
|
||||
ScenePassType passType,
|
||||
const SceneCameraState& view,
|
||||
RenderPassManager* renderPass /* = NULL */,
|
||||
bool usePostEffects /* = true */ )
|
||||
: mSceneManager( sceneManager ),
|
||||
mCullingState( sceneManager, view ),
|
||||
mRenderPass( renderPass ? renderPass : sceneManager->getDefaultRenderPass() ),
|
||||
mScenePassType( passType ),
|
||||
mRenderNonLightmappedMeshes( true ),
|
||||
mRenderLightmappedMeshes( true ),
|
||||
mUsePostEffects( usePostEffects ),
|
||||
mDisableAdvancedLightingBins( false ),
|
||||
mRenderArea( view.getFrustum().getBounds() ),
|
||||
mAmbientLightColor( sceneManager->getAmbientLightColor() )
|
||||
{
|
||||
// Setup the default parameters for the screen metrics methods.
|
||||
mDiffuseCameraTransform = view.getViewWorldMatrix();
|
||||
|
||||
// The vector eye is the camera vector with its
|
||||
// length normalized to 1 / zFar.
|
||||
getCameraTransform().getColumn( 1, &mVectorEye );
|
||||
mVectorEye.normalize( 1.0f / getFarPlane() );
|
||||
|
||||
// TODO: What about ortho modes? Is near plane ok
|
||||
// or do i need to remove it... maybe ortho has a near
|
||||
// plane of 1 and it just works out?
|
||||
|
||||
const Frustum& frustum = view.getFrustum();
|
||||
const RectI& viewport = view.getViewport();
|
||||
|
||||
mWorldToScreenScale.set( ( frustum.getNearDist() * viewport.extent.x ) / ( frustum.getNearRight() - frustum.getNearLeft() ),
|
||||
( frustum.getNearDist() * viewport.extent.y ) / ( frustum.getNearTop() - frustum.getNearBottom() ) );
|
||||
|
||||
// Assign shared matrix data to the render pass.
|
||||
|
||||
mRenderPass->assignSharedXform( RenderPassManager::View, view.getWorldViewMatrix() );
|
||||
mRenderPass->assignSharedXform( RenderPassManager::Projection, view.getProjectionMatrix() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneRenderState::~SceneRenderState()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const MatrixF& SceneRenderState::getWorldViewMatrix() const
|
||||
{
|
||||
return getRenderPass()->getMatrixSet().getWorldToCamera();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const MatrixF& SceneRenderState::getProjectionMatrix() const
|
||||
{
|
||||
return getRenderPass()->getMatrixSet().getCameraToScreen();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneRenderState::renderObjects( SceneObject** objects, U32 numObjects )
|
||||
{
|
||||
// Let the objects batch their stuff.
|
||||
|
||||
PROFILE_START( SceneRenderState_prepRenderImages );
|
||||
for( U32 i = 0; i < numObjects; ++ i )
|
||||
{
|
||||
SceneObject* object = objects[ i ];
|
||||
object->prepRenderImage( this );
|
||||
}
|
||||
PROFILE_END();
|
||||
|
||||
// Render what the objects have batched.
|
||||
|
||||
getRenderPass()->renderPass( this );
|
||||
}
|
||||
315
Engine/source/scene/sceneRenderState.h
Normal file
315
Engine/source/scene/sceneRenderState.h
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENERENDERSTATE_H_
|
||||
#define _SCENERENDERSTATE_H_
|
||||
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENEMANAGER_H_
|
||||
#include "scene/sceneManager.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENECULLINGSTATE_H_
|
||||
#include "scene/culling/sceneCullingState.h"
|
||||
#endif
|
||||
|
||||
#ifndef _UTIL_DELEGATE_H_
|
||||
#include "core/util/delegate.h"
|
||||
#endif
|
||||
|
||||
|
||||
class SceneObject;
|
||||
class RenderPassManager;
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
|
||||
/// The SceneRenderState describes the state of the scene being rendered.
|
||||
///
|
||||
/// It keeps track of the information that objects need to render properly with regard to
|
||||
/// the camera position, any fog information, viewing frustum, the global environment
|
||||
/// map for reflections, viewable distance, etc.
|
||||
///
|
||||
/// It also owns the current culling state.
|
||||
class SceneRenderState
|
||||
{
|
||||
public:
|
||||
|
||||
/// The delegate used for material overrides.
|
||||
/// @see getOverrideMaterial
|
||||
typedef Delegate< BaseMatInstance*( BaseMatInstance* ) > MatDelegate;
|
||||
|
||||
protected:
|
||||
|
||||
/// SceneManager being rendered in this state.
|
||||
SceneManager* mSceneManager;
|
||||
|
||||
/// The type of scene render pass we're doing.
|
||||
ScenePassType mScenePassType;
|
||||
|
||||
/// The render pass which we are setting up with this scene state.
|
||||
RenderPassManager* mRenderPass;
|
||||
|
||||
/// Culling state of the scene.
|
||||
SceneCullingState mCullingState;
|
||||
|
||||
/// The optional material override delegate.
|
||||
MatDelegate mMatDelegate;
|
||||
|
||||
///
|
||||
MatrixF mDiffuseCameraTransform;
|
||||
|
||||
/// The world to screen space scalar used for LOD calculations.
|
||||
Point2F mWorldToScreenScale;
|
||||
|
||||
/// The AABB that encloses the space in the scene that we render.
|
||||
Box3F mRenderArea;
|
||||
|
||||
/// The camera vector normalized to 1 / far dist.
|
||||
Point3F mVectorEye;
|
||||
|
||||
/// Global ambient light color.
|
||||
ColorF mAmbientLightColor;
|
||||
|
||||
/// Forces bin based post effects to be disabled
|
||||
/// during rendering with this scene state.
|
||||
bool mUsePostEffects;
|
||||
|
||||
/// Disables AdvancedLighting bin draws during rendering with this scene state.
|
||||
bool mDisableAdvancedLightingBins;
|
||||
|
||||
/// If true (default) lightmapped meshes should be rendered.
|
||||
bool mRenderLightmappedMeshes;
|
||||
|
||||
/// If true (default) non-lightmapped meshes should be rendered.
|
||||
bool mRenderNonLightmappedMeshes;
|
||||
|
||||
public:
|
||||
|
||||
/// Construct a new SceneRenderState.
|
||||
///
|
||||
/// @param sceneManager SceneManager rendered in this SceneRenderState.
|
||||
/// @param passType Type of rendering pass that the SceneRenderState is for.
|
||||
/// @param view The view that is being rendered
|
||||
/// @param renderPass The render pass which is being set up by this SceneRenderState. If NULL,
|
||||
/// then Scene::getDefaultRenderPass() is used.
|
||||
/// @param usePostEffect Whether PostFX are enabled in the rendering pass.
|
||||
SceneRenderState( SceneManager* sceneManager,
|
||||
ScenePassType passType,
|
||||
const SceneCameraState& view = SceneCameraState::fromGFX(),
|
||||
RenderPassManager* renderPass = NULL,
|
||||
bool usePostEffects = true );
|
||||
|
||||
~SceneRenderState();
|
||||
|
||||
/// Return the SceneManager that is being rendered in this SceneRenderState.
|
||||
SceneManager* getSceneManager() const { return mSceneManager; }
|
||||
|
||||
/// If true then bin based post effects are disabled
|
||||
/// during rendering with this scene state.
|
||||
bool usePostEffects() const { return mUsePostEffects; }
|
||||
void usePostEffects( bool value ) { mUsePostEffects = value; }
|
||||
|
||||
/// @name Culling
|
||||
/// @{
|
||||
|
||||
/// Return the culling state for the scene.
|
||||
const SceneCullingState& getCullingState() const { return mCullingState; }
|
||||
SceneCullingState& getCullingState() { return mCullingState; }
|
||||
|
||||
/// Returns the root frustum.
|
||||
const Frustum& getFrustum() const { return getCullingState().getFrustum(); }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Rendering
|
||||
/// @{
|
||||
|
||||
/// Get the AABB around the scene portion that we render.
|
||||
const Box3F& getRenderArea() const { return mRenderArea; }
|
||||
|
||||
/// Set the AABB of the space that should be rendered.
|
||||
void setRenderArea( const Box3F& area ) { mRenderArea = area; }
|
||||
|
||||
/// Batch the given objects to the render pass manager and then
|
||||
/// render the batched instances.
|
||||
///
|
||||
/// @param objects List of objects.
|
||||
/// @param numObjects Number of objects in @a objects.
|
||||
void renderObjects( SceneObject** objects, U32 numObjects );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Lighting
|
||||
/// @{
|
||||
|
||||
/// Return the ambient light color to use for rendering the scene.
|
||||
///
|
||||
/// At the moment, we only support a single global ambient color with which
|
||||
/// all objects in the scene are rendered. This is because when using
|
||||
/// Advanced Lighting, we are not resolving light contribution on a per-surface
|
||||
/// or per-object basis but rather do it globally by gathering light
|
||||
/// contribution to the whole scene and since the ambient factor is decided
|
||||
/// by the sun/vector light, it simply becomes a base light level onto which
|
||||
/// shadowing/lighting is blended based on the shadow maps of the sun/vector
|
||||
/// light.
|
||||
///
|
||||
/// @return The ambient light color for rendering.
|
||||
ColorF getAmbientLightColor() const { return mAmbientLightColor; }
|
||||
|
||||
/// Set the global ambient light color to render with.
|
||||
void setAmbientLightColor( const ColorF& color ) { mAmbientLightColor = color; }
|
||||
|
||||
/// If true then Advanced Lighting bin draws are disabled during rendering with
|
||||
/// this scene state.
|
||||
bool disableAdvancedLightingBins() const { return mDisableAdvancedLightingBins; }
|
||||
void disableAdvancedLightingBins(bool enabled) { mDisableAdvancedLightingBins = enabled; }
|
||||
|
||||
bool renderLightmappedMeshes() const { return mRenderLightmappedMeshes; }
|
||||
void renderLightmappedMeshes( bool enabled ) { mRenderLightmappedMeshes = enabled; }
|
||||
|
||||
bool renderNonLightmappedMeshes() const { return mRenderNonLightmappedMeshes; }
|
||||
void renderNonLightmappedMeshes( bool enabled ) { mRenderNonLightmappedMeshes = enabled; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Passes
|
||||
/// @{
|
||||
|
||||
/// Return the RenderPassManager that manages rendering objects batched
|
||||
/// for this SceneRenderState.
|
||||
RenderPassManager* getRenderPass() const { return mRenderPass; }
|
||||
|
||||
/// Returns the type of scene rendering pass that we're doing.
|
||||
ScenePassType getScenePassType() const { return mScenePassType; }
|
||||
|
||||
/// Returns true if this is a diffuse scene rendering pass.
|
||||
bool isDiffusePass() const { return mScenePassType == SPT_Diffuse; }
|
||||
|
||||
/// Returns true if this is a reflection scene rendering pass.
|
||||
bool isReflectPass() const { return mScenePassType == SPT_Reflect; }
|
||||
|
||||
/// Returns true if this is a shadow scene rendering pass.
|
||||
bool isShadowPass() const { return mScenePassType == SPT_Shadow; }
|
||||
|
||||
/// Returns true if this is not one of the other rendering passes.
|
||||
bool isOtherPass() const { return mScenePassType >= SPT_Other; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Transforms, projections, and viewports.
|
||||
/// @{
|
||||
|
||||
/// Return the screen-space viewport rectangle.
|
||||
const RectI& getViewport() const { return getCullingState().getCameraState().getViewport(); }
|
||||
|
||||
/// Return the world->view transform matrix.
|
||||
const MatrixF& getWorldViewMatrix() const;
|
||||
|
||||
/// Return the project transform matrix.
|
||||
const MatrixF& getProjectionMatrix() const;
|
||||
|
||||
/// Returns the actual camera position.
|
||||
/// @see getDiffuseCameraPosition
|
||||
const Point3F& getCameraPosition() const { return getCullingState().getCameraState().getViewPosition(); }
|
||||
|
||||
/// Returns the camera transform (view->world) this SceneRenderState is using.
|
||||
const MatrixF& getCameraTransform() const { return getCullingState().getCameraState().getViewWorldMatrix(); }
|
||||
|
||||
/// Returns the minimum distance something must be from the camera to not be culled.
|
||||
F32 getNearPlane() const { return getFrustum().getNearDist(); }
|
||||
|
||||
/// Returns the maximum distance something can be from the camera to not be culled.
|
||||
F32 getFarPlane() const { return getFrustum().getFarDist(); }
|
||||
|
||||
/// Returns the camera vector normalized to 1 / far distance.
|
||||
const Point3F& getVectorEye() const { return mVectorEye; }
|
||||
|
||||
/// Returns the possibly overloaded world to screen scale.
|
||||
/// @see projectRadius
|
||||
const Point2F& getWorldToScreenScale() const { return mWorldToScreenScale; }
|
||||
|
||||
/// Set a new world to screen scale to overload
|
||||
/// future screen metrics operations.
|
||||
void setWorldToScreenScale( const Point2F& scale ) { mWorldToScreenScale = scale; }
|
||||
|
||||
/// Returns the pixel size of the radius projected to the screen at a desired distance.
|
||||
///
|
||||
/// Internally this uses the stored world to screen scale and viewport extents. This
|
||||
/// allows the projection to be overloaded in special cases like when rendering shadows
|
||||
/// or reflections.
|
||||
///
|
||||
/// @see getWorldToScreenScale
|
||||
/// @see getViewportExtent
|
||||
F32 projectRadius( F32 dist, F32 radius ) const
|
||||
{
|
||||
// We fixup any negative or zero distance
|
||||
// so we don't get a divide by zero.
|
||||
dist = dist > 0.0f ? dist : 0.001f;
|
||||
return ( radius / dist ) * mWorldToScreenScale.y;
|
||||
}
|
||||
|
||||
/// Returns the camera position used during the diffuse rendering pass which may be different
|
||||
/// from the actual camera position.
|
||||
///
|
||||
/// This is useful when doing level of detail calculations that need to be relative to the
|
||||
/// diffuse pass.
|
||||
///
|
||||
/// @see getCameraPosition
|
||||
Point3F getDiffuseCameraPosition() const { return mDiffuseCameraTransform.getPosition(); }
|
||||
const MatrixF& getDiffuseCameraTransform() const { return mDiffuseCameraTransform; }
|
||||
|
||||
/// Set a new diffuse camera transform.
|
||||
/// @see getDiffuseCameraTransform
|
||||
void setDiffuseCameraTransform( const MatrixF &mat ) { mDiffuseCameraTransform = mat; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Material Overrides
|
||||
/// @{
|
||||
|
||||
/// When performing a special render pass like shadows this
|
||||
/// returns a specialized override material. It can return
|
||||
/// NULL if the override wants to disable rendering. If
|
||||
/// there is no override in place then the input material is
|
||||
/// returned unaltered.
|
||||
BaseMatInstance* getOverrideMaterial( BaseMatInstance* matInst ) const
|
||||
{
|
||||
if ( !matInst || mMatDelegate.empty() )
|
||||
return matInst;
|
||||
|
||||
return mMatDelegate( matInst );
|
||||
}
|
||||
|
||||
/// Returns the optional material override delegate which is
|
||||
/// used during some special render passes.
|
||||
/// @see getOverrideMaterial
|
||||
MatDelegate& getMaterialDelegate() { return mMatDelegate; }
|
||||
const MatDelegate& getMaterialDelegate() const { return mMatDelegate; }
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // _SCENERENDERSTATE_H_
|
||||
206
Engine/source/scene/sceneSpace.cpp
Normal file
206
Engine/source/scene/sceneSpace.cpp
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/sceneSpace.h"
|
||||
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "math/mOrientedBox.h"
|
||||
#include "gfx/primBuilder.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "materials/materialDefinition.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneSpace::SceneSpace()
|
||||
: mEditorRenderMaterial( NULL )
|
||||
{
|
||||
mNetFlags.set( Ghostable | ScopeAlways );
|
||||
mTypeMask |= StaticObjectType;
|
||||
|
||||
// Except when their rendering is otherwise suppressed, we do not want
|
||||
// spaces to get culled away when in the editor.
|
||||
mObjectFlags |= DisableCullingInEditorFlag;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneSpace::~SceneSpace()
|
||||
{
|
||||
SAFE_DELETE( mEditorRenderMaterial );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SceneSpace::onAdd()
|
||||
{
|
||||
if( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
addToScene();
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSpace::onRemove()
|
||||
{
|
||||
removeFromScene();
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSpace::setTransform(const MatrixF & mat)
|
||||
{
|
||||
Parent::setTransform( mat );
|
||||
|
||||
if( isServerObject() )
|
||||
setMaskBits( TransformMask );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSpace::onEditorEnable()
|
||||
{
|
||||
// If we haven't created a material for editor rendering yet,
|
||||
// try so now.
|
||||
|
||||
if( isClientObject() && !mEditorRenderMaterial )
|
||||
mEditorRenderMaterial = _createEditorRenderMaterial();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSpace::onEditorDisable()
|
||||
{
|
||||
SAFE_DELETE( mEditorRenderMaterial );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
BaseMatInstance* SceneSpace::_createEditorRenderMaterial()
|
||||
{
|
||||
String materialName = String::ToString( "Editor%sMaterial", getClassName() );
|
||||
|
||||
Material* material;
|
||||
if( !Sim::findObject( materialName, material ) )
|
||||
return NULL;
|
||||
|
||||
return material->createMatInstance();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSpace::prepRenderImage( SceneRenderState* state )
|
||||
{
|
||||
if( !gEditingMission )
|
||||
return;
|
||||
|
||||
if( !state->isDiffusePass() )
|
||||
return;
|
||||
|
||||
ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >();
|
||||
ri->renderDelegate.bind( this, &SceneSpace::_renderObject );
|
||||
ri->type = RenderPassManager::RIT_Editor;
|
||||
ri->defaultKey = 0;
|
||||
ri->defaultKey2 = 0;
|
||||
state->getRenderPass()->addInst( ri );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSpace::_renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat )
|
||||
{
|
||||
if( overrideMat )
|
||||
return;
|
||||
|
||||
if( !mEditorRenderMaterial )
|
||||
{
|
||||
// We have no material for rendering so just render
|
||||
// a plain box.
|
||||
|
||||
GFXTransformSaver saver;
|
||||
|
||||
MatrixF mat = getRenderTransform();
|
||||
mat.scale( getScale() );
|
||||
|
||||
GFX->multWorld( mat );
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setZReadWrite( true, false );
|
||||
desc.setBlend( true );
|
||||
desc.setCullMode( GFXCullNone );
|
||||
|
||||
GFXDrawUtil *drawer = GFX->getDrawUtil();
|
||||
drawer->drawCube( desc, mObjBox, _getDefaultEditorSolidColor() );
|
||||
|
||||
// Render black wireframe.
|
||||
|
||||
desc.setFillModeWireframe();
|
||||
drawer->drawCube( desc, mObjBox, _getDefaultEditorWireframeColor() );
|
||||
}
|
||||
else
|
||||
{
|
||||
//RDTODO
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 SceneSpace::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
|
||||
{
|
||||
U32 retMask = Parent::packUpdate( connection, mask, stream );
|
||||
|
||||
if( stream->writeFlag( mask & TransformMask ) )
|
||||
{
|
||||
mathWrite( *stream, mObjToWorld );
|
||||
mathWrite( *stream, mObjScale );
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneSpace::unpackUpdate( NetConnection* connection, BitStream* stream )
|
||||
{
|
||||
Parent::unpackUpdate( connection, stream );
|
||||
|
||||
if( stream->readFlag() ) // TransformMask
|
||||
{
|
||||
mathRead( *stream, &mObjToWorld );
|
||||
mathRead( *stream, &mObjScale );
|
||||
|
||||
setTransform( mObjToWorld );
|
||||
}
|
||||
}
|
||||
83
Engine/source/scene/sceneSpace.h
Normal file
83
Engine/source/scene/sceneSpace.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENESPACE_H_
|
||||
#define _SCENESPACE_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// Abstract base class for SceneObjects that define subspaces in a scene.
|
||||
///
|
||||
/// Use SceneObject::containsPoint to find out whether a given space contains a particular point.
|
||||
class SceneSpace : public SceneObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SceneObject Parent;
|
||||
|
||||
protected:
|
||||
|
||||
enum
|
||||
{
|
||||
TransformMask = Parent::NextFreeMask << 0, ///< Object transform has changed.
|
||||
NextFreeMask = Parent::NextFreeMask << 1,
|
||||
};
|
||||
|
||||
///
|
||||
BaseMatInstance* mEditorRenderMaterial;
|
||||
|
||||
///
|
||||
virtual BaseMatInstance* _createEditorRenderMaterial();
|
||||
|
||||
/// Render a visualization of the volume.
|
||||
virtual void _renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat );
|
||||
|
||||
///
|
||||
virtual ColorI _getDefaultEditorSolidColor() const { return ColorI( 255, 255, 255, 45 ); }
|
||||
virtual ColorI _getDefaultEditorWireframeColor() const { return ColorI::BLACK; }
|
||||
|
||||
public:
|
||||
|
||||
SceneSpace();
|
||||
~SceneSpace();
|
||||
|
||||
// SimObject.
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
|
||||
// SceneObject.
|
||||
virtual void setTransform( const MatrixF &mat );
|
||||
virtual void prepRenderImage( SceneRenderState* state );
|
||||
|
||||
// NetObject.
|
||||
virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream );
|
||||
virtual void unpackUpdate( NetConnection* connection, BitStream* stream );
|
||||
|
||||
// SimObject.
|
||||
virtual void onEditorEnable();
|
||||
virtual void onEditorDisable();
|
||||
};
|
||||
|
||||
#endif // !_SCENESPACE_H_
|
||||
128
Engine/source/scene/sceneTracker.cpp
Normal file
128
Engine/source/scene/sceneTracker.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "scene/sceneTracker.h"
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// SceneObjectLink.
|
||||
//=============================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneObjectLink::SceneObjectLink( SceneTracker* tracker, SceneObject* object )
|
||||
: mTracker( tracker ),
|
||||
mObject( object ),
|
||||
mNextLink( NULL ),
|
||||
mPrevLink( NULL )
|
||||
{
|
||||
if( object )
|
||||
{
|
||||
mNextLink = object->mSceneObjectLinks;
|
||||
if( mNextLink )
|
||||
mNextLink->mPrevLink = this;
|
||||
object->mSceneObjectLinks = this;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneObjectLink::~SceneObjectLink()
|
||||
{
|
||||
if( mObject )
|
||||
{
|
||||
// Unlink from SceneObject's tracker list.
|
||||
|
||||
if( mNextLink )
|
||||
mNextLink->mPrevLink = mPrevLink;
|
||||
if( mPrevLink )
|
||||
mPrevLink->mNextLink = mNextLink;
|
||||
else
|
||||
mObject->mSceneObjectLinks = mNextLink;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneObjectLink::update()
|
||||
{
|
||||
getTracker()->updateObject( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneObjectLink* SceneObjectLink::getLinkForTracker( SceneTracker* tracker, SceneObject* fromObject )
|
||||
{
|
||||
for( SceneObjectLink* link = fromObject->mSceneObjectLinks; link != NULL; link = link->getNextLink() )
|
||||
if( link->getTracker() == tracker )
|
||||
return link;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// SceneObjectLink.
|
||||
//=============================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneTracker::SceneTracker( bool isClientTracker, U32 typeMask )
|
||||
: mIsClientTracker( isClientTracker ),
|
||||
mObjectTypeMask( typeMask )
|
||||
{
|
||||
// Hook up to SceneObject add/remove notifications.
|
||||
|
||||
SceneObject::smSceneObjectAdd.notify( this, &SceneTracker::registerObject );
|
||||
SceneObject::smSceneObjectRemove.notify( this, &SceneTracker::unregisterObject );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SceneTracker::~SceneTracker()
|
||||
{
|
||||
SceneObject::smSceneObjectAdd.remove( this, &SceneTracker::registerObject );
|
||||
SceneObject::smSceneObjectRemove.remove( this, &SceneTracker::unregisterObject );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneTracker::init()
|
||||
{
|
||||
// Register existing scene graph objects.
|
||||
|
||||
SceneContainer* container;
|
||||
if( isClientTracker() )
|
||||
container = &gClientContainer;
|
||||
else
|
||||
container = &gServerContainer;
|
||||
|
||||
container->findObjects( getObjectTypeMask(),
|
||||
( SceneContainer::FindCallback ) &_containerFindCallback,
|
||||
this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SceneTracker::_containerFindCallback( SceneObject* object, SceneTracker* tracker )
|
||||
{
|
||||
tracker->registerObject( object );
|
||||
}
|
||||
163
Engine/source/scene/sceneTracker.h
Normal file
163
Engine/source/scene/sceneTracker.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SCENETRACKER_H_
|
||||
#define _SCENETRACKER_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// @file
|
||||
/// This file contains an abstract framework for tracking SceneObjects.
|
||||
|
||||
|
||||
class SceneTracker;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SceneObjectLink.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/// A SceneObjectLink represents the link between a SceneObject and a SceneTracker.
|
||||
class SceneObjectLink
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void Parent;
|
||||
friend class SceneTracker; // Administers our link fields.
|
||||
|
||||
protected:
|
||||
|
||||
/// SceneObject being linked to; always set and never changes.
|
||||
SceneObject* mObject;
|
||||
|
||||
/// The scene tracker to which this link belongs.
|
||||
SceneTracker* mTracker;
|
||||
|
||||
/// Next scope link on this SceneObject; NULL if last.
|
||||
SceneObjectLink* mNextLink;
|
||||
|
||||
/// Previous scope link on this SceneObject; NULL if first.
|
||||
SceneObjectLink* mPrevLink;
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
SceneObjectLink( SceneTracker* tracker, SceneObject* object );
|
||||
|
||||
virtual ~SceneObjectLink();
|
||||
|
||||
/// @return The SceneScopeTracker managing this link.
|
||||
SceneTracker* getTracker() const { return mTracker; }
|
||||
|
||||
/// @return The object being linked to.
|
||||
SceneObject* getObject() const { return mObject; }
|
||||
|
||||
/// @return The next link in this link chain.
|
||||
SceneObjectLink* getNextLink() const { return mNextLink; }
|
||||
|
||||
/// @return The previous link in this link chain.
|
||||
SceneObjectLink* getPrevLink() const { return mPrevLink; }
|
||||
|
||||
/// Notify the associated tracker that the transform state of the
|
||||
/// scene object represented by this link has changed.
|
||||
void update();
|
||||
|
||||
///
|
||||
static SceneObjectLink* getLinkForTracker( SceneTracker* tracker, SceneObject* fromObject );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SceneTracker.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/// A SceneTracker tracks SceneObjects.
|
||||
///
|
||||
/// This is an abstract base class.
|
||||
class SceneTracker
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void Parent;
|
||||
friend class SceneObjectLink; // SceneObjectLink::update() notifies us on SceneObject state changes.
|
||||
|
||||
protected:
|
||||
|
||||
/// If true, only client SceneObjects will be tracked; otherwise it's only server SceneObjects.
|
||||
bool mIsClientTracker;
|
||||
|
||||
/// Type mask that SceneObjects must match in order to be allowed to register.
|
||||
U32 mObjectTypeMask;
|
||||
|
||||
/// Return true if the given object qualifies for being managed by this SceneTracker.
|
||||
virtual bool _isTrackableObject( SceneObject* object ) const
|
||||
{
|
||||
return ( object->isClientObject() == mIsClientTracker
|
||||
&& ( object->getTypeMask() & getObjectTypeMask() ) );
|
||||
}
|
||||
|
||||
/// Callback used for the initial scan of objects in init().
|
||||
static void _containerFindCallback( SceneObject* object, SceneTracker* tracker );
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
SceneTracker( bool isClientTracker, U32 typeMask = 0xFFFFFFFF );
|
||||
|
||||
virtual ~SceneTracker();
|
||||
|
||||
/// Initialize the tracker from the current scene.
|
||||
virtual void init();
|
||||
|
||||
/// @return The type mask that must be matched by objects in order to be allowed to register.
|
||||
U32 getObjectTypeMask() const { return mObjectTypeMask; }
|
||||
|
||||
/// Set the type mask that objects must match in order to be allowed to register.
|
||||
void setObjectTypeMask( U32 typeMask ) { mObjectTypeMask = typeMask; }
|
||||
|
||||
/// @return True if this tracker only deals with client objects; false if only server objects.
|
||||
bool isClientTracker() const { return mIsClientTracker; }
|
||||
|
||||
/// Register a SceneObject for being tracked by this tracker.
|
||||
///
|
||||
/// Only objects that fit the tracker's client/server state and
|
||||
/// object type mask will actually get registered. For other objects,
|
||||
/// this is a NOP.
|
||||
///
|
||||
/// @param object Scene object.
|
||||
virtual void registerObject( SceneObject* object ) = 0;
|
||||
|
||||
/// Unregister the given object from the tracker.
|
||||
/// @param object Scene object.
|
||||
virtual void unregisterObject( SceneObject* object ) = 0;
|
||||
|
||||
/// Notify the tracker that the transform state of the given scene object has changed.
|
||||
/// @param object Scene object.
|
||||
virtual void updateObject( SceneObjectLink* object ) = 0;
|
||||
};
|
||||
|
||||
#endif // !_SCENETRACKER_H_
|
||||
327
Engine/source/scene/sgUtil.cpp
Normal file
327
Engine/source/scene/sgUtil.cpp
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "scene/sgUtil.h"
|
||||
#include "math/mRect.h"
|
||||
#include "math/mMatrix.h"
|
||||
#include "math/mPlane.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Static state for sgComputeNewFrustum
|
||||
//
|
||||
Point3F sgCamPoint;
|
||||
MatrixF sgWSToOSMatrix;
|
||||
MatrixF sgProjMatrix;
|
||||
PlaneF sgOSPlaneFar;
|
||||
PlaneF sgOSPlaneXMin;
|
||||
PlaneF sgOSPlaneXMax;
|
||||
PlaneF sgOSPlaneYMin;
|
||||
PlaneF sgOSPlaneYMax;
|
||||
|
||||
|
||||
void clipToPlane(Point3F* points, U32& rNumPoints, const PlaneF& rPlane)
|
||||
{
|
||||
S32 start = -1;
|
||||
for (U32 i = 0; i < rNumPoints; i++) {
|
||||
if (rPlane.whichSide(points[i]) == PlaneF::Front) {
|
||||
start = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing was in front of the plane...
|
||||
if (start == -1) {
|
||||
rNumPoints = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Point3F finalPoints[128];
|
||||
U32 numFinalPoints = 0;
|
||||
|
||||
U32 baseStart = start;
|
||||
U32 end = (start + 1) % rNumPoints;
|
||||
|
||||
while (end != baseStart) {
|
||||
const Point3F& rStartPoint = points[start];
|
||||
const Point3F& rEndPoint = points[end];
|
||||
|
||||
PlaneF::Side fSide = rPlane.whichSide(rStartPoint);
|
||||
PlaneF::Side eSide = rPlane.whichSide(rEndPoint);
|
||||
|
||||
S32 code = fSide * 3 + eSide;
|
||||
switch (code) {
|
||||
case 4: // f f
|
||||
case 3: // f o
|
||||
case 1: // o f
|
||||
case 0: // o o
|
||||
// No Clipping required
|
||||
finalPoints[numFinalPoints++] = points[start];
|
||||
start = end;
|
||||
end = (end + 1) % rNumPoints;
|
||||
break;
|
||||
|
||||
|
||||
case 2: { // f b
|
||||
// In this case, we emit the front point, Insert the intersection,
|
||||
// and advancing to point to first point that is in front or on...
|
||||
//
|
||||
finalPoints[numFinalPoints++] = points[start];
|
||||
|
||||
Point3F vector = rEndPoint - rStartPoint;
|
||||
F32 t = -(rPlane.distToPlane(rStartPoint) / mDot(rPlane, vector));
|
||||
|
||||
Point3F intersection = rStartPoint + (vector * t);
|
||||
finalPoints[numFinalPoints++] = intersection;
|
||||
|
||||
U32 endSeek = (end + 1) % rNumPoints;
|
||||
while (rPlane.whichSide(points[endSeek]) == PlaneF::Back)
|
||||
endSeek = (endSeek + 1) % rNumPoints;
|
||||
|
||||
end = endSeek;
|
||||
start = (end + (rNumPoints - 1)) % rNumPoints;
|
||||
|
||||
const Point3F& rNewStartPoint = points[start];
|
||||
const Point3F& rNewEndPoint = points[end];
|
||||
|
||||
vector = rNewEndPoint - rNewStartPoint;
|
||||
t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector));
|
||||
|
||||
intersection = rNewStartPoint + (vector * t);
|
||||
points[start] = intersection;
|
||||
}
|
||||
break;
|
||||
|
||||
case -1: {// o b
|
||||
// In this case, we emit the front point, and advance to point to first
|
||||
// point that is in front or on...
|
||||
//
|
||||
finalPoints[numFinalPoints++] = points[start];
|
||||
|
||||
U32 endSeek = (end + 1) % rNumPoints;
|
||||
while (rPlane.whichSide(points[endSeek]) == PlaneF::Back)
|
||||
endSeek = (endSeek + 1) % rNumPoints;
|
||||
|
||||
end = endSeek;
|
||||
start = (end + (rNumPoints - 1)) % rNumPoints;
|
||||
|
||||
const Point3F& rNewStartPoint = points[start];
|
||||
const Point3F& rNewEndPoint = points[end];
|
||||
|
||||
Point3F vector = rNewEndPoint - rNewStartPoint;
|
||||
F32 t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector));
|
||||
|
||||
Point3F intersection = rNewStartPoint + (vector * t);
|
||||
points[start] = intersection;
|
||||
}
|
||||
break;
|
||||
|
||||
case -2: // b f
|
||||
case -3: // b o
|
||||
case -4: // b b
|
||||
// In the algorithm used here, this should never happen...
|
||||
AssertISV(false, "SGUtil::clipToPlane: error in polygon clipper");
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertFatal(false, "SGUtil::clipToPlane: bad outcode");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Emit the last point.
|
||||
finalPoints[numFinalPoints++] = points[start];
|
||||
AssertFatal(numFinalPoints >= 3, avar("Error, this shouldn't happen! Invalid winding in clipToPlane: %d", numFinalPoints));
|
||||
|
||||
// Copy the new rWinding, and we're set!
|
||||
//
|
||||
dMemcpy(points, finalPoints, numFinalPoints * sizeof(Point3F));
|
||||
rNumPoints = numFinalPoints;
|
||||
AssertISV(rNumPoints <= 128, "MaxWindingPoints exceeded in scenegraph. Fatal error.");
|
||||
}
|
||||
|
||||
|
||||
void fixupViewport(const F64* oldFrustum,
|
||||
const RectI& oldViewport,
|
||||
F64* newFrustum,
|
||||
RectI& newViewport)
|
||||
{
|
||||
F64 widthV = newFrustum[1] - newFrustum[0];
|
||||
F64 heightV = newFrustum[3] - newFrustum[2];
|
||||
|
||||
F64 fx0 = (newFrustum[0] - oldFrustum[0]) / (oldFrustum[1] - oldFrustum[0]);
|
||||
F64 fx1 = (oldFrustum[1] - newFrustum[1]) / (oldFrustum[1] - oldFrustum[0]);
|
||||
|
||||
F64 dV0 = F64(oldViewport.point.x) + fx0 * F64(oldViewport.extent.x);
|
||||
F64 dV1 = F64(oldViewport.point.x +
|
||||
oldViewport.extent.x) - fx1 * F64(oldViewport.extent.x);
|
||||
|
||||
F64 fdV0 = mFloor(dV0);
|
||||
F64 cdV1 = mCeil(dV1);
|
||||
|
||||
F64 new0 = newFrustum[0] - ((dV0 - fdV0) * (widthV / F64(oldViewport.extent.x)));
|
||||
F64 new1 = newFrustum[1] + ((cdV1 - dV1) * (widthV / F64(oldViewport.extent.x)));
|
||||
|
||||
newFrustum[0] = new0;
|
||||
newFrustum[1] = new1;
|
||||
|
||||
newViewport.point.x = S32(fdV0);
|
||||
newViewport.extent.x = S32(cdV1) - newViewport.point.x;
|
||||
|
||||
F64 fy0 = (oldFrustum[3] - newFrustum[3]) / (oldFrustum[3] - oldFrustum[2]);
|
||||
F64 fy1 = (newFrustum[2] - oldFrustum[2]) / (oldFrustum[3] - oldFrustum[2]);
|
||||
|
||||
dV0 = F64(oldViewport.point.y) + fy0 * F64(oldViewport.extent.y);
|
||||
dV1 = F64(oldViewport.point.y + oldViewport.extent.y) - fy1 * F64(oldViewport.extent.y);
|
||||
fdV0 = mFloor(dV0);
|
||||
cdV1 = mCeil(dV1);
|
||||
|
||||
new0 = newFrustum[2] - ((cdV1 - dV1) * (heightV / F64(oldViewport.extent.y)));
|
||||
new1 = newFrustum[3] + ((dV0 - fdV0) * (heightV / F64(oldViewport.extent.y)));
|
||||
newFrustum[2] = new0;
|
||||
newFrustum[3] = new1;
|
||||
|
||||
newViewport.point.y = S32(fdV0);
|
||||
newViewport.extent.y = S32(cdV1) - newViewport.point.y;
|
||||
}
|
||||
|
||||
bool projectClipAndBoundWinding(const SGWinding& rWinding, F64* pResult)
|
||||
{
|
||||
AssertFatal(rWinding.numPoints >= 3, "Error, that's not a winding!");
|
||||
|
||||
static Point3F windingPoints[128];
|
||||
U32 i;
|
||||
for (i = 0; i < rWinding.numPoints; i++)
|
||||
windingPoints[i] = rWinding.points[i];
|
||||
U32 numPoints = rWinding.numPoints;
|
||||
|
||||
clipToPlane(windingPoints, numPoints, sgOSPlaneFar);
|
||||
if (numPoints != 0)
|
||||
clipToPlane(windingPoints, numPoints, sgOSPlaneXMin);
|
||||
if (numPoints != 0)
|
||||
clipToPlane(windingPoints, numPoints, sgOSPlaneXMax);
|
||||
if (numPoints != 0)
|
||||
clipToPlane(windingPoints, numPoints, sgOSPlaneYMin);
|
||||
if (numPoints != 0)
|
||||
clipToPlane(windingPoints, numPoints, sgOSPlaneYMax);
|
||||
|
||||
if (numPoints == 0)
|
||||
return false;
|
||||
|
||||
Point4F projPoint;
|
||||
for (i = 0; i < numPoints; i++) {
|
||||
projPoint.set(windingPoints[i].x, windingPoints[i].y, windingPoints[i].z, 1.0);
|
||||
sgProjMatrix.mul(projPoint);
|
||||
|
||||
AssertFatal(projPoint.w != 0.0, "Error, that's bad! (Point projected with non-zero w.)");
|
||||
projPoint.x /= projPoint.w;
|
||||
projPoint.y /= projPoint.w;
|
||||
|
||||
if (projPoint.x < pResult[0])
|
||||
pResult[0] = projPoint.x;
|
||||
if (projPoint.x > pResult[1])
|
||||
pResult[1] = projPoint.x;
|
||||
if (projPoint.y < pResult[2])
|
||||
pResult[2] = projPoint.y;
|
||||
if (projPoint.y > pResult[3])
|
||||
pResult[3] = projPoint.y;
|
||||
}
|
||||
|
||||
if (pResult[0] < -1.0f) pResult[0] = -1.0f;
|
||||
if (pResult[2] < -1.0f) pResult[2] = -1.0f;
|
||||
if (pResult[1] > 1.0f) pResult[1] = 1.0f;
|
||||
if (pResult[3] > 1.0f) pResult[3] = 1.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace { }
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool sgComputeNewFrustum(const Frustum &oldFrustum,
|
||||
const F64 nearPlane,
|
||||
const F64 farPlane,
|
||||
const RectI& oldViewport,
|
||||
const SGWinding* windings,
|
||||
const U32 numWindings,
|
||||
const MatrixF& modelview,
|
||||
F64 *newFrustum,
|
||||
RectI& newViewport,
|
||||
const bool flippedMatrix)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void sgComputeOSFrustumPlanes(const F64 frustumParameters[6],
|
||||
const MatrixF& worldSpaceToObjectSpace,
|
||||
const Point3F& wsCamPoint,
|
||||
PlaneF& outFarPlane,
|
||||
PlaneF& outXMinPlane,
|
||||
PlaneF& outXMaxPlane,
|
||||
PlaneF& outYMinPlane,
|
||||
PlaneF& outYMaxPlane)
|
||||
{
|
||||
// Create the object space clipping planes...
|
||||
Point3F ul(frustumParameters[0] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[3] * 1000.0);
|
||||
Point3F ur(frustumParameters[1] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[3] * 1000.0);
|
||||
Point3F ll(frustumParameters[0] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[2] * 1000.0);
|
||||
Point3F lr(frustumParameters[1] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[2] * 1000.0);
|
||||
Point3F farPlane(0, frustumParameters[5], 0);
|
||||
|
||||
worldSpaceToObjectSpace.mulP(ul);
|
||||
worldSpaceToObjectSpace.mulP(ur);
|
||||
worldSpaceToObjectSpace.mulP(ll);
|
||||
worldSpaceToObjectSpace.mulP(lr);
|
||||
worldSpaceToObjectSpace.mulP(farPlane);
|
||||
|
||||
outFarPlane.set(farPlane, wsCamPoint - farPlane);
|
||||
outXMinPlane.set(wsCamPoint, ul, ll);
|
||||
outXMaxPlane.set(wsCamPoint, lr, ur);
|
||||
outYMinPlane.set(wsCamPoint, ur, ul);
|
||||
outYMaxPlane.set(wsCamPoint, ll, lr);
|
||||
}
|
||||
|
||||
// MM/JF: Added for mirrorSubObject fix.
|
||||
void sgOrientClipPlanes(
|
||||
PlaneF * planes,
|
||||
const Point3F & camPos,
|
||||
const Point3F & leftUp,
|
||||
const Point3F & leftDown,
|
||||
const Point3F & rightUp,
|
||||
const Point3F & rightDown)
|
||||
{
|
||||
AssertFatal(planes, "orientClipPlanes: NULL planes ptr");
|
||||
planes[0].set(camPos, leftUp, leftDown);
|
||||
planes[1].set(camPos, rightUp, leftUp);
|
||||
planes[2].set(camPos, rightDown, rightUp);
|
||||
planes[3].set(camPos, leftDown, rightDown);
|
||||
planes[4].set(leftUp, rightUp, rightDown);
|
||||
|
||||
// clip-planes through mirror portal are inverted
|
||||
PlaneF plane(leftUp, rightUp, rightDown);
|
||||
if(plane.whichSide(camPos) == PlaneF::Back)
|
||||
for(U32 i = 0; i < 5; i++)
|
||||
planes[i].invert();
|
||||
}
|
||||
75
Engine/source/scene/sgUtil.h
Normal file
75
Engine/source/scene/sgUtil.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SGUTIL_H_
|
||||
#define _SGUTIL_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _MPOINT3_H_
|
||||
#include "math/mPoint3.h"
|
||||
#endif
|
||||
|
||||
class Frustum;
|
||||
class RectI;
|
||||
class MatrixF;
|
||||
class PlaneF;
|
||||
|
||||
struct SGWinding
|
||||
{
|
||||
Point3F points[32];
|
||||
U32 numPoints;
|
||||
};
|
||||
|
||||
bool sgComputeNewFrustum(const Frustum &oldFrustum,
|
||||
const F64 nearPlane,
|
||||
const F64 farPlane,
|
||||
const RectI& oldViewport,
|
||||
const SGWinding* windings,
|
||||
const U32 numWindings,
|
||||
const MatrixF& modelview,
|
||||
F64 *newFrustum,
|
||||
RectI& newViewport,
|
||||
const bool flippedMatrix);
|
||||
|
||||
/// Compute frustrum planes.
|
||||
///
|
||||
/// Frustum parameters are:
|
||||
/// - [0] = left
|
||||
/// - [1] = right
|
||||
/// - [2] = top
|
||||
/// - [3] = bottom
|
||||
/// - [4] = near
|
||||
/// - [5] = far
|
||||
void sgComputeOSFrustumPlanes(const F64 frustumParameters[6],
|
||||
const MatrixF& worldSpaceToObjectSpace,
|
||||
const Point3F& wsCamPoint,
|
||||
PlaneF& outFarPlane,
|
||||
PlaneF& outXMinPlane,
|
||||
PlaneF& outXMaxPlane,
|
||||
PlaneF& outYMinPlane,
|
||||
PlaneF& outYMaxPlane);
|
||||
|
||||
void sgOrientClipPlanes(PlaneF * planes, const Point3F & camPos, const Point3F & leftUp, const Point3F & leftDown, const Point3F & rightUp, const Point3F & rightDown);
|
||||
|
||||
#endif // _H_SGUTIL_
|
||||
526
Engine/source/scene/simPath.cpp
Normal file
526
Engine/source/scene/simPath.cpp
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/simPath.h"
|
||||
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "scene/pathManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-------------------------------------- Console functions and cmp funcs
|
||||
//
|
||||
DefineEngineFunction(pathOnMissionLoadDone, void, (),,
|
||||
"@brief Load all Path information from the mission.\n\n"
|
||||
|
||||
"This function is usually called from the loadMissionStage2() server-side function "
|
||||
"after the mission file has loaded. Internally it places all Paths into the server's "
|
||||
"PathManager. From this point the Paths are ready for transmission to the clients.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Inform the engine to load all Path information from the mission.\n"
|
||||
"pathOnMissionLoadDone();\n\n"
|
||||
"@endtsexample\n"
|
||||
|
||||
"@see NetConnection::transmitPaths()\n"
|
||||
"@see NetConnection::clearPaths()\n"
|
||||
"@see Path\n"
|
||||
|
||||
"@ingroup Networking")
|
||||
{
|
||||
// Need to load subobjects for all loaded interiors...
|
||||
SimGroup* pMissionGroup = dynamic_cast<SimGroup*>(Sim::findObject("MissionGroup"));
|
||||
AssertFatal(pMissionGroup != NULL, "Error, mission done loading and no mission group?");
|
||||
|
||||
U32 currStart = 0;
|
||||
U32 currEnd = 1;
|
||||
Vector<SimGroup*> groups;
|
||||
groups.push_back(pMissionGroup);
|
||||
|
||||
while (true) {
|
||||
for (U32 i = currStart; i < currEnd; i++) {
|
||||
for (SimGroup::iterator itr = groups[i]->begin(); itr != groups[i]->end(); itr++) {
|
||||
if (dynamic_cast<SimGroup*>(*itr) != NULL)
|
||||
groups.push_back(static_cast<SimGroup*>(*itr));
|
||||
}
|
||||
}
|
||||
|
||||
if (groups.size() == currEnd) {
|
||||
break;
|
||||
} else {
|
||||
currStart = currEnd;
|
||||
currEnd = groups.size();
|
||||
}
|
||||
}
|
||||
|
||||
for (U32 i = 0; i < groups.size(); i++) {
|
||||
SimPath::Path* pPath = dynamic_cast<SimPath::Path*>(groups[i]);
|
||||
if (pPath)
|
||||
pPath->updatePath();
|
||||
}
|
||||
}
|
||||
|
||||
S32 FN_CDECL cmpPathObject(const void* p1, const void* p2)
|
||||
{
|
||||
SimObject* o1 = *((SimObject**)p1);
|
||||
SimObject* o2 = *((SimObject**)p2);
|
||||
|
||||
Marker* m1 = dynamic_cast<Marker*>(o1);
|
||||
Marker* m2 = dynamic_cast<Marker*>(o2);
|
||||
|
||||
if (m1 == NULL && m2 == NULL)
|
||||
return 0;
|
||||
else if (m1 != NULL && m2 == NULL)
|
||||
return 1;
|
||||
else if (m1 == NULL && m2 != NULL)
|
||||
return -1;
|
||||
else {
|
||||
// Both markers...
|
||||
return S32(m1->mSeqNum) - S32(m2->mSeqNum);
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleDocClass(SimPath::Path,
|
||||
"@brief A spline along which various objects can move along. The spline object acts like a container for Marker objects, which make\n"
|
||||
"up the joints, or knots, along the path. Paths can be assigned a speed, can be looping or non-looping. Each of a path's markers can be\n"
|
||||
"one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
|
||||
|
||||
"@tsexample\n"
|
||||
"new path()\n"
|
||||
" {\n"
|
||||
" isLooping = \"1\";\n"
|
||||
"\n"
|
||||
" new Marker()\n"
|
||||
" {\n"
|
||||
" seqNum = \"0\";\n"
|
||||
" type = \"Normal\";\n"
|
||||
" msToNext = \"1000\";\n"
|
||||
" smoothingType = \"Spline\";\n"
|
||||
" position = \"-0.054708 -35.0612 234.802\";\n"
|
||||
" rotation = \"1 0 0 0\";\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" };\n"
|
||||
"@endtsexample\n"
|
||||
|
||||
"@see Marker\n"
|
||||
"@see NetConnection::transmitPaths()\n"
|
||||
"@see NetConnection::clearPaths()\n"
|
||||
"@see Path\n"
|
||||
|
||||
"@ingroup enviroMisc\n"
|
||||
);
|
||||
|
||||
namespace SimPath
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-------------------------------------- Implementation
|
||||
//
|
||||
IMPLEMENT_CONOBJECT(Path);
|
||||
|
||||
Path::Path()
|
||||
{
|
||||
mPathIndex = NoPathIndex;
|
||||
mIsLooping = true;
|
||||
}
|
||||
|
||||
Path::~Path()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void Path::initPersistFields()
|
||||
{
|
||||
addField("isLooping", TypeBool, Offset(mIsLooping, Path), "If this is true, the loop is closed, otherwise it is open.\n");
|
||||
|
||||
Parent::initPersistFields();
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool Path::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Path::onRemove()
|
||||
{
|
||||
//
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Sort the markers objects into sequence order
|
||||
void Path::sortMarkers()
|
||||
{
|
||||
dQsort(objectList.address(), objectList.size(), sizeof(SimObject*), cmpPathObject);
|
||||
}
|
||||
|
||||
void Path::updatePath()
|
||||
{
|
||||
// If we need to, allocate a path index from the manager
|
||||
if (mPathIndex == NoPathIndex)
|
||||
mPathIndex = gServerPathManager->allocatePathId();
|
||||
|
||||
sortMarkers();
|
||||
|
||||
Vector<Point3F> positions;
|
||||
Vector<QuatF> rotations;
|
||||
Vector<U32> times;
|
||||
Vector<U32> smoothingTypes;
|
||||
|
||||
for (iterator itr = begin(); itr != end(); itr++)
|
||||
{
|
||||
Marker* pMarker = dynamic_cast<Marker*>(*itr);
|
||||
if (pMarker != NULL)
|
||||
{
|
||||
Point3F pos;
|
||||
pMarker->getTransform().getColumn(3, &pos);
|
||||
positions.push_back(pos);
|
||||
|
||||
QuatF rot;
|
||||
rot.set(pMarker->getTransform());
|
||||
rotations.push_back(rot);
|
||||
|
||||
times.push_back(pMarker->mMSToNext);
|
||||
smoothingTypes.push_back(pMarker->mSmoothingType);
|
||||
}
|
||||
}
|
||||
|
||||
// DMMTODO: Looping paths.
|
||||
gServerPathManager->updatePath(mPathIndex, positions, rotations, times, smoothingTypes);
|
||||
}
|
||||
|
||||
void Path::addObject(SimObject* obj)
|
||||
{
|
||||
Parent::addObject(obj);
|
||||
|
||||
if (mPathIndex != NoPathIndex) {
|
||||
// If we're already finished, and this object is a marker, then we need to
|
||||
// update our path information...
|
||||
if (dynamic_cast<Marker*>(obj) != NULL)
|
||||
updatePath();
|
||||
}
|
||||
}
|
||||
|
||||
void Path::removeObject(SimObject* obj)
|
||||
{
|
||||
bool recalc = dynamic_cast<Marker*>(obj) != NULL;
|
||||
|
||||
Parent::removeObject(obj);
|
||||
|
||||
if (mPathIndex != NoPathIndex && recalc == true)
|
||||
updatePath();
|
||||
}
|
||||
|
||||
DefineEngineMethod( Path, getPathId, S32, (),,
|
||||
"@brief Returns the PathID (not the object ID) of this path.\n\n"
|
||||
"@return PathID (not the object ID) of this path.\n"
|
||||
"@tsexample\n"
|
||||
"// Acquire the PathID of this path object.\n"
|
||||
"%pathID = %thisPath.getPathId();\n\n"
|
||||
"@endtsexample\n\n"
|
||||
)
|
||||
{
|
||||
Path *path = static_cast<Path *>(object);
|
||||
return path->getPathIndex();
|
||||
}
|
||||
|
||||
} // Namespace
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
GFXStateBlockRef Marker::smStateBlock;
|
||||
GFXVertexBufferHandle<GFXVertexPC> Marker::smVertexBuffer;
|
||||
GFXPrimitiveBufferHandle Marker::smPrimitiveBuffer;
|
||||
|
||||
static Point3F wedgePoints[4] = {
|
||||
Point3F(-1, -1, 0),
|
||||
Point3F( 0, 1, 0),
|
||||
Point3F( 1, -1, 0),
|
||||
Point3F( 0,-.75, .5),
|
||||
};
|
||||
|
||||
void Marker::initGFXResources()
|
||||
{
|
||||
if(smVertexBuffer != NULL)
|
||||
return;
|
||||
|
||||
GFXStateBlockDesc d;
|
||||
d.cullDefined = true;
|
||||
d.cullMode = GFXCullNone;
|
||||
|
||||
smStateBlock = GFX->createStateBlock(d);
|
||||
|
||||
smVertexBuffer.set(GFX, 4, GFXBufferTypeStatic);
|
||||
GFXVertexPC* verts = smVertexBuffer.lock();
|
||||
verts[0].point = wedgePoints[0] * 0.25f;
|
||||
verts[1].point = wedgePoints[1] * 0.25f;
|
||||
verts[2].point = wedgePoints[2] * 0.25f;
|
||||
verts[3].point = wedgePoints[3] * 0.25f;
|
||||
verts[0].color = verts[1].color = verts[2].color = verts[3].color = GFXVertexColor(ColorI(0, 255, 0, 255));
|
||||
smVertexBuffer.unlock();
|
||||
|
||||
smPrimitiveBuffer.set(GFX, 24, 12, GFXBufferTypeStatic);
|
||||
U16* prims;
|
||||
smPrimitiveBuffer.lock(&prims);
|
||||
prims[0] = 0;
|
||||
prims[1] = 3;
|
||||
prims[2] = 3;
|
||||
prims[3] = 1;
|
||||
prims[4] = 1;
|
||||
prims[5] = 0;
|
||||
|
||||
prims[6] = 3;
|
||||
prims[7] = 1;
|
||||
prims[8] = 1;
|
||||
prims[9] = 2;
|
||||
prims[10] = 2;
|
||||
prims[11] = 3;
|
||||
|
||||
prims[12] = 0;
|
||||
prims[13] = 3;
|
||||
prims[14] = 3;
|
||||
prims[15] = 2;
|
||||
prims[16] = 2;
|
||||
prims[17] = 0;
|
||||
|
||||
prims[18] = 0;
|
||||
prims[19] = 2;
|
||||
prims[20] = 2;
|
||||
prims[21] = 1;
|
||||
prims[22] = 1;
|
||||
prims[23] = 0;
|
||||
smPrimitiveBuffer.unlock();
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(Marker);
|
||||
|
||||
ConsoleDocClass( Marker,
|
||||
"@brief A single joint, or knot, along a path. Should be stored inside a Path container object. A path markers can be\n"
|
||||
"one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
|
||||
|
||||
"@tsexample\n"
|
||||
"new path()\n"
|
||||
" {\n"
|
||||
" isLooping = \"1\";\n"
|
||||
"\n"
|
||||
" new Marker()\n"
|
||||
" {\n"
|
||||
" seqNum = \"0\";\n"
|
||||
" type = \"Normal\";\n"
|
||||
" msToNext = \"1000\";\n"
|
||||
" smoothingType = \"Spline\";\n"
|
||||
" position = \"-0.054708 -35.0612 234.802\";\n"
|
||||
" rotation = \"1 0 0 0\";\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" };\n"
|
||||
"@endtsexample\n"
|
||||
"@see Path\n"
|
||||
"@ingroup enviroMisc\n"
|
||||
);
|
||||
|
||||
Marker::Marker()
|
||||
{
|
||||
// Not ghostable unless we're editing...
|
||||
mNetFlags.clear(Ghostable);
|
||||
|
||||
mTypeMask |= MarkerObjectType;
|
||||
|
||||
mSeqNum = 0;
|
||||
mSmoothingType = SmoothingTypeLinear;
|
||||
mMSToNext = 1000;
|
||||
mSmoothingType = SmoothingTypeSpline;
|
||||
mKnotType = KnotTypeNormal;
|
||||
}
|
||||
|
||||
Marker::~Marker()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ImplementEnumType( MarkerSmoothingType,
|
||||
"The type of smoothing this marker will have for pathed objects.\n"
|
||||
"@ingroup enviroMisc\n\n")
|
||||
{ Marker::SmoothingTypeSpline , "Spline", "Marker will cause the movements of the pathed object to be smooth.\n" },
|
||||
{ Marker::SmoothingTypeLinear , "Linear", "Marker will have no smoothing effect.\n" },
|
||||
//{ Marker::SmoothingTypeAccelerate , "Accelerate" },
|
||||
EndImplementEnumType;
|
||||
|
||||
ImplementEnumType( MarkerKnotType,
|
||||
"The type of knot that this marker will be.\n"
|
||||
"@ingroup enviroMisc\n\n")
|
||||
{ Marker::KnotTypeNormal , "Normal", "Knot will have a smooth camera translation/rotation effect.\n" },
|
||||
{ Marker::KnotTypePositionOnly, "Position Only", "Will do the same for translations, leaving rotation un-touched.\n" },
|
||||
{ Marker::KnotTypeKink, "Kink", "The rotation will take effect immediately for an abrupt rotation change.\n" },
|
||||
EndImplementEnumType;
|
||||
|
||||
void Marker::initPersistFields()
|
||||
{
|
||||
addGroup( "Misc" );
|
||||
addField("seqNum", TypeS32, Offset(mSeqNum, Marker), "Marker position in sequence of markers on this path.\n");
|
||||
addField("type", TYPEID< KnotType >(), Offset(mKnotType, Marker), "Type of this marker/knot. A \"normal\" knot will have a smooth camera translation/rotation effect.\n\"Position Only\"<EFBFBD>will do the same for translations, leaving rotation un-touched.\nLastly, a \"Kink\" means the rotation will take effect immediately for an abrupt rotation change.\n");
|
||||
addField("msToNext", TypeS32, Offset(mMSToNext, Marker), "Milliseconds to next marker in sequence.\n");
|
||||
addField("smoothingType", TYPEID< SmoothingType >(), Offset(mSmoothingType, Marker), "Path smoothing at this marker/knot. \"Linear\"<EFBFBD>means no smoothing, while \"Spline\" means to smooth.\n");
|
||||
endGroup("Misc");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool Marker::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
mObjBox = Box3F(Point3F(-.25, -.25, -.25), Point3F(.25, .25, .25));
|
||||
resetWorldBox();
|
||||
|
||||
if(gEditingMission)
|
||||
onEditorEnable();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Marker::onRemove()
|
||||
{
|
||||
if(gEditingMission)
|
||||
onEditorDisable();
|
||||
|
||||
Parent::onRemove();
|
||||
|
||||
smVertexBuffer = NULL;
|
||||
smPrimitiveBuffer = NULL;
|
||||
}
|
||||
|
||||
void Marker::onGroupAdd()
|
||||
{
|
||||
mSeqNum = getGroup()->size() - 1;
|
||||
}
|
||||
|
||||
|
||||
/// Enable scoping so we can see this thing on the client.
|
||||
void Marker::onEditorEnable()
|
||||
{
|
||||
mNetFlags.set(Ghostable);
|
||||
setScopeAlways();
|
||||
addToScene();
|
||||
}
|
||||
|
||||
/// Disable scoping so we can see this thing on the client
|
||||
void Marker::onEditorDisable()
|
||||
{
|
||||
removeFromScene();
|
||||
mNetFlags.clear(Ghostable);
|
||||
clearScopeAlways();
|
||||
}
|
||||
|
||||
|
||||
/// Tell our parent that this Path has been modified
|
||||
void Marker::inspectPostApply()
|
||||
{
|
||||
SimPath::Path *path = dynamic_cast<SimPath::Path*>(getGroup());
|
||||
if (path)
|
||||
path->updatePath();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void Marker::prepRenderImage( SceneRenderState* state )
|
||||
{
|
||||
// This should be sufficient for most objects that don't manage zones, and
|
||||
// don't need to return a specialized RenderImage...
|
||||
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
|
||||
ri->renderDelegate.bind( this, &Marker::renderObject );
|
||||
ri->type = RenderPassManager::RIT_Editor;
|
||||
state->getRenderPass()->addInst(ri);
|
||||
}
|
||||
|
||||
|
||||
void Marker::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
|
||||
{
|
||||
initGFXResources();
|
||||
|
||||
for(U32 i = 0; i < GFX->getNumSamplers(); i++)
|
||||
GFX->setTexture(i, NULL);
|
||||
GFXTransformSaver saver;
|
||||
MatrixF mat = getRenderTransform();
|
||||
mat.scale(mObjScale);
|
||||
GFX->multWorld(mat);
|
||||
|
||||
GFX->setStateBlock(smStateBlock);
|
||||
GFX->setVertexBuffer(smVertexBuffer);
|
||||
GFX->setPrimitiveBuffer(smPrimitiveBuffer);
|
||||
GFX->setupGenericShaders();
|
||||
GFX->drawIndexedPrimitive(GFXLineList, 0, 0, 4, 0, 12);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
U32 Marker::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
// Note that we don't really care about efficiency here, since this is an
|
||||
// edit-only ghost...
|
||||
stream->writeAffineTransform(mObjToWorld);
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void Marker::unpackUpdate(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
// Transform
|
||||
MatrixF otow;
|
||||
stream->readAffineTransform(&otow);
|
||||
|
||||
setTransform(otow);
|
||||
}
|
||||
157
Engine/source/scene/simPath.h
Normal file
157
Engine/source/scene/simPath.h
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SIMPATH_H_
|
||||
#define _SIMPATH_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _GFXSTATEBLOCK_H_
|
||||
#include "gfx/gfxStateBlock.h"
|
||||
#endif
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
namespace SimPath
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// A path!
|
||||
class Path : public SimGroup
|
||||
{
|
||||
typedef SimGroup Parent;
|
||||
|
||||
public:
|
||||
enum {
|
||||
NoPathIndex = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
U32 mPathIndex;
|
||||
bool mIsLooping;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
public:
|
||||
Path();
|
||||
~Path();
|
||||
|
||||
void addObject(SimObject*);
|
||||
void removeObject(SimObject*);
|
||||
|
||||
void sortMarkers();
|
||||
void updatePath();
|
||||
bool isLooping() { return mIsLooping; }
|
||||
U32 getPathIndex() const;
|
||||
|
||||
DECLARE_CONOBJECT(Path);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------
|
||||
inline U32 Path::getPathIndex() const
|
||||
{
|
||||
return mPathIndex;
|
||||
}
|
||||
|
||||
} // Namespace
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class Marker : public SceneObject
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
friend class Path;
|
||||
|
||||
public:
|
||||
enum SmoothingType
|
||||
{
|
||||
SmoothingTypeLinear,
|
||||
SmoothingTypeSpline,
|
||||
SmoothingTypeAccelerate,
|
||||
};
|
||||
|
||||
enum KnotType
|
||||
{
|
||||
KnotTypeNormal,
|
||||
KnotTypePositionOnly,
|
||||
KnotTypeKink,
|
||||
};
|
||||
|
||||
|
||||
U32 mSeqNum;
|
||||
U32 mSmoothingType;
|
||||
U32 mKnotType;
|
||||
|
||||
U32 mMSToNext;
|
||||
|
||||
// Rendering
|
||||
protected:
|
||||
void prepRenderImage(SceneRenderState *state);
|
||||
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat);
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void onGroupAdd();
|
||||
|
||||
void onEditorEnable();
|
||||
void onEditorDisable();
|
||||
|
||||
static void initGFXResources();
|
||||
|
||||
static GFXStateBlockRef smStateBlock;
|
||||
static GFXVertexBufferHandle<GFXVertexPC> smVertexBuffer;
|
||||
static GFXPrimitiveBufferHandle smPrimitiveBuffer;
|
||||
|
||||
public:
|
||||
Marker();
|
||||
~Marker();
|
||||
|
||||
DECLARE_CONOBJECT(Marker);
|
||||
static void initPersistFields();
|
||||
void inspectPostApply();
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
};
|
||||
|
||||
typedef Marker::SmoothingType MarkerSmoothingType;
|
||||
typedef Marker::KnotType MarkerKnotType;
|
||||
|
||||
DefineEnumType( MarkerSmoothingType );
|
||||
DefineEnumType( MarkerKnotType );
|
||||
|
||||
#endif // _H_PATH
|
||||
|
||||
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