diff --git a/trinity/Eve/EveEffectRoot2.cpp b/trinity/Eve/EveEffectRoot2.cpp index b37c60d85..c7e088f3d 100644 --- a/trinity/Eve/EveEffectRoot2.cpp +++ b/trinity/Eve/EveEffectRoot2.cpp @@ -4,6 +4,7 @@ #include "EveEffectRoot2.h" #include "Utilities/BoundingSphere.h" +#include "Utilities/BoundingBox.h" #include "TriFrustum.h" #include "Lights/Tr2PointLight.h" #include "Tr2LightManager.h" @@ -398,8 +399,13 @@ void EveEffectRoot2::GetModelCenterWorldPosition( Vector3& position ) const bool EveEffectRoot2::GetLocalBoundingBox( Vector3& min, Vector3& max ) { - // If possible, return an AABB in local coordinates - return false; + if( m_boundingSphere.w <= 0.0f ) + { + return false; + } + + BoundingBoxInitialize( m_boundingSphere, min, max ); + return true; } void EveEffectRoot2::GetLocalToWorldTransform( Matrix& transform ) const @@ -408,6 +414,23 @@ void EveEffectRoot2::GetLocalToWorldTransform( Matrix& transform ) const transform = m_lastUpdateMatrix; } +bool EveEffectRoot2::GetWorldBoundingBox( Vector3& min, Vector3& max ) const +{ + if( m_boundingSphere.w <= 0.0f ) + { + return false; + } + + BoundingBoxInitialize( m_boundingSphere, min, max ); + BoundingBoxTransform( min, max, m_lastUpdateMatrix ); + return true; +} + +bool EveEffectRoot2::IsBoundingBoxReady() const +{ + return m_boundingSphere.w > 0.0f; +} + void EveEffectRoot2::RegisterWithQuadRenderer( Tr2QuadRenderer& quadRenderer ) { for( auto it = m_effectChildren.begin(); it != m_effectChildren.end(); ++it ) @@ -995,4 +1018,4 @@ void EveEffectRoot2::SetProceduralContainerVariable( const char* name, float val auto child = *it; child->SetProceduralContainerVariable( name, value ); } -} \ No newline at end of file +} diff --git a/trinity/Eve/EveEffectRoot2.h b/trinity/Eve/EveEffectRoot2.h index 2f49183c5..ff0cd8275 100644 --- a/trinity/Eve/EveEffectRoot2.h +++ b/trinity/Eve/EveEffectRoot2.h @@ -19,6 +19,7 @@ #include "ITr2SoundEmitterOwner.h" #include "Controllers/ITr2ControllerOwner.h" #include "Lights/ITr2LightOwner.h" +#include "ITr2BoundingBox.h" #include "EveEntity.h" #include @@ -49,6 +50,7 @@ BLUE_CLASS( EveEffectRoot2 ) : public ITr2SoundEmitterOwner, public ITr2ControllerOwner, public ITr2LightOwner, + public ITr2BoundingBox, public EveEntity { @@ -88,6 +90,11 @@ BLUE_CLASS( EveEffectRoot2 ) : void AddQuadsToQuadRenderer( const TriFrustum& frustum, Tr2QuadRenderer& quadRenderer ); void SetProceduralContainerVariable( const char* name, float value ) override; + ///////////////////////////////////////////////////////////////////////////////////// + // ITr2BoundingBox + bool GetWorldBoundingBox( Vector3 & min, Vector3 & max ) const override; + bool IsBoundingBoxReady() const override; + ///////////////////////////////////////////////////////////////////////////////////// // ITr2LightOwner void GetLights( Tr2LightManager & lightManager ) const override; @@ -229,4 +236,4 @@ BLUE_CLASS( EveEffectRoot2 ) : TYPEDEF_BLUECLASS( EveEffectRoot2 ); -#endif // EveEffectRoot2_h \ No newline at end of file +#endif // EveEffectRoot2_h diff --git a/trinity/Eve/EveEffectRoot2_Blue.cpp b/trinity/Eve/EveEffectRoot2_Blue.cpp index f15a86690..047572acb 100644 --- a/trinity/Eve/EveEffectRoot2_Blue.cpp +++ b/trinity/Eve/EveEffectRoot2_Blue.cpp @@ -19,6 +19,7 @@ const Be::ClassInfo* EveEffectRoot2::ExposeToBlue() MAP_INTERFACE( IShaderConfigurer ) MAP_INTERFACE( ITr2SoundEmitterOwner ) MAP_INTERFACE( ITr2LightOwner ) + MAP_INTERFACE( ITr2BoundingBox ) MAP_INTERFACE( IWorldPosition ) MAP_INTERFACE( EveEntity ) diff --git a/trinity/Eve/EvePlanet.cpp b/trinity/Eve/EvePlanet.cpp index 2f213983f..5924a7489 100644 --- a/trinity/Eve/EvePlanet.cpp +++ b/trinity/Eve/EvePlanet.cpp @@ -7,6 +7,7 @@ #include "TriDevice.h" #include "EveUpdateContext.h" #include "Curves/TriCurveSet.h" +#include "Utilities/BoundingBox.h" const float EvePlanet::SCALE = 1000000.0f; @@ -149,6 +150,27 @@ Quaternion EvePlanet::GetWorldRotation() return m_rotation; } +bool EvePlanet::GetWorldBoundingBox( Vector3& min, Vector3& max ) const +{ + Vector4 sphere; + if( m_radius <= 0.0f ) + { + return false; + } + + 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 ); + return true; +} + +bool EvePlanet::IsBoundingBoxReady() const +{ + return m_radius > 0.0f; +} + // -------------------------------------------------------------------------------- // Description: // Calculate the pixeldiameter of this planet sphere at a given position. Is used @@ -386,4 +408,4 @@ void EvePlanet::RenderDebugInfo( ITr2DebugRenderer2& renderer ) renderable->RenderDebugInfo( renderer ); } } -} \ No newline at end of file +} diff --git a/trinity/Eve/EvePlanet.h b/trinity/Eve/EvePlanet.h index e565f6e51..c79c40cb7 100644 --- a/trinity/Eve/EvePlanet.h +++ b/trinity/Eve/EvePlanet.h @@ -59,6 +59,10 @@ BLUE_CLASS( EvePlanet ) : virtual Vector3 GetWorldPosition(); virtual Quaternion GetWorldRotation(); + // ITr2BoundingBox + bool GetWorldBoundingBox( Vector3 & min, Vector3 & max ) const override; + bool IsBoundingBoxReady() const override; + // ITr2SecondaryLightSource virtual void RegisterSecondaryLightSource( Tr2ShLightingManager& ); virtual void UnregisterSecondaryLightSource( Tr2ShLightingManager& ); diff --git a/trinity/Eve/EvePlanet_Blue.cpp b/trinity/Eve/EvePlanet_Blue.cpp index 4d7d1a4fb..9453eb8d8 100644 --- a/trinity/Eve/EvePlanet_Blue.cpp +++ b/trinity/Eve/EvePlanet_Blue.cpp @@ -17,6 +17,7 @@ const Be::ClassInfo* EvePlanet::ExposeToBlue() MAP_INTERFACE( IShaderConfigurer ) MAP_INTERFACE( ITr2SoundEmitterOwner ) MAP_INTERFACE( IWorldPosition ) + MAP_INTERFACE( ITr2BoundingBox ) MAP_ATTRIBUTE( "radius", m_radius, diff --git a/trinity/Eve/EveRootTransform_Blue.cpp b/trinity/Eve/EveRootTransform_Blue.cpp index 2f98d7f7b..4fbe3aa41 100644 --- a/trinity/Eve/EveRootTransform_Blue.cpp +++ b/trinity/Eve/EveRootTransform_Blue.cpp @@ -13,6 +13,7 @@ const Be::ClassInfo* EveRootTransform::ExposeToBlue() MAP_INTERFACE( ITriTargetable ) MAP_INTERFACE( ITr2Pickable ) MAP_INTERFACE( IWorldPosition ) + MAP_INTERFACE( ITr2BoundingBox ) MAP_ATTRIBUTE( "translationCurve", diff --git a/trinity/Eve/EveTransform.cpp b/trinity/Eve/EveTransform.cpp index a02e2bf9d..8126a09e0 100644 --- a/trinity/Eve/EveTransform.cpp +++ b/trinity/Eve/EveTransform.cpp @@ -17,6 +17,26 @@ extern float g_eveSpaceSceneLowUpdateRate; +namespace +{ +bool HasOverrideBounds( const Vector3& boundsMin, const Vector3& boundsMax ) +{ + return boundsMax.x != boundsMin.x || boundsMax.y != boundsMin.y || boundsMax.z != boundsMin.z; +} + +bool GetDirectLocalBounds( const Vector3& overrideBoundsMin, const Vector3& overrideBoundsMax, Tr2MeshBase* mesh, Vector3& min, Vector3& max ) +{ + if( HasOverrideBounds( overrideBoundsMin, overrideBoundsMax ) ) + { + min = overrideBoundsMin; + max = overrideBoundsMax; + return true; + } + + return mesh && mesh->GetBoundingBox( min, max ); +} +} + EveTransform::EveTransform( IRoot* lockobj ) : Tr2Transform( lockobj ), PARENTLOCK( m_children ), @@ -351,6 +371,33 @@ void EveTransform::GetRenderables( std::vector& renderables ) GetRenderables( renderables, nullptr ); } +bool EveTransform::GetLocalBoundingBox( Vector3& min, Vector3& max ) +{ + if( !GetDirectLocalBounds( m_overrideBoundsMin, m_overrideBoundsMax, m_mesh, min, max ) ) + { + return false; + } + + return true; +} + +bool EveTransform::GetWorldBoundingBox( Vector3& min, Vector3& max ) const +{ + if( !GetDirectLocalBounds( m_overrideBoundsMin, m_overrideBoundsMax, m_mesh, min, max ) ) + { + return false; + } + BoundingBoxTransform( min, max, m_worldTransform ); + return true; +} + +bool EveTransform::IsBoundingBoxReady() const +{ + Vector3 min; + Vector3 max; + return GetDirectLocalBounds( m_overrideBoundsMin, m_overrideBoundsMax, m_mesh, min, max ); +} + bool EveTransform::GetBoundingSphere( Vector4& sphere, BoundingSphereQuery query ) const { bool valid = false; @@ -506,4 +553,4 @@ void EveTransform::GetPickingBatches( ITriRenderBatchAccumulator* batches, Tr2Pi GetBatches( batches, TRIBATCHTYPE_TRANSPARENT, perObjectData ); GetBatches( batches, TRIBATCHTYPE_ADDITIVE, perObjectData ); } -} \ No newline at end of file +} diff --git a/trinity/Eve/EveTransform.h b/trinity/Eve/EveTransform.h index 669fdd1e8..57f90e6e1 100644 --- a/trinity/Eve/EveTransform.h +++ b/trinity/Eve/EveTransform.h @@ -16,6 +16,7 @@ #include "IEveTransform.h" #include "IWorldPosition.h" +#include "ITr2BoundingBox.h" BLUE_DECLARE( Tr2ParticleSystem ); BLUE_DECLARE_VECTOR( Tr2ParticleSystem ); @@ -37,6 +38,7 @@ BLUE_CLASS( EveTransform ) : public IWorldPosition, public IInitialize, public ITr2CurveSetOwner, + public ITr2BoundingBox, public ITr2DebugRenderable { public: @@ -59,15 +61,17 @@ BLUE_CLASS( EveTransform ) : bool GetBoundingSphere( Vector4 & sphere, BoundingSphereQuery query = EVE_BOUNDS_NORMAL ) const override; void UpdateModelCenterWorldPosition( Vector3 & position, Be::Time t ) override; void GetModelCenterWorldPosition( Vector3 & position ) const override; - bool GetLocalBoundingBox( Vector3 & min, Vector3 & max ) override - { - return false; - } + bool GetLocalBoundingBox( Vector3 & min, Vector3 & max ) override; void GetLocalToWorldTransform( Matrix & transform ) const override { - transform = IdentityMatrix(); + transform = m_worldTransform; } + ///////////////////////////////////////////////////////////////////////////////////// + // ITr2BoundingBox + bool GetWorldBoundingBox( Vector3 & min, Vector3 & max ) const override; + bool IsBoundingBoxReady() const override; + ///////////////////////////////////////////////////////////////////////////////////// // ITr2Renderable - mostly implemented by Tr2Transform except for these virtual Tr2PerObjectData* GetPerObjectData( ITriRenderBatchAccumulator * accumulator ); diff --git a/trinity/Eve/EveTransform_Blue.cpp b/trinity/Eve/EveTransform_Blue.cpp index 5921ea44f..a473438d0 100644 --- a/trinity/Eve/EveTransform_Blue.cpp +++ b/trinity/Eve/EveTransform_Blue.cpp @@ -16,6 +16,7 @@ const Be::ClassInfo* EveTransform::ExposeToBlue() MAP_INTERFACE( ITr2Pickable ) MAP_INTERFACE( IWorldPosition ) MAP_INTERFACE( IInitialize ) + MAP_INTERFACE( ITr2BoundingBox ) MAP_ATTRIBUTE( "hideOnLowQuality", diff --git a/trinity/Include/TriMath.h b/trinity/Include/TriMath.h index e33d03576..af3066168 100644 --- a/trinity/Include/TriMath.h +++ b/trinity/Include/TriMath.h @@ -40,16 +40,10 @@ struct Vector3d; bool TriVectorIsIdentical( const Vector3* v1, const Vector3* v2, float epsilon = 1e-5f ); //Rotate a vector by the quaternion -Vector3* TriVectorRotateQuaternion( - Vector3* out, - const Vector3* v, - const Quaternion* q ); +Vector3* TriVectorRotateQuaternion( Vector3* out, const Vector3* v, const Quaternion* q ); //Rotate a vector by the rotation part of a matrix (ignores translation) -Vector3* TriVectorRotateMatrix( - Vector3* out, - const Vector3* v, - const Matrix* m ); +Vector3* TriVectorRotateMatrix( Vector3* out, const Vector3* v, const Matrix* m ); //The next two functions are the most common application //of the previous two functions and can be optimized greatly @@ -57,59 +51,41 @@ Vector3* TriVectorRotateMatrix( //Rotate a unit vector aligned to one of the axes //(defined by xyz) by the quaternion -Vector3* TriVectorRotatedBasisQuaternion( - Vector3* out, - const TRITRANSFORMAXIS xyz, - const Quaternion* q ); +Vector3* TriVectorRotatedBasisQuaternion( Vector3* out, const TRITRANSFORMAXIS xyz, const Quaternion* q ); //Rotate a unit vector aligned to one of the axes //(defined by xyz) by the matrix -Vector3* TriVectorRotatedBasisMatrix( - Vector3* out, - const TRITRANSFORMAXIS xyz, - const Matrix* m ); +Vector3* TriVectorRotatedBasisMatrix( Vector3* out, const TRITRANSFORMAXIS xyz, const Matrix* m ); // -Vector3* TriVectorSpherical( - Vector3* v, - float phi, - float theta, - float rad ); - -Vector3* TriVectorExponentialDecayInteger( - Vector3* pos, - const Vector3* pos0, - const Vector3* vel0, - const Vector3* acc0, - const float mass, - const float drag, - float time ); - -Vector3* TriVectorExponentialDecay( - Vector3* vel, - const Vector3* vel0, - const Vector3* acc0, - const float mass, - const float drag, - float time ); - -Vector3* TriVectorExponentialDecayInteger( - Vector3* pos, - const Vector3* x, - const Vector3* v, - const Vector3* a, - const float m, - const float k, - const float t, - const float pow ); - -Vector3* TriVectorExponentialDecay( - Vector3* vel, - const Vector3* v, - const Vector3* a, - const float k, - const float pow ); +Vector3* TriVectorSpherical( Vector3* v, float phi, float theta, float rad ); + +Vector3* TriVectorExponentialDecayInteger( Vector3* pos, + const Vector3* pos0, + const Vector3* vel0, + const Vector3* acc0, + const float mass, + const float drag, + float time ); + +Vector3* TriVectorExponentialDecay( Vector3* vel, + const Vector3* vel0, + const Vector3* acc0, + const float mass, + const float drag, + float time ); + +Vector3* TriVectorExponentialDecayInteger( Vector3* pos, + const Vector3* x, + const Vector3* v, + const Vector3* a, + const float m, + const float k, + const float t, + const float pow ); + +Vector3* TriVectorExponentialDecay( Vector3* vel, const Vector3* v, const Vector3* a, const float k, const float pow ); // Projects a point onto a plane Vector3 TriVectorProjectOnPlane( const Vector3& point, const Vector3& p0, const Vector3& n ); @@ -140,10 +116,7 @@ float TriFloatRandomGauss( float mu, float deviation ); ///////////////////////////////////////////////////////////////////////////////////////// // Turns a normalized vector into a color. Used to change directions into color. -Color* TriColorFromVector( - Color* c, - const Vector3* v, - float height = 0.0f ); +Color* TriColorFromVector( Color* c, const Vector3* v, float height = 0.0f ); ///////////////////////////////////////////////////////////////////////////////////////// @@ -159,46 +132,27 @@ Color* TriColorFromVector( // Ergo, the sqrt is half as much rotation around the *same* axis as the quaternion. // This is very useful, since a quaternion rotates by theta when w = cos(theta/2) // See TriQuaternionRotationArc for an example of use. -Quaternion* TriQuaternionSqrt( - Quaternion* out, - const Quaternion* q ); +Quaternion* TriQuaternionSqrt( Quaternion* out, const Quaternion* q ); // Builds a quaternion that is the rotation between two vectors about the origin -Quaternion* TriQuaternionRotationArc( - Quaternion* out, - const Vector3* v1, - const Vector3* v2 ); +Quaternion* TriQuaternionRotationArc( Quaternion* out, const Vector3* v1, const Vector3* v2 ); -Quaternion* TriQuaternionArcFromForward( - Quaternion* out, - const Vector3* v ); +Quaternion* TriQuaternionArcFromForward( Quaternion* out, const Vector3* v ); // does something nice, Eggert, please specify.... -Quaternion* TriQuaternionAxisHeading( - Quaternion* out, - const Quaternion* q, - const Vector3* v ); +Quaternion* TriQuaternionAxisHeading( Quaternion* out, const Quaternion* q, const Vector3* v ); //Takes in a vector, and returns a pure unit quaternion with the same rotation axis as the vectors heading -Quaternion* TriQuaternionDirVector( - Quaternion* out, - const Vector3* v ); +Quaternion* TriQuaternionDirVector( Quaternion* out, const Vector3* v ); //out = in*length -Quaternion* TriQuaternionScale( - Quaternion* out, - const Quaternion* in, - float length ); +Quaternion* TriQuaternionScale( Quaternion* out, const Quaternion* in, float length ); //Pre: q is a unit quaternion //Post: yaw in [0;2*pi[ , pitch in [-pi/2;pi/2]; roll in [-pi; pi] // if you yaw and then pitch and then roll you get the same result as using the quaternion -void TriQuaternionToYawPitchRoll( - float* yaw, - float* pitch, - float* roll, - const Quaternion* q ); +void TriQuaternionToYawPitchRoll( float* yaw, float* pitch, float* roll, const Quaternion* q ); ///////////////////////////////////////////////////////////////////////////////////////// // Matrix extensions @@ -233,14 +187,9 @@ Matrix* TriMatrixRemoveScaling( Matrix* out, const Matrix* in ); Matrix* TriMatrixRemoveTranslation( Matrix* out, const Matrix* in ); Matrix* TriMatrixOverwriteTranslation( Matrix* out, const Matrix* in, const Vector3* t ); -Matrix* TriMatrixRotationArc( - Matrix* out, - const Vector3* v1, - const Vector3* v2 ); +Matrix* TriMatrixRotationArc( Matrix* out, const Vector3* v1, const Vector3* v2 ); -Matrix* TriMatrixArcFromForward( - Matrix* out, - const Vector3* v ); +Matrix* TriMatrixArcFromForward( Matrix* out, const Vector3* v ); ///////////////////////////////////////////////////////////////////////////////////////// @@ -271,8 +220,7 @@ float TriLinearize( float min, float max, float v ); //pre: f in [0.0 ; 1.0] //post: returnvalue in [0.0 ; 1.0] -float SinSmooth( - float f ); +float SinSmooth( float f ); float CubicInterpolate( float f0, float f1, float f2, float f3, float s ); @@ -334,7 +282,8 @@ class TriPerlinNoise * @param frequencyScale The factor by which to scale the frequency of each octave. Typically greater than 1 (e.g., 2) to ensure that higher octaves have higher frequency. * @return The fractal sum of Perlin noise octaves at the given coordinate. */ - [[nodiscard]] double FractalSum( double x, size_t octaves, double amplitudeScale = 0.5, double frequencyScale = 2 ) const; + [[nodiscard]] double + FractalSum( double x, size_t octaves, double amplitudeScale = 0.5, double frequencyScale = 2 ) const; private: static constexpr int32_t TABLE_SIZE = 256; @@ -350,7 +299,12 @@ double PerlinNoise1D( double x, double invAmplitude, double frequency, int octav // // Coordinate system conversion methods // -bool ConvertProjectionCoordToWorldPickRay( float x, float y, const Matrix* projMat, const Matrix* viewMat, Vector3* rayStart, Vector3* rayDir ); +bool ConvertProjectionCoordToWorldPickRay( float x, + float y, + const Matrix* projMat, + const Matrix* viewMat, + Vector3* rayStart, + Vector3* rayDir ); #endif \ No newline at end of file diff --git a/trinity/Interior/Tr2InteriorPlaceable.cpp b/trinity/Interior/Tr2InteriorPlaceable.cpp index 195c91d67..498d2a98a 100644 --- a/trinity/Interior/Tr2InteriorPlaceable.cpp +++ b/trinity/Interior/Tr2InteriorPlaceable.cpp @@ -137,7 +137,7 @@ bool Tr2InteriorPlaceable::GetWorldBoundingBox( Vector3& min, Vector3& max ) con bool Tr2InteriorPlaceable::IsBoundingBoxReady( void ) const { - return ( m_placeableRes && m_placeableRes->IsReady() ); + return m_isBoundingBoxModified || ( m_placeableRes && m_placeableRes->IsReady() ); } void Tr2InteriorPlaceable::PrePhysicsUpdate( Be::Time time ) @@ -648,4 +648,4 @@ void Tr2InteriorPlaceable::GetPickingBatches( ITriRenderBatchAccumulator* batche GetBatches( batches, TRIBATCHTYPE_TRANSPARENT, perObjectData ); GetBatches( batches, TRIBATCHTYPE_ADDITIVE, perObjectData ); } -} \ No newline at end of file +} diff --git a/trinity/Tr2ProjectBoundingBoxBracket.cpp b/trinity/Tr2ProjectBoundingBoxBracket.cpp index 3c6bb5893..c8e9e5d42 100644 --- a/trinity/Tr2ProjectBoundingBoxBracket.cpp +++ b/trinity/Tr2ProjectBoundingBoxBracket.cpp @@ -2,6 +2,8 @@ #include "StdAfx.h" +#include + #include "Tr2ProjectBoundingBoxBracket.h" #include "include/ITr2BoundingBox.h" #include "Tr2Renderer.h" @@ -12,234 +14,347 @@ extern ITr2DebugRendererPtr g_debugRenderer; +namespace +{ +const float CLIP_EPSILON = 1e-5f; -Tr2ProjectBoundingBoxBracket::Tr2ProjectBoundingBoxBracket( IRoot* lockobj /*= NULL */ ) : - m_minProjectedWidth( 0.0f ), - m_minProjectedHeight( 0.0f ), - m_maxProjectedWidth( 0.0f ), - m_maxProjectedHeight( 0.0f ), - m_projectedX( 0.0f ), - m_projectedY( 0.0f ), - m_projectedZ( 0.0f ), - m_projectedWidth( 0.0f ), - m_projectedHeight( 0.0f ), - m_integerCoordinates( true ), - m_screenMargin( 32.0f ), - m_cameraDistance( 0 ) +struct ProjectedBounds +{ + float x; + float y; + float z; + float width; + float height; + bool extendsOffscreen; + 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 + }; +} -void Tr2ProjectBoundingBoxBracket::UpdateValue( double time ) +// 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: +// +// | | +// 1001 | 1000 | 1010 +// ------+--------+------ +// 0001 | 0000 | 0010 <- 0000 = inside +// ------+--------+------ +// 0101 | 0100 | 0110 +// +// AND of all corner codes != 0 => every corner shares an outside plane +// => the box is fully off-frustum. +// XOR of two corner codes & CLIP_NEAR => the edge crosses the near plane. +enum ClipPlaneBits : uint32_t +{ + CLIP_LEFT = 1 << 0, + CLIP_RIGHT = 1 << 1, + CLIP_BOTTOM = 1 << 2, + CLIP_TOP = 1 << 3, + CLIP_NEAR = 1 << 4, + CLIP_FAR = 1 << 5, +}; + +uint32_t ClipOutcode( const Vector4& point ) { - if( !m_object ) + uint32_t code = 0; + if( point.x + point.w < 0.0f ) { - return; + code |= CLIP_LEFT; } - - if( !m_object->IsBoundingBoxReady() ) + if( point.w - point.x < 0.0f ) { - return; + code |= CLIP_RIGHT; } - - Vector3 bbMin, bbMax; - if( !m_object->GetWorldBoundingBox( bbMin, bbMax ) ) + if( point.y + point.w < 0.0f ) { - return; + code |= CLIP_BOTTOM; } - - Vector3 expansion( 0.5f, 0.5f, 0.5f ); - Vector3 expandedMin = bbMin - expansion; - Vector3 expandedMax = bbMax + expansion; - if( BoundingBoxIsInside( expandedMin, expandedMax, Tr2Renderer::GetViewPosition() ) ) + if( point.w - point.y < 0.0f ) { - // Camera is inside bounding box - can't do any sensible projection - SetEmptyProjection(); - return; + code |= CLIP_TOP; } - - Vector3 center = ( bbMax + bbMin ) * 0.5f; - Vector3 projectedCenter; - Matrix viewProj; - projectedCenter = TransformCoord( center, Tr2Renderer::GetViewTransform() ); - projectedCenter = TransformCoord( projectedCenter, Tr2Renderer::GetProjectionTransform() ); - Vec3TransformByViewport( projectedCenter, Tr2Renderer::GetViewport() ); - if( projectedCenter.z <= 0.0f || projectedCenter.z >= 1.0f ) + if( point.z < 0.0f ) { - SetEmptyProjection(); - return; + code |= CLIP_NEAR; } - - Vector3 d = Tr2Renderer::GetViewPosition() - center; - m_cameraDistance = Length( d ); - - BoundingBoxProject( - bbMin, - bbMax, - Tr2Renderer::GetViewTransform(), - Tr2Renderer::GetProjectionTransform(), - Tr2Renderer::GetViewport() ); - - if( bbMin.z <= 0.0f || bbMax.z >= 1.0f ) + if( point.w - point.z < 0.0f ) { - SetEmptyProjection(); - return; + code |= CLIP_FAR; } + return code; +} - m_projectedZ = std::min( bbMin.z, bbMax.z ); - - unsigned int screenWidth; - unsigned int screenHeight; - Tr2Renderer::GetBackBufferDimensions( screenWidth, screenHeight ); +bool CanPerspectiveDivide( const Vector4& point ) +{ + return fabsf( point.w ) > CLIP_EPSILON; +} - if( ( bbMin.x > screenWidth ) || ( bbMax.x < 0.0f ) || ( bbMin.y > screenHeight ) || ( bbMax.y < 0.0f ) ) +// 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 ) +{ + float denominator = a.z - b.z; + if( fabsf( denominator ) <= CLIP_EPSILON ) { - SetEmptyProjection(); return; } - bbMin.x = std::max( bbMin.x, m_screenMargin ); - bbMax.x = std::min( bbMax.x, (float)screenWidth - m_screenMargin ); - - bbMin.y = std::max( bbMin.y, m_screenMargin ); - bbMax.y = std::min( bbMax.y, (float)screenHeight - m_screenMargin ); - - m_projectedWidth = bbMax.x - bbMin.x; - m_projectedHeight = bbMax.y - bbMin.y; - - bool useCenter3d = false; - if( m_maxProjectedWidth > 0.0f || m_maxProjectedHeight > 0.0f ) + float t = a.z / denominator; + Vector4 point = Lerp( a, b, t ); + if( CanPerspectiveDivide( point ) ) { - useCenter3d = true; + points.push_back( point ); } +} - float maxWidth = m_maxProjectedWidth; - if( maxWidth == 0.0f ) +bool ProjectClipPoint( const Vector4& point, const TriViewport& viewport, Vector3& projected ) +{ + if( !CanPerspectiveDivide( point ) ) { - maxWidth = 1e6f; + return false; } - if( m_projectedWidth < m_minProjectedWidth ) - { - m_projectedWidth = m_minProjectedWidth; - } - else if( m_projectedWidth > maxWidth ) - { - m_projectedWidth = maxWidth; - } + float reciprocalW = 1.0f / point.w; + projected.x = viewport.x + ( 1.0f + point.x * reciprocalW ) * 0.5f * viewport.width; + projected.y = viewport.y + ( 1.0f - point.y * reciprocalW ) * 0.5f * viewport.height; + projected.z = viewport.minZ + point.z * reciprocalW * ( viewport.maxZ - viewport.minZ ); + return true; +} - float maxHeight = m_maxProjectedHeight; - if( maxHeight == 0.0f ) +bool ProjectBoundingBoxToViewport( const Vector3& bbMin, const Vector3& bbMax, 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 ) { - maxHeight = 1e6f; + clipCorners[i] = TransformPointToClip( corners[i], viewProjection ); + outcodes[i] = ClipOutcode( clipCorners[i] ); + combinedOutcode &= outcodes[i]; } - if( m_projectedHeight < m_minProjectedHeight ) - { - m_projectedHeight = m_minProjectedHeight; - } - else if( m_projectedHeight > maxHeight ) + // A surviving bit means every corner is outside the same plane + if( combinedOutcode != 0 ) { - m_projectedHeight = maxHeight; + return false; } - float centerX; - float centerY; - if( useCenter3d ) + std::vector projectablePoints; + projectablePoints.reserve( 20 ); + for( int i = 0; i < 8; ++i ) { - // Bounded brackets are centered around the center of the 3d bounding box - centerX = projectedCenter.x; - centerY = projectedCenter.y; + if( !( outcodes[i] & CLIP_NEAR ) && CanPerspectiveDivide( clipCorners[i] ) ) + { + projectablePoints.push_back( clipCorners[i] ); + } } - else + + 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 ) { - // Unbounded brackets are centered around the center of the projected bounding box - centerX = ( bbMin.x + bbMax.x ) * 0.5f; - centerY = ( bbMin.y + bbMax.y ) * 0.5f; + // XOR: the edge endpoints straddle the near plane + if( ( outcodes[EDGES[i][0]] ^ outcodes[EDGES[i][1]] ) & CLIP_NEAR ) + { + AddNearPlaneIntersection( clipCorners[EDGES[i][0]], clipCorners[EDGES[i][1]], projectablePoints ); + } } - m_projectedX = centerX - m_projectedWidth * 0.5f; - m_projectedY = centerY - m_projectedHeight * 0.5f; - if( m_integerCoordinates ) + if( projectablePoints.empty() ) { - m_projectedX = floor( m_projectedX + 0.5f ); - m_projectedY = floor( m_projectedY + 0.5f ); - m_projectedWidth = floor( m_projectedWidth + 0.5f ); - m_projectedHeight = floor( m_projectedHeight + 0.5f ); + return false; } - if( m_projectedX < m_screenMargin ) + Vector3 projected; + bool hasProjectedPoint = false; + float minX = 0.0f; + float minY = 0.0f; + float minZ = 0.0f; + float maxX = 0.0f; + float maxY = 0.0f; + + for( const Vector4& point : projectablePoints ) { - float d = m_screenMargin - m_projectedX; - if( d < m_projectedWidth - m_screenMargin ) - { - m_projectedX = m_screenMargin; - } - else + if( !ProjectClipPoint( point, viewport, projected ) ) { - SetEmptyProjection(); - return; + continue; } - } - if( m_projectedY < m_screenMargin ) - { - float d = m_screenMargin - m_projectedY; - if( d < m_projectedHeight - m_screenMargin ) + if( !hasProjectedPoint ) { - m_projectedY = m_screenMargin; + minX = maxX = projected.x; + minY = maxY = projected.y; + minZ = projected.z; + hasProjectedPoint = true; } else { - SetEmptyProjection(); - return; + minX = std::min( minX, projected.x ); + maxX = std::max( maxX, projected.x ); + minY = std::min( minY, projected.y ); + maxY = std::max( maxY, projected.y ); + minZ = std::min( minZ, projected.z ); } - m_projectedY = m_screenMargin; } - if( m_projectedX + m_projectedWidth > screenWidth - m_screenMargin ) + if( !hasProjectedPoint ) { - m_projectedWidth = screenWidth - m_screenMargin - m_projectedX; - if( m_projectedWidth < m_screenMargin ) - { - SetEmptyProjection(); - return; - } + return false; } - if( m_projectedY + m_projectedHeight > screenHeight - m_screenMargin ) + float width = maxX - minX; + float height = maxY - minY; + + float viewportLeft = static_cast( viewport.x ); + float viewportTop = static_cast( viewport.y ); + float viewportRight = viewportLeft + static_cast( viewport.width ); + float viewportBottom = viewportTop + static_cast( viewport.height ); + + bounds.x = minX; + bounds.y = minY; + bounds.z = minZ; + bounds.width = width; + bounds.height = height; + bounds.extendsOffscreen = minX < viewportLeft || minY < viewportTop || maxX > viewportRight || maxY > viewportBottom; + bounds.coversViewport = minX <= viewportLeft && minY <= viewportTop && maxX >= viewportRight && maxY >= viewportBottom; + return true; +} + +bool ClampToScreenMargin( const TriViewport& viewport, float margin, float& x, float& y, float& width, float& height ) +{ + if( margin <= 0.0f ) { - m_projectedHeight = screenHeight - m_screenMargin - m_projectedY; - if( m_projectedHeight < m_screenMargin ) - { - SetEmptyProjection(); - return; - } + return true; } - if( m_bracket ) + float left = static_cast( viewport.x ) + margin; + float top = static_cast( viewport.y ) + margin; + float right = static_cast( viewport.x ) + static_cast( viewport.width ) - margin; + float bottom = static_cast( viewport.y ) + static_cast( viewport.height ) - margin; + + float minX = std::max( x, left ); + float minY = std::max( y, top ); + float maxX = std::min( x + width, right ); + float maxY = std::min( y + height, bottom ); + + if( maxX <= minX || maxY <= minY ) { - m_bracket->SetDisplayX( m_projectedX ); - m_bracket->SetDisplayY( m_projectedY ); - m_bracket->SetDisplayWidth( m_projectedWidth ); - m_bracket->SetDisplayHeight( m_projectedHeight ); + return false; } - if( g_debugRenderer ) + x = minX; + y = minY; + width = maxX - minX; + height = maxY - minY; + return true; +} + +float ClampProjectedSize( float size, float minSize, float maxSize ) +{ + if( minSize > 0.0f && size < minSize ) { - int x = (int)m_projectedX; - int y = (int)m_projectedY; - g_debugRenderer->Printf( x, y, 0xffffffff, "%S", m_name.c_str() ); - y += 16; + return minSize; + } + if( maxSize > 0.0f && size > maxSize ) + { + return maxSize; + } + return size; +} +} - g_debugRenderer->Printf( x, y, 0xffffffff, "(%5.2f, %5.2f, %5.2f)", bbMin.x, bbMin.y, bbMin.z ); - y += 16; - g_debugRenderer->Printf( x, y, 0xffffffff, "(%5.2f, %5.2f, %5.2f)", bbMax.x, bbMax.y, bbMax.z ); - y += 16; +Tr2ProjectBoundingBoxBracket::Tr2ProjectBoundingBoxBracket( IRoot* lockobj /*= NULL */ ) : + m_minProjectedWidth( 0.0f ), + m_minProjectedHeight( 0.0f ), + m_maxProjectedWidth( 0.0f ), + m_maxProjectedHeight( 0.0f ), + m_projectedX( 0.0f ), + m_projectedY( 0.0f ), + m_projectedZ( 0.0f ), + m_projectedWidth( 0.0f ), + m_projectedHeight( 0.0f ), + m_integerCoordinates( true ), + m_screenMargin( 0.0f ), + m_cameraDistance( 0 ), + m_isProjectionValid( false ), + m_containsCamera( false ), + m_extendsOffscreen( false ), + m_coversViewport( false ) +{ +} - g_debugRenderer->Printf( x, y, 0xffffffff, "(%5.2f, %5.2f)", m_projectedWidth, m_projectedHeight ); + +void Tr2ProjectBoundingBoxBracket::UpdateValue( double time ) +{ + Vector3 bbMin, bbMax; + if( !m_object || !m_object->IsBoundingBoxReady() || !m_object->GetWorldBoundingBox( bbMin, bbMax ) ) + { + SetEmptyProjection(); + return; + } + + const Vector3 center = ( bbMax + bbMin ) * 0.5f; + const Vector3 viewPosition = Tr2Renderer::GetViewPosition(); + m_cameraDistance = Length( viewPosition - center ); + + const TriViewport& viewport = Tr2Renderer::GetViewport(); + if( BoundingBoxIsInside( bbMin, bbMax, viewPosition ) ) + { + SetFullViewportProjection( viewport ); + return; } + + Matrix viewProjection = Tr2Renderer::GetViewTransform() * Tr2Renderer::GetProjectionTransform(); + ProjectedBounds projectedBounds; + if( !ProjectBoundingBoxToViewport( bbMin, bbMax, viewProjection, viewport, projectedBounds ) ) + { + SetEmptyProjection(); + return; + } + + m_projectedX = projectedBounds.x; + m_projectedY = projectedBounds.y; + m_projectedZ = projectedBounds.z; + m_projectedWidth = projectedBounds.width; + m_projectedHeight = projectedBounds.height; + m_containsCamera = false; + m_extendsOffscreen = projectedBounds.extendsOffscreen; + m_coversViewport = projectedBounds.coversViewport; + + ConstrainProjection( center, viewProjection, viewport ); + PublishProjection( viewport ); } void Tr2ProjectBoundingBoxBracket::SetEmptyProjection() @@ -249,7 +364,16 @@ void Tr2ProjectBoundingBoxBracket::SetEmptyProjection() m_projectedZ = 0.0f; m_projectedWidth = 0.0f; m_projectedHeight = 0.0f; + m_isProjectionValid = false; + m_containsCamera = false; + m_extendsOffscreen = false; + m_coversViewport = false; + + UpdateBracket(); +} +void Tr2ProjectBoundingBoxBracket::UpdateBracket() +{ if( m_bracket ) { m_bracket->SetDisplayX( m_projectedX ); @@ -258,3 +382,78 @@ void Tr2ProjectBoundingBoxBracket::SetEmptyProjection() m_bracket->SetDisplayHeight( m_projectedHeight ); } } +void Tr2ProjectBoundingBoxBracket::SetFullViewportProjection( const TriViewport& viewport ) +{ + m_projectedX = static_cast( viewport.x ); + m_projectedY = static_cast( viewport.y ); + m_projectedZ = viewport.minZ; + m_projectedWidth = static_cast( viewport.width ); + m_projectedHeight = static_cast( viewport.height ); + if( !ClampToScreenMargin( viewport, m_screenMargin, m_projectedX, m_projectedY, m_projectedWidth, m_projectedHeight ) ) + { + SetEmptyProjection(); + return; + } + m_isProjectionValid = true; + m_containsCamera = true; + m_extendsOffscreen = true; + m_coversViewport = true; + UpdateBracket(); +} +void Tr2ProjectBoundingBoxBracket::ConstrainProjection( const Vector3& center, const Matrix& viewProjection, const TriViewport& viewport ) +{ + float centerX = m_projectedX + m_projectedWidth * 0.5f; + float centerY = m_projectedY + m_projectedHeight * 0.5f; + if( m_maxProjectedWidth > 0.0f || m_maxProjectedHeight > 0.0f ) + { + // 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 ); + Vector3 projectedCenter; + if( clipCenter.z >= 0.0f && clipCenter.w > 0.0f && ProjectClipPoint( clipCenter, viewport, projectedCenter ) ) + { + centerX = projectedCenter.x; + centerY = projectedCenter.y; + } + } + + m_projectedWidth = ClampProjectedSize( m_projectedWidth, m_minProjectedWidth, m_maxProjectedWidth ); + m_projectedHeight = ClampProjectedSize( m_projectedHeight, m_minProjectedHeight, m_maxProjectedHeight ); + + m_projectedX = centerX - m_projectedWidth * 0.5f; + m_projectedY = centerY - m_projectedHeight * 0.5f; + + if( m_integerCoordinates ) + { + m_projectedX = floor( m_projectedX + 0.5f ); + m_projectedY = floor( m_projectedY + 0.5f ); + m_projectedWidth = floor( m_projectedWidth + 0.5f ); + m_projectedHeight = floor( m_projectedHeight + 0.5f ); + } +} +void Tr2ProjectBoundingBoxBracket::PublishProjection( const TriViewport& viewport ) +{ + if( m_projectedWidth <= 0.0f || m_projectedHeight <= 0.0f ) + { + SetEmptyProjection(); + return; + } + + if( !ClampToScreenMargin( viewport, m_screenMargin, m_projectedX, m_projectedY, m_projectedWidth, m_projectedHeight ) ) + { + SetEmptyProjection(); + return; + } + + m_isProjectionValid = true; + UpdateBracket(); + + if( g_debugRenderer ) + { + int x = static_cast( m_projectedX ); + int y = static_cast( m_projectedY ); + g_debugRenderer->Printf( x, y, 0xffffffff, "%S", m_name.c_str() ); + y += 16; + g_debugRenderer->Printf( x, y, 0xffffffff, "(%5.2f, %5.2f)", m_projectedWidth, m_projectedHeight ); + } +} diff --git a/trinity/Tr2ProjectBoundingBoxBracket.h b/trinity/Tr2ProjectBoundingBoxBracket.h index 6cb1bab0d..36e62c7f5 100644 --- a/trinity/Tr2ProjectBoundingBoxBracket.h +++ b/trinity/Tr2ProjectBoundingBoxBracket.h @@ -23,8 +23,13 @@ class Tr2ProjectBoundingBoxBracket : public ITriFunction void UpdateValue( double time ); void SetEmptyProjection(); + void UpdateBracket(); protected: + void SetFullViewportProjection( const TriViewport& viewport ); + void ConstrainProjection( const Vector3& center, const Matrix& viewProjection, const TriViewport& viewport ); + void PublishProjection( const TriViewport& viewport ); + std::wstring m_name; ////////////////////////////////////////////////////////////////////////// @@ -54,6 +59,11 @@ class Tr2ProjectBoundingBoxBracket : public ITriFunction float m_projectedHeight; float m_cameraDistance; float m_screenMargin; + + bool m_isProjectionValid; + bool m_containsCamera; + bool m_extendsOffscreen; + bool m_coversViewport; }; TYPEDEF_BLUECLASS( Tr2ProjectBoundingBoxBracket ); diff --git a/trinity/Tr2ProjectBoundingBoxBracket_Blue.cpp b/trinity/Tr2ProjectBoundingBoxBracket_Blue.cpp index 0976638a4..d9e3c7870 100644 --- a/trinity/Tr2ProjectBoundingBoxBracket_Blue.cpp +++ b/trinity/Tr2ProjectBoundingBoxBracket_Blue.cpp @@ -9,7 +9,7 @@ BLUE_DEFINE( Tr2ProjectBoundingBoxBracket ); const Be::ClassInfo* Tr2ProjectBoundingBoxBracket::ExposeToBlue() { - EXPOSURE_BEGIN( Tr2ProjectBoundingBoxBracket, "Projects a 3D bounding box to 2D for brackets \n:jessica-deprecated: True" ) + EXPOSURE_BEGIN( Tr2ProjectBoundingBoxBracket, "Projects a 3D bounding box to 2D for brackets" ) MAP_INTERFACE( ITriFunction ) MAP_INTERFACE( Tr2ProjectBoundingBoxBracket ) @@ -40,25 +40,29 @@ const Be::ClassInfo* Tr2ProjectBoundingBoxBracket::ExposeToBlue() MAP_ATTRIBUTE( "minProjectedWidth", m_minProjectedWidth, - "Minimum width after projection", + "Minimum width after projection. Not applied while containsCamera\n" + "is true: the rect is the full viewport inset by screenMargin.", Be::READWRITE ) MAP_ATTRIBUTE( "minProjectedHeight", m_minProjectedHeight, - "Minimum height after projection", + "Minimum height after projection. Not applied while containsCamera\n" + "is true: the rect is the full viewport inset by screenMargin.", Be::READWRITE ) MAP_ATTRIBUTE( "maxProjectedWidth", m_maxProjectedWidth, - "Maximum width after projection", + "Maximum width after projection. Not applied while containsCamera\n" + "is true: the rect is the full viewport inset by screenMargin.", Be::READWRITE ) MAP_ATTRIBUTE( "maxProjectedHeight", m_maxProjectedHeight, - "Maximum height after projection", + "Maximum height after projection. Not applied while containsCamera\n" + "is true: the rect is the full viewport inset by screenMargin.", Be::READWRITE ) MAP_ATTRIBUTE( @@ -90,7 +94,9 @@ const Be::ClassInfo* Tr2ProjectBoundingBoxBracket::ExposeToBlue() "cameraDistance", m_cameraDistance, "Distance of the object from the camera, as determined by the\n" - "center of the bounding box.", + "center of the bounding box. Measured in the space the object is\n" + "rendered in: render-scaled objects (e.g. planets) report the\n" + "distance to their scaled proxy, not the true world distance.", Be::READ ) MAP_ATTRIBUTE( @@ -102,9 +108,42 @@ const Be::ClassInfo* Tr2ProjectBoundingBoxBracket::ExposeToBlue() MAP_ATTRIBUTE( "screenMargin", m_screenMargin, - "Brackets are never projected outside the screen - this controls the margin from" - "\nthe edges of the screen.", + "If greater than zero, the projected rect is clamped inside the viewport" + "\ninset by this many pixels, and the projection becomes empty when nothing" + "\nremains inside that safe frame. Zero (default) disables clamping.", Be::READWRITE ) + MAP_ATTRIBUTE( + "isProjectionValid", + m_isProjectionValid, + "True when the bounding box produced a valid projected rect.", + Be::READ ) + + MAP_ATTRIBUTE( + "containsCamera", + m_containsCamera, + "True when the camera is inside the tracked bounding box.", + Be::READ ) + + MAP_ATTRIBUTE( + "extendsOffscreen", + m_extendsOffscreen, + "True when the raw projection of the bounding box extends beyond the\n" + "current viewport. Describes the projection itself, not the output rect:\n" + "min/max sizing, recentering and screenMargin clamping do not affect\n" + "this flag. Compare projectedX/Y/Width/Height against the viewport if\n" + "you need the output rect's overlap instead. Always true when\n" + "containsCamera is true.", + Be::READ ) + + MAP_ATTRIBUTE( + "coversViewport", + m_coversViewport, + "True when the raw projection of the bounding box covers the entire\n" + "viewport. Describes the projection itself, not the output rect:\n" + "min/max sizing, recentering and screenMargin clamping do not affect\n" + "this flag. Always true when containsCamera is true.", + Be::READ ) + EXPOSURE_END() } diff --git a/trinity/Utilities/BoundingBox.cpp b/trinity/Utilities/BoundingBox.cpp index 209ca0bd1..3627049f9 100644 --- a/trinity/Utilities/BoundingBox.cpp +++ b/trinity/Utilities/BoundingBox.cpp @@ -265,68 +265,6 @@ void BoundingBoxTransform( Vector3& min, Vector3& max, const Matrix& tf ) } } -void BoundingBoxProject( Vector3& min, Vector3& max, const Matrix& view, const Matrix& proj, const TriViewport& vp ) -{ - Vector3 corners[8]; - - corners[0] = min; - - corners[1].x = min.x; - corners[1].y = min.y; - corners[1].z = max.z; - - corners[2].x = max.x; - corners[2].y = min.y; - corners[2].z = min.z; - - corners[3].x = max.x; - corners[3].y = min.y; - corners[3].z = max.z; - - corners[4] = max; - - corners[5].x = max.x; - corners[5].y = max.y; - corners[5].z = min.z; - - corners[6].x = min.x; - corners[6].y = max.y; - corners[6].z = max.z; - - corners[7].x = min.x; - corners[7].y = max.y; - corners[7].z = min.z; - - Matrix viewProj = view * proj; - for( int i = 0; i < 8; ++i ) - { - corners[i] = TransformCoord( corners[i], viewProj ); - Vec3TransformByViewport( corners[i], vp ); - } - - min = corners[0]; - max = corners[0]; - float* pMin = (float*)&min; - float* pMax = (float*)&max; - - for( int i = 1; i < 8; ++i ) - { - float* pCorner = (float*)&corners[i]; - - for( int component = 0; component < 3; ++component ) - { - if( pCorner[component] < pMin[component] ) - { - pMin[component] = pCorner[component]; - } - if( pCorner[component] > pMax[component] ) - { - pMax[component] = pCorner[component]; - } - } - } -} - bool IntersectAxisAlignedBoxAxisAlignedBox( const Vector3& minBoundsA, const Vector3& maxBoundsA, const Vector3& minBoundsB, const Vector3& maxBoundsB ) { XMVECTOR MinA = XMVectorSet( minBoundsA.x, minBoundsA.y, minBoundsA.z, 0.0f ); diff --git a/trinity/Utilities/BoundingBox.h b/trinity/Utilities/BoundingBox.h index 6b057dd2b..ce193e241 100644 --- a/trinity/Utilities/BoundingBox.h +++ b/trinity/Utilities/BoundingBox.h @@ -28,10 +28,6 @@ void BoundingBoxUpdate( Vector3& min, Vector3& max, const Vector4& sphere ); // the new axis aligned bounding box void BoundingBoxTransform( Vector3& min, Vector3& max, const Matrix& tf ); -// Projects an axis aligned bounding box into screen space with the given view -// and projection matrices, along with a viewport. -void BoundingBoxProject( Vector3& min, Vector3& max, const Matrix& proj, const Matrix& view, const TriViewport& vp ); - bool IntersectAxisAlignedBoxAxisAlignedBox( const Vector3& minBoundsA, const Vector3& maxBoundsA, const Vector3& minBoundsB, const Vector3& maxBoundsB ); bool IntersectOrientedBoxAxisAlignedBox( const Vector3& centerA, const Vector3& extentsA, const Quaternion& orientationA, const Vector3& minBounds, const Vector3& maxBounds ); bool IntersectOrientedBoxOrientedBox( const Vector3& centerA, const Vector3& extentsA, const Quaternion& orientationA, const Vector3& centerB, const Vector3& extentsB, const Quaternion& orientationB );