2018-05-15 08:28:25 +00:00
//
2017-11-25 11:11:57 +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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
* * gl_models . cpp
* *
* * General model handling code
* *
* */
# include "w_wad.h"
# include "cmdlib.h"
# include "sc_man.h"
# include "m_crc32.h"
# include "c_console.h"
# include "g_game.h"
# include "doomstat.h"
# include "g_level.h"
# include "r_state.h"
# include "d_player.h"
# include "g_levellocals.h"
# include "r_utility.h"
2017-11-25 12:19:00 +00:00
# include "r_data/models/models.h"
2018-05-13 00:58:25 +00:00
# include "r_data/models/models_ue1.h"
2018-06-03 07:26:42 +00:00
# include "r_data/models/models_obj.h"
2018-05-06 22:40:12 +00:00
# include "i_time.h"
2017-11-25 11:11:57 +00:00
2017-11-25 12:56:17 +00:00
# ifdef _MSC_VER
# pragma warning(disable:4244) // warning C4244: conversion from 'double' to 'float', possible loss of data
# endif
2017-11-25 11:11:57 +00:00
CVAR ( Bool , gl_interpolate_model_frames , true , CVAR_ARCHIVE )
2018-10-29 12:56:17 +00:00
EXTERN_CVAR ( Bool , r_drawvoxels )
2017-11-25 11:11:57 +00:00
extern TDeletingArray < FVoxel * > Voxels ;
extern TDeletingArray < FVoxelDef * > VoxelDefs ;
DeletingModelArray Models ;
2018-06-19 07:18:27 +00:00
void FModelRenderer : : RenderModel ( float x , float y , float z , FSpriteModelFrame * smf , AActor * actor , double ticFrac )
2017-11-25 11:11:57 +00:00
{
// Setup transformation.
int translation = 0 ;
if ( ! ( smf - > flags & MDL_IGNORETRANSLATION ) )
translation = actor - > Translation ;
// y scale for a sprite means height, i.e. z in the world!
float scaleFactorX = actor - > Scale . X * smf - > xscale ;
float scaleFactorY = actor - > Scale . X * smf - > yscale ;
float scaleFactorZ = actor - > Scale . Y * smf - > zscale ;
float pitch = 0 ;
float roll = 0 ;
double rotateOffset = 0 ;
2018-08-05 11:37:01 +00:00
DRotator angles ;
if ( actor - > renderflags & RF_INTERPOLATEANGLES ) // [Nash] use interpolated angles
angles = actor - > InterpolatedAngles ( ticFrac ) ;
else
angles = actor - > Angles ;
float angle = angles . Yaw . Degrees ;
2017-11-25 11:11:57 +00:00
// [BB] Workaround for the missing pitch information.
if ( ( smf - > flags & MDL_PITCHFROMMOMENTUM ) )
{
const double x = actor - > Vel . X ;
const double y = actor - > Vel . Y ;
const double z = actor - > Vel . Z ;
if ( actor - > Vel . LengthSquared ( ) > EQUAL_EPSILON )
{
// [BB] Calculate the pitch using spherical coordinates.
if ( z | | x | | y ) pitch = float ( atan ( z / sqrt ( x * x + y * y ) ) / M_PI * 180 ) ;
// Correcting pitch if model is moving backwards
if ( fabs ( x ) > EQUAL_EPSILON | | fabs ( y ) > EQUAL_EPSILON )
{
if ( ( x * cos ( angle * M_PI / 180 ) + y * sin ( angle * M_PI / 180 ) ) / sqrt ( x * x + y * y ) < 0 ) pitch * = - 1 ;
}
else pitch = fabs ( pitch ) ;
}
}
if ( smf - > flags & MDL_ROTATING )
{
2018-06-02 18:35:51 +00:00
if ( smf - > rotationSpeed > 0.0000000001 | | smf - > rotationSpeed < - 0.0000000001 )
2018-06-01 18:48:24 +00:00
{
double turns = ( I_GetTime ( ) + I_GetTimeFrac ( ) ) / ( 200.0 / smf - > rotationSpeed ) ;
2018-06-02 06:43:33 +00:00
turns - = floor ( turns ) ;
2018-06-01 18:48:24 +00:00
rotateOffset = turns * 360.0 ;
}
else
{
rotateOffset = 0.0 ;
}
2017-11-25 11:11:57 +00:00
}
// Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing.
// If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the velocity vector pitch.
if ( smf - > flags & MDL_USEACTORPITCH )
{
2018-08-05 11:37:01 +00:00
double d = angles . Pitch . Degrees ;
2017-11-25 11:11:57 +00:00
if ( smf - > flags & MDL_BADROTATION ) pitch + = d ;
else pitch - = d ;
}
2018-08-05 11:37:01 +00:00
if ( smf - > flags & MDL_USEACTORROLL ) roll + = angles . Roll . Degrees ;
2017-11-25 11:11:57 +00:00
VSMatrix objectToWorldMatrix ;
objectToWorldMatrix . loadIdentity ( ) ;
// Model space => World space
objectToWorldMatrix . translate ( x , z , y ) ;
// [Nash] take SpriteRotation into account
angle + = actor - > SpriteRotation . Degrees ;
// Applying model transformations:
// 1) Applying actor angle, pitch and roll to the model
2018-02-28 19:35:01 +00:00
if ( smf - > flags & MDL_USEROTATIONCENTER )
{
objectToWorldMatrix . translate ( smf - > rotationCenterX , smf - > rotationCenterZ , smf - > rotationCenterY ) ;
}
2017-11-25 11:11:57 +00:00
objectToWorldMatrix . rotate ( - angle , 0 , 1 , 0 ) ;
objectToWorldMatrix . rotate ( pitch , 0 , 0 , 1 ) ;
objectToWorldMatrix . rotate ( - roll , 1 , 0 , 0 ) ;
2018-02-28 19:35:01 +00:00
if ( smf - > flags & MDL_USEROTATIONCENTER )
{
objectToWorldMatrix . translate ( - smf - > rotationCenterX , - smf - > rotationCenterZ , - smf - > rotationCenterY ) ;
}
2017-11-25 11:11:57 +00:00
// 2) Applying Doomsday like rotation of the weapon pickup models
// The rotation angle is based on the elapsed time.
if ( smf - > flags & MDL_ROTATING )
{
objectToWorldMatrix . translate ( smf - > rotationCenterX , smf - > rotationCenterY , smf - > rotationCenterZ ) ;
objectToWorldMatrix . rotate ( rotateOffset , smf - > xrotate , smf - > yrotate , smf - > zrotate ) ;
objectToWorldMatrix . translate ( - smf - > rotationCenterX , - smf - > rotationCenterY , - smf - > rotationCenterZ ) ;
}
// 3) Scaling model.
objectToWorldMatrix . scale ( scaleFactorX , scaleFactorZ , scaleFactorY ) ;
// 4) Aplying model offsets (model offsets do not depend on model scalings).
objectToWorldMatrix . translate ( smf - > xoffset / smf - > xscale , smf - > zoffset / smf - > zscale , smf - > yoffset / smf - > yscale ) ;
// 5) Applying model rotations.
objectToWorldMatrix . rotate ( - smf - > angleoffset , 0 , 1 , 0 ) ;
objectToWorldMatrix . rotate ( smf - > pitchoffset , 0 , 0 , 1 ) ;
objectToWorldMatrix . rotate ( - smf - > rolloffset , 1 , 0 , 0 ) ;
// consider the pixel stretching. For non-voxels this must be factored out here
float stretch = ( smf - > modelIDs [ 0 ] ! = - 1 ? Models [ smf - > modelIDs [ 0 ] ] - > getAspectFactor ( ) : 1.f ) / level . info - > pixelstretch ;
objectToWorldMatrix . scale ( 1 , stretch , 1 ) ;
2018-06-02 11:07:47 +00:00
float orientation = scaleFactorX * scaleFactorY * scaleFactorZ ;
BeginDrawModel ( actor , smf , objectToWorldMatrix , orientation < 0 ) ;
2018-05-21 12:23:30 +00:00
RenderFrameModels ( smf , actor - > state , actor - > tics , actor - > GetClass ( ) , translation ) ;
2017-11-25 11:11:57 +00:00
EndDrawModel ( actor , smf ) ;
}
void FModelRenderer : : RenderHUDModel ( DPSprite * psp , float ofsX , float ofsY )
{
AActor * playermo = players [ consoleplayer ] . camera ;
2018-05-21 15:52:03 +00:00
FSpriteModelFrame * smf = FindModelFrame ( playermo - > player - > ReadyWeapon - > GetClass ( ) , psp - > GetState ( ) - > sprite , psp - > GetState ( ) - > GetFrame ( ) , false ) ;
2017-11-25 11:11:57 +00:00
// [BB] No model found for this sprite, so we can't render anything.
if ( smf = = nullptr )
return ;
// The model position and orientation has to be drawn independently from the position of the player,
// but we need to position it correctly in the world for light to work properly.
VSMatrix objectToWorldMatrix = GetViewToWorldMatrix ( ) ;
// Scaling model (y scale for a sprite means height, i.e. z in the world!).
objectToWorldMatrix . scale ( smf - > xscale , smf - > zscale , smf - > yscale ) ;
// Aplying model offsets (model offsets do not depend on model scalings).
objectToWorldMatrix . translate ( smf - > xoffset / smf - > xscale , smf - > zoffset / smf - > zscale , smf - > yoffset / smf - > yscale ) ;
// [BB] Weapon bob, very similar to the normal Doom weapon bob.
objectToWorldMatrix . rotate ( ofsX / 4 , 0 , 1 , 0 ) ;
objectToWorldMatrix . rotate ( ( ofsY - WEAPONTOP ) / - 4. , 1 , 0 , 0 ) ;
// [BB] For some reason the jDoom models need to be rotated.
objectToWorldMatrix . rotate ( 90.f , 0 , 1 , 0 ) ;
// Applying angleoffset, pitchoffset, rolloffset.
objectToWorldMatrix . rotate ( - smf - > angleoffset , 0 , 1 , 0 ) ;
objectToWorldMatrix . rotate ( smf - > pitchoffset , 0 , 0 , 1 ) ;
objectToWorldMatrix . rotate ( - smf - > rolloffset , 1 , 0 , 0 ) ;
2018-06-02 11:07:47 +00:00
float orientation = smf - > xscale * smf - > yscale * smf - > zscale ;
BeginDrawHUDModel ( playermo , objectToWorldMatrix , orientation < 0 ) ;
2018-05-21 12:23:30 +00:00
RenderFrameModels ( smf , psp - > GetState ( ) , psp - > GetTics ( ) , playermo - > player - > ReadyWeapon - > GetClass ( ) , 0 ) ;
2017-11-25 11:11:57 +00:00
EndDrawHUDModel ( playermo ) ;
}
2018-05-21 12:23:30 +00:00
void FModelRenderer : : RenderFrameModels ( const FSpriteModelFrame * smf , const FState * curState , const int curTics , const PClass * ti , int translation )
2017-11-25 11:11:57 +00:00
{
// [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation
// and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame.
FSpriteModelFrame * smfNext = nullptr ;
double inter = 0. ;
if ( gl_interpolate_model_frames & & ! ( smf - > flags & MDL_NOINTERPOLATION ) )
{
FState * nextState = curState - > GetNextState ( ) ;
if ( curState ! = nextState & & nextState )
{
// [BB] To interpolate at more than 35 fps we take tic fractions into account.
float ticFraction = 0. ;
// [BB] In case the tic counter is frozen we have to leave ticFraction at zero.
if ( ConsoleState = = c_up & & menuactive ! = MENU_On & & ! ( level . flags2 & LEVEL2_FROZEN ) )
{
2018-05-21 12:23:30 +00:00
ticFraction = I_GetTimeFrac ( ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 12:23:30 +00:00
inter = static_cast < double > ( curState - > Tics - curTics + ticFraction ) / static_cast < double > ( curState - > Tics ) ;
2017-11-25 11:11:57 +00:00
// [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics.
// In this case inter is negative and we need to set it to zero.
2018-05-21 12:23:30 +00:00
if ( curState - > Tics < curTics )
2017-11-25 11:11:57 +00:00
inter = 0. ;
else
{
// [BB] Workaround for actors that use the same frame twice in a row.
// Most of the standard Doom monsters do this in their see state.
if ( ( smf - > flags & MDL_INTERPOLATEDOUBLEDFRAMES ) )
{
const FState * prevState = curState - 1 ;
if ( ( curState - > sprite = = prevState - > sprite ) & & ( curState - > Frame = = prevState - > Frame ) )
{
inter / = 2. ;
inter + = 0.5 ;
}
if ( ( curState - > sprite = = nextState - > sprite ) & & ( curState - > Frame = = nextState - > Frame ) )
{
inter / = 2. ;
nextState = nextState - > GetNextState ( ) ;
}
}
if ( inter ! = 0.0 )
2018-05-21 15:52:03 +00:00
smfNext = FindModelFrame ( ti , nextState - > sprite , nextState - > Frame , false ) ;
2017-11-25 11:11:57 +00:00
}
}
}
for ( int i = 0 ; i < MAX_MODELS_PER_FRAME ; i + + )
{
if ( smf - > modelIDs [ i ] ! = - 1 )
{
FModel * mdl = Models [ smf - > modelIDs [ i ] ] ;
2018-12-07 01:53:18 +00:00
FTexture * tex = smf - > skinIDs [ i ] . isValid ( ) ? TexMan . GetTexture ( smf - > skinIDs [ i ] , true ) : nullptr ;
2017-11-25 11:11:57 +00:00
mdl - > BuildVertexBuffer ( this ) ;
mdl - > PushSpriteMDLFrame ( smf , i ) ;
if ( smfNext & & smf - > modelframes [ i ] ! = smfNext - > modelframes [ i ] )
mdl - > RenderFrame ( this , tex , smf - > modelframes [ i ] , smfNext - > modelframes [ i ] , inter , translation ) ;
else
mdl - > RenderFrame ( this , tex , smf - > modelframes [ i ] , smf - > modelframes [ i ] , 0.f , translation ) ;
}
}
}
/////////////////////////////////////////////////////////////////////////////
2018-05-21 15:52:03 +00:00
void FlushModels ( )
2017-11-25 11:11:57 +00:00
{
for ( int i = Models . Size ( ) - 1 ; i > = 0 ; i - - )
{
Models [ i ] - > DestroyVertexBuffer ( ) ;
}
}
/////////////////////////////////////////////////////////////////////////////
2018-05-21 15:52:03 +00:00
FModel : : FModel ( )
{
for ( int i = 0 ; i < NumModelRendererTypes ; i + + )
mVBuf [ i ] = nullptr ;
}
2017-11-25 11:11:57 +00:00
FModel : : ~ FModel ( )
{
2018-05-21 15:52:03 +00:00
DestroyVertexBuffer ( ) ;
}
void FModel : : DestroyVertexBuffer ( )
{
for ( int i = 0 ; i < NumModelRendererTypes ; i + + )
{
delete mVBuf [ i ] ;
mVBuf [ i ] = nullptr ;
}
2017-11-25 11:11:57 +00:00
}
static TArray < FSpriteModelFrame > SpriteModelFrames ;
static int * SpriteModelHash ;
//TArray<FStateModelFrame> StateModelFrames;
static void DeleteModelHash ( )
{
if ( SpriteModelHash ! = nullptr ) delete [ ] SpriteModelHash ;
SpriteModelHash = nullptr ;
}
//===========================================================================
//
// FindGFXFile
//
//===========================================================================
static int FindGFXFile ( FString & fn )
{
int lump = Wads . CheckNumForFullName ( fn ) ; // if we find something that matches the name plus the extension, return it and do not enter the substitution logic below.
if ( lump ! = - 1 ) return lump ;
int best = - 1 ;
int dot = fn . LastIndexOf ( ' . ' ) ;
int slash = fn . LastIndexOf ( ' / ' ) ;
if ( dot > slash ) fn . Truncate ( dot ) ;
static const char * extensions [ ] = { " .png " , " .jpg " , " .tga " , " .pcx " , nullptr } ;
for ( const char * * extp = extensions ; * extp ; extp + + )
{
int lump = Wads . CheckNumForFullName ( fn + * extp ) ;
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 : Wads . GetLumpFullName ( texlump ) ;
2018-03-25 18:26:16 +00:00
return TexMan . CheckForTexture ( texname , ETextureType : : Any , FTextureManager : : TEXMAN_TryAny ) ;
2017-11-25 11:11:57 +00:00
}
//===========================================================================
//
// ModelFrameHash
//
//===========================================================================
static int ModelFrameHash ( FSpriteModelFrame * smf )
{
const uint32_t * table = GetCRCTable ( ) ;
uint32_t hash = 0xffffffff ;
const char * s = ( const char * ) ( & smf - > type ) ; // this uses type, sprite and frame for hashing
const char * se = ( const char * ) ( & smf - > hashnext ) ;
for ( ; s < se ; s + + )
{
hash = CRC1 ( hash , * s , table ) ;
}
return hash ^ 0xffffffff ;
}
//===========================================================================
//
// FindModel
//
//===========================================================================
static unsigned FindModel ( const char * path , const char * modelfile )
{
FModel * model = nullptr ;
FString fullname ;
fullname . Format ( " %s%s " , path , modelfile ) ;
int lump = Wads . CheckNumForFullName ( fullname ) ;
if ( lump < 0 )
{
Printf ( " FindModel: '%s' not found \n " , fullname . GetChars ( ) ) ;
return - 1 ;
}
for ( unsigned i = 0 ; i < Models . Size ( ) ; i + + )
{
if ( ! Models [ i ] - > mFileName . CompareNoCase ( fullname ) ) return i ;
}
int len = Wads . LumpLength ( lump ) ;
FMemLump lumpd = Wads . ReadLump ( lump ) ;
char * buffer = ( char * ) lumpd . GetMem ( ) ;
2018-08-28 16:04:42 +00:00
if ( ( size_t ) fullname . LastIndexOf ( " _d.3d " ) = = fullname . Len ( ) - 5 )
2017-11-25 11:11:57 +00:00
{
2018-05-15 08:28:25 +00:00
FString anivfile = fullname . GetChars ( ) ;
anivfile . Substitute ( " _d.3d " , " _a.3d " ) ;
if ( Wads . CheckNumForFullName ( anivfile ) > 0 )
{
model = new FUE1Model ;
}
2017-11-25 11:11:57 +00:00
}
2018-08-28 16:04:42 +00:00
else if ( ( size_t ) fullname . LastIndexOf ( " _a.3d " ) = = fullname . Len ( ) - 5 )
2017-11-25 11:11:57 +00:00
{
2018-05-15 08:28:25 +00:00
FString datafile = fullname . GetChars ( ) ;
datafile . Substitute ( " _a.3d " , " _d.3d " ) ;
if ( Wads . CheckNumForFullName ( datafile ) > 0 )
{
model = new FUE1Model ;
}
2017-11-25 11:11:57 +00:00
}
2018-08-28 16:04:42 +00:00
else if ( ( size_t ) fullname . LastIndexOf ( " .obj " ) = = fullname . Len ( ) - 4 )
{
2018-06-03 07:26:42 +00:00
model = new FOBJModel ;
}
2018-05-15 08:33:24 +00:00
else if ( ! memcmp ( buffer , " DMDM " , 4 ) )
2018-05-13 00:58:25 +00:00
{
2018-05-15 08:33:24 +00:00
model = new FDMDModel ;
}
else if ( ! memcmp ( buffer , " IDP2 " , 4 ) )
{
model = new FMD2Model ;
}
else if ( ! memcmp ( buffer , " IDP3 " , 4 ) )
{
model = new FMD3Model ;
2018-05-13 00:58:25 +00:00
}
2017-11-25 11:11:57 +00:00
if ( model ! = nullptr )
{
if ( ! model - > Load ( path , lump , buffer , len ) )
{
delete model ;
return - 1 ;
}
}
else
{
// try loading as a voxel
FVoxel * voxel = R_LoadKVX ( lump ) ;
if ( voxel ! = nullptr )
{
model = new FVoxelModel ( voxel , true ) ;
}
else
{
Printf ( " LoadModel: Unknown model format in '%s' \n " , fullname . GetChars ( ) ) ;
return - 1 ;
}
}
// The vertex buffer cannot be initialized here because this gets called before OpenGL is initialized
model - > mFileName = fullname ;
return Models . Push ( model ) ;
}
//===========================================================================
//
2018-05-21 23:28:57 +00:00
// InitModels
2017-11-25 11:11:57 +00:00
//
//===========================================================================
2018-05-22 18:48:31 +00:00
static void ParseModelDefLump ( int Lump ) ;
2017-11-25 11:11:57 +00:00
2018-05-21 23:28:57 +00:00
void InitModels ( )
{
for ( unsigned i = 0 ; i < Models . Size ( ) ; i + + )
2017-11-25 11:11:57 +00:00
{
delete Models [ i ] ;
}
Models . Clear ( ) ;
SpriteModelFrames . Clear ( ) ;
DeleteModelHash ( ) ;
// First, create models for each voxel
for ( unsigned i = 0 ; i < Voxels . Size ( ) ; i + + )
{
FVoxelModel * md = new FVoxelModel ( Voxels [ i ] , false ) ;
Voxels [ i ] - > VoxelIndex = Models . Push ( md ) ;
}
// now create GL model frames for the voxeldefs
for ( unsigned i = 0 ; i < VoxelDefs . Size ( ) ; i + + )
{
FVoxelModel * md = ( FVoxelModel * ) Models [ VoxelDefs [ i ] - > Voxel - > VoxelIndex ] ;
2018-05-21 23:28:57 +00:00
FSpriteModelFrame smf ;
2017-11-25 11:11:57 +00:00
memset ( & smf , 0 , sizeof ( smf ) ) ;
2018-10-31 11:28:10 +00:00
smf . isVoxel = true ;
2017-11-25 11:11:57 +00:00
smf . modelIDs [ 1 ] = smf . modelIDs [ 2 ] = smf . modelIDs [ 3 ] = - 1 ;
smf . modelIDs [ 0 ] = VoxelDefs [ i ] - > Voxel - > VoxelIndex ;
smf . skinIDs [ 0 ] = md - > GetPaletteTexture ( ) ;
smf . xscale = smf . yscale = smf . zscale = VoxelDefs [ i ] - > Scale ;
smf . angleoffset = VoxelDefs [ i ] - > AngleOffset . Degrees ;
if ( VoxelDefs [ i ] - > PlacedSpin ! = 0 )
{
smf . yrotate = 1.f ;
smf . rotationSpeed = VoxelDefs [ i ] - > PlacedSpin / 55.55f ;
smf . flags | = MDL_ROTATING ;
}
VoxelDefs [ i ] - > VoxeldefIndex = SpriteModelFrames . Push ( smf ) ;
if ( VoxelDefs [ i ] - > PlacedSpin ! = VoxelDefs [ i ] - > DroppedSpin )
{
if ( VoxelDefs [ i ] - > DroppedSpin ! = 0 )
{
smf . yrotate = 1.f ;
smf . rotationSpeed = VoxelDefs [ i ] - > DroppedSpin / 55.55f ;
smf . flags | = MDL_ROTATING ;
}
else
{
smf . yrotate = 0 ;
smf . rotationSpeed = 0 ;
smf . flags & = ~ MDL_ROTATING ;
}
SpriteModelFrames . Push ( smf ) ;
}
}
2018-05-21 23:28:57 +00:00
int Lump ;
int lastLump = 0 ;
2017-11-25 11:11:57 +00:00
while ( ( Lump = Wads . FindLump ( " MODELDEF " , & lastLump ) ) ! = - 1 )
{
2018-05-21 23:28:57 +00:00
ParseModelDefLump ( Lump ) ;
}
// create a hash table for quick access
SpriteModelHash = new int [ SpriteModelFrames . Size ( ) ] ;
atterm ( DeleteModelHash ) ;
memset ( SpriteModelHash , 0xff , SpriteModelFrames . Size ( ) * sizeof ( int ) ) ;
for ( unsigned int i = 0 ; i < SpriteModelFrames . Size ( ) ; i + + )
{
int j = ModelFrameHash ( & SpriteModelFrames [ i ] ) % SpriteModelFrames . Size ( ) ;
SpriteModelFrames [ i ] . hashnext = SpriteModelHash [ j ] ;
SpriteModelHash [ j ] = i ;
}
}
static void ParseModelDefLump ( int Lump )
{
FScanner sc ( Lump ) ;
while ( sc . GetString ( ) )
{
if ( sc . Compare ( " model " ) )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
int index , surface ;
FString path = " " ;
sc . MustGetString ( ) ;
FSpriteModelFrame smf ;
memset ( & smf , 0 , sizeof ( smf ) ) ;
smf . modelIDs [ 0 ] = smf . modelIDs [ 1 ] = smf . modelIDs [ 2 ] = smf . modelIDs [ 3 ] = - 1 ;
smf . xscale = smf . yscale = smf . zscale = 1.f ;
smf . type = PClass : : FindClass ( sc . String ) ;
if ( ! smf . type | | smf . type - > Defaults = = nullptr )
{
sc . ScriptError ( " MODELDEF: Unknown actor type '%s' \n " , sc . String ) ;
}
sc . MustGetStringName ( " { " ) ;
while ( ! sc . CheckString ( " } " ) )
2017-11-25 11:11:57 +00:00
{
sc . MustGetString ( ) ;
2018-05-21 23:28:57 +00:00
if ( sc . Compare ( " path " ) )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
sc . MustGetString ( ) ;
FixPathSeperator ( sc . String ) ;
path = sc . String ;
if ( path [ ( int ) path . Len ( ) - 1 ] ! = ' / ' ) path + = ' / ' ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
else if ( sc . Compare ( " model " ) )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
sc . MustGetNumber ( ) ;
index = sc . Number ;
if ( index < 0 | | index > = MAX_MODELS_PER_FRAME )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
sc . ScriptError ( " Too many models in %s " , smf . type - > TypeName . GetChars ( ) ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
sc . MustGetString ( ) ;
FixPathSeperator ( sc . String ) ;
smf . modelIDs [ index ] = FindModel ( path . GetChars ( ) , sc . String ) ;
if ( smf . modelIDs [ index ] = = - 1 )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
Printf ( " %s: model not found in %s \n " , sc . String , path . GetChars ( ) ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
}
else if ( sc . Compare ( " scale " ) )
{
sc . MustGetFloat ( ) ;
smf . xscale = sc . Float ;
sc . MustGetFloat ( ) ;
smf . yscale = sc . Float ;
sc . MustGetFloat ( ) ;
smf . zscale = sc . Float ;
}
// [BB] Added zoffset reading.
// Now it must be considered deprecated.
else if ( sc . Compare ( " zoffset " ) )
{
sc . MustGetFloat ( ) ;
smf . zoffset = sc . Float ;
}
// Offset reading.
else if ( sc . Compare ( " offset " ) )
{
sc . MustGetFloat ( ) ;
smf . xoffset = sc . Float ;
sc . MustGetFloat ( ) ;
smf . yoffset = sc . Float ;
sc . MustGetFloat ( ) ;
smf . zoffset = sc . Float ;
}
// angleoffset, pitchoffset and rolloffset reading.
else if ( sc . Compare ( " angleoffset " ) )
{
sc . MustGetFloat ( ) ;
smf . angleoffset = sc . Float ;
}
else if ( sc . Compare ( " pitchoffset " ) )
{
sc . MustGetFloat ( ) ;
smf . pitchoffset = sc . Float ;
}
else if ( sc . Compare ( " rolloffset " ) )
{
sc . MustGetFloat ( ) ;
smf . rolloffset = sc . Float ;
}
// [BB] Added model flags reading.
else if ( sc . Compare ( " ignoretranslation " ) )
{
smf . flags | = MDL_IGNORETRANSLATION ;
}
else if ( sc . Compare ( " pitchfrommomentum " ) )
{
smf . flags | = MDL_PITCHFROMMOMENTUM ;
}
else if ( sc . Compare ( " inheritactorpitch " ) )
{
smf . flags | = MDL_USEACTORPITCH | MDL_BADROTATION ;
}
else if ( sc . Compare ( " inheritactorroll " ) )
{
smf . flags | = MDL_USEACTORROLL ;
}
else if ( sc . Compare ( " useactorpitch " ) )
{
smf . flags | = MDL_USEACTORPITCH ;
}
else if ( sc . Compare ( " useactorroll " ) )
{
smf . flags | = MDL_USEACTORROLL ;
}
else if ( sc . Compare ( " rotating " ) )
{
smf . flags | = MDL_ROTATING ;
smf . xrotate = 0. ;
smf . yrotate = 1. ;
smf . zrotate = 0. ;
smf . rotationCenterX = 0. ;
smf . rotationCenterY = 0. ;
smf . rotationCenterZ = 0. ;
smf . rotationSpeed = 1. ;
}
else if ( sc . Compare ( " rotation-speed " ) )
{
sc . MustGetFloat ( ) ;
smf . rotationSpeed = sc . Float ;
}
else if ( sc . Compare ( " rotation-vector " ) )
{
sc . MustGetFloat ( ) ;
smf . xrotate = sc . Float ;
sc . MustGetFloat ( ) ;
smf . yrotate = sc . Float ;
sc . MustGetFloat ( ) ;
smf . zrotate = sc . Float ;
}
else if ( sc . Compare ( " rotation-center " ) )
{
sc . MustGetFloat ( ) ;
smf . rotationCenterX = sc . Float ;
sc . MustGetFloat ( ) ;
smf . rotationCenterY = sc . Float ;
sc . MustGetFloat ( ) ;
smf . rotationCenterZ = sc . Float ;
}
else if ( sc . Compare ( " interpolatedoubledframes " ) )
{
smf . flags | = MDL_INTERPOLATEDOUBLEDFRAMES ;
}
else if ( sc . Compare ( " nointerpolation " ) )
{
smf . flags | = MDL_NOINTERPOLATION ;
}
else if ( sc . Compare ( " skin " ) )
{
sc . MustGetNumber ( ) ;
index = sc . Number ;
if ( index < 0 | | index > = MAX_MODELS_PER_FRAME )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
sc . ScriptError ( " Too many models in %s " , smf . type - > TypeName . GetChars ( ) ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
sc . MustGetString ( ) ;
FixPathSeperator ( sc . String ) ;
if ( sc . Compare ( " " ) )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
smf . skinIDs [ index ] = FNullTextureID ( ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
else
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
smf . skinIDs [ index ] = LoadSkin ( path . GetChars ( ) , sc . String ) ;
if ( ! smf . skinIDs [ index ] . isValid ( ) )
{
Printf ( " Skin '%s' not found in '%s' \n " ,
sc . String , smf . type - > TypeName . GetChars ( ) ) ;
}
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
}
else if ( sc . Compare ( " surfaceskin " ) )
{
sc . MustGetNumber ( ) ;
index = sc . Number ;
sc . MustGetNumber ( ) ;
surface = sc . Number ;
if ( index < 0 | | index > = MAX_MODELS_PER_FRAME )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
sc . ScriptError ( " Too many models in %s " , smf . type - > TypeName . GetChars ( ) ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
if ( surface < 0 | | surface > = MD3_MAX_SURFACES )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
sc . ScriptError ( " Invalid MD3 Surface %d in %s " , MD3_MAX_SURFACES , smf . type - > TypeName . GetChars ( ) ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
sc . MustGetString ( ) ;
FixPathSeperator ( sc . String ) ;
if ( sc . Compare ( " " ) )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
smf . surfaceskinIDs [ index ] [ surface ] = FNullTextureID ( ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
else
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
smf . surfaceskinIDs [ index ] [ surface ] = LoadSkin ( path . GetChars ( ) , sc . String ) ;
if ( ! smf . surfaceskinIDs [ index ] [ surface ] . isValid ( ) )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
Printf ( " Surface Skin '%s' not found in '%s' \n " ,
sc . String , smf . type - > TypeName . GetChars ( ) ) ;
2017-11-25 11:11:57 +00:00
}
}
2018-05-21 23:28:57 +00:00
}
else if ( sc . Compare ( " frameindex " ) | | sc . Compare ( " frame " ) )
{
bool isframe = ! ! sc . Compare ( " frame " ) ;
2017-11-25 11:11:57 +00:00
2018-05-21 23:28:57 +00:00
sc . MustGetString ( ) ;
smf . sprite = - 1 ;
for ( int i = 0 ; i < ( int ) sprites . Size ( ) ; + + i )
{
if ( strnicmp ( sprites [ i ] . name , sc . String , 4 ) = = 0 )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
if ( sprites [ i ] . numframes = = 0 )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
//sc.ScriptError("Sprite %s has no frames", sc.String);
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
smf . sprite = i ;
break ;
2017-11-25 11:11:57 +00:00
}
}
2018-05-21 23:28:57 +00:00
if ( smf . sprite = = - 1 )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
sc . ScriptError ( " Unknown sprite %s in model definition for %s " , sc . String , smf . type - > TypeName . GetChars ( ) ) ;
}
2017-11-25 11:11:57 +00:00
2018-05-21 23:28:57 +00:00
sc . MustGetString ( ) ;
FString framechars = sc . String ;
2017-11-25 11:11:57 +00:00
2018-05-21 23:28:57 +00:00
sc . MustGetNumber ( ) ;
index = sc . Number ;
if ( index < 0 | | index > = MAX_MODELS_PER_FRAME )
{
sc . ScriptError ( " Too many models in %s " , smf . type - > TypeName . GetChars ( ) ) ;
}
if ( isframe )
{
2017-11-25 11:11:57 +00:00
sc . MustGetString ( ) ;
2018-05-21 23:28:57 +00:00
if ( smf . modelIDs [ index ] ! = - 1 )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
FModel * model = Models [ smf . modelIDs [ index ] ] ;
smf . modelframes [ index ] = model - > FindFrame ( sc . String ) ;
if ( smf . modelframes [ index ] = = - 1 ) sc . ScriptError ( " Unknown frame '%s' in %s " , sc . String , smf . type - > TypeName . GetChars ( ) ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
else smf . modelframes [ index ] = - 1 ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
else
2018-02-28 19:35:01 +00:00
{
2018-05-21 23:28:57 +00:00
sc . MustGetNumber ( ) ;
smf . modelframes [ index ] = sc . Number ;
2018-02-28 19:35:01 +00:00
}
2018-05-21 23:28:57 +00:00
for ( int i = 0 ; framechars [ i ] > 0 ; i + + )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
char map [ 29 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
int c = toupper ( framechars [ i ] ) - ' A ' ;
if ( c < 0 | | c > = 29 )
{
sc . ScriptError ( " Invalid frame character %c found " , c + ' A ' ) ;
}
if ( map [ c ] ) continue ;
smf . frame = c ;
SpriteModelFrames . Push ( smf ) ;
GetDefaultByType ( smf . type ) - > hasmodel = true ;
map [ c ] = 1 ;
2017-11-25 11:11:57 +00:00
}
}
2018-05-21 23:28:57 +00:00
else if ( sc . Compare ( " dontcullbackfaces " ) )
{
smf . flags | = MDL_DONTCULLBACKFACES ;
}
else if ( sc . Compare ( " userotationcenter " ) )
{
smf . flags | = MDL_USEROTATIONCENTER ;
smf . rotationCenterX = 0. ;
smf . rotationCenterY = 0. ;
smf . rotationCenterZ = 0. ;
}
else
{
sc . ScriptMessage ( " Unrecognized string \" %s \" " , sc . String ) ;
}
2017-11-25 11:11:57 +00:00
}
}
2018-05-23 22:59:45 +00:00
else if ( sc . Compare ( " #include " ) )
2018-05-21 23:28:57 +00:00
{
sc . MustGetString ( ) ;
// This is not using sc.Open because it can print a more useful error message when done here
int includelump = Wads . CheckNumForFullName ( sc . String , true ) ;
if ( includelump = = - 1 )
2018-05-23 22:59:45 +00:00
{
if ( strcmp ( sc . String , " sentinel.modl " ) ! = 0 ) // Gene Tech mod has a broken #include statement
sc . ScriptError ( " Lump '%s' not found " , sc . String ) ;
}
2018-05-21 23:28:57 +00:00
else
2018-05-23 22:59:45 +00:00
{
2018-05-21 23:28:57 +00:00
ParseModelDefLump ( includelump ) ;
2018-05-23 22:59:45 +00:00
}
}
2017-11-25 11:11:57 +00:00
}
}
//===========================================================================
//
2018-05-21 15:52:03 +00:00
// FindModelFrame
2017-11-25 11:11:57 +00:00
//
//===========================================================================
2018-05-21 15:52:03 +00:00
FSpriteModelFrame * FindModelFrame ( const PClass * ti , int sprite , int frame , bool dropped )
2017-11-25 11:11:57 +00:00
{
if ( GetDefaultByType ( ti ) - > hasmodel )
{
FSpriteModelFrame smf ;
memset ( & smf , 0 , sizeof ( smf ) ) ;
smf . type = ti ;
smf . sprite = sprite ;
smf . frame = frame ;
int hash = SpriteModelHash [ ModelFrameHash ( & smf ) % SpriteModelFrames . Size ( ) ] ;
while ( hash > = 0 )
{
FSpriteModelFrame * smff = & SpriteModelFrames [ hash ] ;
if ( smff - > type = = ti & & smff - > sprite = = sprite & & smff - > frame = = frame ) return smff ;
hash = smff - > hashnext ;
}
}
// Check for voxel replacements
if ( r_drawvoxels )
{
spritedef_t * sprdef = & sprites [ sprite ] ;
if ( frame < sprdef - > numframes )
{
spriteframe_t * sprframe = & SpriteFrames [ sprdef - > spriteframes + frame ] ;
if ( sprframe - > Voxel ! = nullptr )
{
int index = sprframe - > Voxel - > VoxeldefIndex ;
if ( dropped & & sprframe - > Voxel - > DroppedSpin ! = sprframe - > Voxel - > PlacedSpin ) index + + ;
return & SpriteModelFrames [ index ] ;
}
}
}
return nullptr ;
}
//===========================================================================
//
2018-05-21 15:52:03 +00:00
// IsHUDModelForPlayerAvailable
2017-11-25 11:11:57 +00:00
//
//===========================================================================
2018-05-21 15:52:03 +00:00
bool IsHUDModelForPlayerAvailable ( player_t * player )
2017-11-25 11:11:57 +00:00
{
if ( player = = nullptr | | player - > ReadyWeapon = = nullptr )
return false ;
DPSprite * psp = player - > FindPSprite ( PSP_WEAPON ) ;
if ( psp = = nullptr | | psp - > GetState ( ) = = nullptr )
return false ;
FState * state = psp - > GetState ( ) ;
2018-05-21 15:52:03 +00:00
FSpriteModelFrame * smf = FindModelFrame ( player - > ReadyWeapon - > GetClass ( ) , state - > sprite , state - > GetFrame ( ) , false ) ;
2017-11-25 11:11:57 +00:00
return ( smf ! = nullptr ) ;
}
2018-06-06 05:08:05 +00:00