Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
4 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
}; |
f285e55
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you add the Unreal engine 1 model format, instead of something more well-known, especially if there are no Blender plugins that support this model format? StrikerMan780 mentioned the SMD format here.
f285e55
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because someone submitted the code in ready-to-use form. Why should I reject that?
f285e55
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not saying you shouldn't. I'm saying I don't see the point if the format is so ancient and obscure that practically nobody can use it.
f285e55
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.