News:

One Minute Game Review by The Happy Friar: https://ugetube.com/@OneMinteGameReviews
Also on Rumble: https://rumble.com/c/c-1115371

idTech 4 (aka Doom 3 tech) Discord Server! https://discord.gg/9wtCGHa

Main Menu

Tutorial: FX System

Started by deadite4, July 28, 2014, 10:01:49 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

deadite4

The FX system in id tech 4 is a great way to create an effect that is essentially a multi-layered effect using any combination of media types you'd like.  While you can typically create the same type of effect using various scripting methods, it's often easier and more efficient to create an FX declaration.

The types of actions you can call within a single FX decl. are lights, particles, models, entities, sounds, decals, and projectiles.

You can set these in DEF entries as well as call them directly in scripts.  An FX declaration is contained within a .fx file placed in your /base/fx directory.

Keywords include(table from iddevnet.com):



























delay <time>How long (in seconds) after starting the effect before this action happens
shake <time> <amplitude> <distance> <falloff> <impulse>Shake the player around a bit.
ignoreMasterDon't shake the entity this effect is attached to
random <min>, <max>A random time added to the delay.
fire <sibling>Causes the sibling action to happen when this action does. This is a way of synching two random actions.
duration <time>How long the action lasts before it is killed or restarted
restart <bool>Set to 1 if the action starts again after the 'duration' has run out
fadeIn <time>Fade in the RGB of the light or model over <time> seconds
fadeOut <time>Fade out the light/model. Ignored if fadeIn is set, you can use 2 separate actions (tied together with uselight) if you want a light to fade in and out.
offset <x>, <y>, <z>Offset from the origin of the entity (or bind point) this action is located at
axis <x>, <y>, <z>Axis of the model, mutually exclusive with angle
angle <pitch>, <yaw>, <roll>Alternate way of setting the axis of the model
light <material>, <red>, <green>, <blue>, <radius>Create a light
noshadowsThe light in this effect doesn't cast shadows
attachlight <light>Attach to external light (a light not defined in the effect) for fading.
attachentity <entity>Attach to an external entity.
launch <entity>Launches a projectile.
uselight <sibling>Modify the light values in a sibling action. Can be used to fade out a light that faded in earlier.
useModel <model>Modify the model in a sibling action. Can be used to fade out a particle in a sibling.
model <model>Creates (or fades in) a model
particle <model>Creates (or fades in) a particle
decal <material>Applies the specified decal to the ground (and anything else in the area)
size <int>Size of the decal
trackorigin <bool>Move around with the entity (vs stationary after spawning)
sound <sndshader>Start a sound (on any channel)

Example FX entry:

fx fx/velch_death
{

     {
      name "gibpart1"
      delay 0
      duration 3.0
      particle "hex_gib_eoc_01.prt"     
   }
   
     {
      name "velchdecal"
      delay 0
      duration 25
      decal "textures/decals/dsplat2"
      size 96
   }     
}


This a basic 2 part FX used when monster_velch dies.  When the decl is called it spawns two different entities.  The first named "gibpart1" is the dense blood particle you see in the image.  With a 0 delay and 3.0 duration the particle spawns the specified particle immediately and is removed after 3 seconds.

The second part named "velchdecal" is the splatter you see on the floor.  Again we spawn it immediately using a 0 delay but we leave the decal on the ground for a duration of 25 seconds.  We use the material declaration "textures/decals/dsplat2" scaled at 96x96.

In ai_monster_velch.script, we simply call it during "state_killed":

void monster_velch::state_Killed() {
     startFx("fx/velch_death");

   animState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
   animState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
   
   waitAction( "dead" );
   hide();
   
   sys.wait( 4 );      //give sounds time to fade out
   stopMove();
   stopSound( SND_CHANNEL_ANY, 0 );
   setState( "state_Dead" );
}

Zombie


The Happy Friar

Awesome, thanks.  Never tried to wrap my head around FX. 

You just forgot to mention that in D3 you need to use func_fx vs func_emitter.  ;)  Q4/ETQW just use fx, correct?

For the sake of future users, might not be a bad idea to get this over at moddb.com too.  Don't want info lost like when D3W went down.  :)

deadite4

I've never used a func_fx in a map before so was unaware.  I've always specifically used them as part of some type of entity/script/etc.

If you can list any other use cases, I can update my initial post to make the tutorial more thorough to cover more scenarios.  I'll also revise the attached PDF of the thread as well(which hopefully people will save as the backup to ever loosing the thread).

