/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: W3DGranny.cpp //////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Westwood Studios Pacific. // // Confidential Information // Copyright (C) 2001 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: W3DGranny.cpp // // Created: Mark Wilczynski, Sep 2001 // // Desc: Methods for using Granny models within the W3D engine. //----------------------------------------------------------------------------- #ifdef INCLUDE_GRANNY_IN_BUILD #include "W3DDevice/GameClient/W3DGranny.h" #include "common/GlobalData.h" #include "texture.h" #include "colmath.h" #include "coltest.h" #include "rinfo.h" #include "camera.h" #include "assetmgr.h" #include "WW3D2/DX8Wrapper.h" #include "WW3D2/Scene.h" #pragma comment( lib, "granny2" ) static granny_pnt332_vertex g_blendingBuffer[4096]; ///TrackGroupCount; ++TrackGroupIndex) { if(strcmp(Animation->TrackGroups[TrackGroupIndex]->Name, GrannyGetSourceModel(ModelInstance)->Name) == 0) { return(TrackGroupIndex); } }} } return(-1); } /** Local function used to modify the original granny data - flip coordintes, scale, etc. */ static void TransformFile(granny_file_info *FileInfo) { float Origin[] = {0, 0, 0}; float RightVector[] = {1, 0, 0}; //x float UpVector[] = {0, 0, 1}; //y float BackVector[] = {0, -1, 0};//z return; float Affine3[3]; float Linear3x3[3][3]; float InverseLinear3x3[3][3]; GrannyAutoComputeBasisConversion(FileInfo, 1.0f, Origin, RightVector, UpVector, BackVector, Affine3, (float *)Linear3x3, (float *)InverseLinear3x3); GrannyAutoTransformFile(FileInfo, Affine3, (float *)Linear3x3, (float *)InverseLinear3x3); } //============================================================================= // GrannyRenderObjClass::~GrannyRenderObjClass //============================================================================= /** Destructor. Releases w3d/granny assets. */ //============================================================================= GrannyRenderObjClass::~GrannyRenderObjClass(void) { if (m_animationControl) GrannyFreeControl(m_animationControl); if (m_modelInstance) GrannyFreeModelInstance(m_modelInstance); freeResources(); } //============================================================================= // GrannyRenderObjClass::GrannyRenderObjClass //============================================================================= /** Constructor. Creates an instance of the prototype. */ //============================================================================= GrannyRenderObjClass::GrannyRenderObjClass(const GrannyPrototypeClass &proto) :m_prototype(proto) { granny_file_info *fileInfo = GrannyGetFileInfo((granny_file *)proto.m_file); m_boundingBox = proto.m_boundingBox; m_boundingSphere = proto.m_boundingSphere; m_vertexCount = proto.m_vertexCount; m_animationControl = NULL; //no animation playing by default if (fileInfo) { //Find the skinned model for (Int modelIndex=0; modelIndexModelCount; modelIndex++) { granny_model *sourceModel = fileInfo->Models[modelIndex]; //ignore bounding boxes since they are never rendered if (stricmp(sourceModel->Name,"AABOX") != 0) m_modelInstance = GrannyInstantiateModel(fileInfo->Models[modelIndex]); } if(m_modelInstance) { //assign textures to the model GrannyAutoBindModel(m_modelInstance, _GrannyLoader.Get_Material_Library(), 1); // GrannyAddModelToScene(Global.Scene, ModelInstance); ///@todo: Create a granny scene for quicker updates. float *RootTransform = GrannyGetModelRootTransform(m_modelInstance); RootTransform[12] = 0;//x RootTransform[13] = 0;//y RootTransform[14] = 0;//z }//modelinstance*/ } } /** Set scaling factor applied to prototype during rendering */ void GrannyRenderObjClass::Set_ObjectScale(float scale) { ObjectScale=scale; //adjust bounding volumes we copied from non-scaled prototype. m_boundingBox.Center *= scale; m_boundingBox.Extent *= scale; ///@todo: Remove earlier hacks in W3D to get instance matrix scaling! ///After the W3D hacks are gone, we should also scale the radius here. m_boundingSphere.Center *= scale; } //============================================================================= // GrannyRenderObjClass::Get_Obj_Space_Bounding_Sphere //============================================================================= /** WW3D method that returns object bounding sphere used in frustum culling*/ //============================================================================= void GrannyRenderObjClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const { sphere=m_boundingSphere; } //============================================================================= // GrannyRenderObjClass::Get_Obj_Space_Bounding_Box //============================================================================= /** WW3D method that returns object bounding box used in collision detection*/ //============================================================================= void GrannyRenderObjClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const { box=m_boundingBox; } //============================================================================= // GrannyRenderObjClass::Cast_Ray //============================================================================= /** WW3D method that returns intersection point between ray and model. We will only return results of a simple 'pick box' - not full triangle level detail. */ //============================================================================= Bool GrannyRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest) { if ((Get_Collision_Type() & raytest.CollisionType) == 0) return false; AABoxClass hbox=m_boundingBox; hbox.Transform(Get_Transform()); if (CollisionMath::Collide(raytest.Ray,hbox,raytest.Result)) { raytest.CollidedRenderObj = this; return true; } return false; } //============================================================================= // GrannyRenderObjClass::Class_ID //============================================================================= /** returns the class id, so the scene can tell what kind of render object it has. */ //============================================================================= Int GrannyRenderObjClass::Class_ID(void) const { return RenderObjClass::CLASSID_UNKNOWN; } //============================================================================= // GrannyRenderObjClass::Clone //============================================================================= /** Not used, but required virtual method. */ //============================================================================= RenderObjClass * GrannyRenderObjClass::Clone(void) const { assert(false); return NULL; } //============================================================================= // GrannyRenderObjClass::freeResources //============================================================================= /** Free any W3D resources associated with this object */ //============================================================================= Int GrannyRenderObjClass::freeResources(void) { // REF_PTR_RELEASE(m_stageZeroTexture); return 0; } /** W3D Method used to control the animation assocated with this render object. */ void GrannyRenderObjClass::Set_Animation( HAnimClass * motion, float frame, int anim_mode) { GrannyAnimClass *gAnim=(GrannyAnimClass*)motion; granny_animation *Animation=gAnim->Animation; if(Animation) { Int trackIndex=-1; //Check if this animation works with this model trackIndex=FindTrackGroupFor(Animation,m_modelInstance); if(trackIndex != -1) { //stop previous animations if (m_animationControl) { GrannyFreeControl(m_animationControl); m_animationControl=NULL; } m_animationControl = GrannyPlayControlledAnimation(frame / gAnim->FrameRate, Animation, trackIndex, m_modelInstance, 0); if (m_animationControl) { GrannySetControlSpeed(m_animationControl, 1.0f); //play at normal speed if (anim_mode == ANIM_MODE_LOOP) GrannySetControlLooping(m_animationControl, true); else GrannySetControlLooping(m_animationControl, false); } } } } //============================================================================= // GrannyRenderObjClass::Render //============================================================================= /** Draws this render object to the screen. */ //============================================================================= void GrannyRenderObjClass::Render(RenderInfoClass & rinfo) { TheGrannyRenderObjSystem->AddRenderObject(rinfo, this); return; //update the model GrannySetModelClock(m_modelInstance, LastSyncTime); LastSyncTime = WW3D::Get_Sync_Time()*0.001f; //convert to seconds GrannySampleModelAnimations(m_modelInstance, 0, GrannyGetModelBoneCount(m_modelInstance), GrannyGetModelLocalPose(m_modelInstance)); GrannyBuildModelWorldTransforms(m_modelInstance, 0, GrannyGetModelBoneCount(m_modelInstance), GrannyGetModelLocalPose(m_modelInstance), GrannyGetModelRootTransform(m_modelInstance), GrannyGetModelWorldPose(m_modelInstance)); Real *RootTransform = GrannyGetModelRootTransform(m_modelInstance); RootTransform[12] = 0; //X RootTransform[13] = 0; //Y RootTransform[13] = 0; //Z RootTransform[0] = ObjectScale; RootTransform[5] = ObjectScale; RootTransform[10] = ObjectScale; Int MeshCount = GrannyGetModelMeshCount(m_modelInstance); for(Int MeshIndex = 0; MeshIndex < MeshCount; ++MeshIndex) { granny_mesh_instance *Mesh = (granny_mesh_instance *)GrannyGetModelMeshCookie(m_modelInstance, MeshIndex); granny_mesh_model_binding *Binding = GrannyGetMeshModelBinding(Mesh); granny_pnt332_vertex *Vertices = 0; if(GrannyMeshIsRigid(Mesh)) { assert(0); //have not coded support for rigid meshes yet - only soft skinned supported. // granny_matrix_4x4 TempBuffer; // glMultMatrixf(GrannyGetRigidMeshTransform( // Binding, GrannyGetModelWorldPose(m_modelInstance), // (granny_real32 *)TempBuffer)); Vertices = (granny_pnt332_vertex *)GrannyGetMeshVertices(Mesh); } else { GrannyDeformMesh(Binding, GrannyGetModelWorldPose(m_modelInstance), 0, g_blendingBuffer); Vertices = g_blendingBuffer; } int GroupCount = GrannyGetMeshTriangleGroupCount(Mesh); {for(Int GroupIndex = 0; GroupIndex < GroupCount; ++GroupIndex) { granny_material_instance *Material = (granny_material_instance *) GrannyGetMeshMaterialCookie( Mesh, GrannyGetMeshTriangleGroupMaterialIndex( Mesh, GroupIndex)); if(Material) { Int TextureIndex; if(GrannyGetMaterialTextureIndexByType( Material, GrannyDiffuseColorTexture, &TextureIndex)) { granny_texture_instance *TextureInstance = (granny_texture_instance *)GrannyGetMaterialTextureCookie( Material, TextureIndex); if(TextureInstance) { DX8Wrapper::Set_Texture(0, (TextureClass *)GrannyGetTextureCookie(TextureInstance)); } else DX8Wrapper::Set_Texture(0, NULL); } } DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,m_vertexCount); { DynamicVBAccessClass::WriteLockClass lock(&vb_access); VertexFormatXYZNDUV2* vb=lock.Get_Formatted_Vertex_Array(); if(vb) { for (Int i=0; ix=Vertices->Position[0]; vb->y=Vertices->Position[1]; vb->z=Vertices->Position[2]; vb->nx=Vertices->Normal[0]; vb->ny=Vertices->Normal[1]; vb->nz=Vertices->Normal[2]; vb->diffuse=0xffffffff; vb->u1=Vertices->UV[0]; vb->v1=Vertices->UV[1]; vb++; Vertices++; } } } Int indexCount=GrannyGetMeshTriangleGroupIndexCount(Mesh, GroupIndex); UnsignedInt *indices=(UnsignedInt*)GrannyGetMeshTriangleGroupIndices(Mesh, GroupIndex); DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,indexCount); { DynamicIBAccessClass::WriteLockClass lock(&ib_access); unsigned short* ib=lock.Get_Index_Array(); if(ib) { for (Int i=0; iTextureCount; ++TextureIndex) { _splitpath(fileInfo->Textures[TextureIndex]->FromFileName, drive, dir, fname, ext ); TextureClass *TextureHandle=WW3DAssetManager::Get_Instance()->Get_Texture(strncat(fname,".tga",4)); granny_texture_instance *TextureInstance = GrannyInstantiateTexture(fileInfo->Textures[TextureIndex]); GrannySetTextureCookie(TextureInstance, (granny_uint32)TextureHandle); GrannyAddTextureToLibrary(TextureLibrary,TextureInstance); } //Calculate bounding box and find maximum number of vertices needed to render mesh. for (Int modelIndex=0; modelIndexModelCount; modelIndex++) { granny_model *sourceModel = fileInfo->Models[modelIndex]; if (stricmp(sourceModel->Name,"AABOX") == 0) { //found a collision box, copy out data int MeshCount = sourceModel->MeshBindingCount; if (MeshCount==1) { granny_mesh *sourceMesh = sourceModel->MeshBindings[0].Mesh; granny_pn33_vertex *Vertices = (granny_pn33_vertex *)sourceMesh->PrimaryVertexData->Vertices; Vector3 points[24]; assert (sourceMesh->PrimaryVertexData->VertexCount <= 24); for (Int boxVertex=0; boxVertexPrimaryVertexData->VertexCount; boxVertex++) { points[boxVertex].Set(Vertices[boxVertex].Position[0], Vertices[boxVertex].Position[1], Vertices[boxVertex].Position[2]); } box.Init(points,sourceMesh->PrimaryVertexData->VertexCount); } } else { //mesh is part of model meshCount = sourceModel->MeshBindingCount; for (Int meshIndex=0; meshIndexMeshBindings[meshIndex].Mesh; meshData[meshIndex].vertex=NULL; meshData[meshIndex].vertexCount=0; if (sourceMesh->PrimaryVertexData) { vertexCount+=sourceMesh->PrimaryVertexData->VertexCount; meshData[meshIndex].vertex=(granny_pnt332_vertex *)sourceMesh->PrimaryVertexData->Vertices; meshData[meshIndex].vertexCount=sourceMesh->PrimaryVertexData->VertexCount; } meshData[meshIndex].index=NULL; meshData[meshIndex].indexCount=NULL; if (sourceMesh->PrimaryTopology) { assert (sourceMesh->PrimaryTopology->GroupCount == 1); //should only have 1 material per mesh! granny_tri_material_group &sourceGroup = sourceMesh->PrimaryTopology->Groups[0]; // This is the texture index (relative to the array we just // built) for this group of triangles // SourceGroup.MaterialIndex; // This is the number of indices if(sourceMesh->PrimaryTopology->IndexCount) { meshData[meshIndex].index=(const UnsignedInt*)&sourceMesh->PrimaryTopology->Indices[3*sourceGroup.TriFirst]; // These are the indices for this group meshData[meshIndex].indexCount=3*sourceGroup.TriCount; indexCount += meshData[meshIndex].indexCount; //keep track of total indices in this model } } } } } ///@todo: Convert granny materials into W3D/D3D materials - now using default. // for(int MaterialIndex = 0; MaterialIndex < fileInfo->MaterialCount; ++MaterialIndex) // { // granny_material_instance *MaterialInstance = GrannyInstantiateMaterial(fileInfo->Materials[MaterialIndex]); // GrannyAutoBindAllMaterialTextures(MaterialInstance,TextureLibrary); // GrannyAddMaterialToLibrary(MaterialLibrary,MaterialInstance); // } GrannyAutoBindAllMaterials(MaterialLibrary, fileInfo,TextureLibrary); } //fileInfo if (fileInfo == NULL) { return NULL; } // ok, accept this model! GrannyPrototypeClass * hproto = NEW GrannyPrototypeClass(File); _splitpath(filename, drive, dir, fname, ext ); hproto->Set_Name(strcat(fname,ext)); hproto->setBoundingBox(box); hproto->setBoundingSphere(SphereClass(box.Center,box.Extent.Length())); hproto->setVertexCount(vertexCount); hproto->setIndexCount(indexCount); hproto->setMeshCount(meshCount); for (Int i=0; isetMeshData(meshData[i],i); return hproto; }//FILE return NULL; } GrannyLoaderClass::GrannyLoaderClass(void) { TextureLibrary = GrannyNewTextureLibrary(1 << 12); MaterialLibrary = GrannyNewMaterialLibrary(1 << 12); } GrannyLoaderClass::~GrannyLoaderClass(void) { GrannyFreeTextureLibrary(TextureLibrary); GrannyFreeMaterialLibrary(MaterialLibrary); TextureLibrary=NULL; MaterialLibrary=NULL; } GrannyAnimManagerClass::GrannyAnimManagerClass(void) { // Create the hash tables AnimPtrTable = NEW HashTableClass( 2048 ); MissingAnimTable = NEW HashTableClass( 2048 ); } GrannyAnimManagerClass::~GrannyAnimManagerClass(void) { Free_All_Anims(); delete AnimPtrTable; AnimPtrTable = NULL; delete MissingAnimTable; MissingAnimTable = NULL; } /** Release all loaded animations */ void GrannyAnimManagerClass::Free_All_Anims(void) { // Make an iterator, and release all ptrs GrannyAnimManagerIterator it( *this ); for( it.First(); !it.Is_Done(); it.Next() ) { GrannyAnimClass *anim = it.Get_Current_Anim(); anim->Release_Ref(); } // Then clear the table AnimPtrTable->Reset(); } /** Find animation in cache */ GrannyAnimClass * GrannyAnimManagerClass::Peek_Anim(const char * name) { return (GrannyAnimClass*)AnimPtrTable->Find( name ); } /** Get animation from cache and increment its reference count */ GrannyAnimClass * GrannyAnimManagerClass::Get_Anim(const char * name) { GrannyAnimClass * anim = Peek_Anim( name ); if ( anim != NULL ) { anim->Add_Ref(); } return anim; } /** Add animation to cache */ Bool GrannyAnimManagerClass::Add_Anim(GrannyAnimClass *new_anim) { WWASSERT (new_anim != NULL); // Increment the refcount on the new animation and add it to our table. new_anim->Add_Ref (); AnimPtrTable->Add( new_anim ); return true; } /* ** Missing Anims ** ** The idea here, allow the system to register which anims are determined to be missing ** so that if they are asked for again, we can quickly return NULL, without searching the ** disk again. */ void GrannyAnimManagerClass::Register_Missing( const char * name ) { MissingAnimTable->Add( NEW MissingAnimClass( name ) ); } Bool GrannyAnimManagerClass::Is_Missing( const char * name ) { return ( MissingAnimTable->Find( name ) != NULL ); } /** Load an animation from disk and add to cache */ int GrannyAnimManagerClass::Load_Anim(const char * name) { GrannyAnimClass * newanim = NEW GrannyAnimClass; if (newanim == NULL) { goto Error; } SET_REF_OWNER( newanim ); if (newanim->Load_W3D(name) != GrannyAnimClass::OK) { // load failed! newanim->Release_Ref(); goto Error; } else if (Peek_Anim(newanim->Get_Name()) != NULL) { // duplicate exists! newanim->Release_Ref(); // Release the one we just loaded goto Error; } else { Add_Anim( newanim ); newanim->Release_Ref(); } return 0; Error: return 1; } /* ** Iterator converter from HashableClass to GrannyAnimClass */ GrannyAnimClass * GrannyAnimManagerIterator::Get_Current_Anim( void ) { return (GrannyAnimClass *)Get_Current(); } GrannyAnimClass::GrannyAnimClass(void) : NumFrames(0), FrameRate(0), File(0), Animation(0) { Name[0]='\0'; } /** GrannyAnimClass::~GrannyAnimClass -- Destructor */ GrannyAnimClass::~GrannyAnimClass(void) { GrannyFreeFile(File); } /** Read granny data from disk */ int GrannyAnimClass::Load_W3D(const char *name) { granny_file *file=NULL; file = GrannyReadEntireFile(name); if (file) { granny_file_info *fileInfo; fileInfo = GrannyGetFileInfo(file); if (fileInfo && fileInfo->AnimationCount) { Animation = fileInfo->Animations[0]; File = file; strcpy(Name,name); FrameRate = 1.0f/Animation->TimeStep; NumFrames = Animation->Duration / Animation->TimeStep; return OK; } else GrannyFreeFile(File); //no animations found } return LOAD_ERROR; } GrannyLoaderClass _GrannyLoader; GrannyRenderObjSystem *TheGrannyRenderObjSystem=NULL; /** Adds render object to a queue with other objects using the same material. These queues will be flushed at the end of the frame. */ void GrannyRenderObjSystem::AddRenderObject(RenderInfoClass & rinfo, GrannyRenderObjClass *robj) { if (m_renderObjectCount >= MAX_VISIBLE_GRANNY_MODELS) { assert (m_renderObjectCount < MAX_VISIBLE_GRANNY_MODELS); return; //can't add any more granny models this frame. } for (Int i=0; im_prototype.m_file == robj->m_prototype.m_file) { //found model with same prototype. Add new model to same list. robj->m_nextSystem=m_renderStateModelList[i].list; m_renderStateModelList[i].list=robj; if (rinfo.light_environment) m_renderLocalLightEnv[m_renderObjectCount]=*rinfo.light_environment; m_renderObjectCount++; return; } } //Found a new render state assert (m_renderStateCount < MAX_GRANNY_RENDERSTATES); if (m_renderStateCount > MAX_GRANNY_RENDERSTATES) return; //reached maximum number of render states //store this objects lighting information for use later during drawing. if (rinfo.light_environment) m_renderLocalLightEnv[m_renderObjectCount]=*rinfo.light_environment; m_renderStateModelList[m_renderStateCount++].list=robj; robj->m_nextSystem=NULL; m_renderObjectCount++; } /** Draws all the granny render objects that were rendered in the last frame. Drawing is done in render state order to reduce overhead. */ void GrannyRenderObjSystem::Flush(void) { GrannyRenderObjClass *robj; granny_model_instance *modelInstance; Bool setMaterial; Int modelCount=0; Int indexCount; //per model index count for (Int i=0; im_modelInstance; //update the model GrannySetModelClock(modelInstance,robj->LastSyncTime); robj->LastSyncTime = WW3D::Get_Sync_Time()*0.001f; //convert to seconds GrannySampleModelAnimations(modelInstance, 0, GrannyGetModelBoneCount(modelInstance), GrannyGetModelLocalPose(modelInstance)); GrannyBuildModelWorldTransforms(modelInstance, 0, GrannyGetModelBoneCount(modelInstance), GrannyGetModelLocalPose(modelInstance), GrannyGetModelRootTransform(modelInstance), GrannyGetModelWorldPose(modelInstance)); Real *RootTransform = GrannyGetModelRootTransform(modelInstance); RootTransform[12] = 0; //X RootTransform[13] = 0; //Y RootTransform[13] = 0; //Z RootTransform[0] = robj->ObjectScale; RootTransform[5] = robj->ObjectScale; RootTransform[10] = robj->ObjectScale; Int MeshCount = GrannyGetModelMeshCount(modelInstance); for(Int MeshIndex = 0; MeshIndex < MeshCount; ++MeshIndex) { granny_mesh_instance *Mesh = (granny_mesh_instance *)GrannyGetModelMeshCookie(modelInstance, MeshIndex); granny_mesh_model_binding *Binding = GrannyGetMeshModelBinding(Mesh); granny_pnt332_vertex *Vertices = 0; if(GrannyMeshIsRigid(Mesh)) { assert(0); //have not coded support for rigid meshes yet - only soft skinned supported. // granny_matrix_4x4 TempBuffer; // glMultMatrixf(GrannyGetRigidMeshTransform( // Binding, GrannyGetModelWorldPose(modelInstance), // (granny_real32 *)TempBuffer)); Vertices = (granny_pnt332_vertex *)GrannyGetMeshVertices(Mesh); } else { GrannyDeformMesh(Binding, GrannyGetModelWorldPose(modelInstance), 0, g_blendingBuffer); Vertices = g_blendingBuffer; } int GroupCount = GrannyGetMeshTriangleGroupCount(Mesh); for(Int GroupIndex = 0; GroupIndex < GroupCount; ++GroupIndex) { granny_material_instance *Material = (granny_material_instance *) GrannyGetMeshMaterialCookie( Mesh, GrannyGetMeshTriangleGroupMaterialIndex( Mesh, GroupIndex)); if(setMaterial && Material) { Int TextureIndex; if(GrannyGetMaterialTextureIndexByType( Material, GrannyDiffuseColorTexture, &TextureIndex)) { granny_texture_instance *TextureInstance = (granny_texture_instance *)GrannyGetMaterialTextureCookie( Material, TextureIndex); if(TextureInstance) { DX8Wrapper::Set_Texture(0, (TextureClass *)GrannyGetTextureCookie(TextureInstance)); } else DX8Wrapper::Set_Texture(0, NULL); } } DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,robj->m_vertexCount); { DynamicVBAccessClass::WriteLockClass lock(&vb_access); VertexFormatXYZNDUV2* vb=lock.Get_Formatted_Vertex_Array(); if(vb) { for (Int i=0; im_vertexCount; i++) { vb->x=Vertices->Position[0]; vb->y=Vertices->Position[1]; vb->z=Vertices->Position[2]; vb->nx=Vertices->Normal[0]; vb->ny=Vertices->Normal[1]; vb->nz=Vertices->Normal[2]; vb->diffuse=0xffffffff; vb->u1=Vertices->UV[0]; vb->v1=Vertices->UV[1]; vb++; Vertices++; } } } if (setMaterial) { //we started using a new material queue - really a new model prototype //so reset all relevant data. indexCount=GrannyGetMeshTriangleGroupIndexCount(Mesh, GroupIndex); UnsignedInt *indices=(UnsignedInt*)GrannyGetMeshTriangleGroupIndices(Mesh, GroupIndex); DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,indexCount); { DynamicIBAccessClass::WriteLockClass lock(&ib_access); unsigned short* ib=lock.Get_Index_Array(); if(ib) { for (Int i=0; im_prototype.m_vertexMaterial); DX8Wrapper::Set_Shader(ShaderClass::_PresetOpaqueShader); setMaterial=false; //don't set material again unless it changes. DX8Wrapper::Set_Index_Buffer(ib_access,0); } Matrix3D tm(robj->Transform); DX8Wrapper::Set_Light_Environment(&m_renderLocalLightEnv[modelCount]); DX8Wrapper::Set_Transform(D3DTS_WORLD,tm); DX8Wrapper::Set_Vertex_Buffer(vb_access); DX8Wrapper::Draw_Triangles( 0,indexCount/3, 0, robj->m_vertexCount); //draw a quad, 2 triangles, 4 verts } } robj=robj->m_nextSystem; //move to next object using this material modelCount++; }//while } //reset for next frame m_renderObjectCount=0; m_renderStateCount=0; } #if 0 ///@todo: Will have to implement an optimized granny rendering system //============================================================================= // GrannyRenderObjClassSystem::GrannyRenderObjClassSystem //============================================================================= /** Constructor. Just nulls out some variables. */ //============================================================================= GrannyRenderObjClassSystem::GrannyRenderObjClassSystem() { m_usedModules = NULL; m_freeModules = NULL; m_indexBuffer = NULL; m_vertexMaterialClass = NULL; m_vertexBuffer = NULL; } //============================================================================= // GrannyRenderObjClassSystem::~GrannyRenderObjClassSystem //============================================================================= /** Destructor. Free all pre-allocated track laying render objects*/ //============================================================================= GrannyRenderObjClassSystem::~GrannyRenderObjClassSystem( void ) { // free all data shutdown(); m_vertexMaterialClass=NULL; } //============================================================================= // GrannyRenderObjClassSystem::ReAcquireResources //============================================================================= /** (Re)allocates all W3D assets after a reset.. */ //============================================================================= void GrannyRenderObjClassSystem::ReAcquireResources(void) { // just for paranoia's sake. REF_PTR_RELEASE(m_indexBuffer); REF_PTR_RELEASE(m_vertexBuffer); //Create static index buffers. These will index the vertex buffers holding the map. m_indexBuffer=NEW_REF(DX8IndexBufferClass,(32*6)); // Fill up the IB { DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexBuffer); UnsignedShort *ib=lockIdxBuffer.Get_Index_Array(); } ///@todo: Allocating double sized buffer than really needed... but things go bad otherwise. Figure out why! m_vertexBuffer=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,1*2,DX8VertexBufferClass::USAGE_DYNAMIC)); } //============================================================================= // GrannyRenderObjClassSystem::ReleaseResources //============================================================================= /** (Re)allocates all W3D assets after a reset.. */ //============================================================================= void GrannyRenderObjClassSystem::ReleaseResources(void) { REF_PTR_RELEASE(m_indexBuffer); REF_PTR_RELEASE(m_vertexBuffer); // Note - it is ok to not release the material, as it is a w3d object that // has no dx8 resources. jba. } //============================================================================= // GrannyRenderObjClassSystem::init //============================================================================= /** initialize the system, allocate all the render objects we will need */ //============================================================================= void GrannyRenderObjClassSystem::init() { const Int numModules=TheGlobalData->m_maxTerrainTracks; Int i; GrannyRenderObjClass *mod; ReAcquireResources(); //go with a preset material for now. m_vertexMaterialClass=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE); //use a multi-texture shader: (text1*diffuse)*text2. m_shaderClass = ShaderClass::_PresetAlphaShader;//_PresetATestSpriteShader;//_PresetOpaqueShader; // we cannot initialize a system that is already initialized if( m_freeModules || m_usedModules ) { // system already online! assert( 0 ); return; } // end if // allocate our modules for this system for( i = 0; i < numModules; i++ ) { mod = NEW_REF( GrannyRenderObjClass, () ); if( mod == NULL ) { // unable to allocate modules needed assert( 0 ); return; } // end if mod->m_prevSystem = NULL; mod->m_nextSystem = m_freeModules; if( m_freeModules ) m_freeModules->m_prevSystem = mod; m_freeModules = mod; } // end for i } // end init //============================================================================= // GrannyRenderObjClassSystem::shutdown //============================================================================= /** Shutdown and free all memory for this system */ //============================================================================= void GrannyRenderObjClassSystem::shutdown( void ) { REF_PTR_RELEASE(m_indexBuffer); REF_PTR_RELEASE(m_vertexMaterialClass); REF_PTR_RELEASE(m_vertexBuffer); } // end shutdown //============================================================================= // GrannyRenderObjClassSystem::update //============================================================================= /** Update the state of all active track marks - fade, expire, etc. */ //============================================================================= void GrannyRenderObjClassSystem::update() { Int iTime=timeGetTime(); } //============================================================================= // GrannyRenderObjClassSystem::flush //============================================================================= /** Draw all active track marks for this frame */ //============================================================================= void GrannyRenderObjClassSystem::flush() { Int diffuseLight; GrannyRenderObjClass *mod=0; // adjust shading for time of day. Real shadeR, shadeG, shadeB; shadeR = TheGlobalData->m_terrainAmbientRed; shadeG = TheGlobalData->m_terrainAmbientGreen; shadeB = TheGlobalData->m_terrainAmbientBlue; shadeR += TheGlobalData->m_terrainDiffuseRed/2; shadeG += TheGlobalData->m_terrainDiffuseGreen/2; shadeB += TheGlobalData->m_terrainDiffuseBlue/2; shadeR*=255.0f; shadeG*=255.0f; shadeB*=255.0f; diffuseLight=(int)shadeB | ((int)shadeG << 8) | ((int)shadeR << 16); //check if there is anything to draw and fill vertex buffer { DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexBuffer); VertexFormatXYZDUV1 *verts = (VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array(); }//edges to flush //draw the filled vertex buffers { ShaderClass::Invalidate(); DX8Wrapper::Set_Material(m_vertexMaterialClass); DX8Wrapper::Set_Shader(m_shaderClass); DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0); DX8Wrapper::Set_Vertex_Buffer(m_vertexBuffer); Matrix3D tm(mod->Transform); DX8Wrapper::Set_Transform(D3DTS_WORLD,tm); DX8Wrapper::Set_Index_Buffer_Index_Offset(0); DX8Wrapper::Draw_Triangles( 0,2, 0, 1*2); } //there are some edges to render in pool. } //============================================================================= // GrannyRenderObjClassSystem::createRenderObj //============================================================================= /** Creates an instance of a W3D render object using the specified granny */ /* model. If the model doesn't exist yet, it will be loaded from disk. */ //============================================================================= RenderObjClass *GrannyRenderObjClassSystem::createRenderObj(const char * name) { return NULL; } GrannyRenderObjClassSystem *TheGrannyRenderObjClassSystem=NULL; ///< singleton for track drawing system. #endif //end of GrannyRenderObjClassSystem #endif //INCLUDE_GRANNY_IN_BUILD