mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-16 06:00:33 +01:00
1273 lines
32 KiB
C
1273 lines
32 KiB
C
#include "vk_beams.h"
|
|
#include "vk_common.h"
|
|
#include "camera.h"
|
|
#include "vk_render.h"
|
|
#include "vk_geometry.h"
|
|
#include "vk_textures.h"
|
|
#include "vk_sprite.h"
|
|
#include "vk_scene.h"
|
|
#include "vk_math.h"
|
|
|
|
#include "xash3d_types.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "customentity.h"
|
|
#include "beamdef.h"
|
|
|
|
#define NOISE_DIVISIONS 64 // don't touch - many tripmines cause the crash when it equal 128
|
|
|
|
typedef struct
|
|
{
|
|
vec3_t pos;
|
|
float texcoord; // Y texture coordinate
|
|
float width;
|
|
} beamseg_t;
|
|
|
|
/*
|
|
==============================================================
|
|
|
|
FRACTAL NOISE
|
|
|
|
==============================================================
|
|
*/
|
|
static float rgNoise[NOISE_DIVISIONS+1]; // global noise array
|
|
|
|
// freq2 += step * 0.1;
|
|
// Fractal noise generator, power of 2 wavelength
|
|
static void FracNoise( float *noise, int divs )
|
|
{
|
|
int div2;
|
|
|
|
div2 = divs >> 1;
|
|
if( divs < 2 ) return;
|
|
|
|
// noise is normalized to +/- scale
|
|
noise[div2] = ( noise[0] + noise[divs] ) * 0.5f + divs * gEngine.COM_RandomFloat( -0.125f, 0.125f );
|
|
|
|
if( div2 > 1 )
|
|
{
|
|
FracNoise( &noise[div2], div2 );
|
|
FracNoise( noise, div2 );
|
|
}
|
|
}
|
|
|
|
static void SineNoise( float *noise, int divs )
|
|
{
|
|
float freq = 0;
|
|
float step = M_PI_F / (float)divs;
|
|
int i;
|
|
|
|
for( i = 0; i < divs; i++ )
|
|
{
|
|
noise[i] = sin( freq );
|
|
freq += step;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==============================================================
|
|
|
|
BEAM MATHLIB
|
|
|
|
==============================================================
|
|
*/
|
|
static void R_BeamComputePerpendicular( const vec3_t vecBeamDelta, vec3_t pPerp )
|
|
{
|
|
// direction in worldspace of the center of the beam
|
|
vec3_t vecBeamCenter;
|
|
|
|
VectorNormalize2( vecBeamDelta, vecBeamCenter );
|
|
CrossProduct( g_camera.vforward, vecBeamCenter, pPerp );
|
|
VectorNormalize( pPerp );
|
|
}
|
|
|
|
static void R_BeamComputeNormal( const vec3_t vStartPos, const vec3_t vNextPos, vec3_t pNormal )
|
|
{
|
|
// vTangentY = line vector for beam
|
|
vec3_t vTangentY, vDirToBeam;
|
|
|
|
VectorSubtract( vStartPos, vNextPos, vTangentY );
|
|
|
|
// vDirToBeam = vector from viewer origin to beam
|
|
VectorSubtract( vStartPos, g_camera.vieworg, vDirToBeam );
|
|
|
|
// get a vector that is perpendicular to us and perpendicular to the beam.
|
|
// this is used to fatten the beam.
|
|
CrossProduct( vTangentY, vDirToBeam, pNormal );
|
|
VectorNormalizeFast( pNormal );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
R_BeamCull
|
|
|
|
Cull the beam by bbox
|
|
==============
|
|
*/
|
|
qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly )
|
|
{
|
|
vec3_t mins, maxs;
|
|
int i;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
if( start[i] < end[i] )
|
|
{
|
|
mins[i] = start[i];
|
|
maxs[i] = end[i];
|
|
}
|
|
else
|
|
{
|
|
mins[i] = end[i];
|
|
maxs[i] = start[i];
|
|
}
|
|
|
|
// don't let it be zero sized
|
|
if( mins[i] == maxs[i] )
|
|
maxs[i] += 1.0f;
|
|
}
|
|
|
|
/* FIXME VK
|
|
// check bbox
|
|
if( gEngine.Mod_BoxVisible( mins, maxs, Mod_GetCurrentVis( )))
|
|
{
|
|
if( pvsOnly || !R_CullBox( mins, maxs ))
|
|
{
|
|
// beam is visible
|
|
return false;
|
|
}
|
|
}
|
|
*/
|
|
return false;
|
|
|
|
// beam is culled
|
|
return true;
|
|
}
|
|
|
|
|
|
static void applyBrightness( float brightness, const vec4_t color, vec4_t out )
|
|
{
|
|
out[0] = color[0] * color[3] * brightness;
|
|
out[1] = color[1] * color[3] * brightness;
|
|
out[2] = color[2] * color[3] * brightness;
|
|
out[3] = 1.f;
|
|
}
|
|
|
|
static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments, int flags, const vec4_t color, int texture, int render_mode )
|
|
{
|
|
int noiseIndex, noiseStep;
|
|
int i, total_segs, segs_drawn;
|
|
float div, length, fraction, factor;
|
|
float flMaxWidth, vLast, vStep, brightness;
|
|
vec3_t perp1, vLastNormal = {0};
|
|
beamseg_t curSeg = {0};
|
|
int total_vertices = 0;
|
|
int total_indices = 0;
|
|
r_geometry_buffer_lock_t buffer;
|
|
vk_vertex_t *dst_vtx;
|
|
uint16_t *dst_idx;
|
|
|
|
if( segments < 2 ) return;
|
|
|
|
length = VectorLength( delta );
|
|
flMaxWidth = width * 0.5f;
|
|
div = 1.0f / ( segments - 1 );
|
|
|
|
if( length * div < flMaxWidth * 1.414f )
|
|
{
|
|
// here, we have too many segments; we could get overlap... so lets have less segments
|
|
segments = (int)( length / ( flMaxWidth * 1.414f )) + 1.0f;
|
|
if( segments < 2 ) segments = 2;
|
|
}
|
|
|
|
if( segments > NOISE_DIVISIONS )
|
|
segments = NOISE_DIVISIONS;
|
|
|
|
div = 1.0f / (segments - 1);
|
|
length *= 0.01f;
|
|
vStep = length * div; // Texture length texels per space pixel
|
|
|
|
// Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture)
|
|
vLast = fmod( freq * speed, 1 );
|
|
|
|
if( flags & FBEAM_SINENOISE )
|
|
{
|
|
if( segments < 16 )
|
|
{
|
|
segments = 16;
|
|
div = 1.0f / ( segments - 1 );
|
|
}
|
|
scale *= 100.0f;
|
|
length = segments * 0.1f;
|
|
}
|
|
else
|
|
{
|
|
scale *= length * 2.0f;
|
|
}
|
|
|
|
// Iterator to resample noise waveform (it needs to be generated in powers of 2)
|
|
noiseStep = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f );
|
|
brightness = 1.0f;
|
|
noiseIndex = 0;
|
|
|
|
if( FBitSet( flags, FBEAM_SHADEIN ))
|
|
brightness = 0;
|
|
|
|
// Choose two vectors that are perpendicular to the beam
|
|
R_BeamComputePerpendicular( delta, perp1 );
|
|
|
|
total_segs = segments;
|
|
segs_drawn = 0;
|
|
|
|
total_vertices = (total_segs - 1) * 2 + 2;
|
|
total_indices = (total_vertices - 2) * 3; // STRIP unrolled into LIST (TODO get rid of this)
|
|
ASSERT(total_vertices < UINT16_MAX );
|
|
|
|
if (!R_GeometryBufferAllocAndLock( &buffer, total_vertices, total_indices, LifetimeSingleFrame )) {
|
|
gEngine.Con_Printf(S_ERROR "Cannot allocate geometry for beam\n");
|
|
return;
|
|
}
|
|
|
|
dst_vtx = buffer.vertices.ptr;
|
|
dst_idx = buffer.indices.ptr;
|
|
|
|
// specify all the segments.
|
|
for( i = 0; i < segments; i++ )
|
|
{
|
|
beamseg_t nextSeg;
|
|
vec3_t vPoint1, vPoint2;
|
|
|
|
ASSERT( noiseIndex < ( NOISE_DIVISIONS << 16 ));
|
|
|
|
fraction = i * div;
|
|
|
|
VectorMA( source, fraction, delta, nextSeg.pos );
|
|
|
|
// distort using noise
|
|
if( scale != 0 )
|
|
{
|
|
factor = rgNoise[noiseIndex>>16] * scale;
|
|
|
|
if( FBitSet( flags, FBEAM_SINENOISE ))
|
|
{
|
|
float s, c;
|
|
|
|
SinCos( fraction * M_PI_F * length + freq, &s, &c );
|
|
VectorMA( nextSeg.pos, (factor * s), g_camera.vup, nextSeg.pos );
|
|
|
|
// rotate the noise along the perpendicluar axis a bit to keep the bolt from looking diagonal
|
|
VectorMA( nextSeg.pos, (factor * c), g_camera.vright, nextSeg.pos );
|
|
}
|
|
else
|
|
{
|
|
VectorMA( nextSeg.pos, factor, perp1, nextSeg.pos );
|
|
}
|
|
}
|
|
|
|
// specify the next segment.
|
|
nextSeg.width = width * 2.0f;
|
|
nextSeg.texcoord = vLast;
|
|
|
|
if( segs_drawn > 0 )
|
|
{
|
|
// Get a vector that is perpendicular to us and perpendicular to the beam.
|
|
// This is used to fatten the beam.
|
|
vec3_t vNormal, vAveNormal;
|
|
|
|
R_BeamComputeNormal( curSeg.pos, nextSeg.pos, vNormal );
|
|
|
|
if( segs_drawn > 1 )
|
|
{
|
|
// Average this with the previous normal
|
|
VectorAdd( vNormal, vLastNormal, vAveNormal );
|
|
VectorScale( vAveNormal, 0.5f, vAveNormal );
|
|
VectorNormalizeFast( vAveNormal );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( vNormal, vAveNormal );
|
|
}
|
|
|
|
VectorCopy( vNormal, vLastNormal );
|
|
|
|
// draw regular segment
|
|
VectorMA( curSeg.pos, ( curSeg.width * 0.5f ), vAveNormal, vPoint1 );
|
|
VectorMA( curSeg.pos, (-curSeg.width * 0.5f ), vAveNormal, vPoint2 );
|
|
|
|
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f;
|
|
dst_vtx->gl_tc[0] = 0.0f;
|
|
dst_vtx->gl_tc[1] = curSeg.texcoord;
|
|
//FIXME VK applyBrightness( brightness, color, dst_vtx->color );
|
|
// FIXME VK pglNormal3fv( vAveNormal );
|
|
VectorCopy( vPoint1, dst_vtx->pos );
|
|
VectorCopy( vAveNormal, dst_vtx->normal );
|
|
++dst_vtx;
|
|
|
|
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f;
|
|
dst_vtx->gl_tc[0] = 1.0f;
|
|
dst_vtx->gl_tc[1] = curSeg.texcoord;
|
|
//FIXME VK applyBrightness( brightness, color, dst_vtx->color );
|
|
// FIXME VK pglNormal3fv( vAveNormal );
|
|
VectorCopy( vPoint2, dst_vtx->pos );
|
|
++dst_vtx;
|
|
}
|
|
|
|
curSeg = nextSeg;
|
|
segs_drawn++;
|
|
|
|
if( FBitSet( flags, FBEAM_SHADEIN ) && FBitSet( flags, FBEAM_SHADEOUT ))
|
|
{
|
|
if( fraction < 0.5f ) brightness = fraction;
|
|
else brightness = ( 1.0f - fraction );
|
|
}
|
|
else if( FBitSet( flags, FBEAM_SHADEIN ))
|
|
{
|
|
brightness = fraction;
|
|
}
|
|
else if( FBitSet( flags, FBEAM_SHADEOUT ))
|
|
{
|
|
brightness = 1.0f - fraction;
|
|
}
|
|
|
|
if( segs_drawn == total_segs )
|
|
{
|
|
// draw the last segment
|
|
VectorMA( curSeg.pos, ( curSeg.width * 0.5f ), vLastNormal, vPoint1 );
|
|
VectorMA( curSeg.pos, (-curSeg.width * 0.5f ), vLastNormal, vPoint2 );
|
|
|
|
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f;
|
|
dst_vtx->gl_tc[0] = 0.0f;
|
|
dst_vtx->gl_tc[1] = curSeg.texcoord;
|
|
//FIXME VK applyBrightness( brightness, color, dst_vtx->color );
|
|
// FIXME VK pglNormal3fv( vLastNormal );
|
|
VectorCopy( vPoint1, dst_vtx->pos );
|
|
++dst_vtx;
|
|
|
|
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f;
|
|
dst_vtx->gl_tc[0] = 1.0f;
|
|
dst_vtx->gl_tc[1] = curSeg.texcoord;
|
|
//FIXME VK applyBrightness( brightness, color, dst_vtx->color );
|
|
// FIXME VK pglNormal3fv( vLastNormal );
|
|
VectorCopy( vPoint2, dst_vtx->pos );
|
|
++dst_vtx;
|
|
}
|
|
|
|
vLast += vStep; // Advance texture scroll (v axis only)
|
|
noiseIndex += noiseStep;
|
|
}
|
|
|
|
for (int i = 2; i < total_vertices; ++i) {
|
|
if( i & 1 )
|
|
{
|
|
// draw triangle [n-1 n-2 n]
|
|
dst_idx[(i-2)*3+0] = i - 1;
|
|
dst_idx[(i-2)*3+1] = i - 2;
|
|
dst_idx[(i-2)*3+2] = i;
|
|
}
|
|
else
|
|
{
|
|
// draw triangle [n-2 n-1 n]
|
|
dst_idx[(i-2)*3+0] = i - 2;
|
|
dst_idx[(i-2)*3+1] = i - 1;
|
|
dst_idx[(i-2)*3+2] = i;
|
|
}
|
|
}
|
|
|
|
R_GeometryBufferUnlock( &buffer );
|
|
|
|
{
|
|
const vk_render_geometry_t geometry = {
|
|
.texture = texture,
|
|
.material = kXVkMaterialEmissive,
|
|
|
|
.max_vertex = total_vertices,
|
|
.vertex_offset = buffer.vertices.unit_offset,
|
|
|
|
.element_count = total_indices,
|
|
.index_offset = buffer.indices.unit_offset,
|
|
|
|
.emissive = { color[0], color[1], color[2] },
|
|
};
|
|
|
|
|
|
VK_RenderModelDynamicBegin( render_mode, "beam" /* TODO its name */ );
|
|
VK_RenderModelDynamicAddGeometry( &geometry );
|
|
VK_RenderModelDynamicCommit();
|
|
}
|
|
}
|
|
|
|
static void R_DrawTorus( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments )
|
|
{
|
|
PRINT_NOT_IMPLEMENTED();
|
|
|
|
/* FIXME VK
|
|
int i, noiseIndex, noiseStep;
|
|
float div, length, fraction, factor, vLast, vStep;
|
|
vec3_t last1, last2, point, screen, screenLast, tmp, normal;
|
|
|
|
if( segments < 2 )
|
|
return;
|
|
|
|
if( segments > NOISE_DIVISIONS )
|
|
segments = NOISE_DIVISIONS;
|
|
|
|
length = VectorLength( delta ) * 0.01f;
|
|
if( length < 0.5f ) length = 0.5f; // don't lose all of the noise/texture on short beams
|
|
|
|
div = 1.0f / (segments - 1);
|
|
|
|
vStep = length * div; // Texture length texels per space pixel
|
|
|
|
// Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture)
|
|
vLast = fmod( freq * speed, 1 );
|
|
scale = scale * length;
|
|
|
|
// Iterator to resample noise waveform (it needs to be generated in powers of 2)
|
|
noiseStep = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f );
|
|
noiseIndex = 0;
|
|
|
|
for( i = 0; i < segments; i++ )
|
|
{
|
|
float s, c;
|
|
|
|
fraction = i * div;
|
|
SinCos( fraction * M_PI2_F, &s, &c );
|
|
|
|
point[0] = s * freq * delta[2] + source[0];
|
|
point[1] = c * freq * delta[2] + source[1];
|
|
point[2] = source[2];
|
|
|
|
// distort using noise
|
|
if( scale != 0 )
|
|
{
|
|
if(( noiseIndex >> 16 ) < NOISE_DIVISIONS )
|
|
{
|
|
factor = rgNoise[noiseIndex>>16] * scale;
|
|
VectorMA( point, factor, g_camera.vup, point );
|
|
|
|
// rotate the noise along the perpendicluar axis a bit to keep the bolt from looking diagonal
|
|
factor = rgNoise[noiseIndex>>16] * scale * cos( fraction * M_PI_F * 3 + freq );
|
|
VectorMA( point, factor, g_camera.vright, point );
|
|
}
|
|
}
|
|
|
|
// Transform point into screen space
|
|
TriWorldToScreen( point, screen );
|
|
|
|
if( i != 0 )
|
|
{
|
|
// build world-space normal to screen-space direction vector
|
|
VectorSubtract( screen, screenLast, tmp );
|
|
|
|
// we don't need Z, we're in screen space
|
|
tmp[2] = 0;
|
|
VectorNormalize( tmp );
|
|
VectorScale( g_camera.vup, -tmp[0], normal ); // Build point along noraml line (normal is -y, x)
|
|
VectorMA( normal, tmp[1], g_camera.vright, normal );
|
|
|
|
// Make a wide line
|
|
VectorMA( point, width, normal, last1 );
|
|
VectorMA( point, -width, normal, last2 );
|
|
|
|
vLast += vStep; // advance texture scroll (v axis only)
|
|
TriTexCoord2f( 1, vLast );
|
|
TriVertex3fv( last2 );
|
|
TriTexCoord2f( 0, vLast );
|
|
TriVertex3fv( last1 );
|
|
}
|
|
|
|
VectorCopy( screen, screenLast );
|
|
noiseIndex += noiseStep;
|
|
}
|
|
*/
|
|
}
|
|
|
|
static void R_DrawDisk( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments )
|
|
{
|
|
PRINT_NOT_IMPLEMENTED();
|
|
|
|
/* FIXME VK
|
|
float div, length, fraction;
|
|
float w, vLast, vStep;
|
|
vec3_t point;
|
|
int i;
|
|
|
|
if( segments < 2 ) return;
|
|
|
|
if( segments > NOISE_DIVISIONS ) // UNDONE: Allow more segments?
|
|
segments = NOISE_DIVISIONS;
|
|
|
|
length = VectorLength( delta ) * 0.01f;
|
|
if( length < 0.5f ) length = 0.5f; // don't lose all of the noise/texture on short beams
|
|
|
|
div = 1.0f / (segments - 1);
|
|
vStep = length * div; // Texture length texels per space pixel
|
|
|
|
// scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture)
|
|
vLast = fmod( freq * speed, 1 );
|
|
scale = scale * length;
|
|
|
|
// clamp the beam width
|
|
w = fmod( freq, width * 0.1f ) * delta[2];
|
|
|
|
// NOTE: we must force the degenerate triangles to be on the edge
|
|
for( i = 0; i < segments; i++ )
|
|
{
|
|
float s, c;
|
|
|
|
fraction = i * div;
|
|
VectorCopy( source, point );
|
|
|
|
TriBrightness( 1.0f );
|
|
TriTexCoord2f( 1.0f, vLast );
|
|
TriVertex3fv( point );
|
|
|
|
SinCos( fraction * M_PI2_F, &s, &c );
|
|
point[0] = s * w + source[0];
|
|
point[1] = c * w + source[1];
|
|
point[2] = source[2];
|
|
|
|
TriBrightness( 1.0f );
|
|
TriTexCoord2f( 0.0f, vLast );
|
|
TriVertex3fv( point );
|
|
|
|
vLast += vStep; // advance texture scroll (v axis only)
|
|
}
|
|
*/
|
|
}
|
|
|
|
static void R_DrawCylinder( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments )
|
|
{
|
|
PRINT_NOT_IMPLEMENTED();
|
|
|
|
/* FIXME VK
|
|
float div, length, fraction;
|
|
float vLast, vStep;
|
|
vec3_t point;
|
|
int i;
|
|
|
|
if( segments < 2 )
|
|
return;
|
|
|
|
if( segments > NOISE_DIVISIONS )
|
|
segments = NOISE_DIVISIONS;
|
|
|
|
length = VectorLength( delta ) * 0.01f;
|
|
if( length < 0.5f ) length = 0.5f; // don't lose all of the noise/texture on short beams
|
|
|
|
div = 1.0f / (segments - 1);
|
|
vStep = length * div; // texture length texels per space pixel
|
|
|
|
// Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture)
|
|
vLast = fmod( freq * speed, 1 );
|
|
scale = scale * length;
|
|
|
|
for ( i = 0; i < segments; i++ )
|
|
{
|
|
float s, c;
|
|
|
|
fraction = i * div;
|
|
SinCos( fraction * M_PI2_F, &s, &c );
|
|
|
|
point[0] = s * freq * delta[2] + source[0];
|
|
point[1] = c * freq * delta[2] + source[1];
|
|
point[2] = source[2] + width;
|
|
|
|
TriBrightness( 0 );
|
|
TriTexCoord2f( 1, vLast );
|
|
TriVertex3fv( point );
|
|
|
|
point[0] = s * freq * ( delta[2] + width ) + source[0];
|
|
point[1] = c * freq * ( delta[2] + width ) + source[1];
|
|
point[2] = source[2] - width;
|
|
|
|
TriBrightness( 1 );
|
|
TriTexCoord2f( 0, vLast );
|
|
TriVertex3fv( point );
|
|
|
|
vLast += vStep; // Advance texture scroll (v axis only)
|
|
}
|
|
*/
|
|
}
|
|
|
|
static void R_DrawBeamFollow( BEAM *pbeam, float frametime )
|
|
{
|
|
PRINT_NOT_IMPLEMENTED();
|
|
|
|
/* FIXME VK
|
|
particle_t *pnew, *particles;
|
|
float fraction, div, vLast, vStep;
|
|
vec3_t last1, last2, tmp, screen;
|
|
vec3_t delta, screenLast, normal;
|
|
|
|
gEngine.R_FreeDeadParticles( &pbeam->particles );
|
|
|
|
particles = pbeam->particles;
|
|
pnew = NULL;
|
|
|
|
div = 0;
|
|
if( FBitSet( pbeam->flags, FBEAM_STARTENTITY ))
|
|
{
|
|
if( particles )
|
|
{
|
|
VectorSubtract( particles->org, pbeam->source, delta );
|
|
div = VectorLength( delta );
|
|
|
|
if( div >= 32 )
|
|
{
|
|
pnew = gEngine.CL_AllocParticleFast();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pnew = gEngine.CL_AllocParticleFast();
|
|
}
|
|
}
|
|
|
|
if( pnew )
|
|
{
|
|
VectorCopy( pbeam->source, pnew->org );
|
|
pnew->die = gpGlobals->time + pbeam->amplitude;
|
|
VectorClear( pnew->vel );
|
|
|
|
pnew->next = particles;
|
|
pbeam->particles = pnew;
|
|
particles = pnew;
|
|
}
|
|
|
|
// nothing to draw
|
|
if( !particles ) return;
|
|
|
|
if( !pnew && div != 0 )
|
|
{
|
|
VectorCopy( pbeam->source, delta );
|
|
TriWorldToScreen( pbeam->source, screenLast );
|
|
TriWorldToScreen( particles->org, screen );
|
|
}
|
|
else if( particles && particles->next )
|
|
{
|
|
VectorCopy( particles->org, delta );
|
|
TriWorldToScreen( particles->org, screenLast );
|
|
TriWorldToScreen( particles->next->org, screen );
|
|
particles = particles->next;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
// UNDONE: This won't work, screen and screenLast must be extrapolated here to fix the
|
|
// first beam segment for this trail
|
|
|
|
// build world-space normal to screen-space direction vector
|
|
VectorSubtract( screen, screenLast, tmp );
|
|
// we don't need Z, we're in screen space
|
|
tmp[2] = 0;
|
|
VectorNormalize( tmp );
|
|
|
|
// Build point along noraml line (normal is -y, x)
|
|
VectorScale( g_camera.vup, tmp[0], normal ); // Build point along normal line (normal is -y, x)
|
|
VectorMA( normal, tmp[1], g_camera.vright, normal );
|
|
|
|
// Make a wide line
|
|
VectorMA( delta, pbeam->width, normal, last1 );
|
|
VectorMA( delta, -pbeam->width, normal, last2 );
|
|
|
|
div = 1.0f / pbeam->amplitude;
|
|
fraction = ( pbeam->die - gpGlobals->time ) * div;
|
|
|
|
vLast = 0.0f;
|
|
vStep = 1.0f;
|
|
|
|
while( particles )
|
|
{
|
|
TriBrightness( fraction );
|
|
TriTexCoord2f( 1, 1 );
|
|
TriVertex3fv( last2 );
|
|
TriBrightness( fraction );
|
|
TriTexCoord2f( 0, 1 );
|
|
TriVertex3fv( last1 );
|
|
|
|
// Transform point into screen space
|
|
TriWorldToScreen( particles->org, screen );
|
|
// Build world-space normal to screen-space direction vector
|
|
VectorSubtract( screen, screenLast, tmp );
|
|
|
|
// we don't need Z, we're in screen space
|
|
tmp[2] = 0;
|
|
VectorNormalize( tmp );
|
|
VectorScale( g_camera.vup, tmp[0], normal ); // Build point along noraml line (normal is -y, x)
|
|
VectorMA( normal, tmp[1], g_camera.vright, normal );
|
|
|
|
// Make a wide line
|
|
VectorMA( particles->org, pbeam->width, normal, last1 );
|
|
VectorMA( particles->org, -pbeam->width, normal, last2 );
|
|
|
|
vLast += vStep; // Advance texture scroll (v axis only)
|
|
|
|
if( particles->next != NULL )
|
|
{
|
|
fraction = (particles->die - gpGlobals->time) * div;
|
|
}
|
|
else
|
|
{
|
|
fraction = 0.0;
|
|
}
|
|
|
|
TriBrightness( fraction );
|
|
TriTexCoord2f( 0, 0 );
|
|
TriVertex3fv( last1 );
|
|
TriBrightness( fraction );
|
|
TriTexCoord2f( 1, 0 );
|
|
TriVertex3fv( last2 );
|
|
|
|
VectorCopy( screen, screenLast );
|
|
|
|
particles = particles->next;
|
|
}
|
|
|
|
// drift popcorn trail if there is a velocity
|
|
particles = pbeam->particles;
|
|
|
|
while( particles )
|
|
{
|
|
VectorMA( particles->org, frametime, particles->vel, particles->org );
|
|
particles = particles->next;
|
|
}
|
|
*/
|
|
}
|
|
|
|
static void R_DrawRing( vec3_t source, vec3_t delta, float width, float amplitude, float freq, float speed, int segments )
|
|
{
|
|
PRINT_NOT_IMPLEMENTED();
|
|
|
|
/* FIXME VK
|
|
int i, j, noiseIndex, noiseStep;
|
|
float div, length, fraction, factor, vLast, vStep;
|
|
vec3_t last1, last2, point, screen, screenLast;
|
|
vec3_t tmp, normal, center, xaxis, yaxis;
|
|
float radius, x, y, scale;
|
|
|
|
if( segments < 2 )
|
|
return;
|
|
|
|
VectorClear( screenLast );
|
|
segments = segments * M_PI_F;
|
|
|
|
if( segments > NOISE_DIVISIONS * 8 )
|
|
segments = NOISE_DIVISIONS * 8;
|
|
|
|
length = VectorLength( delta ) * 0.01f * M_PI_F;
|
|
if( length < 0.5f ) length = 0.5f; // Don't lose all of the noise/texture on short beams
|
|
|
|
div = 1.0f / ( segments - 1 );
|
|
|
|
vStep = length * div / 8.0f; // texture length texels per space pixel
|
|
|
|
// Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture)
|
|
vLast = fmod( freq * speed, 1.0f );
|
|
scale = amplitude * length / 8.0f;
|
|
|
|
// Iterator to resample noise waveform (it needs to be generated in powers of 2)
|
|
noiseStep = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f ) * 8;
|
|
noiseIndex = 0;
|
|
|
|
VectorScale( delta, 0.5f, delta );
|
|
VectorAdd( source, delta, center );
|
|
|
|
VectorCopy( delta, xaxis );
|
|
radius = VectorLength( xaxis );
|
|
|
|
// cull beamring
|
|
// --------------------------------
|
|
// Compute box center +/- radius
|
|
VectorSet( last1, radius, radius, scale );
|
|
VectorAdd( center, last1, tmp ); // maxs
|
|
VectorSubtract( center, last1, screen ); // mins
|
|
|
|
if( !WORLDMODEL )
|
|
return;
|
|
|
|
// is that box in PVS && frustum?
|
|
if( !gEngine.Mod_BoxVisible( screen, tmp, Mod_GetCurrentVis( )) || R_CullBox( screen, tmp ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
VectorSet( yaxis, xaxis[1], -xaxis[0], 0.0f );
|
|
VectorNormalize( yaxis );
|
|
VectorScale( yaxis, radius, yaxis );
|
|
|
|
j = segments / 8;
|
|
|
|
for( i = 0; i < segments + 1; i++ )
|
|
{
|
|
fraction = i * div;
|
|
SinCos( fraction * M_PI2_F, &x, &y );
|
|
|
|
VectorMAMAM( x, xaxis, y, yaxis, 1.0f, center, point );
|
|
|
|
// distort using noise
|
|
factor = rgNoise[(noiseIndex >> 16) & (NOISE_DIVISIONS - 1)] * scale;
|
|
VectorMA( point, factor, g_camera.vup, point );
|
|
|
|
// Rotate the noise along the perpendicluar axis a bit to keep the bolt from looking diagonal
|
|
factor = rgNoise[(noiseIndex >> 16) & (NOISE_DIVISIONS - 1)] * scale;
|
|
factor *= cos( fraction * M_PI_F * 24 + freq );
|
|
VectorMA( point, factor, g_camera.vright, point );
|
|
|
|
// Transform point into screen space
|
|
TriWorldToScreen( point, screen );
|
|
|
|
if( i != 0 )
|
|
{
|
|
// build world-space normal to screen-space direction vector
|
|
VectorSubtract( screen, screenLast, tmp );
|
|
|
|
// we don't need Z, we're in screen space
|
|
tmp[2] = 0;
|
|
VectorNormalize( tmp );
|
|
|
|
// Build point along normal line (normal is -y, x)
|
|
VectorScale( g_camera.vup, tmp[0], normal );
|
|
VectorMA( normal, tmp[1], g_camera.vright, normal );
|
|
|
|
// Make a wide line
|
|
VectorMA( point, width, normal, last1 );
|
|
VectorMA( point, -width, normal, last2 );
|
|
|
|
vLast += vStep; // Advance texture scroll (v axis only)
|
|
TriTexCoord2f( 1.0f, vLast );
|
|
TriVertex3fv( last2 );
|
|
TriTexCoord2f( 0.0f, vLast );
|
|
TriVertex3fv( last1 );
|
|
}
|
|
|
|
VectorCopy( screen, screenLast );
|
|
noiseIndex += noiseStep;
|
|
j--;
|
|
|
|
if( j == 0 && amplitude != 0 )
|
|
{
|
|
j = segments / 8;
|
|
FracNoise( rgNoise, NOISE_DIVISIONS );
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_BeamComputePoint
|
|
|
|
compute attachment point for beam
|
|
==============
|
|
*/
|
|
static qboolean R_BeamComputePoint( int beamEnt, vec3_t pt )
|
|
{
|
|
cl_entity_t *ent;
|
|
int attach;
|
|
|
|
ent = gEngine.R_BeamGetEntity( beamEnt );
|
|
|
|
if( beamEnt < 0 )
|
|
attach = BEAMENT_ATTACHMENT( -beamEnt );
|
|
else attach = BEAMENT_ATTACHMENT( beamEnt );
|
|
|
|
if( !ent )
|
|
{
|
|
gEngine.Con_DPrintf( S_ERROR "R_BeamComputePoint: invalid entity %i\n", BEAMENT_ENTITY( beamEnt ));
|
|
VectorClear( pt );
|
|
return false;
|
|
}
|
|
|
|
// get attachment
|
|
if( attach > 0 )
|
|
VectorCopy( ent->attachment[attach - 1], pt );
|
|
else if( ent->index == gEngine.EngineGetParm( PARM_PLAYER_INDEX, 0 ) )
|
|
{
|
|
vec3_t simorg;
|
|
gEngine.GetPredictedOrigin( simorg );
|
|
VectorCopy( simorg, pt );
|
|
}
|
|
else VectorCopy( ent->origin, pt );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_BeamRecomputeEndpoints
|
|
|
|
Recomputes beam endpoints..
|
|
==============
|
|
*/
|
|
static qboolean R_BeamRecomputeEndpoints( BEAM *pbeam )
|
|
{
|
|
if( FBitSet( pbeam->flags, FBEAM_STARTENTITY ))
|
|
{
|
|
cl_entity_t *start = gEngine.R_BeamGetEntity( pbeam->startEntity );
|
|
|
|
if( R_BeamComputePoint( pbeam->startEntity, pbeam->source ))
|
|
{
|
|
if( !pbeam->pFollowModel )
|
|
pbeam->pFollowModel = start->model;
|
|
SetBits( pbeam->flags, FBEAM_STARTVISIBLE );
|
|
}
|
|
else if( !FBitSet( pbeam->flags, FBEAM_FOREVER ))
|
|
{
|
|
ClearBits( pbeam->flags, FBEAM_STARTENTITY );
|
|
}
|
|
}
|
|
|
|
if( FBitSet( pbeam->flags, FBEAM_ENDENTITY ))
|
|
{
|
|
cl_entity_t *end = gEngine.R_BeamGetEntity( pbeam->endEntity );
|
|
|
|
if( R_BeamComputePoint( pbeam->endEntity, pbeam->target ))
|
|
{
|
|
if( !pbeam->pFollowModel )
|
|
pbeam->pFollowModel = end->model;
|
|
SetBits( pbeam->flags, FBEAM_ENDVISIBLE );
|
|
}
|
|
else if( !FBitSet( pbeam->flags, FBEAM_FOREVER ))
|
|
{
|
|
ClearBits( pbeam->flags, FBEAM_ENDENTITY );
|
|
pbeam->die = gpGlobals->time;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( FBitSet( pbeam->flags, FBEAM_STARTENTITY ) && !FBitSet( pbeam->flags, FBEAM_STARTVISIBLE ))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
R_BeamDraw
|
|
|
|
Update beam vars and draw it
|
|
==============
|
|
*/
|
|
void R_BeamDraw( BEAM *pbeam, float frametime )
|
|
{
|
|
model_t *model;
|
|
vec3_t delta;
|
|
vec4_t color;
|
|
int render_mode;
|
|
int texturenum;
|
|
|
|
model = gEngine.pfnGetModelByIndex( pbeam->modelIndex );
|
|
SetBits( pbeam->flags, FBEAM_ISACTIVE );
|
|
|
|
if( !model || model->type != mod_sprite )
|
|
{
|
|
pbeam->flags &= ~FBEAM_ISACTIVE; // force to ignore
|
|
pbeam->die = gpGlobals->time;
|
|
return;
|
|
}
|
|
|
|
// update frequency
|
|
pbeam->freq += frametime;
|
|
|
|
// generate fractal noise
|
|
if( frametime != 0.0f )
|
|
{
|
|
rgNoise[0] = 0;
|
|
rgNoise[NOISE_DIVISIONS] = 0;
|
|
}
|
|
|
|
if( pbeam->amplitude != 0 && frametime != 0.0f )
|
|
{
|
|
if( FBitSet( pbeam->flags, FBEAM_SINENOISE ))
|
|
SineNoise( rgNoise, NOISE_DIVISIONS );
|
|
else FracNoise( rgNoise, NOISE_DIVISIONS );
|
|
}
|
|
|
|
// update end points
|
|
if( FBitSet( pbeam->flags, FBEAM_STARTENTITY|FBEAM_ENDENTITY ))
|
|
{
|
|
// makes sure attachment[0] + attachment[1] are valid
|
|
if( !R_BeamRecomputeEndpoints( pbeam ))
|
|
{
|
|
ClearBits( pbeam->flags, FBEAM_ISACTIVE ); // force to ignore
|
|
return;
|
|
}
|
|
|
|
// compute segments from the new endpoints
|
|
VectorSubtract( pbeam->target, pbeam->source, delta );
|
|
VectorClear( pbeam->delta );
|
|
|
|
if( VectorLength( delta ) > 0.0000001f )
|
|
VectorCopy( delta, pbeam->delta );
|
|
|
|
if( pbeam->amplitude >= 0.50f )
|
|
pbeam->segments = VectorLength( pbeam->delta ) * 0.25f + 3.0f; // one per 4 pixels
|
|
else pbeam->segments = VectorLength( pbeam->delta ) * 0.075f + 3.0f; // one per 16 pixels
|
|
}
|
|
|
|
if( pbeam->type == TE_BEAMPOINTS && R_BeamCull( pbeam->source, pbeam->target, 0 ))
|
|
{
|
|
ClearBits( pbeam->flags, FBEAM_ISACTIVE );
|
|
return;
|
|
}
|
|
|
|
// don't draw really short or inactive beams
|
|
if( !FBitSet( pbeam->flags, FBEAM_ISACTIVE ) || VectorLength( pbeam->delta ) < 0.1f )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( pbeam->flags & ( FBEAM_FADEIN|FBEAM_FADEOUT ))
|
|
{
|
|
// update life cycle
|
|
pbeam->t = pbeam->freq + ( pbeam->die - gpGlobals->time );
|
|
if( pbeam->t != 0.0f ) pbeam->t = 1.0f - pbeam->freq / pbeam->t;
|
|
}
|
|
|
|
if( pbeam->type == TE_BEAMHOSE )
|
|
{
|
|
float flDot;
|
|
|
|
VectorSubtract( pbeam->target, pbeam->source, delta );
|
|
VectorNormalize( delta );
|
|
|
|
flDot = DotProduct( delta, g_camera.vforward );
|
|
|
|
// abort if the player's looking along it away from the source
|
|
if( flDot > 0 )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
float flFade = pow( flDot, 10 );
|
|
vec3_t localDir, vecProjection, tmp;
|
|
float flDistance;
|
|
|
|
// fade the beam if the player's not looking at the source
|
|
VectorSubtract( g_camera.vieworg, pbeam->source, localDir );
|
|
flDot = DotProduct( delta, localDir );
|
|
VectorScale( delta, flDot, vecProjection );
|
|
VectorSubtract( localDir, vecProjection, tmp );
|
|
flDistance = VectorLength( tmp );
|
|
|
|
if( flDistance > 30 )
|
|
{
|
|
flDistance = 1.0f - (( flDistance - 30.0f ) / 64.0f );
|
|
if( flDistance <= 0 ) flFade = 0;
|
|
else flFade *= pow( flDistance, 3 );
|
|
}
|
|
|
|
if( flFade < ( 1.0f / 255.0f ))
|
|
return;
|
|
|
|
// FIXME: needs to be testing
|
|
pbeam->brightness *= flFade;
|
|
}
|
|
}
|
|
|
|
render_mode = FBitSet( pbeam->flags, FBEAM_SOLID ) ? kRenderNormal : kRenderTransAdd;
|
|
|
|
texturenum = R_GetSpriteTexture( model, (int)(pbeam->frame + pbeam->frameRate * gpGlobals->time) % pbeam->frameCount);
|
|
if( texturenum <= 0 ) // FIXME VK || texturenum > MAX_TEXTURES )
|
|
{
|
|
ClearBits( pbeam->flags, FBEAM_ISACTIVE );
|
|
return;
|
|
}
|
|
|
|
if( pbeam->type == TE_BEAMFOLLOW )
|
|
{
|
|
cl_entity_t *pStart;
|
|
|
|
// XASH SPECIFIC: get brightness from head entity
|
|
pStart = gEngine.R_BeamGetEntity( pbeam->startEntity );
|
|
if( pStart && pStart->curstate.rendermode != kRenderNormal )
|
|
pbeam->brightness = CL_FxBlend( pStart ) / 255.0f;
|
|
}
|
|
|
|
color[0] = pbeam->r;
|
|
color[1] = pbeam->g;
|
|
color[2] = pbeam->b;
|
|
if( FBitSet( pbeam->flags, FBEAM_FADEIN ))
|
|
color[3] = pbeam->t * pbeam->brightness;
|
|
else if( FBitSet( pbeam->flags, FBEAM_FADEOUT ))
|
|
color[3] = (1.f - pbeam->t) * pbeam->brightness;
|
|
else
|
|
color[3] = pbeam->brightness;
|
|
|
|
// FIXME VK what is our vk_render matrix state now? do we have all matrices set properly?
|
|
// TODO this can be done only once for all beams, i.e. before calling CL_DrawEFX
|
|
VK_RenderStateSetMatrixModel( matrix4x4_identity );
|
|
|
|
// TODO gl renderer has per-vertex color that is updated using brightness and whatever
|
|
VK_RenderStateSetColor( color[0], color[1], color[2], color[3] );
|
|
|
|
VK_RenderDebugLabelBegin( "beam" );
|
|
|
|
switch( pbeam->type )
|
|
{
|
|
case TE_BEAMTORUS:
|
|
// FIXME VK GL_Cull( GL_NONE );
|
|
R_DrawTorus( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments );
|
|
break;
|
|
case TE_BEAMDISK:
|
|
// FIXME VK GL_Cull( GL_NONE );
|
|
R_DrawDisk( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments );
|
|
break;
|
|
case TE_BEAMCYLINDER:
|
|
// FIXME VK GL_Cull( GL_NONE );
|
|
R_DrawCylinder( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments );
|
|
break;
|
|
case TE_BEAMPOINTS:
|
|
case TE_BEAMHOSE:
|
|
R_DrawSegs( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments, pbeam->flags, color, texturenum, render_mode );
|
|
break;
|
|
case TE_BEAMFOLLOW:
|
|
// FIXME VK TriBegin( TRI_QUADS );
|
|
R_DrawBeamFollow( pbeam, frametime );
|
|
break;
|
|
case TE_BEAMRING:
|
|
// FIXME VK GL_Cull( GL_NONE );
|
|
R_DrawRing( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments );
|
|
break;
|
|
}
|
|
|
|
VK_RenderDebugLabelEnd();
|
|
|
|
// FIXME VK r_stats.c_view_beams_count++;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_BeamSetAttributes
|
|
|
|
set beam attributes
|
|
==============
|
|
*/
|
|
static void R_BeamSetAttributes( BEAM *pbeam, float r, float g, float b, float framerate, int startFrame )
|
|
{
|
|
pbeam->frame = (float)startFrame;
|
|
pbeam->frameRate = framerate;
|
|
pbeam->r = r;
|
|
pbeam->g = g;
|
|
pbeam->b = b;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_BeamSetup
|
|
|
|
generic function. all beams must be
|
|
passed through this
|
|
==============
|
|
*/
|
|
static void R_BeamSetup( BEAM *pbeam, vec3_t start, vec3_t end, int modelIndex, float life, float width, float amplitude, float brightness, float speed )
|
|
{
|
|
model_t *sprite = gEngine.pfnGetModelByIndex( modelIndex );
|
|
|
|
if( !sprite ) return;
|
|
|
|
pbeam->type = BEAM_POINTS;
|
|
pbeam->modelIndex = modelIndex;
|
|
pbeam->frame = 0;
|
|
pbeam->frameRate = 0;
|
|
pbeam->frameCount = sprite->numframes;
|
|
|
|
VectorCopy( start, pbeam->source );
|
|
VectorCopy( end, pbeam->target );
|
|
VectorSubtract( end, start, pbeam->delta );
|
|
|
|
pbeam->freq = speed * gpGlobals->time;
|
|
pbeam->die = life + gpGlobals->time;
|
|
pbeam->amplitude = amplitude;
|
|
pbeam->brightness = brightness;
|
|
pbeam->width = width;
|
|
pbeam->speed = speed;
|
|
|
|
if( amplitude >= 0.50f )
|
|
pbeam->segments = VectorLength( pbeam->delta ) * 0.25f + 3.0f; // one per 4 pixels
|
|
else pbeam->segments = VectorLength( pbeam->delta ) * 0.075f + 3.0f; // one per 16 pixels
|
|
|
|
pbeam->pFollowModel = NULL;
|
|
pbeam->flags = 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_BeamDrawCustomEntity
|
|
|
|
initialize beam from server entity
|
|
==============
|
|
*/
|
|
void R_BeamDrawCustomEntity( cl_entity_t *ent, float frametime )
|
|
{
|
|
BEAM beam;
|
|
float amp = ent->curstate.body / 100.0f;
|
|
float blend = CL_FxBlend( ent ) / 255.0f;
|
|
float r, g, b;
|
|
int beamFlags;
|
|
|
|
r = ent->curstate.rendercolor.r / 255.0f;
|
|
g = ent->curstate.rendercolor.g / 255.0f;
|
|
b = ent->curstate.rendercolor.b / 255.0f;
|
|
|
|
R_BeamSetup( &beam, ent->origin, ent->angles, ent->curstate.modelindex, 0, ent->curstate.scale, amp, blend, ent->curstate.animtime );
|
|
R_BeamSetAttributes( &beam, r, g, b, ent->curstate.framerate, ent->curstate.frame );
|
|
beam.pFollowModel = NULL;
|
|
|
|
switch( ent->curstate.rendermode & 0x0F )
|
|
{
|
|
case BEAM_ENTPOINT:
|
|
beam.type = TE_BEAMPOINTS;
|
|
if( ent->curstate.sequence )
|
|
{
|
|
SetBits( beam.flags, FBEAM_STARTENTITY );
|
|
beam.startEntity = ent->curstate.sequence;
|
|
}
|
|
if( ent->curstate.skin )
|
|
{
|
|
SetBits( beam.flags, FBEAM_ENDENTITY );
|
|
beam.endEntity = ent->curstate.skin;
|
|
}
|
|
break;
|
|
case BEAM_ENTS:
|
|
beam.type = TE_BEAMPOINTS;
|
|
SetBits( beam.flags, FBEAM_STARTENTITY | FBEAM_ENDENTITY );
|
|
beam.startEntity = ent->curstate.sequence;
|
|
beam.endEntity = ent->curstate.skin;
|
|
break;
|
|
case BEAM_HOSE:
|
|
beam.type = TE_BEAMHOSE;
|
|
break;
|
|
case BEAM_POINTS:
|
|
// already set up
|
|
break;
|
|
}
|
|
|
|
beamFlags = ( ent->curstate.rendermode & 0xF0 );
|
|
|
|
if( FBitSet( beamFlags, BEAM_FSINE ))
|
|
SetBits( beam.flags, FBEAM_SINENOISE );
|
|
|
|
if( FBitSet( beamFlags, BEAM_FSOLID ))
|
|
SetBits( beam.flags, FBEAM_SOLID );
|
|
|
|
if( FBitSet( beamFlags, BEAM_FSHADEIN ))
|
|
SetBits( beam.flags, FBEAM_SHADEIN );
|
|
|
|
if( FBitSet( beamFlags, BEAM_FSHADEOUT ))
|
|
SetBits( beam.flags, FBEAM_SHADEOUT );
|
|
|
|
// draw it
|
|
R_BeamDraw( &beam, frametime );
|
|
}
|
|
|