Added OBJ model support based on IcedTech 1

This commit is contained in:
Robert Beckebans 2021-02-14 16:42:01 +01:00
parent 742624d3ac
commit 0c5a6bf301
5 changed files with 739 additions and 34 deletions

View file

@ -3,8 +3,8 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2012-2020 Robert Beckebans
Copyright (C) 2014-2016 Kot in Action Creative Artel
Copyright (C) 2012-2021 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -38,6 +38,7 @@ If you have questions concerning this license or the applicable additional terms
#include "Model_lwo.h"
#include "Model_ma.h"
#include "Model_ColladaParser.h"
#include "Model_obj.h"
idCVar idRenderModelStatic::r_mergeModelSurfaces( "r_mergeModelSurfaces", "1", CVAR_BOOL | CVAR_RENDERER, "combine model surfaces with the same material" );
idCVar idRenderModelStatic::r_slopVertex( "r_slopVertex", "0.01", CVAR_RENDERER, "merge xyz coordinates this far apart" );
@ -320,13 +321,18 @@ void idRenderModelStatic::InitFromFile( const char* fileName )
loaded = LoadASE( name, &sourceTimeStamp );
reloadable = true;
}
// RB: added dae
// RB: Collada DAE and Wavefront OBJ
else if( extension.Icmp( "dae" ) == 0 )
{
loaded = LoadDAE( name, &sourceTimeStamp );
reloadable = true;
}
// RB end
else if( extension.Icmp( "obj" ) == 0 )
{
loaded = LoadOBJ( name, &sourceTimeStamp );
reloadable = true;
}
// RB end
else if( extension.Icmp( "lwo" ) == 0 )
{
loaded = LoadLWO( name, &sourceTimeStamp );
@ -1343,8 +1349,6 @@ bool idRenderModelStatic::ConvertDAEToModelSurfaces( const ColladaParser* dae )
matchVert_t* lastmv;
matchVert_t* mv;
idVec3 normal;
float uOffset, vOffset, textureSin, textureCos;
float uTiling, vTiling;
int* mergeTo;
byte* color;
static byte identityColor[4] = { 255, 255, 255, 255 };
@ -1753,27 +1757,6 @@ bool idRenderModelStatic::ConvertDAEToModelSurfaces( const ColladaParser* dae )
common->FatalError( "ConvertDAEToModelSurfaces: vertex miscount in DAE file %s", name.c_str() );
}
// an ASE allows the texture coordinates to be scaled, translated, and rotated
//if( ase->materials.Num() == 0 )
{
uOffset = vOffset = 0.0f;
uTiling = vTiling = 1.0f;
textureSin = 0.0f;
textureCos = 1.0f;
}
/*
else
{
material = ase->materials[object->materialRef];
uOffset = -material->uOffset;
vOffset = material->vOffset;
uTiling = material->uTiling;
vTiling = material->vTiling;
textureSin = idMath::Sin( material->angle );
textureCos = idMath::Cos( material->angle );
}
*/
// now allocate and generate the combined vertexes
R_AllocStaticTriSurfVerts( tri, tri->numVerts );
for( j = 0; j < tri->numVerts; j++ )
@ -1784,13 +1767,12 @@ bool idRenderModelStatic::ConvertDAEToModelSurfaces( const ColladaParser* dae )
tri->verts[ j ].SetNormal( mv->normal );
*( unsigned* )tri->verts[j].color = *( unsigned* )mv->color;
//if( mesh->numTVFaces == mesh->numFaces && mesh->numTVertexes != 0 )
if( ( *mesh )->mTexCoords.Num() > 0 )
{
const idVec2& tv = ( *mesh )->mTexCoords[ mv->tv ];
float u = tv.x * uTiling + uOffset;
float v = tv.y * vTiling + vOffset;
tri->verts[ j ].SetTexCoord( u * textureCos + v * textureSin, u * -textureSin + v * textureCos );
float u = tv.x;
float v = tv.y;
tri->verts[ j ].SetTexCoord( u, v );
}
}
@ -1817,6 +1799,364 @@ bool idRenderModelStatic::ConvertDAEToModelSurfaces( const ColladaParser* dae )
return true;
}
/*
=================
idRenderModelStatic::ConvertOBJToModelSurfaces
=================
*/
bool idRenderModelStatic::ConvertOBJToModelSurfaces( const objModel_t* model )
{
objObject_t* mesh;
const idMaterial* im1, *im2;
srfTriangles_t* tri;
int objectNum;
int i, j;
int v, tv;
int* vRemap;
int* tvRemap;
matchVert_t* mvTable; // all of the match verts
matchVert_t** mvHash; // points inside mvTable for each xyz index
matchVert_t* lastmv;
matchVert_t* mv;
idVec3 normal;
int* mergeTo;
byte* color;
static byte identityColor[4] = { 255, 255, 255, 255 };
modelSurface_t surf, *modelSurf;
if( !model )
{
return false;
}
if( model->objects.Num() < 1 )
{
return false;
}
timeStamp = model->timeStamp;
// the modeling programs can save out multiple surfaces with a common
// material, but we would like to mege them together where possible
// meaning that this->NumSurfaces() <= ase->objects.currentElements
mergeTo = ( int* )_alloca( model->objects.Num() * sizeof( *mergeTo ) );
surf.geometry = NULL;
/*
if( model->materials.Num() == 0 )
{
// if we don't have any materials, dump everything into a single surface
surf.shader = tr.defaultMaterial;
surf.id = 0;
this->AddSurface( surf );
for( i = 0; i < ase->objects.Num(); i++ )
{
mergeTo[i] = 0;
}
}
else
*/
if( !r_mergeModelSurfaces.GetBool() )
{
// don't merge any
for( i = 0; i < model->objects.Num(); i++ )
{
mergeTo[i] = i;
mesh = model->objects[i];
surf.shader = declManager->FindMaterial( mesh->material );
surf.id = this->NumSurfaces();
this->AddSurface( surf );
}
}
else
{
// search for material matches
for( i = 0; i < model->objects.Num(); i++ )
{
mesh = model->objects[i];
im1 = declManager->FindMaterial( mesh->material );
if( im1->IsDiscrete() )
{
// flares, autosprites, etc
j = this->NumSurfaces();
}
else
{
for( j = 0; j < this->NumSurfaces(); j++ )
{
modelSurf = &this->surfaces[j];
im2 = modelSurf->shader;
if( im1 == im2 )
{
// merge this
mergeTo[i] = j;
break;
}
}
}
if( j == this->NumSurfaces() )
{
// didn't merge
mergeTo[i] = j;
surf.shader = im1;
surf.id = this->NumSurfaces();
this->AddSurface( surf );
}
}
}
idVectorSubset<idVec3, 3> vertexSubset;
idVectorSubset<idVec2, 2> texCoordSubset;
// build the surfaces
for( objectNum = 0; objectNum < model->objects.Num(); objectNum++ )
{
mesh = model->objects[objectNum];
im1 = declManager->FindMaterial( mesh->material );
bool normalsParsed = mesh->normals.Num();
// completely ignore any explict normals on surfaces with a renderbump command
// which will guarantee the best contours and least vertexes.
const char* rb = im1->GetRenderBump();
if( rb != NULL && rb[0] != '\0' )
{
normalsParsed = false;
}
// It seems like the tools our artists are using often generate
// verts and texcoords slightly separated that should be merged
// note that we really should combine the surfaces with common materials
// before doing this operation, because we can miss a slop combination
// if they are in different surfaces
vRemap = ( int* )R_StaticAlloc( mesh->vertexes.Num() * sizeof( vRemap[0] ), TAG_MODEL );
if( fastLoad )
{
// renderbump doesn't care about vertex count
for( j = 0; j < mesh->vertexes.Num(); j++ )
{
vRemap[j] = j;
}
}
else
{
float vertexEpsilon = r_slopVertex.GetFloat();
float expand = 2 * 32 * vertexEpsilon;
idVec3 mins, maxs;
SIMDProcessor->MinMax( mins, maxs, &mesh->vertexes[0], mesh->vertexes.Num() );
mins -= idVec3( expand, expand, expand );
maxs += idVec3( expand, expand, expand );
vertexSubset.Init( mins, maxs, 32, 1024 );
for( j = 0; j < mesh->vertexes.Num(); j++ )
{
vRemap[j] = vertexSubset.FindVector( &mesh->vertexes[0], j, vertexEpsilon );
}
}
tvRemap = ( int* )R_StaticAlloc( mesh->texcoords.Num() * sizeof( tvRemap[0] ), TAG_MODEL );
if( fastLoad )
{
// renderbump doesn't care about vertex count
for( j = 0; j < mesh->texcoords.Num(); j++ )
{
tvRemap[j] = j;
}
}
else
{
float texCoordEpsilon = r_slopTexCoord.GetFloat();
float expand = 2 * 32 * texCoordEpsilon;
idVec2 mins, maxs;
SIMDProcessor->MinMax( mins, maxs, &mesh->texcoords[0], mesh->texcoords.Num() );
mins -= idVec2( expand, expand );
maxs += idVec2( expand, expand );
texCoordSubset.Init( mins, maxs, 32, 1024 );
for( j = 0; j < mesh->texcoords.Num(); j++ )
{
tvRemap[j] = texCoordSubset.FindVector( &mesh->texcoords[0], j, texCoordEpsilon );
}
}
// we need to find out how many unique vertex / texcoord combinations
// there are, because ASE tracks them separately but we need them unified
// the maximum possible number of combined vertexes is the number of indexes
mvTable = ( matchVert_t* )R_ClearedStaticAlloc( mesh->indexes.Num() * sizeof( mvTable[0] ) );
// we will have a hash chain based on the xyz values
mvHash = ( matchVert_t** )R_ClearedStaticAlloc( mesh->vertexes.Num() * sizeof( mvHash[0] ) );
// allocate triangle surface
tri = R_AllocStaticTriSurf();
tri->numVerts = 0;
tri->numIndexes = 0;
R_AllocStaticTriSurfIndexes( tri, mesh->indexes.Num() );
tri->generateNormals = !normalsParsed;
// init default normal, color and tex coord index
normal.Zero();
color = identityColor;
tv = 0;
// find all the unique combinations
float normalEpsilon = 1.0f - r_slopNormal.GetFloat();
for( j = 0; j < ( mesh->indexes.Num() / 3 ); j++ )
{
// construct triangles in reverse order
for( int k = 2; k >= 0; k-- )
{
int index = j * 3 + k;
if( index < 0 || index >= mesh->vertexes.Num() )
{
common->Error( "ConvertOBJToModelSurfaces: bad vertex index in ASE file %s", name.c_str() );
}
// collapse the position if it was slightly offset
v = vRemap[index];
// we may or may not have texcoords to compare
if( mesh->texcoords.Num() != 0 )
{
tv = index;
//tv = mesh->faces[j].tVertexNum[k];
if( tv < 0 || tv >= mesh->texcoords.Num() )
{
common->Error( "ConvertOBJToModelSurfaces: bad tex coord index in ASE file %s", name.c_str() );
}
// collapse the tex coord if it was slightly offset
tv = tvRemap[tv];
}
// we may or may not have normals to compare
if( normalsParsed )
{
normal = mesh->normals[ index ];
}
// we may or may not have colors to compare
/*
if( mesh->colorsParsed )
{
color = mesh->faces[j].vertexColors[k];
}
*/
// find a matching vert
for( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next )
{
if( mv->tv != tv )
{
continue;
}
if( *( unsigned* )mv->color != *( unsigned* )color )
{
continue;
}
if( !normalsParsed )
{
// if we are going to create the normals, just
// matching texcoords is enough
break;
}
if( mv->normal * normal > normalEpsilon )
{
break; // we already have this one
}
}
if( !mv )
{
// allocate a new match vert and link to hash chain
mv = &mvTable[ tri->numVerts ];
mv->v = v;
mv->tv = tv;
mv->normal = normal;
*( unsigned* )mv->color = *( unsigned* )color;
mv->next = NULL;
if( lastmv )
{
lastmv->next = mv;
}
else
{
mvHash[v] = mv;
}
tri->numVerts++;
}
tri->indexes[tri->numIndexes] = mv - mvTable;
tri->numIndexes++;
}
}
// allocate space for the indexes and copy them
if( tri->numIndexes > mesh->indexes.Num() )
{
common->FatalError( "ConvertOBJToModelSurfaces: index miscount in OBJ file %s", name.c_str() );
}
if( tri->numVerts > mesh->indexes.Num() )
{
common->FatalError( "ConvertOBJToModelSurfaces: vertex miscount in OBJ file %s", name.c_str() );
}
// now allocate and generate the combined vertexes
R_AllocStaticTriSurfVerts( tri, tri->numVerts );
for( j = 0; j < tri->numVerts; j++ )
{
mv = &mvTable[j];
tri->verts[ j ].Clear();
tri->verts[ j ].xyz = mesh->vertexes[ mv->v ];
tri->verts[ j ].SetNormal( mv->normal );
*( unsigned* )tri->verts[j].color = *( unsigned* )mv->color;
if( mesh->texcoords.Num() != 0 )
{
const idVec2& tv = mesh->texcoords[ mv->tv ];
float u = tv.x;
float v = tv.y;
tri->verts[j].SetTexCoord( u, v );
}
}
R_StaticFree( mvTable );
R_StaticFree( mvHash );
R_StaticFree( tvRemap );
R_StaticFree( vRemap );
// see if we need to merge with a previous surface of the same material
modelSurf = &this->surfaces[mergeTo[ objectNum ]];
srfTriangles_t* mergeTri = modelSurf->geometry;
if( !mergeTri )
{
modelSurf->geometry = tri;
}
else
{
modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
R_FreeStaticTriSurf( tri );
R_FreeStaticTriSurf( mergeTri );
}
}
return true;
}
// RB end
/*
=================
idRenderModelStatic::ConvertASEToModelSurfaces
@ -3292,6 +3632,31 @@ bool idRenderModelStatic::LoadDAE( const char* fileName, ID_TIME_T* sourceTimeSt
return loaded;
}
/*
=================
idRenderModelStatic::LoadOBJ
=================
*/
bool idRenderModelStatic::LoadOBJ( const char* fileName, ID_TIME_T* sourceTimeStamp )
{
objModel_t* obj;
obj = OBJ_Load( fileName );
if( obj == NULL )
{
return false;
}
// RB
*sourceTimeStamp = obj->timeStamp;
ConvertOBJToModelSurfaces( obj );
OBJ_Free( obj );
return true;
}
// RB end
//=============================================================================

