Glow tags are an object widget used to create a particle-based glowing effect, where camera-facing sprite particles travel along and/or emit from a path defined by markers in the gbxmodel. This tag is capable of much more complex effects than used in the energy sword, though has some known issues too.
Particle types
Glows can contain two types of particles. Normal particles follow the glow path and can be distanced from it radially, as well as animated in colour, speed, rotation, and distance. Once a normal particle reaches the end of the path they can bounce back or wrap to the beginning again. Trailing particles are emitted at a given rate from a defineable segment of the path, either in random directions or vertically. They have limited lifetimes and can fade, scale down, and slow down over the lifetime.
In the case that the glow path is a single marker or the root node default, only trailing particles will be emitted from that single point.
Glow path
The glow path is a smooth 3D spline connecting up to 5 object markers with the same name, determined by the attachment marker field. Any more will be ignored, and any less may be unreliable so it's recommended to use 5. It's also possible to define a "path" of a single marker or leave the field blank to use the root node as the default, in which case only trailing particles can emit.
A model can have multiple sets of glow markers so multiple widgets can be added. For example, the energy sword has 5 glow 1
markers for the upper blade and and 5 glow 2
markers for the lower.
Blender doesn't allow multiple objects to have the same name, so you will need to use the Blender toolset's marker name override feature. You can find it under the Object Data Properties pane when an object's name begins with #
.
Marker rotation matters! A marker's local +X axis (red) is its "forward" direction and +Z (blue) is "up". The ordering of markers is determined by how they point forward to each other in a chain (see initialization). The up direction can matter for trailing particle distribution, and introducing twist to markers along the path allows for more complex radial rotations and "pinching" than can be achieved with effect rotational velocity alone. Markers can also be slightly misaligned with the resulting path to create oval cross sections.
Initialization
Since the markers all have the same name, the game needs to determine their actual ordering and the total path length at runtime in an initialization step. This step is only done once and not redone if the model animates its marker positions.
- Get up to 5 markers from the model.
- For each marker, find which other marker it points at the best to be its "next" marker. This is done using the highest dot product between marker forward and direction to another marker. Negative dot products are ignored, meaning any other markers "behind" it won't be the next.
- Store a lookup table of marker indices in order in the glow effect's gamestate.
- Calculate total length as the sum of segment lengths.
Improper placement and rotation of markers can lead to different failure modes:
- Crash of the game with exception when looking at the glow:
render_cameras.c,#1086: bounds->x0<=bounds->x1
. - Game becoming unresponsive. This happens if markers form a closed loop.
- Some markers being unused.
- Paths looping back on themselves in unexpected ways.
- Sharp discontinuities at some markers in the smooth path.
Some general tips to avoid these issues are:
- Avoid high curvature.
- Point the +X axis of each marker in the path toward the next marker.
- Avoid circular loops.
- Don't use more than 5 markers. Using fewer may be unreliable too.
When creating glow paths it's helpful to set small size bounds, 0
distance, and a high particle count to make the curve easy to see. You should also enable render_model_markers 1
so you can make sure all markers are being used as intended.
Animation
Since markers can be parented to nodes and animated with a model_
Limits
Glow particles are distinct from other particles. Halo's gamestate can store up to 512 glow particles, which are shared across up to 8 glows. Note that a single model with two glow widgets still counts as 2 glows. Each glow effect can have up to 5 markers defining its path.
Be sure to budget number of particles and particle generation freq according to how many glows you expect will exist simultaneously. Although normal particles stop spawning once the limit is reached, creating too many trailing particles with long lifetimes will cause a crash:
EXCEPTION halt in c:\mcc\main\h1\code\h1a2\sources\objects\widgets\glow.c,#547: the map limit for the number of active glow particles has been reached
Known issues
The glow functionality does not appear to be fully implemented by the engine. It's only used for the energy sword, which doesn't make use of all the features of the tag. See the tag structure descriptions below.
This effect does not render on first person models.
Structure and fields
Field | Type | Comments | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
attachment marker | TagString | The name of the gbxmodel marker(s) which make up the glow path. The model can have up 5 markers with the same name. A model can have multiple sets of markers, e.g. If no markers have this name, or the field is left empty, a marker will be derived from the object's root node as the default. When the path is only defined by a single marker (including the root default) then then normal particles will not be generated. Trailing particles can still be generated. | |||||||||||||||||||||
number of particles | uint16 | How many normal particles to distribute along the path. The game is limited to 512 glow particles total, across up to 8 glow effects, so budget according to how many glow effects you expect to exist simultaneously. You can set this to | |||||||||||||||||||||
boundary effect | enum | Determines the behaviour of particles which reach the end of the path. | |||||||||||||||||||||
| |||||||||||||||||||||||
normal particle distribution | enum | Controls how particles are distributed along the glow path. | |||||||||||||||||||||
| |||||||||||||||||||||||
trailing particle distribution | enum | Controls how trailing particles are generated from the path segment defined at the end of the tag. Unaffected by particles move backwards and particles move in both directions. | |||||||||||||||||||||
| |||||||||||||||||||||||
glow flags | bitfield | ||||||||||||||||||||||
| |||||||||||||||||||||||
attachment 0 | enum | Seemingly unused. Setting this to an object function has no effect. | |||||||||||||||||||||
particle rotational velocity | float | No effect on normal or trailing particles. With a non-zero value, particle sprites did not rotate in screen space or radially around the glow path. Neither an object function attachment nor multiplier values changed this. | |||||||||||||||||||||
particle rot vel mul low | float | No visible effect. | |||||||||||||||||||||
particle rot vel mul high | float | No visible effect. | |||||||||||||||||||||
attachment 1 | enum ? | Sets an object function which controls the rotational velocity. | |||||||||||||||||||||
effect rotational velocity | float | Controls how quickly normal particles rotate radially around the path, in radians/sec. | |||||||||||||||||||||
effect rot vel mul low | float | Rotational velocity multipler when the function value is at 0. This only takes effect when a function attachment is set. It's not known exactly how this multiplier works; it has a gradually varying effect over the length of the glow path. | |||||||||||||||||||||
effect rot vel mul high | float | As above, but for a high function value. | |||||||||||||||||||||
attachment 2 | enum ? | Sets an object function which controls the translational velocity. | |||||||||||||||||||||
effect translational velocity | float | Controls how quickly particles move along the path, in world units/sec. This is not used for trailing particles, which instead use velocity of trailing particles. | |||||||||||||||||||||
effect trans vel mul low | float | Multiplier for the translational velocity for the function low value. Must be non-zero when a function is being used, or else the game will crash on | |||||||||||||||||||||
effect trans vel mul high | float | Multiplier for the translational velocity for the function high value. This value can be | |||||||||||||||||||||
attachment 3 | enum ? | Allows the radial distance of normal particles to be scaled by an object function. No effect on trailing particles. | |||||||||||||||||||||
min distance particle to object | float | When the distance attachment function is NONE, sets the minimum radial distance from the glow path that particles will be randomly placed. When distance is scaled by a function, sets the minimum distance of all particles. No effect on trailing particles. Radial distances are perpendicular to the path markers' +X axes, meaning marker rotation matters. Distance cannot vary over the length of the path, even when using a function, but you can twist some of the markers in the path to introduce a pinching effect. | |||||||||||||||||||||
max distance particle to object | float | When the distance attachment function is NONE, sets the maximum radial distance from the glow path that particles will be randomly placed. When distance is scaled by a function, sets the maximum distance of all particles. No effect on trailing particles. | |||||||||||||||||||||
distance to object mul low | float | When distance is scaled by an attachment function, determines the how a function value of | |||||||||||||||||||||
distance to object mul high | float | When distance is scaled by an attachment function, determines the how a function value of | |||||||||||||||||||||
attachment 4 | enum ? | Intended to make particle size scale with a function, but does not work. When not NONE, all particle spites render at their actual resolution regardless of distance rather than scale with the function. This does not affect trailing particles, which continue to be randomly selected between size bounds if trailing particles shrink over time is set. | |||||||||||||||||||||
particle size bounds | Bounds | When particle size is not scaled by a function attachment, particle sizes will be randomly chosen from this range. When scaled by a function, or when size bounds are both Trailing particles will always render at actual resolution unless trailing particles shrink over time is set, in which case size is selected randomly from these bounds regardless of any function attachment. | |||||||||||||||||||||
| |||||||||||||||||||||||
size attachment multiplier | Bounds | Does not work as intended. When a function is used, these values don't affect the sprite size. | |||||||||||||||||||||
attachment 5 | enum ? | If set to a function, all normal particles will blend between color bound 0 and color bound 1 according to the function. Otherwise particle colors are randomly selected from the range. When modify particle color in range is set, both cases are overridden and particles blend over the length of the glow path according to color rate of change. No effect on trailing particles, which always randomly select from the bounds. | |||||||||||||||||||||
color bound 0 | ColorARGB | Sets a lower bound for particle color selection. Particle colors are interpolated in RGB space, not HSL. The chosen color is multiplied with the sprite texture. The alpha value has no effect, unlike in some tags where it controls tint vs. modulation. | |||||||||||||||||||||
| |||||||||||||||||||||||
color bound 1 | ColorARGB ? | Sets an upper bound for particle color selection. | |||||||||||||||||||||
scale color 0 | ColorARGB | No visible effect in any color selection modes. | |||||||||||||||||||||
scale color 1 | ColorARGB | No visible effect in any color selection modes. | |||||||||||||||||||||
color rate of change | float | When modify particle color in range is set, controls how many times the per-channel difference between color bounds is added per world unit of distance along the glow path. | |||||||||||||||||||||
fading percentage of glow | float | Controls the distance that particles fade in and out at the path boundaries. A value of | |||||||||||||||||||||
particle generation freq | float | The frequency that trailing particles are generated, in particles/sec. This is likely limited by the tick rate since values over 30 have no visual difference. | |||||||||||||||||||||
lifetime of trailing particles | float | How long trailing particles live. If particle generation freq is non-zero, then this field must be too. An undefined lifetime will cause a crash on | |||||||||||||||||||||
velocity of trailing particles | float | Initial trailing particle velocity at generation. Although this is labeled "wu/s", testing found that the the actual speed is roughly 1/40th the value given here. The exact factor or what factors might apply is unknown. Unaffected by effect translational velocity. | |||||||||||||||||||||
trailing particle minimum t | float | Trailing particles will spawn from this minimum distance along the glow path, where | |||||||||||||||||||||
trailing particle maximum t | float | Trailing particles will spawn up to this maximum distance along the glow path, where | |||||||||||||||||||||
texture | TagDependency : bitmap | The sprite texture of particles. The referenced bitmap must have type sprites. It will not be randomly selected from sprite sheets (just the first). The particle is always shown camera-facing and with additive framebuffer blending. Alpha channel is ignored. |
Acknowledgements
Thanks to the following individuals for their research or contributions to this topic:
- Conscars (Testing behaviour of tag fields and glow path creation)
- Kavawuvi (Invader tag definitions)
- Kornman (Limits and how path construction works in glow updates)
- MosesOfEgypt (Tag structure research)