mirror of
https://github.com/thug1src/thug.git
synced 2025-01-22 05:43:47 +00:00
2956 lines
97 KiB
C++
2956 lines
97 KiB
C++
|
#include <core/defines.h>
|
||
|
#include <core/debug.h>
|
||
|
#include <core/HashTable.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <xgraphics.h>
|
||
|
#include <xgmath.h>
|
||
|
#include <gfx\xbox\p_nxgeom.h>
|
||
|
#include <sys\timer.h>
|
||
|
#include "nx_init.h"
|
||
|
#include "scene.h"
|
||
|
#include "render.h"
|
||
|
#include "instance.h"
|
||
|
#include "occlude.h"
|
||
|
#include "billboard.h"
|
||
|
|
||
|
#include "PixelShader0.h"
|
||
|
#include "PixelShader0IVA.h"
|
||
|
#include "PixelShader1.h"
|
||
|
#include "PixelShader2.h"
|
||
|
#include "PixelShader3.h"
|
||
|
#include "PixelShader4.h"
|
||
|
#include "PixelShader5.h"
|
||
|
#include "PixelShaderBrighten.h"
|
||
|
#include "PixelShaderBrightenIVA.h"
|
||
|
#include "PixelShaderFocusBlur.h"
|
||
|
#include "PixelShaderFocusIntegrate.h"
|
||
|
#include "PixelShaderFocusLookupIntegrate.h"
|
||
|
#include "PixelShaderNULL.h"
|
||
|
#include "PixelShaderPointSprite.h"
|
||
|
#include "PixelShader_ShadowBuffer.h"
|
||
|
#include "PixelShaderBumpWater.h"
|
||
|
#include "ShadowBufferStaticGeomPS.h"
|
||
|
|
||
|
DWORD PixelShader0;
|
||
|
DWORD PixelShader0IVA;
|
||
|
DWORD PixelShader1;
|
||
|
DWORD PixelShader2;
|
||
|
DWORD PixelShader3;
|
||
|
DWORD PixelShader4;
|
||
|
DWORD PixelShader5;
|
||
|
DWORD PixelShaderBrighten;
|
||
|
DWORD PixelShaderBrightenIVA;
|
||
|
DWORD PixelShaderFocusBlur;
|
||
|
DWORD PixelShaderFocusIntegrate;
|
||
|
DWORD PixelShaderFocusLookupIntegrate;
|
||
|
DWORD PixelShaderNULL;
|
||
|
DWORD PixelShaderPointSprite;
|
||
|
DWORD PixelShader_ShadowBuffer;
|
||
|
DWORD PixelShaderBumpWater;
|
||
|
DWORD ShadowBufferStaticGeomPS;
|
||
|
|
||
|
//D3DXMATRIX *p_bbox_transform = NULL;
|
||
|
//D3DXMATRIX bbox_transform;
|
||
|
XGMATRIX *p_bbox_transform = NULL;
|
||
|
XGMATRIX bbox_transform;
|
||
|
|
||
|
extern DWORD ShadowBufferStaticGeomVS;
|
||
|
|
||
|
namespace NxXbox
|
||
|
{
|
||
|
|
||
|
const float FRONT_TO_BACK_SORT_CUTOFF = ( 50.0f * 12.0f );
|
||
|
|
||
|
Lst::HashTable< sTextureProjectionDetails > *pTextureProjectionDetailsTable = NULL;
|
||
|
|
||
|
// For converting a FLOAT to a DWORD (useful for SetRenderState() calls)
|
||
|
inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }
|
||
|
|
||
|
static Lst::HashTable<DWORD> sPixelShaderTable( 8 );
|
||
|
|
||
|
|
||
|
static const int MAX_FREE_TESTS = 1024;
|
||
|
static bool visibilityTestValuesInitialised = false;
|
||
|
static uint8 visibilityTestValues[MAX_FREE_TESTS];
|
||
|
|
||
|
struct sVisibilityTestFIFO
|
||
|
{
|
||
|
static const int MAX_FIFO_SIZE = 4;
|
||
|
|
||
|
uint32 m_status_fifo[MAX_FIFO_SIZE];
|
||
|
uint32 m_fifo_index;
|
||
|
|
||
|
sVisibilityTestFIFO();
|
||
|
~sVisibilityTestFIFO();
|
||
|
|
||
|
static uint32 GetFreeTestIndex( void );
|
||
|
uint32 AddStatus( void );
|
||
|
uint32 GetStatus( void );
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
void SlideQueue( void );
|
||
|
uint32 m_last_valid_status;
|
||
|
};
|
||
|
|
||
|
|
||
|
struct sLightGlowDetails
|
||
|
{
|
||
|
Mth::Vector m_pos;
|
||
|
float m_glow_radius;
|
||
|
float m_current_radius;
|
||
|
float m_test_radius;
|
||
|
float m_radius_growth;
|
||
|
sVisibilityTestFIFO m_visibility_test_fifo;
|
||
|
};
|
||
|
|
||
|
|
||
|
static Lst::HashTable<sLightGlowDetails> sLightGlowDetailsTable( 8 );
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
BlendModes GetBlendMode( uint32 blend_checksum )
|
||
|
{
|
||
|
BlendModes rv = NxXbox::vBLEND_MODE_DIFFUSE;
|
||
|
|
||
|
switch ( blend_checksum )
|
||
|
{
|
||
|
case 0x54628ed7: // Blend
|
||
|
rv = NxXbox::vBLEND_MODE_BLEND;
|
||
|
break;
|
||
|
case 0x02e58c18: // Add
|
||
|
rv = NxXbox::vBLEND_MODE_ADD;
|
||
|
break;
|
||
|
case 0xa7fd7d23: // Sub
|
||
|
case 0xdea7e576: // Subtract
|
||
|
rv = NxXbox::vBLEND_MODE_SUBTRACT;
|
||
|
break;
|
||
|
case 0x40f44b8a: // Modulate
|
||
|
rv = NxXbox::vBLEND_MODE_MODULATE;
|
||
|
break;
|
||
|
case 0x68e77f40: // Brighten
|
||
|
rv = NxXbox::vBLEND_MODE_BRIGHTEN;
|
||
|
break;
|
||
|
case 0x18b98905: // FixBlend
|
||
|
rv = NxXbox::vBLEND_MODE_BLEND_FIXED;
|
||
|
break;
|
||
|
case 0xa86285a1: // FixAdd
|
||
|
rv = NxXbox::vBLEND_MODE_ADD_FIXED;
|
||
|
break;
|
||
|
case 0x0d7a749a: // FixSub
|
||
|
case 0x0eea99ff: // FixSubtract
|
||
|
rv = NxXbox::vBLEND_MODE_SUB_FIXED;
|
||
|
break;
|
||
|
case 0x90b93703: // FixModulate
|
||
|
rv = NxXbox::vBLEND_MODE_MODULATE_FIXED;
|
||
|
break;
|
||
|
case 0xb8aa03c9: // FixBrighten
|
||
|
rv = NxXbox::vBLEND_MODE_BRIGHTEN_FIXED;
|
||
|
break;
|
||
|
case 0x515e298e: // Diffuse
|
||
|
case 0x806fff30: // None
|
||
|
rv = NxXbox::vBLEND_MODE_DIFFUSE;
|
||
|
break;
|
||
|
default:
|
||
|
Dbg_MsgAssert(0,("Illegal blend mode specified. Please use (fix)blend/add/sub/modulate/brighten or diffuse/none."));
|
||
|
break;
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
sVisibilityTestFIFO::sVisibilityTestFIFO()
|
||
|
{
|
||
|
m_fifo_index = 0;
|
||
|
m_last_valid_status = 0;
|
||
|
|
||
|
if( !visibilityTestValuesInitialised )
|
||
|
{
|
||
|
ZeroMemory( visibilityTestValues, sizeof( uint8 ) * MAX_FREE_TESTS );
|
||
|
|
||
|
// The first index is always reserved.
|
||
|
visibilityTestValues[0] = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
sVisibilityTestFIFO::~sVisibilityTestFIFO()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void sVisibilityTestFIFO::SlideQueue( void )
|
||
|
{
|
||
|
Dbg_Assert( m_fifo_index > 0 );
|
||
|
|
||
|
m_status_fifo[0] = m_status_fifo[1];
|
||
|
m_status_fifo[1] = m_status_fifo[2];
|
||
|
m_status_fifo[2] = m_status_fifo[3];
|
||
|
m_status_fifo[3] = 0;
|
||
|
|
||
|
--m_fifo_index;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
uint32 sVisibilityTestFIFO::GetFreeTestIndex( void )
|
||
|
{
|
||
|
for( uint32 i = 0; i < MAX_FREE_TESTS; ++i )
|
||
|
{
|
||
|
if( visibilityTestValues[i] == 0 )
|
||
|
{
|
||
|
visibilityTestValues[i] = 1;
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
Dbg_Assert( 0 );
|
||
|
return MAX_FREE_TESTS - 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
uint32 sVisibilityTestFIFO::AddStatus( void )
|
||
|
{
|
||
|
// If the queue is already full, do nothing.
|
||
|
if( m_fifo_index >= ( MAX_FIFO_SIZE - 1 ))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Get a free index.
|
||
|
uint32 index = GetFreeTestIndex();
|
||
|
m_status_fifo[m_fifo_index++] = index;
|
||
|
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
uint32 sVisibilityTestFIFO::GetStatus( void )
|
||
|
{
|
||
|
Dbg_Assert( m_fifo_index > 0 );
|
||
|
|
||
|
UINT result;
|
||
|
|
||
|
// Get the result from the least recently issued test.
|
||
|
HRESULT hr = D3DDevice_GetVisibilityTestResult( m_status_fifo[0], &result, NULL );
|
||
|
|
||
|
if( hr == D3D_OK )
|
||
|
{
|
||
|
// Finished with this test. First mark this test index as no longer in use.
|
||
|
visibilityTestValues[m_status_fifo[0]] = 0;
|
||
|
|
||
|
// Remove entry from the queue.
|
||
|
SlideQueue();
|
||
|
|
||
|
m_last_valid_status = (uint32)result;
|
||
|
return m_last_valid_status;
|
||
|
}
|
||
|
else if( hr == D3DERR_TESTINCOMPLETE )
|
||
|
{
|
||
|
// Use the last valid status.
|
||
|
return m_last_valid_status;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
DWORD get_pixel_shader( sMaterial *p_material )
|
||
|
{
|
||
|
const int PIXEL_SHADER_BUFFER_SIZE = 64 * 1024;
|
||
|
static char pixel_shader_buffer[PIXEL_SHADER_BUFFER_SIZE];
|
||
|
static int pixel_shader_buffer_offset = 0;
|
||
|
uint32 blend_modes[4];
|
||
|
|
||
|
bool mcm = true;
|
||
|
// bool mcm = false;
|
||
|
for( uint32 p = 0; p < p_material->m_passes; ++p )
|
||
|
{
|
||
|
if( !mcm )
|
||
|
{
|
||
|
if(( p_material->m_color[p][0] != 0.5f ) || ( p_material->m_color[p][1] != 0.5f ) || ( p_material->m_color[p][2] != 0.5f ))
|
||
|
mcm = true;
|
||
|
}
|
||
|
blend_modes[p] = p_material->m_reg_alpha[p] & sMaterial::BLEND_MODE_MASK;
|
||
|
}
|
||
|
|
||
|
if( p_material->m_passes > 1 )
|
||
|
{
|
||
|
// First we build the unique key from the properties of the required shader, to see if it already exists.
|
||
|
uint32 code = p_material->m_passes;
|
||
|
for( uint32 p = 0; p < p_material->m_passes; ++p )
|
||
|
{
|
||
|
code |= ( blend_modes[p] << ( 5 * ( p + 1 )));
|
||
|
}
|
||
|
|
||
|
// Integrate the ignore vertex alpha flags.
|
||
|
uint32 ignore_bf = p_material->GetIgnoreVertexAlphaPasses();
|
||
|
code |= ( ignore_bf << 25 );
|
||
|
|
||
|
// Check also to see if material color modulation is required.
|
||
|
if( mcm )
|
||
|
{
|
||
|
code |= ( 1 << 30 );
|
||
|
}
|
||
|
|
||
|
// Check to see if the shader exists, if so just return the shader handle.
|
||
|
DWORD *p_shader = sPixelShaderTable.GetItem( code );
|
||
|
if( p_shader )
|
||
|
{
|
||
|
return (DWORD)p_shader;
|
||
|
}
|
||
|
|
||
|
// We need to build the shader.
|
||
|
char shader_buffer[1024];
|
||
|
|
||
|
sprintf( shader_buffer, "xps.1.1\n" );
|
||
|
|
||
|
strcat( shader_buffer, "tex t0\n" );
|
||
|
strcat( shader_buffer, "tex t1\n" );
|
||
|
|
||
|
switch( p_material->m_passes )
|
||
|
{
|
||
|
case 2:
|
||
|
{
|
||
|
Dbg_Assert( ignore_bf <= 0x03 );
|
||
|
|
||
|
if( mcm )
|
||
|
{
|
||
|
// Modulate texture0 and texture 1 color with pass 0 and pass 1 material color, and place into t0.rgb and t1.rgb.
|
||
|
strcat( shader_buffer, "xmma t0.rgb,t1.rgb,discard.rgb,t0.rgb,c0.rgb,t1.rgb,c1.rgb\n" );
|
||
|
|
||
|
// Modulate result color with vertex color.
|
||
|
strcat( shader_buffer, "xmma_x4 r0.rgb,r1.rgb,discard.rgb,t0.rgb,v0.rgb,t1.rgb,v0.rgb\n" );
|
||
|
|
||
|
// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
|
||
|
if( ignore_bf == 0x00 )
|
||
|
{
|
||
|
// Pass0 modulates with vertex alpha. Pass1 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,v0.a\n" );
|
||
|
}
|
||
|
else if( ignore_bf == 0x01 )
|
||
|
{
|
||
|
// Pass0 modulates with constant alpha. Pass1 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,v0.a\n" );
|
||
|
}
|
||
|
else if( ignore_bf == 0x02 )
|
||
|
{
|
||
|
// Pass0 modulates with vertex alpha. Pass1 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,c4.a\n" );
|
||
|
}
|
||
|
else if( ignore_bf == 0x03 )
|
||
|
{
|
||
|
// Pass0 modulates with constant alpha. Pass1 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,c4.a\n" );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Modulate texture0 and texture1 color with vertex color.
|
||
|
strcat( shader_buffer, "xmma_x2 r0.rgb,r1.rgb,discard.rgb,t0.rgb,v0.rgb,t1.rgb,v0.rgb\n" );
|
||
|
|
||
|
// Modulate texture0 and tetxure1 alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
|
||
|
if( ignore_bf == 0x00 )
|
||
|
{
|
||
|
// Pass0 modulates with vertex alpha. Pass1 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,v0.a\n" );
|
||
|
}
|
||
|
else if( ignore_bf == 0x01 )
|
||
|
{
|
||
|
// Pass0 modulates with constant alpha. Pass1 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,v0.a\n" );
|
||
|
}
|
||
|
else if( ignore_bf == 0x02 )
|
||
|
{
|
||
|
// Pass0 modulates with vertex alpha. Pass1 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,c4.a\n" );
|
||
|
}
|
||
|
else if( ignore_bf == 0x03 )
|
||
|
{
|
||
|
// Pass0 modulates with constant alpha. Pass1 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,c4.a\n" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Then deal with the second pass blend.
|
||
|
switch( blend_modes[1] )
|
||
|
{
|
||
|
case vBLEND_MODE_ADD:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ADD_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUBTRACT:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,-r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUB_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,-c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r1.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,c1.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,r1.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,c1.a\n" );
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r0.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r0.a,r0.rgb,r1.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_GLOSS_MAP:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul v1.rgb,v1.rgb,t1.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 3:
|
||
|
{
|
||
|
Dbg_Assert( ignore_bf <= 0x07 );
|
||
|
|
||
|
strcat( shader_buffer, "tex t2\n" );
|
||
|
|
||
|
if( mcm )
|
||
|
{
|
||
|
// Modulate texture0 and texture 1 color with pass 0 and pass 1 material color, and place into t0.rgb and t1.rgb.
|
||
|
strcat( shader_buffer, "xmma t0.rgb,t1.rgb,discard.rgb,t0.rgb,c0.rgb,t1.rgb,c1.rgb\n" );
|
||
|
|
||
|
// Modulate result color with vertex color.
|
||
|
strcat( shader_buffer, "xmma_x4 r0.rgb,r1.rgb,discard.rgb,t0.rgb,v0.rgb,t1.rgb,v0.rgb\n" );
|
||
|
|
||
|
// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
|
||
|
if(( ignore_bf & 0x03 ) == 0x00 )
|
||
|
{
|
||
|
// Pass0 modulates with vertex alpha. Pass1 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,v0.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x03 ) == 0x01 )
|
||
|
{
|
||
|
// Pass0 modulates with constant alpha. Pass1 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,v0.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x03 ) == 0x02 )
|
||
|
{
|
||
|
// Pass0 modulates with vertex alpha. Pass1 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,c4.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x03 ) == 0x03 )
|
||
|
{
|
||
|
// Pass0 modulates with constant alpha. Pass1 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,c4.a\n" );
|
||
|
}
|
||
|
|
||
|
// Modulate texture2 with pass 2 material color.
|
||
|
strcat( shader_buffer, "mul t2.rgb,t2.rgb,c2.rgb\n" );
|
||
|
|
||
|
// Modulate result color with vertex color.
|
||
|
strcat( shader_buffer, "mul_x4 t2.rgb,t2.rgb,v0.rgb\n" );
|
||
|
|
||
|
// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
|
||
|
if(( ignore_bf & 0x04 ) == 0x00 )
|
||
|
{
|
||
|
// Pass2 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+mul_x2 t2.a,t2.a,v0.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x04 ) == 0x04 )
|
||
|
{
|
||
|
// Pass2 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+mul_x2 t2.a,t2.a,c4.a\n" );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Modulate texture0 and texture1 with vertex color.
|
||
|
strcat( shader_buffer, "xmma_x2 r0,r1,discard,t0,v0,t1,v0\n" );
|
||
|
|
||
|
// Modulate texture2 with vertex color.
|
||
|
strcat( shader_buffer, "mul_x2 t2,t2,v0\n" );
|
||
|
}
|
||
|
|
||
|
// Then deal with the second pass blend.
|
||
|
switch( blend_modes[1] )
|
||
|
{
|
||
|
case vBLEND_MODE_ADD:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ADD_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUBTRACT:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,-r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUB_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,-c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r1.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,c1.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,r1.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,c1.a\n" );
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r0.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r0.a,r0.rgb,r1.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_GLOSS_MAP:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul v1.rgb,v1.rgb,t1.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Then deal with the third pass blend.
|
||
|
switch( blend_modes[2] )
|
||
|
{
|
||
|
case vBLEND_MODE_ADD:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t2.rgb,t2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ADD_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t2.rgb,c2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUBTRACT:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t2.rgb,-t2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUB_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t2.rgb,-c2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,t2.a,t2.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,c2.a,t2.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,t2.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,c2.a\n" );
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,t2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,c2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r1.a,t2.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r1.a,r0.rgb,t2.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_GLOSS_MAP:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul v1.rgb,v1.rgb,t2.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 4:
|
||
|
{
|
||
|
Dbg_Assert( ignore_bf <= 0x0F );
|
||
|
|
||
|
strcat( shader_buffer, "tex t2\n" );
|
||
|
strcat( shader_buffer, "tex t3\n" );
|
||
|
|
||
|
if( mcm )
|
||
|
{
|
||
|
// Modulate texture0 and texture 1 color with pass 0 and pass 1 material color, and place into t0.rgb and t1.rgb.
|
||
|
strcat( shader_buffer, "xmma t0.rgb,t1.rgb,discard.rgb,t0.rgb,c0.rgb,t1.rgb,c1.rgb\n" );
|
||
|
|
||
|
// Modulate result color with vertex color.
|
||
|
strcat( shader_buffer, "xmma_x4 r0.rgb,r1.rgb,discard.rgb,t0.rgb,v0.rgb,t1.rgb,v0.rgb\n" );
|
||
|
|
||
|
// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
|
||
|
if(( ignore_bf & 0x03 ) == 0x00 )
|
||
|
{
|
||
|
// Pass0 modulates with vertex alpha. Pass1 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,v0.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x03 ) == 0x01 )
|
||
|
{
|
||
|
// Pass0 modulates with constant alpha. Pass1 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,v0.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x03 ) == 0x02 )
|
||
|
{
|
||
|
// Pass0 modulates with vertex alpha. Pass1 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,c4.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x03 ) == 0x03 )
|
||
|
{
|
||
|
// Pass0 modulates with constant alpha. Pass1 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,c4.a\n" );
|
||
|
}
|
||
|
|
||
|
// Modulate texture2 and texture 3 color with pass 2 and pass 3 material color, and place into t0.rgb and t1.rgb.
|
||
|
strcat( shader_buffer, "xmma t2.rgb,t3.rgb,discard.rgb,t2.rgb,c2.rgb,t3.rgb,c3.rgb\n" );
|
||
|
|
||
|
// Modulate result color with vertex color.
|
||
|
strcat( shader_buffer, "xmma_x4 t2.rgb,t3.rgb,discard.rgb,t2.rgb,v0.rgb,t3.rgb,v0.rgb\n" );
|
||
|
|
||
|
// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
|
||
|
if(( ignore_bf & 0x0C ) == 0x00 )
|
||
|
{
|
||
|
// Pass2 modulates with vertex alpha. Pass3 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 t2.a,t3.a,discard.a,t2.a,v0.a,t3.a,v0.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x0C ) == 0x04 )
|
||
|
{
|
||
|
// Pass2 modulates with constant alpha. Pass3 modulates with vertex alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 t2.a,t3.a,discard.a,t2.a,c4.a,t3.a,v0.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x0C ) == 0x08 )
|
||
|
{
|
||
|
// Pass2 modulates with vertex alpha. Pass3 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 t2.a,t3.a,discard.a,t2.a,v0.a,t3.a,c4.a\n" );
|
||
|
}
|
||
|
else if(( ignore_bf & 0x0C ) == 0x0C )
|
||
|
{
|
||
|
// Pass2 modulates with constant alpha. Pass3 modulates with constant alpha.
|
||
|
strcat( shader_buffer, "+xmma_x2 t2.a,t3.a,discard.a,t2.a,c4.a,t3.a,c4.a\n" );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Modulate texture0 and texture1 with vertex color.
|
||
|
strcat( shader_buffer, "xmma_x2 r0,r1,discard,t0,v0,t1,v0\n" );
|
||
|
|
||
|
// Modulate texture2 and texture3 with vertex color.
|
||
|
strcat( shader_buffer, "xmma_x2 t2,t3,discard,t2,v0,t3,v0\n" );
|
||
|
}
|
||
|
|
||
|
// Then deal with the second pass blend.
|
||
|
switch( blend_modes[1] )
|
||
|
{
|
||
|
case vBLEND_MODE_ADD:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ADD_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUBTRACT:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,-r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUB_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r1.rgb,-c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r1.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,c1.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,r1.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,c1.a\n" );
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,r1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,c1.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r0.a,r1.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r0.a,r0.rgb,r1.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_GLOSS_MAP:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul v1.rgb,v1.rgb,t1.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Then deal with the third pass blend.
|
||
|
switch( blend_modes[2] )
|
||
|
{
|
||
|
case vBLEND_MODE_ADD:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t2.rgb,t2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ADD_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t2.rgb,c2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUBTRACT:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t2.rgb,-t2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUB_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t2.rgb,-c2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,t2.a,t2.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,c2.a,t2.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,t2.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,c2.a\n" );
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,t2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,c2.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r1.a,t2.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,r1.a,r0.rgb,t2.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_GLOSS_MAP:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul v1.rgb,v1.rgb,t2.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Then deal with the fourth pass blend.
|
||
|
switch( blend_modes[3] )
|
||
|
{
|
||
|
case vBLEND_MODE_ADD:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t3.rgb,t3.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ADD_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t3.rgb,c3.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUBTRACT:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t3.rgb,-t3.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUB_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,t3.rgb,-c3.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,t3.a,t3.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,c3.a,t3.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,t3.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul r0.rgb,r0.rgb,c3.a\n" );
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,t3.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN_FIXED:
|
||
|
{
|
||
|
strcat( shader_buffer, "mad r0.rgb,r0.rgb,c3.a,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,t2.a,t3.rgb,r0.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
|
||
|
{
|
||
|
strcat( shader_buffer, "lrp r0.rgb,t2.a,r0.rgb,t3.rgb\n" );
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_GLOSS_MAP:
|
||
|
{
|
||
|
strcat( shader_buffer, "mul v1.rgb,v1.rgb,t3.a\n" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Final combiner.
|
||
|
strcat( shader_buffer, "xfc prod, fog.rgb, sum, zero, 1 - fog.a, c4, r0.a\n" );
|
||
|
|
||
|
LPXGBUFFER pCompiledShader;
|
||
|
HRESULT hr = XGAssembleShader( "autogen.ps", // Source file name, used in error messages.
|
||
|
shader_buffer, // A pointer to the source data.
|
||
|
strlen( shader_buffer ), // The source data length.
|
||
|
SASM_SKIPPREPROCESSOR | SASM_SKIPVALIDATION, // SASM_xxx flags. See xgraphics.h for a complete list.
|
||
|
NULL, // If constants are declared in the shader, they are written here. Pass NULL if you don't care.
|
||
|
&pCompiledShader, // The shader microcode is written here. Pass NULL if you don't care.
|
||
|
NULL, // Errors are written here. Pass NULL if you don't care.
|
||
|
NULL, // A human-readable listing is written here. Pass NULL if you don't want it.
|
||
|
NULL, // Used by the preprocessor. Can be NULL if you don't use #include in your source file.
|
||
|
NULL, // Passed unmodified to the pResolver function.
|
||
|
NULL ); // Returns the type of shader that was assembled. Pass NULL if you don't care.
|
||
|
Dbg_Assert( hr == S_OK );
|
||
|
|
||
|
// Copy the microcode into our buffer.
|
||
|
Dbg_Assert( pixel_shader_buffer_offset + pCompiledShader->size < PIXEL_SHADER_BUFFER_SIZE );
|
||
|
CopyMemory( pixel_shader_buffer + pixel_shader_buffer_offset, pCompiledShader->pData, pCompiledShader->size );
|
||
|
|
||
|
// Generate a handle to this shader.
|
||
|
DWORD shader_handle;
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( pixel_shader_buffer + pixel_shader_buffer_offset ), &shader_handle );
|
||
|
|
||
|
// Update the buffer offset now we have copied a new shader into the buffer.
|
||
|
pixel_shader_buffer_offset += pCompiledShader->size;
|
||
|
|
||
|
// This was allocated during XGAssembleShader().
|
||
|
XGBuffer_Release( pCompiledShader );
|
||
|
|
||
|
// Place the shader handle in the table.
|
||
|
sPixelShaderTable.PutItem( code, (DWORD*)shader_handle );
|
||
|
|
||
|
// Return the handle.
|
||
|
return shader_handle;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void create_pixel_shaders( void )
|
||
|
{
|
||
|
static bool created_shaders = false;
|
||
|
if( !created_shaders )
|
||
|
{
|
||
|
created_shaders = true;
|
||
|
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader0PixelShader[0] ), &PixelShader0 );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader0IVAPixelShader[0] ), &PixelShader0IVA );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader1PixelShader[0] ), &PixelShader1 );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader2PixelShader[0] ), &PixelShader2 );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader3PixelShader[0] ), &PixelShader3 );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader4PixelShader[0] ), &PixelShader4 );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader5PixelShader[0] ), &PixelShader5 );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderBrightenPixelShader[0] ), &PixelShaderBrighten );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderBrightenIVAPixelShader[0] ), &PixelShaderBrightenIVA );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderFocusBlurPixelShader[0] ), &PixelShaderFocusBlur );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderFocusIntegratePixelShader[0] ), &PixelShaderFocusIntegrate );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderFocusLookupIntegratePixelShader[0] ), &PixelShaderFocusLookupIntegrate );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderNULLPixelShader[0] ), &PixelShaderNULL );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderPointSpritePixelShader[0] ), &PixelShaderPointSprite );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderBumpWaterPixelShader[0] ), &PixelShaderBumpWater );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader_ShadowBufferPixelShader[0] ), &PixelShader_ShadowBuffer );
|
||
|
D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwShadowBufferStaticGeomPSPixelShader[0] ), &ShadowBufferStaticGeomPS );
|
||
|
|
||
|
// Shouldn't be doing this here!
|
||
|
pTextureProjectionDetailsTable = new Lst::HashTable< sTextureProjectionDetails >( 8 );
|
||
|
|
||
|
# if 0
|
||
|
// Light glow test code.
|
||
|
sLightGlowDetails *p_details = new sLightGlowDetails;
|
||
|
p_details->m_pos.Set( 0.0f, 48.0f, 0.0f );
|
||
|
p_details->m_glow_radius = 24.0f;
|
||
|
p_details->m_current_radius = 0.0f;
|
||
|
p_details->m_test_radius = 2.0f;
|
||
|
p_details->m_radius_growth = 0.2f;
|
||
|
sLightGlowDetailsTable.PutItem( (uint32)p_details, p_details );
|
||
|
|
||
|
p_details = new sLightGlowDetails;
|
||
|
p_details->m_pos.Set( 60.0f, 48.0f, 0.0f );
|
||
|
p_details->m_glow_radius = 24.0f;
|
||
|
p_details->m_current_radius = 0.0f;
|
||
|
p_details->m_test_radius = 2.0f;
|
||
|
p_details->m_radius_growth = 0.2f;
|
||
|
sLightGlowDetailsTable.PutItem( (uint32)p_details, p_details );
|
||
|
|
||
|
p_details = new sLightGlowDetails;
|
||
|
p_details->m_pos.Set( -60.0f, 48.0f, 0.0f );
|
||
|
p_details->m_glow_radius = 24.0f;
|
||
|
p_details->m_current_radius = 0.0f;
|
||
|
p_details->m_test_radius = 2.0f;
|
||
|
p_details->m_radius_growth = 0.2f;
|
||
|
sLightGlowDetailsTable.PutItem( (uint32)p_details, p_details );
|
||
|
# endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void GetPixelShader( sMaterial *p_material, uint32 *p_pixel_shader_id )
|
||
|
{
|
||
|
if( p_material->m_passes == 1 )
|
||
|
{
|
||
|
// There are only 2 shader for single pass materials, depending on whether a texture is required.
|
||
|
if( p_material->mp_tex[0] == NULL )
|
||
|
{
|
||
|
*p_pixel_shader_id = PixelShader1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint32 ignore_bf = p_material->GetIgnoreVertexAlphaPasses();
|
||
|
|
||
|
if(( p_material->m_reg_alpha[0] & sMaterial::BLEND_MODE_MASK ) == vBLEND_MODE_BRIGHTEN )
|
||
|
{
|
||
|
// The single pass mode is brighten, which requires special handling.
|
||
|
if( ignore_bf == 0 )
|
||
|
*p_pixel_shader_id = PixelShaderBrighten;
|
||
|
else
|
||
|
*p_pixel_shader_id = PixelShaderBrightenIVA;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( ignore_bf == 0 )
|
||
|
*p_pixel_shader_id = PixelShader0;
|
||
|
else
|
||
|
*p_pixel_shader_id = PixelShader0IVA;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Get the pixel shader.
|
||
|
*p_pixel_shader_id = get_pixel_shader( p_material );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void set_texture( uint32 pass, IDirect3DTexture8 *p_texture, IDirect3DPalette8 *p_palette )
|
||
|
{
|
||
|
if((IDirect3DTexture8*)( EngineGlobals.p_texture[pass] ) != p_texture )
|
||
|
{
|
||
|
// Use SwitchTexture() whenever possible. Cannot switch from or to a NULL texture. Also cannot
|
||
|
// switch to a liner texture (in this case the calling code should perform a set_texture( NULL )
|
||
|
// call first, to force D3DDevice_SetTexture() to be called for the linear texture.
|
||
|
if(( p_texture != NULL ) && ( EngineGlobals.p_texture[pass] != NULL ))
|
||
|
{
|
||
|
EngineGlobals.p_Device->SwitchTexture( pass, (LPDIRECT3DBASETEXTURE8)( p_texture ));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
D3DDevice_SetTexture( pass, (LPDIRECT3DBASETEXTURE8)( p_texture ));
|
||
|
}
|
||
|
|
||
|
if( p_palette )
|
||
|
{
|
||
|
D3DDevice_SetPalette( pass, p_palette );
|
||
|
}
|
||
|
|
||
|
EngineGlobals.p_texture[pass] = p_texture;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void set_blend_mode( uint32 mode )
|
||
|
{
|
||
|
if( NxXbox::EngineGlobals.blend_mode_override )
|
||
|
{
|
||
|
mode = NxXbox::EngineGlobals.blend_mode_override;
|
||
|
}
|
||
|
|
||
|
// Only do something if the blend mode is changing.
|
||
|
if( mode != EngineGlobals.blend_mode_value )
|
||
|
{
|
||
|
// Low 24 bits contain the mode, high 8 bits contain the fixed alpha value.
|
||
|
if(( mode & 0x00FFFFFFUL ) != ( EngineGlobals.blend_mode_value & 0x00FFFFFFUL ))
|
||
|
{
|
||
|
// For additive and subtractive, we set the fog color to black.
|
||
|
if( EngineGlobals.fog_enabled )
|
||
|
{
|
||
|
if((( mode & 0x00FFFFFFUL ) >= vBLEND_MODE_ADD ) && (( mode & 0x00FFFFFFUL ) <= vBLEND_MODE_SUB_FIXED ))
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_FOGCOLOR, 0x00000000UL );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_FOGCOLOR, NxXbox::EngineGlobals.fog_color );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int blend_op, src_blend, dest_blend;
|
||
|
|
||
|
switch( mode & 0x00FFFFFFUL )
|
||
|
{
|
||
|
case vBLEND_MODE_DIFFUSE: // ( 0 - 0 ) * 0 + Src
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_ONE;
|
||
|
dest_blend = D3DBLEND_ZERO;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ADD: // ( Src - 0 ) * Src + Dst
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_SRCALPHA;
|
||
|
dest_blend = D3DBLEND_ONE;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ADD_FIXED: // ( Src - 0 ) * Fixed + Dst
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_CONSTANTALPHA;
|
||
|
dest_blend = D3DBLEND_ONE;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUBTRACT: // ( 0 - Src ) * Src + Dst
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_REVSUBTRACT;
|
||
|
src_blend = D3DBLEND_SRCALPHA;
|
||
|
dest_blend = D3DBLEND_ONE;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_SUB_FIXED: // ( 0 - Src ) * Fixed + Dst
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_REVSUBTRACT;
|
||
|
src_blend = D3DBLEND_CONSTANTALPHA;
|
||
|
dest_blend = D3DBLEND_ONE;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND: // ( Src - Dst ) * Src + Dst
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_SRCALPHA;
|
||
|
dest_blend = D3DBLEND_INVSRCALPHA;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_FIXED: // ( Src - Dst ) * Fixed + Dst
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_CONSTANTALPHA;
|
||
|
dest_blend = D3DBLEND_INVCONSTANTALPHA;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE: // ( Dst - 0 ) * Src + 0
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_ZERO;
|
||
|
dest_blend = D3DBLEND_SRCALPHA;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE_FIXED: // ( Dst - 0 ) * Fixed + 0
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_ZERO;
|
||
|
dest_blend = D3DBLEND_CONSTANTALPHA;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN: // ( Dst - 0 ) * Src + Dst
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_DESTCOLOR;
|
||
|
dest_blend = D3DBLEND_ONE;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BRIGHTEN_FIXED: // ( Dst - 0 ) * Fixed + Dst
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_DESTCOLOR;
|
||
|
dest_blend = D3DBLEND_CONSTANTALPHA;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_GLOSS_MAP:
|
||
|
{
|
||
|
// Treat as diffuse for now.
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_ONE;
|
||
|
dest_blend = D3DBLEND_ZERO;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_MODULATE_COLOR: // ( Dst - 0 ) * Src(col) + 0 - specially for the shadow.
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_ZERO;
|
||
|
dest_blend = D3DBLEND_SRCCOLOR;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_PREVIOUS_MASK:
|
||
|
{
|
||
|
// Meaningless unless destination alpha is enabled.
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_DESTALPHA;
|
||
|
dest_blend = D3DBLEND_INVDESTALPHA;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
|
||
|
{
|
||
|
// Meaningless unless destination alpha is enabled.
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_INVDESTALPHA;
|
||
|
dest_blend = D3DBLEND_DESTALPHA;
|
||
|
break;
|
||
|
}
|
||
|
case vBLEND_MODE_ONE_INV_SRC_ALPHA:
|
||
|
{
|
||
|
blend_op = D3DBLENDOP_ADD;
|
||
|
src_blend = D3DBLEND_ONE;
|
||
|
dest_blend = D3DBLEND_INVSRCALPHA;
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
Dbg_Assert( 0 );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now set the values if they have changed.
|
||
|
if( blend_op != EngineGlobals.blend_op )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_BLENDOP, blend_op );
|
||
|
EngineGlobals.blend_op = blend_op;
|
||
|
}
|
||
|
if( src_blend != EngineGlobals.src_blend )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_SRCBLEND, src_blend );
|
||
|
EngineGlobals.src_blend = src_blend;
|
||
|
}
|
||
|
if( dest_blend != EngineGlobals.dest_blend )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_DESTBLEND, dest_blend );
|
||
|
EngineGlobals.dest_blend = dest_blend;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Change the fixed alpha value if different.
|
||
|
if(( mode & 0xFF000000UL ) != ( EngineGlobals.blend_mode_value & 0xFF000000UL ))
|
||
|
{
|
||
|
uint32 fixed_alpha = mode & 0xFF000000UL;
|
||
|
fixed_alpha = fixed_alpha >= 0x80000000UL ? 0xFF000000UL : ( fixed_alpha << 1 );
|
||
|
D3DDevice_SetRenderState( D3DRS_BLENDCOLOR, fixed_alpha );
|
||
|
}
|
||
|
|
||
|
// Set the new blend mode value.
|
||
|
EngineGlobals.blend_mode_value = mode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void set_vertex_shader( DWORD shader_id )
|
||
|
{
|
||
|
if( EngineGlobals.vertex_shader_override == 0 )
|
||
|
{
|
||
|
if( EngineGlobals.vertex_shader_id != shader_id )
|
||
|
{
|
||
|
// Set vertex shader.
|
||
|
D3DDevice_SetVertexShader( shader_id );
|
||
|
EngineGlobals.vertex_shader_id = shader_id;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void set_pixel_shader( uint32 shader_id )
|
||
|
{
|
||
|
if( EngineGlobals.pixel_shader_override == 0 )
|
||
|
{
|
||
|
if( EngineGlobals.pixel_shader_id != shader_id )
|
||
|
{
|
||
|
// Set pixel shader.
|
||
|
D3DDevice_SetPixelShader( shader_id );
|
||
|
EngineGlobals.pixel_shader_id = shader_id;
|
||
|
|
||
|
// Changing pixel shader invalidates the constants, so we need to upload.
|
||
|
EngineGlobals.upload_pixel_shader_constants = true;
|
||
|
}
|
||
|
|
||
|
// Upload any pixel shader constants if required.
|
||
|
if( EngineGlobals.upload_pixel_shader_constants && ( shader_id > 0 ))
|
||
|
{
|
||
|
D3DDevice_SetPixelShaderConstant( 0, EngineGlobals.pixel_shader_constants, 5 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Want to clear this field even if the override is set, since otherwise it will persist and cause problems later.
|
||
|
EngineGlobals.upload_pixel_shader_constants = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void set_pixel_shader( uint32 shader_id, uint32 num_passes )
|
||
|
{
|
||
|
if( EngineGlobals.pixel_shader_override == 0 )
|
||
|
{
|
||
|
if( EngineGlobals.pixel_shader_id != shader_id )
|
||
|
{
|
||
|
// Set pixel shader.
|
||
|
D3DDevice_SetPixelShader( shader_id );
|
||
|
EngineGlobals.pixel_shader_id = shader_id;
|
||
|
|
||
|
// Changing pixel shader invalidates the constants, so we need to upload.
|
||
|
EngineGlobals.upload_pixel_shader_constants = true;
|
||
|
}
|
||
|
|
||
|
// Upload any pixel shader constants if required.
|
||
|
if( EngineGlobals.upload_pixel_shader_constants && ( shader_id > 0 ))
|
||
|
{
|
||
|
D3DDevice_SetPixelShaderConstant( 0, &EngineGlobals.pixel_shader_constants[0], num_passes );
|
||
|
D3DDevice_SetPixelShaderConstant( 4, &EngineGlobals.pixel_shader_constants[16], 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Want to clear this field even if the override is set, since otherwise it will persist and cause problems later.
|
||
|
EngineGlobals.upload_pixel_shader_constants = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void set_render_state( uint32 type, uint32 state )
|
||
|
{
|
||
|
switch( type )
|
||
|
{
|
||
|
case RS_ZBIAS:
|
||
|
{
|
||
|
if( state != EngineGlobals.z_bias )
|
||
|
{
|
||
|
EngineGlobals.z_bias = state;
|
||
|
D3DDevice_SetRenderState( D3DRS_ZBIAS, state );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_CULLMODE:
|
||
|
{
|
||
|
if( state != EngineGlobals.cull_mode )
|
||
|
{
|
||
|
EngineGlobals.cull_mode = state;
|
||
|
D3DDevice_SetRenderState( D3DRS_CULLMODE, state );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_ALPHABLENDENABLE:
|
||
|
{
|
||
|
if( state != EngineGlobals.alpha_blend_enable )
|
||
|
{
|
||
|
EngineGlobals.alpha_blend_enable = state;
|
||
|
D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE, ( state > 0 ) ? TRUE : FALSE );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_ALPHATESTENABLE:
|
||
|
{
|
||
|
if( state != EngineGlobals.alpha_test_enable )
|
||
|
{
|
||
|
EngineGlobals.alpha_test_enable = state;
|
||
|
D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE, ( state > 0 ) ? TRUE : FALSE );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_ZWRITEENABLE:
|
||
|
{
|
||
|
if( state > 0 )
|
||
|
{
|
||
|
if( EngineGlobals.z_write_enabled == FALSE )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
|
||
|
EngineGlobals.z_write_enabled = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( EngineGlobals.z_write_enabled == TRUE )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
|
||
|
EngineGlobals.z_write_enabled = FALSE;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_ZTESTENABLE:
|
||
|
{
|
||
|
if( state > 0 )
|
||
|
{
|
||
|
if( EngineGlobals.z_test_enabled == FALSE )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
|
||
|
EngineGlobals.z_test_enabled = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( EngineGlobals.z_test_enabled == TRUE )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_ZFUNC, D3DCMP_ALWAYS );
|
||
|
EngineGlobals.z_test_enabled = FALSE;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_ALPHACUTOFF:
|
||
|
{
|
||
|
// Convert from state (where 1 means "render all pixels with alpha 1 or higher") to the D3D.
|
||
|
// Also, if alpha cutoff is 1 or greater, enable alphakill, which can in some cases provide an earlier
|
||
|
// rejection of the pixel.
|
||
|
if( state != EngineGlobals.alpha_ref )
|
||
|
{
|
||
|
EngineGlobals.alpha_ref = state;
|
||
|
if( state > 0 )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_ALPHAREF, state );
|
||
|
|
||
|
// Enable alpha testing.
|
||
|
if( EngineGlobals.alpha_test_enable == 0 )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
|
||
|
EngineGlobals.alpha_test_enable = 1;
|
||
|
}
|
||
|
|
||
|
D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAKILL, D3DTALPHAKILL_ENABLE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Disable alpha testing.
|
||
|
if( EngineGlobals.alpha_test_enable > 0 )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
|
||
|
EngineGlobals.alpha_test_enable = 0;
|
||
|
}
|
||
|
D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAKILL, D3DTALPHAKILL_DISABLE );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_SPECULARENABLE:
|
||
|
{
|
||
|
if( state != EngineGlobals.specular_enabled )
|
||
|
{
|
||
|
EngineGlobals.specular_enabled = state;
|
||
|
|
||
|
if( state > 0 )
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_SPECULARENABLE, TRUE );
|
||
|
D3DDevice_SetRenderState( D3DRS_LOCALVIEWER, TRUE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
D3DDevice_SetRenderState( D3DRS_SPECULARENABLE, FALSE );
|
||
|
D3DDevice_SetRenderState( D3DRS_LOCALVIEWER, FALSE );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_FOGENABLE:
|
||
|
{
|
||
|
if( state != EngineGlobals.fog_enabled )
|
||
|
{
|
||
|
EngineGlobals.fog_enabled = state;
|
||
|
D3DDevice_SetRenderState( D3DRS_FOGENABLE, ( state > 0 ) ? TRUE : FALSE );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_UVADDRESSMODE0:
|
||
|
case RS_UVADDRESSMODE1:
|
||
|
case RS_UVADDRESSMODE2:
|
||
|
case RS_UVADDRESSMODE3:
|
||
|
{
|
||
|
int pass = type - RS_UVADDRESSMODE0;
|
||
|
if(( state & 0xFFFFUL ) != ( EngineGlobals.uv_addressing[pass] & 0xFFFFUL ))
|
||
|
{
|
||
|
switch( state & 0xFFFFUL )
|
||
|
{
|
||
|
case 0x0000U:
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP );
|
||
|
break;
|
||
|
}
|
||
|
case 0x0001U:
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP );
|
||
|
break;
|
||
|
}
|
||
|
case 0x0002U:
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSU, D3DTADDRESS_BORDER );
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
Dbg_Assert( 0 );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
EngineGlobals.uv_addressing[pass] = ( EngineGlobals.uv_addressing[pass] & 0xFFFF0000UL ) | ( state & 0xFFFFUL );
|
||
|
}
|
||
|
|
||
|
if(( state & 0xFFFF0000UL ) != ( EngineGlobals.uv_addressing[pass] & 0xFFFF0000UL ))
|
||
|
{
|
||
|
switch( state & 0xFFFF0000UL )
|
||
|
{
|
||
|
case 0x00000000UL:
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP );
|
||
|
break;
|
||
|
}
|
||
|
case 0x00010000UL:
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP );
|
||
|
break;
|
||
|
}
|
||
|
case 0x00020000UL:
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSV, D3DTADDRESS_BORDER );
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
Dbg_Assert( 0 );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
EngineGlobals.uv_addressing[pass] = ( EngineGlobals.uv_addressing[pass] & 0x0000FFFFUL ) | ( state & 0xFFFF0000UL );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_MIPLODBIASPASS0:
|
||
|
case RS_MIPLODBIASPASS1:
|
||
|
case RS_MIPLODBIASPASS2:
|
||
|
case RS_MIPLODBIASPASS3:
|
||
|
{
|
||
|
int pass = type - RS_MIPLODBIASPASS0;
|
||
|
if( state != EngineGlobals.mip_map_lod_bias[pass] )
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_MIPMAPLODBIAS, state );
|
||
|
EngineGlobals.mip_map_lod_bias[pass] = state;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case RS_MINMAGFILTER0:
|
||
|
case RS_MINMAGFILTER1:
|
||
|
case RS_MINMAGFILTER2:
|
||
|
case RS_MINMAGFILTER3:
|
||
|
{
|
||
|
int pass = type - RS_MINMAGFILTER0;
|
||
|
|
||
|
// Magnification filter.
|
||
|
state = state & 0xFFFF0000UL;
|
||
|
if( state != EngineGlobals.min_mag_filter[pass] )
|
||
|
{
|
||
|
if( state == 0x00000000UL )
|
||
|
{
|
||
|
// Point.
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_MAGFILTER, D3DTEXF_POINT );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Linear.
|
||
|
D3DDevice_SetTextureStageState( pass, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
|
||
|
}
|
||
|
EngineGlobals.min_mag_filter[pass] = state;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void create_texture_projection_details( sTexture *p_texture, Nx::CXboxModel *p_model, sScene *p_scene )
|
||
|
{
|
||
|
sTextureProjectionDetails *p_details = new sTextureProjectionDetails;
|
||
|
|
||
|
p_details->p_model = p_model;
|
||
|
p_details->p_scene = p_scene;
|
||
|
p_details->p_texture = p_texture;
|
||
|
|
||
|
XGMatrixIdentity( &p_details->view_matrix );
|
||
|
XGMatrixIdentity( &p_details->projection_matrix );
|
||
|
|
||
|
pTextureProjectionDetailsTable->PutItem((uint32)p_texture, p_details );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void destroy_texture_projection_details( sTexture *p_texture )
|
||
|
{
|
||
|
sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->GetItem((uint32)p_texture );
|
||
|
if( p_details )
|
||
|
{
|
||
|
pTextureProjectionDetailsTable->FlushItem((uint32)p_texture );
|
||
|
delete p_details;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void set_texture_projection_camera( sTexture *p_texture, XGVECTOR3 *p_pos, XGVECTOR3 *p_at )
|
||
|
{
|
||
|
sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->GetItem((uint32)p_texture );
|
||
|
if( p_details )
|
||
|
{
|
||
|
// Check for 'straight down' vector.
|
||
|
if(( p_pos->x == p_at->x ) && ( p_pos->z == p_at->z ))
|
||
|
{
|
||
|
XGMatrixLookAtRH( &p_details->view_matrix, p_pos, p_at, &XGVECTOR3( 0.0f, 0.0f, 1.0f ));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
XGMatrixLookAtRH( &p_details->view_matrix, p_pos, p_at, &XGVECTOR3( 0.0f, 1.0f, 0.0f ));
|
||
|
}
|
||
|
XGMatrixOrthoRH( &p_details->projection_matrix, 96.0f, 96.0f, 1.0f, 128.0f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
// MSM PERFCHANGE - added scale.
|
||
|
void set_camera( Mth::Matrix *p_matrix, Mth::Vector *p_position, float screen_angle, float aspect_ratio, bool render_at_infinity )
|
||
|
{
|
||
|
EngineGlobals.cam_position.x = p_position->GetX();
|
||
|
EngineGlobals.cam_position.y = p_position->GetY();
|
||
|
EngineGlobals.cam_position.z = p_position->GetZ();
|
||
|
|
||
|
EngineGlobals.cam_at.x = p_matrix->GetAt().GetX();
|
||
|
EngineGlobals.cam_at.y = p_matrix->GetAt().GetY();
|
||
|
EngineGlobals.cam_at.z = p_matrix->GetAt().GetZ();
|
||
|
|
||
|
EngineGlobals.cam_up.x = p_matrix->GetUp().GetX();
|
||
|
EngineGlobals.cam_up.y = p_matrix->GetUp().GetY();
|
||
|
EngineGlobals.cam_up.z = p_matrix->GetUp().GetZ();
|
||
|
|
||
|
XGMatrixIdentity( &EngineGlobals.world_matrix );
|
||
|
|
||
|
// XGMatrixLookAtRH() takes an 'at' position rather than a direction, so we need it relative to the camera position.
|
||
|
XGVECTOR3 at;
|
||
|
at.x = EngineGlobals.cam_position.x - EngineGlobals.cam_at.x;
|
||
|
at.y = EngineGlobals.cam_position.y - EngineGlobals.cam_at.y;
|
||
|
at.z = EngineGlobals.cam_position.z - EngineGlobals.cam_at.z;
|
||
|
XGMatrixLookAtRH( &EngineGlobals.view_matrix, &EngineGlobals.cam_position, &at, &EngineGlobals.cam_up );
|
||
|
|
||
|
EngineGlobals.near_plane = 2.0f;
|
||
|
EngineGlobals.far_plane = 32000.0f;
|
||
|
EngineGlobals.screen_angle = screen_angle;
|
||
|
|
||
|
// Figure width and height of viewport at near clip plane.
|
||
|
float half_screen_angle_in_radians = Mth::DegToRad( screen_angle * 0.5f );
|
||
|
float width = EngineGlobals.near_plane * 2.0f * tanf( half_screen_angle_in_radians );
|
||
|
|
||
|
if( EngineGlobals.backbuffer_width == 640.0f )
|
||
|
{
|
||
|
// We need to adjust the aspect ratio for the Xbox, since it is now rendering with D3DPRESENTFLAG_10X11PIXELASPECTRATIO
|
||
|
// set. This changes the regular aspect ratio from 4:3 to 4:3.3, so adjust the value here.
|
||
|
aspect_ratio = aspect_ratio * (( 4.0f / 3.3f ) / ( 4.0f / 3.0f ));
|
||
|
}
|
||
|
|
||
|
float height = width / aspect_ratio;
|
||
|
XGMatrixPerspectiveRH( &EngineGlobals.projection_matrix, width, height, EngineGlobals.near_plane, EngineGlobals.far_plane );
|
||
|
|
||
|
NxXbox::EngineGlobals.near_plane_width = width;
|
||
|
NxXbox::EngineGlobals.near_plane_height = height;
|
||
|
|
||
|
if( render_at_infinity )
|
||
|
{
|
||
|
// Rendering the sky, so set the projection transform up to calculate a constant z value of 1.0.
|
||
|
// W value must remain correct however.
|
||
|
EngineGlobals.projection_matrix.m[2][2] = -0.999999f; // Setting this to -1.0f causes D3D to complain about WNear calculation.
|
||
|
EngineGlobals.projection_matrix.m[3][2] = 0.0f;
|
||
|
}
|
||
|
|
||
|
D3DDevice_SetTransform( D3DTS_WORLD, &EngineGlobals.world_matrix );
|
||
|
D3DDevice_SetTransform( D3DTS_VIEW, &EngineGlobals.view_matrix );
|
||
|
D3DDevice_SetTransform( D3DTS_PROJECTION, &EngineGlobals.projection_matrix );
|
||
|
|
||
|
// Set up view frustum values for bounding sphere culling.
|
||
|
EngineGlobals.ViewFrustumTX = tanf( Mth::DegToRad( screen_angle * 0.5f ));
|
||
|
EngineGlobals.ViewFrustumTY = -( EngineGlobals.ViewFrustumTX / aspect_ratio );
|
||
|
EngineGlobals.ViewFrustumSX = 1.0f / sqrtf( 1.0f + 1.0f / ( EngineGlobals.ViewFrustumTX * EngineGlobals.ViewFrustumTX ));
|
||
|
EngineGlobals.ViewFrustumSY = 1.0f / sqrtf( 1.0f + 1.0f / ( EngineGlobals.ViewFrustumTY * EngineGlobals.ViewFrustumTY ));
|
||
|
EngineGlobals.ViewFrustumCX = 1.0f / sqrtf( 1.0f + EngineGlobals.ViewFrustumTX * EngineGlobals.ViewFrustumTX );
|
||
|
EngineGlobals.ViewFrustumCY = 1.0f / sqrtf( 1.0f + EngineGlobals.ViewFrustumTY * EngineGlobals.ViewFrustumTY );
|
||
|
|
||
|
// Set up matrix for offset bump mapping (the matrix that will be used to set the D3DTSS_BUMPENVMATnn texture states).
|
||
|
float rotate_angle = atan2f( -EngineGlobals.cam_at.z, -EngineGlobals.cam_at.x );
|
||
|
XGMatrixRotationY( &EngineGlobals.bump_env_matrix, rotate_angle - D3DX_PI / 2 );
|
||
|
|
||
|
// Calculate vectors for billboard rendering.
|
||
|
BillboardManager.SetCameraMatrix();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* Quick determination of if something is visible or not, uses */
|
||
|
/* the previously calculated s and c vectors and the */
|
||
|
/* WorldToCamera transform (note, no attempt is made to ensure */
|
||
|
/* this is the same camera that the object will eventually be */
|
||
|
/* rendered with. */
|
||
|
/******************************************************************/
|
||
|
bool IsVisible( Mth::Vector ¢er, float radius )
|
||
|
{
|
||
|
XGVECTOR4 test_out;
|
||
|
|
||
|
XGVec3Transform( &test_out, (XGVECTOR3*)¢er[X], (XGMATRIX*)&EngineGlobals.view_matrix );
|
||
|
|
||
|
if( -test_out.z + radius < EngineGlobals.near_plane )
|
||
|
return false;
|
||
|
|
||
|
float sx_z = EngineGlobals.ViewFrustumSX * test_out.z;
|
||
|
float cx_x = EngineGlobals.ViewFrustumCX * test_out.x;
|
||
|
if(( radius < sx_z - cx_x ) || ( radius < sx_z + cx_x ))
|
||
|
return false;
|
||
|
|
||
|
float sy_z = EngineGlobals.ViewFrustumSY * test_out.z;
|
||
|
float cy_y = EngineGlobals.ViewFrustumCY * test_out.y;
|
||
|
if(( radius < sy_z + cy_y ) || ( radius < sy_z - cy_y ))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void set_frustum_bbox_transform( Mth::Matrix *p_transform )
|
||
|
{
|
||
|
if( p_transform == NULL )
|
||
|
{
|
||
|
p_bbox_transform = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p_bbox_transform = &bbox_transform;
|
||
|
|
||
|
bbox_transform.m[0][0] = ( *p_transform ).GetRight().GetX();
|
||
|
bbox_transform.m[0][1] = ( *p_transform ).GetRight().GetY();
|
||
|
bbox_transform.m[0][2] = ( *p_transform ).GetRight().GetZ();
|
||
|
bbox_transform.m[0][3] = 0.0f;
|
||
|
|
||
|
bbox_transform.m[1][0] = ( *p_transform ).GetUp().GetX();
|
||
|
bbox_transform.m[1][1] = ( *p_transform ).GetUp().GetY();
|
||
|
bbox_transform.m[1][2] = ( *p_transform ).GetUp().GetZ();
|
||
|
bbox_transform.m[1][3] = 0.0f;
|
||
|
|
||
|
bbox_transform.m[2][0] = ( *p_transform ).GetAt().GetX();
|
||
|
bbox_transform.m[2][1] = ( *p_transform ).GetAt().GetY();
|
||
|
bbox_transform.m[2][2] = ( *p_transform ).GetAt().GetZ();
|
||
|
bbox_transform.m[2][3] = 0.0f;
|
||
|
|
||
|
bbox_transform.m[3][0] = p_transform->GetPos().GetX();
|
||
|
bbox_transform.m[3][1] = p_transform->GetPos().GetY();
|
||
|
bbox_transform.m[3][2] = p_transform->GetPos().GetZ();
|
||
|
bbox_transform.m[3][3] = 1.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
float boundingSphereNearestZ = 0.0f;
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
float get_bounding_sphere_nearest_z( void )
|
||
|
{
|
||
|
return boundingSphereNearestZ;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* Checks a bounding sphere against the current view frustum */
|
||
|
/* (ignoring far clipping). Returns true if any part is visible. */
|
||
|
/* Timings suggest this method runs on average around ~0.25us, */
|
||
|
/* faster than test code doing world space culling against a set */
|
||
|
/* of plane equations representing the view frustum in world */
|
||
|
/* space. */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
bool frustum_check_sphere( D3DXVECTOR3 *p_center, float radius )
|
||
|
{
|
||
|
XGVECTOR4 test_out;
|
||
|
|
||
|
// Build the composite transform if required.
|
||
|
if( p_bbox_transform )
|
||
|
{
|
||
|
// Object to world.
|
||
|
test_out.x = p_center->x + p_bbox_transform->_41;
|
||
|
test_out.y = p_center->y + p_bbox_transform->_42;
|
||
|
test_out.z = p_center->z + p_bbox_transform->_43;
|
||
|
// XGVec3Transform( &test_out, (XGVECTOR3*)p_center, p_bbox_transform );
|
||
|
|
||
|
// World to view.
|
||
|
XGVec3Transform( &test_out, (XGVECTOR3*)&test_out, (XGMATRIX*)&EngineGlobals.view_matrix );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// World to view.
|
||
|
XGVec3Transform( &test_out, (XGVECTOR3*)p_center, (XGMATRIX*)&EngineGlobals.view_matrix );
|
||
|
}
|
||
|
|
||
|
boundingSphereNearestZ = -test_out.z - radius;
|
||
|
|
||
|
if( -test_out.z + radius < EngineGlobals.near_plane )
|
||
|
return false;
|
||
|
|
||
|
float sx_z = EngineGlobals.ViewFrustumSX * test_out.z;
|
||
|
float cx_x = EngineGlobals.ViewFrustumCX * test_out.x;
|
||
|
if(( radius < sx_z - cx_x ) || ( radius < sx_z + cx_x ))
|
||
|
return false;
|
||
|
|
||
|
float sy_z = EngineGlobals.ViewFrustumSY * test_out.z;
|
||
|
float cy_y = EngineGlobals.ViewFrustumCY * test_out.y;
|
||
|
if(( radius < sy_z + cy_y ) || ( radius < sy_z - cy_y ))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* Checks a bounding box against the current view frustum */
|
||
|
/* (ignoring far clipping). Returns true if any part is visible. */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
bool frustum_check_box( Mth::CBBox *p_bbox )
|
||
|
{
|
||
|
XGVECTOR3 test_in, test_out;
|
||
|
XGVECTOR4 test_mid;
|
||
|
|
||
|
uint32 cumulative_projection_space_outcode = 0xFF;
|
||
|
float min_x = p_bbox->GetMin().GetX();
|
||
|
float min_y = p_bbox->GetMin().GetY();
|
||
|
float min_z = p_bbox->GetMin().GetZ();
|
||
|
float max_x = p_bbox->GetMax().GetX();
|
||
|
float max_y = p_bbox->GetMax().GetY();
|
||
|
float max_z = p_bbox->GetMax().GetZ();
|
||
|
|
||
|
for( uint32 v = 0; v < 8; ++v )
|
||
|
{
|
||
|
uint32 projection_space_outcode = 0;
|
||
|
|
||
|
test_in.x = ( v & 0x04 ) ? max_x : min_x;
|
||
|
test_in.y = ( v & 0x02 ) ? max_y : min_y;
|
||
|
test_in.z = ( v & 0x01 ) ? max_z : min_z;
|
||
|
|
||
|
if( p_bbox_transform )
|
||
|
{
|
||
|
XGVec3Transform( &test_mid, &test_in, p_bbox_transform );
|
||
|
test_in.x = test_mid.x;
|
||
|
test_in.y = test_mid.y;
|
||
|
test_in.z = test_mid.z;
|
||
|
}
|
||
|
|
||
|
XGVec3Transform( &test_mid, &test_in, &EngineGlobals.view_matrix );
|
||
|
test_in.x = test_mid.x;
|
||
|
test_in.y = test_mid.y;
|
||
|
test_in.z = test_mid.z;
|
||
|
|
||
|
// Do z-checking here.
|
||
|
if( -test_mid.z < EngineGlobals.near_plane )
|
||
|
{
|
||
|
// Behind the camera near plane.
|
||
|
projection_space_outcode |= 0x10;
|
||
|
}
|
||
|
else if( -test_mid.z > EngineGlobals.far_plane )
|
||
|
{
|
||
|
// Beyond the camera far plane.
|
||
|
projection_space_outcode |= 0x20;
|
||
|
}
|
||
|
|
||
|
// At this point it's important to check to see whether the point is in postive or negative z-space, since
|
||
|
// after the projection transform, both very large camera space z values and camera space z values where z < 0
|
||
|
// will give results with z > 1. (Camera space values in the range [0,near] give negative projection space z values).
|
||
|
XGVec3TransformCoord( &test_out, &test_in, &EngineGlobals.projection_matrix );
|
||
|
|
||
|
if(( -test_mid.z < 0.0f ) && ( !EngineGlobals.is_orthographic ))
|
||
|
{
|
||
|
test_out.x = -test_out.x;
|
||
|
test_out.y = -test_out.y;
|
||
|
}
|
||
|
|
||
|
if( test_out.x > 1.0f )
|
||
|
projection_space_outcode |= 0x01;
|
||
|
else if( test_out.x < -1.0f )
|
||
|
projection_space_outcode |= 0x02;
|
||
|
|
||
|
if( test_out.y > 1.0f )
|
||
|
projection_space_outcode |= 0x04;
|
||
|
else if( test_out.y < -1.0f )
|
||
|
projection_space_outcode |= 0x08;
|
||
|
|
||
|
cumulative_projection_space_outcode &= projection_space_outcode;
|
||
|
|
||
|
if( cumulative_projection_space_outcode == 0 )
|
||
|
{
|
||
|
// Early out.
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
struct sSortedMeshEntry
|
||
|
{
|
||
|
sMesh *p_mesh;
|
||
|
float sort;
|
||
|
sSortedMeshEntry *pNext;
|
||
|
};
|
||
|
|
||
|
|
||
|
static sSortedMeshEntry sortedMeshArray[1000];
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void calculate_tex_proj_matrix( XGMATRIX *p_tex_view_matrix, XGMATRIX *p_tex_proj_matrix, XGMATRIX *p_tex_transform_matrix, XGMATRIX *p_world_matrix )
|
||
|
{
|
||
|
// Get the current view matrix.
|
||
|
XGMATRIX matView, matInvView;
|
||
|
D3DDevice_GetTransform( D3DTS_VIEW, (XGMATRIX*)&matView );
|
||
|
XGMatrixInverse( &matInvView, NULL, &matView );
|
||
|
|
||
|
XGMATRIX matBiasScale;
|
||
|
XGMatrixIdentity( &matBiasScale );
|
||
|
|
||
|
static float x0 = 256.0f;
|
||
|
static float y0 = 256.0f;
|
||
|
|
||
|
matBiasScale._11 = x0 * 0.5f;
|
||
|
matBiasScale._22 = y0 * -0.5f;
|
||
|
matBiasScale._33 = D3DZ_MAX_D24S8;
|
||
|
|
||
|
static float x1 = 256.0f;
|
||
|
static float y1 = 256.0f;
|
||
|
|
||
|
matBiasScale._41 = x1 * 0.5f + 0.5f;
|
||
|
matBiasScale._42 = y1 * 0.5f + 0.5f;
|
||
|
|
||
|
XGMATRIX m_matTexProj;
|
||
|
|
||
|
// Don't bother with inverse view transform for Shadow Buffer, since we are picking up world-space coordinates directly.
|
||
|
if( p_world_matrix )
|
||
|
{
|
||
|
m_matTexProj = *p_world_matrix; // Transform to world space.
|
||
|
XGMatrixMultiply( &m_matTexProj, &m_matTexProj, p_tex_view_matrix ); // Transform to projection camera space.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_matTexProj = *p_tex_view_matrix; // Transform to projection camera space.
|
||
|
}
|
||
|
XGMatrixMultiply( &m_matTexProj, &m_matTexProj, p_tex_proj_matrix ); // Situate verts relative to projector's view
|
||
|
XGMatrixMultiply( p_tex_transform_matrix, &m_matTexProj, &matBiasScale ); // Scale and bias to map the near clipping plane to texcoords
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void render_shadow_targets( void )
|
||
|
{
|
||
|
XGMATRIX stored_view_matrix = EngineGlobals.view_matrix;
|
||
|
XGMATRIX stored_projection_matrix = EngineGlobals.projection_matrix;
|
||
|
uint32 stored_fog_state = EngineGlobals.fog_enabled;
|
||
|
DWORD multisample_mode;
|
||
|
|
||
|
// Get multisample mode.
|
||
|
D3DDevice_GetRenderState( D3DRS_MULTISAMPLEMODE, &multisample_mode );
|
||
|
|
||
|
// Disable fogging.
|
||
|
set_render_state( RS_FOGENABLE, 0 );
|
||
|
|
||
|
// Goes through the list of render target textures, rendering to each one in turn.
|
||
|
pTextureProjectionDetailsTable->IterateStart();
|
||
|
sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->IterateNext();
|
||
|
|
||
|
D3DSurface fake_target;
|
||
|
|
||
|
while( p_details )
|
||
|
{
|
||
|
if( p_details->p_model )
|
||
|
{
|
||
|
// Setup dummy color buffer (bad things will happen if you write to it).
|
||
|
// The XGSetSurfaceHeader() function is slow, these values are now set explicitly from the observed values
|
||
|
// set by the function.
|
||
|
// ZeroMemory( &fake_target, sizeof( fake_target ));
|
||
|
// XGSetSurfaceHeader( 256, 256, D3DFMT_LIN_R5G6B5, &fake_target, 0, 0 );
|
||
|
fake_target.Common = 0x00050001UL;
|
||
|
fake_target.Data = 0x00000000UL;
|
||
|
fake_target.Lock = 0x00000000UL;
|
||
|
fake_target.Format = 0x00011129UL;
|
||
|
fake_target.Size = 0x070FF0FFUL;
|
||
|
fake_target.Parent = 0x00000000UL;
|
||
|
|
||
|
// Set the new render target.
|
||
|
LPDIRECT3DSURFACE8 pSurface;
|
||
|
|
||
|
// This call will increase the reference count of the IDirect3DTexture8 object.
|
||
|
p_details->p_texture->pD3DTexture->GetSurfaceLevel( 0, &pSurface );
|
||
|
|
||
|
// This call will increase the reference count of the IDirect3DSurface8 object.
|
||
|
D3DDevice_SetRenderTarget( &fake_target, pSurface );
|
||
|
|
||
|
// Clear the target.
|
||
|
D3DDevice_Clear( 0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0 );
|
||
|
|
||
|
// Disable color writes.
|
||
|
D3DDevice_SetRenderState( D3DRS_COLORWRITEENABLE, 0 );
|
||
|
|
||
|
// Turn on z-offset.
|
||
|
D3DDevice_SetRenderState( D3DRS_SOLIDOFFSETENABLE, TRUE );
|
||
|
D3DDevice_SetRenderState( D3DRS_POLYGONOFFSETZOFFSET, FtoDW( 4.0f ));
|
||
|
D3DDevice_SetRenderState( D3DRS_POLYGONOFFSETZSLOPESCALE, FtoDW( 2.0f ));
|
||
|
|
||
|
// Performance optimiser suggested change.
|
||
|
// Priority 2: Set D3DRS_MULTISAMPLEMODE to D3DMULTISAMPLEMODE_4X for faster fill rate with no quality loss when depth only rendering.
|
||
|
D3DDevice_SetRenderState( D3DRS_MULTISAMPLEMODE, D3DMULTISAMPLEMODE_4X );
|
||
|
|
||
|
// Set the view and projection transforms.
|
||
|
EngineGlobals.view_matrix = p_details->view_matrix;
|
||
|
EngineGlobals.projection_matrix = p_details->projection_matrix;
|
||
|
EngineGlobals.is_orthographic = true;
|
||
|
|
||
|
// Render all instances for the CGeom's contained in this model.
|
||
|
int num_geoms = p_details->p_model->GetNumGeoms();
|
||
|
for( int i = 0; i < num_geoms; ++i )
|
||
|
{
|
||
|
Nx::CXboxGeom *p_xbox_geom = static_cast<Nx::CXboxGeom*>( p_details->p_model->GetGeomByIndex( i ));
|
||
|
CInstance *p_instance = p_xbox_geom->GetInstance();
|
||
|
|
||
|
if( p_instance->GetActive())
|
||
|
{
|
||
|
// Flag the scene as having the shadow version rendered.
|
||
|
p_instance->GetScene()->m_flags |= SCENE_FLAG_RENDERING_SHADOW;
|
||
|
|
||
|
// Render the model.
|
||
|
render_instance( p_instance, vRENDER_NO_CULLING );
|
||
|
|
||
|
// Clear the flag the scene as having the shadow version rendered.
|
||
|
p_instance->GetScene()->m_flags &= ~SCENE_FLAG_RENDERING_SHADOW;
|
||
|
|
||
|
// Flag the scene as self shadowing.
|
||
|
p_instance->GetScene()->m_flags |= SCENE_FLAG_SELF_SHADOWS;
|
||
|
}
|
||
|
}
|
||
|
pSurface->Release();
|
||
|
}
|
||
|
p_details = pTextureProjectionDetailsTable->IterateNext();
|
||
|
}
|
||
|
|
||
|
// Restore important states.
|
||
|
D3DDevice_SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALL );
|
||
|
D3DDevice_SetRenderState( D3DRS_SOLIDOFFSETENABLE, FALSE );
|
||
|
D3DDevice_SetRenderState( D3DRS_MULTISAMPLEMODE, multisample_mode );
|
||
|
set_render_state( RS_FOGENABLE, stored_fog_state );
|
||
|
|
||
|
// Pixel shader override no longer required.
|
||
|
EngineGlobals.pixel_shader_override = 0;
|
||
|
|
||
|
// Restore the view and projection transforms.
|
||
|
EngineGlobals.view_matrix = stored_view_matrix;
|
||
|
EngineGlobals.projection_matrix = stored_projection_matrix;
|
||
|
EngineGlobals.is_orthographic = false;
|
||
|
|
||
|
// It's important to set the internal reference count of the dummy color surface here, otherwise
|
||
|
// the debug version of D3D will complain when it attempts to reduce the internal ref count during
|
||
|
// the subsequent SetRenderTarget() call.
|
||
|
fake_target.Common = 0x000D0001UL;
|
||
|
|
||
|
// Restore the default render target.
|
||
|
D3DDevice_SetRenderTarget( EngineGlobals.p_RenderSurface, EngineGlobals.p_ZStencilSurface );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void render_shadow_meshes( sScene *p_scene, sMesh **p_mesh_indices, int num_meshes )
|
||
|
{
|
||
|
// No anisotropic filtering for the base texture.
|
||
|
DWORD stage_zero_minfilter;
|
||
|
D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &stage_zero_minfilter );
|
||
|
D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
|
||
|
|
||
|
// Disable fogging.
|
||
|
uint32 stored_fog_state = EngineGlobals.fog_enabled;
|
||
|
set_render_state( RS_FOGENABLE, 0 );
|
||
|
|
||
|
// Scan through each entry in the TextureProjectionDetails table, and see whether it relates to this scene.
|
||
|
pTextureProjectionDetailsTable->IterateStart();
|
||
|
sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->IterateNext();
|
||
|
while( p_details )
|
||
|
{
|
||
|
XGMATRIX stored_view_matrix = EngineGlobals.view_matrix;
|
||
|
XGMATRIX stored_projection_matrix = EngineGlobals.projection_matrix;
|
||
|
|
||
|
// Calculate the projection matrix that will project world coordinates into our shadow buffer.
|
||
|
calculate_tex_proj_matrix( &p_details->view_matrix, &p_details->projection_matrix, &p_details->texture_projection_matrix );
|
||
|
|
||
|
// Set the vertex shader.
|
||
|
set_vertex_shader( ShadowBufferStaticGeomVS );
|
||
|
|
||
|
// Set the pixel shader that just does straight texturing.
|
||
|
set_pixel_shader( ShadowBufferStaticGeomPS );
|
||
|
|
||
|
set_render_state( RS_ALPHACUTOFF, 1 );
|
||
|
|
||
|
// Set the other textures to NULL.
|
||
|
set_texture( 0, NULL );
|
||
|
set_texture( 2, NULL );
|
||
|
set_texture( 3, NULL );
|
||
|
|
||
|
// Need to set this texture NULL first, to flush the texture state, since the incoming texture is linear.
|
||
|
set_texture( 1, NULL );
|
||
|
|
||
|
// Set the projected texture (as the second-pass texture).
|
||
|
if( p_details->p_texture->pD3DSurface )
|
||
|
set_texture( 1, (IDirect3DTexture8*)( p_details->p_texture->pD3DSurface ));
|
||
|
else
|
||
|
set_texture( 1, p_details->p_texture->pD3DTexture );
|
||
|
|
||
|
// Set shadowbuffer texture details.
|
||
|
set_render_state( RS_UVADDRESSMODE1, 0x00020002UL ); // Set (border,border) addressing.
|
||
|
D3DDevice_SetTextureStageState( 1, D3DTSS_BORDERCOLOR, 0xffffffff );
|
||
|
D3DDevice_SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
|
||
|
D3DDevice_SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
|
||
|
|
||
|
// Set shadowbuffer state.
|
||
|
D3DDevice_SetRenderState( D3DRS_SHADOWFUNC, D3DCMP_GREATEREQUAL );
|
||
|
|
||
|
// Upload constants to the vertex shader for composite world->view->projection transform (c0 - c3) and
|
||
|
// world->texture transform (c4 - c7).
|
||
|
XGMATRIX dest_matrix;
|
||
|
XGMATRIX temp_matrix;
|
||
|
XGMATRIX projMatrix;
|
||
|
XGMATRIX viewMatrix;
|
||
|
XGMATRIX worldMatrix;
|
||
|
XGMATRIX texProjMatrix;
|
||
|
|
||
|
// Texture projection matrix.
|
||
|
XGMatrixTranspose( &texProjMatrix, &p_details->texture_projection_matrix );
|
||
|
|
||
|
// Projection matrix.
|
||
|
XGMatrixTranspose( &projMatrix, &EngineGlobals.projection_matrix );
|
||
|
|
||
|
// View matrix.
|
||
|
XGMatrixTranspose( &viewMatrix, &EngineGlobals.view_matrix );
|
||
|
viewMatrix.m[3][0] = 0.0f;
|
||
|
viewMatrix.m[3][1] = 0.0f;
|
||
|
viewMatrix.m[3][2] = 0.0f;
|
||
|
viewMatrix.m[3][3] = 1.0f;
|
||
|
|
||
|
// World space transformation matrix.
|
||
|
XGMatrixIdentity( &worldMatrix );
|
||
|
|
||
|
// Calculate composite world->view->projection matrix.
|
||
|
XGMatrixMultiply( &temp_matrix, &viewMatrix, &worldMatrix );
|
||
|
XGMatrixMultiply( &dest_matrix, &projMatrix, &temp_matrix );
|
||
|
|
||
|
// Load up the combined world, camera & projection matrix, and the tetxure transform matrix.
|
||
|
D3DDevice_SetVertexShaderConstantFast( 0, (void*)&dest_matrix, 4 );
|
||
|
D3DDevice_SetVertexShaderConstantFast( 4, (void*)&texProjMatrix, 4 );
|
||
|
|
||
|
// Turn on z-offset.
|
||
|
D3DDevice_SetRenderState( D3DRS_SOLIDOFFSETENABLE, TRUE );
|
||
|
D3DDevice_SetRenderState( D3DRS_POLYGONOFFSETZOFFSET, FtoDW( -4.0f ));
|
||
|
D3DDevice_SetRenderState( D3DRS_POLYGONOFFSETZSLOPESCALE, FtoDW( -2.0f ));
|
||
|
|
||
|
// Set the blend mode to modulate, using the texture color.
|
||
|
set_blend_mode( vBLEND_MODE_MODULATE_COLOR );
|
||
|
|
||
|
// Set up the correct view and projection matrix for frustum culling.
|
||
|
EngineGlobals.view_matrix = p_details->view_matrix;
|
||
|
EngineGlobals.projection_matrix = p_details->projection_matrix;
|
||
|
EngineGlobals.is_orthographic = true;
|
||
|
|
||
|
// Draw the meshes.
|
||
|
for( int i = 0; i < num_meshes; ++i )
|
||
|
{
|
||
|
sMesh *p_mesh = p_mesh_indices[i];
|
||
|
|
||
|
// Check this mesh is okay for shadow rendering.
|
||
|
if( !( p_mesh->m_flags & sMesh::MESH_FLAG_NO_SKATER_SHADOW ))
|
||
|
{
|
||
|
// Cull this mesh against the second view frustum.
|
||
|
if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
|
||
|
{
|
||
|
// if( frustum_check_box( &p_mesh->m_bbox ))
|
||
|
{
|
||
|
// Here we want to set up texture 0 as per the material on the mesh. This way we can use it as an alpha
|
||
|
// mask to avoid drawing the shadow on transparent pixels.
|
||
|
if( p_mesh->mp_material->mp_tex[0] )
|
||
|
{
|
||
|
set_texture( 0, p_mesh->mp_material->mp_tex[0]->pD3DTexture );
|
||
|
set_render_state( RS_UVADDRESSMODE0, p_mesh->mp_material->m_uv_addressing[0] );
|
||
|
}
|
||
|
|
||
|
// Draw the mesh.
|
||
|
D3DDevice_SetStreamSource( 0, p_mesh->mp_vertex_buffer[0], p_mesh->m_vertex_stride );
|
||
|
D3DDevice_DrawIndexedVertices( p_mesh->m_primitive_type, p_mesh->m_num_indices[0], p_mesh->mp_index_buffer[0] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Restore the view and projection transforms.
|
||
|
EngineGlobals.view_matrix = stored_view_matrix;
|
||
|
EngineGlobals.projection_matrix = stored_projection_matrix;
|
||
|
EngineGlobals.is_orthographic = false;
|
||
|
|
||
|
p_details = pTextureProjectionDetailsTable->IterateNext();
|
||
|
}
|
||
|
|
||
|
// Turn off z-offset.
|
||
|
D3DDevice_SetRenderState( D3DRS_SOLIDOFFSETENABLE, FALSE );
|
||
|
D3DDevice_SetRenderState( D3DRS_SHADOWFUNC, D3DCMP_NEVER );
|
||
|
set_pixel_shader( 0 );
|
||
|
|
||
|
// Restore anisotropic filtering if present.
|
||
|
D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, stage_zero_minfilter );
|
||
|
|
||
|
// Restore fogging if present.
|
||
|
set_render_state( RS_FOGENABLE, stored_fog_state );
|
||
|
|
||
|
// Reflush linear texture.
|
||
|
set_texture( 1, NULL );
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
static int cmp( const void *p1, const void *p2 )
|
||
|
{
|
||
|
return((sSortedMeshEntry*)p1)->sort < ((sSortedMeshEntry*)p2)->sort ? -1 : ((sSortedMeshEntry*)p1)->sort > ((sSortedMeshEntry*)p2)->sort ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static bool debug_shadow_volumes = false;
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void render_shadow_volumes( sScene *p_scene, uint32 viewport )
|
||
|
{
|
||
|
// Switch viewport from value to bitfield value.
|
||
|
viewport = ( 1 << viewport );
|
||
|
|
||
|
NxXbox::set_pixel_shader( PixelShader5 );
|
||
|
EngineGlobals.pixel_shader_override = PixelShader5;
|
||
|
NxXbox::set_texture( 0, NULL );
|
||
|
NxXbox::set_render_state( RS_ZWRITEENABLE, 0 );
|
||
|
NxXbox::set_render_state( RS_ZTESTENABLE, 1 );
|
||
|
NxXbox::set_render_state( RS_ALPHACUTOFF, 0 );
|
||
|
|
||
|
if( debug_shadow_volumes == false )
|
||
|
D3DDevice_SetRenderState( D3DRS_ZFUNC, D3DCMP_GREATEREQUAL );
|
||
|
|
||
|
// Render all meshes.
|
||
|
for( int e = 0; e < p_scene->m_num_mesh_entries; ++e )
|
||
|
{
|
||
|
sMesh *p_mesh = p_scene->m_meshes[e];
|
||
|
if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && ( p_mesh->m_flags & sMesh::MESH_FLAG_SHADOW_VOLUME ))
|
||
|
{
|
||
|
// Frustum cull this mesh, using the associated bounding sphere.
|
||
|
if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
|
||
|
{
|
||
|
if( debug_shadow_volumes == false )
|
||
|
{
|
||
|
NxXbox::set_render_state( RS_CULLMODE, D3DCULL_CW );
|
||
|
NxXbox::set_blend_mode( 0x10000000UL | NxXbox::vBLEND_MODE_ADD_FIXED );
|
||
|
p_mesh->Submit();
|
||
|
|
||
|
NxXbox::set_render_state( RS_CULLMODE, D3DCULL_CCW );
|
||
|
NxXbox::set_blend_mode( 0x10000000UL | NxXbox::vBLEND_MODE_SUB_FIXED );
|
||
|
p_mesh->Submit();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NxXbox::set_render_state( RS_CULLMODE, D3DCULL_NONE );
|
||
|
NxXbox::set_blend_mode( 0x30000000UL | NxXbox::vBLEND_MODE_BLEND_FIXED );
|
||
|
p_mesh->Submit();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EngineGlobals.pixel_shader_override = 0;
|
||
|
NxXbox::set_render_state( RS_ZWRITEENABLE, 1 );
|
||
|
NxXbox::set_render_state( RS_CULLMODE, D3DCULL_CW );
|
||
|
D3DDevice_SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
|
||
|
}
|
||
|
|
||
|
|
||
|
#define VISIBLE_MESH_ARRAY_SIZE 4096
|
||
|
static sMesh *visible_mesh_array[VISIBLE_MESH_ARRAY_SIZE];
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void render_scene( sScene *p_scene, uint32 flags, uint32 viewport )
|
||
|
{
|
||
|
sMaterial *p_material = NULL;
|
||
|
bool no_culling = ( flags & vRENDER_NO_CULLING ) > 0 ;
|
||
|
bool render;
|
||
|
int visible_mesh_array_index = 0;
|
||
|
|
||
|
// Don't render dictionary scenes.
|
||
|
if( p_scene->m_is_dictionary )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( flags & vRENDER_SHADOW_VOLUMES )
|
||
|
{
|
||
|
render_shadow_volumes( p_scene, viewport );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Disallow front to back sorting if the number of opaque meshes is larger than the visible mesh array.
|
||
|
if( p_scene->m_first_semitransparent_entry >= VISIBLE_MESH_ARRAY_SIZE )
|
||
|
{
|
||
|
flags &= ~vRENDER_SORT_FRONT_TO_BACK;
|
||
|
}
|
||
|
|
||
|
// Switch viewport from value to bitfield value.
|
||
|
viewport = ( 1 << viewport );
|
||
|
|
||
|
// Render opaque meshes.
|
||
|
if( flags & vRENDER_OPAQUE )
|
||
|
{
|
||
|
for( int e = 0; e < p_scene->m_first_semitransparent_entry; ++e )
|
||
|
{
|
||
|
sMesh *p_mesh = p_scene->m_meshes[e];
|
||
|
|
||
|
__asm mov eax, p_mesh // Store mesh pointer.
|
||
|
__asm prefetcht0 [eax] // Get first 32 bytes of sMesh structure.
|
||
|
|
||
|
if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && (( p_mesh->m_flags & ( sMesh::MESH_FLAG_SHADOW_VOLUME | sMesh::MESH_FLAG_BILLBOARD )) == 0 ))
|
||
|
{
|
||
|
if( no_culling )
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
render = false;
|
||
|
|
||
|
// Check the visibility mask.
|
||
|
if( p_mesh->m_visibility_mask & viewport )
|
||
|
{
|
||
|
// Frustum cull this set of meshes, using the associated bounding sphere and bounding box.
|
||
|
if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
|
||
|
{
|
||
|
// Check against any occluders.
|
||
|
if(( !( flags & vRENDER_OCCLUDED )) || ( !TestSphereAgainstOccluders( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius )))
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Draw this mesh if we decided it is visible.
|
||
|
if( render )
|
||
|
{
|
||
|
if(( flags & vRENDER_SORT_FRONT_TO_BACK ) == 0 )
|
||
|
{
|
||
|
// If the material has changed, submit the new material.
|
||
|
if( p_mesh->mp_material != p_material )
|
||
|
{
|
||
|
p_material = p_mesh->mp_material;
|
||
|
p_material->Submit();
|
||
|
}
|
||
|
p_mesh->Submit();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// If this mesh is within the 'near' section, render it now. Meshes in the far section will be
|
||
|
// deferred for rendering later on, which should gain some benefit from the fast pixel occlusion.
|
||
|
if( boundingSphereNearestZ <= FRONT_TO_BACK_SORT_CUTOFF )
|
||
|
{
|
||
|
if( p_mesh->mp_material != p_material )
|
||
|
{
|
||
|
p_material = p_mesh->mp_material;
|
||
|
p_material->Submit();
|
||
|
}
|
||
|
p_mesh->Submit();
|
||
|
}
|
||
|
|
||
|
// Set the nearest point of the bounding sphere.
|
||
|
p_mesh->m_bounding_sphere_nearest_z = boundingSphereNearestZ;
|
||
|
}
|
||
|
|
||
|
// Add this mesh to the visible list, providing it is within bounds.
|
||
|
visible_mesh_array[visible_mesh_array_index] = p_mesh;
|
||
|
if( visible_mesh_array_index < ( VISIBLE_MESH_ARRAY_SIZE - 1 ))
|
||
|
++visible_mesh_array_index;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(( flags & vRENDER_SORT_FRONT_TO_BACK ) && ( visible_mesh_array_index > 0 ))
|
||
|
{
|
||
|
// At this stage we have an array of meshes, some of which may not yet have been rendered.
|
||
|
// At this point simply scan through the list twice, drawing all 'far' meshes.
|
||
|
for( int vm = 0; vm < visible_mesh_array_index; ++vm )
|
||
|
{
|
||
|
sMesh *p_sorted_mesh = visible_mesh_array[vm];
|
||
|
|
||
|
if( p_sorted_mesh->m_bounding_sphere_nearest_z > FRONT_TO_BACK_SORT_CUTOFF )
|
||
|
{
|
||
|
// If the material has changed, submit the new material.
|
||
|
if( p_sorted_mesh->mp_material != p_material )
|
||
|
{
|
||
|
p_material = p_sorted_mesh->mp_material;
|
||
|
p_material->Submit();
|
||
|
}
|
||
|
p_sorted_mesh->Submit();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now draw the opaque meshes with shadow mapped on them.
|
||
|
if( p_scene->m_flags & SCENE_FLAG_RECEIVE_SHADOWS )
|
||
|
{
|
||
|
set_render_state( RS_ZWRITEENABLE, 0 );
|
||
|
DWORD min_filter;
|
||
|
D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &min_filter );
|
||
|
if( min_filter == D3DTEXF_ANISOTROPIC )
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
|
||
|
}
|
||
|
|
||
|
render_shadow_meshes( p_scene, visible_mesh_array, visible_mesh_array_index );
|
||
|
|
||
|
if( min_filter == D3DTEXF_ANISOTROPIC )
|
||
|
{
|
||
|
D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_ANISOTROPIC );
|
||
|
}
|
||
|
set_render_state( RS_ZWRITEENABLE, 1 );
|
||
|
}
|
||
|
|
||
|
// Reset mesh array
|
||
|
visible_mesh_array_index = 0;
|
||
|
|
||
|
if( flags & vRENDER_BILLBOARDS )
|
||
|
{
|
||
|
BillboardManager.Render( vRENDER_OPAQUE );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( flags & vRENDER_SEMITRANSPARENT )
|
||
|
{
|
||
|
int e = p_scene->m_first_semitransparent_entry;
|
||
|
int next_sorted_mesh_entry = 0;
|
||
|
|
||
|
// Semitransparent rendering is done in three stages.
|
||
|
// The first stage is meshes in the list up to the point where dynamic sorting starts.
|
||
|
// The second stage is meshes in the list which use dynamic sorting.
|
||
|
// The third stage is meshes in the list beyond the point where dynamic sorting ends.
|
||
|
for( ; e < p_scene->m_first_dynamic_sort_entry; ++e )
|
||
|
{
|
||
|
sMesh *p_mesh = p_scene->m_meshes[e];
|
||
|
|
||
|
__asm mov eax, p_mesh // Store mesh pointer.
|
||
|
__asm prefetcht0 [eax] // Get first 32 bytes of sMesh structure.
|
||
|
|
||
|
if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && (( p_mesh->m_flags & ( sMesh::MESH_FLAG_SHADOW_VOLUME | sMesh::MESH_FLAG_BILLBOARD )) == 0 ))
|
||
|
{
|
||
|
if( no_culling )
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
render = false;
|
||
|
|
||
|
// Check the visibility mask.
|
||
|
if( p_mesh->m_visibility_mask & viewport )
|
||
|
{
|
||
|
// Frustum cull this set of meshes, using the associated bounding box.
|
||
|
if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
|
||
|
{
|
||
|
// Check against any occluders.
|
||
|
if(( !( flags & vRENDER_OCCLUDED )) || ( !TestSphereAgainstOccluders( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius )))
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if( render )
|
||
|
{
|
||
|
// If the material has changed, submit the new material.
|
||
|
if( p_mesh->mp_material != p_material )
|
||
|
{
|
||
|
p_material = p_mesh->mp_material;
|
||
|
p_material->Submit();
|
||
|
}
|
||
|
p_mesh->Submit();
|
||
|
|
||
|
// Add this mesh to the visible list, providing it is within bounds.
|
||
|
visible_mesh_array[visible_mesh_array_index] = p_mesh;
|
||
|
if( visible_mesh_array_index < ( VISIBLE_MESH_ARRAY_SIZE - 1 ))
|
||
|
++visible_mesh_array_index;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( p_scene->m_num_dynamic_sort_entries > 0 )
|
||
|
{
|
||
|
// Second stage - dynamically sorted meshes.
|
||
|
int last_dynamic_sort_entry = p_scene->m_first_dynamic_sort_entry + p_scene->m_num_dynamic_sort_entries;
|
||
|
for( ; e < last_dynamic_sort_entry; ++e )
|
||
|
{
|
||
|
sMesh *p_mesh = p_scene->m_meshes[e];
|
||
|
|
||
|
__asm mov eax, p_mesh // Store mesh pointer.
|
||
|
__asm prefetcht0 [eax] // Get first 32 bytes of sMesh structure.
|
||
|
|
||
|
if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && (( p_mesh->m_flags & ( sMesh::MESH_FLAG_SHADOW_VOLUME | sMesh::MESH_FLAG_BILLBOARD )) == 0 ))
|
||
|
{
|
||
|
if( no_culling )
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
render = false;
|
||
|
|
||
|
// Check the visibility mask.
|
||
|
if( p_mesh->m_visibility_mask & viewport )
|
||
|
{
|
||
|
// Frustum cull this set of meshes, using the associated bounding box.
|
||
|
if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
|
||
|
{
|
||
|
// Check against any occluders.
|
||
|
if(( !( flags & vRENDER_OCCLUDED )) || ( !TestSphereAgainstOccluders( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius )))
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if( render )
|
||
|
{
|
||
|
// Add this mesh to the visible list, providing it is within bounds.
|
||
|
visible_mesh_array[visible_mesh_array_index] = p_mesh;
|
||
|
if( visible_mesh_array_index < ( VISIBLE_MESH_ARRAY_SIZE - 1 ))
|
||
|
++visible_mesh_array_index;
|
||
|
|
||
|
sortedMeshArray[next_sorted_mesh_entry].p_mesh = p_mesh;
|
||
|
sortedMeshArray[next_sorted_mesh_entry].sort = boundingSphereNearestZ;
|
||
|
|
||
|
++next_sorted_mesh_entry;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if( next_sorted_mesh_entry > 0 )
|
||
|
{
|
||
|
// Sort the array into ascending sort order.
|
||
|
qsort( sortedMeshArray, next_sorted_mesh_entry, sizeof( sSortedMeshEntry ), cmp );
|
||
|
|
||
|
for( int m = 0; m < next_sorted_mesh_entry; ++m )
|
||
|
{
|
||
|
if( sortedMeshArray[m].p_mesh->mp_material != p_material )
|
||
|
{
|
||
|
sortedMeshArray[m].p_mesh->mp_material->Submit();
|
||
|
p_material = sortedMeshArray[m].p_mesh->mp_material;
|
||
|
}
|
||
|
sortedMeshArray[m].p_mesh->Submit();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Third stage - meshes after the dynamically sorted set.
|
||
|
for( ; e < p_scene->m_num_mesh_entries; ++e )
|
||
|
{
|
||
|
sMesh *p_mesh = p_scene->m_meshes[e];
|
||
|
|
||
|
__asm mov eax, p_mesh // Store mesh pointer.
|
||
|
__asm prefetcht0 [eax] // Get first 32 bytes of sMesh structure.
|
||
|
|
||
|
if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && (( p_mesh->m_flags & ( sMesh::MESH_FLAG_SHADOW_VOLUME | sMesh::MESH_FLAG_BILLBOARD )) == 0 ))
|
||
|
{
|
||
|
if( no_culling )
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
render = false;
|
||
|
|
||
|
// Check the visibility mask.
|
||
|
if( p_mesh->m_visibility_mask & viewport )
|
||
|
{
|
||
|
// Frustum cull this set of meshes, using the associated bounding box.
|
||
|
if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
|
||
|
{
|
||
|
// Check against any occluders.
|
||
|
if(( !( flags & vRENDER_OCCLUDED )) || ( !TestSphereAgainstOccluders( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius )))
|
||
|
{
|
||
|
render = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if( render )
|
||
|
{
|
||
|
// If the material has changed, submit the new material.
|
||
|
if( p_mesh->mp_material != p_material )
|
||
|
{
|
||
|
p_material = p_mesh->mp_material;
|
||
|
p_material->Submit();
|
||
|
}
|
||
|
p_mesh->Submit();
|
||
|
|
||
|
// Add this mesh to the visible list, providing it is within bounds.
|
||
|
visible_mesh_array[visible_mesh_array_index] = p_mesh;
|
||
|
if( visible_mesh_array_index < ( VISIBLE_MESH_ARRAY_SIZE - 1 ))
|
||
|
++visible_mesh_array_index;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now draw the semitransparent meshes with shadow mapped on them.
|
||
|
if( p_scene->m_flags & SCENE_FLAG_RECEIVE_SHADOWS )
|
||
|
{
|
||
|
render_shadow_meshes( p_scene, visible_mesh_array, visible_mesh_array_index );
|
||
|
}
|
||
|
|
||
|
if( flags & vRENDER_BILLBOARDS )
|
||
|
{
|
||
|
BillboardManager.Render( vRENDER_SEMITRANSPARENT );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void render_light_glows( bool test )
|
||
|
{
|
||
|
struct sLightGlowVert
|
||
|
{
|
||
|
D3DVECTOR m_pos;
|
||
|
D3DCOLOR m_col;
|
||
|
};
|
||
|
|
||
|
static sLightGlowVert verts[4];
|
||
|
|
||
|
// This function will be called twice per render, once to test the amount of pixels drawn,
|
||
|
// the other to actually draw the pixels.
|
||
|
|
||
|
// Used to figure the right and up vectors for creating screen-aligned particle quads.
|
||
|
D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;
|
||
|
|
||
|
// Concatenate p_matrix with the emmission angle to create the direction.
|
||
|
Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );
|
||
|
|
||
|
// Get the 'right' vector as the cross product of camera 'at' and world 'up'.
|
||
|
Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
|
||
|
Mth::Vector screen_right = Mth::CrossProduct( at, up );
|
||
|
Mth::Vector screen_up = Mth::CrossProduct( screen_right, at );
|
||
|
|
||
|
screen_right.Normalize();
|
||
|
screen_up.Normalize();
|
||
|
|
||
|
sLightGlowDetailsTable.IterateStart();
|
||
|
sLightGlowDetails *p_details = sLightGlowDetailsTable.IterateNext();
|
||
|
|
||
|
if( test )
|
||
|
{
|
||
|
// Turn of z and color writes.
|
||
|
// D3DDevice_SetRenderState( D3DRS_COLORWRITEENABLE, 0 );
|
||
|
D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
|
||
|
|
||
|
set_blend_mode( vBLEND_MODE_DIFFUSE );
|
||
|
set_texture( 0, NULL );
|
||
|
set_pixel_shader( PixelShader5 );
|
||
|
set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
|
||
|
|
||
|
while( p_details )
|
||
|
{
|
||
|
// Get a new test index.
|
||
|
uint32 index = p_details->m_visibility_test_fifo.AddStatus();
|
||
|
|
||
|
// Only do the check if there is room on the queue.
|
||
|
if( index > 0 )
|
||
|
{
|
||
|
// For each light glow instance, add a render check.
|
||
|
D3DDevice_BeginVisibilityTest();
|
||
|
|
||
|
// Draw the glow.
|
||
|
verts[0].m_pos.x = p_details->m_pos[X] - ( screen_right[X] * p_details->m_test_radius ) - ( screen_up[X] * p_details->m_test_radius );
|
||
|
verts[0].m_pos.y = p_details->m_pos[Y] - ( screen_right[Y] * p_details->m_test_radius ) - ( screen_up[Y] * p_details->m_test_radius );
|
||
|
verts[0].m_pos.z = p_details->m_pos[Z] - ( screen_right[Z] * p_details->m_test_radius ) - ( screen_up[Z] * p_details->m_test_radius );
|
||
|
verts[0].m_col = 0xFFFFFFFF;
|
||
|
|
||
|
verts[1].m_pos.x = p_details->m_pos[X] - ( screen_right[X] * p_details->m_test_radius ) + ( screen_up[X] * p_details->m_test_radius );
|
||
|
verts[1].m_pos.y = p_details->m_pos[Y] - ( screen_right[Y] * p_details->m_test_radius ) + ( screen_up[Y] * p_details->m_test_radius );
|
||
|
verts[1].m_pos.z = p_details->m_pos[Z] - ( screen_right[Z] * p_details->m_test_radius ) + ( screen_up[Z] * p_details->m_test_radius );
|
||
|
verts[1].m_col = 0xFFFFFFFF;
|
||
|
|
||
|
verts[2].m_pos.x = p_details->m_pos[X] + ( screen_right[X] * p_details->m_test_radius ) + ( screen_up[X] * p_details->m_test_radius );
|
||
|
verts[2].m_pos.y = p_details->m_pos[Y] + ( screen_right[Y] * p_details->m_test_radius ) + ( screen_up[Y] * p_details->m_test_radius );
|
||
|
verts[2].m_pos.z = p_details->m_pos[Z] + ( screen_right[Z] * p_details->m_test_radius ) + ( screen_up[Z] * p_details->m_test_radius );
|
||
|
verts[2].m_col = 0xFFFFFFFF;
|
||
|
|
||
|
verts[3].m_pos.x = p_details->m_pos[X] + ( screen_right[X] * p_details->m_test_radius ) - ( screen_up[X] * p_details->m_test_radius );
|
||
|
verts[3].m_pos.y = p_details->m_pos[Y] + ( screen_right[Y] * p_details->m_test_radius ) - ( screen_up[Y] * p_details->m_test_radius );
|
||
|
verts[3].m_pos.z = p_details->m_pos[Z] + ( screen_right[Z] * p_details->m_test_radius ) - ( screen_up[Z] * p_details->m_test_radius );
|
||
|
verts[3].m_col = 0xFFFFFFFF;
|
||
|
|
||
|
D3DDevice_DrawVerticesUP( D3DPT_QUADLIST, 4, verts, sizeof( sLightGlowVert ));
|
||
|
|
||
|
// Push a visibility check onto the queue.
|
||
|
D3DDevice_EndVisibilityTest( index );
|
||
|
}
|
||
|
|
||
|
p_details = sLightGlowDetailsTable.IterateNext();
|
||
|
}
|
||
|
|
||
|
// Restore z and color writes.
|
||
|
D3DDevice_SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALL );
|
||
|
D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
set_blend_mode( vBLEND_MODE_BLEND );
|
||
|
set_pixel_shader( PixelShader5 );
|
||
|
set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
|
||
|
|
||
|
while( p_details )
|
||
|
{
|
||
|
// Get the visibility check result.
|
||
|
uint32 result = p_details->m_visibility_test_fifo.GetStatus();
|
||
|
|
||
|
if( result > 0 )
|
||
|
{
|
||
|
// Tend the radius towards the target.
|
||
|
p_details->m_current_radius += ( p_details->m_glow_radius - p_details->m_current_radius ) * p_details->m_radius_growth;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Tend the radius towards zero.
|
||
|
p_details->m_current_radius -= p_details->m_current_radius * p_details->m_radius_growth;
|
||
|
}
|
||
|
|
||
|
if( p_details->
|
||
|
m_current_radius > 0.0f )
|
||
|
{
|
||
|
// Draw the glow.
|
||
|
verts[0].m_pos.x = p_details->m_pos[X] - ( screen_right[X] * p_details->m_current_radius ) - ( screen_up[X] * p_details->m_current_radius );
|
||
|
verts[0].m_pos.y = p_details->m_pos[Y] - ( screen_right[Y] * p_details->m_current_radius ) - ( screen_up[Y] * p_details->m_current_radius );
|
||
|
verts[0].m_pos.z = p_details->m_pos[Z] - ( screen_right[Z] * p_details->m_current_radius ) - ( screen_up[Z] * p_details->m_current_radius );
|
||
|
verts[0].m_col = 0x40FFFFFF;
|
||
|
|
||
|
verts[1].m_pos.x = p_details->m_pos[X] - ( screen_right[X] * p_details->m_current_radius ) + ( screen_up[X] * p_details->m_current_radius );
|
||
|
verts[1].m_pos.y = p_details->m_pos[Y] - ( screen_right[Y] * p_details->m_current_radius ) + ( screen_up[Y] * p_details->m_current_radius );
|
||
|
verts[1].m_pos.z = p_details->m_pos[Z] - ( screen_right[Z] * p_details->m_current_radius ) + ( screen_up[Z] * p_details->m_current_radius );
|
||
|
verts[1].m_col = 0x40FFFFFF;
|
||
|
|
||
|
verts[2].m_pos.x = p_details->m_pos[X] + ( screen_right[X] * p_details->m_current_radius ) + ( screen_up[X] * p_details->m_current_radius );
|
||
|
verts[2].m_pos.y = p_details->m_pos[Y] + ( screen_right[Y] * p_details->m_current_radius ) + ( screen_up[Y] * p_details->m_current_radius );
|
||
|
verts[2].m_pos.z = p_details->m_pos[Z] + ( screen_right[Z] * p_details->m_current_radius ) + ( screen_up[Z] * p_details->m_current_radius );
|
||
|
verts[2].m_col = 0x40FFFFFF;
|
||
|
|
||
|
verts[3].m_pos.x = p_details->m_pos[X] + ( screen_right[X] * p_details->m_current_radius ) - ( screen_up[X] * p_details->m_current_radius );
|
||
|
verts[3].m_pos.y = p_details->m_pos[Y] + ( screen_right[Y] * p_details->m_current_radius ) - ( screen_up[Y] * p_details->m_current_radius );
|
||
|
verts[3].m_pos.z = p_details->m_pos[Z] + ( screen_right[Z] * p_details->m_current_radius ) - ( screen_up[Z] * p_details->m_current_radius );
|
||
|
verts[3].m_col = 0x40FFFFFF;
|
||
|
|
||
|
D3DDevice_DrawVerticesUP( D3DPT_QUADLIST, 4, verts, sizeof( sLightGlowVert ));
|
||
|
}
|
||
|
|
||
|
p_details = sLightGlowDetailsTable.IterateNext();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
} // namespace NxXbox
|
||
|
|