UE1: Clean up and restructure model class.

UE1: Add support for Deus Ex format vertex data.
UE1: Group triangles by skin index AND type/flags (preparation for per-surface render style support).
UE1: Add handling of Weapon Triangle (preparation for model attachment support).
UE1: Support flat shaded triangle flag.
This commit is contained in:
Marisa Kirisame 2018-06-16 19:24:08 +02:00 committed by Christoph Oelckers
parent 158890e0ce
commit e5249f302a
2 changed files with 145 additions and 68 deletions

View file

@ -28,11 +28,11 @@ float unpackuvert( uint32_t n, int c )
{ {
switch( c ) switch( c )
{ {
case 2: case 0:
return ((int16_t)((n&0x7ff)<<5))/128.f; return ((int16_t)((n&0x7ff)<<5))/128.f;
case 1: case 1:
return ((int16_t)(((n>>11)&0x7ff)<<5))/128.f; return ((int16_t)(((n>>11)&0x7ff)<<5))/128.f;
case 0: case 2:
return ((int16_t)(((n>>22)&0x3ff)<<6))/128.f; return ((int16_t)(((n>>22)&0x3ff)<<6))/128.f;
default: default:
return 0.f; return 0.f;
@ -41,66 +41,74 @@ float unpackuvert( uint32_t n, int c )
bool FUE1Model::Load( const char *filename, int lumpnum, const char *buffer, int length ) bool FUE1Model::Load( const char *filename, int lumpnum, const char *buffer, int length )
{ {
mLumpNum = lumpnum;
int lumpnum2; int lumpnum2;
FMemLump lump2;
const char *buffer2;
FString realfilename = Wads.GetLumpFullName(lumpnum); FString realfilename = Wads.GetLumpFullName(lumpnum);
if ( (size_t)realfilename.IndexOf("_d.3d") == realfilename.Len()-5 ) if ( (size_t)realfilename.IndexOf("_d.3d") == realfilename.Len()-5 )
{ {
realfilename.Substitute("_d.3d","_a.3d"); realfilename.Substitute("_d.3d","_a.3d");
lumpnum2 = Wads.CheckNumForFullName(realfilename); lumpnum2 = Wads.CheckNumForFullName(realfilename);
lump2 = Wads.ReadLump(lumpnum2); mDataLump = lumpnum;
buffer2 = (char*)lump2.GetMem(); mAnivLump = lumpnum2;
// map structures
dhead = (d3dhead*)(buffer);
dpolys = (d3dpoly*)(buffer+sizeof(d3dhead));
ahead = (a3dhead*)(buffer2);
averts = (uint32_t*)(buffer2+sizeof(a3dhead));
} }
else else
{ {
realfilename.Substitute("_a.3d","_d.3d"); realfilename.Substitute("_a.3d","_d.3d");
lumpnum2 = Wads.CheckNumForFullName(realfilename); lumpnum2 = Wads.CheckNumForFullName(realfilename);
lump2 = Wads.ReadLump(lumpnum2); mAnivLump = lumpnum;
buffer2 = (char*)lump2.GetMem(); mDataLump = lumpnum2;
// map structures
dhead = (d3dhead*)(buffer2);
dpolys = (d3dpoly*)(buffer2+sizeof(d3dhead));
ahead = (a3dhead*)(buffer);
averts = (uint32_t*)(buffer+sizeof(a3dhead));
} }
// set counters
numVerts = dhead->numverts;
numFrames = ahead->numframes;
numPolys = dhead->numpolys;
numGroups = 0;
groupIndices.Reset();
uint8_t used[256] = {0};
for ( int i=0; i<numPolys; i++ )
used[dpolys[i].texnum] = 1;
for ( int i=0; i<256; i++ )
{
if ( !used[i] ) continue;
groupIndices.Push(i);
numGroups++;
}
LoadGeometry();
return true; return true;
} }
void FUE1Model::LoadGeometry() void FUE1Model::LoadGeometry()
{ {
FMemLump lump, lump2;
const char *buffer, *buffer2;
lump = Wads.ReadLump(mDataLump);
buffer = (char*)lump.GetMem();
lump2 = Wads.ReadLump(mAnivLump);
buffer2 = (char*)lump2.GetMem();
// map structures
dhead = (d3dhead*)(buffer);
dpolys = (d3dpoly*)(buffer+sizeof(d3dhead));
ahead = (a3dhead*)(buffer2);
// detect deus ex format
if ( (ahead->framesize/dhead->numverts) == 8 )
{
averts = NULL;
dxverts = (dxvert*)(buffer2+sizeof(a3dhead));
}
else
{
averts = (uint32_t*)(buffer2+sizeof(a3dhead));
dxverts = NULL;
}
weaponPoly = -1;
// set counters
numVerts = dhead->numverts;
numFrames = ahead->numframes;
numPolys = dhead->numpolys;
numGroups = 0;
// populate vertex arrays // populate vertex arrays
for ( int i=0; i<numFrames; i++ ) for ( int i=0; i<numFrames; i++ )
{ {
for ( int j=0; j<numVerts; j++ ) for ( int j=0; j<numVerts; j++ )
{ {
UE1Vertex Vert; UE1Vertex Vert;
// unpack position if ( dxverts != NULL )
Vert.Pos = FVector3(unpackuvert(averts[j+i*numVerts],2), {
unpackuvert(averts[j+i*numVerts],0), // convert padded XYZ16
Vert.Pos = FVector3(dxverts[j+i*numVerts].x,
dxverts[j+i*numVerts].z,
-dxverts[j+i*numVerts].y);
}
else
{
// convert packed XY11Z10
Vert.Pos = FVector3(unpackuvert(averts[j+i*numVerts],0),
unpackuvert(averts[j+i*numVerts],2),
-unpackuvert(averts[j+i*numVerts],1)); -unpackuvert(averts[j+i*numVerts],1));
}
// push vertex (without normals, will be calculated later) // push vertex (without normals, will be calculated later)
verts.Push(Vert); verts.Push(Vert);
} }
@ -115,7 +123,11 @@ void FUE1Model::LoadGeometry()
// unpack coords // unpack coords
for ( int j=0; j<3; j++ ) for ( int j=0; j<3; j++ )
Poly.C[j] = FVector2(dpolys[i].uv[j][0]/255.f,dpolys[i].uv[j][1]/255.f); Poly.C[j] = FVector2(dpolys[i].uv[j][0]/255.f,dpolys[i].uv[j][1]/255.f);
Poly.texNum = dpolys[i].texnum; // compute facet normal
FVector3 dir[2];
dir[0] = verts[Poly.V[1]].Pos-verts[Poly.V[0]].Pos;
dir[1] = verts[Poly.V[2]].Pos-verts[Poly.V[0]].Pos;
Poly.Normal = dir[0]^dir[1];
// push // push
polys.Push(Poly); polys.Push(Poly);
} }
@ -130,37 +142,61 @@ void FUE1Model::LoadGeometry()
for ( int k=0; k<numPolys; k++ ) for ( int k=0; k<numPolys; k++ )
{ {
if ( (polys[k].V[0] != j) && (polys[k].V[1] != j) && (polys[k].V[2] != j) ) continue; if ( (polys[k].V[0] != j) && (polys[k].V[1] != j) && (polys[k].V[2] != j) ) continue;
FVector3 vert[3], dir[2], norm; nsum += polys[k].Normal;
// compute facet normal
for ( int l=0; l<3; l++ )
vert[l] = verts[polys[k].V[l]+numVerts*i].Pos;
dir[0] = vert[1]-vert[0];
dir[1] = vert[2]-vert[0];
norm = dir[0]^dir[1];
nsum += norm.Unit();
} }
verts[j+numVerts*i].Normal = nsum.Unit(); verts[j+numVerts*i].Normal = nsum.Unit();
} }
} }
// populate skin groups // populate poly groups (subdivided by texture number and type)
for ( int i=0; i<numGroups; i++ ) // this method minimizes searches in the group list as much as possible
{ // while still doing a single pass through the poly list
int curgroup = -1;
UE1Group Group; UE1Group Group;
Group.numPolys = 0; for ( int i=0; i<numPolys; i++ )
for ( int j=0; j<numPolys; j++ )
{ {
if ( polys[j].texNum != groupIndices[i] ) // while we're at it, look for the weapon triangle
continue; if ( dpolys[i].type&PT_WeaponTriangle ) weaponPoly = i;
Group.P.Push(j); if ( curgroup == -1 )
Group.numPolys++; {
} // no group, create it
Group.P.Reset();
Group.numPolys = 0;
Group.texNum = dpolys[i].texnum;
Group.type = dpolys[i].type;
groups.Push(Group); groups.Push(Group);
curgroup = numGroups++;
}
else if ( (dpolys[i].texnum != groups[curgroup].texNum) || (dpolys[i].type != groups[curgroup].type) )
{
// different attributes than last time
// search for existing group with new attributes, create one if not found
curgroup = -1;
for ( int j=0; j<numGroups; j++ )
{
if ( (groups[j].texNum != dpolys[i].texnum) || (groups[j].type != dpolys[i].type) ) continue;
curgroup = j;
break;
}
// counter the increment that will happen after continuing this loop
// otherwise it'll be skipped over
i--;
continue;
}
groups[curgroup].P.Push(i);
groups[curgroup].numPolys++;
} }
// ... and it's finally done // ... and it's finally done
mDataLoaded = true;
} }
void FUE1Model::UnloadGeometry() void FUE1Model::UnloadGeometry()
{ {
mDataLoaded = false;
weaponPoly = -1;
numVerts = 0;
numFrames = 0;
numPolys = 0;
numGroups = 0;
verts.Reset(); verts.Reset();
polys.Reset(); polys.Reset();
for ( int i=0; i<numGroups; i++ ) for ( int i=0; i<numGroups; i++ )
@ -184,17 +220,25 @@ void FUE1Model::RenderFrame( FModelRenderer *renderer, FTexture *skin, int frame
for ( int i=0; i<numGroups; i++ ) for ( int i=0; i<numGroups; i++ )
{ {
vsize = groups[i].numPolys*3; vsize = groups[i].numPolys*3;
if ( groups[i].type&PT_WeaponTriangle )
{
// weapon triangle should never be drawn, it only exists to calculate attachment position and orientation
vofs += vsize;
continue;
}
FTexture *sskin = skin; FTexture *sskin = skin;
if ( !sskin ) if ( !sskin )
{ {
if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid() ) if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].isValid() )
sskin = TexMan(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i]); sskin = TexMan(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum]);
if ( !sskin ) if ( !sskin )
{ {
vofs += vsize; vofs += vsize;
continue; continue;
} }
} }
// TODO: Handle per-group render styles and other flags once functions for it are implemented
// Future note: poly renderstyles should always be enforced unless the actor itself has a style other than Normal
renderer->SetMaterial(sskin,false,translation); renderer->SetMaterial(sskin,false,translation);
GetVertexBuffer(renderer)->SetupFrame(renderer,vofs+frame*fsize,vofs+frame2*fsize,vsize); GetVertexBuffer(renderer)->SetupFrame(renderer,vofs+frame*fsize,vofs+frame2*fsize,vsize);
renderer->DrawArrays(0,vsize); renderer->DrawArrays(0,vsize);
@ -207,6 +251,8 @@ void FUE1Model::BuildVertexBuffer( FModelRenderer *renderer )
{ {
if (GetVertexBuffer(renderer)) if (GetVertexBuffer(renderer))
return; return;
if ( !mDataLoaded )
LoadGeometry();
int vsize = 0; int vsize = 0;
for ( int i=0; i<numGroups; i++ ) for ( int i=0; i<numGroups; i++ )
vsize += groups[i].numPolys*3; vsize += groups[i].numPolys*3;
@ -227,7 +273,13 @@ void FUE1Model::BuildVertexBuffer( FModelRenderer *renderer )
FVector2 C = polys[groups[j].P[k]].C[l]; FVector2 C = polys[groups[j].P[k]].C[l];
FModelVertex *vert = &vptr[vidx++]; FModelVertex *vert = &vptr[vidx++];
vert->Set(V.Pos.X,V.Pos.Y,V.Pos.Z,C.X,C.Y); vert->Set(V.Pos.X,V.Pos.Y,V.Pos.Z,C.X,C.Y);
vert->SetNormal(V.Normal.X,V.Normal.Y,V.Normal.Z); if ( groups[j].type&PT_Curvy ) // use facet normal
{
vert->SetNormal(polys[groups[j].P[k]].Normal.X,
polys[groups[j].P[k]].Normal.Y,
polys[groups[j].P[k]].Normal.Z);
}
else vert->SetNormal(V.Normal.X,V.Normal.Y,V.Normal.Z);
} }
} }
} }
@ -238,8 +290,8 @@ void FUE1Model::BuildVertexBuffer( FModelRenderer *renderer )
void FUE1Model::AddSkins( uint8_t *hitlist ) void FUE1Model::AddSkins( uint8_t *hitlist )
{ {
for ( int i=0; i<numGroups; i++ ) for ( int i=0; i<numGroups; i++ )
if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid() ) if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].isValid() )
hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTextureManager::HIT_Flat; hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].GetIndex()] |= FTextureManager::HIT_Flat;
} }
FUE1Model::~FUE1Model() FUE1Model::~FUE1Model()

