#include #include #include #include #include "group.h" #include "scene.h" #include "dma.h" #include "vif.h" #include "vu1.h" #include "gif.h" #include "gs.h" #include "texture.h" #include "material.h" #include "mesh.h" #include "vu1code.h" #include "dmacalls.h" #include "render.h" #include #include #include #include namespace NxPs2 { #ifdef __NOPT_ASSERT__ extern uint32 gPassMask1; // 1<<6 | 1<<7 (0x40, 0x80) extern uint32 gPassMask0; // 1<<6 | 1<<7 (0x40, 0x80) #endif #if 0 //---------------------------------------------------------------------------------------- // Mesh Group Functions //---------------------------------------------------------------------------------------- sMesh *sGroup::GetMeshArray() { return &(Meshes[FirstMeshIndex]); } sGroup* NewMeshGroup(uint32 group_ID, uint32 num_meshes) { uint32 i; int Group = -1; // resolve ID for (i=0; ipWeights; s_pWeightIndices = pParams->pWeightIndices; s_pBoneScales = pParams->pBoneScales; s_pBonePositions = pParams->pBonePositions; s_currentVertIndex = 0; } void DisableMeshScaling() { s_meshScalingEnabled = false; s_pWeights = NULL; s_pWeightIndices = NULL; s_pBoneScales = NULL; s_pBonePositions = NULL; s_currentVertIndex = 0; } //---------------------------------------------------------------------------------------- // L O A D M E S H E S //---------------------------------------------------------------------------------------- void * LoadMeshes(void *pFile, sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset) { int i; // initialise sScene bounding sphere to avoid rewriting it multiple times pScene->Sphere[3] = -1.0f; int skinnedVertexCount=0; // iterate over mesh groups for (i=0; iNumGroups; i++) { pFile = LoadMeshGroup(pFile, pScene, IsSkin, IsInstanceable, texDictOffset, skinnedVertexCount); } // Check hierarchy data int num_hier = -1; MEM_Read(&num_hier, sizeof(int), 1, pFile); Dbg_MsgAssert(num_hier == 0, ("num_hier = %d", num_hier)); // clear it out for future use DisableMeshScaling(); return pFile; } void *LoadMeshGroup(void *pFile, sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset, int& skinnedVertexCount) { sMesh *pMesh; sMaterial *pMat; sGroup *pGroup; uint32 MaterialChecksum, GroupChecksum; int NumMeshesThisGroup,i,j; Mth::Vector sphere; // get group checksum and number of meshes MEM_Read(&GroupChecksum, sizeof(uint32), 1, pFile); MEM_Read(&NumMeshesThisGroup, sizeof(uint32), 1, pFile); GroupChecksum += texDictOffset; // resolve checksum for (pGroup=sGroup::pHead; pGroup; pGroup=pGroup->pNext) if (pGroup->Checksum == GroupChecksum) break; Dbg_MsgAssert(pGroup, ("Couldn't find group checksum #%0X\n", GroupChecksum)); // set mesh info in the group pGroup->pMeshes = pScene->pMeshes+pScene->NumMeshes; pGroup->NumMeshes = NumMeshesThisGroup; // loop over meshes for (i=0,pMesh=pGroup->pMeshes; iChecksum, sizeof(uint32), 1, pFile); // get object sphere for mesh version 2 onwards if (sMesh::Version >= 2) { unsigned int lod_stuff_not_used_here; MEM_Read(&lod_stuff_not_used_here, sizeof(unsigned int), 1, pFile); MEM_Read(&lod_stuff_not_used_here, sizeof(unsigned int), 1, pFile); // Just process parent/child stuff int num_child, hier_data; MEM_Read(&hier_data, sizeof(unsigned int), 1, pFile); MEM_Read(&num_child, sizeof(unsigned int), 1, pFile); for (int j = 0; j < num_child; j++) { MEM_Read(&hier_data, sizeof(unsigned int), 1, pFile); } MEM_Read(&sphere, sizeof(Mth::Vector), 1, pFile); // write sphere into sScene if (pScene->Sphere[3] < 0.0f) pScene->Sphere = sphere; #if 0 // GJ: the following assertion is no longer valid... // there are occasionally multiple NxObjects inside a // single MDL file, and each mesh will have a different // bounding sphere depending on which NxObject is belongs // to... Mike agreed to rewrite the bounding sphere // calculation code so that all meshes will have a // new bounding sphere that encompasses all the individual // meshes' bounding spheres... // check object spheres from different meshes match ok Dbg_MsgAssert((pScene->Sphere[0]==sphere[0] && pScene->Sphere[1]==sphere[1] && pScene->Sphere[2]==sphere[2] && pScene->Sphere[3]==sphere[3]), ("object bounding sphere differs between meshes")); #else // ...so, if the object spheres differ, just combine them... if (pScene->Sphere[0]!=sphere[0] || pScene->Sphere[1]!=sphere[1] || pScene->Sphere[2]!=sphere[2] || pScene->Sphere[3]!=sphere[3]) { //printf("\n\n"); //printf("**********************************************\n"); //printf("* *\n"); //printf("* Tell Mike if you see this line printing! *\n"); //printf("* *\n"); //printf("**********************************************\n"); //printf("\n\n"); //printf("combining spheres (%g,%g,%g,%g) and (%g,%g,%g,%g)\n", // pScene->Sphere[0], pScene->Sphere[1], pScene->Sphere[2], pScene->Sphere[3], // sphere[0], sphere[1], sphere[2], sphere[3]); Mth::Vector x0, x1, d; if (pScene->Sphere[3] > sphere[3]) { x0 = pScene->Sphere; x1 = sphere; } else { x0 = sphere; x1 = pScene->Sphere; } float r0=x0[3], r1=x1[3]; // (x0,r0) is the larger sphere, (x1,r1) the smaller // d is the vector between centres d = x1 - x0; // square of distance between centres float D2 = DotProduct(d,d); // (r0-r1)^2 float R2 = (r0-r1)*(r0-r1); // m = max { (r1-r0+|d|)/2, 0 } float m = 0.0f; if (R2-D2 < 0) { m = (r1-r0 + sqrtf(D2)) * 0.5f; } pScene->Sphere = x0 + m * d; pScene->Sphere[3] = r0 + m; //printf("result is (%g,%g,%g,%g)\n", // pScene->Sphere[0], pScene->Sphere[1], pScene->Sphere[2], pScene->Sphere[3]); //Dbg_MsgAssert( 0, ( "Please tell Mike if you see this assert firing off!" ) ); } #endif } // get material checksum and look it up MEM_Read(&MaterialChecksum, sizeof(uint32), 1, pFile); for (j=0,pMat=pScene->pMaterials; jNumMaterials; j++,pMat++) if (pMat->Checksum == MaterialChecksum) break; Dbg_MsgAssert(pMat->Checksum==MaterialChecksum, ("couldn't find material with checksum %08X\n", MaterialChecksum)); pMesh->pMaterial = pMat; // get mesh flags MEM_Read(&pMesh->Flags, sizeof(uint32), 1, pFile); // make it active pMesh->SetActive(true); // get bounding volume data MEM_Read(&pMesh->ObjBox, sizeof(float) * 3, 1, pFile); MEM_Read(&pMesh->Box, sizeof(float) * 3, 1, pFile); MEM_Read(&pMesh->Sphere, sizeof(float) * 4, 1, pFile); // pass number if (sMesh::Version >= 5) { MEM_Read(&pMesh->Pass, sizeof(int), 1, pFile); } // material name if (sMesh::Version >= 6) { MEM_Read(&pMesh->MaterialName, sizeof(int), 1, pFile); } // import vertices of this mesh pFile = LoadVertices(pFile, pMesh, pMat, pGroup, skinnedVertexCount); // end the dma tag dma::EndTag(); // end the model subroutine pMesh->pSubroutine = dma::EndSub3D(); } // add to total number for scene pScene->NumMeshes += NumMeshesThisGroup; return pFile; } //---------------------------------------------------------------------------------------- // L O A D V E R T I C E S //---------------------------------------------------------------------------------------- int UnpackOffset; void * LoadVertices(void *pFile, sMesh *pMesh, sMaterial *pMat, sGroup *pGroup, int& skinnedIndexCount) { uint32 NumVertices; uint REGS, NREG, PRIM, ADDR; sTexture *pTex; uint32 i; uint8 VertexData[64]; int STOffset, ColourOffset, NormalOffset, SkinOffset, XYZOffset, VertexSize; int texture_width=0, texture_height=0; // get number of vertices for this mesh MEM_Read(&NumVertices, sizeof(uint32), 1, pFile); // skip mesh if it has no vertices if (NumVertices==0) return pFile; // work out giftag fields REGS = gs::XYZF2; NREG = 1; PRIM = TRISTRIP|ABE|FGE; ADDR = VU1_ADDR(Proj); if (pMesh->Flags & MESHFLAG_COLOURS) { REGS = REGS<<4 | gs::RGBAQ; NREG++; } if (pMesh->Flags & MESHFLAG_NORMALS) { if (pMat->Flags & MATFLAG_ENVIRONMENT) { ADDR = VU1_ADDR(Refl); REGS = REGS<<4 | gs::ST; PRIM |= TME; } else { REGS = REGS<<4 | gs::NOP; } NREG++; } if ((pMesh->Flags & MESHFLAG_TEXTURE) && !(pMat->Flags & MATFLAG_ENVIRONMENT)) { REGS = REGS<<4 | gs::ST; NREG++; PRIM |= TME; ADDR = VU1_ADDR(PTex); } if (pMat->Flags & MATFLAG_SMOOTH) PRIM |= IIP; // override everything for skinned models! if (pMesh->Flags & MESHFLAG_SKINNED) { if (pMat->RegCLAMP & PackCLAMP(3,3,0,0,0,0)) { // clamped case, send texture coords as 32 bits floating point STs REGS = gs::XYZ2<<16 | gs::RGBAQ<<12 | gs::NOP<<8 | gs::NOP<<4 | gs::ST; NREG = 5; PRIM |= TME; ADDR = VU1_ADDR(Skin); } else { // standard case, non-clamped, compress texture coords to 16 bit fixed point UVs REGS = gs::XYZ2<<16 | gs::RGBAQ<<12 | gs::NOP<<8 | gs::NOP<<4 | gs::UV; NREG = 5; PRIM |= TME|FST; ADDR = VU1_ADDR(Skin); } } // output GS context for material gs::BeginPrim(REL,0,0); gs::Reg1(gs::ALPHA_1, pMat->RegALPHA); gs::Reg1(gs::TEST_1, PackTEST(1,AGEQUAL,pMat->Aref,KEEP,0,0,1,ZGEQUAL)); // texture registers if necessary if (pMat->Flags & MATFLAG_TEXTURED) { pTex = pMat->pTex; gs::Reg1(gs::TEX0_1, pTex->RegTEX0); gs::Reg1(gs::TEX1_1, pMat->RegTEX1); gs::Reg1(gs::CLAMP_1, pMat->RegCLAMP); if (pTex->MXL > 0) { gs::Reg1(gs::MIPTBP1_1,pTex->RegMIPTBP1); if (pTex->MXL > 3) { gs::Reg1(gs::MIPTBP2_1,pTex->RegMIPTBP2); } } // extract texture dimensions texture_width = 1 << ((pTex->RegTEX0 >> 26) & 0xF); texture_height = 1 << ((pTex->RegTEX0 >> 30) & 0xF); } gs::EndPrim(0); // set maximum vu buffer size vu1::MaxBuffer = pMesh->Flags&MESHFLAG_SKINNED ? 240 : 240; // begin a batch of vertices BeginModelMultiPrim(REGS, NREG, PRIM, NumVertices, ADDR); // work out vertex size and data offsets VertexSize = (pMesh->Flags & MESHFLAG_SKINNED) ? 8 : 16; XYZOffset = SkinOffset = NormalOffset = ColourOffset = STOffset = 0; if (pMesh->Flags & MESHFLAG_SKINNED) { VertexSize += 8; XYZOffset += 8; } if (pMesh->Flags & MESHFLAG_NORMALS) { VertexSize += 4; XYZOffset += 4; SkinOffset += 4; } if (pMesh->Flags & MESHFLAG_COLOURS) { VertexSize += 4; XYZOffset += 4; SkinOffset += 4; NormalOffset += 4; } if (pMesh->Flags & MESHFLAG_TEXTURE) { int size = (pMesh->Flags & MESHFLAG_SKINNED) ? 4 : 8; VertexSize += size; XYZOffset += size; SkinOffset += size; NormalOffset += size; ColourOffset += size; } // loop over vertices for (i=0; iFlags & MESHFLAG_SKINNED) { // texture coords UnpackOffset = 1; if ((pMesh->Flags & MESHFLAG_TEXTURE) && !(pMat->Flags & MATFLAG_ENVIRONMENT)) { if (pMat->RegCLAMP & PackCLAMP(3,3,0,0,0,0)) { VertexSTFloat(VertexData+STOffset); } else { VertexUV(VertexData+STOffset, texture_width<<4, texture_height<<4); } } // weights UnpackOffset = 2; VertexWeights(VertexData+SkinOffset); // normal and transform offsets UnpackOffset = 3; VertexSkinNormal(VertexData+NormalOffset, VertexData+SkinOffset+4); // colour UnpackOffset = 4; if (pMesh->Flags & MESHFLAG_COLOURS) { VertexRGBA(VertexData+ColourOffset); } // position UnpackOffset = 5; VertexXYZ(VertexData+XYZOffset, true, skinnedIndexCount); } else { // texture coords if ((pMesh->Flags & MESHFLAG_TEXTURE) && !(pMat->Flags & MATFLAG_ENVIRONMENT)) { VertexST16(VertexData+STOffset); } // normal normals if (pMesh->Flags & MESHFLAG_NORMALS) { VertexNormal(VertexData+NormalOffset); } // colour if (pMesh->Flags & MESHFLAG_COLOURS) { VertexRGBA(VertexData+ColourOffset); } // position VertexXYZ(VertexData+XYZOffset, false, skinnedIndexCount); } EndVertex(); // current vert being processed s_currentVertIndex++; } // accumulate number of vertices sMesh::TotalNumVertices += NumVertices; // finish batch of vertices EndModelMultiPrim(); return pFile; } //--------------------------------------------------------- // M O D E L P R I M C O N S T R U C T I O N //--------------------------------------------------------- uint MeshRegs, MeshNReg, MeshPrim, MeshNLoop, MeshAddr; uint NumVerticesThisBuffer, NumOutputVertices; uint32 *pColour; sint32 *pST32; sint16 *pST16, *pVertex, *pNormal, *pSkinData; uint8 *pWeights; uint16 *pUV; // begin model prim (i.e. begin a vertex packet) void BeginModelPrim(uint32 Regs, uint NReg, uint Prim, uint Pre, uint Addr) { vif::STCYCL(1,NReg); vif::UNPACK(0,V4_32,1,REL,UNSIGNED,0); gif::BeginTag1(Regs, NReg, PACKED, Prim, Pre, Addr); } // end model prim (i.e. end a vertex packet) void EndModelPrim(uint Eop) { gif::EndTag1(Eop); } // begin model prim (i.e. begin a vertex packet) void BeginModelPrimImmediate(uint32 Regs, uint NReg, uint Prim, uint Pre, uint Addr) { vif::STCYCL(1,NReg); vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0); gif::BeginTag1(Regs, NReg, PACKED, Prim, Pre, Addr); } // end model prim (i.e. end a vertex packet) void EndModelPrimImmediate(uint Eop) { gif::EndTag1(Eop); } // begin a model prim which may span multiple VU1 buffers void BeginModelMultiPrim(uint32 Regs, uint NReg, uint Prim, uint NLoop, uint Addr) { // record tag info MeshRegs = Regs; MeshNReg = NReg; MeshPrim = Prim; MeshNLoop = NLoop; MeshAddr = Addr; // work out number of vertices to unpack first // (and go ahead with it for now, even if there's only room for 1 vertex) NumVerticesThisBuffer = (vu1::MaxBuffer - ((vu1::Loc-vu1::Buffer)&0x3FF) - 1) / NReg; if (NumVerticesThisBuffer > MeshNLoop) NumVerticesThisBuffer = MeshNLoop; BeginModelPrim(Regs, NReg, Prim, 1, Addr); NumOutputVertices = 0; } void EndModelMultiPrim(void) { EndModelPrim(1); vif::MSCAL(VU1_ADDR(Parser)); vif::FLUSH(); } int RestartTristrip=0; void BeginVertex(sMesh *pMesh, sMaterial *pMat) { if (NumOutputVertices == NumVerticesThisBuffer) { // end the model prim if ((pMesh->Flags & MESHFLAG_SKINNED) && (768-((vu1::Buffer+vif::UnpackSize*vif::CycleLength+1)&1023) < vu1::MaxBuffer)) { EndModelPrim(0); vif::UNPACK(0, V4_32, 1, REL, UNSIGNED, 0); gif::Tag1(0, 0, PACKED, 0, 0, 1, 0, VU1_ADDR(Jump)); // reset address to 0 vu1::Loc = 0; // start VU1 execution vif::MSCAL(VU1_ADDR(Parser)); vif::FLUSH(); } else { EndModelPrim(1); // start VU1 execution vif::MSCAL(VU1_ADDR(Parser)); vif::FLUSH(); } // reduce number still to go MeshNLoop -= NumOutputVertices; // work out number of vertices to unpack next NumVerticesThisBuffer = vu1::MaxBuffer / MeshNReg; if (NumVerticesThisBuffer > MeshNLoop) NumVerticesThisBuffer = MeshNLoop; // start a new buffalo'd // add a dummy gs context gs::BeginPrim(REL,0,0); gs::Reg1(gs::A_D_NOP,0); gs::EndPrim(0); BeginModelPrim(MeshRegs, MeshNReg, MeshPrim, 1, MeshAddr); NumOutputVertices = 0; // signal tristrip restart RestartTristrip=1; } } void VertexST16(uint8 *pData) { static sint16 s0,t0,s1,t1; if (NumOutputVertices==0) { vif::BeginUNPACK(0, V2_16, REL, SIGNED, UnpackOffset); pST16 = (sint16 *)dma::pLoc; if (RestartTristrip) { *pST16++ = s0; *pST16++ = t0; *pST16++ = s1; *pST16++ = t1; } dma::pLoc = (uint8 *)pST16 + NumVerticesThisBuffer * 4; vif::EndUNPACK(); } *pST16++ = (sint16) (int) (((float *)pData)[0] * 4096.0f); *pST16++ = (sint16) (int) (((float *)pData)[1] * 4096.0f); s0=s1, s1=pST16[-2]; t0=t1, t1=pST16[-1]; UnpackOffset++; } void VertexSTFloat(uint8 *pData) { static sint32 s0,t0,s1,t1; if (NumOutputVertices==0) { vif::STMASK(0xE0); vif::BeginUNPACK(1, V2_32, REL, SIGNED, UnpackOffset); pST32 = (sint32 *)dma::pLoc; if (RestartTristrip) { *pST32++ = s0; *pST32++ = t0; *pST32++ = s1; *pST32++ = t1; } dma::pLoc = (uint8 *)pST32 + NumVerticesThisBuffer * 8; vif::EndUNPACK(); } *(float *)pST32++ = (float)((sint16 *)pData)[0] * 0.000244140625f; *(float *)pST32++ = (float)((sint16 *)pData)[1] * 0.000244140625f; s0=s1, s1=pST32[-2]; t0=t1, t1=pST32[-1]; UnpackOffset++; } void VertexUV(uint8 *pData, int textureWidth16, int textureHeight16) { static uint16 u0,v0,u1,v1; if (NumOutputVertices==0) { vif::BeginUNPACK(0, V2_16, REL, UNSIGNED, UnpackOffset); pUV = (uint16 *)dma::pLoc; if (RestartTristrip) { *pUV++ = u0; *pUV++ = v0; *pUV++ = u1; *pUV++ = v1; } dma::pLoc = (uint8 *)pUV + NumVerticesThisBuffer * 4; vif::EndUNPACK(); } *pUV++ = (uint16)((int)((float)((sint16 *)pData)[0] * (float)textureWidth16 * 0.000244140625f) + 0x00002000); *pUV++ = (uint16)((int)((float)((sint16 *)pData)[1] * (float)textureHeight16 * 0.000244140625f) + 0x00002000); u0=u1, u1=pUV[-2]; v0=v1, v1=pUV[-1]; UnpackOffset++; } void VertexNormal(uint8 *pData) { static sint16 nx0,ny0,nz0,nx1,ny1,nz1; if (NumOutputVertices==0) { vif::BeginUNPACK(0, V3_16, REL, SIGNED, UnpackOffset); pNormal = (sint16 *)dma::pLoc; if (RestartTristrip) { *pNormal++ = nx0; *pNormal++ = ny0; *pNormal++ = nz0; *pNormal++ = nx1; *pNormal++ = ny1; *pNormal++ = nz1; } dma::pLoc = (uint8 *)pNormal + NumVerticesThisBuffer * 6; vif::EndUNPACK(); } *pNormal++ = ((sint16 *)pData)[0]; *pNormal++ = ((sint16 *)pData)[1]; //*pNormal++ = ((sint16 *)pData)[2]; sint16 coord = (sint16)sqrtf(32767.0f*32767.0f - (float)((sint16 *)pData)[0] * (float)((sint16 *)pData)[0] - (float)((sint16 *)pData)[1] * (float)((sint16 *)pData)[1]); if (((sint16 *)pData)[0] & 0x0001) { coord = -coord; } *pNormal++ = coord; nx0=nx1, nx1=pNormal[-3]; ny0=ny1, ny1=pNormal[-2]; nz0=nz1, nz1=pNormal[-1]; UnpackOffset++; } #if 0 void VertexWeights(uint8 *pData) { static sint16 wa0,wb0,wc0,wa1,wb1,wc1; if (NumOutputVertices==0) { vif::BeginUNPACK(0, V3_16, REL, SIGNED, UnpackOffset); pWeights = (sint16 *)dma::pLoc; if (RestartTristrip) { *pWeights++ = wa0; *pWeights++ = wb0; *pWeights++ = wc0; *pWeights++ = wa1; *pWeights++ = wb1; *pWeights++ = wc1; } dma::pLoc = (uint8 *)pWeights + NumVerticesThisBuffer * 6; vif::EndUNPACK(); } *pWeights++ = ((sint16 *)pData)[0]; *pWeights++ = ((sint16 *)pData)[1]; *pWeights++ = 0x7FFF - pWeights[-1] - pWeights[-2]; wa0=wa1, wa1=pWeights[-3]; wb0=wb1, wb1=pWeights[-2]; wc0=wc1, wc1=pWeights[-1]; UnpackOffset++; } #else void VertexWeights(uint8 *pData) { static uint8 wa0,wb0,wc0,wa1,wb1,wc1; if (NumOutputVertices==0) { vif::BeginUNPACK(0, V3_8, REL, UNSIGNED, UnpackOffset); pWeights = dma::pLoc; if (RestartTristrip) { *pWeights++ = wa0; *pWeights++ = wb0; *pWeights++ = wc0; *pWeights++ = wa1; *pWeights++ = wb1; *pWeights++ = wc1; } dma::pLoc = pWeights + NumVerticesThisBuffer * 3; vif::EndUNPACK(); } wa0=wa1; wb0=wb1; wc0=wc1; wa1 = (uint8)(sint8)(((float)((sint16 *)pData)[0] + 0.5f) * 0.007818608f); wb1 = (uint8)(sint8)(((float)((sint16 *)pData)[1] + 0.5f) * 0.007818608f); if (wb1==0) { wa1=255, wb1=1; } wc1 = 256 - wa1 - wb1; *pWeights++ = wa1; *pWeights++ = wb1; *pWeights++ = wc1; UnpackOffset++; } #endif void VertexSkinNormal(uint8 *pNormData, uint8 *pTOData) { static sint16 nx0,ny0,nz0,nx1,ny1,nz1; if (NumOutputVertices==0) { vif::BeginUNPACK(0, V3_16, REL, SIGNED, UnpackOffset); pSkinData = (sint16 *)dma::pLoc; if (RestartTristrip) { *pSkinData++ = nx0; *pSkinData++ = ny0; *pSkinData++ = nz0; *pSkinData++ = nx1; *pSkinData++ = ny1; *pSkinData++ = nz1; } dma::pLoc = (uint8 *)pSkinData + NumVerticesThisBuffer * 6; vif::EndUNPACK(); } *pSkinData++ = (sint16) (((sint32) ((sint16 *)pNormData)[0]) & 0xFFFFFC00 | (((uint32)((uint8 *)pTOData)[0]) << 2)); *pSkinData++ = (sint16) (((sint32) ((sint16 *)pNormData)[1]) & 0xFFFFFC00 | (((uint32)((uint8 *)pTOData)[1]) << 2)); //*pSkinData++ = (sint16) (((sint32) ((sint16 *)pNormData)[2]) & 0xFFFFFC00 | (((uint32)((uint8 *)pTOData)[2]) << 2)); sint16 coord = (sint16)sqrtf(32767.0f*32767.0f - (float)((sint16 *)pNormData)[0] * (float)((sint16 *)pNormData)[0] - (float)((sint16 *)pNormData)[1] * (float)((sint16 *)pNormData)[1]); if (((sint16 *)pNormData)[0] & 0x0001) { coord = -coord; } *pSkinData++ = (sint16) ((sint32)coord & 0xFFFFFC00) | (((uint8 *)pTOData)[2] << 2); nx0=nx1, nx1=pSkinData[-3]; ny0=ny1, ny1=pSkinData[-2]; nz0=nz1, nz1=pSkinData[-1]; UnpackOffset++; } void VertexRGBA(uint8 *pData) { static uint32 rgba0,rgba1; if (NumOutputVertices==0) { vif::BeginUNPACK(0, V4_8, REL, UNSIGNED, UnpackOffset); pColour = (uint32 *)dma::pLoc; if (RestartTristrip) { *pColour++ = rgba0; *pColour++ = rgba1; } dma::pLoc = (uint8 *)pColour + NumVerticesThisBuffer * 4; vif::EndUNPACK(); } *pColour++ = ((uint32 *)pData)[0]; rgba0=rgba1, rgba1=pColour[-1]; UnpackOffset++; } Mth::Vector get_bone_scale( int bone_index ) { Mth::Vector returnVec( 1.0f, 1.0f, 1.0f, 1.0f ); if ( bone_index >= 29 && bone_index <= 33 ) { // this only works with the thps5 skeleton, whose // head bones are between 29 and 33... // (eventually, we can remove the subtract 29 // once the exporter is massaging the data correctly) returnVec = s_pBoneScales[ bone_index - 29 ]; // Y & Z are reversed... odd! Mth::Vector tempVec = returnVec; returnVec[Y] = tempVec[Z]; returnVec[Z] = tempVec[Y]; } else if ( bone_index == -1 ) { // implies that it's not weighted to a bone return returnVec; } else { // implies that it's weighted to the wrong bone return returnVec; } return returnVec; } Mth::Vector get_bone_pos( int bone_index ) { Mth::Vector returnVec( 0.0f, 0.0f, 0.0f, 1.0f ); if ( bone_index >= 29 && bone_index <= 33 ) { // this only works with the thps5 skeleton, whose // head bones are between 29 and 33... // (eventually, we can remove the subtract 29 // once the exporter is massaging the data correctly) returnVec = s_pBonePositions[ bone_index - 29 ]; } else if ( bone_index == -1 ) { // implies that it's not weighted to a bone return returnVec; } else { // implies that it's weighted to the wrong bone return returnVec; } // need to get into fixed point returnVec.Scale( 16.0f ); returnVec[W] = 1.0f; return returnVec; } void VertexXYZ(uint8 *pData, bool IsSkin, int& skinnedVertexCount) { static sint64 x0,y0,z0,x1,y1,z1,x2,y2,z2; sint64 det0,det1,det2; if (NumOutputVertices==0) { vif::STMOD(1); vif::BeginUNPACK(0, V4_16, REL, SIGNED, UnpackOffset); pVertex = (sint16 *)dma::pLoc; if (RestartTristrip) { *pVertex++ = x1; *pVertex++ = y1; *pVertex++ = z1; *pVertex++ = 0x8000; *pVertex++ = x2; *pVertex++ = y2; *pVertex++ = z2; *pVertex++ = 0x8000; } dma::pLoc = (uint8 *)pVertex + NumVerticesThisBuffer * 8; vif::EndUNPACK(); vif::STMOD(0); } if (IsSkin) { if ( s_meshScalingEnabled ) { float x = (float)((sint16 *)pData)[0]; float y = (float)((sint16 *)pData)[1]; float z = (float)((sint16 *)pData)[2]; Mth::Vector origPos( x, y, z, 1.0f ); Mth::Vector bonePos0 = get_bone_pos( s_pWeightIndices[s_currentVertIndex * 3] ); Mth::Vector bonePos1 = get_bone_pos( s_pWeightIndices[s_currentVertIndex * 3 + 1] ); Mth::Vector bonePos2 = get_bone_pos( s_pWeightIndices[s_currentVertIndex * 3 + 2] ); // need to scale each vert relative to its parent bone Mth::Vector localPos0 = origPos - bonePos0; Mth::Vector localPos1 = origPos - bonePos1; Mth::Vector localPos2 = origPos - bonePos2; localPos0.Scale( get_bone_scale( s_pWeightIndices[s_currentVertIndex * 3] ) ); localPos1.Scale( get_bone_scale( s_pWeightIndices[s_currentVertIndex * 3 + 1] ) ); localPos2.Scale( get_bone_scale( s_pWeightIndices[s_currentVertIndex * 3 + 2] ) ); localPos0 += bonePos0; localPos1 += bonePos1; localPos2 += bonePos2; Mth::Vector scaledPos = ( localPos0 * s_pWeights[s_currentVertIndex * 3] ) + ( localPos1 * s_pWeights[s_currentVertIndex * 3 + 1] ) + ( localPos2 * s_pWeights[s_currentVertIndex * 3 + 2] ); x = scaledPos[X]; y = scaledPos[Y]; z = scaledPos[Z]; *pVertex++ = (sint16)x; *pVertex++ = (sint16)y; *pVertex++ = (sint16)z; } else { *pVertex++ = (((sint16 *)pData)[0] * 1); *pVertex++ = (((sint16 *)pData)[1] * 1); *pVertex++ = (((sint16 *)pData)[2] * 1); } } else { *pVertex++ = (sint16) (((float *)pData)[0] * SUB_INCH_PRECISION); *pVertex++ = (sint16) (((float *)pData)[1] * SUB_INCH_PRECISION); *pVertex++ = (sint16) (((float *)pData)[2] * SUB_INCH_PRECISION); } if (IsSkin) { // GJ: if it's a skinned model, then check to see // if the skinnedVertexCount matches one of the ones // that requires a pointer to the ADC bit AddCASFlag( skinnedVertexCount, (uint16*)pVertex ); } skinnedVertexCount++; if (IsSkin) { *pVertex++ = ((uint16 *)pData)[3] ? 0x8000 : 0x0000; } else { *pVertex++ = ((uint32 *)pData)[3] ? 0x8000 : 0x0000; } // advance vertex queue and cull triangle if zero area x0=x1, y0=y1, z0=z1; x1=x2, y1=y2, z1=z2; x2=pVertex[-4], y2=pVertex[-3], z2=pVertex[-2]; det0 = y1*z2+y2*z0+y0*z1-y2*z1-y0*z2-y1*z0; det1 = z1*x2+z2*x0+z0*x1-z2*x1-z0*x2-z1*x0; det2 = x1*y2+x2*y0+x0*y1-x2*y1-x0*y2-x1*y0; if (det0*det0 + det1*det1 + det2*det2 == 0) pVertex[-1] = 0x8000; UnpackOffset++; } void EndVertex(void) { if (NumOutputVertices==0 && RestartTristrip) { RestartTristrip = 0; } NumOutputVertices++; } void sMesh::SetActive(bool active) { if (active) { Flags |= MESHFLAG_ACTIVE; } else { Flags &= ~MESHFLAG_ACTIVE; } } // Note, Meshflags and passmasks are off by 2 bits //#define MESHFLAG_PASS_BIT_0 (1<<8) //#define MESHFLAG_PASS_BIT_1 (1<<9) //uint32 gPassMask1 = 0; // 1<<6 | 1<<7 (0x40, 0x80) //uint32 gPassMask0 = 0; // 1<<6 | 1<<7 (0x40, 0x80) bool sMesh::IsActive() { #ifdef __NOPT_ASSERT__ // Mick: debug check for global pass on/off // Note, shifting flags by 2, so mesh flags match geomnode flags // Eventually, meshes will be replaced by leaf nodes in CGeomNode tree // if (((Flags>>2) & gPassMask1) != gPassMask0) // { // return false; // } #endif return (Flags&MESHFLAG_ACTIVE) ? true : false; } const int MAX_CAS_DATA_LOOKUP = 10000; // array of pointers to sCASData* sCASData** sCASDataLookupTable = NULL; void CreateCASDataLookupTable() { Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); Dbg_MsgAssert( sCASDataLookupTable == NULL, ( "CAS data lookup table already exists" ) ); sCASDataLookupTable = (sCASData**)Mem::Malloc( MAX_CAS_DATA_LOOKUP * sizeof(sCASData*) ); for ( int i = 0; i < MAX_CAS_DATA_LOOKUP; i++ ) { sCASDataLookupTable[i] = NULL; } Mem::Manager::sHandle().PopContext(); } void DestroyCASDataLookupTable() { if ( sCASDataLookupTable ) { Mem::Free( sCASDataLookupTable ); sCASDataLookupTable = NULL; } } void SetCASDataLookupData( int index, sCASData* pData ) { Dbg_MsgAssert( sCASDataLookupTable != NULL, ( "CAS data lookup table doesn't exist" ) ); Dbg_MsgAssert( index >= 0 && index < MAX_CAS_DATA_LOOKUP, ( "Out of range lookup index %d (must be between 0 and %d)", index, MAX_CAS_DATA_LOOKUP ) ); Dbg_MsgAssert( sCASDataLookupTable[index] == NULL, ( "Lookup index %d already used", index, MAX_CAS_DATA_LOOKUP ) ); sCASDataLookupTable[index] = pData; } void AddCASFlag( int skinnedVertexIndex, uint16* pADCBit ) { if ( !sCASDataLookupTable ) { return; } Dbg_MsgAssert( skinnedVertexIndex >= 0 && skinnedVertexIndex < MAX_CAS_DATA_LOOKUP, ( "Out of range lookup index %d (must be between 0 and %d", skinnedVertexIndex, MAX_CAS_DATA_LOOKUP ) ); if ( sCASDataLookupTable[skinnedVertexIndex] != NULL ) { sCASDataLookupTable[skinnedVertexIndex]->pADCBit = pADCBit; } } int sMesh::TotalNumVertices; uint32 sMesh::Version; } // namespace NxPs2