#include #include #include #include #include #include #include #include #include "gfx/xbox/p_nxtexture.h" #include "anim_vertdefs.h" #include "nx_init.h" #include "material.h" #include "scene.h" #include "render.h" namespace NxXbox { uint32 NumMaterials; static const float pi_over_180 = (float)Mth::PI / 180.0f; /******************************************************************/ /* */ /* */ /******************************************************************/ sUVWibbleParams::sUVWibbleParams( void ) { // Zero out the members. ZeroMemory( this, sizeof( sUVWibbleParams )); // Set the matrix correctly. m_UVMatrix[0] = 1.0f; } /******************************************************************/ /* */ /* */ /******************************************************************/ sUVWibbleParams::~sUVWibbleParams( void ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ sMaterial::sMaterial( void ) { m_num_wibble_vc_anims = 0; mp_wibble_vc_params = NULL; mp_wibble_vc_colors = NULL; mp_wibble_texture_params = NULL; m_uv_wibble = false; m_texture_wibble = false; m_grass_layers = 0; m_zbias = 0; for( int p = 0; p < MAX_PASSES; ++p ) { m_flags[p] = 0; mp_UVWibbleParams[p] = NULL; m_envmap_tiling[p][0] = 3.0f; m_envmap_tiling[p][1] = 3.0f; } } /******************************************************************/ /* */ /* */ /******************************************************************/ sMaterial::~sMaterial( void ) { if( mp_wibble_vc_params ) { for( uint32 i = 0; i < m_num_wibble_vc_anims; ++i ) { delete [] mp_wibble_vc_params[i].mp_keyframes; } delete [] mp_wibble_vc_params; } if( mp_wibble_vc_colors ) { delete [] mp_wibble_vc_colors; } for( int p = 0; p < MAX_PASSES; ++p ) { if( mp_UVWibbleParams[p] ) delete mp_UVWibbleParams[p]; } if( mp_wibble_texture_params ) { for( uint32 p = 0; p < MAX_PASSES; ++p ) { delete [] mp_wibble_texture_params->mp_keyframes[p]; } delete mp_wibble_texture_params; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void sMaterial::figure_wibble_uv( void ) { if( m_uv_wibble ) { // Get the time. float t = (float)Tmr::GetTime() * 0.001f; for( uint32 p = 0; p < m_passes; ++p ) { // Figure out UV offsets for wibbling if required. if( m_flags[p] & MATFLAG_UV_WIBBLE ) { if( !( m_flags[p] & MATFLAG_EXPLICIT_UV_WIBBLE )) { float uoff, voff; uoff = ( t * mp_UVWibbleParams[p]->m_UVel ) + ( mp_UVWibbleParams[p]->m_UAmplitude * sinf( mp_UVWibbleParams[p]->m_UFrequency * t + mp_UVWibbleParams[p]->m_UPhase )); voff = ( t * mp_UVWibbleParams[p]->m_VVel ) + ( mp_UVWibbleParams[p]->m_VAmplitude * sinf( mp_UVWibbleParams[p]->m_VFrequency * t + mp_UVWibbleParams[p]->m_VPhase )); // Reduce offset mod 16 and put it in the range -8 to +8. uoff += 8.0f; uoff -= (float)(( Ftoi_ASM( uoff ) >> 4 ) << 4 ); voff += 8.0f; voff -= (float)(( Ftoi_ASM( voff ) >> 4 ) << 4 ); mp_UVWibbleParams[p]->m_UVMatrix[2] = ( uoff < 0.0f ) ? ( uoff + 8.0f ) : ( uoff - 8.0f ); mp_UVWibbleParams[p]->m_UVMatrix[3] = ( voff < 0.0f ) ? ( voff + 8.0f ) : ( voff - 8.0f ); } } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void sMaterial::figure_wibble_vc( void ) { // The vertex color wibble flag is placed in pass 0. if( m_flags[0] & MATFLAG_VC_WIBBLE ) { for( uint32 i = 0; i < m_num_wibble_vc_anims; ++i ) { struct sVCWibbleParams *p_sequence = mp_wibble_vc_params + i; // Get phase-shift. int phase_shift = p_sequence->m_phase; // Time parameters. int num_keys = p_sequence->m_num_keyframes; int start_time = p_sequence->mp_keyframes[0].m_time; int end_time = p_sequence->mp_keyframes[num_keys - 1].m_time; int period = end_time - start_time; int time = start_time + ( NxXbox::EngineGlobals.render_start_time + phase_shift ) % period; // Locate the keyframe. int key; for( key = num_keys - 1; key >= 0; --key ) { if( time >= p_sequence->mp_keyframes[key].m_time ) { break; } } Dbg_Assert( key < ( num_keys - 1 )); // Parameter expressing how far we are between between this keyframe and the next. float t = (float)( time - p_sequence->mp_keyframes[key].m_time ) * ReciprocalEstimateNR_ASM((float)( p_sequence->mp_keyframes[key + 1].m_time - p_sequence->mp_keyframes[key].m_time )); // Interpolate the color. uint32 red = (uint32)Ftoi_ASM((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.r ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.r )); uint32 grn = (uint32)Ftoi_ASM((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.g ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.g )); uint32 blu = (uint32)Ftoi_ASM((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.b ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.b )); uint32 alp = (uint32)Ftoi_ASM((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.a ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.a )); // Switch red and blue, and store. mp_wibble_vc_colors[i] = ( alp << 24 ) | ( red << 16 ) | ( grn << 8 ) | blu; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void sMaterial::figure_wibble_texture( void ) { // The vertex color wibble flag is placed in pass 0. if( m_texture_wibble ) { int current_time = (int)Tmr::GetTime(); struct sTextureWibbleParams *p_sequence = mp_wibble_texture_params; for( int pass = 0; pass < MAX_PASSES; ++pass ) { if( m_flags[pass] & MATFLAG_PASS_TEXTURE_ANIMATES ) { // Get phase-shift. int phase_shift = p_sequence->m_phase[pass]; // Time parameters. int num_keys = p_sequence->m_num_keyframes[pass]; int start_time = p_sequence->mp_keyframes[pass][0].m_time; int end_time = p_sequence->mp_keyframes[pass][num_keys - 1].m_time; int period = end_time - start_time; int time = start_time + (( current_time + phase_shift ) % period ); // Keep track of the iterations, if iterations on this animation are required. if( p_sequence->m_num_iterations[pass] > 0 ) { int iteration = ( current_time - start_time ) / period; if( iteration >= p_sequence->m_num_iterations[pass] ) { // Set the time such that the animation no longer continues. time = start_time + ((( period * p_sequence->m_num_iterations[pass] ) + phase_shift ) % period ); } } // Locate the keyframe. int key; for( key = num_keys - 1; key >= 0; --key ) { if( time >= p_sequence->mp_keyframes[pass][key].m_time ) { break; } } // Set the texture. // Dbg_Assert( p_sequence->mp_keyframes[pass][key].mp_texture ); mp_tex[pass] = p_sequence->mp_keyframes[pass][key].mp_texture; } } } } inline DWORD F2DW( FLOAT f ) { return *( ( DWORD *) & f ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void sMaterial::Submit( void ) { // Dummy 8 element uv 'matrix' for custom pipeline. static float custom_uv_mat[8] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }; // Matrix for UV wibbling texture transform. static D3DMATRIX uv_mat = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; // Matrix for env mapping texture transform. Note that the [0][0] and [1][1] elements of the matrix are set up to scale, based on the // material pass properties. static D3DMATRIX env_mat = { 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f, 0.0f }; // Check for material change lockout. if( EngineGlobals.material_override ) { return; } // Set the alpha blend mode. set_blend_mode( m_reg_alpha[0] ); // Set the alpha cutoff value. set_render_state( RS_ALPHACUTOFF, (uint32)m_alpha_cutoff ); // Set the backface cull mode. set_render_state( RS_CULLMODE, m_no_bfc ? D3DCULL_NONE : D3DCULL_CW ); // Set the z-bias. set_render_state( RS_ZBIAS, m_zbias ); // Figure uv, vc and texture wibble updates if required. figure_wibble_uv(); figure_wibble_vc(); figure_wibble_texture(); // Set specular properties of this material. if( m_flags[0] & MATFLAG_SPECULAR ) { if( EngineGlobals.specular_enabled == 0 ) { set_render_state( RS_SPECULARENABLE, 1 ); // Set the specular material. D3DMATERIAL8 test_mat; ZeroMemory( &test_mat, sizeof( D3DMATERIAL8 )); test_mat.Specular.r = m_specular_color[0]; test_mat.Specular.g = m_specular_color[1]; test_mat.Specular.b = m_specular_color[2]; test_mat.Specular.a = 1.0f; test_mat.Power = m_specular_color[3]; D3DDevice_SetMaterial( &test_mat ); // If using a custom vertex shader, also need to load the specular color and power to vert shader registers here. if( NxXbox::EngineGlobals.custom_pipeline_enabled ) { D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_SPECULAR_COLOR_OFFSET, (void*)&m_specular_color[0], 1 ); } } } else { if( EngineGlobals.specular_enabled > 0 ) { set_render_state( RS_SPECULARENABLE, 0 ); // Set the specular material (basically just to set the power to zero). D3DMATERIAL8 test_mat; ZeroMemory( &test_mat, sizeof( D3DMATERIAL8 )); D3DDevice_SetMaterial( &test_mat ); } } // Set up the textures if present. This involves setting the texture and palette pointers and addressing mode. // Also, for multipass textures, it may require loading of fixed alpha values into specific constant registers. uint32 p; for( p = 0; p < m_passes; ++p ) { if( !( EngineGlobals.texture_stage_override & ( 1 << p ))) { // Load pass color and fixed alpha values to the pixel shader, if required. if( !EngineGlobals.upload_pixel_shader_constants ) { // Do the comparison as 32bit unsigned ints. uint32 *p_test0 = (uint32*)&m_color[p][0]; uint32 *p_test1 = (uint32*)&EngineGlobals.pixel_shader_constants[p * 4]; if(( p_test0[0] != p_test1[0] ) || ( p_test0[1] != p_test1[1] ) || ( p_test0[2] != p_test1[2] ) || ( p_test0[3] != p_test1[3] )) { EngineGlobals.upload_pixel_shader_constants = true; } } if( mp_tex[p] ) { set_texture( p, mp_tex[p]->pD3DTexture, mp_tex[p]->pD3DPalette ); // Set UV adressing mode. set_render_state( RS_UVADDRESSMODE0 + p, m_uv_addressing[p] ); // Set filtering mode. // set_render_state( RS_MINMAGFILTER0 + p, m_filtering_mode[p] ); // Set MIP lod bias. set_render_state( RS_MIPLODBIASPASS0 + p, *((uint32*)( &m_k[p] ))); // Deal with bump mapping setup. if( m_flags[p] & MATFLAG_BUMP_SIGNED_TEXTURE ) { // Channel of bump texture value (v,u) MUST be signed. if( EngineGlobals.color_sign[p] != ( D3DTSIGN_RSIGNED | D3DTSIGN_GSIGNED | D3DTSIGN_BSIGNED )) { EngineGlobals.color_sign[p] = ( D3DTSIGN_RSIGNED | D3DTSIGN_GSIGNED | D3DTSIGN_BSIGNED ); D3DDevice_SetTextureStageState( p, D3DTSS_COLORSIGN, D3DTSIGN_RSIGNED | D3DTSIGN_GSIGNED | D3DTSIGN_BSIGNED ); } } else { if( EngineGlobals.color_sign[p] != ( D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED )) { EngineGlobals.color_sign[p] = ( D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED ); D3DDevice_SetTextureStageState( p, D3DTSS_COLORSIGN, D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED ); } } if( m_flags[p] & MATFLAG_BUMP_LOAD_MATRIX ) { D3DDevice_SetTextureStageState( p, D3DTSS_BUMPENVMAT00, F2DW( EngineGlobals.bump_env_matrix._11 )); D3DDevice_SetTextureStageState( p, D3DTSS_BUMPENVMAT01, F2DW( EngineGlobals.bump_env_matrix._13 )); D3DDevice_SetTextureStageState( p, D3DTSS_BUMPENVMAT10, F2DW( EngineGlobals.bump_env_matrix._31 )); D3DDevice_SetTextureStageState( p, D3DTSS_BUMPENVMAT11, F2DW( EngineGlobals.bump_env_matrix._33 )); } if(( m_flags[p] & MATFLAG_ENVIRONMENT ) && ( EngineGlobals.allow_envmapping )) { // Handle environment mapping. env_mat.m[0][0] = 0.5f * m_envmap_tiling[p][0]; env_mat.m[1][1] = 0.5f * m_envmap_tiling[p][1]; D3DDevice_SetTransform((D3DTRANSFORMSTATETYPE)( D3DTS_TEXTURE0 + p ), &env_mat ); D3DDevice_SetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); D3DDevice_SetTextureStageState( p, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR | p ); } else if( m_flags[p] & MATFLAG_UV_WIBBLE ) { D3DDevice_SetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); D3DDevice_SetTextureStageState( p, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | p ); if( NxXbox::EngineGlobals.custom_pipeline_enabled ) { // If using a custom vertex shader, need to load the uv matrix here. // If using a custom vertex shader, need to load the custom uv matrix here. custom_uv_mat[0] = mp_UVWibbleParams[p]->m_UVMatrix[0]; custom_uv_mat[1] = -mp_UVWibbleParams[p]->m_UVMatrix[1]; custom_uv_mat[3] = mp_UVWibbleParams[p]->m_UVMatrix[2]; custom_uv_mat[4] = mp_UVWibbleParams[p]->m_UVMatrix[1]; custom_uv_mat[5] = mp_UVWibbleParams[p]->m_UVMatrix[0]; custom_uv_mat[7] = mp_UVWibbleParams[p]->m_UVMatrix[3]; D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_UV_MAT_OFFSET + ( p * 2 ), (void*)&custom_uv_mat[0], 2 ); } else { // Handle fixed function UV matrix. uv_mat._31 = mp_UVWibbleParams[p]->m_UVMatrix[2]; uv_mat._32 = mp_UVWibbleParams[p]->m_UVMatrix[3]; D3DDevice_SetTransform((D3DTRANSFORMSTATETYPE)( D3DTS_TEXTURE0 + p ), &uv_mat ); } } else { if( NxXbox::EngineGlobals.custom_pipeline_enabled ) { // If using a custom vertex shader, need to load the custom uv matrix here. custom_uv_mat[0] = 1.0f; custom_uv_mat[1] = 0.0f; custom_uv_mat[3] = 0.0f; custom_uv_mat[4] = 0.0f; custom_uv_mat[5] = 1.0f; custom_uv_mat[7] = 0.0f; D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_UV_MAT_OFFSET + ( p * 2 ), (void*)&custom_uv_mat[0], 2 ); } // Regular case. Check states here since it is quicker for this common case than setting blindly. uint32 tex_coord_index, tex_trans_flags; D3DDevice_GetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, &tex_trans_flags ); D3DDevice_GetTextureStageState( p, D3DTSS_TEXCOORDINDEX, &tex_coord_index ); if( tex_trans_flags != D3DTTFF_DISABLE ) { D3DDevice_SetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); } if( tex_coord_index != ( D3DTSS_TCI_PASSTHRU | p )) { D3DDevice_SetTextureStageState( p, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | p ); } } } else { set_texture( p, NULL, NULL ); } } } if( EngineGlobals.upload_pixel_shader_constants ) { CopyMemory( EngineGlobals.pixel_shader_constants, &m_color[0][0], sizeof( float ) * 4 * m_passes ); } // Make sure to set the textures for unused stages to NULL, to reduce texture overhead. // for( ; p < 4; ++p ) // { // if( !( EngineGlobals.texture_stage_override & ( 1 << p ))) // { // set_texture( p, NULL, NULL ); // } // } } /******************************************************************/ /* */ /* */ /******************************************************************/ uint32 sMaterial::GetIgnoreVertexAlphaPasses( void ) { // Return a bitfield with a bit set for any pass that is flagged to ignore vertex alpha. uint32 bf = 0; for( uint32 p = 0; p < m_passes; ++p ) { if( m_flags[p] & MATFLAG_PASS_IGNORE_VERTEX_ALPHA ) { bf |= ( 1 << p ); } } return bf; } /******************************************************************/ /* */ /* */ /******************************************************************/ sMaterial* GetMaterial( uint32 checksum, sScene *p_scene ) { if( p_scene->pMaterialTable ) { p_scene->pMaterialTable->IterateStart(); sMaterial *p_mat = p_scene->pMaterialTable->IterateNext(); while( p_mat ) { if( p_mat->m_checksum == checksum ) { return p_mat; } p_mat = p_scene->pMaterialTable->IterateNext(); } } return NULL; } #define MemoryRead( dst, size, num, src ) CopyMemory(( dst ), ( src ), (( num ) * ( size ))); \ ( src ) += (( num ) * ( size )) /******************************************************************/ /* */ /* */ /******************************************************************/ Lst::HashTable< sMaterial > *LoadMaterialsFromMemory( void **pp_mem, Lst::HashTable< Nx::CTexture > *p_texture_table ) { uint8 *p_data = (uint8*)( *pp_mem ); uint32 MMAG, MMIN, K, L, NumSeqs, seq, NumKeys; // Get number of materials. uint32 new_materials; MemoryRead( &new_materials, sizeof( uint32 ), 1, p_data ); Lst::HashTable< sMaterial >* pMaterialTable; // Create table, dynamically sizing it based on the number of new materials. uint32 optimal_table_size = new_materials * 2; uint32 test = 4; uint32 size = 2; for( ;; test <<= 1, ++size ) { // Check if this iteration of table size is sufficient, or if we have hit the maximum size. if(( optimal_table_size < test ) || ( size >= 12 )) { pMaterialTable = new Lst::HashTable< sMaterial >( size ); break; } } // Loop over materials. for( uint32 i = 0; i < new_materials; ++i ) { // Create new material. sMaterial *pMat = new sMaterial; // Get material checksum. MemoryRead( &pMat->m_checksum, sizeof( uint32 ), 1, p_data ); // Get material name checksum. MemoryRead( &pMat->m_name_checksum, sizeof( uint32 ), 1, p_data ); // Get number of passes. MemoryRead( &pMat->m_passes, sizeof( uint32 ), 1, p_data ); // Get alpha cutoff value. uint32 AlphaCutoff; MemoryRead( &AlphaCutoff, sizeof( uint32 ), 1, p_data ); Dbg_Assert( AlphaCutoff <= 0xFF ); pMat->m_alpha_cutoff = (uint8)AlphaCutoff; // Get sorted flag. MemoryRead( &pMat->m_sorted, sizeof( bool ), 1, p_data ); // Get draw order. MemoryRead( &pMat->m_draw_order, sizeof( float ), 1, p_data ); // Get single sided flag. bool single_sided; MemoryRead( &single_sided, sizeof( bool ), 1, p_data ); // Get backface cull flag. MemoryRead( &pMat->m_no_bfc, sizeof( bool ), 1, p_data ); // Get z-bias value. int zbias; MemoryRead( &zbias, sizeof( int ), 1, p_data ); pMat->m_zbias = (uint8)(( zbias > 16 ) ? 16 : zbias ); // Get grassify flag and (optionally) grassify data. bool grassify; MemoryRead( &grassify, sizeof( bool ), 1, p_data ); if( grassify ) { MemoryRead( &pMat->m_grass_height, sizeof( float ), 1, p_data ); MemoryRead( &pMat->m_grass_layers, sizeof( int ), 1, p_data ); } // Get specular power and (optionally) specular color. MemoryRead( &pMat->m_specular_color[3], sizeof( float ), 1, p_data ); if( pMat->m_specular_color[3] > 0.0f ) { MemoryRead( &pMat->m_specular_color[0], sizeof( float ) * 3, 1, p_data ); } // Neutral under proven otherwise. bool neutral_material_color = true; for( uint32 pass = 0; pass < pMat->m_passes; ++pass ) { // Get texture checksum. uint32 TextureChecksum; MemoryRead( &TextureChecksum, sizeof( uint32 ), 1, p_data ); // Get material flags. MemoryRead( &pMat->m_flags[pass], sizeof( uint32 ), 1, p_data ); // Get pass color flag. bool has_color; MemoryRead( &has_color, sizeof( bool ), 1, p_data ); // Get pass color. MemoryRead( &pMat->m_color[pass][0], sizeof( float ) * 3, 1, p_data ); // Check for color being neutral. if( neutral_material_color ) { if(( pMat->m_color[pass][0] != 0.5f ) || ( pMat->m_color[pass][1] != 0.5f ) || ( pMat->m_color[pass][2] != 0.5f )) { neutral_material_color = false; } } // Get ALPHA register value. uint64 reg_alpha; MemoryRead( ®_alpha, sizeof( uint64 ), 1, p_data ); uint32 blend_mode = (uint32)( reg_alpha & 0xFFFFFFUL ); uint32 fixed_alpha = (uint32)( reg_alpha >> 32 ); pMat->m_reg_alpha[pass] = blend_mode | ( fixed_alpha << 24 ); // Also calculate the floating point version of the fixed alpha. pMat->m_color[pass][3] = fixed_alpha / 128.0f; // Backface cull test - if this is an alpha blended material, turn off backface culling, except // where the material has been explicitly flagged as single sided. if(( pass == 0 ) && !single_sided && (( pMat->m_reg_alpha[pass] & sMaterial::BLEND_MODE_MASK ) != 0x00 )) { pMat->m_no_bfc = true; } // Get UV addressing types. uint32 u_addressing, v_addressing; MemoryRead( &u_addressing, sizeof( uint32 ), 1, p_data ); MemoryRead( &v_addressing, sizeof( uint32 ), 1, p_data ); pMat->m_uv_addressing[pass] = (( v_addressing << 16 ) | u_addressing ); // Get environment map u and v tiling multiples. MemoryRead( &pMat->m_envmap_tiling[pass][0], sizeof( float ) * 2, 1, p_data ); // Get minification and magnification filtering mode. MemoryRead( &pMat->m_filtering_mode[pass], sizeof( uint32 ), 1, p_data ); // Read uv wibble data if present. if( pMat->m_flags[pass] & MATFLAG_UV_WIBBLE ) { // Flag that this material wibbles. pMat->m_uv_wibble = true; // Create uv wibble params structure. pMat->mp_UVWibbleParams[pass] = new sUVWibbleParams; MemoryRead( pMat->mp_UVWibbleParams[pass], sizeof( float ) * 8, 1, p_data ); } // Read vertex color wibble data. if(( pass == 0 ) && ( pMat->m_flags[0] & MATFLAG_VC_WIBBLE )) { MemoryRead( &NumSeqs, sizeof( uint32 ), 1, p_data ); pMat->m_num_wibble_vc_anims = NumSeqs; // Create sequence data array. pMat->mp_wibble_vc_params = new sVCWibbleParams[NumSeqs]; // Create resultant color array. pMat->mp_wibble_vc_colors = new D3DCOLOR[NumSeqs]; for( seq = 0; seq < NumSeqs; ++seq ) { MemoryRead( &NumKeys, sizeof( uint32 ), 1, p_data ); int phase; MemoryRead( &phase, sizeof( int ), 1, p_data ); // Create array for keyframes. pMat->mp_wibble_vc_params[seq].m_num_keyframes = NumKeys; pMat->mp_wibble_vc_params[seq].m_phase = phase; pMat->mp_wibble_vc_params[seq].mp_keyframes = new sVCWibbleKeyframe[NumKeys]; // Read keyframes into array. MemoryRead( pMat->mp_wibble_vc_params[seq].mp_keyframes, sizeof( sVCWibbleKeyframe ), NumKeys, p_data ); } } // Read texture wibble data. if( pMat->m_flags[pass] & MATFLAG_PASS_TEXTURE_ANIMATES ) { // Create the texture wibble structure if not created yet. if( pMat->mp_wibble_texture_params == NULL ) { pMat->mp_wibble_texture_params = new NxXbox::sTextureWibbleParams; ZeroMemory( pMat->mp_wibble_texture_params, sizeof( NxXbox::sTextureWibbleParams )); // Flag the material as having texture wibble. pMat->m_texture_wibble = true; } int num_keyframes, period, iterations, phase; MemoryRead( &num_keyframes, sizeof( int ), 1, p_data ); MemoryRead( &period, sizeof( int ), 1, p_data ); // This value is currently discarded. MemoryRead( &iterations, sizeof( int ), 1, p_data ); MemoryRead( &phase, sizeof( int ), 1, p_data ); Dbg_Assert( num_keyframes > 0 ); pMat->mp_wibble_texture_params->m_num_keyframes[pass] = num_keyframes; pMat->mp_wibble_texture_params->m_phase[pass] = phase; pMat->mp_wibble_texture_params->m_num_iterations[pass] = iterations; pMat->mp_wibble_texture_params->mp_keyframes[pass] = new NxXbox::sTextureWibbleKeyframe[num_keyframes]; for( int ati = 0; ati < num_keyframes; ++ati ) { MemoryRead( &pMat->mp_wibble_texture_params->mp_keyframes[pass][ati].m_time, sizeof( uint32 ), 1, p_data ); // Read the texture checksum. uint32 cs; MemoryRead( &cs, sizeof( uint32 ), 1, p_data ); // Set the TextureChecksum variable so the mp_tex member will get populated. if( ati == 0 ) { TextureChecksum = cs; } // Resolve the checksum to a texture pointer. Nx::CXboxTexture *p_xbox_texture = static_cast( p_texture_table->GetItem( cs )); pMat->mp_wibble_texture_params->mp_keyframes[pass][ati].mp_texture = ( p_xbox_texture ) ? p_xbox_texture->GetEngineTexture() : NULL; } } if( TextureChecksum ) { // If textured, resolve texture checksum... Nx::CXboxTexture *p_xbox_texture = static_cast( p_texture_table->GetItem( TextureChecksum )); sTexture *mp_tex = ( p_xbox_texture ) ? p_xbox_texture->GetEngineTexture() : NULL; // Bail if checksum not found. if( mp_tex == NULL ) { Dbg_Message( "error: couldn't find texture checksum %08X\n", TextureChecksum ); pMat->mp_tex[pass] = NULL; } else { // Set texture pointer. pMat->mp_tex[pass] = mp_tex; } // Get mipmap info. MemoryRead( &MMAG, sizeof( uint32 ), 1, p_data ); MemoryRead( &MMIN, sizeof( uint32 ), 1, p_data ); MemoryRead( &K, sizeof( uint32 ), 1, p_data ); MemoryRead( &L, sizeof( uint32 ), 1, p_data ); // Default PS2 value for K appears to be -8.0f - we are interested in deviations from this value. pMat->m_k[pass] = ( *(float*)&K ) + 8.0f; // Dave note 09/03/02 - having MIPs selected earlier than normal seems to cause some problems, since Xbox // MIP selection is so different to Ps2. Limit the k value such that Xbox can never select smaller MIPs // earlier than it would do by default. if( pMat->m_k[pass] > 0.0f ) { pMat->m_k[pass] = 0.0f; } } else { // ...otherwise just step past mipmap info. pMat->mp_tex[pass] = NULL; p_data += 16; } } // Set the no material color flag if appropriate. if( neutral_material_color ) { pMat->m_flags[0] |= MATFLAG_NO_MAT_COL_MOD; } // Set the specular flag if appropriate. if( pMat->m_specular_color[3] > 0.0f ) { pMat->m_flags[0] |= MATFLAG_SPECULAR; } // There is a problem adding materials with the same checksum into the table. // This could happen when materials in different scenes share the same name. // It also happens for the dummy material (checksum == 0), so for now just special-case // that one. if( pMat->m_checksum == 0 ) { if( !pMaterialTable->GetItem( 0 )) { pMaterialTable->PutItem( pMat->m_checksum, pMat ); } } else { if( pMaterialTable->GetItem( pMat->m_checksum )) { Dbg_MsgAssert( 0, ( "NXXBOX ERROR: duplicate material: %x\n", pMat->m_checksum )); } else { pMaterialTable->PutItem( pMat->m_checksum, pMat ); } } } // Set the data pointer to the new position on return. *pp_mem = p_data; return pMaterialTable; } /******************************************************************/ /* */ /* */ /******************************************************************/ Lst::HashTable< sMaterial > *LoadMaterials( void *p_FH, Lst::HashTable< Nx::CTexture > *p_texture_table ) { uint32 MMAG, MMIN, K, L, NumSeqs, seq, NumKeys; // Get number of materials. uint32 new_materials; File::Read( &new_materials, sizeof( uint32 ), 1, p_FH ); Lst::HashTable< sMaterial >* pMaterialTable; // Create table, dynamically sizing it based on the number of new materials. uint32 optimal_table_size = new_materials * 2; uint32 test = 4; uint32 size = 2; for( ;; test <<= 1, ++size ) { // Check if this iteration of table size is sufficient, or if we have hit the maximum size. if(( optimal_table_size < test ) || ( size >= 12 )) { pMaterialTable = new Lst::HashTable< sMaterial >( size ); break; } } // Loop over materials. for( uint32 i = 0; i < new_materials; ++i ) { // Create new material. sMaterial *pMat = new sMaterial; // Get material checksum. File::Read( &pMat->m_checksum, sizeof( uint32 ), 1, p_FH ); // Get material name checksum. File::Read( &pMat->m_name_checksum, sizeof( uint32 ), 1, p_FH ); // Get number of passes. File::Read( &pMat->m_passes, sizeof( uint32 ), 1, p_FH ); // Get alpha cutoff value. uint32 AlphaCutoff; File::Read( &AlphaCutoff, sizeof( uint32 ), 1, p_FH ); pMat->m_alpha_cutoff = (uint8)AlphaCutoff; // Get sorted flag. File::Read( &pMat->m_sorted, sizeof( bool ), 1, p_FH ); // Get draw order. File::Read( &pMat->m_draw_order, sizeof( float ), 1, p_FH ); // Get single sided flag. bool single_sided; File::Read( &single_sided, sizeof( bool ), 1, p_FH ); // Get backface cull flag. File::Read( &pMat->m_no_bfc, sizeof( bool ), 1, p_FH ); // Get z-bias value. int zbias; File::Read( &zbias, sizeof( int ), 1, p_FH ); pMat->m_zbias = (uint8)(( zbias > 16 ) ? 16 : zbias ); // Get grassify flag and (optionally) grassify data. bool grassify; File::Read( &grassify, sizeof( bool ), 1, p_FH ); if( grassify ) { File::Read( &pMat->m_grass_height, sizeof( float ), 1, p_FH ); File::Read( &pMat->m_grass_layers, sizeof( int ), 1, p_FH ); } // Get specular power and (optionally) specular color. File::Read( &pMat->m_specular_color[3], sizeof( float ), 1, p_FH ); if( pMat->m_specular_color[3] > 0.0f ) { File::Read( &pMat->m_specular_color[0], sizeof( float ) * 3, 1, p_FH ); } // Neutral under proven otherwise. bool neutral_material_color = true; for( uint32 pass = 0; pass < pMat->m_passes; ++pass ) { // Get texture checksum. uint32 TextureChecksum; File::Read( &TextureChecksum, sizeof( uint32 ), 1, p_FH ); // Get material flags. File::Read( &pMat->m_flags[pass], sizeof( uint32 ), 1, p_FH ); // Get pass color flag. bool has_color; File::Read( &has_color, sizeof( bool ), 1, p_FH ); // Get pass color. File::Read( &pMat->m_color[pass][0], sizeof( float ) * 3, 1, p_FH ); // Check for color being neutral. if( neutral_material_color ) { if(( pMat->m_color[pass][0] != 0.5f ) || ( pMat->m_color[pass][1] != 0.5f ) || ( pMat->m_color[pass][2] != 0.5f )) { neutral_material_color = false; } } // Get ALPHA register value. uint64 reg_alpha; File::Read( ®_alpha, sizeof( uint64 ), 1, p_FH ); uint32 blend_mode = (uint32)( reg_alpha & 0xFFFFFFUL ); uint32 fixed_alpha = (uint32)( reg_alpha >> 32 ); pMat->m_reg_alpha[pass] = blend_mode | ( fixed_alpha << 24 ); // Also calculate the floating point version of the fixed alpha. pMat->m_color[pass][3] = fixed_alpha / 128.0f; // Backface cull test - if this is an alpha blended material, turn off backface culling, except // where the material has been explicitly flagged as single sided. if(( pass == 0 ) && !single_sided && (( pMat->m_reg_alpha[pass] & sMaterial::BLEND_MODE_MASK ) != 0x00 )) { pMat->m_no_bfc = true; } // Get UV addressing types. uint32 u_addressing, v_addressing; File::Read( &u_addressing, sizeof( uint32 ), 1, p_FH ); File::Read( &v_addressing, sizeof( uint32 ), 1, p_FH ); pMat->m_uv_addressing[pass] = (( v_addressing << 16 ) | u_addressing ); // Get environment map u and v tiling multiples. File::Read( &pMat->m_envmap_tiling[pass][0], sizeof( float ) * 2, 1, p_FH ); // Get minification and magnification filtering mode. File::Read( &pMat->m_filtering_mode[pass], sizeof( uint32 ), 1, p_FH ); // Read uv wibble data if present. if( pMat->m_flags[pass] & MATFLAG_UV_WIBBLE ) { // Flag that this material wibbles. pMat->m_uv_wibble = true; // Create uv wibble params structure. pMat->mp_UVWibbleParams[pass] = new sUVWibbleParams; File::Read( pMat->mp_UVWibbleParams[pass], sizeof( float ) * 8, 1, p_FH ); } // Read vertex color wibble data. if(( pass == 0 ) && ( pMat->m_flags[0] & MATFLAG_VC_WIBBLE )) { File::Read( &NumSeqs, sizeof( uint32 ), 1, p_FH ); pMat->m_num_wibble_vc_anims = NumSeqs; // Create sequence data array. pMat->mp_wibble_vc_params = new sVCWibbleParams[NumSeqs]; // Create resultant color array. pMat->mp_wibble_vc_colors = new D3DCOLOR[NumSeqs]; for( seq = 0; seq < NumSeqs; ++seq ) { File::Read( &NumKeys, sizeof( uint32 ), 1, p_FH ); int phase; File::Read( &phase, sizeof( int ), 1, p_FH ); // Create array for keyframes. pMat->mp_wibble_vc_params[seq].m_num_keyframes = NumKeys; pMat->mp_wibble_vc_params[seq].m_phase = phase; pMat->mp_wibble_vc_params[seq].mp_keyframes = new sVCWibbleKeyframe[NumKeys]; // Read keyframes into array. File::Read( pMat->mp_wibble_vc_params[seq].mp_keyframes, sizeof( sVCWibbleKeyframe ), NumKeys, p_FH ); } } // Read texture wibble data. if( pMat->m_flags[pass] & MATFLAG_PASS_TEXTURE_ANIMATES ) { // Create the texture wibble structure if not created yet. if( pMat->mp_wibble_texture_params == NULL ) { pMat->mp_wibble_texture_params = new NxXbox::sTextureWibbleParams; ZeroMemory( pMat->mp_wibble_texture_params, sizeof( NxXbox::sTextureWibbleParams )); // Flag the material as having texture wibble. pMat->m_texture_wibble = true; } int num_keyframes, period, iterations, phase; File::Read( &num_keyframes, sizeof( int ), 1, p_FH ); File::Read( &period, sizeof( int ), 1, p_FH ); // This value is currently discarded. File::Read( &iterations, sizeof( int ), 1, p_FH ); File::Read( &phase, sizeof( int ), 1, p_FH ); Dbg_Assert( num_keyframes > 0 ); pMat->mp_wibble_texture_params->m_num_keyframes[pass] = num_keyframes; pMat->mp_wibble_texture_params->m_phase[pass] = phase; pMat->mp_wibble_texture_params->m_num_iterations[pass] = iterations; pMat->mp_wibble_texture_params->mp_keyframes[pass] = new NxXbox::sTextureWibbleKeyframe[num_keyframes]; for( int ati = 0; ati < num_keyframes; ++ati ) { File::Read( &pMat->mp_wibble_texture_params->mp_keyframes[pass][ati].m_time, sizeof( uint32 ), 1, p_FH ); // Read the texture checksum. uint32 cs; File::Read( &cs, sizeof( uint32 ), 1, p_FH ); // Set the TextureChecksum variable so the mp_tex member will get populated. if( ati == 0 ) { TextureChecksum = cs; } // Resolve the checksum to a texture pointer. Nx::CXboxTexture *p_xbox_texture = static_cast( p_texture_table->GetItem( cs )); pMat->mp_wibble_texture_params->mp_keyframes[pass][ati].mp_texture = ( p_xbox_texture ) ? p_xbox_texture->GetEngineTexture() : NULL; } } if( TextureChecksum ) { // If textured, resolve texture checksum... Nx::CXboxTexture *p_xbox_texture = static_cast( p_texture_table->GetItem( TextureChecksum )); sTexture *mp_tex = ( p_xbox_texture ) ? p_xbox_texture->GetEngineTexture() : NULL; // Bail if checksum not found. if( mp_tex == NULL ) { Dbg_Message( "error: couldn't find texture checksum %08X\n", TextureChecksum ); pMat->mp_tex[pass] = NULL; } else { // Set texture pointer. pMat->mp_tex[pass] = mp_tex; } // Get mipmap info. File::Read( &MMAG, sizeof( uint32 ), 1, p_FH ); File::Read( &MMIN, sizeof( uint32 ), 1, p_FH ); File::Read( &K, sizeof( uint32 ), 1, p_FH ); File::Read( &L, sizeof( uint32 ), 1, p_FH ); // Default PS2 value for K appears to be -8.0f - we are interested in deviations from this value. pMat->m_k[pass] = ( *(float*)&K ) + 8.0f; // Dave note 09/03/02 - having MIPs selected earlier than normal seems to cause some problems, since Xbox // MIP selection is so different to Ps2. Limit the k value such that Xbox can never select smaller MIPs // earlier than it would do by default. if( pMat->m_k[pass] > 0.0f ) { pMat->m_k[pass] = 0.0f; } } else { // ...otherwise just step past mipmap info. pMat->mp_tex[pass] = NULL; File::Seek( p_FH, 16, SEEK_CUR ); } } // Set the no material color flag if appropriate. if( neutral_material_color ) { pMat->m_flags[0] |= MATFLAG_NO_MAT_COL_MOD; } // Set the specular flag if appropriate. if( pMat->m_specular_color[3] > 0.0f ) { pMat->m_flags[0] |= MATFLAG_SPECULAR; } // There is a problem adding materials with the same checksum into the table. // This could happen when materials in different scenes share the same name. // It also happens for the dummy material (checksum == 0), so for now just special-case // that one. if( pMat->m_checksum == 0 ) { if( !pMaterialTable->GetItem( 0 )) { pMaterialTable->PutItem( pMat->m_checksum, pMat ); } } else { if( pMaterialTable->GetItem( pMat->m_checksum )) { Dbg_MsgAssert( 0, ( "NXXBOX ERROR: duplicate material: %x\n", pMat->m_checksum )); } else { pMaterialTable->PutItem( pMat->m_checksum, pMat ); } } } return pMaterialTable; } } // namespace NxXbox