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
* *
* */
2020-04-11 11:36:23 +00:00
# include "filesystem.h"
2017-11-25 11:11:57 +00:00
# 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"
2020-04-27 18:50:46 +00:00
# include "models.h"
2020-04-26 23:29:25 +00:00
# include "model_kvx.h"
2018-05-06 22:40:12 +00:00
# include "i_time.h"
2020-04-11 17:00:07 +00:00
# include "texturemanager.h"
2020-04-26 22:03:23 +00:00
# include "modelrenderer.h"
2017-11-25 11:11:57 +00:00
2020-04-26 23:16:17 +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 ;
2022-07-05 20:29:47 +00:00
void RenderFrameModels ( FModelRenderer * renderer , FLevelLocals * Level , const FSpriteModelFrame * smf , const FState * curState , const int curTics , const PClass * ti , int translation , AActor * actor ) ;
2020-04-26 22:25:53 +00:00
void RenderModel ( FModelRenderer * renderer , 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 ;
2022-08-26 15:38:48 +00:00
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 )
{
2022-08-26 15:38:48 +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 ;
}
2022-08-26 15:38:48 +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
2022-08-26 15:38:48 +00:00
angle + = actor - > SpriteRotation . Degrees ( ) ;
2017-11-25 11:11:57 +00:00
// 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
2020-04-26 22:03:23 +00:00
float stretch = ( smf - > modelIDs [ 0 ] ! = - 1 ? Models [ smf - > modelIDs [ 0 ] ] - > getAspectFactor ( actor - > Level - > info - > pixelstretch ) : 1.f ) / actor - > Level - > info - > pixelstretch ;
2017-11-25 11:11:57 +00:00
objectToWorldMatrix . scale ( 1 , stretch , 1 ) ;
2018-06-02 11:07:47 +00:00
float orientation = scaleFactorX * scaleFactorY * scaleFactorZ ;
2020-04-26 22:25:53 +00:00
renderer - > BeginDrawModel ( actor - > RenderStyle , smf , objectToWorldMatrix , orientation < 0 ) ;
2022-06-30 21:37:31 +00:00
RenderFrameModels ( renderer , actor - > Level , smf , actor - > state , actor - > tics , actor - > modelData ! = nullptr ? actor - > modelData - > modelDef ! = NAME_None ? PClass : : FindActor ( actor - > modelData - > modelDef ) : actor - > GetClass ( ) : actor - > GetClass ( ) , translation , actor ) ;
2020-04-26 22:25:53 +00:00
renderer - > EndDrawModel ( actor - > RenderStyle , smf ) ;
2017-11-25 11:11:57 +00:00
}
2020-04-26 22:25:53 +00:00
void RenderHUDModel ( FModelRenderer * renderer , DPSprite * psp , float ofsX , float ofsY )
2017-11-25 11:11:57 +00:00
{
AActor * playermo = players [ consoleplayer ] . camera ;
2022-06-30 21:37:31 +00:00
FSpriteModelFrame * smf = psp - > Caller ! = nullptr ? FindModelFrame ( psp - > Caller - > modelData ! = nullptr ? psp - > Caller - > modelData - > modelDef ! = NAME_None ? PClass : : FindActor ( psp - > Caller - > modelData - > modelDef ) : psp - > Caller - > GetClass ( ) : psp - > Caller - > GetClass ( ) , psp - > GetSprite ( ) , psp - > GetFrame ( ) , false ) : nullptr ;
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.
2020-04-26 22:25:53 +00:00
VSMatrix objectToWorldMatrix = renderer - > GetViewToWorldMatrix ( ) ;
2017-11-25 11:11:57 +00:00
2022-03-07 20:45:52 +00:00
// [Nash] Optional scale weapon FOV
float fovscale = 1.0f ;
if ( smf - > flags & MDL_SCALEWEAPONFOV )
{
fovscale = tan ( players [ consoleplayer ] . DesiredFOV * ( 0.5f * M_PI / 180.f ) ) ;
fovscale = 1.f + ( fovscale - 1.f ) * cl_scaleweaponfov ;
}
2017-11-25 11:11:57 +00:00
// Scaling model (y scale for a sprite means height, i.e. z in the world!).
2022-03-07 20:45:52 +00:00
objectToWorldMatrix . scale ( smf - > xscale , smf - > zscale , smf - > yscale / fovscale ) ;
2017-11-25 11:11:57 +00:00
// 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 ;
2020-04-26 22:25:53 +00:00
renderer - > BeginDrawHUDModel ( playermo - > RenderStyle , objectToWorldMatrix , orientation < 0 ) ;
2020-10-11 21:14:06 +00:00
uint32_t trans = psp - > GetTranslation ( ) ! = 0 ? psp - > GetTranslation ( ) : 0 ;
if ( ( psp - > Flags & PSPF_PLAYERTRANSLATED ) ) trans = psp - > Owner - > mo - > Translation ;
2022-06-30 21:37:31 +00:00
RenderFrameModels ( renderer , playermo - > Level , smf , psp - > GetState ( ) , psp - > GetTics ( ) , psp - > Caller - > modelData ! = nullptr ? psp - > Caller - > modelData - > modelDef ! = NAME_None ? PClass : : FindActor ( psp - > Caller - > modelData - > modelDef ) : psp - > Caller - > GetClass ( ) : psp - > Caller - > GetClass ( ) , trans , psp - > Caller ) ;
2020-04-26 22:25:53 +00:00
renderer - > EndDrawHUDModel ( playermo - > RenderStyle ) ;
2017-11-25 11:11:57 +00:00
}
A_ChangeModeldef
-Added A_ChangeModelDef
A_ChangeModel(modeldef, modelpath, model, modelindex, skinpath, skin, skinid, flags)
This can change the modeldef, model and skins of an actor.
Currently, modelindex and skinindex accept indices from 0-15.
An actor MUST have a modeldef in order to use this function, either defined from modeldef, or given one through the modeldef parameter. You can pass "" to use the same modeldef. Likewise, passing "" for model or skin will just revert to the default model.
Available flags:
CMDL_WEAPONTOPLAYER - If used on a weapon, this instead change's the model on the player instead.
One issue I am aware of right now is that clearing a model by "" sort of works but is buggy. For now you can just manually set the model back using the names explicitly. However, I am stumped and I think getting more eyes on it would help.
2022-06-28 03:23:28 +00:00
void RenderFrameModels ( FModelRenderer * renderer , FLevelLocals * Level , const FSpriteModelFrame * smf , const FState * curState , const int curTics , const PClass * ti , int translation , AActor * actor )
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. ;
2022-07-23 08:10:15 +00:00
if ( gl_interpolate_model_frames & & ! ( smf - > flags & MDL_NOINTERPOLATION ) )
2017-11-25 11:11:57 +00:00
{
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.
2019-01-29 01:18:19 +00:00
if ( ConsoleState = = c_up & & menuactive ! = MENU_On & & ! Level - > isFrozen ( ) )
2017-11-25 11:11:57 +00:00
{
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.
2022-07-23 08:10:15 +00:00
if ( ( smf - > flags & MDL_INTERPOLATEDOUBLEDFRAMES ) )
2017-11-25 11:11:57 +00:00
{
const FState * prevState = curState - 1 ;
if ( ( curState - > sprite = = prevState - > sprite ) & & ( curState - > Frame = = prevState - > Frame ) )
{
inter / = 2. ;
inter + = 0.5 ;
}
2019-06-12 14:21:43 +00:00
if ( nextState & & ( ( curState - > sprite = = nextState - > sprite ) & & ( curState - > Frame = = nextState - > Frame ) ) )
2017-11-25 11:11:57 +00:00
{
inter / = 2. ;
nextState = nextState - > GetNextState ( ) ;
}
}
2019-06-12 14:21:43 +00:00
if ( nextState & & 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
}
}
}
2022-07-23 08:10:15 +00:00
int modelsamount = smf - > modelsAmount ;
2022-07-09 05:47:12 +00:00
//[SM] - if we added any models for the frame to also render, then we also need to update modelsAmount for this smf
if ( actor - > modelData ! = nullptr )
{
2022-08-03 06:44:38 +00:00
if ( actor - > modelData - > modelIDs . Size ( ) > ( unsigned ) modelsamount )
2022-07-27 00:19:35 +00:00
modelsamount = actor - > modelData - > modelIDs . Size ( ) ;
2022-07-09 05:47:12 +00:00
}
2022-07-23 08:10:15 +00:00
TArray < FTextureID > surfaceskinids ;
for ( int i = 0 ; i < modelsamount ; i + + )
2022-06-28 19:04:10 +00:00
{
2022-07-23 08:10:15 +00:00
int modelid = - 1 ;
int modelframe = - 1 ;
int modelframenext = - 1 ;
FTextureID skinid ; skinid . SetInvalid ( ) ;
surfaceskinids . Clear ( ) ;
2022-06-30 07:12:06 +00:00
if ( actor - > modelData ! = nullptr )
2022-06-28 19:04:10 +00:00
{
2022-07-27 00:19:35 +00:00
if ( i < ( int ) actor - > modelData - > modelIDs . Size ( ) )
modelid = actor - > modelData - > modelIDs [ i ] ;
2022-07-23 08:10:15 +00:00
2022-07-09 05:47:12 +00:00
if ( i < ( int ) actor - > modelData - > modelFrameGenerators . Size ( ) )
{
//[SM] - We will use this little snippet to allow a modder to specify a model index to clone. It's also pointless to clone something that clones something else in this case. And causes me headaches.
2022-07-23 08:10:15 +00:00
if ( actor - > modelData - > modelFrameGenerators [ i ] > = 0 & & smf - > modelframes . Size ( ) < ( unsigned ) i & & smf - > modelframes [ i ] ! = - 1 )
2022-07-09 05:47:12 +00:00
{
2022-07-23 08:10:15 +00:00
modelframe = smf - > modelframes [ actor - > modelData - > modelFrameGenerators [ i ] ] ;
if ( smfNext ) modelframenext = smfNext - > modelframes [ actor - > modelData - > modelFrameGenerators [ i ] ] ;
2022-07-09 05:47:12 +00:00
}
}
2022-06-30 07:12:06 +00:00
if ( i < ( int ) actor - > modelData - > skinIDs . Size ( ) )
{
if ( actor - > modelData - > skinIDs [ i ] . isValid ( ) )
2022-07-23 08:10:15 +00:00
skinid = actor - > modelData - > skinIDs [ i ] ;
2022-07-05 20:29:47 +00:00
}
for ( int surface = i * MD3_MAX_SURFACES ; surface < ( i + 1 ) * MD3_MAX_SURFACES ; surface + + )
{
if ( surface < ( int ) actor - > modelData - > surfaceSkinIDs . Size ( ) )
{
if ( actor - > modelData - > surfaceSkinIDs [ surface ] . isValid ( ) )
2022-07-23 08:10:15 +00:00
{
// only make a copy of the surfaceskinIDs array if really needed
if ( surfaceskinids . Size ( ) = = 0 ) surfaceskinids = smf - > surfaceskinIDs ;
surfaceskinids [ surface ] = actor - > modelData - > surfaceSkinIDs [ surface ] ;
}
2022-07-05 20:29:47 +00:00
}
2022-06-30 07:12:06 +00:00
}
2022-06-28 19:04:10 +00:00
}
2022-07-23 08:10:15 +00:00
if ( i < smf - > modelsAmount )
{
if ( modelid = = - 1 ) modelid = smf - > modelIDs [ i ] ;
if ( modelframe = = - 1 ) modelframe = smf - > modelframes [ i ] ;
if ( modelframenext = = - 1 & & smfNext ) modelframenext = smfNext - > modelframes [ i ] ;
if ( ! skinid . isValid ( ) ) skinid = smf - > skinIDs [ i ] ;
}
if ( modelid > = 0 )
2017-11-25 11:11:57 +00:00
{
2022-07-23 08:10:15 +00:00
FModel * mdl = Models [ modelid ] ;
auto tex = skinid . isValid ( ) ? TexMan . GetGameTexture ( skinid , true ) : nullptr ;
2020-04-26 22:25:53 +00:00
mdl - > BuildVertexBuffer ( renderer ) ;
2017-11-25 11:11:57 +00:00
2022-07-23 08:35:01 +00:00
auto & ssids = surfaceskinids . Size ( ) > 0 ? surfaceskinids : smf - > surfaceskinIDs ;
auto ssidp = ( unsigned ) ( i * MD3_MAX_SURFACES ) < ssids . Size ( ) ? & ssids [ i * MD3_MAX_SURFACES ] : nullptr ;
2017-11-25 11:11:57 +00:00
2022-07-23 08:10:15 +00:00
if ( smfNext & & modelframe ! = modelframenext )
2022-07-23 08:35:01 +00:00
mdl - > RenderFrame ( renderer , tex , modelframe , modelframenext , inter , translation , ssidp ) ;
2017-11-25 11:11:57 +00:00
else
2022-07-23 08:35:01 +00:00
mdl - > RenderFrame ( renderer , tex , modelframe , modelframe , 0.f , translation , ssidp ) ;
2017-11-25 11:11:57 +00:00
}
}
}
2018-05-21 15:52:03 +00:00
2018-12-29 12:27:43 +00:00
static TArray < int > SpriteModelHash ;
2017-11-25 11:11:57 +00:00
//TArray<FStateModelFrame> StateModelFrames;
//===========================================================================
//
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 ( )
{
2018-12-29 12:27:43 +00:00
Models . DeleteAndClear ( ) ;
2017-11-25 11:11:57 +00:00
SpriteModelFrames . Clear ( ) ;
2018-12-29 12:27:43 +00:00
SpriteModelHash . Clear ( ) ;
2017-11-25 11:11:57 +00:00
// 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 ;
2021-02-09 13:48:43 +00:00
smf . modelsAmount = 1 ;
smf . modelframes . Alloc ( 1 ) ;
smf . modelframes [ 0 ] = - 1 ;
smf . modelIDs . Alloc ( 1 ) ;
2017-11-25 11:11:57 +00:00
smf . modelIDs [ 0 ] = VoxelDefs [ i ] - > Voxel - > VoxelIndex ;
2021-02-09 13:48:43 +00:00
smf . skinIDs . Alloc ( 1 ) ;
2017-11-25 11:11:57 +00:00
smf . skinIDs [ 0 ] = md - > GetPaletteTexture ( ) ;
smf . xscale = smf . yscale = smf . zscale = VoxelDefs [ i ] - > Scale ;
2022-08-26 15:38:48 +00:00
smf . angleoffset = VoxelDefs [ i ] - > AngleOffset . Degrees ( ) ;
2022-07-24 22:48:43 +00:00
if ( VoxelDefs [ i ] - > PitchFromMomentum ) smf . flags | = MDL_PITCHFROMMOMENTUM ;
if ( VoxelDefs [ i ] - > UseActorPitch ) smf . flags | = MDL_USEACTORPITCH ;
if ( VoxelDefs [ i ] - > UseActorRoll ) smf . flags | = MDL_USEACTORROLL ;
2017-11-25 11:11:57 +00:00
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 ;
2020-04-11 11:24:34 +00:00
while ( ( Lump = fileSystem . FindLump ( " MODELDEF " , & lastLump ) ) ! = - 1 )
2017-11-25 11:11:57 +00:00
{
2018-05-21 23:28:57 +00:00
ParseModelDefLump ( Lump ) ;
}
// create a hash table for quick access
2018-12-29 12:27:43 +00:00
SpriteModelHash . Resize ( SpriteModelFrames . Size ( ) ) ;
memset ( SpriteModelHash . Data ( ) , 0xff , SpriteModelFrames . Size ( ) * sizeof ( int ) ) ;
2018-05-21 23:28:57 +00:00
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 . xscale = smf . yscale = smf . zscale = 1.f ;
2020-04-26 23:16:17 +00:00
auto type = PClass : : FindClass ( sc . String ) ;
if ( ! type | | type - > Defaults = = nullptr )
2018-05-21 23:28:57 +00:00
{
sc . ScriptError ( " MODELDEF: Unknown actor type '%s' \n " , sc . String ) ;
}
2020-04-26 23:16:17 +00:00
smf . type = type ;
2021-02-09 13:48:43 +00:00
FScanner : : SavedPos scPos = sc . SavePos ( ) ;
sc . MustGetStringName ( " { " ) ;
while ( ! sc . CheckString ( " } " ) )
{
sc . MustGetString ( ) ;
if ( sc . Compare ( " model " ) )
{
sc . MustGetNumber ( ) ;
index = sc . Number ;
if ( index < 0 )
{
sc . ScriptError ( " Model index must be 0 or greater in %s " , type - > TypeName . GetChars ( ) ) ;
}
smf . modelsAmount = index + 1 ;
}
}
2021-03-06 10:28:13 +00:00
//Make sure modelsAmount is at least equal to MIN_MODELS(4) to ensure compatibility with old mods
if ( smf . modelsAmount < MIN_MODELS )
2021-02-09 13:48:43 +00:00
{
2021-03-06 10:28:13 +00:00
smf . modelsAmount = MIN_MODELS ;
2021-02-09 13:48:43 +00:00
}
2021-06-10 08:54:26 +00:00
const auto initArray = [ ] ( auto & array , const unsigned count , const auto value )
2021-02-09 13:48:43 +00:00
{
2021-06-10 08:54:26 +00:00
array . Alloc ( count ) ;
std : : fill ( array . begin ( ) , array . end ( ) , value ) ;
} ;
initArray ( smf . modelIDs , smf . modelsAmount , - 1 ) ;
initArray ( smf . skinIDs , smf . modelsAmount , FNullTextureID ( ) ) ;
initArray ( smf . surfaceskinIDs , smf . modelsAmount * MD3_MAX_SURFACES , FNullTextureID ( ) ) ;
initArray ( smf . modelframes , smf . modelsAmount , 0 ) ;
2021-02-09 13:48:43 +00:00
sc . RestorePos ( scPos ) ;
2018-05-21 23:28:57 +00:00
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 ;
2021-02-09 13:48:43 +00:00
if ( index < 0 )
{
sc . ScriptError ( " Model index must be 0 or greater in %s " , type - > TypeName . GetChars ( ) ) ;
}
else if ( index > = smf . modelsAmount )
2017-11-25 11:11:57 +00:00
{
2020-04-26 23:16:17 +00:00
sc . ScriptError ( " Too many models in %s " , 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 ;
}
2021-10-21 07:55:57 +00:00
else if ( sc . Compare ( " noperpixellighting " ) )
{
smf . flags | = MDL_NOPERPIXELLIGHTING ;
}
2022-03-07 20:45:52 +00:00
else if ( sc . Compare ( " scaleweaponfov " ) )
{
smf . flags | = MDL_SCALEWEAPONFOV ;
}
2018-05-21 23:28:57 +00:00
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 ;
2021-02-09 13:48:43 +00:00
if ( index < 0 | | index > = smf . modelsAmount )
2017-11-25 11:11:57 +00:00
{
2020-04-26 23:16:17 +00:00
sc . ScriptError ( " Too many models in %s " , 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 " ,
2020-04-26 23:16:17 +00:00
sc . String , type - > TypeName . GetChars ( ) ) ;
2018-05-21 23:28:57 +00:00
}
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 ;
2021-02-09 13:48:43 +00:00
if ( index < 0 | | index > = smf . modelsAmount )
2017-11-25 11:11:57 +00:00
{
2020-04-26 23:16:17 +00:00
sc . ScriptError ( " Too many models in %s " , 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
{
2020-04-26 23:16:17 +00:00
sc . ScriptError ( " Invalid MD3 Surface %d in %s " , MD3_MAX_SURFACES , type - > TypeName . GetChars ( ) ) ;
2017-11-25 11:11:57 +00:00
}
2018-05-21 23:28:57 +00:00
sc . MustGetString ( ) ;
FixPathSeperator ( sc . String ) ;
2021-02-09 13:48:43 +00:00
int ssIndex = surface + index * MD3_MAX_SURFACES ;
2018-05-21 23:28:57 +00:00
if ( sc . Compare ( " " ) )
2017-11-25 11:11:57 +00:00
{
2021-02-09 13:48:43 +00:00
smf . surfaceskinIDs [ ssIndex ] = 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
{
2021-02-09 13:48:43 +00:00
smf . surfaceskinIDs [ ssIndex ] = LoadSkin ( path . GetChars ( ) , sc . String ) ;
if ( ! smf . surfaceskinIDs [ ssIndex ] . 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 " ,
2020-04-26 23:16:17 +00:00
sc . String , 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
{
2020-04-26 23:16:17 +00:00
sc . ScriptError ( " Unknown sprite %s in model definition for %s " , sc . String , type - > TypeName . GetChars ( ) ) ;
2018-05-21 23:28:57 +00:00
}
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 ;
2021-02-09 13:48:43 +00:00
if ( index < 0 | | index > = smf . modelsAmount )
2018-05-21 23:28:57 +00:00
{
2020-04-26 23:16:17 +00:00
sc . ScriptError ( " Too many models in %s " , type - > TypeName . GetChars ( ) ) ;
2018-05-21 23:28:57 +00:00
}
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 ) ;
2020-04-26 23:16:17 +00:00
if ( smf . modelframes [ index ] = = - 1 ) sc . ScriptError ( " Unknown frame '%s' in %s " , sc . String , 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 ) ;
2020-04-26 23:16:17 +00:00
GetDefaultByType ( type ) - > hasmodel = true ;
2018-05-21 23:28:57 +00:00
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
2020-04-11 11:24:34 +00:00
int includelump = fileSystem . CheckNumForFullName ( sc . String , true ) ;
2018-05-21 23:28:57 +00:00
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
{
2021-02-20 10:35:14 +00:00
if ( player = = nullptr | | player - > psprites = = nullptr )
2017-11-25 11:11:57 +00:00
return false ;
2021-02-20 10:35:14 +00:00
// [MK] check that at least one psprite uses models
for ( DPSprite * psp = player - > psprites ; psp ! = nullptr & & psp - > GetID ( ) < PSP_TARGETCENTER ; psp = psp - > GetNext ( ) )
{
2021-08-13 12:25:51 +00:00
FSpriteModelFrame * smf = psp - > Caller ! = nullptr ? FindModelFrame ( psp - > Caller - > GetClass ( ) , psp - > GetSprite ( ) , psp - > GetFrame ( ) , false ) : nullptr ;
2021-02-20 10:35:14 +00:00
if ( smf ! = nullptr ) return true ;
}
return false ;
2017-11-25 11:11:57 +00:00
}