diff --git a/trinity/Eve/EveEffectRoot2.cpp b/trinity/Eve/EveEffectRoot2.cpp index 02b00254c..0acff7b76 100644 --- a/trinity/Eve/EveEffectRoot2.cpp +++ b/trinity/Eve/EveEffectRoot2.cpp @@ -5,6 +5,7 @@ #include "Utilities/BoundingSphere.h" #include "Utilities/BoundingBox.h" +#include "Utilities/Obb.h" #include "TriFrustum.h" #include "Lights/Tr2PointLight.h" #include "Tr2LightManager.h" @@ -431,15 +432,16 @@ void EveEffectRoot2::GetLocalToWorldTransform( Matrix& transform ) const transform = m_lastUpdateMatrix; } -bool EveEffectRoot2::GetWorldBoundingBox( Vector3& min, Vector3& max ) const +bool EveEffectRoot2::GetWorldBoundingObb( Obb& obb ) const { if( m_boundingSphere.w <= 0.0f ) { return false; } - BoundingBoxInitialize( m_boundingSphere, min, max ); - BoundingBoxTransform( min, max, m_lastUpdateMatrix ); + CcpMath::Sphere worldSphere( m_boundingSphere ); + worldSphere.Transform( m_lastUpdateMatrix ); + obb.CreateFromSphere( worldSphere ); return true; } diff --git a/trinity/Eve/EveEffectRoot2.h b/trinity/Eve/EveEffectRoot2.h index e3a3af712..bba42d501 100644 --- a/trinity/Eve/EveEffectRoot2.h +++ b/trinity/Eve/EveEffectRoot2.h @@ -92,7 +92,7 @@ BLUE_CLASS( EveEffectRoot2 ) : ///////////////////////////////////////////////////////////////////////////////////// // ITr2BoundingBox - bool GetWorldBoundingBox( Vector3 & min, Vector3 & max ) const override; + bool GetWorldBoundingObb( Obb & obb ) const override; bool IsBoundingBoxReady() const override; ///////////////////////////////////////////////////////////////////////////////////// diff --git a/trinity/Eve/EvePlanet.cpp b/trinity/Eve/EvePlanet.cpp index 5924a7489..ea646bd8d 100644 --- a/trinity/Eve/EvePlanet.cpp +++ b/trinity/Eve/EvePlanet.cpp @@ -8,6 +8,7 @@ #include "EveUpdateContext.h" #include "Curves/TriCurveSet.h" #include "Utilities/BoundingBox.h" +#include "Utilities/Obb.h" const float EvePlanet::SCALE = 1000000.0f; @@ -150,9 +151,8 @@ Quaternion EvePlanet::GetWorldRotation() return m_rotation; } -bool EvePlanet::GetWorldBoundingBox( Vector3& min, Vector3& max ) const +bool EvePlanet::GetWorldBoundingObb( Obb& obb ) const { - Vector4 sphere; if( m_radius <= 0.0f ) { return false; @@ -160,9 +160,9 @@ bool EvePlanet::GetWorldBoundingBox( Vector3& min, Vector3& max ) const const float renderScale = m_renderScale > 0.0f ? m_renderScale : 1.0f; const Matrix scaledTransform = CalculatePlanetScaleTransform( m_worldTransform, renderScale ); - sphere = Vector4( scaledTransform.GetTranslation(), m_radius / renderScale ); - - BoundingBoxInitialize( sphere, min, max ); + const float radius = m_radius / renderScale; + CcpMath::Sphere worldSphere( scaledTransform.GetTranslation(), radius ); + obb.CreateFromSphere( worldSphere ); return true; } diff --git a/trinity/Eve/EvePlanet.h b/trinity/Eve/EvePlanet.h index c79c40cb7..1ebff3862 100644 --- a/trinity/Eve/EvePlanet.h +++ b/trinity/Eve/EvePlanet.h @@ -60,7 +60,7 @@ BLUE_CLASS( EvePlanet ) : virtual Quaternion GetWorldRotation(); // ITr2BoundingBox - bool GetWorldBoundingBox( Vector3 & min, Vector3 & max ) const override; + bool GetWorldBoundingObb( Obb & obb ) const override; bool IsBoundingBoxReady() const override; // ITr2SecondaryLightSource diff --git a/trinity/Eve/EveTransform.cpp b/trinity/Eve/EveTransform.cpp index 8126a09e0..977b5f4ca 100644 --- a/trinity/Eve/EveTransform.cpp +++ b/trinity/Eve/EveTransform.cpp @@ -6,6 +6,7 @@ #include "Utilities/Vector3d.h" #include "Utilities/BoundingSphere.h" #include "Utilities/BoundingBox.h" +#include "Utilities/Obb.h" #include "TriFrustum.h" #include "Particle/Tr2ParticleSystem.h" #include "Particle/ITr2GenericEmitter.h" @@ -381,13 +382,14 @@ bool EveTransform::GetLocalBoundingBox( Vector3& min, Vector3& max ) return true; } -bool EveTransform::GetWorldBoundingBox( Vector3& min, Vector3& max ) const +bool EveTransform::GetWorldBoundingObb( Obb& obb ) const { + Vector3 min, max; if( !GetDirectLocalBounds( m_overrideBoundsMin, m_overrideBoundsMax, m_mesh, min, max ) ) { return false; } - BoundingBoxTransform( min, max, m_worldTransform ); + obb.CreateWorldBoundingObb( min, max, m_worldTransform ); return true; } diff --git a/trinity/Eve/EveTransform.h b/trinity/Eve/EveTransform.h index 57f90e6e1..75078edea 100644 --- a/trinity/Eve/EveTransform.h +++ b/trinity/Eve/EveTransform.h @@ -69,7 +69,7 @@ BLUE_CLASS( EveTransform ) : ///////////////////////////////////////////////////////////////////////////////////// // ITr2BoundingBox - bool GetWorldBoundingBox( Vector3 & min, Vector3 & max ) const override; + bool GetWorldBoundingObb( Obb & obb ) const override; bool IsBoundingBoxReady() const override; ///////////////////////////////////////////////////////////////////////////////////// diff --git a/trinity/Eve/SpaceObject/EveSpaceObject2.cpp b/trinity/Eve/SpaceObject/EveSpaceObject2.cpp index 3ac83d052..951f92042 100644 --- a/trinity/Eve/SpaceObject/EveSpaceObject2.cpp +++ b/trinity/Eve/SpaceObject/EveSpaceObject2.cpp @@ -5,6 +5,7 @@ #include "Utilities/BoundingBox.h" #include "Utilities/BoundingSphere.h" #include "Utilities/MatrixUtils.h" +#include "Utilities/Obb.h" #include "include/ITr2DebugRenderer.h" #include "include/IEveBallpark.h" @@ -4165,11 +4166,9 @@ void EveSpaceObject2::GetPickingBatches( ITriRenderBatchAccumulator* batches, Tr } } -bool EveSpaceObject2::GetWorldBoundingBox( Vector3& min, Vector3& max ) const +bool EveSpaceObject2::GetWorldBoundingObb( Obb& obb ) const { - min = m_localAabbMin; - max = m_localAabbMax; - BoundingBoxTransform( min, max, m_worldTransform ); + obb.CreateWorldBoundingObb( m_localAabbMin, m_localAabbMax, m_worldTransform ); return true; } diff --git a/trinity/Eve/SpaceObject/EveSpaceObject2.h b/trinity/Eve/SpaceObject/EveSpaceObject2.h index 74f6522e4..0986c219d 100644 --- a/trinity/Eve/SpaceObject/EveSpaceObject2.h +++ b/trinity/Eve/SpaceObject/EveSpaceObject2.h @@ -347,7 +347,7 @@ BLUE_CLASS( EveSpaceObject2 ) : ///////////////////////////////////////////////////////////////////////////////////// // ITr2BoundingBox - virtual bool GetWorldBoundingBox( Vector3 & min, Vector3 & max ) const; + virtual bool GetWorldBoundingObb( Obb & obb ) const; virtual bool IsBoundingBoxReady() const; ///////////////////////////////////////////////////////////////////////////////////// diff --git a/trinity/Include/ITr2BoundingBox.h b/trinity/Include/ITr2BoundingBox.h index 0a54ce568..98a143533 100644 --- a/trinity/Include/ITr2BoundingBox.h +++ b/trinity/Include/ITr2BoundingBox.h @@ -5,12 +5,12 @@ #ifndef ITr2BoundingBox_h #define ITr2BoundingBox_h -struct Vector3; +struct Obb; BLUE_INTERFACE( ITr2BoundingBox ) : IRoot { - virtual bool GetWorldBoundingBox( Vector3 & min, Vector3 & max ) const = 0; + virtual bool GetWorldBoundingObb( Obb & obb ) const = 0; virtual bool IsBoundingBoxReady( void ) const = 0; }; diff --git a/trinity/Interior/Tr2IntSkinnedObject.cpp b/trinity/Interior/Tr2IntSkinnedObject.cpp index 319551702..e6ca62742 100644 --- a/trinity/Interior/Tr2IntSkinnedObject.cpp +++ b/trinity/Interior/Tr2IntSkinnedObject.cpp @@ -5,6 +5,7 @@ #include "Tr2IntSkinnedObject.h" #include "Utilities/BoundingSphere.h" +#include "Utilities/Obb.h" #include "TriSettingsRegistrar.h" #include "Tr2PerObjectData.h" #include "Resources/TriGeometryRes.h" @@ -147,10 +148,21 @@ bool Tr2IntSkinnedObject::GetWorldBoundingBox( Vector3& min, Vector3& max ) cons return true; } +bool Tr2IntSkinnedObject::GetWorldBoundingObb( Obb& obb ) const +{ + Vector3 min, max; + if( !GetLocalBoundingBox( min, max ) ) + { + return false; + } + obb.CreateWorldBoundingObb( min, max, GetSkinningTransform() ); + return true; +} + bool Tr2IntSkinnedObject::IsBoundingBoxReady( void ) const { Vector3 min, max; - return GetWorldBoundingBox( min, max ); + return GetLocalBoundingBox( min, max ); } void Tr2IntSkinnedObject::AddToApexScene( Tr2ApexScene* apexScene ) diff --git a/trinity/Interior/Tr2IntSkinnedObject.h b/trinity/Interior/Tr2IntSkinnedObject.h index 8cf33afdf..1da1fe254 100644 --- a/trinity/Interior/Tr2IntSkinnedObject.h +++ b/trinity/Interior/Tr2IntSkinnedObject.h @@ -92,6 +92,9 @@ class Tr2IntSkinnedObject : public ITr2InteriorDynamic, virtual bool GetWorldBoundingBox( Vector3& min, Vector3& max ) const; virtual bool IsBoundingBoxReady( void ) const; + // ITr2BoundingBox + virtual bool GetWorldBoundingObb( Obb& obb ) const; + // Apex void AddToApexScene( Tr2ApexScene* apexScene ); void RemoveFromApexScene( void ); diff --git a/trinity/Interior/Tr2InteriorPlaceable.cpp b/trinity/Interior/Tr2InteriorPlaceable.cpp index 498d2a98a..d519d648a 100644 --- a/trinity/Interior/Tr2InteriorPlaceable.cpp +++ b/trinity/Interior/Tr2InteriorPlaceable.cpp @@ -8,6 +8,7 @@ // Trinity headers #include "Utilities/BoundingSphere.h" +#include "Utilities/Obb.h" #include "Tr2PerObjectData.h" #include "Wod/WodPlaceableRes.h" #include "Tr2Mesh.h" @@ -135,6 +136,17 @@ bool Tr2InteriorPlaceable::GetWorldBoundingBox( Vector3& min, Vector3& max ) con return true; } +bool Tr2InteriorPlaceable::GetWorldBoundingObb( Obb& obb ) const +{ + Vector3 min, max; + if( !GetLocalBoundingBox( min, max ) ) + { + return false; + } + obb.CreateWorldBoundingObb( min, max, m_transform ); + return true; +} + bool Tr2InteriorPlaceable::IsBoundingBoxReady( void ) const { return m_isBoundingBoxModified || ( m_placeableRes && m_placeableRes->IsReady() ); diff --git a/trinity/Interior/Tr2InteriorPlaceable.h b/trinity/Interior/Tr2InteriorPlaceable.h index 7500f45ac..71e5dbeb7 100644 --- a/trinity/Interior/Tr2InteriorPlaceable.h +++ b/trinity/Interior/Tr2InteriorPlaceable.h @@ -74,6 +74,10 @@ class Tr2InteriorPlaceable : public INotify, virtual bool GetLocalBoundingBox( Vector3& min, Vector3& max ) const; virtual bool GetWorldBoundingBox( Vector3& min, Vector3& max ) const; virtual bool IsBoundingBoxReady( void ) const; + + ///////////////////////////////////////////////////////////////////////////////////// + // ITr2BoundingBox + virtual bool GetWorldBoundingObb( Obb& obb ) const; virtual void PrePhysicsUpdate( Be::Time time ); virtual void PostPhysicsUpdate( Be::Time time, Tr2ApexScene* apexScene ); diff --git a/trinity/Tr2ProjectBoundingBoxBracket.cpp b/trinity/Tr2ProjectBoundingBoxBracket.cpp index c8e9e5d42..ba2daed64 100644 --- a/trinity/Tr2ProjectBoundingBoxBracket.cpp +++ b/trinity/Tr2ProjectBoundingBoxBracket.cpp @@ -9,7 +9,7 @@ #include "Tr2Renderer.h" #include "TriViewport.h" #include "Sprite2d/Tr2Sprite2dContainer.h" -#include "Utilities/BoundingBox.h" +#include "Utilities/Obb.h" #include "include/ITr2DebugRenderer.h" extern ITr2DebugRendererPtr g_debugRenderer; @@ -18,6 +18,26 @@ namespace { const float CLIP_EPSILON = 1e-5f; +// Obb::GetPoint corner index bits: 0x1 = -X, 0x2 = -Y, 0x4 = -Z; +// each edge joins the two corners differing in exactly one axis sign. +const int OBB_EDGES[12][2] = { + { 0, 1 }, + { 2, 3 }, + { 4, 5 }, + { 6, 7 }, + { 0, 2 }, + { 1, 3 }, + { 4, 6 }, + { 5, 7 }, + { 0, 4 }, + { 1, 5 }, + { 2, 6 }, + { 3, 7 }, +}; + +// 8 corners plus at most one near-plane cut per edge +const int MAX_PROJECTABLE_POINTS = 20; + struct ProjectedBounds { float x; @@ -29,26 +49,6 @@ struct ProjectedBounds bool coversViewport; }; -Vector4 TransformPointToClip( const Vector3& point, const Matrix& viewProjection ) -{ - return Vector4{ - point.x * viewProjection._11 + point.y * viewProjection._21 + point.z * viewProjection._31 + viewProjection._41, - point.x * viewProjection._12 + point.y * viewProjection._22 + point.z * viewProjection._32 + viewProjection._42, - point.x * viewProjection._13 + point.y * viewProjection._23 + point.z * viewProjection._33 + viewProjection._43, - point.x * viewProjection._14 + point.y * viewProjection._24 + point.z * viewProjection._34 + viewProjection._44 - }; -} - -Vector4 Lerp( const Vector4& a, const Vector4& b, float t ) -{ - return Vector4{ - a.x + ( b.x - a.x ) * t, - a.y + ( b.y - a.y ) * t, - a.z + ( b.z - a.z ) * t, - a.w + ( b.w - a.w ) * t - }; -} - // Cohen-Sutherland style outcodes: one bit per plane of the D3D clip volume // (-w <= x <= w, -w <= y <= w, 0 <= z <= w); a set bit means the point is // outside that plane. In 2D (4 bits) the zones look like: @@ -108,9 +108,17 @@ bool CanPerspectiveDivide( const Vector4& point ) return fabsf( point.w ) > CLIP_EPSILON; } +// The Transform( Vector3, Matrix ) overload computes w from the matrix's fourth +// column alone, dropping the point's components, so points must go through the +// Vector4 overload to get a usable clip-space w. +Vector4 TransformPoint( const Vector3& point, const Matrix& matrix ) +{ + return Transform( Vector4( point, 1.0f ), matrix ); +} + // Both endpoints must be on opposite sides of the near plane; the caller // guarantees this via the outcode test. -void AddNearPlaneIntersection( const Vector4& a, const Vector4& b, std::vector& points ) +void AddNearPlaneIntersection( const Vector4& a, const Vector4& b, Vector4* points, int& pointCount ) { float denominator = a.z - b.z; if( fabsf( denominator ) <= CLIP_EPSILON ) @@ -119,13 +127,31 @@ void AddNearPlaneIntersection( const Vector4& a, const Vector4& b, std::vector obb.sizes[i] * LengthSq( *axes[i] ) ) + { + return false; + } + } + return true; +} + bool ProjectClipPoint( const Vector4& point, const TriViewport& viewport, Vector3& projected ) { if( !CanPerspectiveDivide( point ) ) @@ -140,24 +166,14 @@ bool ProjectClipPoint( const Vector4& point, const TriViewport& viewport, Vector return true; } -bool ProjectBoundingBoxToViewport( const Vector3& bbMin, const Vector3& bbMax, const Matrix& viewProjection, const TriViewport& viewport, ProjectedBounds& bounds ) +bool ProjectBoundingBoxToViewport( const Obb& obb, const Matrix& viewProjection, const TriViewport& viewport, ProjectedBounds& bounds ) { - Vector3 corners[8]; - corners[0] = bbMin; - corners[1] = Vector3( bbMin.x, bbMin.y, bbMax.z ); - corners[2] = Vector3( bbMax.x, bbMin.y, bbMin.z ); - corners[3] = Vector3( bbMax.x, bbMin.y, bbMax.z ); - corners[4] = bbMax; - corners[5] = Vector3( bbMax.x, bbMax.y, bbMin.z ); - corners[6] = Vector3( bbMin.x, bbMax.y, bbMax.z ); - corners[7] = Vector3( bbMin.x, bbMax.y, bbMin.z ); - Vector4 clipCorners[8]; uint32_t outcodes[8]; uint32_t combinedOutcode = ~0u; for( int i = 0; i < 8; ++i ) { - clipCorners[i] = TransformPointToClip( corners[i], viewProjection ); + clipCorners[i] = TransformPoint( obb.GetPoint( i ), viewProjection ); outcodes[i] = ClipOutcode( clipCorners[i] ); combinedOutcode &= outcodes[i]; } @@ -168,30 +184,26 @@ bool ProjectBoundingBoxToViewport( const Vector3& bbMin, const Vector3& bbMax, c return false; } - std::vector projectablePoints; - projectablePoints.reserve( 20 ); + Vector4 projectablePoints[MAX_PROJECTABLE_POINTS]; + int pointCount = 0; for( int i = 0; i < 8; ++i ) { if( !( outcodes[i] & CLIP_NEAR ) && CanPerspectiveDivide( clipCorners[i] ) ) { - projectablePoints.push_back( clipCorners[i] ); + projectablePoints[pointCount++] = clipCorners[i]; } } - static const int EDGES[12][2] = { - { 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 }, { 7, 6 }, { 6, 4 }, { 4, 5 }, { 5, 7 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, { 3, 4 } - }; - for( int i = 0; i < 12; ++i ) { // XOR: the edge endpoints straddle the near plane - if( ( outcodes[EDGES[i][0]] ^ outcodes[EDGES[i][1]] ) & CLIP_NEAR ) + if( ( outcodes[OBB_EDGES[i][0]] ^ outcodes[OBB_EDGES[i][1]] ) & CLIP_NEAR ) { - AddNearPlaneIntersection( clipCorners[EDGES[i][0]], clipCorners[EDGES[i][1]], projectablePoints ); + AddNearPlaneIntersection( clipCorners[OBB_EDGES[i][0]], clipCorners[OBB_EDGES[i][1]], projectablePoints, pointCount ); } } - if( projectablePoints.empty() ) + if( pointCount == 0 ) { return false; } @@ -204,8 +216,9 @@ bool ProjectBoundingBoxToViewport( const Vector3& bbMin, const Vector3& bbMax, c float maxX = 0.0f; float maxY = 0.0f; - for( const Vector4& point : projectablePoints ) + for( int i = 0; i < pointCount; ++i ) { + const Vector4& point = projectablePoints[i]; if( !ProjectClipPoint( point, viewport, projected ) ) { continue; @@ -292,6 +305,50 @@ float ClampProjectedSize( float size, float minSize, float maxSize ) } return size; } + +const uint32_t DEBUG_OBB_COLOR = 0xffffff00; +const uint32_t DEBUG_RECT_COLOR = 0xffff00ff; + +void DrawDebugObb( const Obb& obb ) +{ + for( int i = 0; i < 12; ++i ) + { + g_debugRenderer->DrawLine( obb.GetPoint( OBB_EDGES[i][0] ), obb.GetPoint( OBB_EDGES[i][1] ), DEBUG_OBB_COLOR ); + } +} + +// Draws the published screen rect as world-space lines at the box center's +// depth, so it visually coincides with the bracket sprite. +void DrawDebugRect( float x, float y, float width, float height, const Vector3& center, const Matrix& viewProjection, const TriViewport& viewport ) +{ + if( viewport.width <= 0 || viewport.height <= 0 ) + { + return; + } + + const Vector4 clipCenter = TransformPoint( center, viewProjection ); + if( clipCenter.w <= CLIP_EPSILON || clipCenter.z < 0.0f ) + { + return; + } + const float ndcZ = std::min( std::max( clipCenter.z / clipCenter.w, 0.001f ), 0.999f ); + + const Matrix inverseViewProjection = Inverse( viewProjection ); + const float screenCorners[4][2] = { + { x, y }, { x + width, y }, { x + width, y + height }, { x, y + height } + }; + Vector3 worldCorners[4]; + for( int i = 0; i < 4; ++i ) + { + const float ndcX = 2.0f * ( screenCorners[i][0] - static_cast( viewport.x ) ) / static_cast( viewport.width ) - 1.0f; + const float ndcY = 1.0f - 2.0f * ( screenCorners[i][1] - static_cast( viewport.y ) ) / static_cast( viewport.height ); + worldCorners[i] = TransformCoord( Vector3( ndcX, ndcY, ndcZ ), inverseViewProjection ); + } + for( int i = 0; i < 4; ++i ) + { + g_debugRenderer->DrawLine( worldCorners[i], worldCorners[( i + 1 ) % 4], DEBUG_RECT_COLOR ); + } +} } @@ -306,6 +363,7 @@ Tr2ProjectBoundingBoxBracket::Tr2ProjectBoundingBoxBracket( IRoot* lockobj /*= N m_projectedWidth( 0.0f ), m_projectedHeight( 0.0f ), m_integerCoordinates( true ), + m_debugDraw( false ), m_screenMargin( 0.0f ), m_cameraDistance( 0 ), m_isProjectionValid( false ), @@ -318,19 +376,24 @@ Tr2ProjectBoundingBoxBracket::Tr2ProjectBoundingBoxBracket( IRoot* lockobj /*= N void Tr2ProjectBoundingBoxBracket::UpdateValue( double time ) { - Vector3 bbMin, bbMax; - if( !m_object || !m_object->IsBoundingBoxReady() || !m_object->GetWorldBoundingBox( bbMin, bbMax ) ) + Obb obb; + if( !m_object || !m_object->IsBoundingBoxReady() || !m_object->GetWorldBoundingObb( obb ) ) { SetEmptyProjection(); return; } - const Vector3 center = ( bbMax + bbMin ) * 0.5f; + const bool debugDraw = m_debugDraw && g_debugRenderer; + if( debugDraw ) + { + DrawDebugObb( obb ); + } + const Vector3 viewPosition = Tr2Renderer::GetViewPosition(); - m_cameraDistance = Length( viewPosition - center ); + m_cameraDistance = Length( viewPosition - obb.center ); const TriViewport& viewport = Tr2Renderer::GetViewport(); - if( BoundingBoxIsInside( bbMin, bbMax, viewPosition ) ) + if( ObbContainsPoint( obb, viewPosition ) ) { SetFullViewportProjection( viewport ); return; @@ -338,7 +401,7 @@ void Tr2ProjectBoundingBoxBracket::UpdateValue( double time ) Matrix viewProjection = Tr2Renderer::GetViewTransform() * Tr2Renderer::GetProjectionTransform(); ProjectedBounds projectedBounds; - if( !ProjectBoundingBoxToViewport( bbMin, bbMax, viewProjection, viewport, projectedBounds ) ) + if( !ProjectBoundingBoxToViewport( obb, viewProjection, viewport, projectedBounds ) ) { SetEmptyProjection(); return; @@ -353,8 +416,13 @@ void Tr2ProjectBoundingBoxBracket::UpdateValue( double time ) m_extendsOffscreen = projectedBounds.extendsOffscreen; m_coversViewport = projectedBounds.coversViewport; - ConstrainProjection( center, viewProjection, viewport ); + ConstrainProjection( obb.center, viewProjection, viewport ); PublishProjection( viewport ); + + if( debugDraw && m_isProjectionValid ) + { + DrawDebugRect( m_projectedX, m_projectedY, m_projectedWidth, m_projectedHeight, obb.center, viewProjection, viewport ); + } } void Tr2ProjectBoundingBoxBracket::SetEmptyProjection() @@ -408,7 +476,7 @@ void Tr2ProjectBoundingBoxBracket::ConstrainProjection( const Vector3& center, c { // Bounded brackets are anchored on the projected 3d box center, not the projected // rect center, unless the box center is behind the near plane. - Vector4 clipCenter = TransformPointToClip( center, viewProjection ); + Vector4 clipCenter = TransformPoint( center, viewProjection ); Vector3 projectedCenter; if( clipCenter.z >= 0.0f && clipCenter.w > 0.0f && ProjectClipPoint( clipCenter, viewport, projectedCenter ) ) { @@ -448,7 +516,7 @@ void Tr2ProjectBoundingBoxBracket::PublishProjection( const TriViewport& viewpor m_isProjectionValid = true; UpdateBracket(); - if( g_debugRenderer ) + if( m_debugDraw && g_debugRenderer ) { int x = static_cast( m_projectedX ); int y = static_cast( m_projectedY ); diff --git a/trinity/Tr2ProjectBoundingBoxBracket.h b/trinity/Tr2ProjectBoundingBoxBracket.h index 36e62c7f5..971ddba60 100644 --- a/trinity/Tr2ProjectBoundingBoxBracket.h +++ b/trinity/Tr2ProjectBoundingBoxBracket.h @@ -39,6 +39,9 @@ class Tr2ProjectBoundingBoxBracket : public ITriFunction // Should the coordinates be rounded to the nearest integer? Defaults to true. bool m_integerCoordinates; + // Draw the consumed bounds and the published rect through the debug renderer. + bool m_debugDraw; + float m_minProjectedWidth; float m_minProjectedHeight; float m_maxProjectedWidth; diff --git a/trinity/Tr2ProjectBoundingBoxBracket_Blue.cpp b/trinity/Tr2ProjectBoundingBoxBracket_Blue.cpp index d9e3c7870..78a076392 100644 --- a/trinity/Tr2ProjectBoundingBoxBracket_Blue.cpp +++ b/trinity/Tr2ProjectBoundingBoxBracket_Blue.cpp @@ -105,6 +105,14 @@ const Be::ClassInfo* Tr2ProjectBoundingBoxBracket::ExposeToBlue() "If set, projected x, y, width and height are rounded to the nearest integer.", Be::READWRITE ) + MAP_ATTRIBUTE( + "debugDraw", + m_debugDraw, + "If set, and a debug renderer is set, draws the bounds the projection\n" + "consumed as a world-space wireframe, the published rect, and the\n" + "bracket's name and size at its projected position.", + Be::READWRITE ) + MAP_ATTRIBUTE( "screenMargin", m_screenMargin, diff --git a/trinity/Utilities/Obb.cpp b/trinity/Utilities/Obb.cpp index b5701d274..1534b66fb 100644 --- a/trinity/Utilities/Obb.cpp +++ b/trinity/Utilities/Obb.cpp @@ -7,33 +7,50 @@ // ------------------------------------------------------------- // Description: -// Computes an Oriented Bounding Box in world space, which has been shrunk -// to fit the current viewing frustum as tightly as possible without visibly clipping -// into it. The frustum is derived from Tr2Renderer GetViewTransform, GetProjectionTransform, and so on. +// Computes an Oriented Bounding Box in world space. // The calculation starts out with the AABB from GetLocalBoundingBox, so the explicit bounds // are supported. // Arguments: // localMin - minimum of AABB in local coordinates // localMax - maximum of AABB in local coordinates // localToWorld - [in] transform that takes this skinned object from local to world coordinates -// frustum - frustum to shrink against. If null, no shrinking occurs and this is just a convenience setup helper (AABB->OBB) // Summary: -// Compute a world-space OBB, tightened for the current frustum. +// Compute a world-space OBB. // ------------------------------------------------------------- -void Obb::CreateClippedWorldBoundingObb( const Vector3& min, const Vector3& max, const Matrix& localToWorld, const TriFrustum* frustum ) +void Obb::CreateWorldBoundingObb( const Vector3& localMin, const Vector3& localMax, const Matrix& localToWorld ) { - // take AABB center.. - center = 0.5 * ( max + min ); + // take AABB center + center = 0.5 * ( localMax + localMin ); - // .. and move to world space. + // and move to world space. Vector4 centerWorld = Transform( center, localToWorld ); center = Vector3( centerWorld.x, centerWorld.y, centerWorld.z ); - sizes = 0.5 * ( max - min ); + sizes = 0.5 * ( localMax - localMax ); x = Vector3( localToWorld._11, localToWorld._12, localToWorld._13 ); y = Vector3( localToWorld._21, localToWorld._22, localToWorld._23 ); z = Vector3( localToWorld._31, localToWorld._32, localToWorld._33 ); +} + +// ------------------------------------------------------------- +// Description: +// Computes an Oriented Bounding Box in world space, which has been shrunk +// to fit the current viewing frustum as tightly as possible without visibly clipping +// into it. The frustum is derived from Tr2Renderer GetViewTransform, GetProjectionTransform, and so on. +// The calculation starts out with the AABB from GetLocalBoundingBox, so the explicit bounds +// are supported. +// Arguments: +// localMin - minimum of AABB in local coordinates +// localMax - maximum of AABB in local coordinates +// localToWorld - [in] transform that takes this skinned object from local to world coordinates +// frustum - frustum to shrink against. If null, no shrinking occurs and this is just a convenience setup helper (AABB->OBB) +// Summary: +// Compute a world-space OBB, tightened for the current frustum. +// ------------------------------------------------------------- +void Obb::CreateClippedWorldBoundingObb( const Vector3& localMin, const Vector3& localMax, const Matrix& localToWorld, const TriFrustum* frustum ) +{ + CreateWorldBoundingObb( localMin, localMax, localToWorld ); if( !frustum ) { @@ -189,6 +206,16 @@ void Obb::CreateClippedWorldBoundingObb( const Vector3& min, const Vector3& max, } } +// A sphere's bounds do not depend on orientation, so the box is world-axis aligned. +void Obb::CreateFromSphere( const CcpMath::Sphere& worldSphere ) +{ + x = Vector3( 1.0f, 0.0f, 0.0f ); + y = Vector3( 0.0f, 1.0f, 0.0f ); + z = Vector3( 0.0f, 0.0f, 1.0f ); + center = worldSphere.center; + sizes = Vector3( worldSphere.radius, worldSphere.radius, worldSphere.radius ); +} + // ------------------------------------------------------------- // Description: // Return a corner point of the OBB. diff --git a/trinity/Utilities/Obb.h b/trinity/Utilities/Obb.h index a68b1c3da..87f59db0e 100644 --- a/trinity/Utilities/Obb.h +++ b/trinity/Utilities/Obb.h @@ -31,7 +31,9 @@ struct Obb // sizes - half the size of the OBB along every axis x, y or z. Ie you get to a corner point with center + sizes[0] * x. Full width/height/depth is sizes*2. Vector3 sizes; + void CreateWorldBoundingObb( const Vector3& localMin, const Vector3& localMax, const Matrix& localToWorld ); void CreateClippedWorldBoundingObb( const Vector3& localMin, const Vector3& localMax, const Matrix& localToWorld, const TriFrustum* frustum ); + void CreateFromSphere( const CcpMath::Sphere& worldSphere ); Vector3 GetPoint( unsigned N ) const;