Merge branch 'master' into 635-nvrhi4

This commit is contained in:
Robert Beckebans 2022-11-18 16:51:40 +01:00
commit 37127cde0e
27 changed files with 40925 additions and 196 deletions

23483
base/_tb/fgd/DOOM-3-all.fgd Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,224 @@
// DOOM 3 BFG game definition file (.fgd) generated by RBDOOM 3 BFG 1.4.0
@PointClass base(ammo_belt_small) = ammo_belt_small_mp : "No description"
[
inv_ammo_belt(string) : "" : "120"
]
@PointClass base(ammo_bullets_large) = ammo_bullets_large_mp : "No description"
[
]
@PointClass base(ammo_bullets_small) = ammo_bullets_small_mp : "No description"
[
]
@PointClass base(ammo_cells_large) = ammo_cells_large_mp : "No description"
[
]
@PointClass base(ammo_cells_small) = ammo_cells_small_mp : "No description"
[
]
@PointClass base(ammo_clip_large) = ammo_clip_large_mp : "No description"
[
]
@PointClass base(ammo_clip_small) = ammo_clip_small_mp : "No description"
[
]
@PointClass base(ammo_grenade_small) = ammo_grenade_small_mp : "No description"
[
]
@PointClass base(ammo_rockets_large) = ammo_rockets_large_mp : "No description"
[
]
@PointClass base(ammo_rockets_small) = ammo_rockets_small_mp : "No description"
[
]
@PointClass base(ammo_shells_large) = ammo_shells_large_mp : "No description"
[
]
@PointClass base(ammo_shells_small) = ammo_shells_small_mp : "No description"
[
]
@PointClass base(item_armor_security) = item_armor_security_mp : "No description"
[
]
@PointClass base(item_armor_shard) = item_armor_shard_mp : "No description"
[
]
@PointClass base(item_medkit) = item_medkit_mp : "No description"
[
]
@PointClass base(item_medkit_small) = item_medkit_small_mp : "No description"
[
]
@PointClass base(moveable_item_bfg) = moveable_item_bfg_mp : "No description"
[
]
@PointClass base(moveable_item_chaingun) = moveable_item_chaingun_mp : "No description"
[
]
@PointClass base(moveable_item_chainsaw) = moveable_item_chainsaw_mp : "No description"
[
]
@PointClass base(moveable_item_grenades) = moveable_item_grenades_mp : "No description"
[
damage(string) : "" : "80"
]
@PointClass base(moveable_item_machinegun) = moveable_item_machinegun_mp : "No description"
[
]
@PointClass base(moveable_item_pistol) = moveable_item_pistol_mp : "No description"
[
]
@PointClass base(moveable_item_plasmagun) = moveable_item_plasmagun_mp : "No description"
[
]
@PointClass base(moveable_item_rocketlauncher) = moveable_item_rocketlauncher_mp : "No description"
[
]
@PointClass base(moveable_item_shotgun) = moveable_item_shotgun_mp : "No description"
[
]
@PointClass base(item_team_default) model({ "path": "_tb/models/ctf/ctf_flag.obj" }) = team_ctf_blueflag : "CTF: the blue flag
"
[
model(string) : "" : "models/ctf/ctf_flag.ase"
team(string) : "" : "1"
spin(string) : "" : "0"
skin(string) : "" : "skins/ctf/blue_flag"
skin_carried(string) : "" : "skins/ctf/blue_flag_not_idle"
inv_name(string) : "" : "blueflag"
script_taken(string) : "" : "blue_flag_taken"
script_dropped(string) : "" : ""
script_returned(string) : "" : "blue_flag_returned"
script_captured(string) : "" : "blue_flag_captured"
bouncyness(string) : "" : "0"
friction(string) : "" : "0.8"
density(string) : "" : "500"
gravity(string) : "" : "-35"
pickupDelay(string) : "" : "500"
net_dynamic(string) : "" : "1"
noshadows(string) : "" : "1"
]
@PointClass base(moveable_item_default) model({ "path": "_tb/models/ctf/flagfx/blue_ember.obj" }) = team_ctf_blueflag_nugget : "No description"
[
model(string) : "" : "models/ctf/flagfx/blue_ember.ase"
clipmodel(string) : "" : "models/ctf/flagfx/blue_ember.ase"
density(string) : "" : "0.5"
friction(string) : "" : "0.5"
bouncyness(string) : "" : "0.3"
smoke_trail(string) : "" : "blue_flag_smoke_fx.prt"
nopulse(string) : "" : "1"
repeatSmoke(string) : "" : "1"
networkSync(string) : "" : "1"
]
@PointClass base(item_team_default) model({ "path": "_tb/models/ctf/ctf_flag.obj" }) = team_ctf_redflag : "CTF: the red flag
"
[
model(string) : "" : "models/ctf/ctf_flag.ase"
team(string) : "" : "0"
spin(string) : "" : "0"
skin(string) : "" : "skins/ctf/red_flag"
skin_carried(string) : "" : "skins/ctf/red_flag_not_idle"
inv_name(string) : "" : "redflag"
script_taken(string) : "" : "red_flag_taken"
script_dropped(string) : "" : ""
script_returned(string) : "" : "red_flag_returned"
script_captured(string) : "" : "red_flag_captured"
bouncyness(string) : "" : "0"
friction(string) : "" : "0.8"
density(string) : "" : "500"
gravity(string) : "" : "-35"
pickupDelay(string) : "" : "500"
net_dynamic(string) : "" : "1"
noshadows(string) : "" : "1"
]
@PointClass base(moveable_item_default) model({ "path": "_tb/models/ctf/flagfx/red_ember.obj" }) = team_ctf_redflag_nugget : "No description"
[
model(string) : "" : "models/ctf/flagfx/red_ember.ase"
clipmodel(string) : "" : "models/ctf/flagfx/red_ember.ase"
density(string) : "" : "0.5"
friction(string) : "" : "0.5"
bouncyness(string) : "" : "0.3"
smoke_trail(string) : "" : "red_flag_smoke_fx.prt"
nopulse(string) : "" : "1"
repeatSmoke(string) : "" : "1"
networkSync(string) : "" : "1"
]
@PointClass base(weapon_bfg) = weapon_bfg_mp : "No description"
[
]
@PointClass base(weapon_chaingun) = weapon_chaingun_mp : "No description"
[
spread(string) : "" : "1"
inv_ammo_belt(string) : "" : "60"
]
@PointClass base(weapon_chainsaw) = weapon_chainsaw_mp : "No description"
[
]
@PointClass base(weapon_handgrenade) = weapon_handgrenade_mp : "No description"
[
]
@PointClass base(weapon_machinegun) = weapon_machinegun_mp : "No description"
[
spread(string) : "" : "1"
inv_ammo_clip(string) : "" : "60"
]
@PointClass base(weapon_pistol) = weapon_pistol_mp : "No description"
[
]
@PointClass base(weapon_plasmagun) = weapon_plasmagun_mp : "No description"
[
inv_ammo_cells(string) : "" : "30"
clipSize(string) : "" : "30"
]
@PointClass base(weapon_rocketlauncher) model({ "path": "_tb/models/weapons/rocketlauncher/mp_rocketlauncher.obj" }) = weapon_rocketlauncher_mp : "No description"
[
model(string) : "" : "models/weapons/rocketlauncher/mp_rocketlauncher.lwo"
]
@PointClass base(weapon_shotgun_double) model({ "path": "_tb/models/weapons/doublebarrel/doublebarrel_w.obj" }) = weapon_shotgun_double_mp : "No description"
[
model(string) : "" : "models/weapons/doublebarrel/doublebarrel_w.lwo"
]
@PointClass base(weapon_shotgun) = weapon_shotgun_mp : "No description"
[
inv_ammo_shells(string) : "" : "8"
spread(string) : "" : "11"
]

10523
base/_tb/fgd/DOOM-3-slim.fgd Normal file

File diff suppressed because it is too large Load diff

View file

