thug/Code/Gfx/NGPS/NX/mesh.cpp
2016-02-14 08:39:12 +11:00

1220 lines
30 KiB
C++

#include <core/defines.h>
#include <core/math.h>
#include <stdio.h>
#include <stdlib.h>
#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 <sys/file/filesys.h>
#include <sys/file/memfile.h>
#include <sys/mem/memman.h>
#include <gfx/nx.h>
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; i<NumTextureGroups; i++)
{
if (Groups[i].ID == group_ID)
{
Group = i;
break;
}
}
Dbg_MsgAssert(Groups[i].ID==group_ID, ("Couldn't find group ID #%d\n", group_ID));
// get number of meshes
// set group info
Groups[Group].NumMeshes = num_meshes;
Groups[Group].FirstMeshIndex = NumMeshes;
NumMeshes += num_meshes;
return &(Groups[Group]);
}
#endif
//----------------------------------------------------------------------------------------
// Mesh scaling parameters
// GJ: Used for vertex scaling at load-time (needed for cutscene head scaling)
// I decided to make global functions to set these from the cutscene code,
// so that I wouldn't have to pass extra parameters through the various loading
// functions
//----------------------------------------------------------------------------------------
bool s_meshScalingEnabled = false;
char* s_pWeightIndices = NULL;
float* s_pWeights = NULL;
Mth::Vector* s_pBonePositions = NULL;
Mth::Vector* s_pBoneScales = NULL;
int s_currentVertIndex = 0;
void SetMeshScalingParameters( Nx::SMeshScalingParameters* pParams )
{
Dbg_Assert( pParams );
s_meshScalingEnabled = true;
s_pWeights = pParams->pWeights;
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; i<pScene->NumGroups; 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; i<NumMeshesThisGroup; i++,pMesh++)
{
// start a subroutine for this mesh
dma::BeginSub3D();
// start a ret tag
dma::BeginTag(dma::ret, 0);
// get object checksum
MEM_Read(&pMesh->Checksum, 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; j<pScene->NumMaterials; 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; i<NumVertices; i++)
{
UnpackOffset=1;
MEM_Read(VertexData, 1, VertexSize, pFile);
BeginVertex(pMesh, pMat);
// special case for skinned models
if (pMesh->Flags & 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