From f34db629408956ade0f7dda77f434092df78ead2 Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 30 Mar 2019 20:24:58 +0700 Subject: [PATCH] ref_soft: Add sprite, EFX --- r_beams.c | 1313 +++++++++++++++++++++++++++++++++++++++++++++++++++ r_context.c | 41 +- r_draw.c | 10 +- r_image.c | 4 +- r_local.h | 17 +- r_main.c | 21 +- r_part.c | 300 ++++++++++++ r_sprite.c | 1087 ++++++++++++++++++++++++++++++++++++++++++ r_triapi.c | 15 +- 9 files changed, 2753 insertions(+), 55 deletions(-) create mode 100644 r_beams.c create mode 100644 r_part.c create mode 100644 r_sprite.c diff --git a/r_beams.c b/r_beams.c new file mode 100644 index 00000000..759b751a --- /dev/null +++ b/r_beams.c @@ -0,0 +1,1313 @@ +/* +gl_beams.c - beams rendering +Copyright (C) 2009 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "r_local.h" +#include "r_efx.h" +#include "event_flags.h" +#include "entity_types.h" +#include "triangleapi.h" +#include "customentity.h" +#include "cl_tent.h" +#include "pm_local.h" + +#include "studio.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 * gEngfuncs.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 / (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( RI.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, RI.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; + return false; +/* + 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; + } + + // check bbox + if( gEngfuncs.Mod_BoxVisible( mins, maxs, Mod_GetCurrentVis( ))) + { + if( pvsOnly || !R_CullBox( mins, maxs )) + { + // beam is visible + return false; + } + } + + // beam is culled + return true; + */ +} + +/* +================ +CL_AddCustomBeam + +Add the beam that encoded as custom entity +================ +*/ +void CL_AddCustomBeam( cl_entity_t *pEnvBeam ) +{ + if( tr.draw_list->num_beam_entities >= MAX_VISIBLE_PACKET ) + { + gEngfuncs.Con_Printf( S_ERROR "Too many beams %d!\n", tr.draw_list->num_beam_entities ); + return; + } + + if( pEnvBeam ) + { + tr.draw_list->beam_entities[tr.draw_list->num_beam_entities] = pEnvBeam; + tr.draw_list->num_beam_entities++; + } +} + + +/* +============================================================== + +BEAM DRAW METHODS + +============================================================== +*/ +/* +================ +R_DrawSegs + +general code for drawing beams +================ +*/ +static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments, int flags ) +{ + int noiseIndex, noiseStep; + int i, total_segs, segs_drawn; + float div, length, fraction, factor; + float flMaxWidth, vLast, vStep, brightness; + vec3_t perp1, vLastNormal; + beamseg_t curSeg; + + 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.0; + } + + // 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; + + // 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 * length + freq, &s, &c ); + VectorMA( nextSeg.pos, (factor * s), RI.vup, nextSeg.pos ); + + // rotate the noise along the perpendicluar axis a bit to keep the bolt from looking diagonal + VectorMA( nextSeg.pos, (factor * c), RI.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 ); + + TriTexCoord2f( 0.0f, curSeg.texcoord ); + TriBrightness( brightness ); + //pglNormal3fv( vAveNormal ); + TriVertex3fv( vPoint1 ); + + TriTexCoord2f( 1.0f, curSeg.texcoord ); + TriBrightness( brightness ); + //pflNormal3fv( vAveNormal ); + TriVertex3fv( vPoint2 ); + } + + 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 ); + + // specify the points. + TriTexCoord2f( 0.0f, curSeg.texcoord ); + TriBrightness( brightness ); + //pglNormal3fv( vLastNormal ); + TriVertex3fv( vPoint1 ); + + TriTexCoord2f( 1.0f, curSeg.texcoord ); + TriBrightness( brightness ); + //pglNormal3fv( vLastNormal ); + TriVertex3fv( vPoint2 ); + } + + vLast += vStep; // Advance texture scroll (v axis only) + noiseIndex += noiseStep; + } +} + +/* +================ +R_DrawTorus + +Draw beamtours +================ +*/ +void R_DrawTorus( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments ) +{ + 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.01; + if( length < 0.5 ) length = 0.5; // 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, &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, RI.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 * 3 + freq ); + VectorMA( point, factor, RI.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( RI.vup, -tmp[0], normal ); // Build point along noraml line (normal is -y, x) + VectorMA( normal, tmp[1], RI.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; + } +} + +/* +================ +R_DrawDisk + +Draw beamdisk +================ +*/ +void R_DrawDisk( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments ) +{ + 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 ) * 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, &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) + } +} + +/* +================ +R_DrawCylinder + +Draw beam cylinder +================ +*/ +void R_DrawCylinder( vec3_t source, vec3_t delta, float width, float scale, float freq, float speed, int segments ) +{ + 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, &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) + } +} + +/* +============== +R_DrawBeamFollow + +drawi followed beam +============== +*/ +void R_DrawBeamFollow( BEAM *pbeam, float frametime ) +{ + particle_t *pnew, *particles; + float fraction, div, vLast, vStep, saved_fraction; + vec3_t last1, last2, tmp, screen, saved_last2; + vec3_t delta, screenLast, normal; + + gEngfuncs.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 = gEngfuncs.CL_AllocParticleFast(); + } + } + else + { + pnew = gEngfuncs.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( RI.vup, tmp[0], normal ); // Build point along normal line (normal is -y, x) + VectorMA( normal, tmp[1], RI.vright, normal ); + + // Make a wide line + VectorMA( delta, pbeam->width, normal, last1 ); + VectorMA( delta, -pbeam->width, normal, last2 ); + + div = 1.0 / pbeam->amplitude; + fraction = ( pbeam->die - gpGlobals->time ) * div; + + vLast = 0.0; + vStep = 1.0; + + while( particles ) + { + TriBrightness( fraction ); + TriTexCoord2f( 1, 1 ); + TriVertex3fv( last2 ); + TriBrightness( fraction ); + TriTexCoord2f( 0, 1 ); + TriVertex3fv( last1 ); + + VectorCopy( last2, saved_last2 ); + saved_fraction = fraction; + + // 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( RI.vup, tmp[0], normal ); // Build point along noraml line (normal is -y, x) + VectorMA( normal, tmp[1], RI.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( saved_fraction ); + TriTexCoord2f( 1.0f, 1.0f ); + TriVertex3fv( saved_last2 ); + + TriBrightness( fraction ); + TriTexCoord2f( 0.0f, 0.0f ); + 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; + } +} + +/* +================ +R_DrawRing + +Draw beamring +================ +*/ +void R_DrawRing( vec3_t source, vec3_t delta, float width, float amplitude, float freq, float speed, int segments ) +{ + 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; + + if( segments > NOISE_DIVISIONS * 8 ) + segments = NOISE_DIVISIONS * 8; + + length = VectorLength( delta ) * 0.01f * M_PI; + 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( !gEngfuncs.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, &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, RI.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 * 24 + freq ); + VectorMA( point, factor, RI.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( RI.vup, tmp[0], normal ); + VectorMA( normal, tmp[1], RI.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 = gEngfuncs.R_BeamGetEntity( beamEnt ); + + if( beamEnt < 0 ) + attach = BEAMENT_ATTACHMENT( -beamEnt ); + else attach = BEAMENT_ATTACHMENT( beamEnt ); + + if( !ent ) + { + gEngfuncs.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 == ENGINE_GET_PARM( PARM_PLAYER_INDEX ) ) + { + vec3_t simorg; + gEngfuncs.GetPredictedOrigin( simorg ); + VectorCopy( simorg, pt ); + } + else VectorCopy( ent->origin, pt ); + + return true; +} + +/* +============== +R_BeamRecomputeEndpoints + +Recomputes beam endpoints.. +============== +*/ +qboolean R_BeamRecomputeEndpoints( BEAM *pbeam ) +{ + if( FBitSet( pbeam->flags, FBEAM_STARTENTITY )) + { + cl_entity_t *start = gEngfuncs.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 = gEngfuncs.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; + + model = gEngfuncs.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, RI.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( RI.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; + } + } + + TriRenderMode( FBitSet( pbeam->flags, FBEAM_SOLID ) ? kRenderNormal : kRenderTransAdd ); + + if( !TriSpriteTexture( model, (int)(pbeam->frame + pbeam->frameRate * gpGlobals->time) % pbeam->frameCount )) + { + ClearBits( pbeam->flags, FBEAM_ISACTIVE ); + return; + } + + if( pbeam->type == TE_BEAMFOLLOW ) + { + cl_entity_t *pStart; + + // XASH SPECIFIC: get brightness from head entity + pStart = gEngfuncs.R_BeamGetEntity( pbeam->startEntity ); + if( pStart && pStart->curstate.rendermode != kRenderNormal ) + pbeam->brightness = gEngfuncs.CL_FxBlend( pStart ) / 255.0f; + } + + if( FBitSet( pbeam->flags, FBEAM_FADEIN )) + TriColor4f( pbeam->r, pbeam->g, pbeam->b, pbeam->t * pbeam->brightness ); + else if( FBitSet( pbeam->flags, FBEAM_FADEOUT )) + TriColor4f( pbeam->r, pbeam->g, pbeam->b, ( 1.0f - pbeam->t ) * pbeam->brightness ); + else TriColor4f( pbeam->r, pbeam->g, pbeam->b, pbeam->brightness ); + + switch( pbeam->type ) + { + case TE_BEAMTORUS: + //GL_Cull( GL_NONE ); + TriBegin( TRI_TRIANGLE_STRIP ); + R_DrawTorus( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments ); + TriEnd(); + break; + case TE_BEAMDISK: + //GL_Cull( GL_NONE ); + TriBegin( TRI_TRIANGLE_STRIP ); + R_DrawDisk( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments ); + TriEnd(); + break; + case TE_BEAMCYLINDER: + //GL_Cull( GL_NONE ); + TriBegin( TRI_TRIANGLE_STRIP ); + R_DrawCylinder( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments ); + TriEnd(); + break; + case TE_BEAMPOINTS: + case TE_BEAMHOSE: + TriBegin( TRI_TRIANGLE_STRIP ); + R_DrawSegs( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments, pbeam->flags ); + TriEnd(); + break; + case TE_BEAMFOLLOW: + TriBegin( TRI_TRIANGLES ); + R_DrawBeamFollow( pbeam, frametime ); + TriEnd(); + break; + case TE_BEAMRING: + //GL_Cull( GL_NONE ); + TriBegin( TRI_TRIANGLE_STRIP ); + R_DrawRing( pbeam->source, pbeam->delta, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments ); + TriEnd(); + break; + } + + //GL_Cull( GL_FRONT ); + 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 = gEngfuncs.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 ) +{ + BEAM beam; + float amp = ent->curstate.body / 100.0f; + float blend = gEngfuncs.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, tr.frametime ); +} + + +/* +============== +CL_DrawBeams + +draw beam loop +============== +*/ +void CL_DrawBeams( int fTrans, BEAM *active_beams ) +{ + BEAM *pBeam; + int i, flags; + + //pglShadeModel( GL_SMOOTH ); + //pglDepthMask( fTrans ? GL_FALSE : GL_TRUE ); + + // server beams don't allocate beam chains + // all params are stored in cl_entity_t + for( i = 0; i < tr.draw_list->num_beam_entities; i++ ) + { + RI.currentbeam = tr.draw_list->beam_entities[i]; + flags = RI.currentbeam->curstate.rendermode & 0xF0; + + if( fTrans && FBitSet( flags, FBEAM_SOLID )) + continue; + + if( !fTrans && !FBitSet( flags, FBEAM_SOLID )) + continue; + + R_BeamDrawCustomEntity( RI.currentbeam ); + r_stats.c_view_beams_count++; + } + + RI.currentbeam = NULL; + + // draw temporary entity beams + for( pBeam = active_beams; pBeam; pBeam = pBeam->next ) + { + if( fTrans && FBitSet( pBeam->flags, FBEAM_SOLID )) + continue; + + if( !fTrans && !FBitSet( pBeam->flags, FBEAM_SOLID )) + continue; + + R_BeamDraw( pBeam, gpGlobals->time - gpGlobals->oldtime ); + } + + //pglShadeModel( GL_FLAT ); + //pglDepthMask( GL_TRUE ); +} diff --git a/r_context.c b/r_context.c index 9e3f0307..ceeede80 100644 --- a/r_context.c +++ b/r_context.c @@ -132,10 +132,10 @@ qboolean Mod_ProcessRenderData( model_t *mod, qboolean create, const byte *buf ) switch( mod->type ) { case mod_studio: - // Mod_LoadStudioModel( mod, buf, loaded ); + //Mod_LoadStudioModel( mod, buf, loaded ); break; case mod_sprite: - //Mod_LoadSpriteModel( mod, buf, &loaded, mod->numtexinfo ); + Mod_LoadSpriteModel( mod, buf, &loaded, mod->numtexinfo ); break; case mod_alias: //Mod_LoadAliasModel( mod, buf, &loaded ); @@ -306,7 +306,7 @@ void Mod_UnloadTextures( model_t *mod ) Mod_BrushUnloadTextures( mod ); break; case mod_sprite: - //Mod_SpriteUnloadTextures( mod->cache.data ); + Mod_SpriteUnloadTextures( mod->cache.data ); break; default: gEngfuncs.Host_Error( "Mod_UnloadModel: unsupported type %d\n", mod->type ); } @@ -357,11 +357,6 @@ void GL_SetRenderMode(int mode) /// maybe, setup block drawing function pointers here } -void CL_AddCustomBeam(cl_entity_t *pEnvBeam) -{ - // same for beams -} - void R_ShowTextures() { // textures undone too @@ -397,31 +392,6 @@ void GL_SubdivideSurface(msurface_t *fa) } -void Mod_LoadMapSprite(model_t *mod, const void *buffer, size_t size, qboolean *loaded) -{ - -} - -void CL_DrawParticles(double frametime, particle_t *cl_active_particles, float partsize) -{ - -} - -void CL_DrawBeams(int fTrans, BEAM *active_beams) -{ - -} - -void CL_DrawTracers(double frametime, particle_t *cl_active_tracers) -{ - -} - -qboolean R_BeamCull(const vec3_t start, const vec3_t end, qboolean pvsOnly) -{ - return false; -} - void DrawSingleDecal(decal_t *pDecal, msurface_t *fa) { @@ -457,11 +427,6 @@ void GL_TextureTarget(uint target) } -void CL_DrawParticlesExternal(const ref_viewpass_t *rvp, qboolean trans_pass, float frametime) -{ - // no renderapi support -} - void GL_BuildLightmaps() { CL_RunLightStyles(); diff --git a/r_draw.c b/r_draw.c index cd6aa659..8cd3c79b 100644 --- a/r_draw.c +++ b/r_draw.c @@ -42,11 +42,11 @@ void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int cu mspriteframe_t *pFrame; if( !pSprite || pSprite->type != mod_sprite ) return; // bad model ? - //pFrame = R_GetSpriteFrame( pSprite, currentFrame, 0.0f ); + pFrame = R_GetSpriteFrame( pSprite, currentFrame, 0.0f ); - //if( frameWidth ) *frameWidth = pFrame->width; -// if( frameHeight ) *frameHeight = pFrame->height; - //if( numFrames ) *numFrames = pSprite->numframes; + if( frameWidth ) *frameWidth = pFrame->width; + if( frameHeight ) *frameHeight = pFrame->height; + if( numFrames ) *numFrames = pSprite->numframes; } int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame ) @@ -54,7 +54,7 @@ int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame ) if( !m_pSpriteModel || m_pSpriteModel->type != mod_sprite || !m_pSpriteModel->cache.data ) return 0; - return 0;//R_GetSpriteFrame( m_pSpriteModel, frame, 0.0f )->gl_texturenum; + return R_GetSpriteFrame( m_pSpriteModel, frame, 0.0f )->gl_texturenum; } diff --git a/r_image.c b/r_image.c index e72903fb..bf577aaf 100644 --- a/r_image.c +++ b/r_image.c @@ -502,7 +502,7 @@ static qboolean GL_UploadTexture( image_t *tex, rgbdata_t *pic ) GL_SetTextureDimensions( tex, pic->width, pic->height, pic->depth ); GL_SetTextureFormat( tex, pic->type, pic->flags ); - gEngfuncs.Con_Printf("%s %d %d\n", tex->name, tex->width, tex->height ); + //gEngfuncs.Con_Printf("%s %d %d\n", tex->name, tex->width, tex->height ); Assert( pic != NULL ); Assert( tex != NULL ); @@ -533,7 +533,7 @@ static qboolean GL_UploadTexture( image_t *tex, rgbdata_t *pic ) //GL_TextureImageRAW( tex, i, j, width, height, tex->depth, pic->type, data ); // increase size to workaround triangle renderer bugs // it seems to assume memory readable. maybe it was pointed to WAD? - tex->pixels[j] = (byte*)Mem_Calloc( r_temppool, width * height * sizeof(pixel_t) + 256 ) + 128; + tex->pixels[j] = (byte*)Mem_Calloc( r_temppool, width * height * sizeof(pixel_t) + 1024 ) + 512; if( j == 0 && tex->flags & TF_HAS_ALPHA ) tex->alpha_pixels = (byte*)Mem_Calloc( r_temppool, width * height * sizeof(pixel_t) + 256 ) + 128; diff --git a/r_local.h b/r_local.h index 6f843611..546b6b79 100644 --- a/r_local.h +++ b/r_local.h @@ -394,13 +394,13 @@ void GL_Cull( unsigned int cull ); void R_ShowTextures( void ); void R_ShowTree( void ); void SCR_TimeRefresh_f( void ); - +#endif // // gl_beams.c // void CL_DrawBeams( int fTrans, BEAM *active_beams ); qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly ); - +#if 0 // // gl_cull.c // @@ -534,7 +534,7 @@ void GL_ResetFogColor( void ); void R_GenerateVBO(); void R_ClearVBO(); void R_AddDecalVBO( decal_t *pdecal, msurface_t *surf ); - +#endif // // gl_rpart.c // @@ -550,7 +550,7 @@ void R_SpriteInit( void ); void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, uint texFlags ); mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw ); void R_DrawSpriteModel( cl_entity_t *e ); -#endif + // // gl_studio.c // @@ -679,7 +679,7 @@ void TriVertex3fv( const float *v ); void TriVertex3f( float x, float y, float z ); void _TriColor4f( float r, float g, float b, float a ); void TriColor4ub( byte r, byte g, byte b, byte a ); -int TriWorldToScreen( float *world, float *screen ); +int TriWorldToScreen( const float *world, float *screen ); int TriSpriteTexture( model_t *pSpriteModel, int frame ); void TriFog( float flFogColor[3], float flStart, float flEnd, int bOn ); void TriGetMatrix( const int pname, float *matrix ); @@ -1225,10 +1225,15 @@ extern cvar_t *sw_surfcacheoverride; extern cvar_t *sw_waterwarp; extern cvar_t *sw_texfilt; extern cvar_t *r_decals; - +extern cvar_t *r_traceglow; extern cvar_t *sw_notransbrushes; extern cvar_t *sw_noalphabrushes; +extern cvar_t *tracerred; +extern cvar_t *tracergreen; +extern cvar_t *tracerblue; +extern cvar_t *traceralpha; + extern vec3_t modelorg; diff --git a/r_main.c b/r_main.c index 13b1deef..6283efae 100644 --- a/r_main.c +++ b/r_main.c @@ -94,6 +94,12 @@ cvar_t *r_lerpmodels; cvar_t *r_novis; cvar_t *r_lightmap; cvar_t *r_dynamic; +cvar_t *r_traceglow; + +cvar_t *tracerred; +cvar_t *tracergreen; +cvar_t *tracerblue; +cvar_t *traceralpha; cvar_t *r_speeds; cvar_t *r_lightlevel; //FIXME HACK @@ -1091,6 +1097,7 @@ void R_DrawEntitiesOnList( void ) // GL_CheckForErrors(); + R_SetUpWorldTransform(); // draw sprites seperately, because of alpha blending for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ ) { @@ -1103,7 +1110,7 @@ void R_DrawEntitiesOnList( void ) switch( RI.currentmodel->type ) { case mod_sprite: - // R_DrawSpriteModel( RI.currententity ); + R_DrawSpriteModel( RI.currententity ); break; } } @@ -1133,7 +1140,7 @@ void R_DrawEntitiesOnList( void ) tr.blend = gEngfuncs.CL_FxBlend( RI.currententity ) / 255.0f; else tr.blend = 1.0f; // draw as solid but sorted by distance - //if( tr.blend <= 0.0f ) continue; + if( tr.blend <= 0.0f ) continue; Assert( RI.currententity != NULL ); Assert( RI.currentmodel != NULL ); @@ -1151,7 +1158,8 @@ void R_DrawEntitiesOnList( void ) R_DrawStudioModel( RI.currententity ); break; case mod_sprite: - // R_DrawSpriteModel( RI.currententity ); + R_SetUpWorldTransform(); + R_DrawSpriteModel( RI.currententity ); break; default: break; @@ -1963,6 +1971,7 @@ qboolean R_Init() sw_waterwarp = gEngfuncs.Cvar_Get ("sw_waterwarp", "1", 0, ""); sw_notransbrushes = gEngfuncs.Cvar_Get( "sw_notransbrushes", "0", FCVAR_ARCHIVE, "do not apply transparency to water/glasses (faster)"); sw_noalphabrushes = gEngfuncs.Cvar_Get( "sw_noalphabrushes", "0", FCVAR_ARCHIVE, "do not draw brush holes (faster)"); + r_traceglow = gEngfuncs.Cvar_Get( "r_traceglow", "1", FCVAR_ARCHIVE, "cull flares behind models" ); sw_texfilt = gEngfuncs.Cvar_Get ("sw_texfilt", "0", 0, "texture dither"); //r_lefthand = ri.Cvar_Get( "hand", "0", FCVAR_USERINFO | FCVAR_ARCHIVE ); @@ -1974,6 +1983,11 @@ qboolean R_Init() //r_lerpmodels = ri.Cvar_Get( "r_lerpmodels", "1", 0 ); r_novis = gEngfuncs.Cvar_Get( "r_novis", "0", 0, "" ); + tracerred = gEngfuncs.Cvar_Get( "tracerred", "0.8", 0, "tracer red component weight ( 0 - 1.0 )" ); + tracergreen = gEngfuncs.Cvar_Get( "tracergreen", "0.8", 0, "tracer green component weight ( 0 - 1.0 )" ); + tracerblue = gEngfuncs.Cvar_Get( "tracerblue", "0.4", 0, "tracer blue component weight ( 0 - 1.0 )" ); + traceralpha = gEngfuncs.Cvar_Get( "traceralpha", "0.5", 0, "tracer alpha amount ( 0 - 1.0 )" ); + // create the window and set up the context r_temppool = Mem_AllocPool( "ref_sw zone" ); @@ -1998,6 +2012,7 @@ qboolean R_Init() view_clipplanes[1].leftedge = view_clipplanes[2].leftedge =view_clipplanes[3].leftedge = false; view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = view_clipplanes[3].rightedge = false; R_StudioInit(); + R_SpriteInit(); R_InitTurb(); return true; diff --git a/r_part.c b/r_part.c new file mode 100644 index 00000000..02c10ab3 --- /dev/null +++ b/r_part.c @@ -0,0 +1,300 @@ +/* +cl_part.c - particles and tracers +Copyright (C) 2010 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "r_local.h" +#include "r_efx.h" +#include "event_flags.h" +#include "entity_types.h" +#include "triangleapi.h" +#include "pm_local.h" +#include "cl_tent.h" +#include "studio.h" + +static float gTracerSize[11] = { 1.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; +static color24 gTracerColors[] = +{ +{ 255, 255, 255 }, // White +{ 255, 0, 0 }, // Red +{ 0, 255, 0 }, // Green +{ 0, 0, 255 }, // Blue +{ 0, 0, 0 }, // Tracer default, filled in from cvars, etc. +{ 255, 167, 17 }, // Yellow-orange sparks +{ 255, 130, 90 }, // Yellowish streaks (garg) +{ 55, 60, 144 }, // Blue egon streak +{ 255, 130, 90 }, // More Yellowish streaks (garg) +{ 255, 140, 90 }, // More Yellowish streaks (garg) +{ 200, 130, 90 }, // More red streaks (garg) +{ 255, 120, 70 }, // Darker red streaks (garg) +}; + +/* +================ +CL_DrawParticles + +update particle color, position, free expired and draw it +================ +*/ +void CL_DrawParticles( double frametime, particle_t *cl_active_particles, float partsize ) +{ + particle_t *p; + vec3_t right, up; + color24 *pColor; + int alpha; + float size; + + if( !cl_active_particles ) + return; // nothing to draw? + + //pglEnable( GL_BLEND ); + //pglDisable( GL_ALPHA_TEST ); + //pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + GL_Bind( XASH_TEXTURE0, tr.particleTexture ); + //pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + //pglDepthMask( GL_FALSE ); + + TriBegin( TRI_QUADS ); + + for( p = cl_active_particles; p; p = p->next ) + { + if(( p->type != pt_blob ) || ( p->packedColor == 255 )) + { + size = partsize; // get initial size of particle + + // scale up to keep particles from disappearing + size += (p->org[0] - RI.vieworg[0]) * RI.cull_vforward[0]; + size += (p->org[1] - RI.vieworg[1]) * RI.cull_vforward[1]; + size += (p->org[2] - RI.vieworg[2]) * RI.cull_vforward[2]; + + if( size < 20.0f ) size = partsize; + else size = partsize + size * 0.002f; + + // scale the axes by radius + VectorScale( RI.cull_vright, size, right ); + VectorScale( RI.cull_vup, size, up ); + + p->color = bound( 0, p->color, 255 ); + pColor = gEngfuncs.CL_GetPaletteColor( p->color ); + + alpha = 255 * (p->die - gpGlobals->time) * 16.0f; + if( alpha > 255 || p->type == pt_static ) + alpha = 255; + + TriColor4ub( gEngfuncs.LightToTexGamma( pColor->r ), + gEngfuncs.LightToTexGamma( pColor->g ), + gEngfuncs.LightToTexGamma( pColor->b ), alpha ); + + TriTexCoord2f( 0.0f, 1.0f ); + TriVertex3f( p->org[0] - right[0] + up[0], p->org[1] - right[1] + up[1], p->org[2] - right[2] + up[2] ); + TriTexCoord2f( 0.0f, 0.0f ); + TriVertex3f( p->org[0] + right[0] + up[0], p->org[1] + right[1] + up[1], p->org[2] + right[2] + up[2] ); + TriTexCoord2f( 1.0f, 0.0f ); + TriVertex3f( p->org[0] + right[0] - up[0], p->org[1] + right[1] - up[1], p->org[2] + right[2] - up[2] ); + TriTexCoord2f( 1.0f, 1.0f ); + TriVertex3f( p->org[0] - right[0] - up[0], p->org[1] - right[1] - up[1], p->org[2] - right[2] - up[2] ); + r_stats.c_particle_count++; + } + + gEngfuncs.CL_ThinkParticle( frametime, p ); + } + + TriEnd(); + //pglDepthMask( GL_TRUE ); +} + +/* +================ +CL_CullTracer + +check tracer bbox +================ +*/ +static qboolean CL_CullTracer( particle_t *p, const vec3_t start, const vec3_t end ) +{ + vec3_t mins, maxs; + int i; + return false; +/* + // compute the bounding box + 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] += gTracerSize[p->type] * 2.0f; + } + } + + // check bbox + return R_CullBox( mins, maxs );*/ +} + +/* +================ +CL_DrawTracers + +update tracer color, position, free expired and draw it +================ +*/ +void CL_DrawTracers( double frametime, particle_t *cl_active_tracers ) +{ + float scale, atten, gravity; + vec3_t screenLast, screen; + vec3_t start, end, delta; + particle_t *p; + + // update tracer color if this is changed + if( FBitSet( tracerred->flags|tracergreen->flags|tracerblue->flags|traceralpha->flags, FCVAR_CHANGED )) + { + color24 *customColors = &gTracerColors[4]; + customColors->r = (byte)(tracerred->value * traceralpha->value * 255); + customColors->g = (byte)(tracergreen->value * traceralpha->value * 255); + customColors->b = (byte)(tracerblue->value * traceralpha->value * 255); + ClearBits( tracerred->flags, FCVAR_CHANGED ); + ClearBits( tracergreen->flags, FCVAR_CHANGED ); + ClearBits( tracerblue->flags, FCVAR_CHANGED ); + ClearBits( traceralpha->flags, FCVAR_CHANGED ); + } + + if( !cl_active_tracers ) + return; // nothing to draw? + + if( !TriSpriteTexture( gEngfuncs.GetDefaultSprite( REF_DOT_SPRITE ), 0 )) + return; + + //pglEnable( GL_BLEND ); + //pglBlendFunc( GL_SRC_ALPHA, GL_ONE ); + //pglDisable( GL_ALPHA_TEST ); + //pglDepthMask( GL_FALSE ); + + gravity = frametime * MOVEVARS->gravity; + scale = 1.0 - (frametime * 0.9); + if( scale < 0.0f ) scale = 0.0f; + + for( p = cl_active_tracers; p; p = p->next ) + { + atten = (p->die - gpGlobals->time); + if( atten > 0.1f ) atten = 0.1f; + + VectorScale( p->vel, ( p->ramp * atten ), delta ); + VectorAdd( p->org, delta, end ); + VectorCopy( p->org, start ); + + if( !CL_CullTracer( p, start, end )) + { + vec3_t verts[4], tmp2; + vec3_t tmp, normal; + color24 *pColor; + + // Transform point into screen space + TriWorldToScreen( start, screen ); + TriWorldToScreen( end, screenLast ); + + // 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( RI.cull_vup, tmp[0] * gTracerSize[p->type], normal ); + VectorScale( RI.cull_vright, -tmp[1] * gTracerSize[p->type], tmp2 ); + VectorSubtract( normal, tmp2, normal ); + + // compute four vertexes + VectorSubtract( start, normal, verts[0] ); + VectorAdd( start, normal, verts[1] ); + VectorAdd( verts[0], delta, verts[2] ); + VectorAdd( verts[1], delta, verts[3] ); + + if( p->color > sizeof( gTracerColors ) / sizeof( color24 ) ) + { + gEngfuncs.Con_Printf( S_ERROR "UserTracer with color > %d\n", sizeof( gTracerColors ) / sizeof( color24 )); + p->color = 0; + } + + pColor = &gTracerColors[p->color]; + TriColor4ub( pColor->r, pColor->g, pColor->b, p->packedColor ); + + TriBegin( TRI_QUADS ); + TriTexCoord2f( 0.0f, 0.8f ); + TriVertex3fv( verts[2] ); + TriTexCoord2f( 1.0f, 0.8f ); + TriVertex3fv( verts[3] ); + TriTexCoord2f( 1.0f, 0.0f ); + TriVertex3fv( verts[1] ); + TriTexCoord2f( 0.0f, 0.0f ); + TriVertex3fv( verts[0] ); + TriEnd(); + } + + // evaluate position + VectorMA( p->org, frametime, p->vel, p->org ); + + if( p->type == pt_grav ) + { + p->vel[0] *= scale; + p->vel[1] *= scale; + p->vel[2] -= gravity; + + p->packedColor = 255 * (p->die - gpGlobals->time) * 2; + if( p->packedColor > 255 ) p->packedColor = 255; + } + else if( p->type == pt_slowgrav ) + { + p->vel[2] = gravity * 0.05; + } + } + + //pglDepthMask( GL_TRUE ); +} + +/* +=============== +CL_DrawParticlesExternal + +allow to draw effects from custom renderer +=============== +*/ +void CL_DrawParticlesExternal( const ref_viewpass_t *rvp, qboolean trans_pass, float frametime ) +{ + ref_instance_t oldRI = RI; + + memcpy( &oldRI, &RI, sizeof( ref_instance_t )); + R_SetupRefParams( rvp ); + R_SetupFrustum(); +// R_SetupGL( false ); // don't touch GL-states + + // setup PVS for frame + memcpy( RI.visbytes, tr.visbytes, gpGlobals->visbytes ); + tr.frametime = frametime; + + gEngfuncs.CL_DrawEFX( frametime, trans_pass ); + + // restore internal state + memcpy( &RI, &oldRI, sizeof( ref_instance_t )); +} diff --git a/r_sprite.c b/r_sprite.c new file mode 100644 index 00000000..b6afac8e --- /dev/null +++ b/r_sprite.c @@ -0,0 +1,1087 @@ +/* +gl_sprite.c - sprite rendering +Copyright (C) 2010 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "r_local.h" +#include "pm_local.h" +#include "sprite.h" +#include "studio.h" +#include "entity_types.h" +//#include "cl_tent.h" + +// it's a Valve default value for LoadMapSprite (probably must be power of two) +#define MAPSPRITE_SIZE 128 +#define GLARE_FALLOFF 19000.0f + +cvar_t *r_sprite_lerping; +cvar_t *r_sprite_lighting; +char sprite_name[MAX_QPATH]; +char group_suffix[8]; +static uint r_texFlags = 0; +static int sprite_version; +float sprite_radius; + +/* +==================== +R_SpriteInit + +==================== +*/ +void R_SpriteInit( void ) +{ + r_sprite_lerping = gEngfuncs.Cvar_Get( "r_sprite_lerping", "1", FCVAR_ARCHIVE, "enables sprite animation lerping" ); + r_sprite_lighting = gEngfuncs.Cvar_Get( "r_sprite_lighting", "1", FCVAR_ARCHIVE, "enables sprite lighting (blood etc)" ); +} + +/* +==================== +R_SpriteLoadFrame + +upload a single frame +==================== +*/ +static dframetype_t *R_SpriteLoadFrame( model_t *mod, void *pin, mspriteframe_t **ppframe, int num ) +{ + dspriteframe_t *pinframe; + mspriteframe_t *pspriteframe; + int gl_texturenum = 0; + char texname[128]; + int bytes = 1; + + pinframe = (dspriteframe_t *)pin; + if( sprite_version == SPRITE_VERSION_32 ) + bytes = 4; + + // build uinque frame name + if( FBitSet( mod->flags, MODEL_CLIENT )) // it's a HUD sprite + { + Q_snprintf( texname, sizeof( texname ), "#HUD/%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height * bytes, r_texFlags ); + } + else + { + Q_snprintf( texname, sizeof( texname ), "#%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height * bytes, r_texFlags ); + } + + // setup frame description + pspriteframe = Mem_Malloc( mod->mempool, sizeof( mspriteframe_t )); + pspriteframe->width = pinframe->width; + pspriteframe->height = pinframe->height; + pspriteframe->up = pinframe->origin[1]; + pspriteframe->left = pinframe->origin[0]; + pspriteframe->down = pinframe->origin[1] - pinframe->height; + pspriteframe->right = pinframe->width + pinframe->origin[0]; + pspriteframe->gl_texturenum = gl_texturenum; + *ppframe = pspriteframe; + + return (dframetype_t *)((byte *)(pinframe + 1) + pinframe->width * pinframe->height * bytes ); +} + +/* +==================== +R_SpriteLoadGroup + +upload a group frames +==================== +*/ +static dframetype_t *R_SpriteLoadGroup( model_t *mod, void *pin, mspriteframe_t **ppframe, int framenum ) +{ + dspritegroup_t *pingroup; + mspritegroup_t *pspritegroup; + dspriteinterval_t *pin_intervals; + float *poutintervals; + int i, groupsize, numframes; + void *ptemp; + + pingroup = (dspritegroup_t *)pin; + numframes = pingroup->numframes; + + groupsize = sizeof( mspritegroup_t ) + (numframes - 1) * sizeof( pspritegroup->frames[0] ); + pspritegroup = Mem_Calloc( mod->mempool, groupsize ); + pspritegroup->numframes = numframes; + + *ppframe = (mspriteframe_t *)pspritegroup; + pin_intervals = (dspriteinterval_t *)(pingroup + 1); + poutintervals = Mem_Calloc( mod->mempool, numframes * sizeof( float )); + pspritegroup->intervals = poutintervals; + + for( i = 0; i < numframes; i++ ) + { + *poutintervals = pin_intervals->interval; + if( *poutintervals <= 0.0f ) + *poutintervals = 1.0f; // set error value + poutintervals++; + pin_intervals++; + } + + ptemp = (void *)pin_intervals; + for( i = 0; i < numframes; i++ ) + { + ptemp = R_SpriteLoadFrame( mod, ptemp, &pspritegroup->frames[i], framenum * 10 + i ); + } + + return (dframetype_t *)ptemp; +} + +/* +==================== +Mod_LoadSpriteModel + +load sprite model +==================== +*/ +void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, uint texFlags ) +{ + const dsprite_t *pin; + const short *numi = NULL; + const dframetype_t *pframetype; + msprite_t *psprite; + int i; + + pin = buffer; + psprite = mod->cache.data; + + if( pin->version == SPRITE_VERSION_Q1 || pin->version == SPRITE_VERSION_32 ) + numi = NULL; + else if( pin->version == SPRITE_VERSION_HL ) + numi = (const short *)((const byte*)buffer + sizeof( dsprite_hl_t )); + + r_texFlags = texFlags; + sprite_version = pin->version; + Q_strncpy( sprite_name, mod->name, sizeof( sprite_name )); + COM_StripExtension( sprite_name ); + + if( numi == NULL ) + { + rgbdata_t *pal; + + pal = gEngfuncs.FS_LoadImage( "#id.pal", (byte *)&i, 768 ); + pframetype = (const dframetype_t *)((const byte*)buffer + sizeof( dsprite_q1_t )); // pinq1 + 1 + gEngfuncs.FS_FreeImage( pal ); // palette installed, no reason to keep this data + } + else if( *numi == 256 ) + { + const byte *src = (const byte *)(numi+1); + rgbdata_t *pal; + + // install palette + switch( psprite->texFormat ) + { + case SPR_INDEXALPHA: + pal = gEngfuncs.FS_LoadImage( "#gradient.pal", src, 768 ); + break; + case SPR_ALPHTEST: + pal = gEngfuncs.FS_LoadImage( "#masked.pal", src, 768 ); + break; + default: + pal = gEngfuncs.FS_LoadImage( "#normal.pal", src, 768 ); + break; + } + + pframetype = (const dframetype_t *)(src + 768); + gEngfuncs.FS_FreeImage( pal ); // palette installed, no reason to keep this data + } + else + { + gEngfuncs.Con_DPrintf( S_ERROR "%s has wrong number of palette colors %i (should be 256)\n", mod->name, *numi ); + return; + } + + if( mod->numframes < 1 ) + return; + + for( i = 0; i < mod->numframes; i++ ) + { + frametype_t frametype = pframetype->type; + psprite->frames[i].type = frametype; + + switch( frametype ) + { + case FRAME_SINGLE: + Q_strncpy( group_suffix, "frame", sizeof( group_suffix )); + pframetype = R_SpriteLoadFrame( mod, pframetype + 1, &psprite->frames[i].frameptr, i ); + break; + case FRAME_GROUP: + Q_strncpy( group_suffix, "group", sizeof( group_suffix )); + pframetype = R_SpriteLoadGroup( mod, pframetype + 1, &psprite->frames[i].frameptr, i ); + break; + case FRAME_ANGLED: + Q_strncpy( group_suffix, "angle", sizeof( group_suffix )); + pframetype = R_SpriteLoadGroup( mod, pframetype + 1, &psprite->frames[i].frameptr, i ); + break; + } + if( pframetype == NULL ) break; // technically an error + } + + if( loaded ) *loaded = true; // done +} + +/* +==================== +Mod_LoadMapSprite + +Loading a bitmap image as sprite with multiple frames +as pieces of input image +==================== +*/ +void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean *loaded ) +{ + byte *src, *dst; + rgbdata_t *pix, temp; + char texname[128]; + int i, j, x, y, w, h; + int xl, yl, xh, yh; + int linedelta, numframes; + mspriteframe_t *pspriteframe; + msprite_t *psprite; + + if( loaded ) *loaded = false; + Q_snprintf( texname, sizeof( texname ), "#%s", mod->name ); + gEngfuncs.Image_SetForceFlags( IL_OVERVIEW ); + pix = gEngfuncs.FS_LoadImage( texname, buffer, size ); + gEngfuncs.Image_ClearForceFlags(); + if( !pix ) return; // bad image or something else + + mod->type = mod_sprite; + r_texFlags = 0; // no custom flags for map sprites + + if( pix->width % MAPSPRITE_SIZE ) + w = pix->width - ( pix->width % MAPSPRITE_SIZE ); + else w = pix->width; + + if( pix->height % MAPSPRITE_SIZE ) + h = pix->height - ( pix->height % MAPSPRITE_SIZE ); + else h = pix->height; + + if( w < MAPSPRITE_SIZE ) w = MAPSPRITE_SIZE; + if( h < MAPSPRITE_SIZE ) h = MAPSPRITE_SIZE; + + // resample image if needed + gEngfuncs.Image_Process( &pix, w, h, IMAGE_FORCE_RGBA|IMAGE_RESAMPLE, 0.0f ); + + w = h = MAPSPRITE_SIZE; + + // check range + if( w > pix->width ) w = pix->width; + if( h > pix->height ) h = pix->height; + + // determine how many frames we needs + numframes = (pix->width * pix->height) / (w * h); + mod->mempool = Mem_AllocPool( va( "^2%s^7", mod->name )); + psprite = Mem_Calloc( mod->mempool, sizeof( msprite_t ) + ( numframes - 1 ) * sizeof( psprite->frames )); + mod->cache.data = psprite; // make link to extradata + + psprite->type = SPR_FWD_PARALLEL_ORIENTED; + psprite->texFormat = SPR_ALPHTEST; + psprite->numframes = mod->numframes = numframes; + psprite->radius = sqrt(((w >> 1) * (w >> 1)) + ((h >> 1) * (h >> 1))); + + mod->mins[0] = mod->mins[1] = -w / 2; + mod->maxs[0] = mod->maxs[1] = w / 2; + mod->mins[2] = -h / 2; + mod->maxs[2] = h / 2; + + // create a temporary pic + memset( &temp, 0, sizeof( temp )); + temp.width = w; + temp.height = h; + temp.type = pix->type; + temp.flags = pix->flags; + temp.size = w * h * gEngfuncs.Image_GetPFDesc(temp.type)->bpp; + temp.buffer = Mem_Malloc( r_temppool, temp.size ); + temp.palette = NULL; + + // chop the image and upload into video memory + for( i = xl = yl = 0; i < numframes; i++ ) + { + xh = xl + w; + yh = yl + h; + + src = pix->buffer + ( yl * pix->width + xl ) * 4; + linedelta = ( pix->width - w ) * 4; + dst = temp.buffer; + + // cut block from source + for( y = yl; y < yh; y++ ) + { + for( x = xl; x < xh; x++ ) + for( j = 0; j < 4; j++ ) + *dst++ = *src++; + src += linedelta; + } + + // build uinque frame name + Q_snprintf( texname, sizeof( texname ), "#MAP/%s_%i%i.spr", mod->name, i / 10, i % 10 ); + + psprite->frames[i].frameptr = Mem_Calloc( mod->mempool, sizeof( mspriteframe_t )); + pspriteframe = psprite->frames[i].frameptr; + pspriteframe->width = w; + pspriteframe->height = h; + pspriteframe->up = ( h >> 1 ); + pspriteframe->left = -( w >> 1 ); + pspriteframe->down = ( h >> 1 ) - h; + pspriteframe->right = w + -( w >> 1 ); + pspriteframe->gl_texturenum = GL_LoadTextureInternal( texname, &temp, TF_IMAGE ); + + xl += w; + if( xl >= pix->width ) + { + xl = 0; + yl += h; + } + } + + gEngfuncs.FS_FreeImage( pix ); + Mem_Free( temp.buffer ); + + if( loaded ) *loaded = true; +} + +/* +==================== +Mod_UnloadSpriteModel + +release sprite model and frames +==================== +*/ +void Mod_SpriteUnloadTextures( void *data ) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, j; + + psprite = data; + + if( psprite ) + { + // release all textures + for( i = 0; i < psprite->numframes; i++ ) + { + if( psprite->frames[i].type == SPR_SINGLE ) + { + pspriteframe = psprite->frames[i].frameptr; + GL_FreeTexture( pspriteframe->gl_texturenum ); + } + else + { + pspritegroup = (mspritegroup_t *)psprite->frames[i].frameptr; + + for( j = 0; j < pspritegroup->numframes; j++ ) + { + pspriteframe = pspritegroup->frames[i]; + GL_FreeTexture( pspriteframe->gl_texturenum ); + } + } + } + } +} + +/* +================ +R_GetSpriteFrame + +assume pModel is valid +================ +*/ +mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw ) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe = NULL; + float *pintervals, fullinterval; + int i, numframes; + float targettime; + + Assert( pModel != NULL ); + psprite = pModel->cache.data; + + if( frame < 0 ) + { + frame = 0; + } + else if( frame >= psprite->numframes ) + { + if( frame > psprite->numframes ) + gEngfuncs.Con_Printf( S_WARN "R_GetSpriteFrame: no such frame %d (%s)\n", frame, pModel->name ); + frame = psprite->numframes - 1; + } + + if( psprite->frames[frame].type == SPR_SINGLE ) + { + pspriteframe = psprite->frames[frame].frameptr; + } + else if( psprite->frames[frame].type == SPR_GROUP ) + { + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pintervals = pspritegroup->intervals; + numframes = pspritegroup->numframes; + fullinterval = pintervals[numframes-1]; + + // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values + // are positive, so we don't have to worry about division by zero + targettime = gpGlobals->time - ((int)( gpGlobals->time / fullinterval )) * fullinterval; + + for( i = 0; i < (numframes - 1); i++ ) + { + if( pintervals[i] > targettime ) + break; + } + pspriteframe = pspritegroup->frames[i]; + } + else if( psprite->frames[frame].type == FRAME_ANGLED ) + { + int angleframe = (int)(Q_rint(( RI.viewangles[1] - yaw + 45.0f ) / 360 * 8) - 4) & 7; + + // e.g. doom-style sprite monsters + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pspriteframe = pspritegroup->frames[angleframe]; + } + + return pspriteframe; +} + +/* +================ +R_GetSpriteFrameInterpolant + +NOTE: we using prevblending[0] and [1] for holds interval +between frames where are we lerping +================ +*/ +float R_GetSpriteFrameInterpolant( cl_entity_t *ent, mspriteframe_t **oldframe, mspriteframe_t **curframe ) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + int i, j, numframes, frame; + float lerpFrac, time, jtime, jinterval; + float *pintervals, fullinterval, targettime; + int m_fDoInterp; + + psprite = ent->model->cache.data; + frame = (int)ent->curstate.frame; + lerpFrac = 1.0f; + + // misc info + m_fDoInterp = (ent->curstate.effects & EF_NOINTERP) ? false : true; + + if( frame < 0 ) + { + frame = 0; + } + else if( frame >= psprite->numframes ) + { + gEngfuncs.Con_Reportf( S_WARN "R_GetSpriteFrameInterpolant: no such frame %d (%s)\n", frame, ent->model->name ); + frame = psprite->numframes - 1; + } + + if( psprite->frames[frame].type == FRAME_SINGLE ) + { + if( m_fDoInterp ) + { + if( ent->latched.prevblending[0] >= psprite->numframes || psprite->frames[ent->latched.prevblending[0]].type != FRAME_SINGLE ) + { + // this can be happens when rendering switched between single and angled frames + // or change model on replace delta-entity + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 1.0f; + } + + if( ent->latched.sequencetime < gpGlobals->time ) + { + if( frame != ent->latched.prevblending[1] ) + { + ent->latched.prevblending[0] = ent->latched.prevblending[1]; + ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + else lerpFrac = (gpGlobals->time - ent->latched.sequencetime) * 11.0f; + } + else + { + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + } + else + { + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + lerpFrac = 1.0f; + } + + if( ent->latched.prevblending[0] >= psprite->numframes ) + { + // reset interpolation on change model + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + + // get the interpolated frames + if( oldframe ) *oldframe = psprite->frames[ent->latched.prevblending[0]].frameptr; + if( curframe ) *curframe = psprite->frames[frame].frameptr; + } + else if( psprite->frames[frame].type == FRAME_GROUP ) + { + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pintervals = pspritegroup->intervals; + numframes = pspritegroup->numframes; + fullinterval = pintervals[numframes-1]; + jinterval = pintervals[1] - pintervals[0]; + time = gpGlobals->time; + jtime = 0.0f; + + // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values + // are positive, so we don't have to worry about division by zero + targettime = time - ((int)(time / fullinterval)) * fullinterval; + + // LordHavoc: since I can't measure the time properly when it loops from numframes - 1 to 0, + // i instead measure the time of the first frame, hoping it is consistent + for( i = 0, j = numframes - 1; i < (numframes - 1); i++ ) + { + if( pintervals[i] > targettime ) + break; + j = i; + jinterval = pintervals[i] - jtime; + jtime = pintervals[i]; + } + + if( m_fDoInterp ) + lerpFrac = (targettime - jtime) / jinterval; + else j = i; // no lerping + + // get the interpolated frames + if( oldframe ) *oldframe = pspritegroup->frames[j]; + if( curframe ) *curframe = pspritegroup->frames[i]; + } + else if( psprite->frames[frame].type == FRAME_ANGLED ) + { + // e.g. doom-style sprite monsters + float yaw = ent->angles[YAW]; + int angleframe = (int)(Q_rint(( RI.viewangles[1] - yaw + 45.0f ) / 360 * 8) - 4) & 7; + + if( m_fDoInterp ) + { + if( ent->latched.prevblending[0] >= psprite->numframes || psprite->frames[ent->latched.prevblending[0]].type != FRAME_ANGLED ) + { + // this can be happens when rendering switched between single and angled frames + // or change model on replace delta-entity + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 1.0f; + } + + if( ent->latched.sequencetime < gpGlobals->time ) + { + if( frame != ent->latched.prevblending[1] ) + { + ent->latched.prevblending[0] = ent->latched.prevblending[1]; + ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + else lerpFrac = (gpGlobals->time - ent->latched.sequencetime) * ent->curstate.framerate; + } + else + { + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + ent->latched.sequencetime = gpGlobals->time; + lerpFrac = 0.0f; + } + } + else + { + ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame; + lerpFrac = 1.0f; + } + + pspritegroup = (mspritegroup_t *)psprite->frames[ent->latched.prevblending[0]].frameptr; + if( oldframe ) *oldframe = pspritegroup->frames[angleframe]; + + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + if( curframe ) *curframe = pspritegroup->frames[angleframe]; + } + + return lerpFrac; +} + +/* +================ +R_CullSpriteModel + +Cull sprite model by bbox +================ +*/ +qboolean R_CullSpriteModel( cl_entity_t *e, vec3_t origin ) +{ + vec3_t sprite_mins, sprite_maxs; + float scale = 1.0f; + + if( !e->model->cache.data ) + return true; + + if( e->curstate.scale > 0.0f ) + scale = e->curstate.scale; + + // scale original bbox (no rotation for sprites) + VectorScale( e->model->mins, scale, sprite_mins ); + VectorScale( e->model->maxs, scale, sprite_maxs ); + + sprite_radius = RadiusFromBounds( sprite_mins, sprite_maxs ); + + VectorAdd( sprite_mins, origin, sprite_mins ); + VectorAdd( sprite_maxs, origin, sprite_maxs ); + + return R_CullModel( e, sprite_mins, sprite_maxs ); +} + +/* +================ +R_GlowSightDistance + +Set sprite brightness factor +================ +*/ +static float R_SpriteGlowBlend( vec3_t origin, int rendermode, int renderfx, float *pscale ) +{ + float dist, brightness; + vec3_t glowDist; + pmtrace_t *tr; + + VectorSubtract( origin, RI.vieworg, glowDist ); + dist = VectorLength( glowDist ); + + if( RP_NORMALPASS( )) + { + tr = gEngfuncs.EV_VisTraceLine( RI.vieworg, origin, r_traceglow->value ? PM_GLASS_IGNORE : (PM_GLASS_IGNORE|PM_STUDIO_IGNORE)); + + if(( 1.0f - tr->fraction ) * dist > 8.0f ) + return 0.0f; + } + + if( renderfx == kRenderFxNoDissipation ) + return 1.0f; + + brightness = GLARE_FALLOFF / ( dist * dist ); + brightness = bound( 0.05f, brightness, 1.0f ); + *pscale *= dist * ( 1.0f / 200.0f ); + + return brightness; +} + +/* +================ +R_SpriteOccluded + +Do occlusion test for glow-sprites +================ +*/ +qboolean R_SpriteOccluded( cl_entity_t *e, vec3_t origin, float *pscale ) +{ + if( e->curstate.rendermode == kRenderGlow ) + { + float blend; + vec3_t v; + + TriWorldToScreen( origin, v ); + + if( v[0] < RI.viewport[0] || v[0] > RI.viewport[0] + RI.viewport[2] ) + return true; // do scissor + if( v[1] < RI.viewport[1] || v[1] > RI.viewport[1] + RI.viewport[3] ) + return true; // do scissor + + blend = R_SpriteGlowBlend( origin, e->curstate.rendermode, e->curstate.renderfx, pscale ); + tr.blend *= blend; + + if( blend <= 0.01f ) + return true; // faded + } + else + { + if( R_CullSpriteModel( e, origin )) + return true; + } + + return false; +} + +/* +================= +R_DrawSpriteQuad +================= +*/ +static void R_DrawSpriteQuad( mspriteframe_t *frame, vec3_t org, vec3_t v_right, vec3_t v_up, float scale ) +{ + vec3_t point; + image_t *image; + + r_stats.c_sprite_polys++; + image = R_GetTexture(frame->gl_texturenum); + r_affinetridesc.pskin = image->pixels[0]; + r_affinetridesc.skinwidth = image->width; + r_affinetridesc.skinheight = image->height; + + TriBegin( TRI_QUADS ); + TriTexCoord2f( 0.0f, 1.0f ); + VectorMA( org, frame->down * scale, v_up, point ); + VectorMA( point, frame->left * scale, v_right, point ); + TriVertex3fv( point ); + TriTexCoord2f( 0.0f, 0.0f ); + VectorMA( org, frame->up * scale, v_up, point ); + VectorMA( point, frame->left * scale, v_right, point ); + TriVertex3fv( point ); + TriTexCoord2f( 1.0f, 0.0f ); + VectorMA( org, frame->up * scale, v_up, point ); + VectorMA( point, frame->right * scale, v_right, point ); + TriVertex3fv( point ); + TriTexCoord2f( 1.0f, 1.0f ); + VectorMA( org, frame->down * scale, v_up, point ); + VectorMA( point, frame->right * scale, v_right, point ); + TriVertex3fv( point ); + TriEnd(); + +#if 0 + image_t *pic = R_GetTexture(frame->gl_texturenum); + r_polydesc.pixels = pic->pixels[0]; + r_polydesc.pixel_width = pic->width; + r_polydesc.pixel_height = pic->height; + r_polydesc.dist = 0; + + // generate the sprite's axes, completely parallel to the viewplane. + VectorCopy (v_up, r_polydesc.vup); + VectorCopy (v_right, r_polydesc.vright); + VectorCopy (vpn, r_polydesc.vpn); + +// build the sprite poster in worldspace + VectorScale (r_polydesc.vright, + frame->width - frame->origin_x, right); + VectorScale (r_polydesc.vup, + s_psprframe->height - s_psprframe->origin_y, up); + VectorScale (r_polydesc.vright, + -s_psprframe->origin_x, left); + VectorScale (r_polydesc.vup, + -s_psprframe->origin_y, down); + + // invert UP vector for sprites + VectorInverse( r_polydesc.vup ); + + pverts = r_clip_verts[0]; + + pverts[0][0] = r_entorigin[0] + up[0] + left[0]; + pverts[0][1] = r_entorigin[1] + up[1] + left[1]; + pverts[0][2] = r_entorigin[2] + up[2] + left[2]; + pverts[0][3] = 0; + pverts[0][4] = 0; + + pverts[1][0] = r_entorigin[0] + up[0] + right[0]; + pverts[1][1] = r_entorigin[1] + up[1] + right[1]; + pverts[1][2] = r_entorigin[2] + up[2] + right[2]; + pverts[1][3] = s_psprframe->width; + pverts[1][4] = 0; + + pverts[2][0] = r_entorigin[0] + down[0] + right[0]; + pverts[2][1] = r_entorigin[1] + down[1] + right[1]; + pverts[2][2] = r_entorigin[2] + down[2] + right[2]; + pverts[2][3] = s_psprframe->width; + pverts[2][4] = s_psprframe->height; + + pverts[3][0] = r_entorigin[0] + down[0] + left[0]; + pverts[3][1] = r_entorigin[1] + down[1] + left[1]; + pverts[3][2] = r_entorigin[2] + down[2] + left[2]; + pverts[3][3] = 0; + pverts[3][4] = s_psprframe->height; + + r_polydesc.nump = 4; + r_polydesc.s_offset = ( r_polydesc.pixel_width >> 1); + r_polydesc.t_offset = ( r_polydesc.pixel_height >> 1); + VectorCopy( modelorg, r_polydesc.viewer_position ); + + r_polydesc.stipple_parity = 1; + if ( currententity->flags & RF_TRANSLUCENT ) + R_ClipAndDrawPoly ( currententity->alpha, false, true ); + else + R_ClipAndDrawPoly ( 1.0F, false, true ); + r_polydesc.stipple_parity = 0; +#endif +} + +static qboolean R_SpriteHasLightmap( cl_entity_t *e, int texFormat ) +{ + if( !r_sprite_lighting->value ) + return false; + + if( texFormat != SPR_ALPHTEST ) + return false; + + if( e->curstate.effects & EF_FULLBRIGHT ) + return false; + + if( e->curstate.renderamt <= 127 ) + return false; + + switch( e->curstate.rendermode ) + { + case kRenderNormal: + case kRenderTransAlpha: + case kRenderTransTexture: + break; + default: + return false; + } + + return true; +} + +/* +================= +R_SpriteAllowLerping +================= +*/ +static qboolean R_SpriteAllowLerping( cl_entity_t *e, msprite_t *psprite ) +{ + if( !r_sprite_lerping->value ) + return false; + + if( psprite->numframes <= 1 ) + return false; + + if( psprite->texFormat != SPR_ADDITIVE ) + return false; + + if( e->curstate.rendermode == kRenderNormal || e->curstate.rendermode == kRenderTransAlpha ) + return false; + + return true; +} + +/* +================= +R_DrawSpriteModel +================= +*/ +void R_DrawSpriteModel( cl_entity_t *e ) +{ + mspriteframe_t *frame, *oldframe; + msprite_t *psprite; + model_t *model; + int i, type; + float angle, dot, sr, cr; + float lerp = 1.0f, ilerp, scale; + vec3_t v_forward, v_right, v_up; + vec3_t origin, color, color2; + + if( RI.params & RP_ENVVIEW ) + return; + + model = e->model; + psprite = (msprite_t * )model->cache.data; + VectorCopy( e->origin, origin ); // set render origin + + // do movewith + if( e->curstate.aiment > 0 && e->curstate.movetype == MOVETYPE_FOLLOW ) + { + cl_entity_t *parent; + + parent = gEngfuncs.GetEntityByIndex( e->curstate.aiment ); + + if( parent && parent->model ) + { + if( parent->model->type == mod_studio && e->curstate.body > 0 ) + { + int num = bound( 1, e->curstate.body, MAXSTUDIOATTACHMENTS ); + VectorCopy( parent->attachment[num-1], origin ); + } + else VectorCopy( parent->origin, origin ); + } + } + + scale = e->curstate.scale; + if( !scale ) scale = 1.0f; + + if( R_SpriteOccluded( e, origin, &scale )) + return; // sprite culled + + r_stats.c_sprite_models_drawn++; + + if( e->curstate.rendermode == kRenderGlow || e->curstate.rendermode == kRenderTransAdd ) + R_AllowFog( false ); +#if 0 + // select properly rendermode + switch( e->curstate.rendermode ) + { + case kRenderTransAlpha: + pglDepthMask( GL_FALSE ); + case kRenderTransColor: + case kRenderTransTexture: + pglEnable( GL_BLEND ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + break; + case kRenderGlow: + pglDisable( GL_DEPTH_TEST ); + case kRenderTransAdd: + pglEnable( GL_BLEND ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE ); + pglDepthMask( GL_FALSE ); + break; + case kRenderNormal: + default: + pglDisable( GL_BLEND ); + break; + } + + // all sprites can have color + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + pglEnable( GL_ALPHA_TEST ); +#endif + // NOTE: never pass sprites with rendercolor '0 0 0' it's a stupid Valve Hammer Editor bug + if( e->curstate.rendercolor.r || e->curstate.rendercolor.g || e->curstate.rendercolor.b ) + { + color[0] = (float)e->curstate.rendercolor.r * ( 1.0f / 255.0f ); + color[1] = (float)e->curstate.rendercolor.g * ( 1.0f / 255.0f ); + color[2] = (float)e->curstate.rendercolor.b * ( 1.0f / 255.0f ); + } + else + { + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + } + + if( R_SpriteHasLightmap( e, psprite->texFormat )) + { + colorVec lightColor = R_LightPoint( origin ); + // FIXME: collect light from dlights? + color2[0] = (float)lightColor.r * ( 1.0f / 255.0f ); + color2[1] = (float)lightColor.g * ( 1.0f / 255.0f ); + color2[2] = (float)lightColor.b * ( 1.0f / 255.0f ); + // NOTE: sprites with 'lightmap' looks ugly when alpha func is GL_GREATER 0.0 + // pglAlphaFunc( GL_GREATER, 0.5f ); + } + + if( R_SpriteAllowLerping( e, psprite )) + lerp = R_GetSpriteFrameInterpolant( e, &oldframe, &frame ); + else frame = oldframe = R_GetSpriteFrame( model, e->curstate.frame, e->angles[YAW] ); + + type = psprite->type; + + // automatically roll parallel sprites if requested + if( e->angles[ROLL] != 0.0f && type == SPR_FWD_PARALLEL ) + type = SPR_FWD_PARALLEL_ORIENTED; + + switch( type ) + { + case SPR_ORIENTED: + AngleVectors( e->angles, v_forward, v_right, v_up ); + VectorScale( v_forward, 0.01f, v_forward ); // to avoid z-fighting + VectorSubtract( origin, v_forward, origin ); + break; + case SPR_FACING_UPRIGHT: + VectorSet( v_right, origin[1] - RI.vieworg[1], -(origin[0] - RI.vieworg[0]), 0.0f ); + VectorSet( v_up, 0.0f, 0.0f, 1.0f ); + VectorNormalize( v_right ); + break; + case SPR_FWD_PARALLEL_UPRIGHT: + dot = RI.vforward[2]; + if(( dot > 0.999848f ) || ( dot < -0.999848f )) // cos(1 degree) = 0.999848 + return; // invisible + VectorSet( v_up, 0.0f, 0.0f, 1.0f ); + VectorSet( v_right, RI.vforward[1], -RI.vforward[0], 0.0f ); + VectorNormalize( v_right ); + break; + case SPR_FWD_PARALLEL_ORIENTED: + angle = e->angles[ROLL] * (M_PI2 / 360.0f); + SinCos( angle, &sr, &cr ); + for( i = 0; i < 3; i++ ) + { + v_right[i] = (RI.vright[i] * cr + RI.vup[i] * sr); + v_up[i] = RI.vright[i] * -sr + RI.vup[i] * cr; + } + break; + case SPR_FWD_PARALLEL: // normal sprite + default: + VectorCopy( RI.vright, v_right ); + VectorCopy( RI.vup, v_up ); + break; + } + + //if( psprite->facecull == SPR_CULL_NONE ) + //GL_Cull( GL_NONE ); + + if( oldframe == frame ) + { + // draw the single non-lerped frame + _TriColor4f( color[0], color[1], color[2], tr.blend ); + GL_Bind( XASH_TEXTURE0, frame->gl_texturenum ); + R_DrawSpriteQuad( frame, origin, v_right, v_up, scale ); + } + else + { + // draw two combined lerped frames + lerp = bound( 0.0f, lerp, 1.0f ); + ilerp = 1.0f - lerp; + + if( ilerp != 0.0f ) + { + _TriColor4f( color[0], color[1], color[2], tr.blend * ilerp ); + GL_Bind( XASH_TEXTURE0, oldframe->gl_texturenum ); + R_DrawSpriteQuad( oldframe, origin, v_right, v_up, scale ); + } + + if( lerp != 0.0f ) + { + _TriColor4f( color[0], color[1], color[2], tr.blend * lerp ); + GL_Bind( XASH_TEXTURE0, frame->gl_texturenum ); + R_DrawSpriteQuad( frame, origin, v_right, v_up, scale ); + } + } +#if 0 + // draw the sprite 'lightmap' :-) + if( R_SpriteHasLightmap( e, psprite->texFormat )) + { + if( !r_lightmap->value ) + pglEnable( GL_BLEND ); + else pglDisable( GL_BLEND ); + pglDepthFunc( GL_EQUAL ); + pglDisable( GL_ALPHA_TEST ); + pglBlendFunc( GL_ZERO, GL_SRC_COLOR ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + + pglColor4f( color2[0], color2[1], color2[2], tr.blend ); + GL_Bind( XASH_TEXTURE0, tr.whiteTexture ); + R_DrawSpriteQuad( frame, origin, v_right, v_up, scale ); + pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST ); + pglDepthFunc( GL_LEQUAL ); + } + + if( psprite->facecull == SPR_CULL_NONE ) + GL_Cull( GL_FRONT ); + + pglDisable( GL_ALPHA_TEST ); + pglDepthMask( GL_TRUE ); + + if( e->curstate.rendermode == kRenderGlow || e->curstate.rendermode == kRenderTransAdd ) + R_AllowFog( true ); + + if( e->curstate.rendermode != kRenderNormal ) + { + pglDisable( GL_BLEND ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + pglEnable( GL_DEPTH_TEST ); + } +#endif +} diff --git a/r_triapi.c b/r_triapi.c index e6c3193b..7491ec16 100644 --- a/r_triapi.c +++ b/r_triapi.c @@ -116,6 +116,8 @@ void TriBegin( int mode1 ) pglBegin( mode ); #endif + if( mode1 == TRI_QUADS ) + mode1 = TRI_TRIANGLE_FAN; mode = mode1; vertcount = n = vertcount = 0; } @@ -242,6 +244,17 @@ TriVertex3f */ void TriVertex3f( float x, float y, float z ) { + if( mode == TRI_TRIANGLES ) + { + R_SetupFinalVert( &triv[vertcount], x, y, z, light << 8,s,t); + vertcount++; + if( vertcount == 3 ) + { + R_RenderTriangle( &triv[0], &triv[1], &triv[2] ); + R_RenderTriangle( &triv[2], &triv[1], &triv[0] ); + vertcount = 0; + } + } if( mode == TRI_TRIANGLE_FAN ) { R_SetupFinalVert( &triv[vertcount], x, y, z, light << 8,s,t); @@ -300,7 +313,7 @@ TriWorldToScreen convert world coordinates (x,y,z) into screen (x, y) ============= */ -int TriWorldToScreen( float *world, float *screen ) +int TriWorldToScreen( const float *world, float *screen ) { int retval;