@ -2,8 +2,8 @@ entityDef misc_model
{
"inherit" "func_static"
"editor_color" "0 .5 .8"
"editor_mins" "?"
"editor_maxs" "?"
"editor_mins" "-8 -8 0"
"editor_maxs" "8 8 16"
"editor_rotatable" "1"
"editor_usage" "Inherits from a func_static but uses a model and is a FGD PointClass so it displays correctly in TrenchBroom."
@ -13,32 +13,24 @@ entityDef misc_model
entityDef func_door_model
{
"inherit" "func_door"
"editor_mins" "-8 -8 0"
"editor_maxs" "8 8 16"
"editor_usage" "Inherits from a func_door but uses a model and is a FGD PointClass so it displays correctly in TrenchBroom."
"editor_usage1" "Use it to place all kinds of models"
}
/*
already exists and is called func_mover_amodel ...
entityDef func_mover_model
{
"inherit" "func_mover"
"editor_usage" "Inherits from a func_mover but uses a model and is a FGD PointClass so it displays correctly in TrenchBroom."
}
*/
entityDef func_rotating_model
{
"inherit" "func_rotating"
"editor_mins" "-8 -8 0"
"editor_maxs" "8 8 16"
"editor_usage" "Inherits from a func_rotating but uses a model and is a FGD PointClass so it displays correctly in TrenchBroom."
}
entityDef func_plat_model
{
"inherit" "func_plat"
"editor_mins" "-8 -8 0"
"editor_maxs" "8 8 16"
"editor_usage" "Inherits from a func_plat but uses a model and is a FGD PointClass so it displays correctly in TrenchBroom."
}

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) 2022 Harrie van Ginneken
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -30,7 +31,10 @@ If you have questions concerning this license or the applicable additional terms
#pragma hdrstop
#include "Game_local.h"
#include "gltfParser.h"
static const idMat4 blenderToDoomTransform( idAngles( 0.0f, 0.0f, 90 ).ToMat3(), vec3_origin );
/*
===============================================================================
@ -363,6 +367,10 @@ void idCameraAnim::LoadAnim()
int i;
idStr filename;
const char* key;
idStr gltfFileName;
int animID = -1;
idStr animName;
bool isGLTF = false;
key = spawnArgs.GetString( "anim" );
if( !key )
@ -373,86 +381,102 @@ void idCameraAnim::LoadAnim()
filename = spawnArgs.GetString( va( "anim %s", key ) );
if( !filename.Length() )
{
// RB: TrenchBroom interop use anim.<name> instead so we can build this up using the FGD files
filename = spawnArgs.GetString( va( "anim.%s", key ) );
if( !filename.Length() )
gltfFileName = key;
gltfManager::ExtractIdentifier( gltfFileName, animID, animName );
if( animName.Length() )
{
gameLocal.Error( "Missing 'anim.%s' key on '%s'", key, name.c_str() );
isGLTF = true;
}
// RB end
}
filename.SetFileExtension( MD5_CAMERA_EXT );
if( !parser.LoadFile( filename ) )
{
gameLocal.Error( "Unable to load '%s' on '%s'", filename.c_str(), name.c_str() );
}
cameraCuts.Clear();
cameraCuts.SetGranularity( 1 );
camera.Clear();
camera.SetGranularity( 1 );
parser.ExpectTokenString( MD5_VERSION_STRING );
version = parser.ParseInt();
if( version != MD5_VERSION )
{
parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION );
}
// skip the commandline
parser.ExpectTokenString( "commandline" );
parser.ReadToken( &token );
// parse num frames
parser.ExpectTokenString( "numFrames" );
numFrames = parser.ParseInt();
if( numFrames <= 0 )
{
parser.Error( "Invalid number of frames: %d", numFrames );
}
// parse framerate
parser.ExpectTokenString( "frameRate" );
frameRate = parser.ParseInt();
if( frameRate <= 0 )
{
parser.Error( "Invalid framerate: %d", frameRate );
}
// parse num cuts
parser.ExpectTokenString( "numCuts" );
numCuts = parser.ParseInt();
if( ( numCuts < 0 ) || ( numCuts > numFrames ) )
{
parser.Error( "Invalid number of camera cuts: %d", numCuts );
}
// parse the camera cuts
parser.ExpectTokenString( "cuts" );
parser.ExpectTokenString( "{" );
cameraCuts.SetNum( numCuts );
for( i = 0; i < numCuts; i++ )
{
cameraCuts[ i ] = parser.ParseInt();
if( ( cameraCuts[ i ] < 1 ) || ( cameraCuts[ i ] >= numFrames ) )
else
{
parser.Error( "Invalid camera cut" );
// RB: TrenchBroom interop use anim.<name> instead so we can build this up using the FGD files
filename = spawnArgs.GetString( va( "anim.%s", key ) );
if( !filename.Length() )
{
gameLocal.Error( "Missing 'anim.%s' key on '%s'", key, name.c_str() );
}
// RB end
}
}
parser.ExpectTokenString( "}" );
// parse the camera frames
parser.ExpectTokenString( "camera" );
parser.ExpectTokenString( "{" );
camera.SetNum( numFrames );
for( i = 0; i < numFrames; i++ )
if( isGLTF )
{
parser.Parse1DMatrix( 3, camera[ i ].t.ToFloatPtr() );
parser.Parse1DMatrix( 3, camera[ i ].q.ToFloatPtr() );
camera[ i ].fov = parser.ParseFloat();
gltfLoadAnim( gltfFileName, animName );
}
else
{
filename.SetFileExtension( MD5_CAMERA_EXT );
if( !parser.LoadFile( filename ) )
{
gameLocal.Error( "Unable to load '%s' on '%s'", filename.c_str(), name.c_str() );
}
cameraCuts.Clear();
cameraCuts.SetGranularity( 1 );
camera.Clear();
camera.SetGranularity( 1 );
parser.ExpectTokenString( MD5_VERSION_STRING );
version = parser.ParseInt();
if( version != MD5_VERSION )
{
parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION );
}
// skip the commandline
parser.ExpectTokenString( "commandline" );
parser.ReadToken( &token );
// parse num frames
parser.ExpectTokenString( "numFrames" );
numFrames = parser.ParseInt();
if( numFrames <= 0 )
{
parser.Error( "Invalid number of frames: %d", numFrames );
}
// parse framerate
parser.ExpectTokenString( "frameRate" );
frameRate = parser.ParseInt();
if( frameRate <= 0 )
{
parser.Error( "Invalid framerate: %d", frameRate );
}
// parse num cuts
parser.ExpectTokenString( "numCuts" );
numCuts = parser.ParseInt();
if( ( numCuts < 0 ) || ( numCuts > numFrames ) )
{
parser.Error( "Invalid number of camera cuts: %d", numCuts );
}
// parse the camera cuts
parser.ExpectTokenString( "cuts" );
parser.ExpectTokenString( "{" );
cameraCuts.SetNum( numCuts );
for( i = 0; i < numCuts; i++ )
{
cameraCuts[i] = parser.ParseInt();
if( ( cameraCuts[i] < 1 ) || ( cameraCuts[i] >= numFrames ) )
{
parser.Error( "Invalid camera cut" );
}
}
parser.ExpectTokenString( "}" );
// parse the camera frames
parser.ExpectTokenString( "camera" );
parser.ExpectTokenString( "{" );
camera.SetNum( numFrames );
for( i = 0; i < numFrames; i++ )
{
parser.Parse1DMatrix( 3, camera[i].t.ToFloatPtr() );
parser.Parse1DMatrix( 3, camera[i].q.ToFloatPtr() );
camera[i].fov = parser.ParseFloat();
}
parser.ExpectTokenString( "}" );
}
parser.ExpectTokenString( "}" );
}
/*
@ -692,6 +716,123 @@ void idCameraAnim::Event_Activate( idEntity* _activator )
}
}
void idCameraAnim::gltfLoadAnim( idStr gltfFileName, idStr animName )
{
//we dont wat to load the gltb all the time. write custom binary format !
GLTF_Parser gltf;
if( gltf.Load( gltfFileName ) )
{
ID_TIME_T timeStamp = fileSystem->GetTimestamp( gltfFileName );
gltfData* data = gltf.currentAsset;
auto& accessors = data->AccessorList();
auto& nodes = data->NodeList();
gltfNode* cameraNode = data->GetNode( name );
assert( cameraNode );
gltfAnimation* anim = data->GetAnimation( animName, data->GetNodeIndex( cameraNode ) );
assert( anim );
cameraCuts.Clear();
cameraCuts.SetGranularity( 1 );
camera.Clear();
camera.SetGranularity( 1 );
frameRate = 24;
for( auto channel : anim->channels )
{
auto* sampler = anim->samplers[channel->sampler];
auto* input = accessors[sampler->input];
auto* output = accessors[sampler->output];
auto* target = nodes[channel->target.node];
idList<float>& timeStamps = data->GetAccessorView( input );
int frames = timeStamps.Num();
if( !camera.Num() )
{
cameraFrame_t t;
t.fov = 90;
t.q = mat3_identity.ToCQuat();
t.t = vec3_origin;
for( int i = 0; i < frames; i++ )
{
camera.Alloc() = t;
}
}
//This has to be replaced for correct interpolation between frames
for( int i = 0; i < frames; i++ )
{
cameraFrame_t& cameraFrame = camera[i];
cameraFrame.fov = 90.0f;
switch( channel->target.TRS )
{
default:
break;
case gltfAnimation_Channel_Target::none:
break;
case gltfAnimation_Channel_Target::rotation:
{
idList<idQuat*>& values = data->GetAccessorView<idQuat>( output );
if( values.Num() > i )
{
idQuat q = ( *values[i] );
q = idAngles( 90.0f, 0.0, -90.0f ).ToQuat()
* q.Inverse()
* blenderToDoomTransform.ToMat3().ToQuat();
cameraFrame.q = q.ToCQuat();
//This has to be replaced with correct interpolation between frames.
if( ( ( i + 1 ) == frames ) && ( frames < camera.Num() ) )
{
while( i++ < camera.Num() - 1 )
{
camera[i].q = cameraFrame.q;
}
}
}
}
break;
case gltfAnimation_Channel_Target::translation:
{
idList<idVec3*>& values = data->GetAccessorView<idVec3>( output );
if( values.Num() > i )
{
idVec3 val = *values[i];
cameraFrame.t = blenderToDoomTransform * val;
//This has to be replaced with correct interpolation between frames.
if( ( ( i + 1 ) == frames ) && ( frames < camera.Num() ) )
{
while( i++ < camera.Num() - 1 )
{
camera[i].t = cameraFrame.t;
}
}
}
}
break;
case gltfAnimation_Channel_Target::scale:
idList<idVec3*>& values = data->GetAccessorView<idVec3>( output );
if( values.Num() > i )
{
gameLocal.Printf( "^5Frame: ^7%i ignored scale on /%s \n\n\n", i, anim->name.c_str() );
}
break;
}
}
}
}
else
{
gameLocal.Error( "Missing 'anim' key on '%s'", name.c_str() );
}
}
/*
===============
idCameraAnim::Event_Start

View file

@ -131,6 +131,8 @@ private:
void Event_Stop();
void Event_SetCallback();
void Event_Activate( idEntity* activator );
void gltfLoadAnim( idStr gltfFileName, idStr animName );
};
#endif /* !__GAME_CAMERA_H__ */

