Merged Quake 1 lightstyle support from Iced-Hellfire

This commit is contained in:
Robert Beckebans 2021-02-20 16:24:45 +01:00
parent b85db1e865
commit 1f2f6896e1
10 changed files with 379 additions and 21 deletions

View file

@ -1692,7 +1692,7 @@ int idEntity::GetModelDefHandle()
idEntity::UpdateRenderEntity
================
*/
bool idEntity::UpdateRenderEntity( renderEntity_s* renderEntity, const renderView_t* renderView )
bool idEntity::UpdateRenderEntity( renderEntity_t* renderEntity, const renderView_t* renderView )
{
if( gameLocal.inCinematic && gameLocal.skipCinematic )
{
@ -1721,7 +1721,7 @@ idEntity::ModelCallback
NOTE: may not change the game state whatsoever!
================
*/
bool idEntity::ModelCallback( renderEntity_s* renderEntity, const renderView_t* renderView )
bool idEntity::ModelCallback( renderEntity_t* renderEntity, const renderView_t* renderView )
{
idEntity* ent;

View file

@ -302,8 +302,8 @@ public:
// animation
virtual bool UpdateAnimationControllers();
bool UpdateRenderEntity( renderEntity_s* renderEntity, const renderView_t* renderView );
static bool ModelCallback( renderEntity_s* renderEntity, const renderView_t* renderView );
bool UpdateRenderEntity( renderEntity_t* renderEntity, const renderView_t* renderView );
static bool ModelCallback( renderEntity_t* renderEntity, const renderView_t* renderView );
virtual idAnimator* GetAnimator(); // returns animator object used by this entity
// sound
@ -571,6 +571,14 @@ private:
void UpdatePVSAreas();
// events
public:
// jmarshall
idVec3 GetOrigin( void );
float DistanceTo( idEntity* ent );
float DistanceTo( const idVec3& pos ) const;
idStr GetNextKey( const char* prefix, const char* lastMatch );
// jmarshall end
void Event_GetName();
void Event_SetName( const char* name );
void Event_FindTargets();
@ -641,6 +649,16 @@ private:
void Event_GuiNamedEvent( int guiNum, const char* event );
};
ID_INLINE float idEntity::DistanceTo( idEntity* ent )
{
return DistanceTo( ent->GetPhysics()->GetOrigin() );
}
ID_INLINE float idEntity::DistanceTo( const idVec3& pos ) const
{
return ( pos - GetPhysics()->GetOrigin() ).LengthFast();
}
/*
===============================================================================

View file

@ -2590,6 +2590,22 @@ void idGameLocal::RunEntityThink( idEntity& ent, idUserCmdMgr& userCmdMgr )
idCVar g_recordTrace( "g_recordTrace", "0", CVAR_BOOL, "" );
// jmarshall
/*
================
idGameLocal::RunSharedThink
================
*/
void idGameLocal::RunSharedThink( void )
{
idEntity* ent;
for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() )
{
ent->SharedThink();
}
}
// jmarshall end
/*
================
idGameLocal::RunFrame
@ -2766,7 +2782,9 @@ void idGameLocal::RunFrame( idUserCmdMgr& cmdMgr, gameReturn_t& ret )
}
RunTimeGroup2( cmdMgr );
// jmarshall
RunSharedThink();
// jmarshall end
// Run catch-up for any client projectiles.
// This is done after the main think so that all projectiles will be up-to-date
// when snapshots are created.

View file

@ -718,6 +718,8 @@ private:
void ShowTargets();
void RunDebugInfo();
void RunSharedThink();
void InitScriptForMap();
void SetScriptFPS( const float com_engineHz );
void SpawnPlayer( int clientNum );

View file

@ -166,6 +166,7 @@ void idGameEdit::ParseSpawnArgsToRenderLight( const idDict* args, renderLight_t*
args->GetBool( "parallel", "0", renderLight->parallel );
args->GetString( "texture", "lights/squarelight1", &texture );
// allow this to be NULL
renderLight->shader = declManager->FindMaterial( texture, false );
}
@ -177,6 +178,12 @@ idLight::UpdateChangeableSpawnArgs
*/
void idLight::UpdateChangeableSpawnArgs( const idDict* source )
{
// jmarshall
lightStyleFrameTime = spawnArgs.GetInt( "ls_frametime", "100" );
lightStyle = spawnArgs.GetInt( "style", -1 );
lightStyleState.Reset();
// jmarshall end
idEntity::UpdateChangeableSpawnArgs( source );
@ -336,6 +343,9 @@ void idLight::Spawn()
// do the parsing the same way dmap and the editor do
gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &renderLight );
// jmarshall: Store the original light radius for the light style.
lightStyleBase = renderLight.lightRadius;
// jmarshall end
// we need the origin and axis relative to the physics origin/axis
localLightOrigin = ( renderLight.origin - GetPhysics()->GetOrigin() ) * GetPhysics()->GetAxis().Transpose();
@ -397,6 +407,30 @@ void idLight::Spawn()
spawnArgs.GetBool( "break", "0", breakOnTrigger );
spawnArgs.GetInt( "count", "1", count );
// jmarshall
lightStyleFrameTime = spawnArgs.GetInt( "ls_frametime", "100" );
lightStyle = spawnArgs.GetInt( "style", -1 );
int numStyles = spawnArgs.GetInt( "num_styles", "0" );
if( numStyles > 0 )
{
for( int i = 0; i < numStyles; i++ )
{
idStr style = spawnArgs.GetString( va( "light_style%d", i ) );
light_styles.Append( style );
}
}
else
{
// RB: it's not defined in entityDef light so use predefined Quake 1 table
for( int i = 0; i < 12; i++ )
{
idStr style = spawnArgs.GetString( va( "light_style%d", i ), predef_lightstyles[ i ] );
light_styles.Append( style );
}
}
// jmarshall end
triggercount = 0;
fadeFrom.Set( 1, 1, 1, 1 );
@ -886,6 +920,104 @@ void idLight::Think()
Present();
}
/*
================
idLight::SharedThink
================
*/
// jmarshall
void idLight::SharedThink()
{
float lightval;
int stringlength;
float offset;
int offsetwhole;
int otime;
int lastch, nextch;
if( lightStyle == -1 )
{
return;
}
if( lightStyle > light_styles.Num() )
{
//gameLocal.Error( "Light style out of range\n" );
return;
}
idStr dl_stylestring = light_styles[lightStyle];
otime = gameLocal.time - lightStyleState.dl_time;
stringlength = dl_stylestring.Length();
// it's been a long time since you were updated, lets assume a reset
if( otime > 2 * lightStyleFrameTime )
{
otime = 0;
lightStyleState.dl_frame = lightStyleState.dl_oldframe = 0;
lightStyleState.dl_backlerp = 0;
}
lightStyleState.dl_time = gameLocal.time;
offset = ( ( float )otime ) / lightStyleFrameTime;
offsetwhole = ( int )offset;
lightStyleState.dl_backlerp += offset;
if( lightStyleState.dl_backlerp > 1 ) // we're moving on to the next frame
{
lightStyleState.dl_oldframe = lightStyleState.dl_oldframe + ( int )lightStyleState.dl_backlerp;
lightStyleState.dl_frame = lightStyleState.dl_oldframe + 1;
if( lightStyleState.dl_oldframe >= stringlength )
{
lightStyleState.dl_oldframe = ( lightStyleState.dl_oldframe ) % stringlength;
//if (cent->dl_oldframe < 3 && cent->dl_sound) { // < 3 so if an alarm comes back into the pvs it will only start a sound if it's going to be closely synced with the light, otherwise wait till the next cycle
// engine->S_StartSound(NULL, cent->currentState.number, CHAN_AUTO, cgs.gameSounds[cent->dl_sound]);
//}
}
if( lightStyleState.dl_frame >= stringlength )
{
lightStyleState.dl_frame = ( lightStyleState.dl_frame ) % stringlength;
}
lightStyleState.dl_backlerp = lightStyleState.dl_backlerp - ( int )lightStyleState.dl_backlerp;
}
lastch = dl_stylestring[lightStyleState.dl_oldframe] - 'a';
nextch = dl_stylestring[lightStyleState.dl_frame] - 'a';
lightval = ( lastch * ( 1.0 - lightStyleState.dl_backlerp ) ) + ( nextch * lightStyleState.dl_backlerp );
// ydnar: dlight values go from 0-1.5ish
#if 0
lightval = ( lightval * ( 1000.0f / 24.0f ) ) - 200.0f; // they want 'm' as the "middle" value as 300
lightval = max( 0.0f, lightval );
lightval = min( 1000.0f, lightval );
#else
lightval *= 0.071429f;
lightval = Max( 0.0f, lightval );
lightval = Min( 20.0f, lightval );
#endif
renderLight.lightRadius.x = lightval * lightStyleBase.x;
renderLight.lightRadius.y = lightval * lightStyleBase.y;
renderLight.lightRadius.z = lightval * lightStyleBase.z;
if( !common->IsClient() )
{
BecomeActive( TH_THINK );
}
PresentLightDefChange();
}
// jmarshall end
/*
================
idLight::ClientThink

View file

@ -41,6 +41,37 @@ extern const idEventDef EV_Light_GetLightParm;
extern const idEventDef EV_Light_SetLightParm;
extern const idEventDef EV_Light_SetLightParms;
// jmarshall
struct rvmLightStyleState_t
{
rvmLightStyleState_t();
int dl_frame;
float dl_framef;
int dl_oldframe;
int dl_time;
float dl_backlerp;
void Reset();
};
ID_INLINE rvmLightStyleState_t::rvmLightStyleState_t()
{
Reset();
}
ID_INLINE void rvmLightStyleState_t::Reset()
{
dl_frame = 0;
dl_framef = 0;
dl_oldframe = 0;
dl_time = 0;
dl_backlerp = 0;
}
// jmarshall end
class idLight : public idEntity
{
public:
@ -60,7 +91,9 @@ public:
virtual void FreeLightDef();
virtual bool GetPhysicsToSoundTransform( idVec3& origin, idMat3& axis );
void Present();
// jmarshall
virtual void SharedThink();
// jmarshall end
void SaveState( idDict* args );
virtual void SetColor( float red, float green, float blue );
virtual void SetColor( const idVec4& color );
@ -122,6 +155,11 @@ private:
bool breakOnTrigger;
int count;
// jmarshall
int lightStyle;
int lightStyleFrameTime;
idVec3 lightStyleBase;
// jmarshall end
int triggercount;
idEntity* lightParent;
idVec4 fadeFrom;
@ -148,6 +186,11 @@ private:
void Event_SetSoundHandles();
void Event_FadeOut( float time );
void Event_FadeIn( float time );
// jmarshall
idList<idStr> light_styles;
rvmLightStyleState_t lightStyleState;
// jmarshall end
};
#endif /* !__GAME_LIGHT_H__ */

