mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-28 15:02:01 +00:00
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:
parent
158890e0ce
commit
e5249f302a
2 changed files with 145 additions and 68 deletions
|
@ -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()
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue