2020-05-30 22:01:00 +00:00
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2005-2016 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
2021-12-29 09:29:22 +00:00
// the Free Software Foundation, either version 2 of the License, or
2020-05-30 22:01:00 +00:00
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
* * gl_models . cpp
* *
* * General model handling code
* *
* */
2023-09-23 07:56:27 +00:00
# include <stddef.h> // offsetof() macro.
2020-05-30 22:01:00 +00:00
# include "filesystem.h"
# include "cmdlib.h"
# include "sc_man.h"
# include "m_crc32.h"
# include "printf.h"
# include "model_ue1.h"
# include "model_obj.h"
# include "model_md2.h"
# include "model_md3.h"
# include "model_kvx.h"
2022-10-20 20:24:25 +00:00
# include "model_iqm.h"
2020-05-30 22:01:00 +00:00
# include "i_time.h"
# include "voxels.h"
# include "texturemanager.h"
# include "modelrenderer.h"
2022-07-23 10:05:27 +00:00
TArray < FString > savedModelFiles ;
2020-05-30 22:01:00 +00:00
TDeletingArray < FModel * > Models ;
TArray < FSpriteModelFrame > SpriteModelFrames ;
2023-12-10 12:30:50 +00:00
TMap < void * , FSpriteModelFrame > BaseSpriteModelFrames ;
2020-05-30 22:01:00 +00:00
/////////////////////////////////////////////////////////////////////////////
void FlushModels ( )
{
for ( int i = Models . Size ( ) - 1 ; i > = 0 ; i - - )
{
Models [ i ] - > DestroyVertexBuffer ( ) ;
}
}
/////////////////////////////////////////////////////////////////////////////
FModel : : FModel ( )
{
for ( int i = 0 ; i < NumModelRendererTypes ; i + + )
mVBuf [ i ] = nullptr ;
}
FModel : : ~ FModel ( )
{
DestroyVertexBuffer ( ) ;
}
void FModel : : DestroyVertexBuffer ( )
{
for ( int i = 0 ; i < NumModelRendererTypes ; i + + )
{
delete mVBuf [ i ] ;
mVBuf [ i ] = nullptr ;
}
}
//===========================================================================
//
// FindGFXFile
//
//===========================================================================
static int FindGFXFile ( FString & fn )
{
2023-10-07 16:44:31 +00:00
int lump = fileSystem . CheckNumForFullName ( fn . GetChars ( ) ) ; // if we find something that matches the name plus the extension, return it and do not enter the substitution logic below.
2020-05-30 22:01:00 +00:00
if ( lump ! = - 1 ) return lump ;
int best = - 1 ;
2021-08-14 08:04:45 +00:00
auto dot = fn . LastIndexOf ( ' . ' ) ;
auto slash = fn . LastIndexOf ( ' / ' ) ;
2020-05-30 22:01:00 +00:00
if ( dot > slash ) fn . Truncate ( dot ) ;
static const char * extensions [ ] = { " .png " , " .jpg " , " .tga " , " .pcx " , nullptr } ;
for ( const char * * extp = extensions ; * extp ; extp + + )
{
2023-10-07 16:44:31 +00:00
lump = fileSystem . CheckNumForFullName ( ( fn + * extp ) . GetChars ( ) ) ;
2020-05-30 22:01:00 +00:00
if ( lump > = best ) best = lump ;
}
return best ;
}
//===========================================================================
//
// LoadSkin
//
//===========================================================================
FTextureID LoadSkin ( const char * path , const char * fn )
{
FString buffer ;
buffer . Format ( " %s%s " , path , fn ) ;
int texlump = FindGFXFile ( buffer ) ;
const char * const texname = texlump < 0 ? fn : fileSystem . GetFileFullName ( texlump ) ;
2022-12-08 11:43:37 +00:00
return TexMan . CheckForTexture ( texname , ETextureType : : Any , FTextureManager : : TEXMAN_TryAny | FTextureManager : : TEXMAN_ForceLookup ) ;
2020-05-30 22:01:00 +00:00
}
//===========================================================================
//
// ModelFrameHash
//
//===========================================================================
int ModelFrameHash ( FSpriteModelFrame * smf )
{
2023-09-23 07:56:27 +00:00
return crc32 ( 0 , ( const unsigned char * ) ( & smf - > type ) , offsetof ( FSpriteModelFrame , hashnext ) - offsetof ( FSpriteModelFrame , type ) ) ;
2020-05-30 22:01:00 +00:00
}
//===========================================================================
//
// FindModel
//
//===========================================================================
2022-09-30 14:25:21 +00:00
unsigned FindModel ( const char * path , const char * modelfile , bool silent )
2020-05-30 22:01:00 +00:00
{
FModel * model = nullptr ;
FString fullname ;
2021-12-29 08:24:13 +00:00
if ( path ) fullname . Format ( " %s%s " , path , modelfile ) ;
else fullname = modelfile ;
2023-10-07 16:44:31 +00:00
int lump = fileSystem . CheckNumForFullName ( fullname . GetChars ( ) ) ;
2020-05-30 22:01:00 +00:00
if ( lump < 0 )
{
2021-12-29 08:24:13 +00:00
Printf ( PRINT_HIGH , " FindModel: '%s' not found \n " , fullname . GetChars ( ) ) ;
2020-05-30 22:01:00 +00:00
return - 1 ;
}
for ( unsigned i = 0 ; i < Models . Size ( ) ; i + + )
{
if ( ! Models [ i ] - > mFileName . CompareNoCase ( fullname ) ) return i ;
}
2023-12-20 23:03:37 +00:00
auto len = fileSystem . FileLength ( lump ) ;
if ( len > = 0x80000000ll )
{
Printf ( PRINT_HIGH , " LoadModel: File to large: '%s' \n " , fullname . GetChars ( ) ) ;
return - 1 ;
}
2023-08-23 18:36:19 +00:00
auto lumpd = fileSystem . ReadFile ( lump ) ;
2023-12-17 11:48:03 +00:00
const char * buffer = lumpd . string ( ) ;
2020-05-30 22:01:00 +00:00
if ( ( size_t ) fullname . LastIndexOf ( " _d.3d " ) = = fullname . Len ( ) - 5 )
{
FString anivfile = fullname . GetChars ( ) ;
anivfile . Substitute ( " _d.3d " , " _a.3d " ) ;
2023-10-07 16:44:31 +00:00
if ( fileSystem . CheckNumForFullName ( anivfile . GetChars ( ) ) > 0 )
2020-05-30 22:01:00 +00:00
{
model = new FUE1Model ;
}
}
else if ( ( size_t ) fullname . LastIndexOf ( " _a.3d " ) = = fullname . Len ( ) - 5 )
{
FString datafile = fullname . GetChars ( ) ;
datafile . Substitute ( " _a.3d " , " _d.3d " ) ;
2023-10-07 16:44:31 +00:00
if ( fileSystem . CheckNumForFullName ( datafile . GetChars ( ) ) > 0 )
2020-05-30 22:01:00 +00:00
{
model = new FUE1Model ;
}
}
else if ( ( size_t ) fullname . LastIndexOf ( " .obj " ) = = fullname . Len ( ) - 4 )
{
model = new FOBJModel ;
}
else if ( ! memcmp ( buffer , " DMDM " , 4 ) )
{
model = new FDMDModel ;
}
else if ( ! memcmp ( buffer , " IDP2 " , 4 ) )
{
model = new FMD2Model ;
}
else if ( ! memcmp ( buffer , " IDP3 " , 4 ) )
{
model = new FMD3Model ;
}
2022-10-20 20:24:25 +00:00
else if ( ! memcmp ( buffer , " INTERQUAKEMODEL \0 " , 16 ) )
{
model = new IQMModel ;
}
2020-05-30 22:01:00 +00:00
if ( model ! = nullptr )
{
2023-12-20 23:03:37 +00:00
if ( ! model - > Load ( path , lump , buffer , ( int ) len ) )
2020-05-30 22:01:00 +00:00
{
delete model ;
return - 1 ;
}
}
else
{
// try loading as a voxel
FVoxel * voxel = R_LoadKVX ( lump ) ;
if ( voxel ! = nullptr )
{
model = new FVoxelModel ( voxel , true ) ;
}
else
{
2021-12-29 08:24:13 +00:00
Printf ( PRINT_HIGH , " LoadModel: Unknown model format in '%s' \n " , fullname . GetChars ( ) ) ;
2020-05-30 22:01:00 +00:00
return - 1 ;
}
}
// The vertex buffer cannot be initialized here because this gets called before OpenGL is initialized
model - > mFileName = fullname ;
return Models . Push ( model ) ;
}