View file

@ -5,6 +5,23 @@
class FUE1Model : public FModel class FUE1Model : public FModel
{ {
public: public:
enum EPolyType
{
PT_Normal = 0, // normal renderstyle
PT_TwoSided = 1, // like normal, but don't cull backfaces
PT_Translucent = 2, // additive blending
PT_Masked = 3, // draw with alpha testing
PT_Modulated = 4, // overlay-like blending (rgb values below 128 darken, 128 is unchanged, and above 128 lighten)
// types mask
PT_Type = 7,
// flags
PT_WeaponTriangle = 0x08, // this poly is used for positioning a weapon attachment and should not be drawn
PT_Unlit = 0x10, // this poly is fullbright
PT_Curvy = 0x20, // this poly uses the facet normal
PT_EnvironmentMap = 0x40, // vertex UVs are remapped to their view-space X and Z normals, fake cubemap look
PT_NoSmooth = 0x80 // this poly forcibly uses nearest filtering
};
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override; bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
int FindFrame(const char * name) override; int FindFrame(const char * name) override;
void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0) override; void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0) override;
@ -14,7 +31,9 @@ public:
void UnloadGeometry(); void UnloadGeometry();
FUE1Model() FUE1Model()
{ {
mLumpNum = -1; mDataLump = -1;
mAnivLump = -1;
mDataLoaded = false;
dhead = NULL; dhead = NULL;
dpolys = NULL; dpolys = NULL;
ahead = NULL; ahead = NULL;
@ -27,7 +46,8 @@ public:
~FUE1Model(); ~FUE1Model();
private: private:
int mLumpNum; int mDataLump, mAnivLump;
bool mDataLoaded;
// raw data structures // raw data structures
struct d3dhead struct d3dhead
@ -54,6 +74,11 @@ private:
d3dpoly * dpolys; d3dpoly * dpolys;
a3dhead * ahead; a3dhead * ahead;
uint32_t * averts; uint32_t * averts;
struct dxvert
{
int16_t x, y, z, pad;
};
dxvert * dxverts;
// converted data structures // converted data structures
struct UE1Vertex struct UE1Vertex
@ -64,21 +89,21 @@ private:
{ {
int V[3]; int V[3];
FVector2 C[3]; FVector2 C[3];
int texNum; FVector3 Normal;
}; };
struct UE1Group struct UE1Group
{ {
TArray<int> P; TArray<int> P;
int numPolys; int numPolys, texNum, type;
}; };
int numVerts; int numVerts;
int numFrames; int numFrames;
int numPolys; int numPolys;
int numGroups; int numGroups;
int weaponPoly; // for future model attachment support, unused for now
TArray<UE1Vertex> verts; TArray<UE1Vertex> verts;
TArray<UE1Poly> polys; TArray<UE1Poly> polys;
TArray<UE1Group> groups; TArray<UE1Group> groups;
TArray<int> groupIndices;
}; };