ncWeapon: streamline muzzleflash assignment and make it fireInfo oriented.

This commit is contained in:
Marco Cawthorne 2025-02-07 19:21:17 -08:00
parent d0e90cd07b
commit 475000a1b8
7 changed files with 103 additions and 69 deletions

View file

@ -67,7 +67,7 @@ Spawns the specified entity class name at the desired position. If the classname
#### sendInput(targetEntity, inputName, dataString, targetActivator)
Sends an input event to an NSEntity and with a specific activator. While that can be __NULL__, this, or world most of the time - some inputs very much depend on the activator being valid within their respective contexts.
Sends an input event to an ncEntity and with a specific activator. While that can be __NULL__, this, or world most of the time - some inputs very much depend on the activator being valid within their respective contexts.
### Map-specific entities

View file

@ -1,6 +1,6 @@
# Shaders {#shaders}
Shaders are referring to GPU-oriented pieces of a program, performing shading and rendering related functions instead of letting the engine handle it.
Shaders are referring to **GPU**-oriented pieces of a program, performing shading and rendering related functions instead of letting the engine handle it.
In [FTE](https://www.fteqw.org/) you can specify a custom GLSL or HLSL shader using the [program](@ref program) command inside a [Material](@ref materials).
@ -60,3 +60,61 @@ At some point in the `main` function, we do have to set `gl_Position` and `gl_Fr
### Can I keep my vertex and fragment/pixel shader as separate files from one another?
You can not have separate files handle vertex/fragment programs.
## Post-processing
While Nuclide sets up the viewport by itself, it still uses the regular compositing path provided by the engine. So like in most FTE games you can set the console variable `r_postprocshader` to a material of your choice.
For example, if you define a material called `postproc_test` with this text inside `<gamedir>/scripts/postproc.shader`:
```
postproc_test
{
program postproc_dither
{
map $sourcecolour
nodepthtest
}
}
```
... and the shader it is referencing located at `<gamedir>/glsl/postproc_dither.glsl`:
```
!!samps screen=0
!!ver 120
#include "sys/defs.h"
varying vec2 texcoord;
#ifdef VERTEX_SHADER
void main()
{
texcoord = v_texcoord.xy;
texcoord.y = 1.0 - texcoord.y;
gl_Position = ftetransform();
}
#endif
#ifdef FRAGMENT_SHADER
uniform vec2 e_sourcesize;
void main(void)
{
vec2 pos = vec2(gl_FragCoord.x, gl_FragCoord.y);
vec3 col = texture2D(s_screen, texcoord).rgb;
// limit to 16 bit rendering, alternatively remove this
// and do whatever else you'd like here
col.rgb = floor(col.rgb * vec3(32,64,32))/vec3(32,64,32);
gl_FragColor = vec4(col, 1.0);
}
#endif
```
Followed by setting the post-processing material via `set r_postprocshader postproc_test` in the console (**SHIFT+ESC**), you will now be processing all framebuffer output through the `postproc_test` material and the `postproc_dither` shader.
**Of course, a Nuclide game might decide to override the ncView class in such a way to prevent FTE's own post-processing system from being used.**
@note Use the console command `vid_reload` to reload your material/shader changes while in game! This will save you a lot of time iterating upon them.

View file

@ -4,9 +4,9 @@
### Nuclide APIs
Every NSEntity class usually comes with a `NSEntity::Save()` and `NSEntity::Restore()` override. These are also used for [level transitions](@ref transitions).
Every ncEntity class usually comes with a `ncEntity::Save()` and `ncEntity::Restore()` override. These are also used for [level transitions](@ref transitions).
When a save-game has been restored, the overridable method `NSEntity::RestoreComplete()` will be called. At that point AI or some time based triggers get the chance to re-align themselves with the reloaded environment (if necessary).
When a save-game has been restored, the overridable method `ncEntity::RestoreComplete()` will be called. At that point AI or some time based triggers get the chance to re-align themselves with the reloaded environment (if necessary).
### LVC Files
The engine stores cached copies for the current (non-saved) game into the `<gamedir>/saves` directory, in the form of **lvc** files. Each **lvc** file represents a map, containing a copy of its **entity lump**.

View file

@ -8,4 +8,4 @@ Level designers create level changes by utilising the trigger_changelevel entity
In addition, it is recommended that they place a trigger_transition with the same name as the info_landmark. Otherwise you risk carrying over too much entity data. Please read the entity documentation for more details.
When a level transition is completed, Nuclide will call the overridable method `NSEntity::TransitionComplete()` on every entity, to give it a chance to re-align itself with the new environment.
When a level transition is completed, Nuclide will call the overridable method `ncEntity::TransitionComplete()` on every entity, to give it a chance to re-align itself with the new environment.

View file

@ -482,7 +482,7 @@ ncEntity::RestoreAngles(void)
vector spawnAngles = g_vec_null;
string spawnAngleString = GetSpawnString("angles");
if (spawnAngleString == "") {
if (!STRING_SET(spawnAngleString)) {
spawnAngles[1] = GetSpawnFloat("angle");
} else {
spawnAngles = stov(spawnAngleString);

View file

@ -188,7 +188,6 @@ public:
virtual bool TestFireAbility(string);
#ifdef CLIENT
virtual void UpdateViewmodel(void);
virtual void ClientFX(bool);
virtual void PredictPreFrame(void);
virtual void PredictPostFrame(void);
@ -323,8 +322,6 @@ private:
string m_secondaryFireInfo;
int m_primaryAmmoType;
int m_secondaryAmmoType;
float m_muzzleModelIndex;
float m_altMuzzleModelIndex;
float m_flPrimedFuse;
float m_flTriggerDelay;
float m_flZoomFOV;

View file

@ -25,7 +25,6 @@ ncWeapon::ncWeapon(void)
m_strAmmoType = __NULL__;
m_bAmmoRequired = false;
m_strFlashShader = __NULL__;
m_strFlashModel = __NULL__;
m_vecFlashColor = g_vec_null;
m_flFlashRadius = 0.0f;
m_strDropItemDef = __NULL__;
@ -224,15 +223,43 @@ ncWeapon::UpdateFireInfoCache(void)
} else {
m_fiAmmoPerShot = 1i;
}
#ifdef CLIENT
if (pSeat != __NULL__) {
ncRenderableEntity viewModel = (ncRenderableEntity)pSeat->m_eViewModel;
ncRenderableEntity viewModel2 = (ncRenderableEntity)pSeat->m_eViewModelL;
if (viewModel && viewModel.classname == "vm") {
string viewModelPath2 = GetSubDefString(m_primaryFireInfo, "model_view2");
string viewModelPath3 = GetSubDefString(m_primaryFireInfo, "model_view3");
string viewModelPath4 = GetSubDefString(m_primaryFireInfo, "model_view4");
viewModel.modelindex = m_viewModel;
viewModel.modelindex2 = getmodelindex(viewModelPath2);
viewModel.modelindex3 = getmodelindex(viewModelPath3);
viewModel.modelindex4 = getmodelindex(viewModelPath4);
string modelFlash = GetSubDefString(m_strLastFireInfo, "model_flash");
viewModel2.m_iMuzzleModel = viewModel.m_iMuzzleModel = (int)getmodelindex(modelFlash);
if (viewModel) {
viewModel._UpdateBoneCount();
}
if (viewModel2) {
viewModel2._UpdateBoneCount();
}
}
}
#endif
}
void
ncWeapon::SwitchFireInfo(string newInfo)
{
/* nothing changed. */
if (m_strLastFireInfo == newInfo) {
return;
}
//if (m_strLastFireInfo == newInfo) {
// return;
//}
m_strLastFireInfo = newInfo;
UpdateFireInfoCache();
@ -288,9 +315,6 @@ ncWeapon::SpawnKey(string keyName, string setValue)
case "mtr_flashShader":
m_strFlashShader = ReadString(setValue);
break;
case "model_flash":
m_strFlashModel = ReadString(setValue);
break;
case "flashColor":
m_vecFlashColor = ReadVector(setValue);
break;
@ -326,7 +350,6 @@ ncWeapon::Save(float handle)
SaveInt(handle, "m_iClipStartSize", m_iClipStartSize);
SaveInt(handle, "m_iMode", m_iMode);
SaveString(handle, "m_strFlashShader", m_strFlashShader);
SaveString(handle, "m_strFlashModel", m_strFlashModel);
SaveVector(handle, "m_vecFlashColor", m_vecFlashColor);
SaveFloat(handle, "m_flFlashRadius", m_flFlashRadius);
SaveString(handle, "m_strDropItemDef", m_strDropItemDef);
@ -362,9 +385,6 @@ ncWeapon::Restore(string strKey, string strValue)
case "m_strFlashShader":
m_strFlashShader = ReadString(strValue);
break;
case "m_strFlashModel":
m_strFlashModel = ReadString(strValue);
break;
case "m_vecFlashColor":
m_vecFlashColor = ReadVector(strValue);
break;
@ -606,18 +626,18 @@ ncWeapon::ReceiveEntity(float flNew, float flChanged)
READENTITY_BYTE(m_bFiring, WEAPONFL_CHANGED_STATE)
READENTITY_FLOAT(m_flOverheating, WEAPONFL_CHANGED_STATE)
/* weapon view model update */
ncActor ourOwner = (ncActor)findfloat(world, ::entnum, owner_entnum);
if (ourOwner.m_activeWeapon == this) {
UpdateViewmodel();
}
/* this has to be done first, any code below may query decl */
if (flChanged & WEAPONFL_CHANGED_MODELINDEX) {
classname = EntityDef_NameFromNetID(entityDefID);
declclass = classname;
}
ncActor ourOwner = (ncActor)findfloat(world, ::entnum, owner_entnum);
if (ourOwner.m_activeWeapon == this) {
SwitchFireInfo(m_primaryFireInfo);
}
if (flChanged & WEAPONFL_CHANGED_BODY)
_UpdateGeomset();
@ -736,7 +756,6 @@ void
ncWeapon::_CacheWeaponDefVariables(void)
{
string firstType, secondType;
string muzzleModel;
string ammoRequired;
m_strWeaponTitle = GetDefString("inv_name");
@ -767,8 +786,6 @@ ncWeapon::_CacheWeaponDefVariables(void)
ammoRequired = GetSubDefString(m_primaryFireInfo, "ammoRequired");
m_bAmmoRequired = stof(ammoRequired);
m_primaryAmmoType = ammoNumForName(firstType);
muzzleModel = GetSubDefString(m_primaryFireInfo, "model_flash");
m_muzzleModelIndex = getmodelindex(muzzleModel, false);
m_flPrimedFuse = GetDefFloat("primed_fuse");
m_flZoomFOV = GetDefFloat("zoomFov") / 90;
m_bPowerAmmo = GetDefBool("powerAmmo");
@ -795,14 +812,11 @@ ncWeapon::_CacheWeaponDefVariables(void)
secondType = GetSubDefString(m_secondaryFireInfo, "ammoType");
m_secondaryAmmoType = ammoNumForName(secondType);
muzzleModel = GetSubDefString(m_secondaryFireInfo, "model_flash");
m_altMuzzleModelIndex = getmodelindex(muzzleModel, false);
}
void
ncWeapon::_SwitchedToCallback(void)
{
SwitchFireInfo(m_primaryFireInfo);
SetAttackNext(0.0f);
SetIdleNext(0.0f);
Draw();
@ -856,45 +870,10 @@ ncWeapon::IsEmpty(void)
return (false);
}
#ifdef CLIENT
.float vw_index;
void
ncWeapon::UpdateViewmodel(void)
{
if (pSeat != __NULL__) {
ncRenderableEntity viewModel = (ncRenderableEntity)pSeat->m_eViewModel;
ncRenderableEntity viewModel2 = (ncRenderableEntity)pSeat->m_eViewModelL;
if (viewModel && viewModel.classname == "vm") {
string viewModelPath2 = GetSubDefString(m_primaryFireInfo, "model_view2");
string viewModelPath3 = GetSubDefString(m_primaryFireInfo, "model_view3");
string viewModelPath4 = GetSubDefString(m_primaryFireInfo, "model_view4");
string modelFlash = GetDefString("model_flash");
viewModel.modelindex = m_viewModel;
viewModel.modelindex2 = getmodelindex(viewModelPath2);
viewModel.modelindex3 = getmodelindex(viewModelPath3);
viewModel.modelindex4 = getmodelindex(viewModelPath4);
viewModel2.m_iMuzzleModel = viewModel.m_iMuzzleModel = (int)getmodelindex(modelFlash);
if (viewModel) {
viewModel._UpdateBoneCount();
}
if (viewModel2) {
viewModel2._UpdateBoneCount();
}
}
}
}
#endif
void
ncWeapon::Draw(void)
{
#ifdef CLIENT
UpdateViewmodel();
#endif
SwitchFireInfo(m_primaryFireInfo);
float drawAnimation = -1;
float drawTime = 0.0f;