The-Simpsons-Hit-and-Run/game/code/worldsim/skidmarks/SkidMarkGenerator.cpp

273 lines
8.6 KiB
C++

//===========================================================================
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
//
// Component: SkidMarkGenerator
//
// Description: A per-vehicle class that generates skidmarks
//
// Authors: Michael Riegger
//
//===========================================================================
//---------------------------------------------------------------------------
// Includes
//===========================================================================
#include <stdlib.h>
#include <worldsim/SkidMarks/SkidMarkGenerator.h>
#include <worldsim/SkidMarks/skidmarkmanager.h>
#include <worldsim/SkidMarks/skidmark.h>
#include <radmath/util.hpp>
#include <float.h>
#include <debug/profiler.h>
#include <raddebug.hpp>
#include <raddebugwatch.hpp>
#ifdef RAD_RELEASE
const float SKID_INTENSITY_DAMPENING = 0.5f;
#else
static float SKID_INTENSITY_DAMPENING = 0.5f;
#endif
SkidMarkGenerator::SkidMarkGenerator()
{
#ifndef RAD_RELEASE
static bool s_AddedDebugging = false;
if ( s_AddedDebugging == false )
{
radDbgWatchAddFloat( &SKID_INTENSITY_DAMPENING, "Skid mark dampening", "Skidmarks" );
s_AddedDebugging = true;
}
#endif
}
SkidMarkGenerator::~SkidMarkGenerator()
{
for(int i = 0; i < 4; i++)
{
if(m_CurrentSkidData[i].currentSkid)
{
GetSkidmarkManager()->ReturnUsedSkidmark( m_CurrentSkidData[i].currentSkid );
}
m_CurrentSkidData[i].currentSkid = NULL;
}
}
// Height in meters to raise the skids above the ground to avoid z fighting
// Keep pointers to the tShaders
static tShader* spPavementShader = NULL;
static tShader* spDirtShader = NULL;
static tShader* spGrassShader = NULL;
// The shader names for use in searching for skid mark shaders in the inventory
const char* PAVEMENT_SKIDMARK_SHADERNAME = "skid_m";
const char* DIRT_SKIDMARK_SHADERNAME = "dirt_skidmark_shader";
const char* GRASS_SKIDMARK_SHADERNAME = "grass_skidmark_shader";
// enables z test, disables z test, required for correct
// skid mark appearance
static void SetShaderSkidSettings( tShader* pShader )
{
pShader->SetInt(PDDI_SP_ALPHAOP1, PDDI_BLEND_ALPHA);
pShader->SetInt(PDDI_SP_TEXTUREOP1, PDDI_TEXBLEND_MODULATE);
pShader->SetInt(PDDI_SP_COLOUROP1, PDDI_BLEND_SUBTRACT);
}
// Find the shaders in the inventory, set zwrite/ztest (since the artists can't
// export these shader settings directly and addref them
// SkidMarkGenerator::ReleaseShaders() must be called afterwards
void SkidMarkGenerator::InitShaders()
{
// Just so that we never do an accidental double addref
ReleaseShaders();
spPavementShader = p3d::find< tShader >( PAVEMENT_SKIDMARK_SHADERNAME );
if ( spPavementShader != NULL )
{
spPavementShader->AddRef();
SetShaderSkidSettings( spPavementShader );
}
rTuneWarningMsg( spPavementShader != NULL, "Pavement skid shader not found in inventory! Some skids will not appear!");
spDirtShader = p3d::find< tShader >( DIRT_SKIDMARK_SHADERNAME );
if ( spDirtShader != NULL )
{
spDirtShader->AddRef();
SetShaderSkidSettings( spDirtShader );
}
rTuneWarningMsg( spDirtShader != NULL, "Dirt skid shader not found in inventory! Some skids will not appear!");
spGrassShader = p3d::find< tShader >( GRASS_SKIDMARK_SHADERNAME );
if ( spGrassShader != NULL )
{
spGrassShader->AddRef();
SetShaderSkidSettings( spGrassShader );
}
rTuneWarningMsg( spGrassShader != NULL, "Grass skid shader not found in inventory! Some skids will not appear!");
}
// Fetch the appropriate shader for the terrain type
tShader* SkidMarkGenerator::GetShader( eTerrainType terrainType )
{
tShader* shader = NULL;
switch (terrainType)
{
case TT_Dirt:
shader = spDirtShader;
break;
case TT_Grass:
shader = spGrassShader;
break;
case TT_Road:
default:
shader = spPavementShader;
break;
};
return shader;
}
// Releases all the shaders that were addrefed in InitShaders()
void SkidMarkGenerator::ReleaseShaders()
{
if ( spPavementShader != NULL )
{
spPavementShader->Release();
spPavementShader = NULL;
}
if ( spDirtShader != NULL )
{
spDirtShader->Release();
spDirtShader = NULL;
}
if ( spGrassShader != NULL )
{
spGrassShader->Release();
spGrassShader = NULL;
}
}
void SkidMarkGenerator::GenerateSkid( int wheel, const SkidMarkGenerator::SkidData& skidData )
{
if ( skidData.velocityDirection.MagnitudeSqr() < 0.01f ||
skidData.intensity < 0.01f )
return;
float dampenedIntensity = skidData.intensity * SKID_INTENSITY_DAMPENING;
rAssert( wheel >=0 && wheel < 4 );
TireGeneratorData& generatorData = m_CurrentSkidData[ wheel ];
// Is the tire currently skidding?
// if so, continue it
// otherwise start a new skid
rmt::Vector alignedOffset;
skidData.transform.RotateVector( generatorData.offset, &alignedOffset );
rmt::Vector skidCenter = skidData.transform.Row(3) + alignedOffset;
rmt::Vector skidDirection = skidData.velocityDirection;
skidDirection.Normalize();
// Check to see if the terrain type changed
if ( generatorData.currentSkid != NULL )
{
const float ANGLE_TOO_SHARP = 0.7f;
bool angleTooSharp = generatorData.currentSkid->GetCurrentDirection().Dot( skidDirection ) < ANGLE_TOO_SHARP;
bool terrainTypeChanged = generatorData.terrainType != skidData.terrainType;
if ( angleTooSharp || terrainTypeChanged )
{
// Finish off the current skid and start a new one
generatorData.currentSkid->FadeOutTrailingVertices();
GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
generatorData.currentSkid = NULL;
GenerateSkid( wheel, skidData );
}
}
// Check to see if
if ( generatorData.currentSkid != NULL )
{
// Is there space left for more vertices?
if ( generatorData.currentSkid->SpaceLeft() )
{
generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
generatorData.skidExtendedThisFrame = true;
}
else
{
// No space left. We must continue this skid with a new skidmark
Skidmark* skidMark = GetSkidmarkManager()->GetUnusedSkidmark();
if ( skidMark != NULL )
{
generatorData.currentSkid->ContinueSkidmark( skidMark );
GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
generatorData.currentSkid = skidMark;
generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
generatorData.skidExtendedThisFrame = true;
}
else
{
// No space available!
// Tell the current skid to fade out, we are done. Pretend the skid is finished
generatorData.currentSkid->FadeOutTrailingVertices();
GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
generatorData.currentSkid = NULL;
generatorData.skidExtendedThisFrame = false;
}
}
}
else
{
// Fetch a skidmark thats not being used right now
generatorData.currentSkid = GetSkidmarkManager()->GetUnusedSkidmark();
if ( generatorData.currentSkid != NULL )
{
generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
generatorData.skidExtendedThisFrame = true;
generatorData.terrainType = skidData.terrainType;
generatorData.currentSkid->SetShader( GetShader( skidData.terrainType ) );
generatorData.currentSkid->FadeInVertices();
}
}
}
void
SkidMarkGenerator::Update( )
{
for ( int i = 0 ; i < 4 ; i++ )
{
if ( m_CurrentSkidData[i].skidExtendedThisFrame == false)
{
if ( m_CurrentSkidData[i].currentSkid != NULL )
{
m_CurrentSkidData[i].currentSkid->FadeOutTrailingVertices();
GetSkidmarkManager()->ReturnUsedSkidmark( m_CurrentSkidData[i].currentSkid );
m_CurrentSkidData[i].currentSkid = NULL;
}
}
else
{
m_CurrentSkidData[i].skidExtendedThisFrame = false;
}
}
}