From 3b1e7be1121dd61f9ea644d2cd5bbf9019227229 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Wed, 17 Feb 2021 13:26:09 -0800 Subject: [PATCH] add beams rendering not all beam types are supported yet also changes studio api init sequence, fixes missing cvars vertex type struct is no longer names as brush-specific uniform buffer allocation is now in vk_render blending issues still remain --- ref_vk/TODO.md | 23 +- ref_vk/vk_beams.c | 1283 ++++++++++++++++++++++++++++++++++++++++++++ ref_vk/vk_beams.h | 11 + ref_vk/vk_brush.c | 6 +- ref_vk/vk_cvar.c | 3 + ref_vk/vk_render.c | 20 +- ref_vk/vk_render.h | 7 +- ref_vk/vk_rmain.c | 19 +- ref_vk/vk_scene.c | 128 ++++- ref_vk/vk_scene.h | 15 +- ref_vk/vk_sprite.c | 4 +- ref_vk/vk_studio.c | 11 +- ref_vk/vk_studio.h | 2 + 13 files changed, 1461 insertions(+), 71 deletions(-) create mode 100644 ref_vk/vk_beams.c create mode 100644 ref_vk/vk_beams.h diff --git a/ref_vk/TODO.md b/ref_vk/TODO.md index c562d027..5029e98e 100644 --- a/ref_vk/TODO.md +++ b/ref_vk/TODO.md @@ -1,18 +1,17 @@ -## 2021-02-15 -- [x] weapon models -- viewmodel -- [x] coalesce studio model draw calls -- [x] initual sprite support - # Next - [ ] fix sprite blending +- [ ] wrap viewmodel in vk debug label # Planned +- [ ] draw more types of beams +- [ ] fix brush blending +- [ ] sprite depth offset +- [ ] fix incorrect viewport sprite culling - [ ] improve g_camera handling; trace SetViewPass vs RenderScene ... - [ ] loading to the same map breaks geometry - [ ] studio model lighting - [ ] move all consts to vk_const - [ ] what is GL_Backend*/GL_RenderFrame ??? -- [ ] beams - [ ] particles - [ ] decals - [ ] issue: transparent brushes are too transparent (train ride) @@ -25,6 +24,7 @@ - [ ] RTX - [ ] studio models survive NewMap; need to compactify buffers after removing all brushes - [ ] sometimes it gets very slow (1fps) when ran under lldb (only on stream?) +- [ ] optimize perf: cmdbuf managements and semaphores, upload to gpu, ... # Someday - [ ] cleanup unused stuff in vk_studio.c @@ -37,6 +37,8 @@ - [ ] better 2d renderer: fill DRAWQUAD(texture, color, ...) command into storage buffer instead of 4 vertices - [ ] auto-atlas lots of smol textures: most of model texture are tiny (64x64 or less), can we not rebind them all the time? alt: bindless texture array - [ ] can we also try to coalesce sprite draw calls? +- [ ] not visibly watertight map brushes + ## 2021-02-06 - [x] alpha test @@ -64,3 +66,12 @@ - [x] studio models vk debug markers - [x] studio models white texture as lightmap - [x] studio models fixes + +## 2021-02-15 +- [x] weapon models -- viewmodel +- [x] coalesce studio model draw calls +- [x] initual sprite support + +## 2021-02-17 +- [x] draw some beams + diff --git a/ref_vk/vk_beams.c b/ref_vk/vk_beams.c new file mode 100644 index 00000000..b0f6c228 --- /dev/null +++ b/ref_vk/vk_beams.c @@ -0,0 +1,1283 @@ +#include "vk_beams.h" +#include "vk_common.h" +#include "vk_global.h" +#include "vk_render.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, int ubo_index, 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; + beamseg_t curSeg; + int total_vertices = 0; + int total_indices = 0; + vk_buffer_alloc_t vertex_buffer, index_buffer; + vk_vertex_t *pvtx = NULL; + uint16_t *pidx = NULL; + + 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 ); + + vertex_buffer = VK_RenderTempBufferAlloc(sizeof(vk_vertex_t), total_vertices); + index_buffer = VK_RenderTempBufferAlloc(sizeof(uint16_t), total_indices); + + if (!vertex_buffer.ptr || !index_buffer.ptr) { + gEngine.Con_Printf(S_ERROR "Couldn't allocate %d vertices or %d indices for beam\n", total_vertices, total_indices); + return; + } + + pvtx = vertex_buffer.ptr; + pidx = index_buffer.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 ); + + pvtx->lm_tc[0] = pvtx->lm_tc[1] = 0.f; + pvtx->gl_tc[0] = 0.0f; + pvtx->gl_tc[1] = curSeg.texcoord; + //FIXME VK applyBrightness( brightness, color, pvtx->color ); + // FIXME VK pglNormal3fv( vAveNormal ); + VectorCopy( vPoint1, pvtx->pos ); + ++pvtx; + + pvtx->lm_tc[0] = pvtx->lm_tc[1] = 0.f; + pvtx->gl_tc[0] = 1.0f; + pvtx->gl_tc[1] = curSeg.texcoord; + //FIXME VK applyBrightness( brightness, color, pvtx->color ); + // FIXME VK pglNormal3fv( vAveNormal ); + VectorCopy( vPoint2, pvtx->pos ); + ++pvtx; + } + + 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 ); + + pvtx->lm_tc[0] = pvtx->lm_tc[1] = 0.f; + pvtx->gl_tc[0] = 0.0f; + pvtx->gl_tc[1] = curSeg.texcoord; + //FIXME VK applyBrightness( brightness, color, pvtx->color ); + // FIXME VK pglNormal3fv( vLastNormal ); + VectorCopy( vPoint1, pvtx->pos ); + ++pvtx; + + pvtx->lm_tc[0] = pvtx->lm_tc[1] = 0.f; + pvtx->gl_tc[0] = 1.0f; + pvtx->gl_tc[1] = curSeg.texcoord; + //FIXME VK applyBrightness( brightness, color, pvtx->color ); + // FIXME VK pglNormal3fv( vLastNormal ); + VectorCopy( vPoint2, pvtx->pos ); + ++pvtx; + } + + 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] + pidx[(i-2)*3+0] = i - 1; + pidx[(i-2)*3+1] = i - 2; + pidx[(i-2)*3+2] = i; + } + else + { + // draw triangle [n-2 n-1 n] + pidx[(i-2)*3+0] = i - 2; + pidx[(i-2)*3+1] = i - 1; + pidx[(i-2)*3+2] = i; + } + } + { + const render_draw_t draw = { + .ubo_index = ubo_index, + .lightmap = tglob.whiteTexture, + .texture = texture, + .render_mode = render_mode, + .element_count = total_indices, + .vertex_offset = vertex_buffer.buffer_offset_in_units, + .index_offset = index_buffer.buffer_offset_in_units, + }; + + VK_RenderDraw( &draw ); + } +} + +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; + int ubo_index = VK_RenderUniformAlloc(); + uniform_data_t *ubo = VK_RenderGetUniformSlot( ubo_index ); + + 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; + + // Ran out of UBO slots + if (!ubo) + return; + + { + matrix4x4 tmp; + const matrix4x4 vk_proj_fixup = { + {1, 0, 0, 0}, + {0, -1, 0, 0}, + {0, 0, .5, 0}, + {0, 0, .5, 1} + }; + Matrix4x4_Concat( tmp, vk_proj_fixup, g_camera.worldviewProjectionMatrix); + Matrix4x4_ToArrayFloatGL( tmp, (float*)ubo->mvp ); + } + Vector4Set(ubo->color, 1, 1, 1, 1); + + if (vk_core.debug) { + VkDebugUtilsLabelEXT label = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .pLabelName = "beam", + }; + vkCmdBeginDebugUtilsLabelEXT(vk_core.cb, &label); + } + + 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, ubo_index, 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; + } + + if (vk_core.debug) + vkCmdEndDebugUtilsLabelEXT(vk_core.cb); + + // 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 ); +} + diff --git a/ref_vk/vk_beams.h b/ref_vk/vk_beams.h new file mode 100644 index 00000000..4a08c5bb --- /dev/null +++ b/ref_vk/vk_beams.h @@ -0,0 +1,11 @@ +#pragma once + +#include "xash3d_types.h" + +struct cl_entity_s; +struct beam_s; + +qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly ); + +void R_BeamDrawCustomEntity( struct cl_entity_s *ent, float frametime ); +void R_BeamDraw( struct beam_s *pbeam, float frametime ); diff --git a/ref_vk/vk_brush.c b/ref_vk/vk_brush.c index cb03f995..18b5dae1 100644 --- a/ref_vk/vk_brush.c +++ b/ref_vk/vk_brush.c @@ -217,7 +217,7 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_ uint32_t vertex_offset = 0; int num_surfaces = 0; vk_buffer_alloc_t vertex_alloc, index_alloc; - brush_vertex_t *bvert = NULL; + vk_vertex_t *bvert = NULL; uint16_t *bind = NULL; uint32_t index_offset = 0; @@ -243,7 +243,7 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_ max_texture_id = surf->texinfo->texture->gl_texturenum; } - vertex_alloc = VK_RenderBufferAlloc( sizeof(brush_vertex_t), num_vertices ); + vertex_alloc = VK_RenderBufferAlloc( sizeof(vk_vertex_t), num_vertices ); bvert = vertex_alloc.ptr; bmodel->vertex_offset = vertex_alloc.buffer_offset_in_units; if (!bvert) @@ -306,7 +306,7 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_ const int iedge = mod->surfedges[surf->firstedge + k]; const medge_t *edge = mod->edges + (iedge >= 0 ? iedge : -iedge); const mvertex_t *in_vertex = mod->vertexes + (iedge >= 0 ? edge->v[0] : edge->v[1]); - brush_vertex_t vertex = { + vk_vertex_t vertex = { {in_vertex->position[0], in_vertex->position[1], in_vertex->position[2]}, }; diff --git a/ref_vk/vk_cvar.c b/ref_vk/vk_cvar.c index 20f6f85a..f3602ba8 100644 --- a/ref_vk/vk_cvar.c +++ b/ref_vk/vk_cvar.c @@ -5,8 +5,11 @@ DECLARE_CVAR(NONEXTERN_CVAR) #undef NONEXTERN_CVAR +static cvar_t *r_drawentities; + void VK_LoadCvars( void ) { r_lighting_modulate = gEngine.Cvar_Get( "r_lighting_modulate", "0.6", FCVAR_ARCHIVE, "lightstyles modulate scale" ); cl_lightstyle_lerping = gEngine.pfnGetCvarPointer( "cl_lightstyle_lerping", 0 ); + r_drawentities = gEngine.Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT, "render entities" ); } diff --git a/ref_vk/vk_render.c b/ref_vk/vk_render.c index 976a5315..6b6f48cd 100644 --- a/ref_vk/vk_render.c +++ b/ref_vk/vk_render.c @@ -22,6 +22,7 @@ static struct { } stat; uint32_t temp_buffer_offset; + int next_free_uniform_slot; VkPipelineLayout pipeline_layout; VkPipeline pipelines[kRenderTransAdd + 1]; @@ -75,9 +76,9 @@ static qboolean createPipelines( void ) }; VkVertexInputAttributeDescription attribs[] = { - {.binding = 0, .location = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(brush_vertex_t, pos)}, - {.binding = 0, .location = 1, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(brush_vertex_t, gl_tc)}, - {.binding = 0, .location = 2, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(brush_vertex_t, lm_tc)}, + {.binding = 0, .location = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk_vertex_t, pos)}, + {.binding = 0, .location = 1, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(vk_vertex_t, gl_tc)}, + {.binding = 0, .location = 2, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(vk_vertex_t, lm_tc)}, }; VkPipelineShaderStageCreateInfo shader_stages[] = { @@ -101,7 +102,7 @@ static qboolean createPipelines( void ) .stages = shader_stages, .num_stages = ARRAYSIZE(shader_stages), - .vertex_stride = sizeof(brush_vertex_t), + .vertex_stride = sizeof(vk_vertex_t), .depthTestEnable = VK_TRUE, .depthWriteEnable = VK_TRUE, @@ -274,6 +275,7 @@ vk_buffer_alloc_t VK_RenderBufferAlloc( uint32_t unit_size, uint32_t count ) void VK_RenderBufferClearAll( void ) { g_render.buffer_free_offset = 0; + g_render.next_free_uniform_slot = 0; g_render.stat.align_holes_size = 0; } @@ -308,6 +310,8 @@ static struct { } g_render_state; void VK_RenderBegin( void ) { + g_render.next_free_uniform_slot = 0; + g_render_state.pipeline = -1; g_render_state.lightmap = -1; g_render_state.texture = -1; @@ -375,6 +379,14 @@ void VK_RenderDraw( const render_draw_t *draw ) } } +int VK_RenderUniformAlloc( void ) { + if (g_render.next_free_uniform_slot == MAX_UNIFORM_SLOTS) + return -1; + + return g_render.next_free_uniform_slot++; +} + + void VK_RenderEnd( void ) { } diff --git a/ref_vk/vk_render.h b/ref_vk/vk_render.h index 17bc4999..0affc676 100644 --- a/ref_vk/vk_render.h +++ b/ref_vk/vk_render.h @@ -33,11 +33,11 @@ vk_buffer_alloc_t VK_RenderTempBufferAlloc( uint32_t unit_size, uint32_t count ) void VK_RenderTempBufferEnd( void ); // TODO is this a good place? -typedef struct brush_vertex_s { +typedef struct vk_vertex_s { vec3_t pos; vec2_t gl_tc; vec2_t lm_tc; -} brush_vertex_t; +} vk_vertex_t; typedef struct render_draw_s { int ubo_index; @@ -48,5 +48,8 @@ typedef struct render_draw_s { } render_draw_t; void VK_RenderBegin( void ); +// Allocate one uniform slot, -1 if no slot available +int VK_RenderUniformAlloc( void ); +// FIXME rename to Submit something void VK_RenderDraw( const render_draw_t *draw ); void VK_RenderEnd( void ); diff --git a/ref_vk/vk_rmain.c b/ref_vk/vk_rmain.c index 9bca3e8a..43e451c1 100644 --- a/ref_vk/vk_rmain.c +++ b/ref_vk/vk_rmain.c @@ -8,6 +8,7 @@ #include "vk_lightmap.h" #include "vk_sprite.h" #include "vk_studio.h" +#include "vk_beams.h" #include "xash3d_types.h" #include "com_strings.h" @@ -47,11 +48,6 @@ static void GL_BackendEndFrame( void ) PRINT_NOT_IMPLEMENTED(); } -static void CL_AddCustomBeam( cl_entity_t *pEnvBeam ) -{ - PRINT_NOT_IMPLEMENTED(); -} - // debug static void R_ShowTextures( void ) { @@ -127,10 +123,6 @@ static void R_StudioLerpMovement( cl_entity_t *e, double time, vec3_t origin, ve { PRINT_NOT_IMPLEMENTED(); } -static void CL_InitStudioAPI( void ) -{ - PRINT_NOT_IMPLEMENTED(); -} // bmodel static void R_InitSkyClouds( struct mip_s *mt, struct texture_s *tx, qboolean custom_palette ) @@ -198,15 +190,6 @@ static void CL_DrawTracers( double frametime, particle_t *tracers ) { PRINT_NOT_IMPLEMENTED(); } -static void CL_DrawBeams( int fTrans , BEAM *beams ) -{ - PRINT_NOT_IMPLEMENTED(); -} -static qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly ) -{ - PRINT_NOT_IMPLEMENTED(); - return false; -} // Xash3D Render Interface // Get renderer info (doesn't changes engine state at all) diff --git a/ref_vk/vk_scene.c b/ref_vk/vk_scene.c index e9a43935..afe7de7c 100644 --- a/ref_vk/vk_scene.c +++ b/ref_vk/vk_scene.c @@ -9,6 +9,7 @@ #include "vk_core.h" #include "vk_sprite.h" #include "vk_global.h" +#include "vk_beams.h" #include "com_strings.h" #include "ref_params.h" @@ -17,6 +18,15 @@ #include // qsort #include +typedef struct draw_list_s { + struct cl_entity_s *solid_entities[MAX_SCENE_ENTITIES]; // opaque moving or alpha brushes + vk_trans_entity_t trans_entities[MAX_SCENE_ENTITIES]; // translucent brushes or studio models kek + struct cl_entity_s *beam_entities[MAX_SCENE_ENTITIES]; + uint num_solid_entities; + uint num_trans_entities; + uint num_beam_entities; +} draw_list_t; + static struct { draw_list_t draw_stack[MAX_SCENE_STACK]; int draw_stack_pos; @@ -391,7 +401,7 @@ static int R_TransEntityCompare( const void *a, const void *b) // FIXME where should this function be #define RP_NORMALPASS() true // FIXME ??? -static int CL_FxBlend( cl_entity_t *e, const vec3_t vieworg ) // FIXME do R_SetupFrustum: , vec3_t vforward ) +int CL_FxBlend( cl_entity_t *e ) // FIXME do R_SetupFrustum: , vec3_t vforward ) { int blend = 0; float offset, dist; @@ -474,12 +484,11 @@ static int CL_FxBlend( cl_entity_t *e, const vec3_t vieworg ) // FIXME do R_Setu if( blend < 0 ) blend = 0; else blend = e->curstate.renderamt; break; - /* FIXME case kRenderFxHologram: case kRenderFxDistort: VectorCopy( e->origin, tmp ); - VectorSubtract( tmp, vieworg, tmp ); - dist = DotProduct( tmp, vforward ); + VectorSubtract( tmp, g_camera.vieworg, tmp ); + dist = DotProduct( tmp, g_camera.vforward ); // turn off distance fade if( e->curstate.renderfx == kRenderFxDistort ) @@ -497,7 +506,6 @@ static int CL_FxBlend( cl_entity_t *e, const vec3_t vieworg ) // FIXME do R_Setu blend += gEngine.COM_RandomLong( -32, 31 ); } break; - */ default: blend = e->curstate.renderamt; break; @@ -526,22 +534,29 @@ static void prepareMatrix( const ref_viewpass_t *rvp, matrix4x4 worldview, matri Matrix4x4_Concat( mvp, projection, worldview); } -static int drawEntity( cl_entity_t *ent, int render_mode, int ubo_index, const matrix4x4 mvp ) +static void drawEntity( cl_entity_t *ent, int render_mode, const matrix4x4 mvp ) { const model_t *mod = ent->model; matrix4x4 model, ent_mvp; - uniform_data_t *ubo = VK_RenderGetUniformSlot(ubo_index); + uniform_data_t *ubo; float alpha; + int ubo_index; if (!mod) - return 0; + return; // handle studiomodels with custom rendermodes on texture - alpha = render_mode == kRenderNormal ? 1.f : CL_FxBlend( ent, fixme_rvp.vieworigin ) / 255.0f; + alpha = render_mode == kRenderNormal ? 1.f : CL_FxBlend( ent ) / 255.0f; // TODO ref_gl does this earlier, can we too? if( alpha <= 0.0f ) - return 0; + return; + + // TODO might accidentally alloc ubo for things that won't be rendered; optimize this + ubo_index = VK_RenderUniformAlloc(); + if (ubo_index < 0) + return; // TODO complain + ubo = VK_RenderGetUniformSlot(ubo_index); switch (render_mode) { case kRenderNormal: @@ -592,19 +607,18 @@ static int drawEntity( cl_entity_t *ent, int render_mode, int ubo_index, const m Matrix4x4_ToArrayFloatGL( mvp, (float*)ubo->mvp); VK_SpriteDrawModel( ent, ubo_index ); break; - - default: - return 0; } - - return 1; } +static float g_frametime = 0; void VK_SceneRender( void ) { matrix4x4 worldview, projection, mvp; int current_pipeline_index = kRenderNormal; - int ubo_index = 0; + + g_frametime = /*FIXME VK RP_NORMALPASS( )) ? */ + gpGlobals->time - gpGlobals->oldtime + /* FIXME VK : 0.f */; if (!VK_BrushRenderBegin()) return; @@ -617,11 +631,14 @@ void VK_SceneRender( void ) // Draw view model { - uniform_data_t *ubo = VK_RenderGetUniformSlot(ubo_index); + const int ubo_index = VK_RenderUniformAlloc(); + uniform_data_t *ubo; + ASSERT(ubo_index >= 0); + ubo = VK_RenderGetUniformSlot(ubo_index); Matrix4x4_ToArrayFloatGL( mvp, (float*)ubo->mvp ); Vector4Set(ubo->color, 1.f, 1.f, 1.f, 1.f); R_RunViewmodelEvents(); - R_DrawViewModel( ubo_index++ ); + R_DrawViewModel( ubo_index ); } // Draw world brush @@ -629,11 +646,13 @@ void VK_SceneRender( void ) cl_entity_t *world = gEngine.GetEntityByIndex( 0 ); if( world && world->model ) { - uniform_data_t *ubo = VK_RenderGetUniformSlot(ubo_index); + const int ubo_index = VK_RenderUniformAlloc(); + uniform_data_t *ubo; + ASSERT(ubo_index >= 0); + ubo = VK_RenderGetUniformSlot(ubo_index); Matrix4x4_ToArrayFloatGL( mvp, (float*)ubo->mvp ); Vector4Set(ubo->color, 1.f, 1.f, 1.f, 1.f); VK_BrushDrawModel( world, kRenderNormal, ubo_index ); - ubo_index++; } } @@ -641,9 +660,12 @@ void VK_SceneRender( void ) for (int i = 0; i < g_lists.draw_list->num_solid_entities; ++i) { cl_entity_t *ent = g_lists.draw_list->solid_entities[i]; - ubo_index += drawEntity(ent, kRenderNormal, ubo_index, mvp); + drawEntity(ent, kRenderNormal, mvp); } + // Draw opaque beams + gEngine.CL_DrawEFX( g_frametime, false ); + { if (vk_core.debug) { VkDebugUtilsLabelEXT label = { @@ -661,13 +683,16 @@ void VK_SceneRender( void ) for (int i = 0; i < g_lists.draw_list->num_trans_entities; ++i) { const vk_trans_entity_t *ent = g_lists.draw_list->trans_entities + i; - ubo_index += drawEntity(ent->entity, ent->render_mode, ubo_index, mvp); + drawEntity(ent->entity, ent->render_mode, mvp); } if (vk_core.debug) vkCmdEndDebugUtilsLabelEXT(vk_core.cb); } + // Draw transparent beams + gEngine.CL_DrawEFX( g_frametime, true ); + VK_RenderEnd(); VK_RenderTempBufferEnd(); @@ -719,3 +744,62 @@ int TriWorldToScreen( const float *world, float *screen ) return retval; } + +/* +================ +CL_AddCustomBeam + +Add the beam that encoded as custom entity +================ +*/ +void CL_AddCustomBeam( cl_entity_t *pEnvBeam ) +{ + if( g_lists.draw_list->num_beam_entities >= ARRAYSIZE(g_lists.draw_list->beam_entities) ) + { + gEngine.Con_Printf( S_ERROR "Too many beams %d!\n", g_lists.draw_list->num_beam_entities ); + return; + } + + if( pEnvBeam ) + { + g_lists.draw_list->beam_entities[g_lists.draw_list->num_beam_entities] = pEnvBeam; + g_lists.draw_list->num_beam_entities++; + } +} + +void CL_DrawBeams( int fTrans, BEAM *active_beams ) +{ + BEAM *pBeam; + int i, flags; + + // FIXME VK 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 < g_lists.draw_list->num_beam_entities; i++ ) + { + cl_entity_t *currentbeam = g_lists.draw_list->beam_entities[i]; + flags = currentbeam->curstate.rendermode & 0xF0; + + if( fTrans && FBitSet( flags, FBEAM_SOLID )) + continue; + + if( !fTrans && !FBitSet( flags, FBEAM_SOLID )) + continue; + + R_BeamDrawCustomEntity( currentbeam, g_frametime ); + // FIXME VK r_stats.c_view_beams_count++; + } + + // 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, g_frametime ); + } +} diff --git a/ref_vk/vk_scene.h b/ref_vk/vk_scene.h index 80c29f90..2d1bde7e 100644 --- a/ref_vk/vk_scene.h +++ b/ref_vk/vk_scene.h @@ -14,15 +14,6 @@ typedef struct vk_trans_entity_s { int render_mode; } vk_trans_entity_t; -typedef struct draw_list_s { - struct cl_entity_s *solid_entities[MAX_SCENE_ENTITIES]; // opaque moving or alpha brushes - vk_trans_entity_t trans_entities[MAX_SCENE_ENTITIES]; // translucent brushes or studio models kek - //cl_entity_t *beam_entities[MAX_DRAW_ENTITIES]; - uint num_solid_entities; - uint num_trans_entities; - //uint num_beam_entities; -} draw_list_t; - void VK_SceneInit( void ); void FIXME_VK_SceneSetViewPass( const struct ref_viewpass_s *rvp ); void VK_SceneRender( void ); @@ -37,3 +28,9 @@ void R_PopScene( void ); void R_NewMap( void ); void R_RenderScene( void ); + +// TODO should this be here? +int CL_FxBlend( struct cl_entity_s *e ); +struct beam_s; +void CL_DrawBeams( int fTrans, struct beam_s *active_beams ); +void CL_AddCustomBeam( struct cl_entity_s *pEnvBeam ); diff --git a/ref_vk/vk_sprite.c b/ref_vk/vk_sprite.c index 244d8650..d36ed702 100644 --- a/ref_vk/vk_sprite.c +++ b/ref_vk/vk_sprite.c @@ -650,11 +650,11 @@ static void R_DrawSpriteQuad( mspriteframe_t *frame, vec3_t org, vec3_t v_right, { vec3_t point; vk_buffer_alloc_t buf_vertex, buf_index; - brush_vertex_t *dst_vtx; + vk_vertex_t *dst_vtx; uint16_t *dst_idx; // Get buffer region for vertices and indices - buf_vertex = VK_RenderTempBufferAlloc( sizeof(brush_vertex_t), 4 ); + buf_vertex = VK_RenderTempBufferAlloc( sizeof(vk_vertex_t), 4 ); if (!buf_vertex.ptr) { gEngine.Con_Printf(S_ERROR "Cannot render mesh\n"); // TODO mesh signature? diff --git a/ref_vk/vk_studio.c b/ref_vk/vk_studio.c index b95929fe..caf1aaa1 100644 --- a/ref_vk/vk_studio.c +++ b/ref_vk/vk_studio.c @@ -432,13 +432,15 @@ static model_t *R_GetChromeSprite( void ) return gEngine.GetDefaultSprite( REF_CHROME_SPRITE ); } +static int fixme_studio_models_drawn; + static void pfnGetModelCounters( int **s, int **a ) { *s = &g_studio.framecount; /* FIXME VK NOT IMPLEMENTED */ /* *a = &r_stats.c_studio_models_drawn; */ - *a = 0; + *a = &fixme_studio_models_drawn; } static void pfnGetAliasScale( float *x, float *y ) @@ -1913,7 +1915,7 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float int i; int num_vertices = 0, num_indices = 0; vk_buffer_alloc_t buf_vertex, buf_index; - brush_vertex_t *dst_vtx; + vk_vertex_t *dst_vtx; uint16_t *dst_idx; uint32_t vertex_offset, index_offset; short* const ptricmds_initial = ptricmds; @@ -1938,7 +1940,7 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float ASSERT(num_indices > 0); // Get buffer region for vertices and indices - buf_vertex = VK_RenderTempBufferAlloc( sizeof(brush_vertex_t), num_vertices ); + buf_vertex = VK_RenderTempBufferAlloc( sizeof(vk_vertex_t), num_vertices ); if (!buf_vertex.ptr) { gEngine.Con_Printf(S_ERROR "Cannot render mesh\n"); // TODO mesh signature? @@ -1966,7 +1968,7 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float for(int j = 0; j < vertices ; ++j, ++dst_vtx, ptricmds += 4 ) { - ASSERT((((brush_vertex_t*)buf_vertex.ptr) + num_vertices) > dst_vtx); + ASSERT((((vk_vertex_t*)buf_vertex.ptr) + num_vertices) > dst_vtx); VectorCopy(g_studio.verts[ptricmds[0]], dst_vtx->pos); dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = mode == FAN ? .5f : 0.f; @@ -3507,7 +3509,6 @@ void CL_InitStudioAPI( void ) void VK_StudioInit( void ) { R_StudioInit(); - CL_InitStudioAPI(); } void VK_StudioShutdown( void ) diff --git a/ref_vk/vk_studio.h b/ref_vk/vk_studio.h index aed8fd9a..c3b9423b 100644 --- a/ref_vk/vk_studio.h +++ b/ref_vk/vk_studio.h @@ -16,3 +16,5 @@ void VK_StudioDrawModel( cl_entity_t *ent, int render_mode, int ubo_index ); void R_RunViewmodelEvents( void ); void R_DrawViewModel( int ubo_index ); + +void CL_InitStudioAPI( void );