#include #include #include #include #include "texture.h" #include "mesh.h" #include "scene.h" #include "render.h" #include #include "nx_init.h" #include #include #include #define ENV_MAP_SCROLL_SCALE ( 1.0f / 32768.0f ) int g_dl = 1; bool gOverDraw = false; namespace NxNgc { static s32 last_audio_update = 0; /******************************************************************/ /* */ /* */ /******************************************************************/ static void do_audio_update( void ) { s32 t = OSGetTick(); if(( t < last_audio_update ) || ( OSDiffTick( t, last_audio_update ) > (s32)( OS_TIMER_CLOCK / 60 ))) { last_audio_update = t; Pcm::PCMAudio_Update(); } } /******************************************************************/ /* */ /* */ /******************************************************************/ static int sort_by_material_draw_order( const void *p1, const void *p2 ) { sMesh *p_mesh1 = *((sMesh**)p1 ); sMesh *p_mesh2 = *((sMesh**)p2 ); sMaterialHeader *p_material1 = p_mesh1->mp_dl->m_material.p_header; sMaterialHeader *p_material2 = p_mesh2->mp_dl->m_material.p_header; Dbg_Assert( p_material1 != NULL ); Dbg_Assert( p_material2 != NULL ); if( p_material1->m_draw_order == p_material2->m_draw_order ) { // Have to do some special case processing for the situation where two or more meshes have the same draw order, but only some are // marked as being dynamically sorted. In such a situation the non dynamically sorted ones should come first. if( ( p_material1->m_flags & (1<<0) ) == ( p_material2->m_flags & (1<<0) ) ) { if( p_material1 == p_material2 ) { // Same material, no further sorting required. return 0; } else { // If the blend mode is the same, sort by material address. if( p_material1->m_base_blend == p_material2->m_base_blend ) { // If the pixel shaders are the same, sort by material address, otherwise sort by pixel shader value. if( p_material1->m_texture_dl_id == p_material2->m_texture_dl_id ) { return ((uint32)p_material1 > (uint32)p_material2 ) ? 1 : -1; } else { return ((uint32)p_material1->m_texture_dl_id < (uint32)p_material2->m_texture_dl_id ) ? 1 : -1; } } else { return ((uint32)p_material1->m_base_blend > (uint32)p_material2->m_base_blend ) ? 1 : -1; } } } else if( p_material1->m_flags & (1<<0) ) { return 1; } return -1; } return ( p_material1->m_draw_order > p_material2->m_draw_order ) ? 1 : -1; } /******************************************************************/ /* */ /* */ /******************************************************************/ #define INDEX_WORKBUFFER_SIZE 1700 #define MAX_STRIPS 1024 #define MAX_INDEX_WIDTH 7 // Maximum index width: // 1 position // 1 normal // 2 colors // 4 tex coord static void hide_mesh( uint32 mask, sCASData *p_cas_data, uint32 num_entries, sMesh * p_mesh ) { uint16 index_workbuffer[INDEX_WORKBUFFER_SIZE][MAX_INDEX_WIDTH]; uint32 new_indices_index = 0; int index_strip_len[MAX_STRIPS]; int num_strips = 0; // Clear strips. for ( int lp = 0; lp < MAX_STRIPS; lp++ ) { index_strip_len[lp] = 0; } unsigned char * p_start = (unsigned char *)&p_mesh->mp_dl[1]; unsigned char * p_end = &p_start[p_mesh->mp_dl->m_size]; p_start = &p_start[p_mesh->mp_dl->m_index_offset]; // Skip to actual 1st GDBegin. unsigned char * p8 = p_start; uint8 begin_token = p8[0]; // Save token for use when rebuilding mesh. int stride = p_mesh->mp_dl->m_index_stride; int w; while ( p8 < p_end ) { if ( ( p8[0] & 0xf8 ) == GX_TRIANGLESTRIP ) { // Found a triangle strip - parse it. int num_verts = ( p8[1] << 8 ) | p8[2]; p8 += 3; // Skip GDBegin uint16 idx0[MAX_INDEX_WIDTH]; uint16 idx1[MAX_INDEX_WIDTH]; uint16 idx2[MAX_INDEX_WIDTH]; for ( w = 0; w < stride; w++ ) { idx1[w] = ( p8[0] << 8 ) | p8[1]; p8 += 2; } for ( w = 0; w < stride; w++ ) { idx2[w] = ( p8[0] << 8 ) | p8[1]; p8 += 2; } for ( int v = 2; v < num_verts; v++ ) { // Read next index to form triangle. for ( w = 0; w < stride; w++ ) idx0[w] = idx1[w]; for ( w = 0; w < stride; w++ ) idx1[w] = idx2[w]; for ( w = 0; w < stride; w++ ) { idx2[w] = ( p8[0] << 8 ) | p8[1]; p8 += 2; } // There shuold be no degenerate triangles. // ...check against every CAS entry. bool keep = true; for( uint32 entry = 0; entry < num_entries; ++entry ) { // Check this CAS entry has the correct mask... if( p_cas_data[entry].mask & mask ) { // ...and the right mesh... uint32 mesh = p_cas_data[entry].data0 >> 16; // The CAS data is ordered first in increasing mesh size - so we can early out here if applicable. if ( mesh > p_mesh->mp_dl->m_mesh_index ) { break; } if ( mesh == p_mesh->mp_dl->m_mesh_index ) { // ...and the correct index. uint32 i0 = ( p_cas_data[entry].data0 & 0xFFFF ); // The CAS data is next ordered in increasing size of i0 - so we can early out here if applicable. if ( i0 > idx0[0] ) { break; } uint32 i1 = ( p_cas_data[entry].data1 >> 16 ); uint32 i2 = ( p_cas_data[entry].data1 & 0xFFFF ); if ( ( i0 == idx0[0] ) && ( i1 == idx1[0] ) && ( i2 == idx2[0] ) ) { keep = false; break; } } } } // Copy index. if ( keep ) { if ( index_strip_len[num_strips] ) { // Already put 1st triangle in, just add index. for ( w = 0; w < stride; w++ ) index_workbuffer[new_indices_index][w] = idx2[w]; new_indices_index++; index_strip_len[num_strips]++; } else { // 1st triangle, so add all 3 verts. for ( w = 0; w < stride; w++ ) index_workbuffer[new_indices_index][w] = idx0[w]; new_indices_index ++; for ( w = 0; w < stride; w++ ) index_workbuffer[new_indices_index][w] = idx1[w]; new_indices_index ++; for ( w = 0; w < stride; w++ ) index_workbuffer[new_indices_index][w] = idx2[w]; new_indices_index ++; index_strip_len[num_strips] += 3; } } else { // If we don't keep this tri & we already started a strip, we need // to start a new strip. if ( index_strip_len[num_strips] ) { // Need to start a new strip. num_strips++; } } } // Start a new strip if we put data in this one. if ( index_strip_len[num_strips] ) { // Need to start a new strip. num_strips++; } } else { break; } } do_audio_update(); Dbg_MsgAssert( new_indices_index <= INDEX_WORKBUFFER_SIZE, ( "Too many indices in new mesh: %d\n", new_indices_index ) ); // See if we'll fit in the existing buffer. uint32 new_size = ( 3 * num_strips ) + ( sizeof( uint16 ) * stride * new_indices_index ) + p_mesh->mp_dl->m_index_offset; // OSReport( "New: %d Old: %d Diff: %d\n", new_size, p_mesh->mp_dl->m_size, p_mesh->mp_dl->m_size - new_size ); // if ( new_size > p_mesh->mp_dl->m_original_dl_size ) // { // // Need to allocate a new buffer for this. // } if ( new_size <= p_mesh->mp_dl->m_original_dl_size ) { p8 = p_start; int buffer_index = 0; for ( int strip = 0; strip < num_strips; strip++ ) { *p8++ = begin_token; *p8++ = (uint8)(index_strip_len[strip] >> 8 ); *p8++ = (uint8)(index_strip_len[strip] & 0xff ); for ( int index = 0; index < index_strip_len[strip]; index++ ) { for ( w = 0; w < stride; w++ ) { *p8++ = (uint8)(index_workbuffer[buffer_index][w] >> 8 ); *p8++ = (uint8)(index_workbuffer[buffer_index][w] & 0xff ); } buffer_index++; } } // Set new size of display list. int new_rounded_size = ( new_size + 31 ) & ~31; p_mesh->mp_dl->m_size = new_rounded_size; // Pad the DL with 0s to avoid spurious crap at the end & flush cache. int pad_size = new_rounded_size - new_size; for ( int pad = 0; pad < pad_size; pad++ ) *p8++ = 0; DCFlushRange( &p_mesh->mp_dl[1], new_rounded_size ); } else { #ifdef __NOPT_FINAL__ OSReport( "Warning: DL too big after hide_mesh - New: %d Old: %d Diff: %d\n", new_size, p_mesh->mp_dl->m_size, p_mesh->mp_dl->m_size - new_size ); #else Dbg_MsgAssert( false, ( "Error: DL too big after hide_mesh - New: %d Old: %d Diff: %d\n", new_size, p_mesh->mp_dl->m_size, p_mesh->mp_dl->m_size - new_size ) ); #endif // __NOPT_FINAL__ } do_audio_update(); } sScene::sScene( void ) { m_flags = 0; m_num_meshes = 0; // No meshes as yet. m_num_filled_meshes = 0; mpp_mesh_list = NULL; mp_hierarchyObjects = NULL; m_numHierarchyObjects = 0; mp_scene_data = NULL; mp_dl = NULL; mp_blend_dl = NULL; mp_material_header = NULL; mp_hierarchyObjects = NULL; m_numHierarchyObjects = 0; m_is_dictionary = false; } sScene::~sScene( void ) { // Remove the material table. // if( mp_material_array ) // { // delete mp_material_array; // } // // if( m_opaque_meshes != NULL ) // { // delete [] m_opaque_meshes; // } // if( m_semitransparent_meshes != NULL ) // { // delete [] m_semitransparent_meshes; // } // // if ( mp_hierarchyObjects ) // { // delete [] mp_hierarchyObjects; // } // // // Go through, and see if any DLs got allocated. // int dl = 0; // for( uint s = 0; s < mp_scene_data->m_num_objects; ++s ) // { // int num_mesh = mp_dl[dl].mp_object_header->m_num_meshes; // for ( int m = 0; m < num_mesh; m++ ) // { // if ( mp_dl[dl].mp_pos_pool ) // { // } // } // } if ( mp_scene_data && !( m_flags & SCENE_FLAG_CLONED_GEOM ) ) { delete [] mp_scene_data; } if ( mpp_mesh_list ) { delete [] mpp_mesh_list; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void sScene::AddMeshes( int num_meshes, sMesh **pp_meshes ) { // Add each mesh. for( int m = 0; m < num_meshes; ++m ) { if ( m_num_filled_meshes < m_num_meshes ) { mpp_mesh_list[m_num_filled_meshes] = pp_meshes[m]; ++m_num_filled_meshes; } else { Dbg_MsgAssert( false, ( "Too many meshes being added." ) ); } } // for( int m = 0; m < num_meshes; ++m ) // { // sMaterialHeader * p_material = pp_meshes[m]->mp_dl->m_material.p_header; // sMaterialPassHeader * p_pass = &mp_material_pass[p_material->m_pass_item]; // // bool transparent = (( p_material ) && ( p_pass->m_flags & (1<<3) )); // // if ( transparent ) // { // if ( m_num_filled_meshes < m_num_meshes ) // { // mpp_mesh_list[m_num_filled_meshes] = pp_meshes[m]; // ++m_num_filled_meshes; // } // else // { // Dbg_MsgAssert( false, ( "Too many meshes being added." ) ); // } // } // } // // for( int m = 0; m < num_meshes; ++m ) // { // sMaterialHeader * p_material = pp_meshes[m]->mp_dl->m_material.p_header; // sMaterialPassHeader * p_pass = &mp_material_pass[p_material->m_pass_item]; // // bool transparent = (( p_material ) && ( p_pass->m_flags & (1<<3) )); // // if ( !transparent ) // { // if ( m_num_filled_meshes < m_num_meshes ) // { // mpp_mesh_list[m_num_filled_meshes] = pp_meshes[m]; // ++m_num_filled_meshes; // } // else // { // Dbg_MsgAssert( false, ( "Too many meshes being added." ) ); // } // } // } } /******************************************************************/ /* */ /* */ /******************************************************************/ void sScene::CountMeshes( int num_meshes, sMesh **pp_meshes ) { m_num_meshes += (uint16)num_meshes; // // Count each mesh. // for( int m = 0; m < num_meshes; ++m ) // { // ++m_num_meshes; //// bool transparent = (( pp_meshes[m]->mp_material ) && ( pp_meshes[m]->mp_material->Flags[0] & 0x40 )); //// if( transparent ) //// { //// ++m_num_semitransparent_entries; //// } //// else //// { //// ++m_num_opaque_entries; //// } // } } /******************************************************************/ /* */ /* */ /******************************************************************/ void sScene::CreateMeshArrays( void ) { if ( m_num_meshes > 0 ) { mpp_mesh_list = new sMesh*[m_num_meshes]; } // if( m_num_semitransparent_entries > 0 ) // { // m_semitransparent_meshes = new sMesh*[m_num_semitransparent_entries]; // } // // if( m_num_opaque_entries > 0 ) // { // m_opaque_meshes = new sMesh*[m_num_opaque_entries]; // } } /******************************************************************/ /* */ /* */ /******************************************************************/ void sScene::FigureBoundingVolumes( void ) { // // Figure bounding sphere assuming bounding box has already been set up (during individual mesh initialisation). // Mth::Vector radial = ( m_bbox.GetMax() - m_bbox.GetMin() ) * 0.5f; // Mth::Vector center = m_bbox.GetMin() + radial; // m_sphere_center.set( center[X], center[Y], center[Z] ); // m_sphere_radius = sqrtf(( radial[X] * radial[X] ) + ( radial[Y] * radial[Y] ) + ( radial[Z] * radial[Z] )); } void sScene::RemoveMeshes( int num_meshes, sMesh **pp_meshes ) { int num_opaque_entries_removed = 0; int num_pre_semitransparent_entries_removed = 0; int num_dynamic_semitransparent_entries_removed = 0; int num_post_semitransparent_entries_removed = 0; int opaque = m_num_opaque_meshes; int presemi = opaque + m_num_pre_semitrans_meshes; int dynsemi = presemi + m_num_dynamic_semitrans_meshes; int postsemi = dynsemi + m_num_post_semitrans_meshes; for( int m = 0; m < num_meshes; ++m ) { sMesh *p_mesh = pp_meshes[m]; bool found = false; // Search Opaque for ( int i = 0; i < opaque; i++ ) { if( mpp_mesh_list[i] == p_mesh ) { found = true; mpp_mesh_list[i] = NULL; ++num_opaque_entries_removed; --m_num_opaque_meshes; break; } } if( found ) continue; // Search Pre Semitransparent for ( int i = opaque; i < presemi; i++ ) { if( mpp_mesh_list[i] == p_mesh ) { found = true; mpp_mesh_list[i] = NULL; ++num_pre_semitransparent_entries_removed; --m_num_pre_semitrans_meshes; break; } } if( found ) continue; // Search Dynamic Semitransparent for ( int i = presemi; i < dynsemi; i++ ) { if( mpp_mesh_list[i] == p_mesh ) { found = true; mpp_mesh_list[i] = NULL; ++num_dynamic_semitransparent_entries_removed; --m_num_dynamic_semitrans_meshes; break; } } if( found ) continue; // Search Post Semitransparent for ( int i = dynsemi; i < postsemi; i++ ) { if( mpp_mesh_list[i] == p_mesh ) { found = true; mpp_mesh_list[i] = NULL; ++num_post_semitransparent_entries_removed; --m_num_post_semitrans_meshes; break; } } Dbg_Assert( found ); } // Now go through and compact the arrays. // int total = m_num_opaque_meshes + m_num_pre_semitrans_meshes + m_num_dynamic_semitrans_meshes + m_num_post_semitrans_meshes; int total_removed = num_opaque_entries_removed + num_pre_semitransparent_entries_removed + num_dynamic_semitransparent_entries_removed + num_post_semitransparent_entries_removed; for ( int lp = 0; lp < total_removed; lp++ ) { for ( int i = 0; i < m_num_filled_meshes; i++ ) { if( !mpp_mesh_list[i] ) { // m_num_filled_meshes--; // mpp_mesh_list[i] = mpp_mesh_list[m_num_filled_meshes]; // Only worth copying if there is anything beyond this mesh. if( i < ( m_num_filled_meshes - 1 )) { memcpy( &mpp_mesh_list[i], &mpp_mesh_list[i + 1], sizeof( sMesh* ) * ( m_num_filled_meshes - ( i + 1 ))); } m_num_filled_meshes--; break; } } } // SortMeshes(); // m_num_filled_meshes -= total_removed; } void sScene::SortMeshes( void ) { // Sort the list of meshes. qsort( mpp_mesh_list, m_num_filled_meshes, sizeof( sMesh* ), sort_by_material_draw_order ); if ( mp_scene_data ) { m_num_opaque_meshes = 0; m_num_pre_semitrans_meshes = 0; m_num_dynamic_semitrans_meshes = 0; m_num_post_semitrans_meshes = 0; // Force opaque to be before semitrans. sMesh *p_mesh[8192]; int mesh_entry = 0; Dbg_MsgAssert( m_num_filled_meshes < 8192, ( "Too many meshes for temporary sort buffer." ) ); // Find 1st dynamic entry. int first_dynamic = -1; for( int i = 0; i < m_num_filled_meshes; ++i ) { if ( mpp_mesh_list[i] ) { bool sorted = mpp_mesh_list[i]->mp_dl->m_material.p_header->m_flags & (1<<0) ? true : false; if ( ( first_dynamic == -1 ) && sorted ) first_dynamic = i; } } for ( int pass = 0; pass < 4; pass++ ) { for( int i = 0; i < m_num_filled_meshes; ++i ) { if ( mpp_mesh_list[i] ) { bool transparent = ( mp_material_pass[mpp_mesh_list[i]->mp_dl->m_material.p_header->m_pass_item].m_flags & (1<<3) ); // bool transparent = ( mpp_mesh_list[i]->mp_dl->m_material.p_header->m_flags & (1<<3) ) ? true : false; bool sorted = mpp_mesh_list[i]->mp_dl->m_material.p_header->m_flags & (1<<0) ? true : false; if ( ( first_dynamic == -1 ) && sorted ) first_dynamic = i; switch ( pass ) { case 0: // Opaque if ( !transparent ) { p_mesh[mesh_entry] = mpp_mesh_list[i]; mpp_mesh_list[i] = NULL; mesh_entry++; m_num_opaque_meshes++; } break; case 1: // Pre semi if ( transparent && !sorted && ( i < first_dynamic ) ) { // if ( mpp_mesh_list[i]->mp_dl ) printf( "Pre Semi: %d, %8.3f\n", i, mpp_mesh_list[i]->mp_dl->m_material.p_header->m_draw_order ); p_mesh[mesh_entry] = mpp_mesh_list[i]; mpp_mesh_list[i] = NULL; mesh_entry++; m_num_pre_semitrans_meshes++; } break; case 2: // Dynamic semi if ( transparent && sorted ) { // if ( mpp_mesh_list[i]->mp_dl ) printf( "Dyn Semi: %d, %8.3f\n", i, mpp_mesh_list[i]->mp_dl->m_material.p_header->m_draw_order ); p_mesh[mesh_entry] = mpp_mesh_list[i]; mpp_mesh_list[i] = NULL; mesh_entry++; m_num_dynamic_semitrans_meshes++; } break; case 3: // Post semi (everything else)... // if ( transparent && !sorted && ( ( i >= first_dynamic ) && ( first_dynamic != -1 ) ) ) { // if ( mpp_mesh_list[i]->mp_dl ) printf( "Pst Semi: %d, %8.3f\n", i, mpp_mesh_list[i]->mp_dl->m_material.p_header->m_draw_order ); p_mesh[mesh_entry] = mpp_mesh_list[i]; mpp_mesh_list[i] = NULL; mesh_entry++; m_num_post_semitrans_meshes++; } break; default: Dbg_MsgAssert( false, ( "This should never happen." ) ); break; } } } } Dbg_MsgAssert( m_num_filled_meshes == mesh_entry, ( "Sorted meshes differs from actual meshes." ) ); // float order = p_mesh[0]->mp_dl->m_material.p_header->m_draw_order; for( int i = 0; i < m_num_filled_meshes; ++i ) { // Dbg_MsgAssert( p_mesh[i]->mp_dl->m_material.p_header->m_draw_order >= order, ( "Out of order on entry %d: last=%f, current = %f", i, order, p_mesh[i]->mp_dl->m_material.p_header->m_draw_order ) ); // order = p_mesh[i]->mp_dl->m_material.p_header->m_draw_order; mpp_mesh_list[i] = p_mesh[i]; } } else { m_num_opaque_meshes = m_num_filled_meshes; m_num_pre_semitrans_meshes = 0; m_num_dynamic_semitrans_meshes = 0; m_num_post_semitrans_meshes = 0; } } sMaterial *sScene::GetMaterial( uint32 checksum ) { // for ( int lp = 0; lp < m_num_materials; lp++ ) // { // if( mp_material_array[lp].Checksum == checksum ) // { // return &mp_material_array[lp]; // } // } return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ void sScene::HidePolys( uint32 mask, sCASData *p_cas_data, uint32 num_entries ) { if(( num_entries == 0 ) || ( mask == 0 )) { return; } // For each mesh, need to find all cas data which references verts in that mesh. for( int m = 0; m < this->m_num_filled_meshes; ++m ) { sMesh *p_mesh = mpp_mesh_list[m]; hide_mesh( mask, p_cas_data, num_entries, p_mesh ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ sScene *LoadScene( const char *Filename, sScene *pScene ) { return NULL; } void DeleteScene( sScene *pScene ) { // // Iterate through the table of materials, deleting them. // for ( int lp = 0; lp < pScene->m_num_materials; lp++ ) // { // if( pScene->mp_material_array[lp].mp_wibble_vc_params ) // { // for( uint32 i = 0; i < pScene->mp_material_array[lp].m_num_wibble_vc_anims; ++i ) // { // delete [] pScene->mp_material_array[lp].mp_wibble_vc_params[i].mp_keyframes; // } // delete [] pScene->mp_material_array[lp].mp_wibble_vc_params; // } // if( pScene->mp_material_array[lp].mp_wibble_vc_colors ) // { // delete [] pScene->mp_material_array[lp].mp_wibble_vc_colors; // } // if( pScene->mp_material_array[lp].m_pUVControl ) // { // delete [] pScene->mp_material_array[lp].m_pUVControl; // } // } // delete pScene->mp_material_array; // pScene->mp_material_array = NULL; // // // Delete the scene itself. delete pScene; } /******************************************************************/ /* */ /* */ /******************************************************************/ void MaterialBuild( sMesh * p_mesh, sScene * p_scene, bool bl, bool tx ) { if ( !p_mesh->mp_dl->m_material.p_header ) return; // Construct the DLs. uint32 size; Mem::Manager::sHandle().TopDownHeap()->PushAlign( 32 ); #define DL_BUILD_SIZE (8*1024) uint8 * p_build_dl = new (Mem::Manager::sHandle().TopDownHeap()) uint8[DL_BUILD_SIZE]; // Build the texture upload DL. DCFlushRange ( p_build_dl, DL_BUILD_SIZE ); GX::begin( p_build_dl, DL_BUILD_SIZE ); // multi_mesh( p_mesh, p_scene ); multi_mesh( p_mesh->mp_dl->m_material.p_header, &p_scene->mp_material_pass[p_mesh->mp_dl->m_material.p_header->m_pass_item], bl, tx ); size = GX::end(); DCFlushRange ( p_build_dl, DL_BUILD_SIZE ); if ( size && ( size <= p_scene->mp_texture_dl[p_mesh->mp_dl->m_material.p_header->m_texture_dl_id].m_dl_size ) ) { // p_mesh->mp_dl->mp_texture_dl = new uint8[size]; memcpy ( p_scene->mp_texture_dl[p_mesh->mp_dl->m_material.p_header->m_texture_dl_id].mp_dl, p_build_dl, size ); DCFlushRange ( p_scene->mp_texture_dl[p_mesh->mp_dl->m_material.p_header->m_texture_dl_id].mp_dl, size ); p_scene->mp_texture_dl[p_mesh->mp_dl->m_material.p_header->m_texture_dl_id].m_dl_size = (uint16)size; } else { // p_mesh->mp_dl->mp_texture_dl = NULL; // p_mesh->mp_dl->m_texture_dl_size = 0; } Mem::Manager::sHandle().TopDownHeap()->PopAlign(); // Done constructing DL. delete p_build_dl; } int g_material_id = -1; GXBool g_comploc = (GXBool)2; void ResetMaterialChange( void ) { g_material_id = -1; g_comploc = (GXBool)2; } /******************************************************************/ /* */ /* */ /******************************************************************/ //static void MyGXProject ( // f32 x, // model coordinates // f32 y, // f32 z, // f32 mtx[3][4], // model-view matrix // f32* pm, // projection matrix, as returned by GXGetProjectionv // f32* vp, // viewport, as returned by GXGetViewportv // f32* sx, // screen coordinates // f32* sy, // f32* sz ) //{ // Vec peye; // f32 xc, yc, zc, wc; // // ASSERTMSG(pm && vp && sx && sy && sz, GXERR_GET_NULL_PTR); // // // transform to eye space // peye.x = mtx[0][0]*x + mtx[0][1]*y + mtx[0][2]*z + mtx[0][3]; // peye.y = mtx[1][0]*x + mtx[1][1]*y + mtx[1][2]*z + mtx[1][3]; // peye.z = mtx[2][0]*x + mtx[2][1]*y + mtx[2][2]*z + mtx[2][3]; // // // My addition: Just a frig to stop stuff messing up as it gets close to the near plane. // peye.z -= 512.0f; // if ( peye.z > -1.0f ) peye.z = -1.0f; // // // transform to clip space // if (pm[0] == (f32)GX_PERSPECTIVE) { // perspective // xc = peye.x * pm[1] + peye.z * pm[2]; // yc = peye.y * pm[3] + peye.z * pm[4]; // zc = peye.z * pm[5] + pm[6]; // wc = 1.0f / -peye.z; // } else { // ortho // xc = peye.x * pm[1] + pm[2]; // yc = peye.y * pm[3] + pm[4]; // zc = peye.z * pm[5] + pm[6]; // wc = 1.0f; // } // // // compute screen scale and offset // *sx = xc * vp[2]/2 * wc + vp[0] + vp[2]/2; // *sy = -yc * vp[3]/2 * wc + vp[1] + vp[3]/2; // *sz = zc * (vp[5] - vp[4]) * wc + vp[5]; //} // void MaterialSubmit( sMesh * p_mesh, sScene *pScene = NULL ) { if ( !pScene->mp_scene_data ) return; if ( !p_mesh->mp_dl->m_material.p_header ) return; // if ( !p_mesh->mp_dl->mp_texture_dl ) return; if ( gOverDraw ) { GX::SetFog( GX_FOG_NONE, 0.0f, 0.0f, 0.0f, 0.0f, (GXColor){0,0,0,0} ); GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0 ); GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_KONST, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV ); GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV, GX_TEV_SWAP0, GX_TEV_SWAP0 ); GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE ); GX::SetTevKSel( GX_TEVSTAGE0, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A ); GX::SetTevKColor( GX_KCOLOR0, (GXColor){8,8,8,255} ); GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255}); GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255}); GX::SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE ); return; } // if ( g_dl /*&& !( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<3) )*/ ) // if ( g_dl/* && !( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<3) )*/ ) if ( g_dl && !( p_mesh->mp_dl->m_material.p_header->m_flags & ((1<<3)|(1<<4)) ) ) { //// if ( p_mesh->mp_dl->m_material.p_header->m_material_dl_id != g_material_id ) // { // g_material_id = p_mesh->mp_dl->m_material.p_header->m_material_dl_id; // // if ( pScene->mp_blend_dl[g_material_id].mp_dl && pScene->mp_blend_dl[g_material_id].m_dl_size ) // { //// GX::CallDisplayList( pScene->mp_blend_dl[g_material_id].mp_dl, pScene->mp_blend_dl[g_material_id].m_dl_size ); // multi_mesh( p_mesh->mp_dl->m_material.p_header, // &pScene->mp_material_pass[p_mesh->mp_dl->m_material.p_header->m_pass_item], // true, // false ); // } // } int tex_id = (int)p_mesh->mp_dl->m_material.p_header->m_texture_dl_id; GX::CallDisplayList( pScene->mp_texture_dl[tex_id].mp_dl, pScene->mp_texture_dl[tex_id].m_dl_size ); } else { multi_mesh( p_mesh->mp_dl->m_material.p_header, &pScene->mp_material_pass[p_mesh->mp_dl->m_material.p_header->m_pass_item], true, true, p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6) ? true : false, p_mesh->mp_dl->mp_object_header->m_num_skin_verts ? false : true ); } // See if we need to upload env mapping matrices. sMaterialHeader * p_mat = p_mesh->mp_dl->m_material.p_header; sMaterialPassHeader *p_pass = &pScene->mp_material_pass[p_mat->m_pass_item]; // Set Comploc u8 alphacutoff = p_mesh->mp_dl->m_material.p_header->m_alpha_cutoff; GXBool comploc; // if ( p_pass->m_texture.p_data && ( p_pass->m_texture.p_data->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_HOLES ) ) if ( alphacutoff > 0 ) { comploc = GX_FALSE; } else { comploc = GX_TRUE; } comploc = GX_FALSE; if ( g_comploc != comploc ) { g_comploc = comploc; GX::SetZCompLoc( comploc ); } // GX::SetAlphaCompare(GX_GEQUAL, alphacutoff, GX_AOP_AND, GX_GEQUAL, alphacutoff ); // GX::SetZCompLoc( GX_FALSE ); GX::SetAlphaCompare(GX_GEQUAL, alphacutoff, GX_AOP_AND, GX_GEQUAL, alphacutoff ); for ( int lp = 0; lp < p_mat->m_passes; lp++, p_pass++ ) { if ( p_pass->m_flags & ( (1<<1) | (1<<2) ) ) { // UV Wibbled or Environment mapped. GXTexMtx mtx; switch ( lp ) { case 0: mtx = GX_TEXMTX0; break; case 1: mtx = GX_TEXMTX1; break; case 2: mtx = GX_TEXMTX2; break; case 3: mtx = GX_TEXMTX3; break; default: mtx = GX_IDENTITY; break; } if( p_pass->m_flags & (1<<1) ) { // Env mapping. Mtx s, t, e, mv; MTXInvXpose((Mtx)&EngineGlobals.local_to_camera, mv ); // Project bounding sphere into screen space to get a metric to scroll the environment map. f32 p[GX_PROJECTION_SZ]; f32 vp[GX_VIEWPORT_SZ]; float rx, ry; //, rz; GX::GetProjectionv( p ); GX::GetViewportv( vp ); float x = EngineGlobals.local_to_camera.getPosX(); float y = EngineGlobals.local_to_camera.getPosY(); // float z = EngineGlobals.local_to_camera.getPosZ(); rx = -x; //EngineGlobals.local_to_camera.getRightX()*x + EngineGlobals.local_to_camera.getRightY()*y + EngineGlobals.local_to_camera.getRightZ()*z; ry = -y; //EngineGlobals.local_to_camera.getUpX()*x + EngineGlobals.local_to_camera.getUpY()*y + EngineGlobals.local_to_camera.getUpZ()*z; // MyGXProject( p_mesh->mp_dl->m_sphere[X], // p_mesh->mp_dl->m_sphere[Y], // p_mesh->mp_dl->m_sphere[Z], // (Mtx)&EngineGlobals.local_to_camera, // p, vp, &rx, &ry, &rz ); float u; float v; float ut = (float)p_pass->m_u_tile; float vt = (float)p_pass->m_v_tile; ut = ( ut * (1.0f / (float)(1<<12)) ); vt = ( vt * (1.0f / (float)(1<<12)) ); u = ( rx * ( ENV_MAP_SCROLL_SCALE * ut ) ); v = ( ry * ( ENV_MAP_SCROLL_SCALE * vt ) ); u -= (float)(int)u; // Keep within +/-1. v -= (float)(int)v; // Create the rotational component. MTXScale( s, 0.5f * ut, -0.5f * vt, 0.0f ); MTXTrans( t, 0.5f, 0.5f, 1.0f ); MTXConcat( t, s, e ); MTXConcat(e, mv, e); e[0][3] += u; e[1][3] += v; GX::LoadTexMtxImm(e, mtx, GX_MTX2x4); } else { if ( p_pass->m_uv_enabled ) { // Explicit matrix. Mtx real; // MTXCopy( p_pass->mp_explicit_wibble->m_matrix, real ); // real[0][3] = ( p_pass->mp_explicit_wibble->m_matrix[0][3] * -real[0][0] ) + ( p_pass->mp_explicit_wibble->m_matrix[1][3] * -real[0][1] ); // real[1][3] = ( p_pass->mp_explicit_wibble->m_matrix[0][3] * -real[1][0] ) + ( p_pass->mp_explicit_wibble->m_matrix[1][3] * -real[1][1] ); real[0][0] = ( (float)p_pass->m_uv_mat[0] ) * ( 1.0f / ((float)(1<<12)) ); real[0][1] = -( (float)p_pass->m_uv_mat[1] ) * ( 1.0f / ((float)(1<<12)) ); real[0][2] = 1.0f; real[0][3] = ( (float)p_pass->m_uv_mat[2] ) * ( 1.0f / ((float)(1<<12)) ) - 1.0f; real[1][0] = ( (float)p_pass->m_uv_mat[1] ) * ( 1.0f / ((float)(1<<12)) ); real[1][1] = ( (float)p_pass->m_uv_mat[0] ) * ( 1.0f / ((float)(1<<12)) ); real[1][2] = 1.0f; real[1][3] = ( (float)p_pass->m_uv_mat[3] ) * ( 1.0f / ((float)(1<<12)) ) - 1.0f; GX::LoadTexMtxImm( real, mtx, GX_MTX2x4 ); } else { // Wibbled. float uoff, voff, t; t = (float)Tmr::GetTime() * 0.001f; Mtx m; sMaterialUVWibble * p_uv = &pScene->mp_uv_wibble[p_pass->m_uv_wibble_index]; uoff = ( t * p_uv->m_u_vel ) + ( p_uv->m_u_amp * sinf( p_uv->m_u_freq * t + p_uv->m_u_phase )); voff = ( t * p_uv->m_v_vel ) + ( p_uv->m_v_amp * sinf( p_uv->m_v_freq * t + p_uv->m_v_phase )); // Reduce offset mod 16 and put it in the range -8 to +8. uoff += 8.0f; uoff -= (float)(( (int)uoff >> 4 ) << 4 ); voff += 8.0f; voff -= (float)(( (int)voff >> 4 ) << 4 ); uoff = ( uoff < 0.0f ) ? ( uoff + 8.0f ) : ( uoff - 8.0f ); voff = ( voff < 0.0f ) ? ( voff + 8.0f ) : ( voff - 8.0f ); MTXTrans( m, uoff, voff, 0.0f ); GX::LoadTexMtxImm(m, mtx, GX_MTX2x4); } } } } } } // namespace NxNgc