View file

@ -342,8 +342,8 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool
idRenderModel* model = NULL;
// RB: added dae
if( ( extension.Icmp( "dae" ) == 0 ) || ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) || ( extension.Icmp( "flt" ) == 0 ) || ( extension.Icmp( "ma" ) == 0 ) )
// RB: Collada DAE and Wavefront OBJ
if( ( extension.Icmp( "dae" ) == 0 ) || ( extension.Icmp( "obj" ) == 0 ) || ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) || ( extension.Icmp( "flt" ) == 0 ) || ( extension.Icmp( "ma" ) == 0 ) )
{
model = new( TAG_MODEL ) idRenderModelStatic;
}

View file

@ -3,6 +3,7 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2012-2021 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -39,7 +40,8 @@ If you have questions concerning this license or the applicable additional terms
class idJointMat;
struct deformInfo_t;
class ColladaParser; // RB: Collada support
class ColladaParser; // RB: Collada support
struct objModel_t; // RB: Wavefront OBJ support
class idRenderModelStatic : public idRenderModel
{
@ -116,11 +118,13 @@ public:
void MakeDefaultModel();
bool LoadASE( const char* fileName, ID_TIME_T* sourceTimeStamp );
bool LoadDAE( const char* fileName, ID_TIME_T* sourceTimeStamp ); // RB
bool LoadLWO( const char* fileName, ID_TIME_T* sourceTimeStamp );
bool LoadMA( const char* filename, ID_TIME_T* sourceTimeStamp );
bool LoadDAE( const char* fileName, ID_TIME_T* sourceTimeStamp ); // RB
bool LoadOBJ( const char* fileName, ID_TIME_T* sourceTimeStamp ); // RB
bool ConvertDAEToModelSurfaces( const ColladaParser* dae ); // RB
bool ConvertOBJToModelSurfaces( const objModel_t* obj ); // RB
bool ConvertASEToModelSurfaces( const struct aseModel_s* ase );
bool ConvertLWOToModelSurfaces( const struct st_lwObject* lwo );
bool ConvertMAToModelSurfaces( const struct maModel_s* ma );

273
neo/renderer/Model_obj.cpp Normal file
View file

@ -0,0 +1,273 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2019 Justin Marshal (IcedTech)
Copyright (C) 2021 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "precompiled.h"
#include "Model_obj.h"
/*
======================================================================
Parses Wavefront export files. This parser is designed to work with Blender and 3D Studio Max
The goal is to parse the information into memory exactly as it is
represented in the file. Users of the data will then move it
into a form that is more convenient for them.
======================================================================
*/
/*
=================
OBJ_Parse
=================
*/
objModel_t* OBJ_Parse( const char* fileName, const char* objFileBuffer, int length )
{
objModel_t* model = new( TAG_MODEL ) objModel_t;
idLexer src;
idToken token, token2;
// total
idList<idVec3> vertexes;
idList<idVec2> texCoords;
idList<idVec3> normals;
int numCollectedVerts = 0;
// current object or group
//idList<idDrawVert> drawVerts;
idList<idVec3> objVertexes;
idList<idVec2> objTexCoords;
idList<idVec3> objNormals;
idList<triIndex_t> objIndices;
idStrStatic< MAX_OSPATH > material;
src.LoadMemory( objFileBuffer, length, fileName, 0 );
while( true )
{
if( !src.ReadToken( &token ) )
{
break;
}
if( token == "v" )
{
idVec3 vertex;
#if 0
vertex.x = src.ParseFloat();
vertex.z = src.ParseFloat();
vertex.y = -src.ParseFloat();
#else
vertex.x = src.ParseFloat();
vertex.y = src.ParseFloat();
vertex.z = src.ParseFloat();
#endif
vertexes.Append( vertex );
}
else if( token == "vt" )
{
idVec2 st;
st.x = src.ParseFloat();
st.y = 1.0f - src.ParseFloat();
texCoords.Append( st );
}
else if( token == "#" )
{
idStr line;
// Skip comments
src.ReadRestOfLine( line );
}
else if( token == "mtllib" )
{
idStr line;
// We don't use obj materials.
src.ReadRestOfLine( line );
}
else if( token == "s" )
{
idStr line;
src.ReadRestOfLine( line );
}
else if( token == "o" || token == "g" )
{
idStr line;
src.ReadRestOfLine( line );
if( objVertexes.Num() )
{
objObject_t* obj = new objObject_t;
obj->material = material;
obj->vertexes = objVertexes;
obj->texcoords = objTexCoords;
obj->normals = objNormals;
obj->indexes = objIndices;
numCollectedVerts += objVertexes.Num();
objVertexes.Clear();
objTexCoords.Clear();
objNormals.Clear();
objIndices.Clear();
model->objects.Append( obj );
}
}
else if( token == "usemtl" )
{
idStr line;
src.ReadRestOfLine( line );
material = line;
}
else if( token == "vn" )
{
idVec3 normal;
normal.x = src.ParseFloat();
normal.y = src.ParseFloat();
normal.z = src.ParseFloat();
normals.Append( normal );
}
else if( token == "f" )
{
idStr line;
int vertexIndex[3];
int uvIndex[3];
int normalIndex[3];
src.ReadRestOfLine( line );
int matches = sscanf( line.c_str(), "%d/%d/%d %d/%d/%d %d/%d/%d", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2] );
if( matches != 9 )
{
common->FatalError( "Failed to parse face line" );
}
for( int i = 0; i < 3; i++ )
{
idDrawVert drawVert;
int vertidx = vertexIndex[i] - 1;
int texidx = uvIndex[i] - 1;
int normalidx = normalIndex[i] - 1;
//drawVert.xyz = vertexes[vertidx];
//drawVert.SetTexCoord( texCoords[texidx] );
//drawVert.SetNormal( normals[normalidx] );
objVertexes.Append( vertexes[vertidx] );
objTexCoords.Append( texCoords[texidx] );
objNormals.Append( normals[normalidx] );
//objIndices.Append( vertidx - numCollectedVerts );
objIndices.Append( objIndices.Num() );
}
}
else
{
common->FatalError( "idRenderModelStatic::ParseOBJ: Unknown or unexpected token %s", token.c_str() );
}
}
if( objVertexes.Num() )
{
objObject_t* obj = new objObject_t;
obj->material = material;
obj->vertexes = objVertexes;
obj->texcoords = objTexCoords;
obj->normals = objNormals;
obj->indexes = objIndices;
model->objects.Append( obj );
}
return model;
}
/*
=================
OBJ_Load
=================
*/
objModel_t* OBJ_Load( const char* fileName )
{
char* objBuffer;
ID_TIME_T timeStamp;
objModel_t* obj;
int objBufferLen = fileSystem->ReadFile( fileName, ( void** )&objBuffer, &timeStamp );
if( objBufferLen <= 0 || objBuffer == nullptr )
{
return NULL;
}
obj = OBJ_Parse( fileName, objBuffer, objBufferLen );
obj->timeStamp = timeStamp;
fileSystem->FreeFile( objBuffer );
return obj;
}
/*
=================
OBJ_Free
=================
*/
void OBJ_Free( objModel_t* model )
{
if( !model )
{
return;
}
for( int i = 0; i < model->objects.Num(); i++ )
{
objObject_t* obj = model->objects[i];
delete obj;
}
model->objects.Clear();
delete model;
}

63
neo/renderer/Model_obj.h Normal file
View file

@ -0,0 +1,63 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2021 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __MODEL_OBJ_H__
#define __MODEL_OBJ_H__
/*
===============================================================================
Wavefront OBJ loader.
This is meant to be a very simple model format and we don't even care for .mtl files
because we want all material properties to be defined through the D3 material system
===============================================================================
*/
struct objObject_t
{
idStrStatic< MAX_OSPATH > material;
idList<idVec3> vertexes;
idList<idVec2> texcoords;
idList<idVec3> normals;
idList<triIndex_t> indexes;
};
struct objModel_t
{
ID_TIME_T timeStamp;
idList<objObject_t*, TAG_MODEL> objects;
};
objModel_t* OBJ_Load( const char* fileName );
void OBJ_Free( objModel_t* obj );
#endif /* !__MODEL_ASE_H__ */