!!ARBvp1.0
OPTION ARB_position_invariant;
MOV	   result.texcoord, vertex.attrib[8];
MOV	   result.color, vertex.attrib[3];
END

!!ARBfp1.0
# == Fragment Program ==
#
# Input textures
#   texture[0]   particle diffusemap
#   texture[1]   _currentDepth
# 	
# Constants set by the engine:
#   program.env[4] is reciprocal of _currentDepth size. Lets us convert a screen position to a texcoord in _currentDepth
#   program.env[5] is the particle radius, given as { radius, 1/(fadeRange), 1/radius } 
#		fadeRange is the particle diameter for alpha blends (like smoke), but the particle radius for additive 
# 		blends (light glares), because additive effects work differently. Fog is half as apparent when a wall 
# 		is in the middle of it. Light glares lose no visibility when they have something to reflect off.
#   program.env[6] is the color channel mask. Particles with additive blend need their RGB channels modifying to blend them out. 
#                                             Particles with an alpha blend need their alpha channel modifying.
#
# Hard-coded constants
#    depth_consts allows us to recover the original depth in Doom units of anything in the depth 
#    buffer. TDM's projection matrix differs slightly from the classic projection matrix as it 
#    implements a "nearly-infinite" zFar. The matrix is hard-coded in the engine, so we use hard-coded 
#    constants here for efficiency. depth_consts is derived from the numbers in that matrix.
#

PARAM   depth_consts = { 0.33333333, -0.33316667, 0.0, 0.0 }; 
PARAM	particle_radius  = program.env[5];
TEMP    tmp, scene_depth, particle_depth, near_fade, fade;

# Map the fragment to a texcoord on our depth image, and sample to find scene_depth
MUL   tmp.xy, fragment.position, program.env[4];
TEX   scene_depth, tmp, texture[1], 2D;
MIN	  scene_depth, scene_depth, 0.9994;	# Required by TDM projection matrix. Equates to max recoverable 
										# depth of 30k units, which is enough. 0.9995 is infinite depth. This
										# is needed only if there is caulk sky on show (which writes no depth, 
										# so leaves 1 in the depth texture).

# Recover original depth in doom units
MAD   tmp, scene_depth, depth_consts.x, depth_consts.y;
RCP   scene_depth, tmp.x;

# Convert particle depth to doom units too
MAD   tmp, fragment.position.z, depth_consts.x, depth_consts.y;
RCP   particle_depth, tmp.x;

# Scale the depth difference by the particle diameter to calc an alpha 
# value based on how much of the 3d volume represented by the particle 
# is in front of the solid scene
ADD		 tmp, -scene_depth, particle_depth;	 # NB depth is negative. 0 at the eye, -100 at 100 units into the screen.
ADD      tmp, tmp, particle_radius.x; 		 # Add the radius so a depth difference of particle radius now equals 0
MUL_SAT  fade, tmp, particle_radius.y; 		 # divide by the particle radius or diameter and clamp

# Also fade if the particle is too close to our eye position, so it doesn't 'pop' in and out of view
# Start a linear fade at particle_radius distance from the particle.
MUL_SAT  near_fade, particle_depth, -particle_radius.z; 

# Calculate final fade and apply the channel mask
MUL      fade, near_fade, fade;
ADD_SAT  fade, fade, program.env[6];  # saturate the channels that don't want modifying

# Set the color. Multiply by vertex/fragment color as that's how the particle system fades particles in and out
TEMP  oColor;
TEX   oColor, fragment.texcoord, texture[0], 2D;
MUL   oColor, oColor, fade;
MUL   result.color, oColor, fragment.color; 

END