Never spent any time trying to work with Q4/ETQW though.  Feel free to move this thread to the scripting forum if you want.  Should we post all tutorials over there?  I'll try to put a few others together that have been asked for over the next week.

Mr A

Thank you so much for this! I've been trying to find out how to make enemies leave pools of blood for ages. I know what I'm doing tonight =D


The Happy Friar

I'm still figuring out where stuff should go.  Since you give permission I'll move it to scripting (it was considered scripting on D3W I think).  Hopefully Zombie could give pointers on if FX work different in ETQW, I just know that in Q4 everything is FX & in D3 you need a func_fx vs a func_emitter.  Never bothered much to use that so this is helpful.

bkt

Here is the Q4 FX Editor tutorial originally created by Goliath on D3W.

Part 1
Part 2

solarsplace

Hi

Nice tutorial  8)

Thought I would chip in with something that cost me a few wasted hours with the FX system...

Basically in the vanilla code "trackorigin" will not work unless "fadeIn" or "fadeOut" is set too.

Most if not all people would probably work around this issue by setting a very small fadeIn / fadeOut to work around the issue. However, if like me, your material / particle does not play nicely with the fadeIn / fadeOut e.g. results in black particles flying around and you don't want to fiddle with the texture / material, then the only option is to apply a work-around / fix to the SDK.

Hope this helps someone  :D

/*
================
idEntityFx::ApplyFade
================
*/
void idEntityFx::ApplyFade( const idFXSingleAction& fxaction, idFXLocalAction& laction, const int time, const int actualStart ) {
if ( fxaction.fadeInTime || fxaction.fadeOutTime ) {
float fadePct = (float)( time - actualStart ) / ( 1000.0f * ( ( fxaction.fadeInTime != 0 ) ? fxaction.fadeInTime : fxaction.fadeOutTime ) );
if (fadePct > 1.0) {
fadePct = 1.0;
}
if ( laction.modelDefHandle != -1 ) {
laction.renderEntity.shaderParms[SHADERPARM_RED] = (fxaction.fadeInTime) ? fadePct : 1.0f - fadePct;
laction.renderEntity.shaderParms[SHADERPARM_GREEN] = (fxaction.fadeInTime) ? fadePct : 1.0f - fadePct;
laction.renderEntity.shaderParms[SHADERPARM_BLUE] = (fxaction.fadeInTime) ? fadePct : 1.0f - fadePct;

gameRenderWorld->UpdateEntityDef( laction.modelDefHandle, &laction.renderEntity );
}
if ( laction.lightDefHandle != -1 ) {
laction.renderLight.shaderParms[SHADERPARM_RED] = fxaction.lightColor.x * ( (fxaction.fadeInTime) ? fadePct : 1.0f - fadePct );
laction.renderLight.shaderParms[SHADERPARM_GREEN] = fxaction.lightColor.y * ( (fxaction.fadeInTime) ? fadePct : 1.0f - fadePct );
laction.renderLight.shaderParms[SHADERPARM_BLUE] = fxaction.lightColor.z * ( (fxaction.fadeInTime) ? fadePct : 1.0f - fadePct );

gameRenderWorld->UpdateLightDef( laction.lightDefHandle, &laction.renderLight );
}
} else if ( fxaction.trackOrigin ) {
// Solarsplace - Arx End Of Sun - 15th Dec 2012
// Fix bug where gameRenderWorld->UpdateEntityDef would not be called when fxaction.trackOrigin is true
// without fxaction.fadeInTime || fxaction.fadeOutTime being set - which does not work with some particles
gameRenderWorld->UpdateEntityDef( laction.modelDefHandle, &laction.renderEntity );
}
}

VGames

So how would u attach an FX to a specific bone?
Get the latest on Perfected Doom 3 here - http://www.moddb.com/mods/perfected-doom-3-version-500

solarsplace

#9
Quote from: VGames on September 15, 2014, 01:57:20 PM
So how would u attach an FX to a specific bone?

Hi

As is the case with IDTech 4, there are actually several ways to accomplish this. Here is an easy way using built in script functions.

I also suggest that you have a look through doom_events.script file to get an idea of the multitude of built in script functions you can use such as:

// Starts an FX on this entity.
scriptEvent void startFx( string fx );


Create your own fx definition to do what you want such as lights, sounds and particles and from within your definition you can use the "bindto" key to set the joint of the MD5 model that you want to bind the fx to.

Example fx:

fx fx/arx_magic_levitate
{
bindto "origin"
{
name "part1"
duration 30
model "arx_spellfx_levitate.prt"
restart 0
offset 0, 0, 0
trackorigin 1
}
}


