mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-10 14:51:40 +00:00
Add support for packed Unreal Engine 1 vertex mesh format.
(concatenated "UMSH" signature + datafile + anivfile) This is pretty much 100% functional by now. Hasn't been tested on platforms other than Linux yet, though. Code definitely deserves some cleaning.
This commit is contained in:
parent
0fae13bab4
commit
f285e550d6
4 changed files with 345 additions and 0 deletions
|
@ -1134,6 +1134,7 @@ set (PCH_SOURCES
|
|||
r_data/models/models_md3.cpp
|
||||
r_data/models/models_md2.cpp
|
||||
r_data/models/models_voxel.cpp
|
||||
r_data/models/models_ue1.cpp
|
||||
scripting/symbols.cpp
|
||||
scripting/types.cpp
|
||||
scripting/thingdef.cpp
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "g_levellocals.h"
|
||||
#include "r_utility.h"
|
||||
#include "r_data/models/models.h"
|
||||
#include "r_data/models/models_ue1.h"
|
||||
#include "i_time.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -426,6 +427,10 @@ static unsigned FindModel(const char * path, const char * modelfile)
|
|||
{
|
||||
model = new FMD3Model;
|
||||
}
|
||||
else if (!memcmp(buffer, "UMSH", 4))
|
||||
{
|
||||
model = new FUE1Model;
|
||||
}
|
||||
|
||||
if (model != nullptr)
|
||||
{
|
||||
|
|
248
src/r_data/models/models_ue1.cpp
Normal file
248
src/r_data/models/models_ue1.cpp
Normal file
|
@ -0,0 +1,248 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2018 Marisa Kirisame
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "w_wad.h"
|
||||
#include "cmdlib.h"
|
||||
#include "r_data/models/models_ue1.h"
|
||||
|
||||
double unpackuvert( uint32_t n, int c )
|
||||
{
|
||||
switch( c )
|
||||
{
|
||||
case 2:
|
||||
return ((int16_t)((n&0x7ff)<<5))/127.;
|
||||
case 1:
|
||||
return ((int16_t)(((n>>11)&0x7ff)<<5))/127.;
|
||||
case 0:
|
||||
return ((int16_t)(((n>>22)&0x3ff)<<6))/127.;
|
||||
default:
|
||||
return 0.;
|
||||
}
|
||||
}
|
||||
|
||||
bool FUE1Model::Load( const char *filename, int lumpnum, const char *buffer, int length )
|
||||
{
|
||||
mLumpNum = lumpnum;
|
||||
// read signature
|
||||
if ( memcmp(buffer,"UMSH",4) )
|
||||
return false;
|
||||
// map structures
|
||||
int ofs = 4;
|
||||
dhead = (d3dhead*)(buffer+ofs);
|
||||
ofs += sizeof(d3dhead);
|
||||
dpolys = (d3dpoly*)(buffer+ofs);
|
||||
ofs += sizeof(d3dpoly)*dhead->numpolys;
|
||||
ahead = (a3dhead*)(buffer+ofs);
|
||||
ofs += sizeof(a3dhead);
|
||||
averts = (uint32_t*)(buffer+ofs);
|
||||
// set counters
|
||||
numVerts = dhead->numverts;
|
||||
numFrames = ahead->numframes;
|
||||
numPolys = dhead->numpolys;
|
||||
numGroups = 0;
|
||||
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] ) numGroups++;
|
||||
LoadGeometry();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FUE1Model::LoadGeometry()
|
||||
{
|
||||
// populate vertex arrays
|
||||
for ( int i=0; i<numFrames; i++ )
|
||||
{
|
||||
for ( int j=0; j<numVerts; j++ )
|
||||
{
|
||||
UE1Vertex Vert;
|
||||
// unpack position
|
||||
Vert.Pos.X = unpackuvert(averts[j+i*numVerts],0);
|
||||
Vert.Pos.Y = unpackuvert(averts[j+i*numVerts],1);
|
||||
Vert.Pos.Z = unpackuvert(averts[j+i*numVerts],2);
|
||||
// push vertex (without normals, will be calculated later)
|
||||
verts.Push(Vert);
|
||||
}
|
||||
}
|
||||
// populate poly arrays
|
||||
for ( int i=0; i<numPolys; i++ )
|
||||
{
|
||||
UE1Poly Poly;
|
||||
// set indices
|
||||
for ( int j=0; j<3; j++ )
|
||||
Poly.V[j] = dpolys[i].vertices[j];
|
||||
// unpack coords
|
||||
for ( int j=0; j<3; j++ )
|
||||
{
|
||||
Poly.C[j].S = dpolys[i].uv[j][0]/255.;
|
||||
Poly.C[j].T = dpolys[i].uv[j][1]/255.;
|
||||
}
|
||||
Poly.texNum = dpolys[i].texnum;
|
||||
// push
|
||||
polys.Push(Poly);
|
||||
}
|
||||
// compute normals for vertex arrays
|
||||
// iterates through all polys and compute the average of all facet normals
|
||||
// from those who use this vertex. not pretty, but does the job
|
||||
for ( int i=0; i<numFrames; i++ )
|
||||
{
|
||||
for ( int j=0; j<numVerts; j++ )
|
||||
{
|
||||
UE1Vector nsum = {0,0,0};
|
||||
double total = 0.;
|
||||
for ( int k=0; k<numPolys; k++ )
|
||||
{
|
||||
if ( (polys[k].V[0] != j) && (polys[k].V[1] != j) && (polys[k].V[2] != j) ) continue;
|
||||
UE1Vector vert[3], dir[2], norm;
|
||||
// compute facet normal
|
||||
for ( int l=0; l<3; l++ )
|
||||
vert[l] = verts[polys[k].V[l]+numVerts*i].Pos;
|
||||
dir[0].X = vert[1].X-vert[0].X;
|
||||
dir[0].Y = vert[1].Y-vert[0].Y;
|
||||
dir[0].Z = vert[1].Z-vert[0].Z;
|
||||
dir[1].X = vert[2].X-vert[0].X;
|
||||
dir[1].Y = vert[2].Y-vert[0].Y;
|
||||
dir[1].Z = vert[2].Z-vert[0].Z;
|
||||
norm.X = dir[0].Y*dir[1].Z-dir[0].Z*dir[1].Y;
|
||||
norm.Y = dir[0].Z*dir[1].X-dir[0].X*dir[1].Z;
|
||||
norm.Z = dir[0].X*dir[1].Y-dir[0].Y*dir[1].X;
|
||||
double s = sqrt(norm.X*norm.X+norm.Y*norm.Y+norm.Z*norm.Z);
|
||||
if ( s != 0. )
|
||||
{
|
||||
norm.X /= s;
|
||||
norm.Y /= s;
|
||||
norm.Z /= s;
|
||||
}
|
||||
nsum.X += norm.X;
|
||||
nsum.Y += norm.Y;
|
||||
nsum.Z += norm.Z;
|
||||
total+=1.;
|
||||
}
|
||||
verts[j+numVerts*i].Normal.X = nsum.X/total;
|
||||
verts[j+numVerts*i].Normal.Y = nsum.Y/total;
|
||||
verts[j+numVerts*i].Normal.Z = nsum.Z/total;
|
||||
}
|
||||
}
|
||||
// populate skin groups
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
{
|
||||
UE1Group Group;
|
||||
Group.numPolys = 0;
|
||||
for ( int j=0; j<numPolys; j++ )
|
||||
{
|
||||
if ( polys[j].texNum != i )
|
||||
continue;
|
||||
Group.P.Push(j);
|
||||
Group.numPolys++;
|
||||
}
|
||||
groups.Push(Group);
|
||||
}
|
||||
// ... and it's finally done
|
||||
}
|
||||
|
||||
void FUE1Model::UnloadGeometry()
|
||||
{
|
||||
verts.Reset();
|
||||
polys.Reset();
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
groups[i].P.Reset();
|
||||
groups.Reset();
|
||||
}
|
||||
|
||||
int FUE1Model::FindFrame( const char *name )
|
||||
{
|
||||
// unsupported, there are no named frames
|
||||
return -1;
|
||||
}
|
||||
|
||||
void FUE1Model::RenderFrame( FModelRenderer *renderer, FTexture *skin, int frame, int frame2, double inter, int translation )
|
||||
{
|
||||
// the moment of magic
|
||||
if ( (frame >= numFrames) || (frame2 >= numFrames) ) return;
|
||||
renderer->SetInterpolation(inter);
|
||||
int vsize, fsize = 0, vofs = 0;
|
||||
for ( int i=0; i<numGroups; i++ ) fsize += groups[i].numPolys*3;
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
{
|
||||
vsize = groups[i].numPolys*3;
|
||||
FTexture *sskin = skin;
|
||||
if ( !sskin )
|
||||
{
|
||||
if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid() )
|
||||
sskin = TexMan(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i]);
|
||||
if ( !sskin )
|
||||
{
|
||||
continue;
|
||||
vofs += vsize;
|
||||
}
|
||||
}
|
||||
renderer->SetMaterial(sskin,false,translation);
|
||||
mVBuf->SetupFrame(renderer,vofs+frame*fsize,vofs+frame2*fsize,vsize);
|
||||
renderer->DrawArrays(0,vsize);
|
||||
vofs += vsize;
|
||||
}
|
||||
renderer->SetInterpolation(0.f);
|
||||
}
|
||||
|
||||
void FUE1Model::BuildVertexBuffer( FModelRenderer *renderer )
|
||||
{
|
||||
if ( mVBuf != NULL )
|
||||
return;
|
||||
int vsize = 0;
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
vsize += groups[i].numPolys*3;
|
||||
vsize *= numFrames;
|
||||
mVBuf = renderer->CreateVertexBuffer(false,numFrames==1);
|
||||
FModelVertex *vptr = mVBuf->LockVertexBuffer(vsize);
|
||||
int vidx = 0;
|
||||
for ( int i=0; i<numFrames; i++ )
|
||||
{
|
||||
for ( int j=0; j<numGroups; j++ )
|
||||
{
|
||||
for ( int k=0; k<groups[j].numPolys; k++ )
|
||||
{
|
||||
for ( int l=0; l<3; l++ )
|
||||
{
|
||||
UE1Vertex V = verts[polys[groups[j].P[k]].V[l]+i*numVerts];
|
||||
UE1Coord C = polys[groups[j].P[k]].C[l];
|
||||
FModelVertex *vert = &vptr[vidx++];
|
||||
vert->Set(V.Pos.X,V.Pos.Y,V.Pos.Z,C.S,C.T);
|
||||
vert->SetNormal(V.Normal.X,V.Normal.Y,V.Normal.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mVBuf->UnlockVertexBuffer();
|
||||
}
|
||||
|
||||
void FUE1Model::AddSkins( uint8_t *hitlist )
|
||||
{
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid() )
|
||||
hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
|
||||
FUE1Model::~FUE1Model()
|
||||
{
|
||||
UnloadGeometry();
|
||||
}
|
91
src/r_data/models/models_ue1.h
Normal file
91
src/r_data/models/models_ue1.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
#pragma once
|
||||
|
||||
#include "models.h"
|
||||
|
||||
class FUE1Model : public FModel
|
||||
{
|
||||
public:
|
||||
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||
int FindFrame(const char * name) override;
|
||||
void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0) override;
|
||||
void BuildVertexBuffer(FModelRenderer *renderer) override;
|
||||
void AddSkins(uint8_t *hitlist) override;
|
||||
void LoadGeometry();
|
||||
void UnloadGeometry();
|
||||
FUE1Model()
|
||||
{
|
||||
mLumpNum = -1;
|
||||
dhead = NULL;
|
||||
dpolys = NULL;
|
||||
ahead = NULL;
|
||||
averts = NULL;
|
||||
numVerts = 0;
|
||||
numFrames = 0;
|
||||
numPolys = 0;
|
||||
numGroups = 0;
|
||||
}
|
||||
~FUE1Model();
|
||||
|
||||
private:
|
||||
int mLumpNum;
|
||||
|
||||
// raw data structures
|
||||
struct d3dhead
|
||||
{
|
||||
uint16_t numpolys, numverts;
|
||||
uint16_t bogusrot, bogusframe;
|
||||
uint32_t bogusnorm[3];
|
||||
uint32_t fixscale;
|
||||
uint32_t unused[3];
|
||||
uint8_t padding[12];
|
||||
};
|
||||
struct d3dpoly
|
||||
{
|
||||
uint16_t vertices[3];
|
||||
uint8_t type, color;
|
||||
uint8_t uv[3][2];
|
||||
uint8_t texnum, flags;
|
||||
};
|
||||
struct a3dhead
|
||||
{
|
||||
uint16_t numframes, framesize;
|
||||
};
|
||||
d3dhead * dhead;
|
||||
d3dpoly * dpolys;
|
||||
a3dhead * ahead;
|
||||
uint32_t * averts;
|
||||
|
||||
// converted data structures
|
||||
struct UE1Coord
|
||||
{
|
||||
float S, T;
|
||||
};
|
||||
struct UE1Vector
|
||||
{
|
||||
float X, Y, Z;
|
||||
};
|
||||
struct UE1Vertex
|
||||
{
|
||||
UE1Vector Pos, Normal;
|
||||
};
|
||||
struct UE1Poly
|
||||
{
|
||||
int V[3];
|
||||
UE1Coord C[3];
|
||||
int texNum;
|
||||
};
|
||||
struct UE1Group
|
||||
{
|
||||
TArray<int> P;
|
||||
int numPolys;
|
||||
};
|
||||
|
||||
int numVerts;
|
||||
int numFrames;
|
||||
int numPolys;
|
||||
int numGroups;
|
||||
|
||||
TArray<UE1Vertex> verts;
|
||||
TArray<UE1Poly> polys;
|
||||
TArray<UE1Group> groups;
|
||||
};
|
Loading…
Reference in a new issue