View file

@ -246,6 +246,7 @@ public:
const char* GetSuperclass() const;
void FindUninitializedMemory();
virtual void SharedThink() { }
void Save( idSaveGame* savefile ) const {};
void Restore( idRestoreGame* savefile ) {};

View file

@ -367,6 +367,40 @@ typedef enum
class idSoundEmitter;
// RB: predefined Quake 1 light styles
static char* predef_lightstyles[] =
{
"m",
"mmnmmommommnonmmonqnmmo",
"abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba",
"mmmmmaaaaammmmmaaaaaabcdefgabcdefg",
"mamamamamama",
"jklmnopqrstuvwxyzyxwvutsrqponmlkj",
"nmonqnmomnmomomno",
"mmmaaaabcdefgmmmmaaaammmaamm",
"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa",
"aaaaaaaazzzzzzzz",
"mmamammmmammamamaaamammma",
"abcdefghijklmnopqrrqponmlkjihgfedcba"
};
static char* predef_lightstylesinfo[] =
{
"Normal",
"Flicker A",
"Slow Strong Pulse",
"Candle A",
"Fast Strobe",
"Gentle Pulse",
"Flicker B",
"Candle B",
"Candle C",
"Slow Strobe",
"Fluorescent Flicker",
"Slow Pulse (no black)"
};
// RB end
class idMaterial : public idDecl
{
public:

View file

@ -4,6 +4,7 @@
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2015 Daniel Gibson
Copyright (C) 2020-2021 Robert Beckebans
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
@ -68,9 +69,9 @@ void LightInfo::Defaults()
lightRadius.Zero();
castShadows = true;
castSpecular = true;
castDiffuse = true;
hasCenter = false;
isParallel = false;
lightStyle = -1;
}
@ -95,7 +96,6 @@ void LightInfo::DefaultProjected()
void LightInfo::FromDict( const idDict* e )
{
lightRadius.Zero();
lightTarget.Zero();
lightRight.Zero();
@ -106,7 +106,6 @@ void LightInfo::FromDict( const idDict* e )
castShadows = !e->GetBool( "noshadows" );
castSpecular = !e->GetBool( "nospecular" );
castDiffuse = !e->GetBool( "nodiffuse" );
fallOff = e->GetFloat( "falloff" );
strTexture = e->GetString( "texture" );
@ -176,6 +175,9 @@ void LightInfo::FromDict( const idDict* e )
hasCenter = true;
}
}
// RB: Quake 1 light styles
lightStyle = e->GetInt( "style", -1 );
}
// the returned idDict is supposed to be used by idGameEdit::EntityChangeSpawnArgs()
@ -190,7 +192,6 @@ void LightInfo::ToDict( idDict* e )
e->Set( "noshadows", ( !castShadows ) ? "1" : "0" );
e->Set( "nospecular", ( !castSpecular ) ? "1" : "0" );
e->Set( "nodiffuse", ( !castDiffuse ) ? "1" : "0" );
e->SetFloat( "falloff", fallOff );
@ -265,6 +266,16 @@ void LightInfo::ToDict( idDict* e )
e->Set( "light_center", DELETE_VAL );
e->Set( "parallel", DELETE_VAL );
}
// RB: Quake 1 light styles
if( lightStyle != -1 )
{
e->SetInt( "style", lightStyle );
}
else
{
e->Set( "style", DELETE_VAL );
}
}
LightInfo::LightInfo()
@ -303,10 +314,14 @@ void LightEditor::Init( const idDict* dict, idEntity* light )
LoadLightTextures();
}
if( styleNames.Num() == 0 )
{
LoadLightStyles();
}
if( dict )
{
original.FromDict( dict );
//current = original;
cur.FromDict( dict );
const char* name = dict->GetString( "name", NULL );
@ -319,6 +334,7 @@ void LightEditor::Init( const idDict* dict, idEntity* light )
{
idassert( 0 && "LightEditor::Init(): Given entity has no 'name' property?!" );
entityName = ""; // TODO: generate name or handle gracefully or something?
title.Format( "Light Editor: <unnamed> light" );
}
currentTextureIndex = 0;
@ -336,6 +352,12 @@ void LightEditor::Init( const idDict* dict, idEntity* light )
}
}
}
// RB: light styles
if( original.lightStyle >= 0 )
{
currentStyleIndex = original.lightStyle + 1;
}
}
this->lightEntity = light;
}
@ -348,6 +370,8 @@ void LightEditor::Reset()
lightEntity = NULL;
currentTextureIndex = 0;
currentTexture = NULL;
currentStyleIndex = 0;
}
namespace
@ -365,37 +389,45 @@ public:
void LightEditor::LoadLightTextures()
{
textureNames.Clear();
int count = declManager->GetNumDecls( DECL_MATERIAL );
const idMaterial* mat;
for( int i = 0; i < count; i++ )
{
mat = declManager->MaterialByIndex( i, false );
const idMaterial* mat = declManager->MaterialByIndex( i, false );
idStr str = mat->GetName();
str.ToLower(); // FIXME: why? (this is from old doom3 code)
if( str.Icmpn( "lights/", strlen( "lights/" ) ) == 0 || str.Icmpn( "fogs/", strlen( "fogs/" ) ) == 0 )
{
textureNames.Append( str );
}
}
textureNames.SortWithTemplate( idSort_textureNames() );
}
// static
bool LightEditor::TextureItemsGetter( void* data, int idx, const char** out_text )
bool LightEditor::TextureItemsGetter( void* data, int idx, const char** outText )
{
LightEditor* self = static_cast<LightEditor*>( data );
if( idx == 0 )
{
*out_text = "<No Texture>";
*outText = "<No Texture>";
return true;
}
--idx; // as index 0 has special purpose, the "real" index is one less
// as index 0 has special purpose, the "real" index is one less
--idx;
if( idx < 0 || idx >= self->textureNames.Num() )
{
*out_text = "<Invalid Index!>";
*outText = "<Invalid Index!>";
return false;
}
*out_text = self->textureNames[idx].c_str();
*outText = self->textureNames[idx].c_str();
return true;
}
@ -403,6 +435,7 @@ bool LightEditor::TextureItemsGetter( void* data, int idx, const char** out_text
void LightEditor::LoadCurrentTexture()
{
currentTexture = NULL;
if( currentTextureIndex > 0 && cur.strTexture.Length() > 0 )
{
const idMaterial* mat = declManager->FindMaterial( cur.strTexture, false );
@ -413,12 +446,67 @@ void LightEditor::LoadCurrentTexture()
}
}
void LightEditor::LoadLightStyles()
{
styleNames.Clear();
const idDeclEntityDef* decl = static_cast<const idDeclEntityDef*>( declManager->FindType( DECL_ENTITYDEF, "light", false ) );
if( decl == NULL )
{
return;
}
int numStyles = decl->dict.GetInt( "num_styles", "0" );
if( numStyles > 0 )
{
for( int i = 0; i < numStyles; i++ )
{
idStr style = decl->dict.GetString( va( "light_style%d", i ) );
styleNames.Append( style );
}
}
else
{
// RB: it's not defined in entityDef light so use predefined Quake 1 table
for( int i = 0; i < 12; i++ )
{
idStr style( predef_lightstylesinfo[ i ] );
styleNames.Append( style );
}
}
}
// static
bool LightEditor::StyleItemsGetter( void* data, int idx, const char** outText )
{
LightEditor* self = static_cast<LightEditor*>( data );
if( idx == 0 )
{
*outText = "<No Lightstyle>";
return true;
}
// as index 0 has special purpose, the "real" index is one less
--idx;
if( idx < 0 || idx >= self->styleNames.Num() )
{
*outText = "<Invalid Index!>";
return false;
}
*outText = self->styleNames[idx].c_str();
return true;
}
void LightEditor::TempApplyChanges()
{
if( lightEntity != NULL )
{
idDict d;
cur.ToDict( &d );
gameEdit->EntityChangeSpawnArgs( lightEntity, &d );
gameEdit->EntityUpdateChangeableSpawnArgs( lightEntity, NULL );
}
@ -448,6 +536,7 @@ void LightEditor::SaveChanges()
gameEdit->MapAddEntity( &d );
#endif // 0
}
gameEdit->MapSave();
}
@ -457,6 +546,7 @@ void LightEditor::CancelChanges()
{
idDict d;
original.ToDict( &d );
gameEdit->EntityChangeSpawnArgs( lightEntity, &d );
gameEdit->EntityUpdateChangeableSpawnArgs( lightEntity, NULL );
}
@ -476,8 +566,7 @@ void LightEditor::DrawWindow()
bool changes = false;
changes |= ImGui::Checkbox( "Cast Shadows", &cur.castShadows );
ImGui::SameLine();
changes |= ImGui::Checkbox( "Cast Diffuse", &cur.castDiffuse );
//ImGui::SameLine();
changes |= ImGui::Checkbox( "Cast Specular", &cur.castSpecular );
ImGui::Spacing();
@ -573,9 +662,20 @@ void LightEditor::DrawWindow()
ImGui::Unindent();
if( ImGui::Combo( "Style", &currentStyleIndex, StyleItemsGetter, this, styleNames.Num() + 1 ) )
{
changes = true;
// -1 because 0 is "<No Lightstyle>"
cur.lightStyle = ( currentStyleIndex > 0 ) ? currentStyleIndex - 1 : -1;
}
ImGui::Spacing();
if( ImGui::Combo( "Texture", &currentTextureIndex, TextureItemsGetter, this, textureNames.Num() + 1 ) )
{
changes = true;
// -1 because 0 is "<No Texture>"
cur.strTexture = ( currentTextureIndex > 0 ) ? textureNames[currentTextureIndex - 1] : "";
LoadCurrentTexture();
@ -592,6 +692,8 @@ void LightEditor::DrawWindow()
// then only the changed attribute (e.g. color) would be set to all lights,
// but they'd keep their other individual properties (eg radius)
ImGui::Spacing();
if( ImGui::Button( "Save to .map" ) )
{
SaveChanges();

View file

@ -70,9 +70,9 @@ public:
idVec3 lightRadius;
bool castShadows;
bool castSpecular;
bool castDiffuse;
bool hasCenter;
bool isParallel;
int lightStyle;
LightInfo();
@ -85,6 +85,7 @@ public:
class LightEditor
{
private:
idStr title;
idStr entityName;
LightInfo original;
@ -96,6 +97,13 @@ class LightEditor
int currentTextureIndex;
idImage* currentTexture;
// RB: light style support
idList<idStr> styleNames;
int currentStyleIndex;
void LoadLightStyles();
static bool StyleItemsGetter( void* data, int idx, const char** out_text );
void Init( const idDict* dict, idEntity* light );
void Reset();