443 lines
14 KiB
C++
443 lines
14 KiB
C++
#include <worldsim/SkidMarks/skidmark.h>
|
|
#include <camera/supercammanager.h>
|
|
#include <debug/profiler.h>
|
|
|
|
const float METERS_PER_TEX_UNIT = 0.5f;
|
|
const float TEX_UNITS_PER_METER = 1.0f / METERS_PER_TEX_UNIT;
|
|
|
|
|
|
|
|
Skidmark::Skidmark():
|
|
m_NumSegments( 0 ),
|
|
mpShader( NULL ),
|
|
m_IsInDSG( false ),
|
|
m_LastU( 0.0f ),
|
|
m_TextureDirection( 1.0f ),
|
|
m_FadedOut( false ),
|
|
m_FadingIn( false )
|
|
{
|
|
// Skidmarks are always translucent
|
|
mTranslucent = true;
|
|
SetName( "Skidmark" );
|
|
}
|
|
|
|
Skidmark::~Skidmark()
|
|
{
|
|
if ( mpShader != NULL )
|
|
{
|
|
mpShader->Release();
|
|
mpShader = NULL;
|
|
}
|
|
if ( m_IsInDSG )
|
|
{
|
|
RemoveFromDSG();
|
|
m_IsInDSG = false;
|
|
}
|
|
}
|
|
|
|
bool Skidmark::IsVisible()const
|
|
{
|
|
if ( m_NumSegments < 2 || m_FadedOut )
|
|
return false;
|
|
|
|
rmt::Sphere boundingSphere = m_BoundingBox.GetBoundingSphere();
|
|
|
|
if (GetSuperCamManager()->GetSCC( 0 )->GetCamera()->SphereVisible( boundingSphere.centre, boundingSphere.radius ) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void Skidmark::Display()
|
|
{
|
|
if ( m_NumSegments < 2 || mpShader == NULL )
|
|
return;
|
|
|
|
BEGIN_PROFILE("Skidmark::Display")
|
|
bool oldZWrite = p3d::pddi->GetZWrite();
|
|
pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
|
|
if( oldZWrite )
|
|
{
|
|
p3d::pddi->SetZWrite( false );
|
|
}
|
|
|
|
pddiPrimStream* pStream = p3d::pddi->BeginPrims( mpShader->GetShader(), PDDI_PRIM_TRISTRIP, PDDI_V_CT, m_NumSegments * 2 );
|
|
for ( int i = 0 ; i < m_NumSegments ; i++ )
|
|
{
|
|
tColour colour( m_Segments[i].intensity, m_Segments[i].intensity, m_Segments[i].intensity, m_Segments[i].intensity );
|
|
pStream->Vertex( &m_Segments[ i ].left.position, colour, &m_Segments[ i ].left.uv );
|
|
pStream->Vertex( &m_Segments[ i ].right.position, colour, &m_Segments[ i ].right.uv );
|
|
}
|
|
p3d::pddi->EndPrims( pStream );
|
|
|
|
if( oldZWrite )
|
|
{
|
|
p3d::pddi->SetZWrite( true );
|
|
}
|
|
END_PROFILE("Skidmark::Display")
|
|
}
|
|
|
|
void Skidmark::Extend( const rmt::Vector& position, const rmt::Vector& forward, const rmt::Vector groundNormal, float halfWidth, float intensity )
|
|
{
|
|
const float Z_FIGHTING_OFFSET = 0.1f;
|
|
|
|
rAssert( SpaceLeft () );
|
|
rmt::Vector elevatedPosition = position + (groundNormal * Z_FIGHTING_OFFSET);
|
|
|
|
|
|
|
|
rmt::Box3D oldBoundingBox;
|
|
GetBoundingBox( &oldBoundingBox );
|
|
|
|
m_CurrentDirection = forward;
|
|
|
|
unsigned char ucIntensity = static_cast< unsigned char >( intensity * 255.0f );
|
|
|
|
// if vertices have been laid already
|
|
// find texture break position
|
|
// it is on the straight line vector from the last position to the current one
|
|
// extend the last two vertices laid to the texture break position
|
|
// the texture break position is recorded as the last position written
|
|
// lay down two more at the current (given position)
|
|
if ( m_NumSegments > 0 )
|
|
{
|
|
// Check to see that we have travelled the minimum distance from the last
|
|
// point that we laid down a segment
|
|
if ( ( elevatedPosition - m_LastPositionWritten ).MagnitudeSqr() < 0.05f )
|
|
return;
|
|
|
|
rmt::Vector toPosition = elevatedPosition - m_LastPositionWritten;
|
|
toPosition.Normalize();
|
|
rmt::Vector right;
|
|
right.CrossProduct( toPosition, groundNormal );
|
|
right.Normalize();
|
|
|
|
rmt::Vector textureBreakPosition;
|
|
float breakPointU;
|
|
float distanceToTextureBreakPoint;
|
|
float distanceToGivenPoint;
|
|
if ( FindTextureBreakPosition( elevatedPosition, &textureBreakPosition, &breakPointU, &distanceToTextureBreakPoint, &distanceToGivenPoint ) )
|
|
{
|
|
float distanceFromStart = distanceToTextureBreakPoint + m_Segments[ m_NumSegments - 1].distanceFromStart;
|
|
// ExtendVertices( textureBreakPosition, right, halfWidth, ucIntensity, breakPointU, distanceFromStart );
|
|
m_LastPositionWritten = textureBreakPosition;
|
|
// Reverse texture direction
|
|
m_TextureDirection *= -1.0f;
|
|
// Calculate the texture coordinate of the elevated position
|
|
// u = distance( tbp, ep ) * TEX_UNITS_PER_METER * texturedirection
|
|
//float distance = (textureBreakPosition - elevatedPosition).Magnitude();
|
|
float currentU = breakPointU + distanceToTextureBreakPoint * TEX_UNITS_PER_METER * m_TextureDirection;
|
|
if ( currentU > 1.0f )
|
|
currentU = 1.0f;
|
|
else if ( currentU < 0 )
|
|
currentU = 0;
|
|
m_LastU = currentU;
|
|
|
|
distanceFromStart = distanceToGivenPoint + m_Segments[ m_NumSegments - 1].distanceFromStart;
|
|
WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
|
|
WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
|
|
m_LastPositionWritten = elevatedPosition;
|
|
}
|
|
else
|
|
{
|
|
// We haven't crossed a texture break point boundary
|
|
// extend the last two vertices written to the current position
|
|
// then write two more vertices, also at the current position for use in
|
|
// the next extension
|
|
float distance = (m_LastPositionWritten - elevatedPosition).Magnitude();
|
|
|
|
float currentU = m_LastU + distance * TEX_UNITS_PER_METER * m_TextureDirection;
|
|
|
|
float distanceFromStart = m_Segments[ m_NumSegments - 1 ].distanceFromStart + distance;
|
|
|
|
ExtendVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
|
|
WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
|
|
m_LastPositionWritten = elevatedPosition;
|
|
m_LastU = currentU;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
rmt::Vector right;
|
|
right.CrossProduct( forward, groundNormal );
|
|
right.Normalize();
|
|
|
|
// Brand new strip
|
|
// write 4 vertices, first two are fixed, 2nd two are temporaries
|
|
WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, 0, 0 );
|
|
WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, 0, 0 );
|
|
m_TextureDirection = 1.0f;
|
|
m_LastU = 0;
|
|
m_LastPositionWritten = elevatedPosition;
|
|
}
|
|
// Toss it in the dsg if its not there already
|
|
if ( m_IsInDSG == false )
|
|
{
|
|
AddToDSG( RenderEnums::LevelSlot );
|
|
}
|
|
else
|
|
{
|
|
MoveInDSG( oldBoundingBox );
|
|
}
|
|
|
|
}
|
|
|
|
void Skidmark::ClearVertices()
|
|
{
|
|
// Reset the bounding box
|
|
|
|
// Which direction are we laying our coordinates, either 1.0 or -1.0
|
|
m_LastU = 0.0f;
|
|
|
|
RemoveFromDSG();
|
|
m_NumSegments = 0;
|
|
m_FadedOut = false;
|
|
m_FadingIn = false;
|
|
|
|
m_BoundingBox.low = rmt::Vector(FLT_MAX,FLT_MAX,FLT_MAX);
|
|
m_BoundingBox.high = rmt::Vector(-FLT_MAX,-FLT_MAX,-FLT_MAX);
|
|
|
|
}
|
|
|
|
void Skidmark::ContinueSkidmark( Skidmark* newSkidmark )
|
|
{
|
|
tRefCounted::Assign( newSkidmark->mpShader, mpShader );
|
|
newSkidmark->ClearVertices();
|
|
newSkidmark->m_LastPositionWritten = m_LastPositionWritten;
|
|
newSkidmark->m_Segments[ 0 ] = m_Segments[ m_NumSegments - 1 ];
|
|
newSkidmark->m_Segments[ 1 ] = m_Segments[ m_NumSegments - 2 ];
|
|
newSkidmark->m_NumSegments = 2;
|
|
newSkidmark->m_LastU = m_LastU;
|
|
newSkidmark->m_TextureDirection = m_TextureDirection;
|
|
newSkidmark->m_CurrentDirection = m_CurrentDirection;
|
|
rmt::Box3D oldBox;
|
|
GetBoundingBox( &oldBox );
|
|
|
|
newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 0 ].left.position );
|
|
newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 0 ].right.position );
|
|
newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 1 ].left.position );
|
|
newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 1 ].right.position );
|
|
|
|
m_NumSegments--;
|
|
|
|
}
|
|
|
|
void Skidmark::FadeInVertices()
|
|
{
|
|
const float DISTANCE_TO_FADE_IN = 1.0f;
|
|
|
|
// We want to fade in the last few vertices, but we don't want to make the fade greater in intensity than
|
|
// existing ones, so check before writing new values
|
|
|
|
if ( m_NumSegments < 1 )
|
|
return;
|
|
|
|
for ( int i = 0 ; i < m_NumSegments ; i++ )
|
|
{
|
|
int intensity = static_cast< int >( m_Segments[ i ].distanceFromStart / DISTANCE_TO_FADE_IN * 255.0f );
|
|
if ( m_Segments[ i ].intensity > intensity )
|
|
m_Segments[ i ].intensity = intensity;
|
|
}
|
|
|
|
}
|
|
|
|
void Skidmark::FadeOutTrailingVertices()
|
|
{
|
|
const float DISTANCE_TO_FADE_OUT = 1.0f;
|
|
|
|
rAssert(m_NumSegments != 0); // happened to me once, probably bad, you can click
|
|
// through okay now, but we might want to fix it at some point
|
|
// nbrooke, apr 11, 2003
|
|
if(m_NumSegments == 0)
|
|
return;
|
|
|
|
// We want to fade out the last few vertices, but we don't want to make the fade greater in intensity than
|
|
// existing ones, so check before writing new values
|
|
|
|
// Last segment is zero
|
|
// next to last is -1.dist - this.dist
|
|
|
|
float distFadedOutSoFar = 0;
|
|
m_Segments[ m_NumSegments - 1 ].intensity = 0;
|
|
int intensity = 0;
|
|
|
|
for ( int i = m_NumSegments - 2 ; i >= 0 ; i-- )
|
|
{
|
|
distFadedOutSoFar += m_Segments[ i + 1 ].distanceFromStart - m_Segments[ i ].distanceFromStart;
|
|
intensity = static_cast< int >( distFadedOutSoFar / DISTANCE_TO_FADE_OUT * 255.0f );
|
|
if ( m_Segments[ i ].intensity > intensity )
|
|
m_Segments[ i ].intensity = intensity;
|
|
}
|
|
|
|
}
|
|
|
|
void Skidmark::FadeOut( float deltaAlpha )
|
|
{
|
|
int iDeltaAlpha = static_cast< unsigned char > ( deltaAlpha * 255.0f );
|
|
|
|
bool stillVisible = false;
|
|
for ( int i = 0 ; i < m_NumSegments ; i++ )
|
|
{
|
|
int newAlpha = m_Segments[i].intensity - iDeltaAlpha;
|
|
if ( newAlpha <= 0 )
|
|
{
|
|
newAlpha = 0;
|
|
}
|
|
else
|
|
{
|
|
stillVisible = true;
|
|
}
|
|
m_Segments[ i ].intensity = newAlpha;
|
|
}
|
|
if ( stillVisible == false )
|
|
m_FadedOut = true;
|
|
}
|
|
|
|
void Skidmark::AddToDSG( RenderEnums::LayerEnum renderLayer )
|
|
{
|
|
if ( m_IsInDSG )
|
|
RemoveFromDSG();
|
|
|
|
m_RenderLayer = renderLayer;
|
|
WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
|
|
rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
|
|
pWorldRenderLayer->pWorldScene()->Add( this );
|
|
m_IsInDSG = true;
|
|
|
|
}
|
|
|
|
void Skidmark::RemoveFromDSG()
|
|
{
|
|
|
|
if ( m_IsInDSG )
|
|
{
|
|
WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
|
|
rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
|
|
pWorldRenderLayer->pWorldScene()->Remove( this );
|
|
m_IsInDSG = false;
|
|
}
|
|
}
|
|
|
|
void Skidmark::SetShader( tShader* pShader )
|
|
{
|
|
tRefCounted::Assign( mpShader, pShader );
|
|
}
|
|
|
|
void Skidmark::WriteVertices( const rmt::Vector& elevatedPosition,
|
|
const rmt::Vector& right,
|
|
float halfWidth,
|
|
unsigned char intensity,
|
|
float u,
|
|
float distanceFromStart )
|
|
{
|
|
|
|
|
|
rmt::Vector leftVertex = elevatedPosition - (right * halfWidth);
|
|
rmt::Vector rightVertex = elevatedPosition + (right * halfWidth);
|
|
|
|
// Increase bounding box
|
|
m_BoundingBox.Expand( rightVertex );
|
|
m_BoundingBox.Expand( leftVertex );
|
|
|
|
|
|
// Set position data
|
|
m_Segments[ m_NumSegments ].left.position = leftVertex;
|
|
m_Segments[ m_NumSegments ].right.position = rightVertex;
|
|
|
|
|
|
// Intensity
|
|
m_Segments[ m_NumSegments ].intensity = intensity;
|
|
|
|
// Tex coordinates
|
|
m_Segments[ m_NumSegments ].left.uv = pddiVector2( 0.0f, u );
|
|
m_Segments[ m_NumSegments ].right.uv = pddiVector2( 1.0f, u );
|
|
|
|
m_Segments[ m_NumSegments ].distanceFromStart = distanceFromStart;
|
|
|
|
m_NumSegments++;
|
|
}
|
|
|
|
void Skidmark::ExtendVertices( const rmt::Vector& elevatedPosition,
|
|
const rmt::Vector& right,
|
|
float halfWidth,
|
|
unsigned char intensity,
|
|
float u,
|
|
float distance )
|
|
{
|
|
|
|
rmt::Vector leftVertex = elevatedPosition - (right * halfWidth);
|
|
rmt::Vector rightVertex = elevatedPosition + (right * halfWidth);
|
|
|
|
// Increase bounding box
|
|
m_BoundingBox.Expand( rightVertex );
|
|
m_BoundingBox.Expand( leftVertex );
|
|
|
|
// Set position data
|
|
m_Segments[ m_NumSegments - 1 ].left.position = leftVertex;
|
|
m_Segments[ m_NumSegments - 1 ].right.position = rightVertex;
|
|
|
|
// Intensity
|
|
m_Segments[ m_NumSegments - 1 ].intensity = intensity;
|
|
|
|
// Tex coordinates
|
|
m_Segments[ m_NumSegments -1 ].left.uv = pddiVector2( 0.0f, u );
|
|
m_Segments[ m_NumSegments -1 ].right.uv = pddiVector2( 1.0f, u );
|
|
|
|
m_Segments[ m_NumSegments - 1].distanceFromStart = distance;
|
|
}
|
|
|
|
bool Skidmark::FindTextureBreakPosition( const rmt::Vector& position, rmt::Vector* texBreakPosition, float* u, float* distanceToTextureBreakPoint, float* distanceToGivenPoint )
|
|
{
|
|
// Calculate the point on the vector from the last vertex written
|
|
rmt::Vector toPosition = position - m_LastPositionWritten;
|
|
toPosition.Normalize();
|
|
float deltaU;
|
|
float breakTexCoordinate;
|
|
if ( m_TextureDirection > 0 )
|
|
{
|
|
deltaU = 1.0f - m_LastU;
|
|
breakTexCoordinate = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
deltaU = m_LastU - 0.0f;
|
|
breakTexCoordinate = 0.0f;
|
|
}
|
|
|
|
float distance = METERS_PER_TEX_UNIT * deltaU;
|
|
// Check to see that the break position is actually between the last position and
|
|
// the given position
|
|
*texBreakPosition = m_LastPositionWritten + toPosition * distance;
|
|
*u = breakTexCoordinate;
|
|
|
|
float distanceToBreakPointSqr = ( *texBreakPosition - m_LastPositionWritten ).MagnitudeSqr();
|
|
float distanceToGivenPointSqr = ( position - m_LastPositionWritten ).MagnitudeSqr();
|
|
if ( distanceToGivenPointSqr > distanceToBreakPointSqr )
|
|
{
|
|
*distanceToTextureBreakPoint = rmt::Sqrt ( distanceToBreakPointSqr );
|
|
*distanceToGivenPoint = rmt::Sqrt ( distanceToGivenPointSqr );
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
}
|
|
|
|
void Skidmark::MoveInDSG( rmt::Box3D& oldBox )
|
|
{
|
|
if ( m_IsInDSG )
|
|
{
|
|
WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
|
|
rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
|
|
pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
|
|
}
|
|
}
|
|
|
|
|