Important !!! - Note what I said previously about track origin - Basically in the vanilla code "trackorigin" will not work unless "fadeIn" or "fadeOut" is set too. If "trackorigin" is not set and working, then the fx will not move with the model is is bound to it will stay in the same spot that it was spawned while you monster (Or whatever) wonders off somewhere else...

Then you could start the effect in all sorts of places. Perhaps you would locate the scriptobject file that your entity uses and add the startFx to the following event to have it start immediately?

void monster_whatever::init() {
.
startFx( "fx/arx_magic_levitate" );
.
}


Or if you want the fx just for specific animation(s) you could use a framecommand in the entity def.

Take a look at monster_boss_sabaoth.def & ai_monster_boss_sabaoth.script for an example of calling a script function from a framecommand. See "object_call" below.

anim range_attack models/md5/monsters/sabaoth/attack_bfg.md5anim {
frame 5 object_call fire_bfg
frame 3 sound_weapon sabaoth_bfg_fire
}


Hope it helps.


VGames

Heck yeah that helped man. Thanks a lot. Now my Barons of Hell and Soul Knights can have lighting effects along with the fiery FX that I added to their hands without causing as much lag as they did when I was using projectiles to emit the light.
Get the latest on Perfected Doom 3 here - http://www.moddb.com/mods/perfected-doom-3-version-500

Mr A

I've been trying to get the FX to paint a decal on the ground when a monster is fragged, just like in your example but it's not working.  (Code is identicle to the one in exaple) Particle effects etc. work fine, but it just won't paint a blood decal on the floor. 

Here's a screen shot of what's happening. 
https://www.dropbox.com/s/vks5mjj2sdrfq6s/blood.jpg?dl=0

solarsplace

Quote from: Mr A on October 24, 2014, 09:03:51 AM
I've been trying to get the FX to paint a decal on the ground when a monster is fragged, just like in your example but it's not working.  (Code is identicle to the one in exaple) Particle effects etc. work fine, but it just won't paint a blood decal on the floor. 

Here's a screen shot of what's happening. 
https://www.dropbox.com/s/vks5mjj2sdrfq6s/blood.jpg?dl=0

Hi

The difference as more thoroughly explained here (http://idtechforums.fuzzylogicinc.com/index.php?topic=64.msg1054;topicseen#msg1054) is that the Hexen Edge Of Chaos Velch monster does not offset the origin in the af "body" containing the origin joint:

body "body" {
joint "origin"
mod orientation
model box( ( -10, -10, -10 ), ( 10, 10, 10 ) )
origin ( 0, 0, 0 )


Cheers

VGames

Ok this isn't working for me. I finally got around to trying this out and I'm stumped. The particle FX aren't showing up except for just the light and its not tracking the joint I want it to stay on when its created by the Baron of Hell upon initialization of the beast. Here's my code for the FX file:


fx fx/Baron_Hands_LightFX
{

bindto "rhand"
{
name "Light_RHand"
delay 0
duration 30
particle "baronofhell_hand.prt"
light "lights/roundfire2", 0.16, 0.46, 0.16, 80
restart 1
offset 1, 1, 1
FadeIn 0.01
FadeOut 0.01
trackorigin 1
}

}

fx fx/Baron_Hands_LightFX2
{

bindto "lhand"
{
name "Light_LHand"
delay 0
duration 30
particle "baronofhell_hand.prt"
light "lights/roundfire2", 0.16, 0.46, 0.16, 80
restart 1
offset 1, 1, 1
FadeIn 0.01
FadeOut 0.01
trackorigin 1
}

}


I have 2 separate FX for each hand in the same FX file. Don't know if this is ok.

Here's the code that calls on the FX in the demon's script file:


void monster_demon_baronofhell::init() {

startFx( "fx/Baron_Hands_LightFX" );
startFx( "fx/Baron_Hands_LightFX2" );

setState( "state_Begin" );
}


Please help if you can. The light effect shows up most of the time and just sits there in one spot and the particle effect is a no show.

Get the latest on Perfected Doom 3 here - http://www.moddb.com/mods/perfected-doom-3-version-500

solarsplace

Hi

I think the game code / syntax is case sensitive. Not at a suitable comp to check right now. But your code is not the same case as what is in the code.

if ( !token.Icmp( "fadeIn" ) ) {
FXAction.fadeInTime = src.ParseFloat();
continue;
}


Look here ( http://www.iddevnet.com/doom3/fx.php ) and copy / paste the "FX decl keywords" in place of yours to make sure they are 100% correct.

Give that a try, the rest looks ok from a quick look over.

Cheers