The-Simpsons-Hit-and-Run/game/code/worldsim/character/characterrenderable.cpp

582 lines
18 KiB
C++

#include <worldsim/character/characterrenderable.h>
#include <mission/gameplaymanager.h>
#include <p3d/texture.hpp>
#include <p3d/shader.hpp>
#include <p3d/anim/drawablepose.hpp>
#include <p3d/anim/pose.hpp>
#include <p3d/matrixstack.hpp>
#include <p3d/shadow.hpp>
#include <p3d/view.hpp>
#include <pddi/pddi.hpp>
#include <camera/supercammanager.h>
#include <contexts/bootupcontext.h>
// Hack.
// [6/27/2002]
#include <worldsim/character/charactermanager.h>
#include <worldsim/character/character.h>
/*
==============================================================================
CharacterRenderable::CharacterRenderable
==============================================================================
Description: Comment
Parameters: (
tDrawablePose* pDrawablePoseHigh,
tDrawablePose* pDrawablePoseMedium,
tDrawablePose* pDrawablePoseLow,
tPose* pPose )
Return: CharacterRenderable
=============================================================================
*/
CharacterRenderable::CharacterRenderable(
tDrawablePose* pDrawablePoseHigh,
tDrawablePose* pDrawablePoseMedium,
tDrawablePose* pDrawablePoseLow)
:
mCurrentLOD( High ),
mbInAnyonesFrustrum( false ),
mpSwatchTexture( NULL ),
mpSwatchShader( NULL ),
mHaveShadowJoints( false ),
mFadeAlpha( 255 ),
mpShockedDrawable( NULL ),
mIsShocked( false ),
mDisplayingSkeletonShock( false )
{
int i;
for ( i = 0; i < MAX_LOD; i++ )
{
mpDrawableList[ i ] = 0;
}
tRefCounted::Assign( mpDrawableList[ High ], pDrawablePoseHigh );
tRefCounted::Assign( mpDrawableList[ Medium ], pDrawablePoseMedium );
tRefCounted::Assign( mpDrawableList[ Low ], pDrawablePoseLow );
if(pDrawablePoseHigh)
{
pDrawablePoseHigh->SetPose(NULL);
}
if(pDrawablePoseMedium)
{
pDrawablePoseMedium->SetPose(NULL);
}
if(pDrawablePoseLow)
{
pDrawablePoseLow->SetPose(NULL);
}
// null these out for now... CharacterManager::SetupCharacter will be
// setting these swatch textures...
for( i=0; i<CharacterRenderable::NUM_CHARACTER_SWATCHES; i++ )
{
mpSwatchTextures[i] = NULL;
}
mShadowColour.Set( 0, 0, 0 );
}
/*
==============================================================================
CharacterRenderable::~CharacterRenderable
==============================================================================
Description: Comment
Parameters: ( void )
Return: CharacterRenderable
=============================================================================
*/
CharacterRenderable::~CharacterRenderable( void )
{
int i;
for ( i = 0; i < MAX_LOD; i++ )
{
if ( mpDrawableList[ i ] )
{
mpDrawableList[ i ]->Release();
mpDrawableList[ i ] = 0;
}
}
if( mpSwatchTexture )
{
mpSwatchTexture->Release();
mpSwatchTexture = NULL;
}
for( i=0; i<CharacterRenderable::NUM_CHARACTER_SWATCHES; i++ )
{
if( mpSwatchTextures[i] != NULL )
{
mpSwatchTextures[i]->Release();
mpSwatchTextures[i] = NULL;
}
}
if( mpSwatchShader )
{
mpSwatchShader->Release();
mpSwatchShader = NULL;
}
if ( mpShockedDrawable != NULL )
{
mpShockedDrawable->Release();
mpShockedDrawable = NULL;
}
}
/*
==============================================================================
CharacterRenderable::Display
==============================================================================
Description: Comment
Parameters: ( void )
Return: void
=============================================================================
*/
void CharacterRenderable::Display( rmt::Vector iPosn, tPose* pose )
{
BEGIN_PROFILE("CharRender Cull")
tPointCamera* pCam = (tPointCamera*)GetSuperCamManager()->GetSCC(0)->GetCamera();
rmt::Vector camPosn;
pCam->GetPosition( &camPosn );
iPosn.Sub( iPosn, camPosn );
float sqrDistFromCam = iPosn.MagnitudeSqr();
END_PROFILE("CharRender Cull")
float dist = (pCam->GetNearPlane() + 1.5f);
dist *= dist;
if ( sqrDistFromCam < dist )
{
return;
}
if( mbInAnyonesFrustrum )
{
if(sqrDistFromCam > 900.0f )
{
if(sqrDistFromCam > 6400.0f ) //>80m
{
SetLOD(Low);
}
else // <= 80m
{
SetLOD(Medium);
}
}
else // <= 30m
{
SetLOD(High);
}
BEGIN_PROFILE("CharRender Display")
if ( mIsShocked == false )
{
DisplayModel( pose );
}
else
{
DisplayShocked( pose );
}
END_PROFILE("CharRender Display")
}
}
//////////////////////////////////////////////////////////////////////////
int CharacterRenderable::CastsShadow()
{
return 1;
}
/*
==============================================================================
CharacterRenderable::DisplayShadow
==============================================================================
Description: Comment
Parameters: ()
Return: void
=============================================================================
*/
void CharacterRenderable::DisplayShadow( tPose* pose, const BlobShadowParams* BlobParams )
{
if( BlobParams )
{
BEGIN_PROFILE("Char Blobby Shadow");
if( !mHaveShadowJoints )
{
mHaveShadowJoints = true;
mShadowJoints[ 0 ] = pose->FindJoint( "Elbow_L" );
mShadowJoints[ 1 ] = pose->FindJoint( "Ankle_L" );
mShadowJoints[ 2 ] = pose->FindJoint( "Elbow_R" );
mShadowJoints[ 3 ] = pose->FindJoint( "Ankle_R" );
}
tColour OutsideColour;
tColour insideColour;
// Hack for brightly colored shadows for special game mode
if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
{
OutsideColour.Set( 0, 0, 0, 0 ); // black outline
//OutsideColour = mShadowColour;
insideColour = mShadowColour;
}
else
{
OutsideColour.Set( 255, 255, 255, 255 );
const int Inside = 128;
float shadowAlpha = BlobParams->ShadowAlpha;
if( mFadeAlpha != 255 )
{
shadowAlpha = mFadeAlpha / 255.0f;
}
int c = rmt::Clamp( int( Inside + ( 255 - Inside ) * ( 1.0f - shadowAlpha ) ), 0, 255 );
insideColour.Set( c, c, c, c );
}
const float ArticulatedBlobbyShadowLODThreshold = 15.0f;
const float MinShadowSize = 0.15f;
const float NonArticulatedScale = 0.2f;
const int NumShadowJoints = 4;
const float BlobRadius = 1.0f;
const float BlobFadeOff = 0.2f;
const int NumBlobSlices = 16; // Keep the number even or you're in trouble.
const int HalfCircle = NumBlobSlices >> 1; // We only keep half the circle and then mirror it. Hence the reason to keep the number of points even.
static float BlobPoints[ HalfCircle ][ 2 ];
static float FadePoints[ HalfCircle ][ 2 ];
float pointScales[ NumBlobSlices ]; // How much to move the points to envolope the character.
bool articulated = false;
static float DoOnce = true;
if( DoOnce )
{
DoOnce = false;
const float angleStep = rmt::PI_2 / ((float) NumBlobSlices );
const float angleHalfStep = angleStep * 0.5f;
float angle = rmt::PI_2;
for( int j = 0; j < HalfCircle; ++j )
{
float sa, ca;
rmt::SinCos( angle, &sa, &ca);
BlobPoints[ j ][ 0 ] = sa * BlobRadius;
BlobPoints[ j ][ 1 ] = ca * BlobRadius;
rmt::SinCos( angle - angleHalfStep, &sa, &ca );
FadePoints[ j ][ 0 ] = sa * BlobFadeOff;
FadePoints[ j ][ 1 ] = ca * BlobFadeOff;
angle -= angleStep;
}
}
// Translate the shadow towards the camera slightly, instead of moving it off the
//ground in the direction of the the ground normal. Hopefully this will cause less distortion of the shadow.
rmt::Vector camPos;
tCamera* camera = p3d::context->GetView()->GetCamera();
rAssert( camera );
camera->GetWorldPosition( &camPos );
camPos.Sub( BlobParams->GroundPos );
float camDirDot = camPos.DotProduct( camera->GetCameraToWorldMatrix().Row( 2 ) );
// Note that the camPos vector is towards the camera so the camDirDot will be negative when the camera is facing
//the object. So if it's positive then we can assume it's behind the camera.
if( camDirDot > 0.0f )
{
// Please excuse the early return.
END_PROFILE( "Char Blobby Shadow" );
return;
}
float cameraToShadow = camPos.Magnitude();
camPos.Normalize();
camPos.Scale( 0.25f );
rmt::Matrix transform;
transform.Identity();
transform.FillTranslate( BlobParams->GroundPos );
transform.FillHeading( BlobParams->GroundNormal, BlobParams->ShadowFacing );
transform.Row( 3 ).Add( camPos );
p3d::stack->PushMultiply( transform );
if( ( mHaveShadowJoints ) && ( cameraToShadow < ArticulatedBlobbyShadowLODThreshold ) )
{
articulated = true;
// Find the position of the joints to deform the shadow by the character's movement.
float jointPos[ NumShadowJoints ][ 2 ];
/*
Uncomment this if you want to weight the joint position by how close
they line up to the blob point position.
float jointNor[ NumShadowJoints ][ 2 ];
*/
rmt::Vector tempPos;
rmt::Matrix m = pose->GetJoint( 0 )->worldMatrix;
m.Invert();
for( int i = 0; i < NumShadowJoints; ++i )
{
m.Transform( mShadowJoints[ i ]->worldMatrix.Row( 3 ), &tempPos );
jointPos[ i ][ 0 ] = tempPos.x;
jointPos[ i ][ 1 ] = -tempPos.z;
/*
Uncomment this if you want to weight the joint positions by how close
they line up to the blob point position.
float mag;
mag = rmt::Sqrt( ( tempPos.x * tempPos.x ) + ( tempPos.z * tempPos.z ) );
mag = ( mag != 0.0f ) ? 1 / mag : 0.0f;
jointNor[ i ][ 0 ] = jointPos[ i ][ 0 ] * mag;
jointNor[ i ][ 1 ] = jointPos[ i ][ 1 ] * mag;
*/
}
for( int i = 0; i < HalfCircle; ++i )
{
pointScales[ i ] = MinShadowSize;
pointScales[ i + HalfCircle ] = -MinShadowSize;
for( int j = 0; j < NumShadowJoints; ++j )
{
float dot;
// Project joint position onto blob point position.
dot = ( BlobPoints[ i ][ 0 ] * jointPos[ j ][ 0 ] ) + ( BlobPoints[ i ][ 1 ] * jointPos[ j ][ 1 ] );
/*
Uncomment this if you want to weight the joint position by how close
they line up to the blob point position.
dot *= rmt::Abs( ( BlobPoints[ i ][ 0 ] * jointNor[ j ][ 0 ] ) + ( BlobPoints[ i ][ 1 ] * jointNor[ j ][ 1 ] ) );
*/
pointScales[ i ] = rmt::Max( pointScales[ i ], dot );
pointScales[ i + HalfCircle ] = rmt::Min( pointScales[ i + HalfCircle ], dot );
}
}
// Since we'll be mirroring the circle, we'll conviently keep the second half of the scales array
//as negative numbers so that it will automatically mirror our points for us.
}
pddiPrimStream* blob = 0;
pddiShader* blobShader = BootupContext::GetInstance()->GetSharedShader();
rAssert( blobShader != NULL );
if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
{
blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_NONE );
}
else
{
blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_MODULATE );
}
blobShader->SetInt( PDDI_SP_ISLIT, 0 );
blobShader->SetInt( PDDI_SP_ALPHATEST, 0 );
blobShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_GOURAUD );
blob = p3d::pddi->BeginPrims( blobShader, PDDI_PRIM_TRIANGLES, PDDI_V_C, 9 * NumBlobSlices );
for(int i=0; i < NumBlobSlices; i++)
{
float x0, y0, x1, y1, x2, y2, x3, y3;
int index = i % HalfCircle;
int nextIndex = ( i + 1 ) % HalfCircle;
int nextScaleIndex = ( i + 1 ) % NumBlobSlices;
float fadeScale = ( i < HalfCircle ) ? BlobParams->ShadowScale : -BlobParams->ShadowScale;
float nextFadeScale = ( nextScaleIndex < HalfCircle ) ? BlobParams->ShadowScale : -BlobParams->ShadowScale;
// Draw inside.
blob->Colour( insideColour );
blob->Coord( 0.0f, 0.0f, 0.0f );
blob->Colour( insideColour );
x0 = BlobPoints[ index ][ 0 ];
x0 *= BlobParams->ShadowScale;
x0 *= articulated ? pointScales[ i ] : ( index == i ) ? NonArticulatedScale : -NonArticulatedScale;
y0 = BlobPoints[ index ][ 1 ];
y0 *= BlobParams->ShadowScale;
y0 *= articulated ? pointScales[ i ] : ( index == i ) ? NonArticulatedScale : -NonArticulatedScale;
blob->Coord( x0, y0, 0.0f );
blob->Colour( insideColour );
x1 = BlobPoints[ nextIndex ][ 0 ];
x1 *= BlobParams->ShadowScale;
x1 *= articulated ? pointScales[ nextScaleIndex ] : ( nextIndex == nextScaleIndex ) ? NonArticulatedScale : -NonArticulatedScale;
y1 = BlobPoints[ nextIndex ][ 1 ];
y1 *= BlobParams->ShadowScale;
y1 *= articulated ? pointScales[ nextScaleIndex ] : ( nextIndex == nextScaleIndex ) ? NonArticulatedScale : -NonArticulatedScale;
blob->Coord( x1, y1, 0.0f );
// Draw first part of outside.
blob->Colour( insideColour );
blob->Coord( x0, y0, 0.0f );
blob->Colour( OutsideColour );
x2 = x1 + ( FadePoints[ nextIndex ][ 0 ] * nextFadeScale );
y2 = y1 + ( FadePoints[ nextIndex ][ 1 ] * nextFadeScale );
blob->Coord( x2, y2, 0.0f );
blob->Colour( insideColour );
blob->Coord( x1, y1, 0.0f );
// Draw second part of outside.
blob->Colour( insideColour );
blob->Coord( x0, y0, 0.0f );
blob->Colour( OutsideColour );
x3 = x0 + ( FadePoints[ index ][ 0 ] * fadeScale );
y3 = y0 + ( FadePoints[ index ][ 1 ] * fadeScale );
blob->Coord( x3, y3, 0.0f );
blob->Colour( OutsideColour );
blob->Coord( x2, y2, 0.0f );
}
p3d::pddi->EndPrims( blob );
p3d::stack->Pop();
END_PROFILE( "Char Blobby Shadow" );
}
}
/*
==============================================================================
CharacterRenderable::GetDrawable
==============================================================================
Description: Comment
Parameters: ( )
Return: tDrawablePose
=============================================================================
*/
tDrawablePose* CharacterRenderable::GetDrawable( ) const
{
for(int lod = (int)mCurrentLOD; lod >= (int)Low; lod--)
{
if(mpDrawableList[ lod ] )
{
return mpDrawableList[ lod ];
}
}
return NULL;
}
/*
==============================================================================
CharacterRenderable::GetLOD
==============================================================================
Description: Comment
Parameters: ( void )
Return: CharacterRenderable
=============================================================================
*/
int CharacterRenderable::GetLOD( void ) const
{
return mCurrentLOD;
}
/*
==============================================================================
CharacterRenderable::SetLOD
==============================================================================
Description: Comment
Parameters: ( CharacterLOD LOD )
Return: void
=============================================================================
*/
void CharacterRenderable::SetLOD( int LOD )
{
mCurrentLOD = (LOD > High) ? LOD : High;
}
void CharacterRenderable::SetSwatch( int index )
{
rAssert( 0 <= index && index < NUM_CHARACTER_SWATCHES );
if( mpSwatchTextures[index] != NULL && mpSwatchTexture != mpSwatchTextures[index] )
{
// TODO:
// Assign here? or just set mpSwatchTexture = mpSwatchTextures[index]?
tRefCounted::Assign(mpSwatchTexture, mpSwatchTextures[index]);
}
}
void CharacterRenderable::SetSwatchTexture( int index, tTexture* pTexture )
{
rAssert( 0 <= index && index < NUM_CHARACTER_SWATCHES );
tRefCounted::Assign(mpSwatchTextures[index], pTexture);
}
void CharacterRenderable::SetShockEffect( tDrawable* pDrawable )
{
tRefCounted::Assign(mpShockedDrawable, pDrawable);
}
void CharacterRenderable::SetSwatchShader( tShader* pShader )
{
tRefCounted::Assign(mpSwatchShader, pShader);
}
void CharacterRenderable::SetFadeAlpha( int fadeAlpha )
{
mFadeAlpha = rmt::Clamp( fadeAlpha, 0, 255 );
}
void CharacterRenderable::SetShadowColour( tColour colour )
{
mShadowColour = colour;
}
void CharacterRenderable::DisplayModel( tPose* pose )
{
tDrawablePose* draw = GetDrawable();
if(draw)
{
if( mpSwatchShader != NULL )
{
if( mpSwatchTexture != NULL )
{
mpSwatchShader->SetTexture( PDDI_SP_BASETEX, mpSwatchTexture );
}
}
tShaderIntBroadcast blendAlpha( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
draw->ProcessShaders( blendAlpha );
tShaderIntBroadcast emissiveFade( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
draw->ProcessShaders( emissiveFade );
draw->Display( pose );
}
}
void CharacterRenderable::DisplayShocked( tPose* pose )
{
// Alternate displaying the skeleton and displaying the model
if ( mDisplayingSkeletonShock )
{
if ( mpShockedDrawable != NULL )
{
p3d::stack->PushMultiply( pose->GetJoint(0)->worldMatrix );
mpShockedDrawable->Display();
p3d::stack->Pop();
}
mDisplayingSkeletonShock = false;
}
else
{
//pose->GetSkeleton();
//DisplayModel( pose );
mDisplayingSkeletonShock = true;
}
}