From c664c9e9400220a685c3ba6035950bf0891c72f6 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Wed, 16 Mar 2016 23:18:47 +0100 Subject: [PATCH] Save rendermodels as OBJ if postLoadExportModels is set --- neo/renderer/Model.cpp | 121 ++++++++++++++++++++++++++++++ neo/renderer/Model.h | 4 + neo/renderer/ModelManager.cpp | 37 ++++++++- neo/renderer/Model_local.h | 4 + neo/renderer/RenderWorld_load.cpp | 6 +- 5 files changed, 166 insertions(+), 6 deletions(-) diff --git a/neo/renderer/Model.cpp b/neo/renderer/Model.cpp index 17f64f0c..0b265f0b 100644 --- a/neo/renderer/Model.cpp +++ b/neo/renderer/Model.cpp @@ -714,6 +714,127 @@ void idRenderModelStatic::WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp file->WriteBig( hasShadowCastingSurfaces ); } +// RB begin +void idRenderModelStatic::ExportOBJ( idFile* objFile, idFile* mtlFile, ID_TIME_T* _timeStamp ) const +{ + if( objFile == NULL || mtlFile == NULL ) + { + common->Printf( "Failed to ExportOBJ\n" ); + return; + } + + //objFile->Printf( "// generated by %s\n//\n\n", ENGINE_VERSION ); + + int numVerts = 0; + idList< const idMaterial* > materials; + + for( int i = 0; i < surfaces.Num(); i++ ) + { + // shadow models use numVerts but have no verts + if( ( surfaces[i].geometry != NULL ) && ( surfaces[i].geometry->numVerts > 0 ) && ( surfaces[i].geometry->numIndexes > 0 ) && ( surfaces[i].geometry->verts != NULL ) ) + { + objFile->Printf( "o Geometry.%i\n", surfaces[i].id ); + + srfTriangles_t& tri = *surfaces[i].geometry; + + //file->WriteVec3( tri.bounds[0] ); + //file->WriteVec3( tri.bounds[1] ); + + // TODO print additional info ? + + //file->WriteBig( tri.generateNormals ); + //file->WriteBig( tri.tangentsCalculated ); + //file->WriteBig( tri.perfectHull ); + //file->WriteBig( tri.referencedIndexes ); + + if( tri.numVerts > 0 && tri.verts != NULL ) + { + for( int j = 0; j < tri.numVerts; j++ ) + { + objFile->Printf( "v %1.6f %1.6f %1.6f\n", tri.verts[j].xyz.x, tri.verts[j].xyz.y, tri.verts[j].xyz.z ); + } + + for( int j = 0; j < tri.numVerts; j++ ) + { + const idVec2 vST = tri.verts[j].GetTexCoord(); + + objFile->Printf( "vt %1.6f %1.6f\n", vST.x, 1.0f - vST.y ); + } + + for( int j = 0; j < tri.numVerts; j++ ) + { + const idVec3 n = tri.verts[j].GetNormalRaw(); + + objFile->Printf( "vn %1.6f %1.6f %1.6f\n", n.x, n.y, n.z ); + } + + //file->WriteBigArray( tri.verts[j].st, 2 ); + //file->WriteBigArray( tri.verts[j].normal, 4 ); + //file->WriteBigArray( tri.verts[j].tangent, 4 ); + //file->WriteBigArray( tri.verts[j].color, sizeof( tri.verts[j].color ) / sizeof( tri.verts[j].color[0] ) ); + //file->WriteBigArray( tri.verts[j].color2, sizeof( tri.verts[j].color2 ) / sizeof( tri.verts[j].color2[0] ) ); + } + + if( surfaces[i].shader != NULL && surfaces[i].shader->GetName() != NULL ) + { + objFile->Printf( "usemtl %s\n", surfaces[i].shader->GetName() ); + + materials.AddUnique( surfaces[i].shader ); + } + + objFile->Printf( "s 1\n" ); + + for( int j = 0; j < tri.numIndexes; j += 3 ) + { + objFile->Printf( "f %i/%i/%i %i/%i/%i %i/%i/%i\n", + tri.indexes[j + 2] + 1 + numVerts, + tri.indexes[j + 2] + 1 + numVerts, + tri.indexes[j + 2] + 1 + numVerts, + + tri.indexes[j + 1] + 1 + numVerts, + tri.indexes[j + 1] + 1 + numVerts, + tri.indexes[j + 1] + 1 + numVerts, + + tri.indexes[j + 0] + 1 + numVerts, + tri.indexes[j + 0] + 1 + numVerts, + tri.indexes[j + 0] + 1 + numVerts ); + } + + objFile->Printf( "\n" ); + + numVerts += tri.numVerts; + } + } + + for( int i = 0; i < materials.Num(); i++ ) + { + const idMaterial* material = materials[i]; + + mtlFile->Printf( "newmtl %s\n", material->GetName() ); + + if( material->GetFastPathDiffuseImage() ) + { + idStr path = material->GetFastPathDiffuseImage()->GetName(); + path.SlashesToBackSlashes(); + path.DefaultFileExtension( ".tga" ); + + mtlFile->Printf( "\tmap_Kd //..\\..\\..\\%s\n", path.c_str() ); + } + else if( material->GetEditorImage() ) + { + idStr path = material->GetEditorImage()->GetName(); + path.SlashesToBackSlashes(); + path.DefaultFileExtension( ".tga" ); + + mtlFile->Printf( "\tmap_Kd //..\\..\\..\\%s\n", path.c_str() ); + } + + + mtlFile->Printf( "\n" ); + } +} +// RB end + /* ================ idRenderModelStatic::LoadModel diff --git a/neo/renderer/Model.h b/neo/renderer/Model.h index 19d020e9..93ddfdb8 100644 --- a/neo/renderer/Model.h +++ b/neo/renderer/Model.h @@ -172,6 +172,10 @@ public: virtual void WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp = NULL ) const = 0; virtual bool SupportsBinaryModel() = 0; + // RB begin + virtual void ExportOBJ( idFile* objFile, idFile* mtlFile, ID_TIME_T* _timeStamp = NULL ) const = 0; + // RB end + // renderBump uses this to load the very high poly count models, skipping the // shadow and tangent generation, along with some surface cleanup to make it load faster virtual void PartialInitFromFile( const char* fileName ) = 0; diff --git a/neo/renderer/ModelManager.cpp b/neo/renderer/ModelManager.cpp index 37aa10e2..07eb8d45 100644 --- a/neo/renderer/ModelManager.cpp +++ b/neo/renderer/ModelManager.cpp @@ -32,9 +32,13 @@ If you have questions concerning this license or the applicable additional terms #include "Model_local.h" #include "tr_local.h" // just for R_FreeWorldInteractions and R_CreateWorldInteractions -idCVar r_binaryLoadRenderModels( "r_binaryLoadRenderModels", "1", 0, "enable binary load/write of render models" ); +idCVar binaryLoadRenderModels( "binaryLoadRenderModels", "1", 0, "enable binary load/write of render models" ); idCVar preload_MapModels( "preload_MapModels", "1", CVAR_SYSTEM | CVAR_BOOL, "preload models during begin or end levelload" ); +// RB begin +idCVar postLoadExportModels( "postLoadExportModels", "0", CVAR_BOOL | CVAR_RENDERER, "export models after loading to OBJ model format" ); +// RB end + class idRenderModelManagerLocal : public idRenderModelManager { public: @@ -305,7 +309,7 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool // Get the timestamp on the original file, if it's newer than what is stored in binary model, regenerate it ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( canonical ); - if( model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) + if( model->SupportsBinaryModel() && binaryLoadRenderModels.GetBool() ) { idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) ); model->PurgeModel(); @@ -372,7 +376,7 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) ); - if( !model->SupportsBinaryModel() || !r_binaryLoadRenderModels.GetBool() ) + if( !model->SupportsBinaryModel() || !binaryLoadRenderModels.GetBool() ) { model->InitFromFile( canonical ); } @@ -436,6 +440,33 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool fileSystem->AddModelPreload( model->Name() ); } + // RB begin + if( postLoadExportModels.GetBool() && ( model != defaultModel && model != beamModel && model != spriteModel ) ) + { + idStrStatic< MAX_OSPATH > exportedFileName; + + exportedFileName = "exported/rendermodels/"; + exportedFileName.AppendPath( canonical ); + exportedFileName.SetFileExtension( ".obj" ); + + ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( canonical ); + ID_TIME_T timeStamp = fileSystem->GetTimestamp( exportedFileName ); + + // TODO only update if generated has changed + + //if( timeStamp == FILE_NOT_FOUND_TIMESTAMP ) + { + idFileLocal objFile( fileSystem->OpenFileWrite( exportedFileName, "fs_basepath" ) ); + idLib::Printf( "Writing %s\n", exportedFileName.c_str() ); + + exportedFileName.SetFileExtension( ".mtl" ); + idFileLocal mtlFile( fileSystem->OpenFileWrite( exportedFileName, "fs_basepath" ) ); + + model->ExportOBJ( objFile, mtlFile ); + } + } + // RB end + AddModel( model ); return model; diff --git a/neo/renderer/Model_local.h b/neo/renderer/Model_local.h index 3b46719c..b954925c 100644 --- a/neo/renderer/Model_local.h +++ b/neo/renderer/Model_local.h @@ -57,6 +57,10 @@ public: return true; } + // RB begin + virtual void ExportOBJ( idFile* objFile, idFile* mtlFile, ID_TIME_T* _timeStamp = NULL ) const; + // RB end + virtual void PartialInitFromFile( const char* fileName ); virtual void PurgeModel(); virtual void Reset() {}; diff --git a/neo/renderer/RenderWorld_load.cpp b/neo/renderer/RenderWorld_load.cpp index 34d9dc94..763a13c8 100644 --- a/neo/renderer/RenderWorld_load.cpp +++ b/neo/renderer/RenderWorld_load.cpp @@ -134,7 +134,7 @@ idRenderModel* idRenderWorldLocal::ReadBinaryModel( idFile* fileIn ) return NULL; } -extern idCVar r_binaryLoadRenderModels; +extern idCVar binaryLoadRenderModels; /* ================ @@ -298,7 +298,7 @@ idRenderModel* idRenderWorldLocal::ParseModel( idLexer* src, const char* mapName model->FinishSurfaces(); - if( fileOut != NULL && model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) + if( fileOut != NULL && model->SupportsBinaryModel() && binaryLoadRenderModels.GetBool() ) { model->WriteBinaryModel( fileOut, &mapTimeStamp ); } @@ -398,7 +398,7 @@ idRenderModel* idRenderWorldLocal::ParseShadowModel( idLexer* src, idFile* fileO // NOTE: we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc. - if( fileOut != NULL && model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) + if( fileOut != NULL && model->SupportsBinaryModel() && binaryLoadRenderModels.GetBool() ) { model->WriteBinaryModel( fileOut, &mapTimeStamp ); }