mirror of
https://github.com/thug1src/thug.git
synced 2025-01-21 21:33:46 +00:00
1280 lines
50 KiB
C++
1280 lines
50 KiB
C++
#include <core/defines.h>
|
|
|
|
#include <gel/collision/collision.h>
|
|
#include <gel/collision/colltridata.h>
|
|
#include <gel/collision/movcollman.h>
|
|
#include <gel/collision/batchtricoll.h>
|
|
#include <engine/SuperSector.h>
|
|
|
|
#include <gfx/nx.h>
|
|
#include <gfx/nxtexman.h>
|
|
#include <gfx/debuggfx.h>
|
|
#include <gfx/NxViewMan.h>
|
|
#include <gfx/NxMiscFX.h>
|
|
|
|
#include <gfx/xbox/p_nxgeom.h>
|
|
#include <gfx/xbox/p_nxtexture.h>
|
|
|
|
#include <gfx/xbox/nx/render.h>
|
|
|
|
namespace Nx
|
|
{
|
|
|
|
#define DRAW_DEBUG_LINES 0
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
struct sXboxScreenFlashVert
|
|
{
|
|
float x, y, z;
|
|
float rhw;
|
|
D3DCOLOR col;
|
|
float u, v;
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
struct sXboxSplatVert
|
|
{
|
|
D3DXVECTOR3 pos;
|
|
D3DCOLOR col;
|
|
float u, v;
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
struct sXboxSplatInstanceDetails : public sSplatInstanceDetails
|
|
{
|
|
// Platform specific part.
|
|
NxXbox::CInstance *mp_instance;
|
|
NxXbox::sMaterial *mp_material;
|
|
sXboxSplatVert m_verts[SPLAT_POLYS_PER_MESH * 3];
|
|
};
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
struct sXboxShatterInstanceDetails : public sShatterInstanceDetails
|
|
{
|
|
// Platform specific part.
|
|
|
|
sXboxShatterInstanceDetails( int num_tris, NxXbox::sMesh *p_mesh );
|
|
~sXboxShatterInstanceDetails( void );
|
|
|
|
NxXbox::sMesh *mp_mesh;
|
|
uint8 *mp_vertex_buffer;
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
** Private Functions **
|
|
*****************************************************************************/
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
sXboxShatterInstanceDetails::sXboxShatterInstanceDetails( int num_tris, NxXbox::sMesh *p_mesh ) : sShatterInstanceDetails( num_tris )
|
|
{
|
|
mp_mesh = p_mesh;
|
|
mp_vertex_buffer = new uint8[num_tris * 3 * p_mesh->m_vertex_stride];
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
sXboxShatterInstanceDetails::~sXboxShatterInstanceDetails( void )
|
|
{
|
|
delete [] mp_vertex_buffer;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
sXboxSplatInstanceDetails * getDetailsForTextureSplat( NxXbox::sTexture *p_texture )
|
|
{
|
|
sXboxSplatInstanceDetails *p_xbox_details;
|
|
|
|
Dbg_Assert( p_splat_details_table );
|
|
|
|
// Check to see whether we have a scene already created for this type of texture splat.
|
|
p_splat_details_table->IterateStart();
|
|
sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
|
|
while( p_details )
|
|
{
|
|
p_xbox_details = static_cast<sXboxSplatInstanceDetails*>( p_details );
|
|
NxXbox::sMaterial *p_material = p_xbox_details->mp_material;
|
|
if( p_material->mp_tex[0] == p_texture )
|
|
{
|
|
// This scene contains a material with the required texture, so use this scene.
|
|
return p_xbox_details;
|
|
}
|
|
p_details = p_splat_details_table->IterateNext();
|
|
}
|
|
|
|
// Create an (opaque) material used to render the mesh.
|
|
NxXbox::sMaterial *p_material = new NxXbox::sMaterial();
|
|
p_material->m_flags[0] = (( p_texture ) ? MATFLAG_TEXTURED : 0 );
|
|
p_material->m_checksum = (uint32)rand() * (uint32)rand();
|
|
p_material->m_passes = 1;
|
|
p_material->mp_tex[0] = p_texture;
|
|
p_material->m_no_bfc = true;
|
|
p_material->m_zbias = 1; // To ensure it will sort above most geometry.
|
|
p_material->m_reg_alpha[0] = 0x00000005UL; // Blend for now.
|
|
p_material->m_color[0][0] = 0x80;
|
|
p_material->m_color[0][1] = 0x80;
|
|
p_material->m_color[0][2] = 0x80;
|
|
p_material->m_uv_addressing[0] = 0x00020002UL; // We want the texture to border - most efficient for alphakill.
|
|
p_material->m_k[0] = 0.0f;
|
|
p_material->m_alpha_cutoff = 1;
|
|
|
|
p_xbox_details = new sXboxSplatInstanceDetails;
|
|
p_xbox_details->m_highest_active_splat = 0;
|
|
p_xbox_details->mp_material = p_material;
|
|
ZeroMemory( p_xbox_details->m_lifetimes, sizeof( int ) * SPLAT_POLYS_PER_MESH );
|
|
|
|
for( int v = 0; v < SPLAT_POLYS_PER_MESH * 3; ++v )
|
|
{
|
|
p_xbox_details->m_verts[v].pos = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
|
|
p_xbox_details->m_verts[v].col = D3DCOLOR_RGBA( 0x80, 0x80, 0x80, 0x80 );
|
|
}
|
|
|
|
p_splat_details_table->PutItem((uint32)p_xbox_details, p_xbox_details );
|
|
|
|
return p_xbox_details;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
bool subdivide_tri_stack( uint8 **p_write, NxXbox::sMesh *p_mesh )
|
|
{
|
|
// Three temporary buffers.
|
|
static uint8 v0[128];
|
|
static uint8 v1[128];
|
|
static uint8 v2[128];
|
|
|
|
// Three more temporary buffers.
|
|
static uint8 i01[128];
|
|
static uint8 i12[128];
|
|
static uint8 i20[128];
|
|
|
|
// If there are elements on the stack, pop off the top three vertices and subdivide if necessary.
|
|
if( triSubdivideStack.IsEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
D3DXVECTOR3 *p_v0 = (D3DXVECTOR3*)v0;
|
|
D3DXVECTOR3 *p_v1 = (D3DXVECTOR3*)v1;
|
|
D3DXVECTOR3 *p_v2 = (D3DXVECTOR3*)v2;
|
|
|
|
// Stack is LIFO, so Pop() off in reverse order.
|
|
triSubdivideStack.Pop( p_v2 );
|
|
triSubdivideStack.Pop( p_v1 );
|
|
triSubdivideStack.Pop( p_v0 );
|
|
|
|
// Calculate the area of this tri.
|
|
Mth::Vector p( p_v1->x - p_v0->x, p_v1->y - p_v0->y, p_v1->z - p_v0->z );
|
|
Mth::Vector q( p_v2->x - p_v0->x, p_v2->y - p_v0->y, p_v2->z - p_v0->z );
|
|
Mth::Vector r(( p[Y] * q[Z] ) - ( q[Y] * p[Z] ), ( p[Z] * q[X] ) - ( q[Z] * p[X] ), ( p[X] * q[Y] ) - ( q[X] * p[Y] ));
|
|
float area_squared = r.LengthSqr();
|
|
|
|
if( area_squared > shatterAreaTest )
|
|
{
|
|
// We need to subdivide this tri. Calculate the three intermediate points.
|
|
int block_size = triSubdivideStack.GetBlockSize();
|
|
|
|
memcpy( i01, v0, block_size );
|
|
memcpy( i12, v1, block_size );
|
|
memcpy( i20, v2, block_size );
|
|
|
|
// Deal with positions (always present).
|
|
((D3DXVECTOR3*)i01 )->x = p_v0->x + (( p_v1->x - p_v0->x ) * 0.5f );
|
|
((D3DXVECTOR3*)i01 )->y = p_v0->y + (( p_v1->y - p_v0->y ) * 0.5f );
|
|
((D3DXVECTOR3*)i01 )->z = p_v0->z + (( p_v1->z - p_v0->z ) * 0.5f );
|
|
|
|
((D3DXVECTOR3*)i12 )->x = p_v1->x + (( p_v2->x - p_v1->x ) * 0.5f );
|
|
((D3DXVECTOR3*)i12 )->y = p_v1->y + (( p_v2->y - p_v1->y ) * 0.5f );
|
|
((D3DXVECTOR3*)i12 )->z = p_v1->z + (( p_v2->z - p_v1->z ) * 0.5f );
|
|
|
|
((D3DXVECTOR3*)i20 )->x = p_v2->x + (( p_v0->x - p_v2->x ) * 0.5f );
|
|
((D3DXVECTOR3*)i20 )->y = p_v2->y + (( p_v0->y - p_v2->y ) * 0.5f );
|
|
((D3DXVECTOR3*)i20 )->z = p_v2->z + (( p_v0->z - p_v2->z ) * 0.5f );
|
|
|
|
// Deal with colors (not always present).
|
|
if( p_mesh->m_diffuse_offset > 0 )
|
|
{
|
|
uint8 *p_v0col = (uint8*)( v0 + p_mesh->m_diffuse_offset );
|
|
uint8 *p_v1col = (uint8*)( v1 + p_mesh->m_diffuse_offset );
|
|
uint8 *p_v2col = (uint8*)( v2 + p_mesh->m_diffuse_offset );
|
|
uint8 *p_i01col = (uint8*)( i01 + p_mesh->m_diffuse_offset );
|
|
uint8 *p_i12col = (uint8*)( i12 + p_mesh->m_diffuse_offset );
|
|
uint8 *p_i20col = (uint8*)( i20 + p_mesh->m_diffuse_offset );
|
|
|
|
for( int i = 0; i < 4; ++i )
|
|
{
|
|
p_i01col[i] = p_v0col[i] + (((int)p_v1col[i] - (int)p_v0col[i] ) / 2 );
|
|
p_i12col[i] = p_v1col[i] + (((int)p_v2col[i] - (int)p_v1col[i] ) / 2 );
|
|
p_i20col[i] = p_v2col[i] + (((int)p_v0col[i] - (int)p_v2col[i] ) / 2 );
|
|
}
|
|
}
|
|
|
|
// Deal with uv0 (not always present).
|
|
if( p_mesh->m_uv0_offset > 0 )
|
|
{
|
|
float *p_v0uv = (float*)( v0 + p_mesh->m_uv0_offset );
|
|
float *p_v1uv = (float*)( v1 + p_mesh->m_uv0_offset );
|
|
float *p_v2uv = (float*)( v2 + p_mesh->m_uv0_offset );
|
|
float *p_i01uv = (float*)( i01 + p_mesh->m_uv0_offset );
|
|
float *p_i12uv = (float*)( i12 + p_mesh->m_uv0_offset );
|
|
float *p_i20uv = (float*)( i20 + p_mesh->m_uv0_offset );
|
|
|
|
for( int i = 0; i < 2; ++i )
|
|
{
|
|
p_i01uv[i] = p_v0uv[i] + (( p_v1uv[i] - p_v0uv[i] ) * 0.5f );
|
|
p_i12uv[i] = p_v1uv[i] + (( p_v2uv[i] - p_v1uv[i] ) * 0.5f );
|
|
p_i20uv[i] = p_v2uv[i] + (( p_v0uv[i] - p_v2uv[i] ) * 0.5f );
|
|
}
|
|
}
|
|
|
|
// Push the four new tris onto the stack.
|
|
triSubdivideStack.Push( v0 );
|
|
triSubdivideStack.Push( i01 );
|
|
triSubdivideStack.Push( i20 );
|
|
|
|
triSubdivideStack.Push( i01 );
|
|
triSubdivideStack.Push( v1 );
|
|
triSubdivideStack.Push( i12 );
|
|
|
|
triSubdivideStack.Push( i01 );
|
|
triSubdivideStack.Push( i12 );
|
|
triSubdivideStack.Push( i20 );
|
|
|
|
triSubdivideStack.Push( i20 );
|
|
triSubdivideStack.Push( i12 );
|
|
triSubdivideStack.Push( v2 );
|
|
}
|
|
else
|
|
{
|
|
// Don't need to subdivide this tri.
|
|
int block_size = triSubdivideStack.GetBlockSize();
|
|
|
|
// Just copy the tri into the next available slot.
|
|
memcpy( *p_write, v0, block_size );
|
|
*p_write += block_size;
|
|
memcpy( *p_write, v1, block_size );
|
|
*p_write += block_size;
|
|
memcpy( *p_write, v2, block_size );
|
|
*p_write += block_size;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
** Public Functions **
|
|
*****************************************************************************/
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void plat_screen_flash_render( sScreenFlashDetails *p_details )
|
|
{
|
|
// Get viewport details.
|
|
CViewport *p_vp = CViewportManager::sGetActiveViewport( p_details->m_viewport );
|
|
|
|
sXboxScreenFlashVert verts[4];
|
|
|
|
verts[0].x = p_vp->GetOriginX() * NxXbox::EngineGlobals.backbuffer_width;
|
|
verts[0].y = p_vp->GetOriginY() * NxXbox::EngineGlobals.backbuffer_height;
|
|
verts[0].z = p_details->m_z;
|
|
|
|
verts[1].x = verts[0].x + ( p_vp->GetWidth() * NxXbox::EngineGlobals.backbuffer_width );
|
|
verts[1].y = verts[0].y;
|
|
verts[1].z = verts[0].z;
|
|
|
|
verts[2].x = verts[0].x + ( p_vp->GetWidth() * NxXbox::EngineGlobals.backbuffer_width );
|
|
verts[2].y = verts[0].y + ( p_vp->GetHeight() * NxXbox::EngineGlobals.backbuffer_height );
|
|
verts[2].z = verts[0].z;
|
|
|
|
verts[3].x = verts[0].x;
|
|
verts[3].y = verts[0].y + ( p_vp->GetHeight() * NxXbox::EngineGlobals.backbuffer_height );
|
|
verts[3].z = verts[0].z;
|
|
|
|
for( int v = 0; v < 4; ++v )
|
|
{
|
|
verts[v].col = D3DCOLOR_ARGB( p_details->m_current.a, p_details->m_current.r, p_details->m_current.g, p_details->m_current.b );
|
|
verts[v].rhw = 1.0f;
|
|
}
|
|
|
|
if( p_details->mp_texture )
|
|
{
|
|
verts[0].u = 0.0f;
|
|
verts[0].v = 0.0f;
|
|
verts[1].u = 1.0f;
|
|
verts[1].v = 0.0f;
|
|
verts[2].u = 1.0f;
|
|
verts[2].v = 1.0f;
|
|
verts[3].u = 0.0f;
|
|
verts[3].v = 1.0f;
|
|
|
|
Nx::CXboxTexture *p_xbox_texture = static_cast<CXboxTexture*>( p_details->mp_texture );
|
|
|
|
NxXbox::set_texture( 0, p_xbox_texture->GetEngineTexture()->pD3DTexture, p_xbox_texture->GetEngineTexture()->pD3DPalette );
|
|
NxXbox::set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );
|
|
}
|
|
else
|
|
{
|
|
NxXbox::set_texture( 0, NULL );
|
|
}
|
|
|
|
NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_BLEND );
|
|
|
|
NxXbox::set_render_state( RS_ZWRITEENABLE, 0 );
|
|
NxXbox::set_render_state( RS_ZTESTENABLE, 0 );
|
|
|
|
NxXbox::set_pixel_shader( 0 );
|
|
NxXbox::set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
|
|
|
|
NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_QUADLIST, 1, verts, sizeof( sXboxScreenFlashVert ));
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void plat_texture_splat_initialize( void )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void plat_texture_splat_cleanup( void )
|
|
{
|
|
sXboxSplatInstanceDetails *p_xbox_details;
|
|
|
|
Dbg_Assert( p_splat_details_table );
|
|
|
|
p_splat_details_table->IterateStart();
|
|
sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
|
|
while( p_details )
|
|
{
|
|
p_xbox_details = static_cast<sXboxSplatInstanceDetails*>( p_details );
|
|
|
|
delete p_xbox_details->mp_material;
|
|
|
|
p_details = p_splat_details_table->IterateNext();
|
|
|
|
p_splat_details_table->FlushItem((uint32)p_xbox_details );
|
|
delete p_xbox_details;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void plat_texture_splat_reset_poly( sSplatInstanceDetails *p_details, int index )
|
|
{
|
|
// Cast the details to Xbox details.
|
|
sXboxSplatInstanceDetails *p_xbox_details = static_cast<sXboxSplatInstanceDetails *>( p_details );
|
|
|
|
// Force this poly to be degenerate.
|
|
p_xbox_details->m_verts[index * 3 + 1] = p_xbox_details->m_verts[index * 3];
|
|
p_xbox_details->m_verts[index * 3 + 2] = p_xbox_details->m_verts[index * 3];
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
inline float CrossProduct2D( const Mth::Vector& v1, const Mth::Vector& v2 )
|
|
{
|
|
// Assumes for both v1 and v2 that the [z] and [w] components are 0.
|
|
return ( v1[X] * v2[Y] ) - ( v1[Y] * v2[X] );
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
static bool same_side( Mth::Vector &p1, Mth::Vector &p2, Mth::Vector &a, Mth::Vector &b )
|
|
{
|
|
float cp1 = CrossProduct2D( b - a, p1 - a );
|
|
float cp2 = CrossProduct2D( b - a, p2 - a );
|
|
if(( cp1 * cp2 ) >= 0.0f )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
static bool point_in_triangle( Mth::Vector &p, Mth::Vector &a, Mth::Vector &b, Mth::Vector &c )
|
|
{
|
|
if( same_side( p, a, b, c ) && same_side( p, b, c, a ) && same_side( p, c, a, b ))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
static inline bool line_segment_intersection( float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4 )
|
|
{
|
|
float ax = x2 - x1;
|
|
float bx = x3 - x4;
|
|
float ay = y2 - y1;
|
|
float by = y3 - y4;
|
|
float cx = x1 - x3;
|
|
float cy = y1 - y3;
|
|
float d = by * cx - bx * cy; // Alpha numerator.
|
|
float f = ay * bx - ax * by; // Both denominator.
|
|
|
|
// Alpha tests.
|
|
if( f > 0.0f )
|
|
{
|
|
if( d < 0.0f || d > f )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if( d > 0 || d < f )
|
|
return false;
|
|
}
|
|
|
|
float e = ax * cy - ay * cx; // Beta numerator.
|
|
|
|
// Beta tests.
|
|
if( f > 0.0f )
|
|
{
|
|
if( e < 0.0f || e > f )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if( e > 0 || e < f )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
static inline bool tri_texture_intersect( float u0, float v0, float u1, float v1, float u2, float v2 )
|
|
{
|
|
// Trivial check to see if all three points are outside range of texture.
|
|
if(( u0 < -1.0f ) && ( u1 < -1.0f ) && ( u2 < -1.0f ))
|
|
return false;
|
|
if(( u0 > 1.0f ) && ( u1 > 1.0f ) && ( u2 > 1.0f ))
|
|
return false;
|
|
if(( v0 < -1.0f ) && ( v1 < -1.0f ) && ( v2 < -1.0f ))
|
|
return false;
|
|
if(( v0 > 1.0f ) && ( v1 > 1.0f ) && ( v2 > 1.0f ))
|
|
return false;
|
|
|
|
// Perform the check to see if any line segment forming the tri intersects any line segment forming the texture.
|
|
Mth::Vector texture_square[4] = { Mth::Vector( -1.0f, -1.0f, 0.0f, 0.0f ),
|
|
Mth::Vector( 1.0f, -1.0f, 0.0f, 0.0f ),
|
|
Mth::Vector( 1.0f, 1.0f, 0.0f, 0.0f ),
|
|
Mth::Vector( -1.0f, 1.0f, 0.0f, 0.0f )};
|
|
for( int p = 0; p < 4; ++p )
|
|
{
|
|
int q = ( p + 1 ) % 4;
|
|
if( line_segment_intersection( u0, v0, u1, v1, texture_square[p][X], texture_square[p][Y], texture_square[q][X], texture_square[q][Y] ))
|
|
return true;
|
|
if( line_segment_intersection( u1, v1, u2, v2, texture_square[p][X], texture_square[p][Y], texture_square[q][X], texture_square[q][Y] ))
|
|
return true;
|
|
if( line_segment_intersection( u2, v2, u0, v0, texture_square[p][X], texture_square[p][Y], texture_square[q][X], texture_square[q][Y] ))
|
|
return true;
|
|
}
|
|
|
|
// If we reach this point there are three remaining possibilities:
|
|
// 1) That the tri lies entirely within the texture
|
|
// 2) That the texture lies entirely within the tri
|
|
// 3) That there is no space shared by tri and texture
|
|
|
|
// 1) Perform a trivial check to see whether a corner of the tri lies within the texture.
|
|
if(( u0 >= -1.0f ) && ( u0 <= 1.0f ) && ( v0 >= -1.0f ) && ( v0 <= 1.0f ))
|
|
return true;
|
|
if(( u1 >= -1.0f ) && ( u1 <= 1.0f ) && ( v1 >= -1.0f ) && ( v1 <= 1.0f ))
|
|
return true;
|
|
if(( u2 >= -1.0f ) && ( u2 <= 1.0f ) && ( v2 >= -1.0f ) && ( v2 <= 1.0f ))
|
|
return true;
|
|
|
|
// 2) Check that at least one corner of the texture falls within the tri.
|
|
Mth::Vector a( u0, v0, 0.0f );
|
|
Mth::Vector b( u1, v1, 0.0f );
|
|
Mth::Vector c( u2, v2, 0.0f );
|
|
for( int p = 0; p < 4; ++p )
|
|
{
|
|
if( point_in_triangle( texture_square[p], a, b, c ))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// 3) No space shared.
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
bool plat_texture_splat( Nx::CSector **pp_sectors, Nx::CCollStatic **pp_collision, Mth::Vector& start, Mth::Vector& end, float size, float lifetime, Nx::CTexture *p_texture, Nx::sSplatTrailInstanceDetails *p_trail_details )
|
|
{
|
|
XGMATRIX view_matrix, ortho_matrix, projection_matrix;
|
|
|
|
# if DRAW_DEBUG_LINES
|
|
Gfx::AddDebugLine( start, end, MAKE_RGB( 200, 200, 0 ), MAKE_RGB( 200, 200, 0 ), 1 );
|
|
# endif // DRAW_DEBUG_LINES
|
|
|
|
// The length of the start->end line defines the view depth of the frustum.
|
|
Mth::Vector splat_vector = end - start;
|
|
float view_depth = splat_vector.Length();
|
|
splat_vector.Normalize();
|
|
|
|
// Calculate the parallel projection matrix. Generally the projection vector will tend to point downwards, so we use a
|
|
// random vector in the x-z plane to define the up vector for the projection. However if this splat is part of a trail,
|
|
// use the previous point to generate the up vector.
|
|
Mth::Vector up;
|
|
if( p_trail_details )
|
|
{
|
|
up.Set( start[X] - p_trail_details->m_last_pos[X], start[Y] - p_trail_details->m_last_pos[Y], start[Z] - p_trail_details->m_last_pos[Z] );
|
|
|
|
// The height of the viewport is defined by the distance between the two points.
|
|
float height = up.Length() * 0.5f;
|
|
|
|
// Now we move start and end halfway back along the up vector.
|
|
start -= up * 0.5f;
|
|
end -= up * 0.5f;
|
|
|
|
up.Normalize();
|
|
|
|
XGMatrixLookAtRH( &view_matrix, (XGVECTOR3*)( &start[X] ), (XGVECTOR3*)( &end[X] ), (XGVECTOR3*)( &up[X] ));
|
|
XGMatrixOrthoRH( &ortho_matrix, size, height, 0.1f, view_depth );
|
|
}
|
|
else if( fabsf( splat_vector[Y] ) > 0.5f )
|
|
{
|
|
float angle = ((float)rand() * 2.0f * Mth::PI ) / (float)RAND_MAX;
|
|
up.Set( sinf( angle ), 0.0f, cosf( angle ));
|
|
XGMatrixLookAtRH( &view_matrix, (XGVECTOR3*)( &start[X] ), (XGVECTOR3*)( &end[X] ), (XGVECTOR3*)( &up[X] ));
|
|
XGMatrixOrthoRH( &ortho_matrix, size, size, 0.1f, view_depth );
|
|
}
|
|
else
|
|
{
|
|
up.Set( 0.0f, 1.0f, 0.0f );
|
|
XGMatrixLookAtRH( &view_matrix, (XGVECTOR3*)( &start[X] ), (XGVECTOR3*)( &end[X] ), (XGVECTOR3*)( &up[X] ));
|
|
XGMatrixOrthoRH( &ortho_matrix, size, size, 0.1f, view_depth );
|
|
}
|
|
|
|
XGMatrixMultiply( &projection_matrix, &view_matrix, &ortho_matrix );
|
|
|
|
// Pointer to the mesh we will be modifying. (Don't want to set the pointer up until we know for
|
|
// sure that we will be adding some polys).
|
|
sXboxSplatInstanceDetails *p_details = NULL;
|
|
sXboxSplatVert *p_target_verts = NULL;
|
|
|
|
Nx::CSector *p_sector;
|
|
|
|
while( p_sector = *pp_sectors )
|
|
{
|
|
Nx::CXboxGeom *p_xbox_geom = static_cast<Nx::CXboxGeom*>( p_sector->GetGeom());
|
|
|
|
if( p_xbox_geom )
|
|
{
|
|
# if DRAW_DEBUG_LINES
|
|
Mth::Vector min = p_xbox_geom->GetBoundingBox().GetMin();
|
|
Mth::Vector max = p_xbox_geom->GetBoundingBox().GetMax();
|
|
|
|
Mth::Vector box[8];
|
|
box[0] = box[1] = box[2] = box[3] = max;
|
|
box[1][X] = min[X];
|
|
box[2][Y] = min[Y];
|
|
box[3][Z] = min[Z];
|
|
box[5] = box[6] = box[7] = box[4] = min;;
|
|
box[5][X] = max[X];
|
|
box[6][Y] = max[Y];
|
|
box[7][Z] = max[Z];
|
|
|
|
for ( int i = 1; i < 4; i++ )
|
|
{
|
|
Gfx::AddDebugLine( box[0], box[i], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
|
|
}
|
|
for ( int i = 5; i < 8; i++ )
|
|
{
|
|
Gfx::AddDebugLine( box[4], box[i], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
|
|
}
|
|
Gfx::AddDebugLine( box[1], box[6], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
|
|
Gfx::AddDebugLine( box[1], box[7], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
|
|
Gfx::AddDebugLine( box[2], box[5], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
|
|
Gfx::AddDebugLine( box[2], box[7], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
|
|
Gfx::AddDebugLine( box[3], box[5], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
|
|
Gfx::AddDebugLine( box[3], box[6], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
|
|
# endif // DRAW_DEBUG_LINES
|
|
|
|
// For each mesh in the geom...
|
|
for( uint32 m = 0; m < p_xbox_geom->m_num_mesh; ++m )
|
|
{
|
|
NxXbox::sMesh *p_mesh = p_xbox_geom->m_mesh_array[m];
|
|
|
|
// Not allowed on meshes which are flagged not to shadow.
|
|
if( p_mesh->m_flags & NxXbox::sMesh::MESH_FLAG_NO_SKATER_SHADOW )
|
|
continue;
|
|
|
|
// Check the bounding box of this mesh falls within the scope of the line.
|
|
|
|
// Transform the mesh bounding box to see whether it falls within the projection frustum.
|
|
|
|
// If it falls within the projection frustum, we need to explicitly transform all the vertices.
|
|
BYTE *p_vert_data;
|
|
p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_vert_data, D3DLOCK_READONLY );
|
|
|
|
// Now scan through each non-degenerate tri, checking the verts to see if they are within scope.
|
|
uint32 index0;
|
|
uint32 index1 = p_mesh->mp_index_buffer[0][0];
|
|
uint32 index2 = p_mesh->mp_index_buffer[0][1];
|
|
for( uint32 i = 2; i < p_mesh->m_num_indices[0]; ++i )
|
|
{
|
|
// Wrap the indices round.
|
|
index0 = index1;
|
|
index1 = index2;
|
|
index2 = p_mesh->mp_index_buffer[0][i];
|
|
|
|
if(( index0 != index1 ) && ( index0 != index2 ) && ( index1 != index2 ))
|
|
{
|
|
XGVECTOR3 uvprojections[3];
|
|
XGVec3TransformCoord( &uvprojections[0], (XGVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 )), &projection_matrix );
|
|
XGVec3TransformCoord( &uvprojections[1], (XGVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 )), &projection_matrix );
|
|
XGVec3TransformCoord( &uvprojections[2], (XGVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 )), &projection_matrix );
|
|
|
|
// Check the z-values here, everything else is checked in tri_texture_intersect().
|
|
if(( uvprojections[0].z < 0.0f ) && ( uvprojections[1].z < 0.0f ) && ( uvprojections[2].z < 0.0f ))
|
|
continue;
|
|
if(( uvprojections[0].z > 1.0f ) && ( uvprojections[1].z > 1.0f ) && ( uvprojections[2].z > 1.0f ))
|
|
continue;
|
|
|
|
// Okay, this tri lies within the projection frustum. Now check that it intersects the texture
|
|
if( !tri_texture_intersect( uvprojections[0].x, uvprojections[0].y,
|
|
uvprojections[1].x, uvprojections[1].y,
|
|
uvprojections[2].x, uvprojections[2].y ))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get a pointer to the mesh used for rendering texture splats with the given texture.
|
|
// (Note this will create a new instance to handle texture splats of this texture if one does not already exist).
|
|
if( p_target_verts == NULL )
|
|
{
|
|
CXboxTexture *p_xbox_texture = static_cast<CXboxTexture*>( p_texture );
|
|
p_details = getDetailsForTextureSplat( p_xbox_texture->GetEngineTexture());
|
|
p_target_verts = p_details->m_verts;
|
|
Dbg_Assert( p_target_verts );
|
|
}
|
|
|
|
// Scan through the lifetimes, finding a 'dead' poly (lifetime == 0), or the oldest.
|
|
uint32 idx = p_details->GetOldestSplat();
|
|
|
|
// Convert lifetime from seconds to milliseconds.
|
|
p_details->m_lifetimes[idx] = (int)( lifetime * 1000.0f );
|
|
|
|
// Set up the corresponding vertices. First write the positions...
|
|
uint32 index = idx * 3;
|
|
p_target_verts[index + 0].pos = *(D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ));
|
|
p_target_verts[index + 1].pos = *(D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ));
|
|
p_target_verts[index + 2].pos = *(D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ));
|
|
|
|
// ...then the uv's...
|
|
p_target_verts[index + 0].u = ( uvprojections[0].x + 1.0f ) * 0.5f;
|
|
p_target_verts[index + 0].v = ( uvprojections[0].y + 1.0f ) * 0.5f;
|
|
p_target_verts[index + 1].u = ( uvprojections[1].x + 1.0f ) * 0.5f;
|
|
p_target_verts[index + 1].v = ( uvprojections[1].y + 1.0f ) * 0.5f;
|
|
p_target_verts[index + 2].u = ( uvprojections[2].x + 1.0f ) * 0.5f;
|
|
p_target_verts[index + 2].v = ( uvprojections[2].y + 1.0f ) * 0.5f;
|
|
|
|
// ...then the vertex colors.
|
|
p_target_verts[index + 0].col = ( *(D3DCOLOR*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ) + p_mesh->m_diffuse_offset ) & 0xFFFFFFUL ) | 0x80000000UL;
|
|
p_target_verts[index + 1].col = ( *(D3DCOLOR*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ) + p_mesh->m_diffuse_offset ) & 0xFFFFFFUL ) | 0x80000000UL;
|
|
p_target_verts[index + 2].col = ( *(D3DCOLOR*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ) + p_mesh->m_diffuse_offset ) & 0xFFFFFFUL ) | 0x80000000UL;
|
|
|
|
D3DXVECTOR3 *p_v0 = &( p_target_verts[index + 0].pos );
|
|
D3DXVECTOR3 *p_v1 = &( p_target_verts[index + 1].pos );
|
|
D3DXVECTOR3 *p_v2 = &( p_target_verts[index + 2].pos );
|
|
Mth::Vector pv( p_v1->x - p_v0->x, p_v1->y - p_v0->y, p_v1->z - p_v0->z );
|
|
Mth::Vector qv( p_v2->x - p_v0->x, p_v2->y - p_v0->y, p_v2->z - p_v0->z );
|
|
Mth::Vector r(( pv[Y] * qv[Z] ) - ( qv[Y] * pv[Z] ), ( pv[Z] * qv[X] ) - ( qv[Z] * pv[X] ), ( pv[X] * qv[Y] ) - ( qv[X] * pv[Y] ));
|
|
float area_squared = r.LengthSqr();
|
|
|
|
// Set the shatter test to ensure that we don't subdivide too far. Note that each successive subdivision will quarter
|
|
// the area of each triangle, which means the area *squared* of each triangle will become 1/16th of the previous value.
|
|
shatterAreaTest = area_squared / 128.0f;
|
|
|
|
triSubdivideStack.Reset();
|
|
triSubdivideStack.SetBlockSize( sizeof( sXboxSplatVert ));
|
|
triSubdivideStack.Push( &p_target_verts[index + 0] );
|
|
triSubdivideStack.Push( &p_target_verts[index + 1] );
|
|
triSubdivideStack.Push( &p_target_verts[index + 2] );
|
|
|
|
// Allocate a block of memory into which the subdivision stack will write the results.
|
|
uint8 *p_array = new uint8[8 * 1024];
|
|
uint8 *p_array_start = p_array;
|
|
uint8 *p_array_loop = p_array;
|
|
memset( p_array, 0, 8 * 1024 );
|
|
|
|
NxXbox::sMesh *p_dummy_mesh = new NxXbox::sMesh();
|
|
p_dummy_mesh->m_diffuse_offset = 12;
|
|
p_dummy_mesh->m_uv0_offset = 16;
|
|
|
|
while( subdivide_tri_stack( &p_array, p_dummy_mesh ));
|
|
|
|
// Ensure we haven't overrun the buffer.
|
|
Dbg_Assert((uint32)p_array - (uint32)p_array_loop < ( 8 * 1024 ));
|
|
|
|
bool subdivided_tri_added = false;
|
|
|
|
while( p_array_loop != p_array )
|
|
{
|
|
// Add this triangle, *if* it is valid.
|
|
if( tri_texture_intersect((((sXboxSplatVert*)p_array_loop )[0].u * 2.0f ) - 1.0f,
|
|
(((sXboxSplatVert*)p_array_loop )[0].v * 2.0f ) - 1.0f,
|
|
(((sXboxSplatVert*)p_array_loop )[1].u * 2.0f ) - 1.0f,
|
|
(((sXboxSplatVert*)p_array_loop )[1].v * 2.0f ) - 1.0f,
|
|
(((sXboxSplatVert*)p_array_loop )[2].u * 2.0f ) - 1.0f,
|
|
(((sXboxSplatVert*)p_array_loop )[2].v * 2.0f ) - 1.0f ))
|
|
{
|
|
// We have added at least one subdivided tri.
|
|
subdivided_tri_added = true;
|
|
|
|
// Convert lifetime from seconds to milliseconds.
|
|
p_details->m_lifetimes[idx] = (int)( lifetime * 1000.0f );
|
|
|
|
p_target_verts[index + 0].pos = ((sXboxSplatVert*)p_array_loop )[0].pos;
|
|
p_target_verts[index + 1].pos = ((sXboxSplatVert*)p_array_loop )[1].pos;
|
|
p_target_verts[index + 2].pos = ((sXboxSplatVert*)p_array_loop )[2].pos;
|
|
|
|
p_target_verts[index + 0].u = ((sXboxSplatVert*)p_array_loop )[0].u;
|
|
p_target_verts[index + 0].v = ((sXboxSplatVert*)p_array_loop )[0].v;
|
|
p_target_verts[index + 1].u = ((sXboxSplatVert*)p_array_loop )[1].u;
|
|
p_target_verts[index + 1].v = ((sXboxSplatVert*)p_array_loop )[1].v;
|
|
p_target_verts[index + 2].u = ((sXboxSplatVert*)p_array_loop )[2].u;
|
|
p_target_verts[index + 2].v = ((sXboxSplatVert*)p_array_loop )[2].v;
|
|
|
|
p_target_verts[index + 0].col = (((sXboxSplatVert*)p_array_loop )[0].col & 0xFFFFFFUL ) | 0x80000000UL;
|
|
p_target_verts[index + 1].col = (((sXboxSplatVert*)p_array_loop )[1].col & 0xFFFFFFUL ) | 0x80000000UL;
|
|
p_target_verts[index + 2].col = (((sXboxSplatVert*)p_array_loop )[2].col & 0xFFFFFFUL ) | 0x80000000UL;
|
|
|
|
idx = p_details->GetOldestSplat();
|
|
index = idx * 3;
|
|
}
|
|
|
|
p_array_loop += ( sizeof( sXboxSplatVert ) * 3 );
|
|
}
|
|
|
|
if( !subdivided_tri_added )
|
|
{
|
|
// No subdivided tris were added. This means we still have the large tri sitting in the list, which we don't want.
|
|
p_details->m_lifetimes[idx] = 0;
|
|
plat_texture_splat_reset_poly( p_details, idx );
|
|
}
|
|
|
|
delete p_dummy_mesh;
|
|
delete [] p_array_start;
|
|
|
|
# if DRAW_DEBUG_LINES
|
|
D3DXVECTOR3* p_d3dvert;
|
|
p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ));
|
|
Mth::Vector v0( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
|
|
p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ));
|
|
Mth::Vector v1( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
|
|
p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ));
|
|
Mth::Vector v2( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
|
|
Gfx::AddDebugLine( v0, v1, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );
|
|
Gfx::AddDebugLine( v1, v2, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );
|
|
Gfx::AddDebugLine( v2, v0, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );
|
|
# endif // DRAW_DEBUG_LINES
|
|
}
|
|
}
|
|
}
|
|
}
|
|
++pp_sectors;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void plat_texture_splat_render( void )
|
|
{
|
|
sXboxSplatInstanceDetails *p_xbox_details;
|
|
|
|
Dbg_Assert( p_splat_details_table );
|
|
|
|
NxXbox::set_pixel_shader( 0 );
|
|
NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
|
|
|
|
D3DDevice_SetTextureStageState( 0, D3DTSS_BORDERCOLOR, 0x00000000UL );
|
|
|
|
// Store the stage zero minfilter, since it may be anisotropic.
|
|
DWORD stage_zero_minfilter;
|
|
D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &stage_zero_minfilter );
|
|
D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
|
|
|
|
p_splat_details_table->IterateStart();
|
|
sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
|
|
|
|
while( p_details )
|
|
{
|
|
p_xbox_details = static_cast<sXboxSplatInstanceDetails*>( p_details );
|
|
|
|
if( p_xbox_details->m_highest_active_splat >= 0 )
|
|
{
|
|
p_xbox_details->mp_material->Submit();
|
|
NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_TRIANGLELIST, p_xbox_details->m_highest_active_splat + 1, p_xbox_details->m_verts, sizeof( sXboxSplatVert ));
|
|
}
|
|
|
|
p_details = p_splat_details_table->IterateNext();
|
|
}
|
|
|
|
// Restore the stage zero minfilter.
|
|
D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, stage_zero_minfilter );
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void plat_shatter_initialize( void )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void plat_shatter_cleanup( void )
|
|
{
|
|
sXboxShatterInstanceDetails *p_xbox_details;
|
|
|
|
Dbg_Assert( p_shatter_details_table );
|
|
|
|
p_shatter_details_table->IterateStart();
|
|
sShatterInstanceDetails *p_details = p_shatter_details_table->IterateNext();
|
|
while( p_details )
|
|
{
|
|
p_xbox_details = static_cast<sXboxShatterInstanceDetails*>( p_details );
|
|
|
|
p_details = p_shatter_details_table->IterateNext();
|
|
|
|
p_shatter_details_table->FlushItem((uint32)p_xbox_details );
|
|
delete p_xbox_details;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void plat_shatter( CGeom *p_geom )
|
|
{
|
|
CXboxGeom *p_xbox_geom = static_cast<CXboxGeom*>( p_geom );
|
|
|
|
// For each mesh in the geom...
|
|
for( uint32 m = 0; m < p_xbox_geom->m_num_mesh; ++m )
|
|
{
|
|
NxXbox::sMesh *p_mesh = p_xbox_geom->m_mesh_array[m];
|
|
|
|
if( p_mesh->m_num_indices[0] >= 3 )
|
|
{
|
|
// Set the block size for this mesh.
|
|
triSubdivideStack.SetBlockSize( p_mesh->m_vertex_stride );
|
|
|
|
// Get a pointer to the renderable data.
|
|
BYTE *p_vert_data;
|
|
p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_vert_data, D3DLOCK_READONLY );
|
|
|
|
// First scan through each non-degenerate tri, counting them to see how many verts we'll need.
|
|
// We also have to figure the area of the tris here, since we need to calculate the worst case given the requirements for subdivision.
|
|
uint32 valid_tris = 0;
|
|
uint32 index0;
|
|
uint32 index1 = p_mesh->mp_index_buffer[0][0];
|
|
uint32 index2 = p_mesh->mp_index_buffer[0][1];
|
|
for( uint32 i = 2; i < p_mesh->m_num_indices[0]; ++i )
|
|
{
|
|
// Wrap the indices round.
|
|
index0 = index1;
|
|
index1 = index2;
|
|
index2 = p_mesh->mp_index_buffer[0][i];
|
|
|
|
if(( index0 != index1 ) && ( index0 != index2 ) && ( index1 != index2 ))
|
|
{
|
|
++valid_tris;
|
|
|
|
D3DXVECTOR3 *p_vert0 = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ));
|
|
D3DXVECTOR3 *p_vert1 = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ));
|
|
D3DXVECTOR3 *p_vert2 = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ));
|
|
|
|
// Push this tri onto the stack.
|
|
triSubdivideStack.Push( p_vert0 );
|
|
triSubdivideStack.Push( p_vert1 );
|
|
triSubdivideStack.Push( p_vert2 );
|
|
|
|
// Figure the area of this tri.
|
|
Mth::Vector p( p_vert1->x - p_vert0->x, p_vert1->y - p_vert0->y, p_vert1->z - p_vert0->z );
|
|
Mth::Vector q( p_vert2->x - p_vert0->x, p_vert2->y - p_vert0->y, p_vert2->z - p_vert0->z );
|
|
Mth::Vector r(( p[Y] * q[Z] ) - ( q[Y] * p[Z] ), ( p[Z] * q[X] ) - ( q[Z] * p[X] ), ( p[X] * q[Y] ) - ( q[X] * p[Y] ));
|
|
float area_squared = r.LengthSqr();
|
|
if( area_squared > shatterAreaTest )
|
|
{
|
|
// We will need to subdivide - each subdivision will result in an area one quarter the previous area
|
|
// (and thusly the square of the area will be one sixteenth the previous area).
|
|
int num_extra_tris = 1;
|
|
while( area_squared > shatterAreaTest )
|
|
{
|
|
num_extra_tris *= 4;
|
|
area_squared *= ( 1.0f / 16.0f );
|
|
}
|
|
|
|
// This original tri will not be added...
|
|
--valid_tris;
|
|
|
|
// ...however, the subdivided versions will.
|
|
valid_tris += num_extra_tris;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( valid_tris == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Create a tracking structure for this mesh.
|
|
sXboxShatterInstanceDetails *p_details = new sXboxShatterInstanceDetails( valid_tris, p_mesh );
|
|
uint8 *p_write_vertex = p_details->mp_vertex_buffer;
|
|
uint32 details_index = 0;
|
|
|
|
Mth::Vector spread_center = shatterVelocity * -shatterSpreadFactor;
|
|
float base_speed = shatterVelocity.Length();
|
|
|
|
spread_center += Mth::Vector( p_mesh->m_sphere_center.x, p_mesh->m_sphere_center.y, p_mesh->m_sphere_center.z );
|
|
|
|
// Add the tracking structure to the table.
|
|
p_shatter_details_table->PutItem((uint32)p_details, p_details );
|
|
|
|
// Process-subdivide the entire stack.
|
|
uint8 *p_copy_vertex = p_write_vertex;
|
|
while( subdivide_tri_stack( &p_write_vertex, p_mesh ));
|
|
|
|
// Copy the (possibly subdivided) vertex data over.
|
|
Dbg_Assert(((uint32)p_write_vertex - (uint32)p_copy_vertex ) <= ( valid_tris * 3 * p_mesh->m_vertex_stride ));
|
|
while( p_copy_vertex < p_write_vertex )
|
|
{
|
|
Dbg_Assert( details_index < valid_tris );
|
|
|
|
D3DXVECTOR3 *p_vert0 = (D3DXVECTOR3*)( p_copy_vertex + ( p_mesh->m_vertex_stride * 0 ));
|
|
D3DXVECTOR3 *p_vert1 = (D3DXVECTOR3*)( p_copy_vertex + ( p_mesh->m_vertex_stride * 1 ));
|
|
D3DXVECTOR3 *p_vert2 = (D3DXVECTOR3*)( p_copy_vertex + ( p_mesh->m_vertex_stride * 2 ));
|
|
|
|
// Calculate position as the midpoint of the three vertices per poly.
|
|
p_details->mp_positions[details_index][X] = ( p_vert0->x + p_vert1->x + p_vert2->x ) * ( 1.0f / 3.0f );
|
|
p_details->mp_positions[details_index][Y] = ( p_vert0->y + p_vert1->y + p_vert2->y ) * ( 1.0f / 3.0f );
|
|
p_details->mp_positions[details_index][Z] = ( p_vert0->z + p_vert1->z + p_vert2->z ) * ( 1.0f / 3.0f );
|
|
|
|
// Calculate the vector <velocity> back from the bounding box of the object. Then use this to figure the 'spread' of the
|
|
// shards by calculating the vector from this position to the center of each shard.
|
|
float speed = base_speed + ( base_speed * (( shatterVelocityVariance * rand() ) / RAND_MAX ));
|
|
p_details->mp_velocities[details_index] = ( p_details->mp_positions[details_index] - spread_center ).Normalize( speed );
|
|
|
|
Mth::Vector axis( -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ));
|
|
axis.Normalize();
|
|
p_details->mp_matrices[details_index].Ident();
|
|
p_details->mp_matrices[details_index].Rotate( axis, 0.1f * ((float)rand() / RAND_MAX ));
|
|
|
|
p_copy_vertex += ( p_mesh->m_vertex_stride * 3 );
|
|
|
|
++details_index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
void plat_shatter_update( sShatterInstanceDetails *p_details, float framelength )
|
|
{
|
|
sXboxShatterInstanceDetails *p_xbox_details = static_cast<sXboxShatterInstanceDetails*>( p_details );
|
|
|
|
BYTE *p_vert_data = p_xbox_details->mp_vertex_buffer;
|
|
|
|
// Load up initial three vertex pointers.
|
|
D3DXVECTOR3 *p_v0 = (D3DXVECTOR3*)( p_vert_data );
|
|
D3DXVECTOR3 *p_v1 = (D3DXVECTOR3*)( p_vert_data + p_xbox_details->mp_mesh->m_vertex_stride );
|
|
D3DXVECTOR3 *p_v2 = (D3DXVECTOR3*)( p_vert_data + ( 2 * p_xbox_details->mp_mesh->m_vertex_stride ));
|
|
|
|
for( int i = 0; i < p_details->m_num_triangles; ++i )
|
|
{
|
|
// To move the shatter pieces:
|
|
// 1) subtract position from each vertex
|
|
// 2) rotate
|
|
// 3) update position with velocity
|
|
// 4) add new position to each vertex
|
|
|
|
// The matrix holds 3 vectors at once.
|
|
Mth::Matrix m;
|
|
m[X].Set( p_v0->x - p_details->mp_positions[i][X], p_v0->y - p_details->mp_positions[i][Y], p_v0->z - p_details->mp_positions[i][Z] );
|
|
m[Y].Set( p_v1->x - p_details->mp_positions[i][X], p_v1->y - p_details->mp_positions[i][Y], p_v1->z - p_details->mp_positions[i][Z] );
|
|
m[Z].Set( p_v2->x - p_details->mp_positions[i][X], p_v2->y - p_details->mp_positions[i][Y], p_v2->z - p_details->mp_positions[i][Z] );
|
|
|
|
m[X].Rotate( p_details->mp_matrices[i] );
|
|
m[Y].Rotate( p_details->mp_matrices[i] );
|
|
m[Z].Rotate( p_details->mp_matrices[i] );
|
|
|
|
// Update the position and velocity of the shatter piece, dealing with bouncing if necessary.
|
|
p_details->UpdateParameters( i, framelength );
|
|
|
|
m[X] += p_details->mp_positions[i];
|
|
m[Y] += p_details->mp_positions[i];
|
|
m[Z] += p_details->mp_positions[i];
|
|
|
|
p_v0->x = m[X][X]; p_v0->y = m[X][Y]; p_v0->z = m[X][Z];
|
|
p_v1->x = m[Y][X]; p_v1->y = m[Y][Y]; p_v1->z = m[Y][Z];
|
|
p_v2->x = m[Z][X]; p_v2->y = m[Z][Y]; p_v2->z = m[Z][Z];
|
|
|
|
p_v0 = (D3DXVECTOR3*)(((BYTE*)p_v0 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
|
|
p_v1 = (D3DXVECTOR3*)(((BYTE*)p_v1 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
|
|
p_v2 = (D3DXVECTOR3*)(((BYTE*)p_v2 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
|
|
}
|
|
|
|
// Also process normals if they exist.
|
|
if( p_xbox_details->mp_mesh->m_normal_offset > 0 )
|
|
{
|
|
p_v0 = (D3DXVECTOR3*)( p_vert_data + p_xbox_details->mp_mesh->m_normal_offset );
|
|
p_v1 = (D3DXVECTOR3*)( p_vert_data + p_xbox_details->mp_mesh->m_normal_offset + p_xbox_details->mp_mesh->m_vertex_stride );
|
|
p_v2 = (D3DXVECTOR3*)( p_vert_data + p_xbox_details->mp_mesh->m_normal_offset + ( 2 * p_xbox_details->mp_mesh->m_vertex_stride ));
|
|
|
|
for( int i = 0; i < p_details->m_num_triangles; ++i )
|
|
{
|
|
// The matrix holds 3 vectors at once.
|
|
Mth::Matrix m;
|
|
m[X].Set( p_v0->x, p_v0->y, p_v0->z );
|
|
m[Y].Set( p_v1->x, p_v1->y, p_v1->z );
|
|
m[Z].Set( p_v2->x, p_v2->y, p_v2->z );
|
|
|
|
m[X].Rotate( p_details->mp_matrices[i] );
|
|
m[Y].Rotate( p_details->mp_matrices[i] );
|
|
m[Z].Rotate( p_details->mp_matrices[i] );
|
|
|
|
p_v0->x = m[X][X]; p_v0->y = m[X][Y]; p_v0->z = m[X][Z];
|
|
p_v1->x = m[Y][X]; p_v1->y = m[Y][Y]; p_v1->z = m[Y][Z];
|
|
p_v2->x = m[Z][X]; p_v2->y = m[Z][Y]; p_v2->z = m[Z][Z];
|
|
|
|
p_v0 = (D3DXVECTOR3*)(((BYTE*)p_v0 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
|
|
p_v1 = (D3DXVECTOR3*)(((BYTE*)p_v1 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
|
|
p_v2 = (D3DXVECTOR3*)(((BYTE*)p_v2 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
void plat_shatter_render( sShatterInstanceDetails *p_details )
|
|
{
|
|
sXboxShatterInstanceDetails *p_xbox_details = static_cast<sXboxShatterInstanceDetails*>( p_details );
|
|
|
|
p_xbox_details->mp_mesh->mp_material->Submit();
|
|
|
|
NxXbox::set_pixel_shader( p_xbox_details->mp_mesh->m_pixel_shader );
|
|
NxXbox::set_vertex_shader( p_xbox_details->mp_mesh->m_vertex_shader[0] );
|
|
|
|
NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_TRIANGLELIST, p_xbox_details->m_num_triangles, p_xbox_details->mp_vertex_buffer, p_xbox_details->mp_mesh->m_vertex_stride );
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
// FOG
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CFog::s_plat_enable_fog( bool enable )
|
|
{
|
|
if( enable != (bool)NxXbox::EngineGlobals.fog_enabled )
|
|
{
|
|
NxXbox::EngineGlobals.fog_enabled = enable;
|
|
D3DDevice_SetRenderState( D3DRS_FOGENABLE, NxXbox::EngineGlobals.fog_enabled );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CFog::s_plat_set_fog_near_distance( float distance )
|
|
{
|
|
NxXbox::EngineGlobals.fog_start = -distance;
|
|
|
|
// Test code for now.
|
|
NxXbox::EngineGlobals.fog_end = NxXbox::EngineGlobals.fog_start - FEET_TO_INCHES( 600.0f );
|
|
|
|
D3DDevice_SetRenderState( D3DRS_FOGSTART, *((DWORD*)( &NxXbox::EngineGlobals.fog_start )));
|
|
D3DDevice_SetRenderState( D3DRS_FOGEND, *((DWORD*)( &NxXbox::EngineGlobals.fog_end )));
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CFog::s_plat_set_fog_exponent( float exponent )
|
|
{
|
|
// This is no longer a valid call.
|
|
// if( exponent > 0.0f )
|
|
// {
|
|
// s_plat_enable_fog( true );
|
|
//
|
|
// NxXbox::EngineGlobals.fog_start = FEET_TO_INCHES( -20.0f );
|
|
// NxXbox::EngineGlobals.fog_end = FEET_TO_INCHES( -60.0f );
|
|
// D3DDevice_SetRenderState( D3DRS_FOGSTART, *((DWORD*)( &NxXbox::EngineGlobals.fog_start )));
|
|
// D3DDevice_SetRenderState( D3DRS_FOGEND, *((DWORD*)( &NxXbox::EngineGlobals.fog_end )));
|
|
// }
|
|
// else
|
|
// {
|
|
// s_plat_enable_fog( false );
|
|
// }
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CFog::s_plat_set_fog_color( void )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CFog::s_plat_fog_update( void )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CFog::s_plat_set_fog_rgba( Image::RGBA rgba )
|
|
{
|
|
// Alpha effectively determines the fog density, with zero alpha meaning no fog.
|
|
if( rgba.a == 0 )
|
|
{
|
|
s_plat_enable_fog( false );
|
|
}
|
|
else
|
|
{
|
|
s_plat_enable_fog( true );
|
|
}
|
|
|
|
// Calculate alpha (and clamp between 0.0 and 1.0).
|
|
float f_alpha = (float)rgba.a / 128.0f;
|
|
f_alpha = Mth::Min( f_alpha, 1.0f );
|
|
f_alpha = Mth::Max( f_alpha, 0.0f );
|
|
|
|
// Set the density register in the pixel shader constants (uses c4.r/g/b).
|
|
NxXbox::EngineGlobals.pixel_shader_constants[16] = f_alpha;
|
|
NxXbox::EngineGlobals.pixel_shader_constants[17] = f_alpha;
|
|
NxXbox::EngineGlobals.pixel_shader_constants[18] = f_alpha;
|
|
|
|
NxXbox::EngineGlobals.fog_color = ((uint32)rgba.r << 16 ) | ((uint32)rgba.g << 8 ) | ((uint32)rgba.b );
|
|
D3DDevice_SetRenderState( D3DRS_FOGCOLOR, NxXbox::EngineGlobals.fog_color );
|
|
}
|
|
|
|
} // Nx
|