mirror of
https://github.com/thug1src/thug.git
synced 2025-01-22 05:43:47 +00:00
754 lines
28 KiB
C++
754 lines
28 KiB
C++
|
#include <gfx\NxLight.h>
|
||
|
#include <gfx\DebugGfx.h>
|
||
|
#include <gfx\DebugGfx.h>
|
||
|
#include <gel/scripting/symboltable.h>
|
||
|
#include "nx_init.h"
|
||
|
#include "instance.h"
|
||
|
#include "anim.h"
|
||
|
#include "render.h"
|
||
|
#include "occlude.h"
|
||
|
#include "anim_vertdefs.h"
|
||
|
|
||
|
namespace NxXbox
|
||
|
{
|
||
|
|
||
|
static Mth::Matrix *pLastBoneTransforms = NULL;
|
||
|
CInstance *pFirstInstance = NULL;
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
static int sort_by_bone_transform( const void *p1, const void *p2 )
|
||
|
{
|
||
|
CInstance *p_mesh1 = *((CInstance**)p1 );
|
||
|
CInstance *p_mesh2 = *((CInstance**)p2 );
|
||
|
|
||
|
Mth::Matrix *p_mat1 = p_mesh1->GetBoneTransforms();
|
||
|
Mth::Matrix *p_mat2 = p_mesh2->GetBoneTransforms();
|
||
|
|
||
|
if(( p_mat1 == NULL ) || ( p_mesh1->GetScene()->m_numHierarchyObjects > 0 ))
|
||
|
{
|
||
|
if(( p_mat2 == NULL ) || ( p_mesh2->GetScene()->m_numHierarchyObjects > 0 ))
|
||
|
{
|
||
|
int num_st1 = p_mesh1->GetScene()->m_num_semitransparent_mesh_entries;
|
||
|
int num_st2 = p_mesh2->GetScene()->m_num_semitransparent_mesh_entries;
|
||
|
|
||
|
if( num_st1 == num_st2 )
|
||
|
{
|
||
|
// Try sorting on the material draw order of the first semitransparent mesh.
|
||
|
if( num_st1 > 0 )
|
||
|
{
|
||
|
NxXbox::sMaterial *p_material1 = p_mesh1->GetScene()->m_meshes[p_mesh1->GetScene()->m_first_semitransparent_entry]->mp_material;
|
||
|
NxXbox::sMaterial *p_material2 = p_mesh2->GetScene()->m_meshes[p_mesh2->GetScene()->m_first_semitransparent_entry]->mp_material;
|
||
|
|
||
|
if( p_material1 && p_material2 )
|
||
|
{
|
||
|
return ( p_material1->m_draw_order > p_material2->m_draw_order ) ? 1 : (( p_material1->m_draw_order < p_material2->m_draw_order ) ? -1 : 0 );
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return ( num_st1 > num_st2 ) ? 1 : -1;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
else if(( p_mat2 == NULL ) || ( p_mesh2->GetScene()->m_numHierarchyObjects > 0 ))
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// At this stage we know both instances have bone transforms.
|
||
|
if( p_mat1 == p_mat2 )
|
||
|
{
|
||
|
int num_st1 = p_mesh1->GetScene()->m_num_semitransparent_mesh_entries;
|
||
|
int num_st2 = p_mesh2->GetScene()->m_num_semitransparent_mesh_entries;
|
||
|
|
||
|
if( num_st1 == num_st2 )
|
||
|
return 0;
|
||
|
|
||
|
return ( num_st1 > num_st2 ) ? 1 : -1;
|
||
|
}
|
||
|
|
||
|
return((uint32)p_mat1 > (uint32)p_mat2 ) ? 1 : -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void render_instance( CInstance* p_instance, uint32 flags )
|
||
|
{
|
||
|
// Seed the static pointer off to NULL, otherwise if there is only one object with bone transforms, it will never update.
|
||
|
pLastBoneTransforms = NULL;
|
||
|
|
||
|
if( p_instance->GetActive())
|
||
|
{
|
||
|
set_frustum_bbox_transform( p_instance->GetTransform());
|
||
|
|
||
|
bool render = true;
|
||
|
if( !( flags & vRENDER_NO_CULLING ))
|
||
|
{
|
||
|
// Check whether this instance is visible.
|
||
|
render = false;
|
||
|
if( frustum_check_sphere( &p_instance->GetScene()->m_sphere_center, p_instance->GetScene()->m_sphere_radius ))
|
||
|
{
|
||
|
if( !TestSphereAgainstOccluders( &p_instance->GetScene()->m_sphere_center, p_instance->GetScene()->m_sphere_radius ))
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( render )
|
||
|
{
|
||
|
if( p_instance->GetBoneTransforms() != NULL )
|
||
|
{
|
||
|
startup_weighted_mesh_vertex_shader();
|
||
|
p_instance->Render( vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT );
|
||
|
shutdown_weighted_mesh_vertex_shader();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p_instance->Render( vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Restore world transform to identity.
|
||
|
D3DDevice_SetTransform( D3DTS_WORLD, &EngineGlobals.world_matrix );
|
||
|
set_frustum_bbox_transform( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void render_instances( uint32 flags )
|
||
|
{
|
||
|
#define INSTANCE_ARRAY_SIZE 4096
|
||
|
static CInstance *p_instances[INSTANCE_ARRAY_SIZE];
|
||
|
int current_index = 0;
|
||
|
|
||
|
Mth::Matrix t;
|
||
|
t.Identity();
|
||
|
|
||
|
// Seed the static pointer off to NULL, otherwise if there is only one object with bone transforms, it will never update.
|
||
|
pLastBoneTransforms = NULL;
|
||
|
|
||
|
// First go through and build a list of the visible instances.
|
||
|
CInstance *p_instance = pFirstInstance;
|
||
|
while( p_instance )
|
||
|
{
|
||
|
if( p_instance->GetActive())
|
||
|
{
|
||
|
// Check to see whether this instance is of the type we want to render - opaque or semitransparent.
|
||
|
if((( flags & vRENDER_OPAQUE ) && ( p_instance->GetScene()->m_num_mesh_entries > p_instance->GetScene()->m_num_semitransparent_mesh_entries )) ||
|
||
|
(( flags & vRENDER_SEMITRANSPARENT ) && ( p_instance->GetScene()->m_num_semitransparent_mesh_entries > 0 )))
|
||
|
{
|
||
|
// Check whether this instance is visible - if so, place it in the visible array.
|
||
|
t.SetPos( p_instance->GetTransform()->GetPos());
|
||
|
set_frustum_bbox_transform( &t );
|
||
|
|
||
|
// For skinned objects, we have no idea how the skeleton transforms will be affecting the final position of each object.
|
||
|
// This can sometimes result in small objects getting culled incorrectly. For these objects, increase the size of
|
||
|
// the bounding sphere slightly.
|
||
|
float radius = p_instance->GetScene()->m_sphere_radius;
|
||
|
if( p_instance->GetBoneTransforms() && ( p_instance->GetScene()->m_numHierarchyObjects == 0 ))
|
||
|
{
|
||
|
radius = ( radius < 72.0f ) ? 72.0f : radius;
|
||
|
}
|
||
|
|
||
|
// The logic code already sets the active flag based on visibility, so there is no need to perform a second visibility check
|
||
|
// at this point.
|
||
|
// We do, however, want to test against occluders.
|
||
|
if( !TestSphereAgainstOccluders( &p_instance->GetScene()->m_sphere_center, radius ))
|
||
|
{
|
||
|
Dbg_Assert( current_index < INSTANCE_ARRAY_SIZE );
|
||
|
p_instances[current_index++] = p_instance;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
p_instance = p_instance->GetNextInstance();
|
||
|
}
|
||
|
|
||
|
// Now sort the list based on bone transform and number of semitransparent objects in scene.
|
||
|
if( current_index > 0 )
|
||
|
{
|
||
|
qsort( p_instances, current_index, sizeof( CInstance* ), sort_by_bone_transform );
|
||
|
|
||
|
int i = 0;
|
||
|
|
||
|
// First render the instances with bone transforms.
|
||
|
startup_weighted_mesh_vertex_shader();
|
||
|
for( ; i < current_index; ++i )
|
||
|
{
|
||
|
if(( p_instances[i]->GetBoneTransforms() == NULL ) || ( p_instances[i]->GetScene()->m_numHierarchyObjects > 0 ))
|
||
|
break;
|
||
|
|
||
|
if(( flags & vRENDER_OPAQUE ) || (( flags & vRENDER_SEMITRANSPARENT ) && ( flags & vRENDER_INSTANCE_PRE_WORLD_SEMITRANSPARENT )))
|
||
|
p_instances[i]->Render( flags );
|
||
|
}
|
||
|
shutdown_weighted_mesh_vertex_shader();
|
||
|
|
||
|
// Then the instances with no bone transforms. These will require fixed function lighting.
|
||
|
D3DDevice_SetRenderState( D3DRS_LIGHTING, TRUE );
|
||
|
D3DDevice_SetRenderState( D3DRS_COLORVERTEX, TRUE );
|
||
|
D3DDevice_LightEnable( 0, TRUE );
|
||
|
D3DDevice_LightEnable( 1, TRUE );
|
||
|
|
||
|
Mth::Vector current_cam_pos( EngineGlobals.cam_position.x, EngineGlobals.cam_position.y, EngineGlobals.cam_position.z );
|
||
|
|
||
|
// Get the (square of) the distance at which env mapping will be disabled, in inches.
|
||
|
float env_map_disable_distance = Script::GetFloat( CRCD( 0x4e7dc608, "EnvMapDisableDistance")) * 12.0f;
|
||
|
env_map_disable_distance = env_map_disable_distance * env_map_disable_distance;
|
||
|
|
||
|
for( ; i < current_index; ++i )
|
||
|
{
|
||
|
// Calculate the distance from the current camera to the instance.
|
||
|
float dist_squared = Mth::DistanceSqr( p_instances[i]->GetTransform()->GetPos(), current_cam_pos );
|
||
|
|
||
|
// At forty feet, environment mapping is turned off.
|
||
|
EngineGlobals.allow_envmapping = ( dist_squared > env_map_disable_distance ) ? false : true;
|
||
|
|
||
|
if(( flags & vRENDER_OPAQUE ) || (( flags & vRENDER_SEMITRANSPARENT ) && ( flags & vRENDER_INSTANCE_POST_WORLD_SEMITRANSPARENT )))
|
||
|
p_instances[i]->Render( flags );
|
||
|
}
|
||
|
|
||
|
// Restore environment mapping..
|
||
|
EngineGlobals.allow_envmapping = true;
|
||
|
|
||
|
// Shut down fixed function lighting.
|
||
|
D3DDevice_SetRenderState( D3DRS_LIGHTING, FALSE );
|
||
|
D3DDevice_SetRenderState( D3DRS_COLORVERTEX, FALSE );
|
||
|
D3DDevice_LightEnable( 0, FALSE );
|
||
|
D3DDevice_LightEnable( 1, FALSE );
|
||
|
}
|
||
|
|
||
|
// Restore world transform to identity.
|
||
|
D3DDevice_SetTransform( D3DTS_WORLD, &EngineGlobals.world_matrix );
|
||
|
set_frustum_bbox_transform( NULL );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
CInstance::CInstance( sScene *pScene, Mth::Matrix &transform, int numBones, Mth::Matrix *pBoneTransforms )
|
||
|
{
|
||
|
SetTransform( transform );
|
||
|
mp_bone_transforms = pBoneTransforms;
|
||
|
m_num_bones = numBones;
|
||
|
mp_scene = pScene;
|
||
|
mp_model = NULL;
|
||
|
m_active = true;
|
||
|
m_flags = 0;
|
||
|
|
||
|
mp_next_instance = pFirstInstance;
|
||
|
pFirstInstance = this;
|
||
|
|
||
|
// Check to see whether this instance is allowed to be lit or not (for non-skinned instances).
|
||
|
// Currently, we assume that instances with a model containing valid normals requires lighting, except in the
|
||
|
// situation where that model contains a mesh specifically flagged not to be lit.
|
||
|
if(( pBoneTransforms == NULL ) || ( GetScene()->m_numHierarchyObjects > 0 ))
|
||
|
{
|
||
|
for( int m = 0; m < pScene->m_num_mesh_entries; ++m )
|
||
|
{
|
||
|
NxXbox::sMesh *p_mesh = pScene->m_meshes[m];
|
||
|
if(( p_mesh->m_vertex_shader[0] & D3DFVF_NORMAL ) == 0 )
|
||
|
return;
|
||
|
if( p_mesh->m_flags & sMesh::MESH_FLAG_UNLIT )
|
||
|
return;
|
||
|
}
|
||
|
m_flags |= CInstance::INSTANCE_FLAG_LIGHTING_ALLOWED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
CInstance::~CInstance()
|
||
|
{
|
||
|
if( m_flags & INSTANCE_FLAG_DELETE_ATTACHED_SCENE )
|
||
|
{
|
||
|
if( mp_scene )
|
||
|
{
|
||
|
delete mp_scene;
|
||
|
mp_scene = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Remove this instance from the list.
|
||
|
CInstance **pp_instance = &pFirstInstance;
|
||
|
while( *pp_instance )
|
||
|
{
|
||
|
if( *pp_instance == this )
|
||
|
{
|
||
|
*pp_instance = mp_next_instance;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pp_instance = &(( *pp_instance )->mp_next_instance );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void CInstance::Render( uint32 flags )
|
||
|
{
|
||
|
const int MAX_SUPPORTED_BONES = 58;
|
||
|
static float root_matrix[12];
|
||
|
|
||
|
static D3DLIGHT8 l0 =
|
||
|
{
|
||
|
D3DLIGHT_DIRECTIONAL,
|
||
|
{ 0.0f, 0.0f, 0.0f, 1.0f }, // Diffuse color
|
||
|
{ 1.0f, 1.0f, 1.0f, 0.0f }, // Specular color
|
||
|
{ 0.0f, 0.0f, 0.0f, 0.0f }, // Ambient color
|
||
|
{ 0.0f, 0.0f, 0.0f }, // Position
|
||
|
{ 0.0f, 0.0f, 0.0f }, // Direction
|
||
|
0.0f, 0.0f, // Range, falloff
|
||
|
0.0f, 0.0f, 0.0f, // Attenuation0, 1, 2
|
||
|
0.0f, 0.0f // Theta, Phi
|
||
|
};
|
||
|
static D3DLIGHT8 l1 =
|
||
|
{
|
||
|
D3DLIGHT_DIRECTIONAL,
|
||
|
{ 0.0f, 0.0f, 0.0f, 1.0f }, // Diffuse color
|
||
|
{ 0.0f, 0.0f, 0.0f, 0.0f }, // Specular color
|
||
|
{ 0.0f, 0.0f, 0.0f, 0.0f }, // Ambient color
|
||
|
{ 0.0f, 0.0f, 0.0f }, // Position
|
||
|
{ 0.0f, 0.0f, 0.0f }, // Direction
|
||
|
0.0f, 0.0f, // Range, falloff
|
||
|
0.0f, 0.0f, 0.0f, // Attenuation0, 1, 2
|
||
|
0.0f, 0.0f // Theta, Phi
|
||
|
};
|
||
|
|
||
|
if(( GetBoneTransforms() == NULL ) || ( GetScene()->m_numHierarchyObjects > 0 ))
|
||
|
{
|
||
|
// Is a non-skinned object.
|
||
|
pLastBoneTransforms = NULL;
|
||
|
|
||
|
// Do the lighting setup here.
|
||
|
if((( m_flags & CInstance::INSTANCE_FLAG_LIGHTING_ALLOWED ) == 0 ) || ( GetScene()->m_flags & SCENE_FLAG_RENDERING_SHADOW ))
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_LIGHTING, FALSE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Nx::CModelLights *p_lights = GetModel() ? GetModel()->GetModelLights() : NULL;
|
||
|
if( p_lights )
|
||
|
{
|
||
|
// Obtain position from the transform, for calculating Scene Lighting.
|
||
|
p_lights->UpdateEngine( GetTransform()->GetPos(), true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Nx::CLightManager::sUpdateEngine();
|
||
|
}
|
||
|
|
||
|
D3DDevice_SetRenderState( D3DRS_LIGHTING, TRUE );
|
||
|
|
||
|
l0.Diffuse.r = EngineGlobals.directional_light_color[4];
|
||
|
l0.Diffuse.g = EngineGlobals.directional_light_color[5];
|
||
|
l0.Diffuse.b = EngineGlobals.directional_light_color[6];
|
||
|
l0.Direction.x = EngineGlobals.directional_light_color[0];
|
||
|
l0.Direction.y = EngineGlobals.directional_light_color[1];
|
||
|
l0.Direction.z = EngineGlobals.directional_light_color[2];
|
||
|
D3DDevice_SetLight( 0, &l0 );
|
||
|
|
||
|
l1.Diffuse.r = EngineGlobals.directional_light_color[12];
|
||
|
l1.Diffuse.g = EngineGlobals.directional_light_color[13];
|
||
|
l1.Diffuse.b = EngineGlobals.directional_light_color[14];
|
||
|
l1.Direction.x = EngineGlobals.directional_light_color[8];
|
||
|
l1.Direction.y = EngineGlobals.directional_light_color[9];
|
||
|
l1.Direction.z = EngineGlobals.directional_light_color[10];
|
||
|
D3DDevice_SetLight( 1, &l1 );
|
||
|
|
||
|
D3DDevice_SetRenderState( D3DRS_AMBIENT, D3DCOLOR_RGBA( Ftoi_ASM( EngineGlobals.ambient_light_color[0] * 255.0f ),
|
||
|
Ftoi_ASM( EngineGlobals.ambient_light_color[1] * 255.0f ),
|
||
|
Ftoi_ASM( EngineGlobals.ambient_light_color[2] * 255.0f ),
|
||
|
0xFF ));
|
||
|
}
|
||
|
|
||
|
// If the object has a 'fake skeleton', like the cars with rotating wheels, then set up accordingly.
|
||
|
if( GetScene()->m_numHierarchyObjects )
|
||
|
{
|
||
|
int num_bones = ( GetNumBones() < MAX_SUPPORTED_BONES ) ? GetNumBones() : MAX_SUPPORTED_BONES;
|
||
|
for( int lp = 0; lp < num_bones; ++lp )
|
||
|
{
|
||
|
Mth::Matrix temp = *GetTransform();
|
||
|
temp = GetBoneTransforms()[lp] * temp;
|
||
|
set_frustum_bbox_transform( &temp );
|
||
|
D3DDevice_SetTransform( D3DTS_WORLD, (D3DXMATRIX*)&temp );
|
||
|
|
||
|
// Scan through the meshes, setting only those with the current bone active.
|
||
|
for( int m = 0; m < GetScene()->m_num_mesh_entries; ++m )
|
||
|
{
|
||
|
NxXbox::sMesh *p_mesh = GetScene()->m_meshes[m];
|
||
|
if( p_mesh->GetBoneIndex() == lp )
|
||
|
p_mesh->SetActive( true );
|
||
|
else
|
||
|
p_mesh->SetActive( false );
|
||
|
}
|
||
|
render_scene( GetScene(), flags | vRENDER_NO_CULLING );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Has no skeleton.
|
||
|
set_frustum_bbox_transform( GetTransform());
|
||
|
D3DDevice_SetTransform( D3DTS_WORLD, (D3DXMATRIX*)GetTransform());
|
||
|
|
||
|
render_scene( GetScene(), flags | vRENDER_NO_CULLING );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Has bone transforms, is therefore an animated object such as a skater, pedestrian etc.
|
||
|
set_frustum_bbox_transform( GetTransform());
|
||
|
|
||
|
// We only want to upload all the bone transforms if they have changed from the last call.
|
||
|
bool upload_bone_transforms = false;
|
||
|
|
||
|
if( pLastBoneTransforms != GetBoneTransforms())
|
||
|
{
|
||
|
// Okay, the bone transforms have changed from the last call.
|
||
|
upload_bone_transforms = true;
|
||
|
|
||
|
// Do the lighting setup here (if not rendering shadow, which requires no lighting).
|
||
|
if(( GetScene()->m_flags & SCENE_FLAG_RENDERING_SHADOW ) == 0 )
|
||
|
{
|
||
|
Nx::CModelLights *p_lights = GetModel() ? GetModel()->GetModelLights() : NULL;
|
||
|
if( p_lights )
|
||
|
{
|
||
|
p_lights->UpdateEngine( GetTransform()->GetPos(), true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Nx::CLightManager::sUpdateEngine();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pLastBoneTransforms = GetBoneTransforms();
|
||
|
|
||
|
int num_bones = ( GetNumBones() < MAX_SUPPORTED_BONES ) ? GetNumBones() : MAX_SUPPORTED_BONES;
|
||
|
}
|
||
|
|
||
|
root_matrix[0] = GetTransform()->GetRight()[X];
|
||
|
root_matrix[1] = GetTransform()->GetUp()[X];
|
||
|
root_matrix[2] = GetTransform()->GetAt()[X];
|
||
|
root_matrix[3] = GetTransform()->GetPos()[X];
|
||
|
root_matrix[4] = GetTransform()->GetRight()[Y];
|
||
|
root_matrix[5] = GetTransform()->GetUp()[Y];
|
||
|
root_matrix[6] = GetTransform()->GetAt()[Y];
|
||
|
root_matrix[7] = GetTransform()->GetPos()[Y];
|
||
|
root_matrix[8] = GetTransform()->GetRight()[Z];
|
||
|
root_matrix[9] = GetTransform()->GetUp()[Z];
|
||
|
root_matrix[10] = GetTransform()->GetAt()[Z];
|
||
|
root_matrix[11] = GetTransform()->GetPos()[Z];
|
||
|
|
||
|
setup_weighted_mesh_vertex_shader( &root_matrix, &GetBoneTransforms()[0][Mth::RIGHT][X], upload_bone_transforms ? GetNumBones() : 0 );
|
||
|
|
||
|
if( GetScene()->m_flags & SCENE_FLAG_RENDERING_SHADOW )
|
||
|
{
|
||
|
// Set the simple vertex shader that does no normal transform or lighting.
|
||
|
set_vertex_shader( WeightedMeshVertexShader_SBWrite );
|
||
|
EngineGlobals.vertex_shader_override = 1;
|
||
|
|
||
|
// Set the simple pixel shader that just writes constant (1,1,1,1) out.
|
||
|
set_pixel_shader( PixelShaderNULL );
|
||
|
EngineGlobals.pixel_shader_override = 1;
|
||
|
|
||
|
// No backface culling.
|
||
|
set_render_state( RS_CULLMODE, D3DCULL_NONE );
|
||
|
|
||
|
// Lock out material changes.
|
||
|
EngineGlobals.material_override = 1;
|
||
|
|
||
|
render_scene( GetScene(), flags | vRENDER_NO_CULLING );
|
||
|
|
||
|
// RenderShadowVolume();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
render_scene( GetScene(), flags | vRENDER_NO_CULLING );
|
||
|
|
||
|
// Render the self-shadowing pass here, if so flagged.
|
||
|
if( GetScene()->m_flags & SCENE_FLAG_SELF_SHADOWS )
|
||
|
{
|
||
|
static float texture_projection_matrix[16];
|
||
|
|
||
|
// Calculate distance to the camera - the self shadowing fades out beyond a certain distance, (and also fades out
|
||
|
// close up, to avoid the visible streaks). Also need to take into account the view angle since that directly affects
|
||
|
// the relative screen space size of the object.
|
||
|
const float SHADOW_FADE_CLOSE_START = 150.0f;
|
||
|
const float SHADOW_FADE_CLOSE_COMPLETE = 120.0f;
|
||
|
const float SHADOW_FADE_FAR_START = 500.0f;
|
||
|
const float SHADOW_FADE_FAR_COMPLETE = 600.0f;
|
||
|
const float DEFAULT_SCREEN_ANGLE = 0.72654f; // tan( 72 / 2 )
|
||
|
|
||
|
Mth::Vector pos_to_cam = GetTransform()->GetPos() - Mth::Vector( NxXbox::EngineGlobals.cam_position.x, NxXbox::EngineGlobals.cam_position.y, NxXbox::EngineGlobals.cam_position.z );
|
||
|
float dist = pos_to_cam.Length() * ( tanf( Mth::DegToRad( EngineGlobals.screen_angle * 0.5f )) / DEFAULT_SCREEN_ANGLE );
|
||
|
|
||
|
if(( dist > SHADOW_FADE_CLOSE_COMPLETE ) && ( dist < SHADOW_FADE_FAR_COMPLETE ))
|
||
|
{
|
||
|
// Find which set of details relates to this instance.
|
||
|
pTextureProjectionDetailsTable->IterateStart();
|
||
|
sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->IterateNext();
|
||
|
while( p_details )
|
||
|
{
|
||
|
if( p_details->p_model == mp_model )
|
||
|
break;
|
||
|
|
||
|
p_details = pTextureProjectionDetailsTable->IterateNext();
|
||
|
}
|
||
|
|
||
|
if( p_details )
|
||
|
{
|
||
|
// Need to incorporate the world matrix into the texture projection matrix, since we will be using the bone-transformed
|
||
|
// vertex positions, which are in object space.
|
||
|
XGMATRIX world_matrix;
|
||
|
world_matrix.m[0][0] = GetTransform()->GetRight()[X];
|
||
|
world_matrix.m[0][1] = GetTransform()->GetRight()[Y];
|
||
|
world_matrix.m[0][2] = GetTransform()->GetRight()[Z];
|
||
|
world_matrix.m[0][3] = 0.0f;
|
||
|
world_matrix.m[1][0] = GetTransform()->GetUp()[X];
|
||
|
world_matrix.m[1][1] = GetTransform()->GetUp()[Y];
|
||
|
world_matrix.m[1][2] = GetTransform()->GetUp()[Z];
|
||
|
world_matrix.m[1][3] = 0.0f;
|
||
|
world_matrix.m[2][0] = GetTransform()->GetAt()[X];
|
||
|
world_matrix.m[2][1] = GetTransform()->GetAt()[Y];
|
||
|
world_matrix.m[2][2] = GetTransform()->GetAt()[Z];
|
||
|
world_matrix.m[2][3] = 0.0f;
|
||
|
world_matrix.m[3][0] = GetTransform()->GetPos()[X];
|
||
|
world_matrix.m[3][1] = GetTransform()->GetPos()[Y];
|
||
|
world_matrix.m[3][2] = GetTransform()->GetPos()[Z];
|
||
|
world_matrix.m[3][3] = 1.0f;
|
||
|
calculate_tex_proj_matrix( &p_details->view_matrix, &p_details->projection_matrix, &p_details->texture_projection_matrix, &world_matrix );
|
||
|
|
||
|
// Upload that matrix to the GPU also.
|
||
|
texture_projection_matrix[0] = p_details->texture_projection_matrix.m[0][0];
|
||
|
texture_projection_matrix[1] = p_details->texture_projection_matrix.m[1][0];
|
||
|
texture_projection_matrix[2] = p_details->texture_projection_matrix.m[2][0];
|
||
|
texture_projection_matrix[3] = p_details->texture_projection_matrix.m[3][0];
|
||
|
texture_projection_matrix[4] = p_details->texture_projection_matrix.m[0][1];
|
||
|
texture_projection_matrix[5] = p_details->texture_projection_matrix.m[1][1];
|
||
|
texture_projection_matrix[6] = p_details->texture_projection_matrix.m[2][1];
|
||
|
texture_projection_matrix[7] = p_details->texture_projection_matrix.m[3][1];
|
||
|
texture_projection_matrix[8] = p_details->texture_projection_matrix.m[0][2];
|
||
|
texture_projection_matrix[9] = p_details->texture_projection_matrix.m[1][2];
|
||
|
texture_projection_matrix[10] = p_details->texture_projection_matrix.m[2][2];
|
||
|
texture_projection_matrix[11] = p_details->texture_projection_matrix.m[3][2];
|
||
|
texture_projection_matrix[12] = p_details->texture_projection_matrix.m[0][3];
|
||
|
texture_projection_matrix[13] = p_details->texture_projection_matrix.m[1][3];
|
||
|
texture_projection_matrix[14] = p_details->texture_projection_matrix.m[2][3];
|
||
|
texture_projection_matrix[15] = p_details->texture_projection_matrix.m[3][3];
|
||
|
|
||
|
// We can upload this matrix to the space taken up by the directional lighting details, since this render pass requires no lighting.
|
||
|
D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_DIR_LIGHT_OFFSET, (void*)texture_projection_matrix, 4 );
|
||
|
|
||
|
// Scan through each mesh in this scene, setting the vertex shader to be the equivalent vertex shader
|
||
|
// for shadow buffering.
|
||
|
sScene *p_scene = GetScene();
|
||
|
for( int m = 0; m < p_scene->m_num_mesh_entries; ++m )
|
||
|
{
|
||
|
sMesh *p_mesh = p_scene->m_meshes[m];
|
||
|
if(( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_1Weight ) ||
|
||
|
( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_Specular_1Weight ) ||
|
||
|
( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_1Weight_UVTransform ))
|
||
|
{
|
||
|
p_mesh->PushVertexShader( WeightedMeshVS_VXC_1Weight_SBPassThru );
|
||
|
}
|
||
|
else if(( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_2Weight ) ||
|
||
|
( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_Specular_2Weight ) ||
|
||
|
( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_2Weight_UVTransform ))
|
||
|
{
|
||
|
p_mesh->PushVertexShader( WeightedMeshVS_VXC_2Weight_SBPassThru );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p_mesh->PushVertexShader( WeightedMeshVS_VXC_3Weight_SBPassThru );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float max = 0.25f;
|
||
|
if( dist < SHADOW_FADE_CLOSE_START )
|
||
|
{
|
||
|
max = (( dist - SHADOW_FADE_CLOSE_COMPLETE ) / ( SHADOW_FADE_CLOSE_START - SHADOW_FADE_CLOSE_COMPLETE )) * max;
|
||
|
}
|
||
|
else if( dist > SHADOW_FADE_FAR_START )
|
||
|
{
|
||
|
max = (( SHADOW_FADE_FAR_COMPLETE - dist ) / ( SHADOW_FADE_FAR_COMPLETE - SHADOW_FADE_FAR_START )) * max;
|
||
|
}
|
||
|
|
||
|
// Set ambient color multipliers in pixel shader C0 and C1.
|
||
|
NxXbox::EngineGlobals.pixel_shader_constants[0] = 1.0f - max; // Always present
|
||
|
NxXbox::EngineGlobals.pixel_shader_constants[1] = 1.0f - max;
|
||
|
NxXbox::EngineGlobals.pixel_shader_constants[2] = 1.0f - max;
|
||
|
NxXbox::EngineGlobals.pixel_shader_constants[3] = 1.0f;
|
||
|
NxXbox::EngineGlobals.pixel_shader_constants[4] = max; // The part the shadow affects.
|
||
|
NxXbox::EngineGlobals.pixel_shader_constants[5] = max;
|
||
|
NxXbox::EngineGlobals.pixel_shader_constants[6] = max;
|
||
|
NxXbox::EngineGlobals.pixel_shader_constants[7] = 1.0f;
|
||
|
NxXbox::EngineGlobals.upload_pixel_shader_constants = true;
|
||
|
|
||
|
// Set the pixel shader that will do the shadow attenuation.
|
||
|
extern DWORD PixelShader_ShadowBuffer;
|
||
|
set_pixel_shader( PixelShader_ShadowBuffer );
|
||
|
EngineGlobals.pixel_shader_override = 1;
|
||
|
|
||
|
// Set the material properties used for shadow modulation.
|
||
|
set_blend_mode( vBLEND_MODE_MODULATE_COLOR );
|
||
|
set_texture( 0, NULL );
|
||
|
set_texture( 1, NULL );
|
||
|
set_texture( 2, NULL );
|
||
|
EngineGlobals.material_override = 1;
|
||
|
|
||
|
// Set texture stage 3 to use the shadow buffer.
|
||
|
EngineGlobals.texture_stage_override |= ( 1 << 3 );
|
||
|
set_texture( 3, NULL );
|
||
|
set_texture( 3, p_details->p_texture->pD3DTexture );
|
||
|
set_render_state( RS_UVADDRESSMODE0 + 3, 0x00020002UL );
|
||
|
D3DDevice_SetTextureStageState( 3, D3DTSS_BORDERCOLOR, 0x00000000UL );
|
||
|
D3DDevice_SetTextureStageState( 3, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
|
||
|
D3DDevice_SetTextureStageState( 3, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
|
||
|
|
||
|
D3DDevice_SetTextureStageState( 3, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
|
||
|
D3DDevice_SetTextureStageState( 3, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | 3 );
|
||
|
|
||
|
// Set shadowbuffer state.
|
||
|
D3DDevice_SetRenderState( D3DRS_SHADOWFUNC, D3DCMP_GREATER );
|
||
|
|
||
|
// Render the shadow pass with fogging disabled.
|
||
|
if( EngineGlobals.fog_enabled )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_FOGENABLE, FALSE );
|
||
|
render_scene( GetScene(), flags | vRENDER_NO_CULLING );
|
||
|
D3DDevice_SetRenderState( D3DRS_FOGENABLE, TRUE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
render_scene( GetScene(), flags | vRENDER_NO_CULLING );
|
||
|
}
|
||
|
|
||
|
// Restore shadowbuffer state.
|
||
|
D3DDevice_SetRenderState( D3DRS_SHADOWFUNC, D3DCMP_NEVER );
|
||
|
|
||
|
// Scan through each mesh in this scene, restoring the vertex shader to the original.
|
||
|
for( int m = 0; m < p_scene->m_num_mesh_entries; ++m )
|
||
|
{
|
||
|
sMesh *p_mesh = p_scene->m_meshes[m];
|
||
|
p_mesh->PopVertexShader();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Restore state.
|
||
|
EngineGlobals.vertex_shader_override = 0;
|
||
|
EngineGlobals.pixel_shader_override = 0;
|
||
|
EngineGlobals.material_override = 0;
|
||
|
EngineGlobals.texture_stage_override &= ~( 1 << 3 );
|
||
|
set_texture( 3, NULL );
|
||
|
|
||
|
// shutdown_weighted_mesh_vertex_shader();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Test code...
|
||
|
void CInstance::RenderShadowVolume( void )
|
||
|
{
|
||
|
if( GetBoneTransforms())
|
||
|
{
|
||
|
XGMATRIX root_matrix = *((XGMATRIX*)GetTransform());
|
||
|
|
||
|
sScene* p_scene = GetScene();
|
||
|
|
||
|
// Process each mesh in turn.
|
||
|
for( int m = 0; m < p_scene->m_num_mesh_entries; ++m )
|
||
|
{
|
||
|
sMesh* p_mesh = p_scene->m_meshes[m];
|
||
|
|
||
|
// Lock the vertex buffer so we can read the data.
|
||
|
BYTE* p_byte;
|
||
|
p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_byte, D3DLOCK_READONLY );
|
||
|
|
||
|
// Grab a buffer for the transformed points.
|
||
|
XGVECTOR4* p_t_buffer = new XGVECTOR4[p_mesh->m_num_vertices];
|
||
|
|
||
|
for( int v = 0; v < p_mesh->m_num_vertices; ++v )
|
||
|
{
|
||
|
// Get untransformed point.
|
||
|
XGVECTOR3* p_source_vertex = (XGVECTOR3*)( p_byte + ( v * p_mesh->m_vertex_stride ));
|
||
|
|
||
|
// Get and unpack weights.
|
||
|
uint32 packed_weights = *((uint32*)( p_byte + ( v * p_mesh->m_vertex_stride ) + 12 ));
|
||
|
float w0 = (( packed_weights >> 0 ) & 0x7ff ) * ( 1.0f / 1023.0f );
|
||
|
float w1 = (( packed_weights >> 11 ) & 0x7ff ) * ( 1.0f / 1023.0f );
|
||
|
float w2 = (( packed_weights >> 22 ) & 0x3ff ) * ( 1.0f / 511.0f );
|
||
|
|
||
|
// Get and unpack matrix indices.
|
||
|
uint32 packed_indices = *((uint32*)( p_byte + ( v * p_mesh->m_vertex_stride ) + 16 ));
|
||
|
uint32 i0 = ( packed_indices >> 0 ) & 0xff;
|
||
|
uint32 i1 = ( packed_indices >> 8 ) & 0xff;
|
||
|
uint32 i2 = ( packed_indices >> 16 ) & 0xff;
|
||
|
|
||
|
// Get bone matrix pointers.
|
||
|
XGMATRIX* p_bone_mat0 = (XGMATRIX*)( GetBoneTransforms() + i0 );
|
||
|
XGMATRIX* p_bone_mat1 = (XGMATRIX*)( GetBoneTransforms() + i1 );
|
||
|
XGMATRIX* p_bone_mat2 = (XGMATRIX*)( GetBoneTransforms() + i2 );
|
||
|
|
||
|
XGVECTOR4 tp0;
|
||
|
XGVECTOR4 tp1;
|
||
|
XGVECTOR4 tp2;
|
||
|
|
||
|
// Transform point by bone matrix 0, 1 and 2 and scale with weight 0, 1, 2.
|
||
|
XGVec3Transform( &tp0, p_source_vertex, p_bone_mat0 );
|
||
|
XGVec3Scale((XGVECTOR3*)&tp0, (XGVECTOR3*)&tp0, w0 );
|
||
|
|
||
|
XGVec3Transform( &tp1, p_source_vertex, p_bone_mat1 );
|
||
|
XGVec3Scale((XGVECTOR3*)&tp1, (XGVECTOR3*)&tp1, w1 );
|
||
|
|
||
|
XGVec3Transform( &tp2, p_source_vertex, p_bone_mat2 );
|
||
|
XGVec3Scale((XGVECTOR3*)&tp2, (XGVECTOR3*)&tp2, w2 );
|
||
|
|
||
|
// Obtain cumulative result.
|
||
|
tp0 = tp0 + tp1 + tp2;
|
||
|
|
||
|
// Tranform point by object transform.
|
||
|
XGVec3Transform( p_t_buffer + v, (XGVECTOR3*)&tp0, &root_matrix );
|
||
|
}
|
||
|
delete [] p_t_buffer;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
} // namespace NxXbox
|
||
|
|