//**************************************************************************** //* MODULE: Gfx //* FILENAME: p_nxGeom.cpp //* OWNER: Gary Jesdanun //* CREATION DATE: 3/4/2002 //**************************************************************************** #include #include #include #include #include #include #include #include namespace Nx { /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ CXboxGeom::CXboxGeom() : mp_init_mesh_list( NULL ), m_mesh_array( NULL ), m_visible( 0xDEADBEEF ) { mp_instance = NULL; // mp_sector = NULL; m_scale.Set( 1.0f, 1.0f, 1.0f ); m_active = true; } /******************************************************************/ /* */ /* */ /******************************************************************/ CXboxGeom::~CXboxGeom( void ) { DestroyMeshArray(); if( mp_instance != NULL ) { delete mp_instance; mp_instance = NULL; } } ///////////////////////////////////////////////////////////////////////////////////// // Mesh List Functions // // The mesh list is only for initialization of the CGeom. As we find all the // meshes for the CGeoms, we add them to the temporary lists. When we are // done, the list is copied into a permanent array. // // All the list functions use the TopDownHeap for memory allocation. /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::InitMeshList( void ) { Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap()); mp_init_mesh_list = new Lst::Head< NxXbox::sMesh >; Mem::Manager::sHandle().PopContext(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::AddMesh( NxXbox::sMesh *mesh ) { Dbg_Assert( mp_init_mesh_list ); Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap()); Lst::Node< NxXbox::sMesh > *node = new Lst::Node< NxXbox::sMesh > (mesh); mp_init_mesh_list->AddToTail( node ); Mem::Manager::sHandle().PopContext(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::CreateMeshArray( void ) { if( !m_mesh_array ) { Dbg_Assert( mp_init_mesh_list ); m_num_mesh = mp_init_mesh_list->CountItems(); if (m_num_mesh) { Lst::Node< NxXbox::sMesh > *mesh, *next; m_mesh_array = new NxXbox::sMesh*[m_num_mesh]; int k = 0; for( mesh = mp_init_mesh_list->GetNext(); mesh; mesh = next ) { next = mesh->GetNext(); m_mesh_array[k] = mesh->GetData(); delete mesh; k++; } } // Delete temporary list. delete mp_init_mesh_list; mp_init_mesh_list = NULL; } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::RegisterMeshArray( bool just_count ) { // Tells the engine to count or add the meshes for this sector to the rendering list for supplied scene id. if( just_count ) { mp_scene->GetEngineScene()->CountMeshes( m_num_mesh, m_mesh_array ); } else { mp_scene->GetEngineScene()->AddMeshes( m_num_mesh, m_mesh_array ); } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::DestroyMeshArray( void ) { if( m_mesh_array ) { // Tells the engine to remove the meshes for this sector from the rendering list for supplied scene id. if( mp_scene && mp_scene->GetEngineScene()) { mp_scene->GetEngineScene()->RemoveMeshes( m_num_mesh, m_mesh_array ); } // Now actually go through and delete each mesh. for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; delete p_mesh; } delete [] m_mesh_array; m_mesh_array = NULL; } } /******************************************************************/ /* */ /* Generates an entirely new engine scene. */ /* Used when instance-cloning CGeoms. */ /* */ /******************************************************************/ NxXbox::sScene* CXboxGeom::GenerateScene( void ) { NxXbox::sScene *p_scene = new NxXbox::sScene(); p_scene->CountMeshes( m_num_mesh, m_mesh_array ); p_scene->CreateMeshArrays(); p_scene->AddMeshes( m_num_mesh, m_mesh_array ); p_scene->SortMeshes(); // Set the bounding box and bounding sphere. p_scene->m_bbox = m_bbox; p_scene->FigureBoundingVolumes(); return p_scene; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::plat_load_geom_data( CMesh *pMesh, CModel *pModel, bool color_per_material ) { // The skeleton must exist by this point (unless it's a hacked up car). int numBones = pModel->GetNumBones(); Mth::Matrix temp; temp.Identity(); CXboxMesh *p_xbox_mesh = static_cast( pMesh ); // Store a pointer to the CXboxMesh, used for obtaining CAS poly removal data. mp_mesh = p_xbox_mesh; CXboxModel *p_xbox_model = static_cast( pModel ); NxXbox::CInstance *p_instance = new NxXbox::CInstance( p_xbox_mesh->GetScene()->GetEngineScene(), temp, numBones, pModel->GetBoneTransforms()); SetInstance( p_instance ); p_instance->SetModel( pModel ); return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_finalize( void ) { // Scan through and remove degenerate indices... if( mp_instance ) { NxXbox::sScene *p_scene = mp_instance->GetScene(); if( p_scene ) { for( int m = 0; m < p_scene->m_num_mesh_entries; ++m ) { NxXbox::sMesh *p_mesh = p_scene->m_meshes[m]; p_mesh->Crunch(); } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_active( bool active ) { if( mp_instance ) { mp_instance->SetActive( active ); } else { m_active = active; if( m_mesh_array ) { for( uint m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; p_mesh->SetActive( active ); } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::plat_is_active( void ) const { if( mp_instance ) { return mp_instance->GetActive(); } return m_active; } /******************************************************************/ /* */ /* */ /******************************************************************/ const Mth::Vector &CXboxGeom::plat_get_world_position( void ) const { static Mth::Vector pos; if( mp_instance ) { pos = mp_instance->GetTransform()->GetPos(); } else { pos.Set( 0.0f, 0.0f, 0.0f, 1.0f ); NxXbox::sMesh *p_mesh = m_mesh_array[0]; if( p_mesh ) { p_mesh->GetPosition( &pos ); } } Dbg_Assert( pos[W] == 1.0f ); return pos; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_world_position( const Mth::Vector& pos ) { // Ensure w component is correct. Mth::Vector proper_pos( pos[X], pos[Y], pos[Z], 1.0f ); if( mp_instance ) { mp_instance->GetTransform()->SetPos( proper_pos ); } else { // Go through and adjust the individual meshes. for( uint32 i = 0; i < m_num_mesh; ++i ) { NxXbox::sMesh *p_mesh = m_mesh_array[i]; p_mesh->SetPosition( proper_pos ); } } } /******************************************************************/ /* */ /* */ /******************************************************************/ const Mth::CBBox & CXboxGeom::plat_get_bounding_box( void ) const { return m_bbox; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_bounding_sphere( const Mth::Vector& boundingSphere ) { if( mp_instance ) { NxXbox::sScene *p_scene = mp_instance->GetScene(); if( p_scene ) { p_scene->m_sphere_center.x = boundingSphere[X]; p_scene->m_sphere_center.y = boundingSphere[Y]; p_scene->m_sphere_center.z = boundingSphere[Z]; p_scene->m_sphere_radius = boundingSphere[W]; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ const Mth::Vector CXboxGeom::plat_get_bounding_sphere( void ) const { if( mp_instance ) { NxXbox::sScene *p_scene = mp_instance->GetScene(); if( p_scene ) { return Mth::Vector( p_scene->m_sphere_center.x, p_scene->m_sphere_center.y, p_scene->m_sphere_center.z, p_scene->m_sphere_radius ); } } return Mth::Vector( 0.0f, 0.0f, 0.0f, 10000.0f ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_orientation( const Mth::Matrix& orient ) { if( mp_instance ) { Mth::Matrix *p_matrix = mp_instance->GetTransform(); Mth::Matrix new_orientation = *p_matrix; new_orientation[X] = orient[X]; new_orientation[Y] = orient[Y]; new_orientation[Z] = orient[Z]; mp_instance->SetTransform( new_orientation ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ const Mth::Matrix &CXboxGeom::plat_get_orientation( void ) const { static Mth::Matrix orientation; if( mp_instance ) { orientation = *( mp_instance->GetTransform()); orientation[W] = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f ); } else { orientation.Identity(); } return orientation; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_rotate_y( Mth::ERot90 rot ) { if( rot != Mth::ROT_0 ) { if( mp_instance ) { Mth::Matrix *p_matrix = mp_instance->GetTransform(); // Zero out translation component. Mth::Matrix instance_transform = *p_matrix; instance_transform[W] = Mth::Vector(0.0f, 0.0f, 0.0f, 1.0f); // Build rotation matrix. Mth::Matrix rot_mat; float rad = (float)((int)rot) * ( Mth::PI * 0.5f ); Mth::CreateRotateYMatrix( rot_mat, rad ); // Rotate matrix. rot_mat = instance_transform * rot_mat; // Replace translation component. rot_mat[W] = p_matrix->GetPos(); mp_instance->SetTransform( rot_mat ); } else { // Go through and adjust the individual meshes. for( uint32 i = 0; i < m_num_mesh; ++i ) { NxXbox::sMesh *p_mesh = m_mesh_array[i]; p_mesh->SetYRotation( rot ); } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* pBoneMatrices, int numBones) { if( mp_instance ) { mp_instance->SetTransform( *pRootMatrix ); } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_bone_matrix_data( Mth::Matrix* pBoneMatrices, int numBones ) { if( mp_instance ) { Mth::Matrix* p_bone_matrices = mp_instance->GetBoneTransforms(); CopyMemory( p_bone_matrices, pBoneMatrices, numBones * sizeof( Mth::Matrix )); } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::plat_hide_polys( uint32 mask ) { if( mp_mesh ) { // Obtain a pointer to the Xbox scene. NxXbox::sScene *p_engine_scene = GetInstance()->GetScene(); // Request the scene to hide the relevant polys. p_engine_scene->HidePolys( mask, mp_mesh->GetCASData(), mp_mesh->GetNumCASData()); } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_visibility( uint32 mask ) { // Set values m_visible = mask; if( m_mesh_array ) { for( uint m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; p_mesh->SetVisibility((uint8)mask ); } } } /******************************************************************/ /* */ /* */ /******************************************************************/ uint32 CXboxGeom::plat_get_visibility( void ) const { return m_visible | 0xFFFFFF00; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_color( Image::RGBA rgba ) { // If we are setting the color (which is used as a multiplier) to (0x80,0x80,0x80), it is effectively // just turning the coloring off... // Note that some indoor stuff is set to 0x808081, since on Ps2 it needs to be set to something // other than 0x808080 in order to ensure that it doesn't inherit the color from the parent node. // This is not an issue for Xbox, so it treats the two values the same. if((( rgba.r == 0x80 ) || ( rgba.r == 0x81 )) && ( rgba.g == 0x80 ) && ( rgba.b == 0x80 )) { plat_clear_color(); return; } // Oofa, nasty hack. if( mp_instance ) { // Grab the engine scene from the geom, and set all meshes to the color. NxXbox::sScene *p_scene = mp_instance->GetScene(); for( int i = 0; i < p_scene->m_num_mesh_entries; ++i ) { NxXbox::sMesh *p_mesh = p_scene->m_meshes[i]; p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE; p_mesh->m_material_color_override[0] = (float)rgba.r / 255.0f; p_mesh->m_material_color_override[1] = (float)rgba.g / 255.0f; p_mesh->m_material_color_override[2] = (float)rgba.b / 255.0f; } } else if( m_mesh_array != NULL ) { for( uint32 i = 0; i < m_num_mesh; ++i ) { NxXbox::sMesh *p_mesh = m_mesh_array[i]; p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE; p_mesh->m_material_color_override[0] = (float)rgba.r / 255.0f; p_mesh->m_material_color_override[1] = (float)rgba.g / 255.0f; p_mesh->m_material_color_override[2] = (float)rgba.b / 255.0f; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_clear_color( void ) { // Oofa, nasty hack. if( mp_instance ) { // Grab the engine scene from the geom, and clear all meshes of the flag. NxXbox::sScene *p_scene = mp_instance->GetScene(); for( int i = 0; i < p_scene->m_num_mesh_entries; ++i ) { NxXbox::sMesh *p_mesh = p_scene->m_meshes[i]; p_mesh->m_flags &= ~NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE; } } else if( m_mesh_array != NULL ) { for( uint32 i = 0; i < m_num_mesh; ++i ) { NxXbox::sMesh *p_mesh = m_mesh_array[i]; p_mesh->m_flags &= ~NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::plat_set_material_color( uint32 mat_name_checksum, int pass, Image::RGBA rgba ) { bool something_changed = false; if( mp_instance && mp_instance->GetScene()) { for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m ) { NxXbox::sMesh *p_mesh = mp_instance->GetScene()->m_meshes[m]; NxXbox::sMaterial *p_mat = p_mesh->mp_material; if( p_mat ) { bool want_this_material = false; int adjusted_pass = pass; // We are searching for materials with a matching name. However there is a caveat in that the // conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for // geometry which is mapped with only certain passes of a multipass material (or both cases). // In such a case, the new material name checksum will differ from the original material name checksum, // with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ): // // Bits 3->6 Pass flags indicating which passes of the original material this material uses // Bits 0->2 Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents. if( p_mat->m_name_checksum == mat_name_checksum ) want_this_material = true; else if(( p_mat->m_name_checksum > mat_name_checksum ) && (( p_mat->m_name_checksum - mat_name_checksum ) <= 0x7F )) { uint32 checksum_diff = p_mat->m_name_checksum - mat_name_checksum; uint32 render_separate_pass = checksum_diff & 0x07; uint32 pass_flags = checksum_diff >> 3; if( render_separate_pass ) { if( render_separate_pass == ( pass + 1 )) { want_this_material = true; // Readjust the pass to zero, since this material was formed as a single pass of a multipass material. adjusted_pass = 0; } } else if( pass_flags ) { // This material was created during scene conversion from another material with more passes. if( pass_flags & ( 1 << pass )) { want_this_material = true; for( int p = 0; p < pass; ++p ) { if(( pass_flags & ( 1 << p )) == 0 ) { // Readjust the pass down by 1, since this material was created as a subset of another material. --adjusted_pass; } } } } } if( want_this_material ) { if((uint32)adjusted_pass < p_mat->m_passes ) { if( !( p_mat->m_flags[adjusted_pass] & MATFLAG_PASS_COLOR_LOCKED )) { p_mat->m_color[adjusted_pass][0] = (float)rgba.r / 255.0f; p_mat->m_color[adjusted_pass][1] = (float)rgba.g / 255.0f; p_mat->m_color[adjusted_pass][2] = (float)rgba.b / 255.0f; something_changed = true; } } } } } } return something_changed; } /******************************************************************/ /* */ /* */ /******************************************************************/ Image::RGBA CXboxGeom::plat_get_color( void ) const { Image::RGBA rgba( 0, 0, 0, 0 ); NxXbox::sMesh *p_mesh = NULL; if( mp_instance ) { // Grab the engine scene from the geom, and get a mesh color. NxXbox::sScene *p_scene = mp_instance->GetScene(); if( p_scene->m_num_mesh_entries > 0 ) p_mesh = p_scene->m_meshes[0]; } else if( m_mesh_array != NULL ) { p_mesh = m_mesh_array[0]; } if( p_mesh && ( p_mesh->m_flags & NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE )) { rgba.r = (uint8)( p_mesh->m_material_color_override[0] * 255.0f ); rgba.g = (uint8)( p_mesh->m_material_color_override[1] * 255.0f ); rgba.b = (uint8)( p_mesh->m_material_color_override[2] * 255.0f ); } return rgba; } /******************************************************************/ /* */ /* */ /******************************************************************/ int CXboxGeom::plat_get_num_render_verts( void ) { Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" )); int total_verts = 0; if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; total_verts += p_mesh->m_num_vertices; } } return total_verts; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_get_render_verts( Mth::Vector *p_verts ) { Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" )); if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; // Obtain a read-only lock on the mesh data. D3DVECTOR *p_pos; p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, (BYTE**)&p_pos, D3DLOCK_READONLY | D3DLOCK_NOFLUSH ); // Copy over every vertex position in this mesh. for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v ) { p_verts->Set( p_pos->x, p_pos->y, p_pos->z ); ++p_verts; p_pos = (D3DVECTOR*)((BYTE*)p_pos + p_mesh->m_vertex_stride ); } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_get_render_colors( Image::RGBA *p_colors ) { Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" )); if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; // Obtain a read-only lock on the mesh data. Image::RGBA *p_col; p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, (BYTE**)&p_col, D3DLOCK_READONLY | D3DLOCK_NOFLUSH ); p_col = (Image::RGBA*)((BYTE*)p_col + p_mesh->m_diffuse_offset ); // Copy over every vertex color in this mesh, swapping red and blue. for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v ) { p_colors->r = p_col->b; p_colors->g = p_col->g; p_colors->b = p_col->r; p_colors->a = p_col->a; ++p_colors; p_col = (Image::RGBA*)((BYTE*)p_col + p_mesh->m_vertex_stride ); } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_render_verts( Mth::Vector *p_verts ) { Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" )); if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; // Obtain a writeable lock on the mesh data. D3DVECTOR *p_pos; p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, (BYTE**)&p_pos, D3DLOCK_NOFLUSH ); // Will need to store the min and max points in order to calculate the new bounding sphere for the mesh. Mth::CBBox bbox; // Copy over every vertex position in this mesh. for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v ) { p_pos->x = p_verts->GetX(); p_pos->y = p_verts->GetY(); p_pos->z = p_verts->GetZ(); // Add this point to the bounding box. bbox.AddPoint( *p_verts ); ++p_verts; p_pos = (D3DVECTOR*)((BYTE*)p_pos + p_mesh->m_vertex_stride ); } // Now refigure the bounding sphere. Mth::Vector sphere_center = bbox.GetMin() + ( 0.5f * ( bbox.GetMax() - bbox.GetMin())); p_mesh->m_sphere_center.x = sphere_center[X]; p_mesh->m_sphere_center.y = sphere_center[Y]; p_mesh->m_sphere_center.z = sphere_center[Z]; p_mesh->m_sphere_radius = ( bbox.GetMax() - sphere_center ).Length(); } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_render_colors( Image::RGBA *p_colors ) { Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" )); if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh* p_mesh = m_mesh_array[m]; Image::RGBA* p_colors_save = p_colors; // The mesh may contain more than one vertex set, usually in the case of vertex wibbling. for( uint32 v = 0; v < p_mesh->m_num_vertex_buffers; ++v ) { p_colors = p_colors_save; // Obtain a writeable lock on the mesh data. Image::RGBA *p_col; p_mesh->mp_vertex_buffer[v]->Lock( 0, 0, (BYTE**)&p_col, D3DLOCK_NOFLUSH ); p_col = (Image::RGBA*)((BYTE*)p_col + p_mesh->m_diffuse_offset ); // Copy over every vertex color in this mesh, swapping red and blue. for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v ) { p_col->b = p_colors->r; p_col->g = p_colors->g; p_col->r = p_colors->b; p_col->a = p_colors->a; ++p_colors; p_col = (Image::RGBA*)((BYTE*)p_col + p_mesh->m_vertex_stride ); } } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_scale( const Mth::Vector & scale ) { Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" )); m_scale = scale; if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; Mth::Vector current_pos( 0.0f, 0.0f, 0.0f, 1.0f ); p_mesh->GetPosition( ¤t_pos ); // Obtain a writeable lock on the mesh data. D3DVECTOR *p_pos; p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, (BYTE**)&p_pos, D3DLOCK_NOFLUSH ); // Scale over every vertex position in this mesh. for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v ) { p_pos->x = (( p_pos->x - current_pos[X] ) * scale[X] ) + current_pos[X]; p_pos->y = (( p_pos->y - current_pos[Y] ) * scale[Y] ) + current_pos[Y]; p_pos->z = (( p_pos->z - current_pos[Z] ) * scale[Z] ) + current_pos[Z]; p_pos = (D3DVECTOR*)((BYTE*)p_pos + p_mesh->m_vertex_stride ); } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ Mth::Vector CXboxGeom::plat_get_scale() const { return m_scale; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_uv_wibble_params( float u_vel, float u_amp, float u_freq, float u_phase, float v_vel, float v_amp, float v_freq, float v_phase ) { if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; NxXbox::sMaterial *p_mat = p_mesh->mp_material; if( p_mat ) { // Find the first pass that wibbles. for( uint32 p = 0; p < p_mat->m_passes; ++p ) { if( p_mat->mp_UVWibbleParams[p] ) { p_mat->mp_UVWibbleParams[p]->m_UVel = u_vel; p_mat->mp_UVWibbleParams[p]->m_VVel = v_vel; p_mat->mp_UVWibbleParams[p]->m_UAmplitude = u_amp; p_mat->mp_UVWibbleParams[p]->m_VAmplitude = v_amp; p_mat->mp_UVWibbleParams[p]->m_UFrequency = u_freq; p_mat->mp_UVWibbleParams[p]->m_VFrequency = v_freq; p_mat->mp_UVWibbleParams[p]->m_UPhase = u_phase; p_mat->mp_UVWibbleParams[p]->m_VPhase = v_phase; break; } } } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_use_explicit_uv_wibble( bool yes ) { if( mp_instance && mp_instance->GetScene()) { for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m ) { NxXbox::sMesh *p_mesh = mp_instance->GetScene()->m_meshes[m]; NxXbox::sMaterial *p_mat = p_mesh->mp_material; if( p_mat ) { for( uint32 p = 0; p < p_mat->m_passes; ++p ) { if( p_mat->mp_UVWibbleParams[p] ) { p_mat->m_flags[p] |= MATFLAG_EXPLICIT_UV_WIBBLE; } } } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_uv_wibble_offsets( float u_offset, float v_offset ) { if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; NxXbox::sMaterial *p_mat = p_mesh->mp_material; if( p_mat ) { // Find the first pass that wibbles. for( uint32 p = 0; p < p_mat->m_passes; ++p ) { if( p_mat->mp_UVWibbleParams[p] ) { p_mat->mp_UVWibbleParams[p]->m_UVMatrix[2] = u_offset; p_mat->mp_UVWibbleParams[p]->m_UVMatrix[3] = v_offset; plat_use_explicit_uv_wibble( true ); break; } } } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::plat_set_uv_wibble_offsets( uint32 mat_name_checksum, int pass, float u_offset, float v_offset ) { if( m_mesh_array ) { for( uint32 m = 0; m < m_num_mesh; ++m ) { NxXbox::sMesh *p_mesh = m_mesh_array[m]; NxXbox::sMaterial *p_mat = p_mesh->mp_material; if( p_mat && ( p_mat->m_name_checksum == mat_name_checksum )) { // Find the first pass that wibbles. // for( uint32 p = 0; p < p_mat->m_passes; ++p ) // { // if( p_mat->mp_UVWibbleParams[p] ) // { // p_mat->mp_UVWibbleParams[p]->m_UWibbleOffset = u_offset; // p_mat->mp_UVWibbleParams[p]->m_VWibbleOffset = v_offset; // plat_use_explicit_uv_wibble( true ); // break; // } // } } } } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::plat_set_uv_matrix( uint32 mat_name_checksum, int pass, const Mth::Matrix& mat ) { if( mp_instance && mp_instance->GetScene()) { for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m ) { NxXbox::sMesh *p_mesh = mp_instance->GetScene()->m_meshes[m]; NxXbox::sMaterial *p_mat = p_mesh->mp_material; if( p_mat ) { bool want_this_material = false; int adjusted_pass = pass; // We are searching for materials with a matching name. However there is a caveat in that the // conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for // geometry which is mapped with only certain passes of a multipass material (or both cases). // In such a case, the new material name checksum will differ from the original material name checksum, // with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ): // // Bits 3->6 Pass flags indicating which passes of the original material this material uses // Bits 0->2 Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents. if( p_mat->m_name_checksum == mat_name_checksum ) { want_this_material = true; } else if(( p_mat->m_name_checksum > mat_name_checksum ) && (( p_mat->m_name_checksum - mat_name_checksum ) <= 0x7F )) { uint32 checksum_diff = p_mat->m_name_checksum - mat_name_checksum; uint32 render_separate_pass = checksum_diff & 0x07; uint32 pass_flags = checksum_diff >> 3; if( render_separate_pass ) { if( render_separate_pass == ( pass + 1 )) { want_this_material = true; // Readjust the pass to zero, since this material was formed as a single pass of a multipass material. adjusted_pass = 0; } } else if( pass_flags ) { // This material was created during scene conversion from another material with more passes. if( pass_flags & ( 1 << pass )) { want_this_material = true; for( int p = 0; p < pass; ++p ) { if(( pass_flags & ( 1 << p )) == 0 ) { // Readjust the pass down by 1, since this material was created as a subset of another material. --adjusted_pass; } } } } } if( want_this_material ) { if((uint32)adjusted_pass < p_mat->m_passes ) { // Create the wibble params if they don't exist already. if( p_mat->mp_UVWibbleParams[adjusted_pass] == NULL ) { p_mat->mp_UVWibbleParams[adjusted_pass] = new NxXbox::sUVWibbleParams; // Need to set flags to indicate that uv wibble is now in effect. p_mat->m_uv_wibble = true; p_mat->m_flags[adjusted_pass] |= MATFLAG_UV_WIBBLE | MATFLAG_EXPLICIT_UV_WIBBLE; } // Also need to switch vertex shaders if the current vertex shader does not support UV Transforms. if( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_1Weight ) { p_mesh->m_vertex_shader[0] = WeightedMeshVS_VXC_1Weight_UVTransform; } else if( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_2Weight ) { p_mesh->m_vertex_shader[0] = WeightedMeshVS_VXC_2Weight_UVTransform; } else if( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_3Weight ) { p_mesh->m_vertex_shader[0] = WeightedMeshVS_VXC_3Weight_UVTransform; } // Set the matrix values. p_mat->mp_UVWibbleParams[adjusted_pass]->m_UVMatrix[0] = mat[0][0]; p_mat->mp_UVWibbleParams[adjusted_pass]->m_UVMatrix[1] = mat[0][1]; p_mat->mp_UVWibbleParams[adjusted_pass]->m_UVMatrix[2] = mat[3][0]; p_mat->mp_UVWibbleParams[adjusted_pass]->m_UVMatrix[3] = mat[3][1]; } } } } } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxGeom::plat_allocate_uv_matrix_params( uint32 mat_name_checksum, int pass ) { if( mp_instance && mp_instance->GetScene()) { bool changed_something = false; for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m ) { NxXbox::sMesh *p_mesh = mp_instance->GetScene()->m_meshes[m]; NxXbox::sMaterial *p_mat = p_mesh->mp_material; if( p_mat ) { bool want_this_material = false; int adjusted_pass = pass; // We are searching for materials with a matching name. However there is a caveat in that the // conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for // geometry which is mapped with only certain passes of a multipass material (or both cases). // In such a case, the new material name checksum will differ from the original material name checksum, // with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ): // // Bits 3->6 Pass flags indicating which passes of the original material this material uses // Bits 0->2 Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents. if( p_mat->m_name_checksum == mat_name_checksum ) { want_this_material = true; } else if(( p_mat->m_name_checksum > mat_name_checksum ) && (( p_mat->m_name_checksum - mat_name_checksum ) <= 0x7F )) { uint32 checksum_diff = p_mat->m_name_checksum - mat_name_checksum; uint32 render_separate_pass = checksum_diff & 0x07; uint32 pass_flags = checksum_diff >> 3; if( render_separate_pass ) { if( render_separate_pass == ( pass + 1 )) { want_this_material = true; // Readjust the pass to zero, since this material was formed as a single pass of a multipass material. adjusted_pass = 0; } } else if( pass_flags ) { // This material was created during scene conversion from another material with more passes. if( pass_flags & ( 1 << pass )) { want_this_material = true; for( int p = 0; p < pass; ++p ) { if(( pass_flags & ( 1 << p )) == 0 ) { // Readjust the pass down by 1, since this material was created as a subset of another material. --adjusted_pass; } } } } } if( want_this_material ) { if((uint32)adjusted_pass < p_mat->m_passes ) { // Create the wibble params if they don't exist already. if( p_mat->mp_UVWibbleParams[adjusted_pass] == NULL ) { p_mat->mp_UVWibbleParams[adjusted_pass] = new NxXbox::sUVWibbleParams; // Need to set flags to indicate that uv wibble is now in effect. p_mat->m_uv_wibble = true; p_mat->m_flags[adjusted_pass] |= MATFLAG_UV_WIBBLE | MATFLAG_EXPLICIT_UV_WIBBLE; changed_something = true; } } } } } return changed_something; } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxGeom::plat_set_model_lights( CModelLights* p_model_lights ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ CXboxGeom *CXboxGeom::plat_clone( bool instance, CScene *p_dest_scene ) { CXboxGeom *p_clone = NULL; // This is a CXboxGeom 'hanging' from a sector. Create a new CXboxGeom which will store the CInstance. p_clone = new CXboxGeom(); // Create new meshes for the clone. p_clone->m_mesh_array = new NxXbox::sMesh*[m_num_mesh]; p_clone->m_num_mesh = m_num_mesh; for( uint32 m = 0; m < p_clone->m_num_mesh; ++m ) { p_clone->m_mesh_array[m] = m_mesh_array[m]->Clone( instance ); } if( instance == false ) { p_clone->SetActive( true ); // In this situation, we need to add the individual meshes to the scene. // Grab a temporary workspace buffer. Nx::CXboxScene *p_xbox_scene = static_cast( p_dest_scene ); NxXbox::sScene *p_scene = p_xbox_scene->GetEngineScene(); NxXbox::sMesh **p_temp_mesh_buffer = ( p_scene->m_num_mesh_entries > 0 ) ? new NxXbox::sMesh*[p_scene->m_num_mesh_entries] : NULL; // Set the scene pointer for the clone. p_clone->SetScene( p_xbox_scene ); // Copy meshes over into the temporary workspace buffer. for( int i = 0; i < p_scene->m_num_mesh_entries; ++i ) { p_temp_mesh_buffer[i] = p_scene->m_meshes[i]; } // Store how many meshes were present. int old_num_mesh_entries = p_scene->m_num_mesh_entries; // Delete current mesh array. delete [] p_scene->m_meshes; // Important to set this to NULL. p_scene->m_meshes = NULL; // Include new meshes in count. p_scene->CountMeshes( m_num_mesh, m_mesh_array ); // Allocate new mesh arrays. p_scene->CreateMeshArrays(); // Copy old mesh data back in. for( int i = 0; i < old_num_mesh_entries; ++i ) { p_scene->m_meshes[i] = p_temp_mesh_buffer[i]; } // Remove temporary arrays. delete [] p_temp_mesh_buffer; // Add new meshes. p_scene->AddMeshes( p_clone->m_num_mesh, p_clone->m_mesh_array ); // Sort the meshes. p_scene->SortMeshes(); } else { // Create a new scene which will be attached via the instance. p_clone->m_bbox = m_bbox; NxXbox::sScene *p_scene = p_clone->GenerateScene(); p_clone->SetActive( true ); // Create the instance. Mth::Matrix temp; temp.Identity(); NxXbox::CInstance *p_instance = new NxXbox::CInstance( p_scene, temp, 1, NULL ); // This instance will be the only object maintaining a reference to the attached scene, so we want to delete // the scene when the instance gets removed. p_instance->SetFlag( NxXbox::CInstance::INSTANCE_FLAG_DELETE_ATTACHED_SCENE ); // Hook the clone up to the instance. p_clone->SetInstance( p_instance ); } return p_clone; } /******************************************************************/ /* */ /* */ /******************************************************************/ CXboxGeom* CXboxGeom::plat_clone( bool instance, CModel* p_dest_model ) { CXboxGeom* p_clone = NULL; Nx::CXboxScene* p_xbox_scene = new CXboxScene; p_xbox_scene->SetEngineScene( mp_instance->GetScene() ); p_clone = new CXboxGeom(); p_clone->mp_scene = p_xbox_scene; int num_mesh = mp_instance->GetScene()->m_num_mesh_entries; // Create new meshes for the clone. p_clone->m_mesh_array = num_mesh ? new NxXbox::sMesh*[num_mesh] : NULL; p_clone->m_num_mesh = num_mesh; for( uint32 m = 0; m < p_clone->m_num_mesh; ++m ) { p_clone->m_mesh_array[m] = mp_instance->GetScene()->m_meshes[m]->Clone( instance ); } // Create a new scene which will be attached via the instance. p_clone->m_bbox = m_bbox; NxXbox::sScene* p_scene = p_clone->GenerateScene(); // Kill the temp scene. p_xbox_scene->SetEngineScene( NULL ); delete p_xbox_scene; p_clone->mp_scene = NULL; p_clone->SetActive( true ); // Create the new bone array. int num_bones = p_dest_model->GetNumBones(); Mth::Matrix* p_bones = new Mth::Matrix[num_bones]; for( int b = 0; b < num_bones; ++b ) { p_bones[b].Identity(); } // Create the instance. Mth::Matrix temp; temp.Identity(); NxXbox::CInstance* p_instance = new NxXbox::CInstance( p_scene, temp, num_bones, p_bones ); Nx::CXboxModel* p_xbox_model = static_cast( p_dest_model ); ((CXboxModel*)p_dest_model )->SetInstance( p_instance ); // This instance will be the only object maintaining a reference to the attached scene, so we want to delete // the scene when the instance gets removed. p_instance->SetFlag( NxXbox::CInstance::INSTANCE_FLAG_DELETE_ATTACHED_SCENE ); // Hook the clone up to the instance. p_clone->SetInstance( p_instance ); return p_clone; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // Nx