View file

@ -31,7 +31,6 @@ If you have questions concerning this license or the applicable additional terms
#include "../Game_local.h"
//should go before release?
#include "renderer/Model_gltf.h"
idCVar binaryLoadAnim( "binaryLoadAnim", "1", 0, "enable binary load/write of idMD5Anim" );
@ -158,7 +157,7 @@ bool idMD5Anim::Reload()
filename = name;
Free();
return LoadAnim( filename );
return LoadAnim( filename, &importOptions );
}
/*
@ -177,7 +176,7 @@ size_t idMD5Anim::Allocated() const
idMD5Anim::LoadAnim
====================
*/
bool idMD5Anim::LoadAnim( const char* filename )
bool idMD5Anim::LoadAnim( const char* filename, const idImportOptions* options )
{
idLexer parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT );
idToken token;
@ -189,38 +188,39 @@ bool idMD5Anim::LoadAnim( const char* filename )
generatedFileName.AppendPath( filename );
generatedFileName.SetFileExtension( ".bMD5anim" );
idStr gltfFileName = idStr( filename );
int gltfAnimId = -1;
idStr gltfAnimName;
// Get the timestamp on the original file, if it's newer than what is stored in binary model, regenerate it
ID_TIME_T sourceTimeStamp;
bool isGLTF = ( extension.Icmp( GLTF_GLB_EXT ) == 0 ) || ( extension.Icmp( GLTF_EXT ) == 0 ) ;
bool isGLTF = ( extension.Icmp( GLTF_GLB_EXT ) == 0 ) || ( extension.Icmp( GLTF_EXT ) == 0 );
if( isGLTF )
{
idStr gltfFileName = idStr( filename );
int gltfAnimId = -1;
idStr gltfAnimName;
gltfManager::ExtractIdentifier( gltfFileName, gltfAnimId, gltfAnimName );
sourceTimeStamp = fileSystem->GetTimestamp( gltfFileName );
importOptions = *options;
}
else
{
sourceTimeStamp = fileSystem->GetTimestamp( filename );
}
// glTF2 animations will be saved straight to .bMD5anim files instead of the old ASCII format
idFile* fileptr = fileSystem->OpenFileReadMemory( generatedFileName );
bool doWrite = false;
if( fileptr == nullptr && isGLTF )
{
fileptr = idRenderModelGLTF::GetAnimBin( filenameStr , sourceTimeStamp );
fileptr = idRenderModelGLTF::GetAnimBin( filenameStr, sourceTimeStamp, options );
doWrite = fileptr != nullptr;
}
idFileLocal file( fileptr );
if( binaryLoadAnim.GetBool() && LoadBinary( file, sourceTimeStamp ) )
if( ( binaryLoadAnim.GetBool() || isGLTF ) && LoadBinary( file, sourceTimeStamp ) )
{
name = filename;
if( cvarSystem->GetCVarBool( "fs_buildresources" ) )
@ -228,6 +228,7 @@ bool idMD5Anim::LoadAnim( const char* filename )
// for resource gathering write this anim to the preload file for this map
fileSystem->AddAnimPreload( name );
}
if( doWrite && binaryLoadAnim.GetBool() )
{
idLib::Printf( "Writing %s\n", generatedFileName.c_str() );
@ -1197,7 +1198,7 @@ void idAnimManager::Shutdown()
idAnimManager::GetAnim
====================
*/
idMD5Anim* idAnimManager::GetAnim( const char* name )
idMD5Anim* idAnimManager::GetAnim( const char* name, const idImportOptions* options )
{
idMD5Anim** animptrptr;
idMD5Anim* anim;
@ -1220,7 +1221,7 @@ idMD5Anim* idAnimManager::GetAnim( const char* name )
}
anim = new( TAG_ANIM ) idMD5Anim();
if( !anim->LoadAnim( filename ) )
if( !anim->LoadAnim( filename, options ) )
{
gameLocal.Warning( "Couldn't load anim: '%s'", filename.c_str() );
delete anim;
@ -1249,7 +1250,7 @@ void idAnimManager::Preload( const idPreloadManifest& manifest )
const preloadEntry_s& p = manifest.GetPreloadByIndex( i );
if( p.resType == PRELOAD_ANIM )
{
GetAnim( p.resourceName );
GetAnim( p.resourceName, NULL );
numLoaded++;
}
}

View file

@ -217,6 +217,8 @@ private:
idStr name;
idVec3 totaldelta;
mutable int ref_count;
// RB
idImportOptions importOptions;
public:
idMD5Anim();
@ -229,7 +231,7 @@ public:
{
return sizeof( *this ) + Allocated();
};
bool LoadAnim( const char* filename );
bool LoadAnim( const char* filename, const idImportOptions* options );
bool LoadBinary( idFile* file, ID_TIME_T sourceTimeStamp );
void WriteBinary( idFile* file, ID_TIME_T sourceTimeStamp );
@ -348,7 +350,7 @@ public:
private:
void CopyDecl( const idDeclModelDef* decl );
bool ParseAnim( idLexer& src, int numDefaultAnims );
bool ParseAnim( idLexer& src, int numDefaultAnims, const idStr& defaultCommands );
private:
idVec3 offset;
@ -602,7 +604,7 @@ public:
static bool forceExport;
void Shutdown();
idMD5Anim* GetAnim( const char* name );
idMD5Anim* GetAnim( const char* name, const idImportOptions* options ); // RB: added import options
void Preload( const idPreloadManifest& manifest );
void ReloadAnims();
void ListAnims() const;

View file

@ -3038,7 +3038,7 @@ void idDeclModelDef::SetupJoints( int* numJoints, idJointMat** jointList, idBoun
idDeclModelDef::ParseAnim
=====================
*/
bool idDeclModelDef::ParseAnim( idLexer& src, int numDefaultAnims )
bool idDeclModelDef::ParseAnim( idLexer& src, int numDefaultAnims, const idStr& defaultCommands )
{
int i;
int len;
@ -3112,6 +3112,40 @@ bool idDeclModelDef::ParseAnim( idLexer& src, int numDefaultAnims )
// parse the anims from the string
do
{
// RB: check if there are any options within [ ]
idStr optionsStr = defaultCommands;
idStr parms;
idImportOptions options;
if( src.PeekTokenString( "[" ) )
{
while( src.ReadToken( &token ) )
{
if( token == "]" )
{
break;
}
if( token == "[" )
{
continue;
}
if( parms.Length() )
{
parms += " ";
}
parms += token;
}
if( parms.Length() )
{
optionsStr = " ";
optionsStr += parms;
}
}
if( !src.ReadToken( &token ) )
{
src.Warning( "Unexpected end of file" );
@ -3119,8 +3153,20 @@ bool idDeclModelDef::ParseAnim( idLexer& src, int numDefaultAnims )
return false;
}
try
{
options.Init( optionsStr.c_str(), token.c_str() );
}
catch( idException& ex )
{
src.Warning( "Model '%s': Failed to parse import options'%s'", token.c_str(), ex.GetError() );
MakeDefault();
return false;
}
// RB end
// lookup the animation
md5anim = animationLib.GetAnim( token );
md5anim = animationLib.GetAnim( token, &options );
if( !md5anim )
{
src.Warning( "Couldn't load anim '%s'", token.c_str() );
@ -3266,6 +3312,9 @@ bool idDeclModelDef::Parse( const char* text, const int textLength, bool allowBi
jointHandle_t jointnum;
idList<jointHandle_t> jointList;
int numDefaultAnims;
// RB: import options
idStr defaultCommands;
idStr temp;
src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
src.SetFlags( DECL_LEXER_FLAGS );
@ -3284,7 +3333,19 @@ bool idDeclModelDef::Parse( const char* text, const int textLength, bool allowBi
break;
}
if( token == "inherit" )
// RB: add import options
if( token == "options" )
{
src.ParseRestOfLine( defaultCommands );
}
else if( token == "addoptions" )
{
src.ParseRestOfLine( temp );
defaultCommands += " ";
defaultCommands += temp;
}
// RB end
else if( token == "inherit" )
{
if( !src.ReadToken( &token2 ) )
{
@ -3328,6 +3389,39 @@ bool idDeclModelDef::Parse( const char* text, const int textLength, bool allowBi
}
else if( token == "mesh" )
{
// RB: parse import options right before filename
idStr optionsStr = defaultCommands;
idStr parms;
if( src.PeekTokenString( "[" ) )
{
while( src.ReadToken( &token2 ) )
{
if( token2 == "]" )
{
break;
}
if( token2 == "[" )
{
continue;
}
if( parms.Length() )
{
parms += " ";
}
parms += token2;
}
if( parms.Length() )
{
optionsStr = " ";
optionsStr += parms;
}
}
if( !src.ReadToken( &token2 ) )
{
src.Warning( "Unexpected end of file" );
@ -3344,7 +3438,29 @@ bool idDeclModelDef::Parse( const char* text, const int textLength, bool allowBi
MakeDefault();
return false;
}
modelHandle = renderModelManager->FindModel( filename );
idImportOptions options;
if( isGltf )
{
try
{
options.Init( optionsStr.c_str(), filename.c_str() );
}
catch( idException& ex )
{
src.Warning( "Model '%s': Failed to parse import options'%s'", filename.c_str(), ex.GetError() );
MakeDefault();
return false;
}
modelHandle = renderModelManager->FindModel( filename, &options );
}
else
{
modelHandle = renderModelManager->FindModel( filename );
}
// RB end
if( !modelHandle )
{
src.Warning( "Model '%s' not found", filename.c_str() );
@ -3435,7 +3551,7 @@ bool idDeclModelDef::Parse( const char* text, const int textLength, bool allowBi
MakeDefault();
return false;
}
if( !ParseAnim( src, numDefaultAnims ) )
if( !ParseAnim( src, numDefaultAnims, defaultCommands ) )
{
MakeDefault();
return false;
@ -5960,7 +6076,7 @@ idGameEdit::ANIM_GetAnim
*/
const idMD5Anim* idGameEdit::ANIM_GetAnim( const char* fileName )
{
return animationLib.GetAnim( fileName );
return animationLib.GetAnim( fileName, NULL );
}
/*
@ -6129,7 +6245,8 @@ idRenderModel* idGameEdit::ANIM_CreateMeshForAnim( idRenderModel* model, const c
animname = args->GetString( va( "anim %s", animname ) );
}
md5anim = animationLib.GetAnim( animname );
// no modelDef no options!
md5anim = animationLib.GetAnim( animname, NULL );
offset.Zero();
}

View file

@ -2164,6 +2164,8 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
ignoreList.AddUnique( "blooper" );
ignoreList.AddUnique( "npc" );
ignoreList.AddUnique( "zombie" );
ignoreList.AddUnique( "space" );
ignoreList.AddUnique( "static" );
idStrList solidClassNames;
solidClassNames.AddUnique( "worldspawn" );
@ -2178,6 +2180,7 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
solidClassNames.AddUnique( "func_splinemover" );
solidClassNames.AddUnique( "func_static" );
solidClassNames.AddUnique( "func_mover" );
solidClassNames.AddUnique( "func_door" );
solidClassNames.AddUnique( "moveable_base" );
solidClassNames.AddUnique( "trigger_" );
@ -2263,13 +2266,21 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
}
}
// ignore autogenerated model definitions for slim FGD
bool genmodel = false;
if( idStr::Icmpn( decl->GetName(), "genmodel_", 9 ) == 0 )
{
genmodel = true;
}
// filter multiplayer entities
bool multiplayer = ( idStr::FindText( decl->GetName(), "_mp", false ) != -1 ||
idStr::FindText( decl->GetName(), "team_ctf", false ) != -1 ||
idStr::FindText( decl->GetName(), "_coop", false ) != -1 );
if( f == 2 )
{
if( !multiplayer )
if( !multiplayer || genmodel )
{
continue;
}
@ -2280,6 +2291,11 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
{
continue;
}
if( f == 1 && genmodel )
{
continue;
}
}
bool solidClass = false;
@ -2583,7 +2599,7 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
idStr::Icmp( decl->GetName(), "func_rotating_model" ) == 0 )
{
// dynamic model that prefers a TB specific proxymodel
file->Printf( "model({{\n\tproxymodel != null -> { \"path\": proxymodel },\n\t{ \"path\": model }\n}})" );
file->Printf( "model({{\n\tproxymodel -> { \"path\": proxymodel },\n\t{ \"path\": model }\n}})" );
}
else if( idStr::Icmp( decl->GetName(), "light" ) == 0 )
{
@ -3140,13 +3156,15 @@ void idDeclManagerLocal::ExportModelsToTrenchBroom_f( const idCmdArgs& args )
fgdFile->Printf( "@PointClass " );
#if 1
#if 0
if( bounds.GetVolume() > 0 )
{
fgdFile->Printf( "size(%i %i %i, %i %i %i) ",
int( bounds[0].x ), int( bounds[0].y ), int( bounds[0].z ),
int( bounds[1].x ), int( bounds[1].y ), int( bounds[1].z ) );
}
//#else
//fgdFile->Printf( "size(-8 -8 0, 8 8 16) " );
#endif
fgdFile->Printf( "base(auto_generated_model) model({ \"path\": \"%s\" }) = %s : \"Display entity\"\n[\n", exportedModelFileName.c_str(), entityName.c_str() );
@ -3163,6 +3181,13 @@ void idDeclManagerLocal::ExportModelsToTrenchBroom_f( const idCmdArgs& args )
defFile->Printf( "\t \"inherit\" \"misc_model\"\n" );
defFile->Printf( "\t \"proxymodel\" \"%s\"\n", exportedModelFileName.c_str() );
defFile->Printf( "\t \"model\" \"%s\"\n", originalModelFileName.c_str() );
#if 0
if( bounds.GetVolume() > 0 )
{
defFile->Printf( "\t \"editor_mins\" \"%i %i %i\"\n", int( bounds[0].x ), int( bounds[0].y ), int( bounds[0].z ) );
defFile->Printf( "\t \"editor_maxs\" \"%i %i %i\"\n", int( bounds[1].x ), int( bounds[1].y ), int( bounds[1].z ) );
}
#endif
defFile->Printf( "}\n\n", exportedModelFileName.c_str() );
totalEntitiesCount++;

View file

@ -3059,6 +3059,17 @@ bool idMapFile::ConvertToValve220Format()
ent->epairs.SetAngles( "angles", angles );
}
// TODO use angles instead of angle
#if 0
if( ent->epairs.FindKey( "angle" ) )
{
ent->epairs.Delete( "angle" );
idAngles angles = rot.ToAngles();
ent->epairs.SetAngles( "angles", angles );
}
#endif
const idKeyValue* kv = classTypeOverview.FindKey( classname );
if( kv && kv->GetValue().Length() )
{

View file

@ -457,13 +457,15 @@ void ResolveLight( gltfData* data, idMapEntity* newEntity, gltfNode* node )
case gltfExt_KHR_lights_punctual::Spot:
{
// RB: this code is based on Cmd_TestLight_f which sets the light properties in world space
idMat4 entityToWorldTransform = mat4_identity;
gltfData::ResolveNodeMatrix( node, &entityToWorldTransform );
idMat4 worldToEntityTransform = entityToWorldTransform.Inverse();
float fov = tan( light->spot.outerConeAngle ) / 2 ;
idMat3 axis = idAngles( 0.0f, 90.0f, 90.0f ).ToMat3() * entityToWorldTransform.ToMat3();
// HarrievG: TODO cleanup this was done by try & error until it worked
idQuat q = ( entityToWorldTransform ).ToMat3().ToQuat();
q = idAngles( 90.0f, 0.0, -90.0f ).ToQuat() * q * idAngles( 180.0f, 180.0f, -90.0f ).ToQuat();
idMat3 axis = q.ToMat3();
newEntity->epairs.SetVector( "light_target", axis[0] );
newEntity->epairs.SetVector( "light_right", axis[1] * -fov );
newEntity->epairs.SetVector( "light_up", axis[2] * fov );
@ -507,10 +509,13 @@ void ResolveEntity( gltfData* data, idMapEntity* newEntity, gltfNode* node )
newEntity->epairs = newPairs;
// gather entity transform and bring it into id Tech 4 space
gltfData::ResolveNodeMatrix( node );
idMat4 entityToWorldTransform = mat4_identity;
gltfData::ResolveNodeMatrix( node, &entityToWorldTransform );
// set entity transform in a way the game and physics code understand it
idVec3 origin = blenderToDoomTransform * node->translation;
// RB: should be idVec3 origin = ( blenderToDoomTransform * entityToWorldTransform ).GetTranslation();
newEntity->epairs.SetVector( "origin", origin );
if( node->extensions.KHR_lights_punctual != nullptr )
@ -518,12 +523,32 @@ void ResolveEntity( gltfData* data, idMapEntity* newEntity, gltfNode* node )
ResolveLight( data, newEntity, node );
}
// TODO set rotation key to store rotation and scaling
//if (idStr::Icmp(classname, "info_player_start") == 0)
// if( !node->matrix.IsIdentity() )
//{
// newEntity->epairs.SetMatrix("rotation", axis );
//}
// HarrievG: TODO cleanup this was done by try & error until it worked
if( node->camera >= 0 && !newEntity->epairs.FindKey( "rotation" ) )
{
idQuat q = entityToWorldTransform.ToMat3().ToQuat();
q = idAngles( 90.0f, 0.0, -90.0f ).ToQuat() * q * blenderToDoomTransform.ToMat3().ToQuat();
newEntity->epairs.SetMatrix( "rotation", q.ToMat3() );
}
else if( idStr::Icmp( classname, "info_player_start" ) == 0 && !newEntity->epairs.FindKey( "rotation" ) )
{
idQuat q = entityToWorldTransform.ToMat3().ToQuat();
q = idAngles( -90.0f, 0.0, -90.0f ).ToQuat() * q * blenderToDoomTransform.ToMat3().ToQuat();
newEntity->epairs.SetMatrix( "rotation", q.ToMat3() );
}
else if( node->extras.strPairs.GetBool( "useNodeOrientation", false ) )
{
//Nodes that are an instance of an collection containing a mesh that is not inline, ea; a gltfModel; static _or_ dynamic,
//which has its transformations applied on vertex level so we do not apply it here.
origin = blenderToDoomTransform * ( node->translation * ( entityToWorldTransform * node->matrix.Inverse() ) );
newEntity->epairs.SetVector( "origin", origin );
idQuat q = ( entityToWorldTransform * node->matrix.Inverse() ).ToMat3().ToQuat();
q = blenderToDoomTransform.Inverse().ToMat3().ToQuat() * q * blenderToDoomTransform.ToMat3().ToQuat();
idMat3 rot = q.ToMat3();
//idMat3 rot = ( blenderToDoomTransform * entityToWorldTransform ).ToMat3();
newEntity->epairs.SetMatrix( "rotation", rot );
}
#if 0
for( int i = 0; i < newEntity->epairs.GetNumKeyVals(); i++ )

View file

@ -1150,7 +1150,7 @@ void GLTF_Parser::Shutdown()
bufferViewsDone = false;
}
GLTF_Parser::GLTF_Parser()
: parser( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ) , buffersDone( false ), bufferViewsDone( false ) { }
: parser( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ) , buffersDone( false ), bufferViewsDone( false ), currentAsset( nullptr ) { }
void GLTF_Parser::Parse_ASSET( idToken& token )
{

View file

@ -401,11 +401,6 @@ public:
{
return false;
}
const idVec3& TotalMovementDelta() const
{
static idVec3 temp;
return temp;
}
int NumFrames() const
{
return numFrames;

View file

@ -963,6 +963,7 @@ public:
// jmarshall
idMat3 ToMat3() const;
// jmarshall end
idVec3 GetTranslation() const;
private:
idVec4 mat[ 4 ];
};
@ -991,6 +992,19 @@ ID_INLINE idMat3 idMat4::ToMat3() const
}
// jmarshall end
// RB begin
ID_INLINE idVec3 idMat4::GetTranslation() const
{
idVec3 pos;
pos.x = mat[ 0 ][ 3 ];
pos.y = mat[ 1 ][ 3 ];
pos.z = mat[ 2 ][ 3 ];
return pos;
}
// RB end
ID_INLINE idMat4::idMat4()
{
}

View file

@ -295,7 +295,7 @@ idRenderModelStatic::PartialInitFromFile
void idRenderModelStatic::PartialInitFromFile( const char* fileName )
{
fastLoad = true;
InitFromFile( fileName );
InitFromFile( fileName, nullptr );
}
/*
@ -303,20 +303,22 @@ void idRenderModelStatic::PartialInitFromFile( const char* fileName )
idRenderModelStatic::InitFromFile
================
*/
void idRenderModelStatic::InitFromFile( const char* fileName )
void idRenderModelStatic::InitFromFile( const char* fileName, const idImportOptions* options )
{
bool loaded;
idStr extension;
InitEmpty( fileName );
// FIXME: load new .proc map format
ID_TIME_T sourceTimeStamp;
name.ExtractFileExtension( extension );
if( extension.Icmp( "ase" ) == 0 )
if( extension.Icmp( "glb" ) == 0 || extension.Icmp( "gltf" ) == 0 )
{
loaded = false;
reloadable = true;
}
else if( extension.Icmp( "ase" ) == 0 )
{
loaded = LoadASE( name, &sourceTimeStamp );
reloadable = true;
@ -872,7 +874,7 @@ idRenderModelStatic::LoadModel
void idRenderModelStatic::LoadModel()
{
PurgeModel();
InitFromFile( name );
InitFromFile( name, nullptr );
}
/*

View file

@ -62,6 +62,7 @@ struct dominantTri_t
const int SHADOW_CAP_INFINITE = 64;
class idRenderModelStatic;
class idImportOptions;
struct viewDef_t;
// our only drawing geometry type
@ -168,7 +169,7 @@ public:
virtual ~idRenderModel() {};
// Loads static models only, dynamic models must be loaded by the modelManager
virtual void InitFromFile( const char* fileName ) = 0;
virtual void InitFromFile( const char* fileName, const idImportOptions* options ) = 0;
// Supports reading/writing binary file formats
virtual bool LoadBinaryModel( idFile* file, const ID_TIME_T sourceTimeStamp ) = 0;

View file

@ -4,6 +4,7 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
Copyright (C) 2022 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -56,7 +57,7 @@ public:
virtual void Shutdown();
virtual idRenderModel* AllocModel();
virtual void FreeModel( idRenderModel* model );
virtual idRenderModel* FindModel( const char* modelName );
virtual idRenderModel* FindModel( const char* modelName, const idImportOptions* options = NULL );
virtual idRenderModel* CheckModel( const char* modelName );
virtual idRenderModel* DefaultModel();
virtual void AddModel( idRenderModel* model );
@ -80,7 +81,7 @@ private:
bool insideLevelLoad; // don't actually load now
nvrhi::CommandListHandle commandList;
idRenderModel* GetModel( const char* modelName, bool createIfNotFound );
idRenderModel* GetModel( const char* modelName, bool createIfNotFound, const idImportOptions* options );
static void PrintModel_f( const idCmdArgs& args );
static void ListModels_f( const idCmdArgs& args );
@ -296,7 +297,7 @@ void idRenderModelManagerLocal::Shutdown()
idRenderModelManagerLocal::GetModel
=================
*/
idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool createIfNotFound )
idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool createIfNotFound, const idImportOptions* options )
{
if( !_modelName || !_modelName[0] )
{
@ -309,6 +310,13 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool
idStrStatic< MAX_OSPATH > extension;
canonical.ExtractFileExtension( extension );
bool isGLTF = false;
// HvG: GLTF 2 support
if( ( extension.Icmp( GLTF_GLB_EXT ) == 0 ) || ( extension.Icmp( GLTF_EXT ) == 0 ) )
{
isGLTF = true;
}
// see if it is already present
int key = hash.GenerateKey( canonical, false );
for( int i = hash.First( key ); i != -1; i = hash.Next( i ) )
@ -325,7 +333,22 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool
generatedFileName.SetFileExtension( va( "b%s", extension.c_str() ) );
// 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 );
ID_TIME_T sourceTimeStamp;
if( isGLTF )
{
idStr gltfFileName = idStr( canonical );
int gltfMeshId = -1;
idStr gltfMeshName;
gltfManager::ExtractIdentifier( gltfFileName, gltfMeshId, gltfMeshName );
sourceTimeStamp = fileSystem->GetTimestamp( gltfFileName );
}
else
{
sourceTimeStamp = fileSystem->GetTimestamp( canonical );
}
if( model->SupportsBinaryModel() && binaryLoadRenderModels.GetBool() )
{
@ -333,7 +356,14 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool
model->PurgeModel();
if( !model->LoadBinaryModel( file, sourceTimeStamp ) )
{
model->LoadModel();
if( isGLTF )
{
model->InitFromFile( canonical, options );
}
else
{
model->LoadModel();
}
}
}
else
@ -359,10 +389,12 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool
// determine which subclass of idRenderModel to initialize
idRenderModel* model = NULL;
// HvG: GLTF 2 support
if( ( extension.Icmp( GLTF_GLB_EXT ) == 0 ) || ( extension.Icmp( GLTF_EXT ) == 0 ) )
if( isGLTF )
{
model = new( TAG_MODEL ) idRenderModelGLTF;
isGLTF = true;
}
// RB: Collada DAE and Wavefront OBJ
else if( ( extension.Icmp( "dae" ) == 0 ) || ( extension.Icmp( "obj" ) == 0 )
@ -397,19 +429,33 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool
generatedFileName.SetFileExtension( va( "b%s", extension.c_str() ) );
// 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 );
ID_TIME_T sourceTimeStamp;
if( isGLTF )
{
idStr gltfFileName = idStr( canonical );
int gltfMeshId = -1;
idStr gltfMeshName;
gltfManager::ExtractIdentifier( gltfFileName, gltfMeshId, gltfMeshName );
sourceTimeStamp = fileSystem->GetTimestamp( gltfFileName );
}
else
{
sourceTimeStamp = fileSystem->GetTimestamp( canonical );
}
idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
if( !model->SupportsBinaryModel() || !binaryLoadRenderModels.GetBool() )
{
model->InitFromFile( canonical );
model->InitFromFile( canonical, options );
}
else
{
if( !model->LoadBinaryModel( file, sourceTimeStamp ) )
{
model->InitFromFile( canonical );
model->InitFromFile( canonical, options );
// RB: default models shouldn't be cached as binary models
if( !model->IsDefaultModel() )
@ -555,9 +601,9 @@ void idRenderModelManagerLocal::FreeModel( idRenderModel* model )
idRenderModelManagerLocal::FindModel
=================
*/
idRenderModel* idRenderModelManagerLocal::FindModel( const char* modelName )
idRenderModel* idRenderModelManagerLocal::FindModel( const char* modelName, const idImportOptions* options )
{
return GetModel( modelName, true );
return GetModel( modelName, true, options );
}
/*
@ -567,7 +613,7 @@ idRenderModelManagerLocal::CheckModel
*/
idRenderModel* idRenderModelManagerLocal::CheckModel( const char* modelName )
{
return GetModel( modelName, false );
return GetModel( modelName, false, nullptr );
}
/*
@ -633,22 +679,44 @@ void idRenderModelManagerLocal::ReloadModels( bool forceAll )
{
continue;
}
bool isGLTF = false;
idStr filename = model->Name();
idStr extension;
idStr assetName = filename;
assetName.ExtractFileExtension( extension );
isGLTF = extension.Icmp( "glb" ) == 0 || extension.Icmp( "gltf" ) == 0;
if( !forceAll )
{
// check timestamp
ID_TIME_T current;
fileSystem->ReadFile( model->Name(), NULL, &current );
if( isGLTF )
{
idStr meshName;
int meshID = -1;
gltfManager::ExtractIdentifier( filename, meshID, meshName );
}
fileSystem->ReadFile( filename, NULL, &current );
if( current <= model->Timestamp() )
{
continue;
}
}
common->DPrintf( "reloading %s.\n", model->Name() );
common->DPrintf( "^1Reloading %s.\n", model->Name() );
if( isGLTF )
{
// RB: we don't have the options here so make sure this only applies to static models
model->InitFromFile( model->Name(), NULL );
}
else
{
model->LoadModel();
}
model->LoadModel();
}
// we must force the world to regenerate, because models may
@ -932,3 +1000,483 @@ void idRenderModelManagerLocal::PrintMemInfo( MemInfo_t* mi )
f->Printf( "\nTotal model bytes allocated: %s\n", idStr::FormatNumber( totalMem ).c_str() );
fileSystem->CloseFile( f );
}
// RB: added Maya exporter options
/*
==============================================================================================
idTokenizer
==============================================================================================
*/
/*
=================
MayaError
=================
*/
void MayaError( const char* fmt, ... )
{
va_list argptr;
char text[ 8192 ];
va_start( argptr, fmt );
idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
va_end( argptr );
throw idException( text );
}
class idTokenizer
{
private:
int currentToken;
idStrList tokens;
public:
idTokenizer()
{
Clear();
};
void Clear()
{
currentToken = 0;
tokens.Clear();
};
int SetTokens( const char* buffer );
const char* NextToken( const char* errorstring = NULL );
bool TokenAvailable()
{
return currentToken < tokens.Num();
};
int Num()
{
return tokens.Num();
};
void UnGetToken()
{
if( currentToken > 0 )
{
currentToken--;
}
};
const char* GetToken( int index )
{
if( ( index >= 0 ) && ( index < tokens.Num() ) )
{
return tokens[ index ];
}
else
{
return NULL;
}
};
const char* CurrentToken()
{
return GetToken( currentToken );
};
};
/*
====================
idTokenizer::SetTokens
====================
*/
int idTokenizer::SetTokens( const char* buffer )
{
const char* cmd;
Clear();
// tokenize commandline
cmd = buffer;
while( *cmd )
{
// skip whitespace
while( *cmd && isspace( *cmd ) )
{
cmd++;
}
if( !*cmd )
{
break;
}
idStr& current = tokens.Alloc();
while( *cmd && !isspace( *cmd ) )
{
current += *cmd;
cmd++;
}
}
return tokens.Num();
}
/*
====================
idTokenizer::NextToken
====================
*/
const char* idTokenizer::NextToken( const char* errorstring )
{
if( currentToken < tokens.Num() )
{
return tokens[ currentToken++ ];
}
if( errorstring )
{
MayaError( "Error: %s", errorstring );
}
return NULL;
}
/*
==============================================================================================
idImportOptions
==============================================================================================
*/
#define DEFAULT_ANIM_EPSILON 0.125f
#define DEFAULT_QUAT_EPSILON ( 1.0f / 8192.0f )
void idImportOptions::Init( const char* commandline, const char* ospath )
{
idStr token;
idNamePair joints;
int i;
idAnimGroup* group;
idStr sourceDir;
idStr destDir;
//Reset( commandline );
scale = 1.0f;
//type = WRITE_MESH;
startframe = -1;
endframe = -1;
ignoreMeshes = false;
clearOrigin = false;
clearOriginAxis = false;
framerate = 24;
align = "";
rotate = 0.0f;
commandLine = commandline;
prefix = "";
jointThreshold = 0.05f;
ignoreScale = false;
xyzPrecision = DEFAULT_ANIM_EPSILON;
quatPrecision = DEFAULT_QUAT_EPSILON;
cycleStart = -1;
src.Clear();
dest.Clear();
idTokenizer tokens;
tokens.SetTokens( commandline );
keepjoints.Clear();
renamejoints.Clear();
remapjoints.Clear();
exportgroups.Clear();
skipmeshes.Clear();
keepmeshes.Clear();
groups.Clear();
/*
token = tokens.NextToken( "Missing export command" );
if( token == "mesh" )
{
type = WRITE_MESH;
}
else if( token == "anim" )
{
type = WRITE_ANIM;
}
else if( token == "camera" )
{
type = WRITE_CAMERA;
}
else
{
MayaError( "Unknown export command '%s'", token.c_str() );
}
*/
//src = tokens.NextToken( "Missing source filename" );
//dest = src;
for( token = tokens.NextToken(); token != ""; token = tokens.NextToken() )
{
if( token == "-" )
{
token = tokens.NextToken( "Missing import parameter" );
if( token == "force" )
{
// skip
}
else if( token == "game" )
{
// parse game name
game = tokens.NextToken( "Expecting game name after -game" );
}
else if( token == "rename" )
{
// parse joint to rename
joints.from = tokens.NextToken( "Missing joint name for -rename. Usage: -rename [joint name] [new name]" );
joints.to = tokens.NextToken( "Missing new name for -rename. Usage: -rename [joint name] [new name]" );
renamejoints.Append( joints );
}
else if( token == "prefix" )
{
prefix = tokens.NextToken( "Missing name for -prefix. Usage: -prefix [joint prefix]" );
}
else if( token == "parent" )
{
// parse joint to reparent
joints.from = tokens.NextToken( "Missing joint name for -parent. Usage: -parent [joint name] [new parent]" );
joints.to = tokens.NextToken( "Missing new parent for -parent. Usage: -parent [joint name] [new parent]" );
remapjoints.Append( joints );
}
else if( !token.Icmp( "sourcedir" ) )
{
// parse source directory
sourceDir = tokens.NextToken( "Missing filename for -sourcedir. Usage: -sourcedir [directory]" );
}
else if( !token.Icmp( "destdir" ) )
{
// parse destination directory
destDir = tokens.NextToken( "Missing filename for -destdir. Usage: -destdir [directory]" );
}
else if( token == "dest" )
{
// parse destination filename
dest = tokens.NextToken( "Missing filename for -dest. Usage: -dest [filename]" );
}
else if( token == "range" )
{
// parse frame range to export
token = tokens.NextToken( "Missing start frame for -range. Usage: -range [start frame] [end frame]" );
startframe = atoi( token );
token = tokens.NextToken( "Missing end frame for -range. Usage: -range [start frame] [end frame]" );
endframe = atoi( token );
if( startframe > endframe )
{
MayaError( "Start frame is greater than end frame." );
}
}
else if( !token.Icmp( "cycleStart" ) )
{
// parse start frame of cycle
token = tokens.NextToken( "Missing cycle start frame for -cycleStart. Usage: -cycleStart [first frame of cycle]" );
cycleStart = atoi( token );
}
else if( token == "scale" )
{
// parse scale
token = tokens.NextToken( "Missing scale amount for -scale. Usage: -scale [scale amount]" );
scale = atof( token );
}
else if( token == "align" )
{
// parse align joint
align = tokens.NextToken( "Missing joint name for -align. Usage: -align [joint name]" );
}
else if( token == "rotate" )
{
// parse angle rotation
token = tokens.NextToken( "Missing value for -rotate. Usage: -rotate [yaw]" );
rotate = -atof( token );
}
else if( token == "nomesh" )
{
ignoreMeshes = true;
}
else if( token == "clearorigin" )
{
clearOrigin = true;
clearOriginAxis = true;
}
else if( token == "clearoriginaxis" )
{
clearOriginAxis = true;
}
else if( token == "ignorescale" )
{
ignoreScale = true;
}
else if( token == "xyzprecision" )
{
// parse quaternion precision
token = tokens.NextToken( "Missing value for -xyzprecision. Usage: -xyzprecision [precision]" );
xyzPrecision = atof( token );
if( xyzPrecision < 0.0f )
{
MayaError( "Invalid value for -xyzprecision. Must be >= 0" );
}
}
else if( token == "quatprecision" )
{
// parse quaternion precision
token = tokens.NextToken( "Missing value for -quatprecision. Usage: -quatprecision [precision]" );
quatPrecision = atof( token );
if( quatPrecision < 0.0f )
{
MayaError( "Invalid value for -quatprecision. Must be >= 0" );
}
}
else if( token == "jointthreshold" )
{
// parse joint threshold
token = tokens.NextToken( "Missing weight for -jointthreshold. Usage: -jointthreshold [minimum joint weight]" );
jointThreshold = atof( token );
}
else if( token == "skipmesh" )
{
token = tokens.NextToken( "Missing name for -skipmesh. Usage: -skipmesh [name of mesh to skip]" );
skipmeshes.AddUnique( token );
}
else if( token == "keepmesh" )
{
token = tokens.NextToken( "Missing name for -keepmesh. Usage: -keepmesh [name of mesh to keep]" );
keepmeshes.AddUnique( token );
}
else if( token == "jointgroup" )
{
token = tokens.NextToken( "Missing name for -jointgroup. Usage: -jointgroup [group name] [joint1] [joint2]...[joint n]" );
group = groups.Ptr();
for( i = 0; i < groups.Num(); i++, group++ )
{
if( group->name == token )
{
break;
}
}
if( i >= groups.Num() )
{
// create a new group
group = &groups.Alloc();
group->name = token;
}
while( tokens.TokenAvailable() )
{
token = tokens.NextToken();
if( token[ 0 ] == '-' )
{
tokens.UnGetToken();
break;
}
group->joints.AddUnique( token );
}
}
else if( token == "group" )
{
// add the list of groups to export (these don't affect the hierarchy)
while( tokens.TokenAvailable() )
{
token = tokens.NextToken();
if( token[ 0 ] == '-' )
{
tokens.UnGetToken();
break;
}
group = groups.Ptr();
for( i = 0; i < groups.Num(); i++, group++ )
{
if( group->name == token )
{
break;
}
}
if( i >= groups.Num() )
{
MayaError( "Unknown group '%s'", token.c_str() );
}
exportgroups.AddUnique( group );
}
}
else if( token == "keep" )
{
// add joints that are kept whether they're used by a mesh or not
while( tokens.TokenAvailable() )
{
token = tokens.NextToken();
if( token[ 0 ] == '-' )
{
tokens.UnGetToken();
break;
}
keepjoints.AddUnique( token );
}
}
else
{
MayaError( "Unknown option '%s'", token.c_str() );
}
}
}
token = src;
src = ospath;
src.BackSlashesToSlashes();
src.AppendPath( sourceDir );
src.AppendPath( token );
token = dest;
dest = ospath;
dest.BackSlashesToSlashes();
dest.AppendPath( destDir );
dest.AppendPath( token );
// Maya only accepts unix style path separators
src.BackSlashesToSlashes();
dest.BackSlashesToSlashes();
if( skipmeshes.Num() && keepmeshes.Num() )
{
MayaError( "Can't use -keepmesh and -skipmesh together." );
}
}
// RB end

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) 2022 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -29,6 +30,68 @@ If you have questions concerning this license or the applicable additional terms
#ifndef __MODELMANAGER_H__
#define __MODELMANAGER_H__
/*
==============================================================================================
idImportOptions
==============================================================================================
*/
class idNamePair
{
public:
idStr from;
idStr to;
};
class idAnimGroup
{
public:
idStr name;
idStrList joints;
};
class idImportOptions
{
private:
//idTokenizer tokens;
//void Reset( const char* commandline );
public:
idStr commandLine;
idStr src;
idStr dest;
idStr game;
idStr prefix;
float scale;
//exportType_t type;
bool ignoreMeshes;
bool clearOrigin;
bool clearOriginAxis;
bool ignoreScale;
int startframe;
int endframe;
int framerate;
float xyzPrecision;
float quatPrecision;
idStr align;
idList<idNamePair> renamejoints;
idList<idNamePair> remapjoints;
idStrList keepjoints;
idStrList skipmeshes;
idStrList keepmeshes;
idList<idAnimGroup*> exportgroups;
idList<idAnimGroup> groups;
float rotate;
float jointThreshold;
int cycleStart;
void Init( const char* commandline, const char* ospath );
//bool JointInExportGroup( const char* jointname );
};
/*
===============================================================================
@ -67,7 +130,7 @@ public:
// returns NULL if modelName is NULL or an empty string, otherwise
// it will create a default model if not loadable
virtual idRenderModel* FindModel( const char* modelName ) = 0;
virtual idRenderModel* FindModel( const char* modelName, const idImportOptions* options = NULL ) = 0;
// returns NULL if not loadable
virtual idRenderModel* CheckModel( const char* modelName ) = 0;

View file

@ -57,7 +57,7 @@ bool idRenderModelStatic::ConvertGltfMeshToModelsurfaces( const gltfMesh* mesh )
return false;
}
void idRenderModelGLTF::ProcessNode_r( gltfNode* modelNode, idMat4 parentTransform, gltfData* data )
void idRenderModelGLTF::ProcessNode_r( gltfNode* modelNode, const idMat4& parentTransform, const idMat4& globalTransform, gltfData* data )
{
auto& meshList = data->MeshList();
auto& nodeList = data->NodeList();
@ -73,7 +73,7 @@ void idRenderModelGLTF::ProcessNode_r( gltfNode* modelNode, idMat4 parentTransfo
for( auto prim : targetMesh->primitives )
{
//ConvertFromMeshGltf should only be used for the map, ConvertGltfMeshToModelsurfaces should be used.
auto* mesh = MapPolygonMesh::ConvertFromMeshGltf( prim, data, blenderToDoomTransform * nodeToWorldTransform );
auto* mesh = MapPolygonMesh::ConvertFromMeshGltf( prim, data, globalTransform * nodeToWorldTransform );
modelSurface_t surf;
gltfMaterial* mat = NULL;
@ -126,7 +126,7 @@ void idRenderModelGLTF::ProcessNode_r( gltfNode* modelNode, idMat4 parentTransfo
for( auto& child : modelNode->children )
{
ProcessNode_r( nodeList[child], nodeToWorldTransform, data );
ProcessNode_r( nodeList[child], nodeToWorldTransform, globalTransform, data );
}
}
@ -135,7 +135,7 @@ void idRenderModelGLTF::ProcessNode_r( gltfNode* modelNode, idMat4 parentTransfo
// warning : nodeName cannot have dots!
//[fileName].[nodeName/nodeId].[gltf/glb]
//If no nodeName/nodeId is given, all primitives active in default scene will be added as surfaces.
void idRenderModelGLTF::InitFromFile( const char* fileName )
void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOptions* options )
{
hasAnimations = false;
fileExclusive = false;
@ -144,12 +144,13 @@ void idRenderModelGLTF::InitFromFile( const char* fileName )
int meshID = -1;
name = fileName;
currentSkin = nullptr;
globalTransform = blenderToDoomTransform;
PurgeModel();
//FIXME FIXME FIXME
maxJointVertDist = 10;
idStr gltfFileName = idStr( fileName );
gltfFileName = idStr( fileName );
model_state = DM_STATIC;
gltfManager::ExtractIdentifier( gltfFileName, meshID, meshName );
@ -231,12 +232,25 @@ void idRenderModelGLTF::InitFromFile( const char* fileName )
hasAnimations = totalAnims > 0;
model_state = hasAnimations ? DM_CACHED : DM_STATIC;
ProcessNode_r( root, mat4_identity, data );
//idMat4 globalTransform = blenderToDoomTransform;
if( options )
{
const auto blenderToDoomRotation = idAngles( 0.0f, 0.0f, 90 ).ToMat3();
float scale = options->scale;
idMat3 scaleMat( scale, 0, 0, 0, scale, 0, 0, 0, scale );
globalTransform = idMat4( scaleMat * blenderToDoomRotation, vec3_origin );
}
ProcessNode_r( root, mat4_identity, globalTransform, data );
if( surfaces.Num() <= 0 )
{
common->Warning( "Couldn't load model: '%s'", name.c_str() );
MakeDefaultModel();
data = nullptr;
return;
}
@ -259,6 +273,7 @@ bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTim
if( !idRenderModelStatic::LoadBinaryModel( file, sourceTimeStamp ) )
{
data = nullptr;
return false;
}
@ -267,6 +282,7 @@ bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTim
if( magic != GLMB_MAGIC )
{
data = nullptr;
return false;
}
@ -343,6 +359,7 @@ bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTim
model_state = hasAnimations ? DM_CONTINUOUS : DM_STATIC;
lastMeshFromFile = this;
data = nullptr;
return true;
}
@ -467,7 +484,7 @@ void idRenderModelGLTF::DrawJoints( const struct renderEntity_s* ent, const view
}
}
bool gatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idList<gltfNode*>& nodes , idList<int, TAG_MODEL>& bones, idList<jointAnimInfo_t, TAG_MD5_ANIM>& jointInfo )
static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idList<gltfNode*>& nodes , idList<int, TAG_MODEL>& bones, idList<jointAnimInfo_t, TAG_MD5_ANIM>& jointInfo )
{
//Gather Bones;
bool boneLess = false;
@ -508,7 +525,7 @@ bool gatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idList<gltfN
return boneLess;
}
idList<idJointQuat> GetPose( idList<gltfNode>& bones, idJointMat* poseMat )
static idList<idJointQuat> GetPose( idList<gltfNode>& bones, idJointMat* poseMat, const idMat4& globalTransform )
{
idList<idJointQuat> ret;
ret.AssureSize( bones.Num() );
@ -522,7 +539,7 @@ idList<idJointQuat> GetPose( idList<gltfNode>& bones, idJointMat* poseMat )
if( node->parent == nullptr )
{
node->matrix *= blenderToDoomTransform;
node->matrix *= globalTransform;
trans = node->matrix;
}
@ -549,7 +566,7 @@ idList<idJointQuat> GetPose( idList<gltfNode>& bones, idJointMat* poseMat )
return ret;
}
int copyBones( gltfData* data, const idList<int>& bones, idList<gltfNode>& out )
static int CopyBones( gltfData* data, const idList<int>& bones, idList<gltfNode>& out )
{
out.Clear();
@ -584,12 +601,15 @@ int copyBones( gltfData* data, const idList<int>& bones, idList<gltfNode>& out )
return out.Num();
}
idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T sourceTimeStamp )
idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TIME_T sourceTimeStamp, const idImportOptions* options )
{
assert( lastMeshFromFile );
///keep in sync with game!
//keep in sync with game!
static const byte B_ANIM_MD5_VERSION = 101;
static const unsigned int B_ANIM_MD5_MAGIC = ( 'B' << 24 ) | ( 'M' << 16 ) | ( 'D' << 8 ) | B_ANIM_MD5_VERSION;
// convert animName to original glTF2 filename and load it
GLTF_Parser gltf;
int id;
idStr gltfFileName = idStr( animName );
@ -617,8 +637,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
idList<int, TAG_MODEL> bones;
idList<jointAnimInfo_t, TAG_MD5_ANIM> jointInfo;
bool boneLess = gatherBoneInfo( data, gltfAnim, nodes, bones, jointInfo );
bool boneLess = GatherBoneInfo( data, gltfAnim, nodes, bones, jointInfo );
idList<idList<gltfNode>> animBones;
idList<float, TAG_MD5_ANIM> componentFrames;
@ -632,6 +651,9 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
gameLocal.Printf( "Generating MD5Anim for GLTF anim %s from scene %s\n", name.c_str(), gltf_ModelSceneName.GetString() );
// TODO use idImportOptions to build globalTransform
gltfNode* root = nullptr;
int channelCount = 0;
for( auto channel : gltfAnim->channels )
@ -675,16 +697,20 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
{
default:
break;
case gltfAnimation_Channel_Target::none:
break;
case gltfAnimation_Channel_Target::rotation:
newJoint->animBits |= ANIM_QX | ANIM_QY | ANIM_QZ;
numAnimatedComponents += 3;
break;
case gltfAnimation_Channel_Target::translation:
newJoint->animBits |= ANIM_TX | ANIM_TY | ANIM_TZ;
numAnimatedComponents += 3;
break;
case gltfAnimation_Channel_Target::scale: // this is not supported by engine, but it should be for gltf
break;
}
@ -695,7 +721,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
animBones.SetNum( numFrames );
for( int i = 0; i < numFrames; i++ )
{
int totalCopied = copyBones( data, bones, animBones[i] );
int totalCopied = CopyBones( data, bones, animBones[i] );
assert( totalCopied );
}
@ -722,7 +748,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
baseFrame.SetNum( bones.Num() );
idJointMat* poseMat = ( idJointMat* ) _alloca16( bones.Num() * sizeof( poseMat[0] ) );
baseFrame = GetPose( animBones[0], poseMat );
baseFrame = GetPose( animBones[0], poseMat, blenderToDoomTransform );
componentFrames.SetGranularity( 1 );
componentFrames.SetNum( ( ( numAnimatedComponents * numFrames ) ) + 1 );
@ -748,8 +774,10 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
{
default:
break;
case gltfAnimation_Channel_Target::none:
break;
case gltfAnimation_Channel_Target::rotation:
{
idList<idQuat*>& values = data->GetAccessorView<idQuat>( output );
@ -757,8 +785,9 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
{
animBones[i][boneIndex].rotation = *values[i];
}
break;
}
break;
case gltfAnimation_Channel_Target::translation:
{
idList<idVec3*>& values = data->GetAccessorView<idVec3>( output );
@ -766,17 +795,21 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
{
animBones[i][boneIndex].translation = *values[i];
}
break;
}
break;
case gltfAnimation_Channel_Target::scale:
{
idList<idVec3*>& values = data->GetAccessorView<idVec3>( output );
if( values.Num() > i )
{
animBones[i][boneIndex].scale = *values[i] ;
}
break;
}
}
}
for( int b = 0; b < bones.Num(); b++ )
{
auto* node = &animBones[i][b];
@ -836,7 +869,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
}
idList<idJointMat> joints;
GetPose( animBones[i], currJoints );
GetPose( animBones[i], currJoints, blenderToDoomTransform );
for( int b = 0; b < animBones[i].Num(); b++ )
{
idJointMat mat = poseMat[b];
@ -893,9 +926,6 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T
file->WriteBig( numJoints );
file->WriteBig( numAnimatedComponents );
file->WriteBig( bounds.Num() );
for( int i = 0; i < bounds.Num(); i++ )
{
@ -1049,6 +1079,7 @@ void idRenderModelGLTF::WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp /*
void idRenderModelGLTF::PurgeModel()
{
idRenderModelStatic::PurgeModel();
purged = true;
md5joints.Clear();
defaultPose.Clear();
@ -1057,16 +1088,20 @@ void idRenderModelGLTF::PurgeModel()
animIds.Clear();
bones.Clear();
MeshNodeIds.Clear();
gltfFileName.Clear();
//if no root id was set, it is a generated one.
if( rootID == -1 && root )
{
delete root;
}
data = nullptr;
}
void idRenderModelGLTF::LoadModel()
{
assert( data );
int num;
auto& accessors = data->AccessorList();
auto& nodes = data->NodeList();
@ -1074,6 +1109,7 @@ void idRenderModelGLTF::LoadModel()
if( !fileExclusive )
{
meshRoot = data->GetNode( gltf_ModelSceneName.GetString(), meshName );
assert( meshRoot );
}
gltfSkin* skin = nullptr;
@ -1132,8 +1168,8 @@ void idRenderModelGLTF::LoadModel()
idJointMat* poseMat = ( idJointMat* ) _alloca16( bones.Num() * sizeof( poseMat[0] ) );
idList<gltfNode> animBones;
int totalCopied = copyBones( data, bones, animBones );
defaultPose = GetPose( animBones, poseMat );
int totalCopied = CopyBones( data, bones, animBones );
defaultPose = GetPose( animBones, poseMat, globalTransform );
if( !currentSkin )
{
@ -1462,9 +1498,6 @@ idRenderModel* idRenderModelGLTF::InstantiateDynamicModel( const struct renderEn
if( purged )
{
common->DWarning( "model %s instantiated while purged", Name() );
GLTF_Parser gltf;
gltf.Load( name );
data = gltf.currentAsset;
LoadModel();
}

View file

@ -32,7 +32,7 @@ If you have questions concerning this license or the applicable additional terms
class idRenderModelGLTF : public idRenderModelStatic
{
public:
virtual void InitFromFile( const char* fileName ) override;
virtual void InitFromFile( const char* fileName, const idImportOptions* options ) override;
virtual bool LoadBinaryModel( idFile* file, const ID_TIME_T sourceTimeStamp ) override;
virtual void WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp = NULL ) const override;
virtual dynamicModel_t IsDynamicModel() const override;
@ -54,10 +54,10 @@ public:
{
return true;
}
static idFile_Memory* GetAnimBin( idStr animName, const ID_TIME_T sourceTimeStamp );
static idFile_Memory* GetAnimBin( const idStr& animName, const ID_TIME_T sourceTimeStamp, const idImportOptions* options );
int rootID;
private:
void ProcessNode_r( gltfNode* modelNode, idMat4 trans, gltfData* data );
void ProcessNode_r( gltfNode* modelNode, const idMat4& parentTransform, const idMat4& globalTransform, gltfData* data );
void UpdateSurface( const struct renderEntity_s* ent, const idJointMat* entJoints, const idJointMat* entJointsInverted, modelSurface_t* surf, const modelSurface_t& sourceSurf );
void UpdateMd5Joints();
@ -73,11 +73,15 @@ private:
idList<int, TAG_MODEL> MeshNodeIds;
dynamicModel_t model_state;
idStr meshName;
idStr gltfFileName;
idList<idMD5Joint, TAG_MODEL> md5joints;
idList<idJointQuat, TAG_MODEL> defaultPose;
idList<idJointMat, TAG_MODEL> invertedDefaultPose;
gltfSkin* currentSkin;
// derived reimport options
idMat4 globalTransform; // Blender to Doom + exta scaling, rotation
private:
void DrawJoints( const struct renderEntity_s* ent, const viewDef_t* view );
};

View file

@ -373,7 +373,7 @@ void idRenderModelLiquid::Reset()
idRenderModelLiquid::InitFromFile
====================
*/
void idRenderModelLiquid::InitFromFile( const char* fileName, nvrhi::ICommandList* commandList )
void idRenderModelLiquid::InitFromFile( const char* fileName, nvrhi::ICommandList* commandList, const idImportOptions* options )
{
int i, x, y;
idToken token;

View file

@ -53,7 +53,7 @@ public:
idRenderModelStatic();
virtual ~idRenderModelStatic();
virtual void InitFromFile( const char* fileName );
virtual void InitFromFile( const char* fileName, const idImportOptions* options );
virtual bool LoadBinaryModel( idFile* file, const ID_TIME_T sourceTimeStamp );
virtual void WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp = NULL ) const;
virtual bool SupportsBinaryModel()
@ -218,7 +218,7 @@ class idRenderModelMD5 : public idRenderModelStatic
{
friend class idRenderModelGLTF;
public:
void InitFromFile( const char* fileName ) override;
void InitFromFile( const char* fileName, const idImportOptions* options );
bool LoadBinaryModel( idFile* file, const ID_TIME_T sourceTimeStamp ) override;
void WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp = NULL ) const override;
dynamicModel_t IsDynamicModel() const override;
@ -271,7 +271,7 @@ struct md3Surface_s;
class idRenderModelMD3 : public idRenderModelStatic
{
public:
virtual void InitFromFile( const char* fileName );
virtual void InitFromFile( const char* fileName, const idImportOptions* options );
virtual bool SupportsBinaryModel()
{
return false;
@ -302,7 +302,7 @@ class idRenderModelLiquid : public idRenderModelStatic
public:
idRenderModelLiquid();
virtual void InitFromFile( const char* fileName, nvrhi::ICommandList* commandList );
virtual void InitFromFile( const char* fileName, nvrhi::ICommandList* commandList, const idImportOptions* options );
virtual bool SupportsBinaryModel()
{
return false;
@ -363,7 +363,7 @@ class idRenderModelPrt : public idRenderModelStatic
public:
idRenderModelPrt();
virtual void InitFromFile( const char* fileName );
virtual void InitFromFile( const char* fileName, const idImportOptions* options );
virtual bool SupportsBinaryModel()
{
return false;

View file

@ -46,7 +46,7 @@ If you have questions concerning this license or the applicable additional terms
idRenderModelMD3::InitFromFile
=================
*/
void idRenderModelMD3::InitFromFile( const char* fileName )
void idRenderModelMD3::InitFromFile( const char* fileName, const idImportOptions* options )
{
int i, j;
md3Header_t* pinmodel;

View file

@ -706,7 +706,7 @@ void idRenderModelMD5::ParseJoint( idLexer& parser, idMD5Joint* joint, idJointQu
idRenderModelMD5::InitFromFile
====================
*/
void idRenderModelMD5::InitFromFile( const char* fileName )
void idRenderModelMD5::InitFromFile( const char* fileName, const idImportOptions* options )
{
name = fileName;
LoadModel();

View file

@ -49,7 +49,7 @@ idRenderModelPrt::idRenderModelPrt()
idRenderModelPrt::InitFromFile
====================
*/
void idRenderModelPrt::InitFromFile( const char* fileName )
void idRenderModelPrt::InitFromFile( const char* fileName, const idImportOptions* options )
{
name = fileName;
particleSystem = static_cast<const idDeclParticle*>( declManager->FindType( DECL_PARTICLE, fileName ) );