mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2025-02-11 22:48:24 +00:00
1545 lines
49 KiB
C++
1545 lines
49 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2024 Gian 'myT' Schellenbaum
|
|
|
|
This file is part of Challenge Quake 3 (CNQ3).
|
|
|
|
Challenge Quake 3 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 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Challenge Quake 3 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.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
|
===========================================================================
|
|
*/
|
|
// Cinematic Rendering Pipeline - fog and particles lit by the sun and local lights
|
|
|
|
|
|
#include "crp_local.h"
|
|
#include "../client/cl_imgui.h"
|
|
#include "shaders/crp/scene_view.h.hlsli"
|
|
#include "shaders/crp/vl_common.h.hlsli"
|
|
#include "compshaders/crp/fullscreen.h"
|
|
#if defined(VL_CPU_PARTICLES)
|
|
#include "compshaders/crp/vl_extinction_injection_particles.h"
|
|
#include "compshaders/crp/vl_frustum_injection_particles.h"
|
|
#include "compshaders/crp/vl_particles_dispatch.h"
|
|
#include "compshaders/crp/vl_particles_preprocess_extinction.h"
|
|
#include "compshaders/crp/vl_particles_preprocess_frustum.h"
|
|
#endif
|
|
#include "compshaders/crp/vl_extinction_injection_fog.h"
|
|
#include "compshaders/crp/vl_frustum_anisotropy_average.h"
|
|
#include "compshaders/crp/vl_frustum_injection_fog.h"
|
|
#include "compshaders/crp/vl_frustum_inscatter_ambient.h"
|
|
#include "compshaders/crp/vl_frustum_inscatter_point_light.h"
|
|
#include "compshaders/crp/vl_frustum_inscatter_sunlight.h"
|
|
#include "compshaders/crp/vl_frustum_raymarch.h"
|
|
#include "compshaders/crp/vl_frustum_sunlight_visibility.h"
|
|
#include "compshaders/crp/vl_frustum_temporal.h"
|
|
#include "compshaders/crp/vl_shadow_point_light.h"
|
|
#include "compshaders/crp/vl_shadow_sun.h"
|
|
#include "compshaders/crp/vl_debug_ambient.h"
|
|
#include "compshaders/crp/vl_debug_extinction.h"
|
|
#include "compshaders/crp/vl_debug_shadow_sun.h"
|
|
|
|
|
|
#pragma pack(push, 4)
|
|
|
|
struct VLGlobalFogRC
|
|
{
|
|
FogVolume fog;
|
|
float time;
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t materialTextureCIndex;
|
|
};
|
|
|
|
struct VLParticlePreProcessRC
|
|
{
|
|
uvec3_t fullResolution;
|
|
uint32_t tileBufferIndex;
|
|
uvec3_t tileResolution;
|
|
uint32_t particleBufferIndex;
|
|
uvec3_t tileScale;
|
|
uint32_t particleCount;
|
|
};
|
|
|
|
struct VLParticlePreProcessExtinctionRC
|
|
{
|
|
uvec3_t fullResolution;
|
|
uint32_t tileBufferIndex;
|
|
uvec3_t tileResolution;
|
|
uint32_t particleBufferIndex;
|
|
uvec3_t tileScale;
|
|
uint32_t particleCount;
|
|
float extinctionWorldScale;
|
|
};
|
|
|
|
struct VLParticleDispatchRC
|
|
{
|
|
uvec3_t tileResolution;
|
|
uint32_t tileBufferIndex;
|
|
uint32_t dispatchBufferIndex;
|
|
uint32_t particleTileBufferIndex;
|
|
};
|
|
|
|
struct VLParticleRC
|
|
{
|
|
uvec3_t tileScale;
|
|
uint32_t particleBufferIndex;
|
|
uint32_t particleCount;
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t materialTextureCIndex;
|
|
uint32_t tileBufferIndex;
|
|
uint32_t tileCount;
|
|
};
|
|
|
|
struct VLParticleExtinctionRC
|
|
{
|
|
uvec3_t tileScale;
|
|
uint32_t particleBufferIndex;
|
|
uint32_t particleCount;
|
|
uint32_t extinctionTextureIndex;
|
|
uint32_t tileBufferIndex;
|
|
uint32_t tileCount;
|
|
float extinctionWorldScale;
|
|
};
|
|
|
|
struct VLAnisotropyRC
|
|
{
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t materialTextureCIndex;
|
|
};
|
|
|
|
struct VLSunlightVisRC
|
|
{
|
|
vec3_t jitter;
|
|
uint32_t visTextureIndex;
|
|
uint32_t depthMip;
|
|
};
|
|
|
|
struct VLSunlightRC
|
|
{
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t scatterExtTextureIndex;
|
|
uint32_t sunlightVisTextureIndex;
|
|
};
|
|
|
|
struct VLAmbientRC
|
|
{
|
|
vec3_t centerPosition;
|
|
uint32_t materialTextureAIndex;
|
|
vec3_t worldScale;
|
|
uint32_t scatterExtTextureIndex;
|
|
uint32_t ambientLightTextureAIndex;
|
|
uint32_t ambientLightTextureBIndex;
|
|
uint32_t ambientSamplerIndex;
|
|
uint32_t isLightGridAvailable;
|
|
};
|
|
|
|
struct VLRaymarchRC
|
|
{
|
|
uint32_t scatterTextureIndex;
|
|
uint32_t resolveTextureIndex;
|
|
uint32_t materialTextureBIndex;
|
|
};
|
|
|
|
struct VLExtinctionFogRC
|
|
{
|
|
FogVolume fog;
|
|
float time;
|
|
uint32_t extinctionTextureIndex;
|
|
float worldScale;
|
|
};
|
|
|
|
struct VLPointLightShadowRC
|
|
{
|
|
vec3_t lightPosition;
|
|
float extinctionWorldScale;
|
|
float shadowWorldScale;
|
|
uint32_t shadowTextureIndex;
|
|
};
|
|
|
|
struct VLSunlightShadowRC
|
|
{
|
|
uint32_t shadowTextureIndex;
|
|
uint32_t sourceTextureIndex;
|
|
float shadowWorldScale;
|
|
float sourceWorldScale;
|
|
};
|
|
|
|
struct VLPointLightScatterRC
|
|
{
|
|
DynamicLight light;
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t scatterExtTextureIndex;
|
|
uint32_t transmittanceTextureIndex;
|
|
uint32_t transmittanceSamplerIndex;
|
|
float shadowWorldScale;
|
|
};
|
|
|
|
struct VLExtinctionVizRC
|
|
{
|
|
vec3_t color;
|
|
float worldScale;
|
|
vec3_t cameraPosition;
|
|
float boxScale; // 1 for full size
|
|
float extinctionScale;
|
|
uint32_t extinctionTextureIndex;
|
|
};
|
|
|
|
struct VLSunShadowVizRC
|
|
{
|
|
vec3_t color;
|
|
float worldScale;
|
|
vec3_t cameraPosition;
|
|
float boxScale; // 1 for full size
|
|
uint32_t shadowTextureIndex;
|
|
};
|
|
|
|
struct VLAmbientVizRC
|
|
{
|
|
vec3_t centerPosition;
|
|
float sphereScale;
|
|
vec3_t worldScale;
|
|
uint32_t lightGridTextureAIndex;
|
|
uint32_t lightGridTextureBIndex;
|
|
};
|
|
|
|
struct VLTemporalRC
|
|
{
|
|
uint32_t currTextureIndex;
|
|
uint32_t prevTextureIndex;
|
|
uint32_t prevTextureSamplerIndex;
|
|
float alpha;
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
const float MaxFogCoordinate = 69420.0f;
|
|
|
|
|
|
static uint32_t ReverseBits32(uint32_t n)
|
|
{
|
|
n = (n << 16) | (n >> 16);
|
|
n = ((n & 0x00ff00ff) << 8) | ((n & 0xff00ff00) >> 8);
|
|
n = ((n & 0x0f0f0f0f) << 4) | ((n & 0xf0f0f0f0) >> 4);
|
|
n = ((n & 0x33333333) << 2) | ((n & 0xcccccccc) >> 2);
|
|
n = ((n & 0x55555555) << 1) | ((n & 0xaaaaaaaa) >> 1);
|
|
|
|
return n;
|
|
}
|
|
|
|
static float RadicalInverseBase2(uint32_t seqIndex)
|
|
{
|
|
return ReverseBits32(seqIndex) * 0x1p-32;
|
|
}
|
|
|
|
static float RadicalInverse(uint64_t seqIndex, uint32_t base)
|
|
{
|
|
const float oneMinusEpsilon = 0x1.fffffep-1;
|
|
const float invBase = 1.0f / (float)base;
|
|
uint32_t reversedDigits = 0;
|
|
float invBaseN = 1.0f;
|
|
while(seqIndex)
|
|
{
|
|
uint32_t next = seqIndex / base;
|
|
uint32_t digit = seqIndex - next * base;
|
|
reversedDigits = reversedDigits * base + digit;
|
|
invBaseN *= invBase;
|
|
seqIndex = next;
|
|
}
|
|
|
|
return fminf(reversedDigits * invBaseN, oneMinusEpsilon);
|
|
}
|
|
|
|
static float VanDerCorputSequence(uint32_t seqIndex)
|
|
{
|
|
return RadicalInverseBase2(seqIndex);
|
|
}
|
|
|
|
static void Halton23Sequence(vec2_t values01, uint32_t seqIndex)
|
|
{
|
|
values01[0] = RadicalInverseBase2(seqIndex);
|
|
values01[1] = RadicalInverse(seqIndex, 3);
|
|
}
|
|
|
|
static float Brightness(const vec3_t color)
|
|
{
|
|
return
|
|
color[0] * 0.299f +
|
|
color[1] * 0.587f +
|
|
color[2] * 0.114f;
|
|
}
|
|
|
|
static void ConvertFog(FogVolume& dst, const VolumetricLight::Fog& src, const VolumetricLight& vl)
|
|
{
|
|
const float scatter = src.albedo * src.extinction / Brightness(src.scatterColor);
|
|
VectorScale(src.scatterColor, scatter, dst.scatter);
|
|
dst.absorption = src.extinction - Brightness(dst.scatter);
|
|
VectorScale(src.emissiveColor, src.emissive, dst.emissive);
|
|
dst.anisotropy = src.anisotropy;
|
|
if(src.isGlobalFog)
|
|
{
|
|
VectorCopy(vl.mapBoxMin, dst.boxMin);
|
|
VectorCopy(vl.mapBoxMax, dst.boxMax);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(src.boxMin, dst.boxMin);
|
|
VectorCopy(src.boxMax, dst.boxMax);
|
|
}
|
|
dst.noiseMin = 1.0f;
|
|
dst.noiseMax = src.noiseStrength;
|
|
dst.noiseScale = 1.0f / src.noiseSpatialPeriod;
|
|
dst.noiseTimeScale = 1.0f / src.noiseTimePeriod;
|
|
dst.isHeightFog = src.isHeightFog;
|
|
}
|
|
|
|
static const float OpaqueTransmittanceThreshold = 1.0f / 256.0f;
|
|
static const float LnOpaqueTransmittanceThreshold = logf(OpaqueTransmittanceThreshold);
|
|
|
|
static float OpaqueDistanceToExtinction(float opaqueDistance)
|
|
{
|
|
return -LnOpaqueTransmittanceThreshold / opaqueDistance;
|
|
}
|
|
|
|
static float ExtinctionToOpaqueDistance(float extinction)
|
|
{
|
|
return -LnOpaqueTransmittanceThreshold / extinction;
|
|
}
|
|
|
|
#if defined(VL_CPU_PARTICLES)
|
|
|
|
void CRP_AddParticle(const vec3_t position, float radius, float alpha)
|
|
{
|
|
uint32_t& particleCount = crp.volumetricLight.particleCount;
|
|
if(particleCount >= MAX_PARTICLES)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Particle p = {};
|
|
VectorCopy(position, p.position);
|
|
p.radius = radius;
|
|
p.scattering[0] = 0.1f * alpha;
|
|
p.scattering[1] = 0.1f * alpha;
|
|
p.scattering[2] = 0.1f * alpha;
|
|
p.absorption = 0.1f * alpha;
|
|
p.anisotropy = 0.0f;
|
|
p.isEmissive = 0;
|
|
|
|
HBuffer buffer = crp.volumetricLight.particleBuffer;
|
|
Particle* const particle = (Particle*)MapBuffer(buffer) + particleCount;
|
|
memcpy(particle, &p, sizeof(p));
|
|
UnmapBuffer(buffer);
|
|
particleCount++;
|
|
}
|
|
|
|
// for use with billboarded quads such as CPMA's rocket smoke
|
|
void CRP_AddPolygonAsParticle(const polyVert_t* vertices, int vertexCount)
|
|
{
|
|
vec3_t bounds[2];
|
|
VectorCopy(vertices[0].xyz, bounds[0]);
|
|
VectorCopy(vertices[0].xyz, bounds[1]);
|
|
for(int i = 1; i < vertexCount; i++)
|
|
{
|
|
AddPointToBounds(vertices[i].xyz, bounds[0], bounds[1]);
|
|
}
|
|
|
|
vec3_t position;
|
|
VectorAdd(bounds[0], bounds[1], position);
|
|
VectorScale(position, 0.5f, position);
|
|
|
|
vec3_t extents;
|
|
VectorSubtract(bounds[1], bounds[0], extents);
|
|
|
|
const float radius = max(max(extents[0], extents[1]), extents[2]) * 0.5f;
|
|
const float alpha = (float)vertices[0].modulate[3] / 255.0f;
|
|
|
|
CRP_AddParticle(position, radius, alpha);
|
|
}
|
|
|
|
#endif
|
|
|
|
void VolumetricLight::Init()
|
|
{
|
|
if(srp.firstInit)
|
|
{
|
|
VectorSet(ambientColor, 0.125f, 0.125f, 0.125f);
|
|
ambientIntensity = 1.0f;
|
|
}
|
|
|
|
// patched on world load
|
|
VectorSet(mapBoxMin, -MaxFogCoordinate, -MaxFogCoordinate, -MaxFogCoordinate);
|
|
VectorSet(mapBoxMax, MaxFogCoordinate, MaxFogCoordinate, MaxFogCoordinate);
|
|
|
|
VectorSet(frustumSize, glConfig.vidWidth / 8, glConfig.vidHeight / 8, 256);
|
|
depthMip = 3;
|
|
VectorSet(frustumTileScale, 8, 8, 16); // x*y*z == 1024, must match the shader
|
|
VectorSet(frustumTileSize,
|
|
(frustumSize[0] + frustumTileScale[0] - 1) / frustumTileScale[0],
|
|
(frustumSize[1] + frustumTileScale[1] - 1) / frustumTileScale[1],
|
|
(frustumSize[2] + frustumTileScale[2] - 1) / frustumTileScale[2]);
|
|
VectorSet(extinctionSize, 128, 128, 128);
|
|
VectorSet(extinctionTileScale, 8, 8, 8); // 8*8*8 == 512, must match the shader
|
|
VectorSet(extinctionTileSize,
|
|
(extinctionSize[0] + extinctionTileScale[0] - 1) / extinctionTileScale[0],
|
|
(extinctionSize[1] + extinctionTileScale[1] - 1) / extinctionTileScale[1],
|
|
(extinctionSize[2] + extinctionTileScale[2] - 1) / extinctionTileScale[2]);
|
|
Vector4Set(extinctionVolumeScale, 8, 16, 32, 64); // patched on world load
|
|
VectorSet(sunShadowSize, 128, 128, 128);
|
|
Vector4Set(sunShadowVolumeScale, 8, 16, 32, 64); // patched on world load
|
|
shadowPixelCount = 64;
|
|
pointShadowVolumeScale = 8.0f;
|
|
jitterCounter = 0;
|
|
|
|
#if defined(VL_CPU_PARTICLES)
|
|
extinctionParticlePipeline = CreateComputePipeline("VL Extinction Particles", ShaderByteCode(g_vl_extinction_injection_particles_cs));
|
|
frustumParticlePipeline = CreateComputePipeline("VL Frustum Particles", ShaderByteCode(g_vl_frustum_injection_particles_cs));
|
|
particleDispatchPipeline = CreateComputePipeline("VL Particles Dispatch", ShaderByteCode(g_vl_particles_dispatch_cs));
|
|
particlePreProcessExtinctionPipeline = CreateComputePipeline("VL Particles Extinction Pre-process", ShaderByteCode(g_vl_particles_preprocess_extinction_cs));
|
|
particlePreProcessFrustumPipeline = CreateComputePipeline("VL Particles Frustum Pre-process", ShaderByteCode(g_vl_particles_preprocess_frustum_cs));
|
|
#endif
|
|
extinctionFogPipeline = CreateComputePipeline("VL Extinction Fog", ShaderByteCode(g_vl_extinction_injection_fog_cs));
|
|
frustumAmbientPipeline = CreateComputePipeline("VL Frustum Ambient Light Scatter", ShaderByteCode(g_vl_frustum_inscatter_ambient_cs));
|
|
frustumAnisotropyPipeline = CreateComputePipeline("VL Frustum Finalize Material", ShaderByteCode(g_vl_frustum_anisotropy_average_cs));
|
|
frustumFogPipeline = CreateComputePipeline("VL Frustum Fog", ShaderByteCode(g_vl_frustum_injection_fog_cs));
|
|
frustumRaymarchPipeline = CreateComputePipeline("VL Frustum Raymarch", ShaderByteCode(g_vl_frustum_raymarch_cs));
|
|
frustumTemporalPipeline = CreateComputePipeline("VL Frustum Temporal Reprojection", ShaderByteCode(g_vl_frustum_temporal_cs));
|
|
pointLightShadowPipeline = CreateComputePipeline("VL Shadow Raymarch Point Light", ShaderByteCode(g_vl_shadow_point_light_cs));
|
|
sunlightScatterPipeline = CreateComputePipeline("VL Frustum Sunlight Scatter", ShaderByteCode(g_vl_frustum_inscatter_sunlight_cs));
|
|
sunlightShadowPipeline = CreateComputePipeline("VL Shadow Raymarch Sun", ShaderByteCode(g_vl_shadow_sun_cs));
|
|
if(rhiInfo.hasInlineRaytracing)
|
|
{
|
|
frustumSunlightVisPipeline = CreateComputePipeline("VL Frustum Sunlight Visibility", ShaderByteCode(g_vl_frustum_sunlight_visibility_cs));
|
|
frustumPointLightScatterPipeline = CreateComputePipeline("VL Frustum Point Light Scatter", ShaderByteCode(g_vl_frustum_inscatter_point_light_cs));
|
|
}
|
|
else
|
|
{
|
|
frustumSunlightVisPipeline = RHI_MAKE_NULL_HANDLE();
|
|
frustumPointLightScatterPipeline = RHI_MAKE_NULL_HANDLE();
|
|
}
|
|
|
|
{
|
|
GraphicsPipelineDesc desc("VL Extinction Viz");
|
|
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
|
|
desc.vertexShader.Set(g_vl_debug_extinction_vs);
|
|
desc.pixelShader.Set(g_vl_debug_extinction_ps);
|
|
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
|
|
desc.depthStencil.depthComparison = ComparisonFunction::GreaterEqual;
|
|
desc.depthStencil.enableDepthTest = true;
|
|
desc.depthStencil.enableDepthWrites = true;
|
|
desc.rasterizer.cullMode = CT_TWO_SIDED; // @TODO:
|
|
desc.AddRenderTarget(0, crp.renderTargetFormat);
|
|
extinctionVizPipeline = CreateGraphicsPipeline(desc);
|
|
}
|
|
|
|
{
|
|
GraphicsPipelineDesc desc("VL Sun Shadow Viz");
|
|
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
|
|
desc.vertexShader.Set(g_vl_debug_shadow_sun_vs);
|
|
desc.pixelShader.Set(g_vl_debug_shadow_sun_ps);
|
|
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
|
|
desc.depthStencil.depthComparison = ComparisonFunction::GreaterEqual;
|
|
desc.depthStencil.enableDepthTest = true;
|
|
desc.depthStencil.enableDepthWrites = true;
|
|
desc.rasterizer.cullMode = CT_TWO_SIDED; // @TODO:
|
|
desc.AddRenderTarget(0, crp.renderTargetFormat);
|
|
sunShadowVizPipeline = CreateGraphicsPipeline(desc);
|
|
}
|
|
|
|
{
|
|
GraphicsPipelineDesc desc("VL Ambient Viz");
|
|
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
|
|
desc.vertexShader.Set(g_vl_debug_ambient_vs);
|
|
desc.pixelShader.Set(g_vl_debug_ambient_ps);
|
|
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
|
|
desc.depthStencil.depthComparison = ComparisonFunction::GreaterEqual;
|
|
desc.depthStencil.enableDepthTest = true;
|
|
desc.depthStencil.enableDepthWrites = true;
|
|
desc.rasterizer.cullMode = CT_TWO_SIDED; // @TODO:
|
|
desc.AddRenderTarget(0, crp.renderTargetFormat);
|
|
ambientVizPipeline = CreateGraphicsPipeline(desc);
|
|
}
|
|
|
|
{
|
|
TextureDesc desc("VL", frustumSize[0], frustumSize[1]);
|
|
desc.shortLifeTime = true;
|
|
desc.committedResource = true;
|
|
desc.initialState = ResourceStates::UnorderedAccessBit;
|
|
desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit;
|
|
desc.depth = frustumSize[2];
|
|
|
|
desc.name = "VL scatter/absorption";
|
|
desc.format = TextureFormat::R16G16B16A16_Float;
|
|
materialTextureA = CreateTexture(desc);
|
|
|
|
desc.name = "VL emissive/anisotropy";
|
|
desc.format = TextureFormat::R16G16B16A16_Float;
|
|
materialTextureB = CreateTexture(desc);
|
|
|
|
desc.name = "VL anisotropy counter";
|
|
desc.format = TextureFormat::R16_Float;
|
|
materialTextureC = CreateTexture(desc);
|
|
|
|
desc.name = "VL sunlight vis";
|
|
desc.format = TextureFormat::R8_UNorm;
|
|
sunlightVisTexture = CreateTexture(desc);
|
|
|
|
desc.name = "VL sunlight vis temporal";
|
|
desc.format = TextureFormat::R8_UNorm;
|
|
prevSunlightVisTexture = CreateTexture(desc);
|
|
|
|
desc.name = "VL in-scatter/ext";
|
|
desc.format = TextureFormat::R16G16B16A16_Float;
|
|
scatterExtTexture = CreateTexture(desc);
|
|
|
|
desc.name = "VL in-scatter/trans";
|
|
desc.format = TextureFormat::R16G16B16A16_Float;
|
|
scatterTransTexture = CreateTexture(desc);
|
|
|
|
desc.width = extinctionSize[0];
|
|
desc.height = extinctionSize[1];
|
|
desc.depth = extinctionSize[2];
|
|
desc.format = TextureFormat::R16_Float;
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
desc.name = va("VL extinction #%d", i + 1);
|
|
extinctionTextures[i] = CreateTexture(desc);
|
|
}
|
|
|
|
desc.width = sunShadowSize[0];
|
|
desc.height = sunShadowSize[1];
|
|
desc.depth = sunShadowSize[2];
|
|
desc.format = TextureFormat::R16_Float;
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
desc.name = va("VL sun shadow #%d", i + 1);
|
|
sunShadowTextures[i] = CreateTexture(desc);
|
|
}
|
|
|
|
desc.width = shadowPixelCount;
|
|
desc.height = shadowPixelCount;
|
|
desc.depth = shadowPixelCount;
|
|
desc.name = "VL point light shadow";
|
|
desc.format = TextureFormat::R16_Float;
|
|
pointShadowTexture = CreateTexture(desc);
|
|
|
|
ambientLightTextureA = RHI_MAKE_NULL_HANDLE(); // created on world load when available
|
|
ambientLightTextureB = RHI_MAKE_NULL_HANDLE(); // created on world load when available
|
|
}
|
|
|
|
#if defined(VL_CPU_PARTICLES)
|
|
const uint32_t tileCountF = frustumTileSize[0] * frustumTileSize[1] * frustumTileSize[2];
|
|
const uint32_t tileCountE = extinctionTileSize[0] * extinctionTileSize[1] * extinctionTileSize[2];
|
|
const uint32_t maxTileCount = max(tileCountF, tileCountE);
|
|
|
|
{
|
|
BufferDesc desc("particle", MAX_PARTICLES * sizeof(Particle), ResourceStates::ComputeShaderAccessBit);
|
|
desc.shortLifeTime = true;
|
|
desc.memoryUsage = MemoryUsage::Upload;
|
|
desc.structureByteCount = sizeof(Particle);
|
|
particleBuffer = CreateBuffer(desc);
|
|
}
|
|
|
|
{
|
|
const uint32_t tileSize = 4; // 1 uint
|
|
const uint32_t byteCount = maxTileCount * tileSize;
|
|
BufferDesc desc("particle hit", byteCount, ResourceStates::UnorderedAccessBit);
|
|
desc.shortLifeTime = true;
|
|
particleHitBuffer = CreateBuffer(desc);
|
|
}
|
|
|
|
{
|
|
const uint32_t tileSize = 12; // 3 uint
|
|
const uint32_t byteCount = maxTileCount * tileSize;
|
|
BufferDesc desc("particle tile", byteCount, ResourceStates::UnorderedAccessBit);
|
|
desc.shortLifeTime = true;
|
|
desc.structureByteCount = tileSize;
|
|
particleTileBuffer = CreateBuffer(desc);
|
|
}
|
|
|
|
{
|
|
BufferDesc desc("particle dispatch", 12, ResourceStates::UnorderedAccessBit);
|
|
desc.shortLifeTime = true;
|
|
particleDispatchBuffer = CreateBuffer(desc);
|
|
}
|
|
|
|
{
|
|
BufferDesc desc("particle dispatch clear", 12, ResourceStates::CopySourceBit);
|
|
desc.shortLifeTime = true;
|
|
desc.memoryUsage = MemoryUsage::Upload; // @TODO: not ideal...
|
|
particleDispatchClearBuffer = CreateBuffer(desc);
|
|
|
|
uint32_t* const groupCounts = (uint32_t*)MapBuffer(particleDispatchClearBuffer);
|
|
groupCounts[0] = 0;
|
|
groupCounts[1] = 1;
|
|
groupCounts[2] = 1;
|
|
UnmapBuffer(particleDispatchClearBuffer);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void VolumetricLight::ProcessWorld(world_t& world)
|
|
{
|
|
Q_assert(world.nodes != NULL);
|
|
Q_assert(world.numnodes > 0);
|
|
if(world.nodes == NULL || world.numnodes <= 0)
|
|
{
|
|
VectorSet(mapBoxMin, -MaxFogCoordinate, -MaxFogCoordinate, -MaxFogCoordinate);
|
|
VectorSet(mapBoxMax, MaxFogCoordinate, MaxFogCoordinate, MaxFogCoordinate);
|
|
return;
|
|
}
|
|
|
|
const mnode_t& node = world.nodes[0];
|
|
vec3_t mapDimensions;
|
|
VectorSubtract(node.maxs, node.mins, mapDimensions);
|
|
|
|
VectorCopy(node.mins, mapBoxMin);
|
|
VectorCopy(node.maxs, mapBoxMax);
|
|
|
|
if(world.lightGridData != NULL)
|
|
{
|
|
for(int i = 0; i < 3; i++)
|
|
{
|
|
lightGridCenter[i] =
|
|
world.lightGridOrigin[i] +
|
|
(world.lightGridBounds[i] * 0.5f - 0.5f) * world.lightGridSize[i];
|
|
}
|
|
|
|
TextureDesc desc("VL", world.lightGridBounds[0], world.lightGridBounds[1]);
|
|
desc.shortLifeTime = true;
|
|
desc.committedResource = true;
|
|
desc.initialState = ResourceStates::ComputeShaderAccessBit;
|
|
desc.allowedState = ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit;
|
|
desc.depth = world.lightGridBounds[2];
|
|
desc.format = TextureFormat::R8G8B8A8_UNorm;
|
|
desc.name = "VL ambient light A";
|
|
ambientLightTextureA = CreateTexture(desc);
|
|
desc.name = "VL ambient light B";
|
|
ambientLightTextureB = CreateTexture(desc);
|
|
|
|
MappedTexture texture;
|
|
BeginTextureUpload(texture, ambientLightTextureA);
|
|
const uint32_t rowCount = texture.rowCount * texture.sliceCount;
|
|
const uint32_t columnCount = texture.columnCount;
|
|
const uint32_t srcRowByteCount = world.lightGridBounds[0] * 8;
|
|
for(uint32_t r = 0; r < rowCount; r++)
|
|
{
|
|
uint32_t* dst = (uint32_t*)(texture.mappedData + r * texture.dstRowByteCount);
|
|
const uint32_t* src = (const uint32_t*)(world.lightGridData + r * srcRowByteCount);
|
|
for(uint32_t c = 0; c < columnCount; c++)
|
|
{
|
|
*dst = src[0];
|
|
dst += 1;
|
|
src += 2;
|
|
}
|
|
}
|
|
EndTextureUpload();
|
|
|
|
BeginTextureUpload(texture, ambientLightTextureB);
|
|
for(uint32_t r = 0; r < rowCount; r++)
|
|
{
|
|
uint32_t* dst = (uint32_t*)(texture.mappedData + r * texture.dstRowByteCount);
|
|
const uint32_t* src = (const uint32_t*)(world.lightGridData + r * srcRowByteCount);
|
|
for(uint32_t c = 0; c < columnCount; c++)
|
|
{
|
|
*dst = src[1];
|
|
dst += 1;
|
|
src += 2;
|
|
}
|
|
}
|
|
EndTextureUpload();
|
|
}
|
|
|
|
{
|
|
const float largest = max(max(mapDimensions[0], mapDimensions[1]), mapDimensions[2]);
|
|
const float scale3 = largest / (float)extinctionSize[0];
|
|
const float scale2 = extinctionVolumeScale[2];
|
|
float downScale = 1.0f;
|
|
while(scale3 <= 1.5f * scale2 * downScale)
|
|
{
|
|
downScale *= 0.5f;
|
|
}
|
|
extinctionVolumeScale[0] *= downScale;
|
|
extinctionVolumeScale[1] *= downScale;
|
|
extinctionVolumeScale[2] *= downScale;
|
|
extinctionVolumeScale[3] = scale3;
|
|
}
|
|
|
|
{
|
|
const float length = VectorLength(mapDimensions);
|
|
const float scale3 = length / (float)sunShadowSize[0];
|
|
const float scale2 = sunShadowVolumeScale[2];
|
|
float downScale = 1.0f;
|
|
while(scale3 <= 1.5f * scale2 * downScale)
|
|
{
|
|
downScale *= 0.5f;
|
|
}
|
|
sunShadowVolumeScale[0] *= downScale;
|
|
sunShadowVolumeScale[1] *= downScale;
|
|
sunShadowVolumeScale[2] *= downScale;
|
|
sunShadowVolumeScale[3] = scale3;
|
|
}
|
|
|
|
if(!LoadFogFile(va("fogs/%s.fogs", world.baseName)))
|
|
{
|
|
fogCount = 0;
|
|
|
|
// @NOTE: fog 0 is invalid
|
|
for(int f = 1; f < world.numfogs && fogCount < ARRAY_LEN(fogs); f++)
|
|
{
|
|
const float transmittanceThreshold = 1.0 / 256.0f;
|
|
const float lnThreshold = logf(transmittanceThreshold);
|
|
const fog_t& q3fog = world.fogs[f];
|
|
const float distance = q3fog.parms.depthForOpaque;
|
|
const float extinction = -lnThreshold / distance;
|
|
|
|
Fog& fog = fogs[fogCount++];
|
|
fog = {};
|
|
VectorCopy(q3fog.bounds[0], fog.boxMin);
|
|
VectorCopy(q3fog.bounds[1], fog.boxMax);
|
|
fog.extinction = extinction;
|
|
fog.albedo = 0.75f;
|
|
VectorCopy(q3fog.parms.color, fog.scatterColor);
|
|
fog.emissive = 0.0f;
|
|
VectorSet(fog.emissiveColor, 1, 1, 1);
|
|
fog.anisotropy = 0.0f;
|
|
fog.noiseStrength = 2.0f;
|
|
fog.noiseSpatialPeriod = 128.0f;
|
|
fog.noiseTimePeriod = 8.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawBegin()
|
|
{
|
|
{
|
|
SCOPED_RENDER_PASS("VL Clear", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
CmdTextureBarrier(extinctionTextures[i], ResourceStates::UnorderedAccessBit);
|
|
}
|
|
CmdEndBarrier();
|
|
|
|
const uint32_t values[4] = {};
|
|
CmdClearTextureUAV(materialTextureA, 0, values);
|
|
CmdClearTextureUAV(materialTextureB, 0, values);
|
|
CmdClearTextureUAV(materialTextureC, 0, values);
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
CmdClearTextureUAV(extinctionTextures[i], 0, values);
|
|
}
|
|
}
|
|
|
|
{
|
|
SCOPED_RENDER_PASS("VL Fog", 1.0f, 1.0f, 1.0f);
|
|
|
|
for(int f = 0; f < fogCount; f++)
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL Frustum Fog", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLGlobalFogRC rc = {};
|
|
ConvertFog(rc.fog, fogs[f], *this);
|
|
rc.time = backEnd.refdef.floatTime;
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.materialTextureCIndex = GetTextureIndexUAV(materialTextureC, 0);
|
|
|
|
CmdBindPipeline(frustumFogPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
for(int f = 0; f < fogCount; f++)
|
|
{
|
|
VLExtinctionFogRC rc = {};
|
|
ConvertFog(rc.fog, fogs[f], *this);
|
|
rc.time = backEnd.refdef.floatTime;
|
|
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL Extinction Fog", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
rc.extinctionTextureIndex = GetTextureIndexUAV(extinctionTextures[c], 0);
|
|
rc.worldScale = extinctionVolumeScale[c];
|
|
|
|
CmdBindPipeline(extinctionFogPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((extinctionSize[0] + 3) / 4, (extinctionSize[1] + 3) / 4, (extinctionSize[2] + 3) / 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(VL_CPU_PARTICLES)
|
|
if(particleCount > 0)
|
|
{
|
|
SCOPED_RENDER_PASS("VL Frustum Particles", 1.0f, 1.0f, 1.0f);
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Pre-pass", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleBuffer, ResourceStates::ComputeShaderAccessBit);
|
|
CmdBufferBarrier(particleHitBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdClearBufferUAV(particleHitBuffer, 0);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleHitBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticlePreProcessRC rc = {};
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleHitBuffer);
|
|
rc.particleBufferIndex = GetBufferIndexSRV(particleBuffer);
|
|
rc.particleCount = particleCount;
|
|
VectorCopy(frustumSize, rc.fullResolution);
|
|
VectorCopy(frustumTileSize, rc.tileResolution);
|
|
VectorCopy(frustumTileScale, rc.tileScale);
|
|
|
|
CmdBindPipeline(particlePreProcessFrustumPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((particleCount + 63) / 64, 1, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Clear Dispatch", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleDispatchClearBuffer, ResourceStates::CopySourceBit);
|
|
CmdBufferBarrier(particleDispatchBuffer, ResourceStates::CopyDestinationBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdCopyBuffer(particleDispatchBuffer, particleDispatchClearBuffer);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Prepare Dispatch", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleHitBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleDispatchBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleDispatchRC rc = {};
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleHitBuffer);
|
|
rc.dispatchBufferIndex = GetBufferIndexUAV(particleDispatchBuffer);
|
|
rc.particleTileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
VectorCopy(frustumTileSize, rc.tileResolution);
|
|
|
|
CmdBindPipeline(particleDispatchPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumTileSize[0] + 3) / 4, (frustumTileSize[1] + 3) / 4, (frustumTileSize[2] + 3) / 4);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Injection", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleBuffer, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleDispatchBuffer, ResourceStates::IndirectDispatchBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleRC rc = {};
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.materialTextureCIndex = GetTextureIndexUAV(materialTextureC, 0);
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
rc.particleBufferIndex = GetBufferIndexSRV(particleBuffer);
|
|
rc.particleCount = particleCount;
|
|
rc.tileCount = frustumTileSize[0] * frustumTileSize[1] * frustumTileSize[2];
|
|
VectorCopy(frustumTileScale, rc.tileScale);
|
|
|
|
CmdBindPipeline(frustumParticlePipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatchIndirect(particleDispatchBuffer, 0);
|
|
}
|
|
}
|
|
|
|
if(particleCount > 0)
|
|
{
|
|
SCOPED_RENDER_PASS("VL Extinction Particles", 1.0f, 1.0f, 1.0f);
|
|
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
{
|
|
SCOPED_DEBUG_LABEL("Pre-pass", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleBuffer, ResourceStates::ComputeShaderAccessBit);
|
|
CmdBufferBarrier(particleHitBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdClearBufferUAV(particleHitBuffer, 0);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleHitBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticlePreProcessExtinctionRC rc = {};
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleHitBuffer);
|
|
rc.particleBufferIndex = GetBufferIndexSRV(particleBuffer);
|
|
rc.particleCount = particleCount;
|
|
VectorCopy(extinctionSize, rc.fullResolution);
|
|
VectorCopy(extinctionTileSize, rc.tileResolution);
|
|
VectorCopy(extinctionTileScale, rc.tileScale);
|
|
rc.extinctionWorldScale = extinctionVolumeScale[c];
|
|
|
|
CmdBindPipeline(particlePreProcessExtinctionPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((particleCount + 63) / 64, 1, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Clear Dispatch", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleDispatchClearBuffer, ResourceStates::CopySourceBit);
|
|
CmdBufferBarrier(particleDispatchBuffer, ResourceStates::CopyDestinationBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdCopyBuffer(particleDispatchBuffer, particleDispatchClearBuffer);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Prepare Dispatch", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleHitBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleDispatchBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleDispatchRC rc = {};
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleHitBuffer);
|
|
rc.dispatchBufferIndex = GetBufferIndexUAV(particleDispatchBuffer);
|
|
rc.particleTileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
VectorCopy(extinctionTileSize, rc.tileResolution);
|
|
|
|
CmdBindPipeline(particleDispatchPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((extinctionTileSize[0] + 3) / 4, (extinctionTileSize[1] + 3) / 4, (extinctionTileSize[2] + 3) / 4);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Injection", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleBuffer, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleDispatchBuffer, ResourceStates::IndirectDispatchBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleExtinctionRC rc = {};
|
|
rc.extinctionTextureIndex = GetTextureIndexUAV(extinctionTextures[c], 0);
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
rc.particleBufferIndex = GetBufferIndexSRV(particleBuffer);
|
|
rc.particleCount = particleCount;
|
|
rc.tileCount = extinctionTileSize[0] * extinctionTileSize[1] * extinctionTileSize[2];
|
|
rc.extinctionWorldScale = extinctionVolumeScale[c];
|
|
VectorCopy(extinctionTileScale, rc.tileScale);
|
|
|
|
CmdBindPipeline(extinctionParticlePipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatchIndirect(particleDispatchBuffer, 0);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
{
|
|
SCOPED_RENDER_PASS("VL Anisotropy Avg", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLAnisotropyRC rc = {};
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.materialTextureCIndex = GetTextureIndexUAV(materialTextureC, 0);
|
|
|
|
CmdBindPipeline(frustumAnisotropyPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
{
|
|
SCOPED_RENDER_PASS("VL Ambient", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
if(tr.world->lightGridData != NULL)
|
|
{
|
|
CmdTextureBarrier(ambientLightTextureA, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(ambientLightTextureB, ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdEndBarrier();
|
|
|
|
VLAmbientRC rc = {};
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.scatterExtTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
rc.isLightGridAvailable = tr.world->lightGridData != NULL;
|
|
if(rc.isLightGridAvailable)
|
|
{
|
|
rc.ambientLightTextureAIndex = GetTextureIndexSRV(ambientLightTextureA);
|
|
rc.ambientLightTextureBIndex = GetTextureIndexSRV(ambientLightTextureB);
|
|
VectorCopy(lightGridCenter, rc.centerPosition);
|
|
VectorCopy(tr.world->lightGridSize, rc.worldScale);
|
|
rc.ambientSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
|
|
}
|
|
|
|
CmdBindPipeline(frustumAmbientPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
firstFrame = false;
|
|
}
|
|
|
|
void VolumetricLight::DrawPointLight(const dlight_t& light)
|
|
{
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL DL Shadow", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdTextureBarrier(pointShadowTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLPointLightShadowRC rc = {};
|
|
rc.shadowTextureIndex = GetTextureIndexUAV(pointShadowTexture, 0);
|
|
rc.extinctionWorldScale = extinctionVolumeScale[0]; // pick the highest resolution possible
|
|
rc.shadowWorldScale = pointShadowVolumeScale;
|
|
VectorCopy(light.origin, rc.lightPosition);
|
|
|
|
const uint32_t groupCount = (shadowPixelCount + 3) / 4;
|
|
CmdBindPipeline(pointLightShadowPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch(groupCount, groupCount, groupCount);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL DL Scatter", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(pointShadowTexture, ResourceStates::ComputeShaderAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLPointLightScatterRC rc = {};
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.scatterExtTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
rc.transmittanceTextureIndex = GetTextureIndexSRV(pointShadowTexture);
|
|
rc.transmittanceSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
|
|
rc.shadowWorldScale = pointShadowVolumeScale;
|
|
VectorCopy(light.origin, rc.light.position);
|
|
rc.light.radius = light.radius;
|
|
VectorCopy(light.color, rc.light.color);
|
|
|
|
CmdBindPipeline(frustumPointLightScatterPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawSunlight()
|
|
{
|
|
SCOPED_RENDER_PASS("VL Sunlight", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
for(int c = 0; c < 3; c++)
|
|
{
|
|
CmdTextureBarrier(sunShadowTextures[c], ResourceStates::UnorderedAccessBit);
|
|
}
|
|
CmdTextureBarrier(sunShadowTextures[3], ResourceStates::ComputeShaderAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
// each cascade needs to sample the higher level for setting the initial transmittance value
|
|
for(int c = 3; c >= 0; c--)
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL Sunlight Shadow", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(sunShadowTextures[c], ResourceStates::UnorderedAccessBit);
|
|
if(c < 3)
|
|
{
|
|
CmdTextureBarrier(sunShadowTextures[c + 1], ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdEndBarrier();
|
|
|
|
VLSunlightShadowRC rc = {};
|
|
rc.shadowTextureIndex = GetTextureIndexUAV(sunShadowTextures[c], 0);
|
|
rc.shadowWorldScale = sunShadowVolumeScale[c];
|
|
if(c < 3)
|
|
{
|
|
rc.sourceTextureIndex = GetTextureIndexSRV(sunShadowTextures[c + 1]);
|
|
rc.sourceWorldScale = sunShadowVolumeScale[c + 1];
|
|
}
|
|
|
|
CmdBindPipeline(sunlightShadowPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((sunShadowSize[0] + 7) / 8, (sunShadowSize[1] + 7) / 8, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL Sunlight Vis", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(crp.depthMinMaxTexture, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(sunlightVisTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
const float maxJitterDepthDistance = 16.0f;
|
|
const float maxJitterRadiusXY = 4.0f;
|
|
|
|
vec2_t point01;
|
|
Halton23Sequence(point01, jitterCounter);
|
|
|
|
// 6 points on the circle, rotated by 15 degrees
|
|
// ordered such that it oscillates around (0, 0)
|
|
const float jitterXY[] =
|
|
{
|
|
0.965926f, 0.258819f,
|
|
-0.965926f, -0.258819f,
|
|
0.707107f, -0.707107f,
|
|
-0.707107f, 0.707107f,
|
|
0.258819f, 0.965926f,
|
|
-0.258819f, -0.965926f
|
|
};
|
|
const int jitterXYSampleIndex = jitterCounter % 6;
|
|
|
|
VLSunlightVisRC rc = {};
|
|
rc.visTextureIndex = GetTextureIndexUAV(sunlightVisTexture, 0);
|
|
rc.depthMip = depthMip;
|
|
rc.jitter[0] = (0.5f * jitterXY[2 * jitterXYSampleIndex + 0]) * maxJitterRadiusXY;
|
|
rc.jitter[1] = (0.5f * jitterXY[2 * jitterXYSampleIndex + 1]) * maxJitterRadiusXY;
|
|
rc.jitter[2] = VanDerCorputSequence(jitterCounter) * maxJitterDepthDistance;
|
|
|
|
CmdBindPipeline(frustumSunlightVisPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
if(!firstFrame)
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL Sunlight Vis Temporal", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(prevSunlightVisTexture, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(sunlightVisTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLTemporalRC rc = {};
|
|
rc.currTextureIndex = GetTextureIndexUAV(sunlightVisTexture, 0);
|
|
rc.prevTextureIndex = GetTextureIndexSRV(prevSunlightVisTexture);
|
|
rc.prevTextureSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
|
|
rc.alpha = 0.9f;
|
|
|
|
CmdBindPipeline(frustumTemporalPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL Sunlight Vis Copy", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(sunlightVisTexture, ResourceStates::CopySourceBit);
|
|
CmdTextureBarrier(prevSunlightVisTexture, ResourceStates::CopyDestinationBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdCopyTexture(prevSunlightVisTexture, sunlightVisTexture);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL Sunlight", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(sunShadowTextures[c], ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(sunlightVisTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLSunlightRC rc = {};
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.sunlightVisTextureIndex = GetTextureIndexUAV(sunlightVisTexture, 0);
|
|
rc.scatterExtTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
|
|
CmdBindPipeline(sunlightScatterPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawEnd()
|
|
{
|
|
SCOPED_RENDER_PASS("VL Raymarch", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterTransTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLRaymarchRC rc = {};
|
|
rc.scatterTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
rc.resolveTextureIndex = GetTextureIndexUAV(scatterTransTexture, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
|
|
CmdBindPipeline(frustumRaymarchPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 7) / 8, (frustumSize[1] + 7) / 8, 1);
|
|
|
|
jitterCounter++;
|
|
}
|
|
|
|
void VolumetricLight::DrawDebug()
|
|
{
|
|
if(!ShouldDrawDebug())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(drawExtinctionDebug)
|
|
{
|
|
SCOPED_RENDER_PASS("VL Extinction Viz", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::UnorderedAccessBit);
|
|
}
|
|
CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
|
|
CmdEndBarrier();
|
|
|
|
const int c = debugExtinctionCascadeIndex;
|
|
VLExtinctionVizRC rc = {};
|
|
rc.extinctionTextureIndex = GetTextureIndexUAV(extinctionTextures[c], 0);
|
|
rc.worldScale = extinctionVolumeScale[c];
|
|
rc.boxScale = debugBoxScale;
|
|
rc.extinctionScale = debugExtinctionScale;
|
|
VectorCopy(debugCameraPosition, rc.cameraPosition);
|
|
VectorCopy(colorRed, rc.color);
|
|
|
|
const uint32_t voxelCount = extinctionSize[0] * extinctionSize[1] * extinctionSize[2];
|
|
const uint32_t vertexCount = voxelCount * 36;
|
|
CmdBindRenderTargets(1, &crp.renderTarget, &crp.depthTexture);
|
|
CmdBindPipeline(extinctionVizPipeline);
|
|
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
|
|
CmdDraw(vertexCount, 0);
|
|
}
|
|
|
|
if(drawSunShadowDebug)
|
|
{
|
|
SCOPED_RENDER_PASS("VL Sun Shadow Viz", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(sunShadowTextures[c], ResourceStates::UnorderedAccessBit);
|
|
}
|
|
CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
|
|
CmdEndBarrier();
|
|
|
|
const int c = debugSunShadowCascadeIndex;
|
|
VLSunShadowVizRC rc = {};
|
|
rc.shadowTextureIndex = GetTextureIndexUAV(sunShadowTextures[c], 0);
|
|
rc.worldScale = sunShadowVolumeScale[c];
|
|
rc.boxScale = debugBoxScale;
|
|
VectorCopy(debugCameraPosition, rc.cameraPosition);
|
|
VectorCopy(colorRed, rc.color);
|
|
|
|
const uint32_t voxelCount = sunShadowSize[0] * sunShadowSize[1] * sunShadowSize[2];
|
|
const uint32_t vertexCount = voxelCount * 36;
|
|
CmdBindRenderTargets(1, &crp.renderTarget, &crp.depthTexture);
|
|
CmdBindPipeline(sunShadowVizPipeline);
|
|
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
|
|
CmdDraw(vertexCount, 0);
|
|
}
|
|
|
|
if(drawAmbientDebug && tr.world->lightGridData != NULL)
|
|
{
|
|
SCOPED_RENDER_PASS("VL Ambient Viz", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(ambientLightTextureA, ResourceStates::PixelShaderAccessBit);
|
|
CmdTextureBarrier(ambientLightTextureB, ResourceStates::PixelShaderAccessBit);
|
|
CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
|
|
CmdEndBarrier();
|
|
|
|
VLAmbientVizRC rc = {};
|
|
rc.lightGridTextureAIndex = GetTextureIndexSRV(ambientLightTextureA);
|
|
rc.lightGridTextureBIndex = GetTextureIndexSRV(ambientLightTextureB);
|
|
VectorCopy(tr.world->lightGridSize, rc.worldScale);
|
|
rc.sphereScale = debugSphereScale;
|
|
VectorCopy(lightGridCenter, rc.centerPosition);
|
|
|
|
const uint32_t voxelCount = tr.world->lightGridBounds[0] * tr.world->lightGridBounds[1] * tr.world->lightGridBounds[2];
|
|
const uint32_t vertexCount = voxelCount * 6;
|
|
CmdBindRenderTargets(1, &crp.renderTarget, &crp.depthTexture);
|
|
CmdBindPipeline(ambientVizPipeline);
|
|
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
|
|
CmdDraw(vertexCount, 0);
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawGUI()
|
|
{
|
|
if(!tr.worldMapLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GUI_AddMainMenuItem(GUI_MainMenu::Tools, "Edit Volumetrics", "", &windowActive);
|
|
|
|
if(!lockCameraPosition)
|
|
{
|
|
VectorCopy(backEnd.viewParms.world.viewOrigin, debugCameraPosition);
|
|
}
|
|
|
|
if(!windowActive)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(ImGui::Begin("Volumetric Data", &windowActive, ImGuiWindowFlags_AlwaysAutoResize))
|
|
{
|
|
const ImGuiColorEditFlags colorEditFlags =
|
|
ImGuiColorEditFlags_Float |
|
|
ImGuiColorEditFlags_NoAlpha |
|
|
ImGuiColorEditFlags_DisplayRGB |
|
|
ImGuiColorEditFlags_InputRGB;
|
|
ImGui::ColorEdit3("Ambient light color", ambientColor, colorEditFlags);
|
|
ImGui::SliderFloat("Ambient light intensity", &ambientIntensity, 0.0f, 10.0f);
|
|
|
|
ImGui::Separator();
|
|
|
|
if(fogCount < ARRAY_LEN(fogs) && ImGui::Button("Add Fog"))
|
|
{
|
|
const float fogSize = 40.0f;
|
|
Fog& fog = fogs[fogCount++];
|
|
fog = {};
|
|
fog.extinction = 0.1f;
|
|
fog.albedo = 0.5f;
|
|
VectorSet(fog.scatterColor, 1, 1, 1);
|
|
fog.emissive = 0.0f;
|
|
VectorSet(fog.emissiveColor, 1, 1, 1);
|
|
fog.anisotropy = 0.0f;
|
|
fog.noiseStrength = 1.0f;
|
|
fog.noiseSpatialPeriod = 50.0f;
|
|
fog.noiseTimePeriod = 8.0f;
|
|
fog.isHeightFog = false;
|
|
fog.isGlobalFog = false;
|
|
for(int a = 0; a < 3; a++)
|
|
{
|
|
fog.boxMin[a] = backEnd.refdef.vieworg[a] - fogSize;
|
|
fog.boxMax[a] = backEnd.refdef.vieworg[a] + fogSize;
|
|
}
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if(fogCount > 0 && ImGui::Button("Save Config..."))
|
|
{
|
|
OpenSaveFileDialog("fogs", ".fogs");
|
|
}
|
|
ImGui::SameLine();
|
|
if(ImGui::Button("Open Config..."))
|
|
{
|
|
OpenOpenFileDialog("fogs", ".fogs");
|
|
}
|
|
|
|
const char* axisNames[3] = { "X", "Y", "Z" };
|
|
|
|
ImGui::BeginTabBar("Tabs#Fogs", ImGuiTabBarFlags_AutoSelectNewTabs);
|
|
for(int i = 0; i < fogCount; i++)
|
|
{
|
|
if(ImGui::BeginTabItem(va("#%d", i + 1)))
|
|
{
|
|
Fog& fog = fogs[i];
|
|
ImGui::Checkbox("Global fog", &fog.isGlobalFog);
|
|
if(!fog.isGlobalFog)
|
|
{
|
|
for(int a = 0; a < 3; a++)
|
|
{
|
|
ImGui::SliderFloat(va("Box min %s", axisNames[a]), &fog.boxMin[a], mapBoxMin[a], mapBoxMax[a], "%g");
|
|
ImGui::SliderFloat(va("Box max %s", axisNames[a]), &fog.boxMax[a], mapBoxMin[a], mapBoxMax[a], "%g");
|
|
}
|
|
}
|
|
float opaqueDistance = ExtinctionToOpaqueDistance(fog.extinction);
|
|
ImGui::SliderFloat("Distance to opaque", &opaqueDistance, 1.0f, 10000.0f, "%g");
|
|
fog.extinction = OpaqueDistanceToExtinction(opaqueDistance);
|
|
ImGui::ColorEdit3("Reflective (scatter) color", fog.scatterColor, colorEditFlags);
|
|
ImGui::SliderFloat("Reflectivity (albedo)", &fog.albedo, 0.0f, 1.0f, "%g");
|
|
ImGui::SliderFloat("Directionality (anisotropy)", &fog.anisotropy, 0.0f, 1.0f, "%g");
|
|
ImGui::SliderFloat("Emissive strength", &fog.emissive, 0.0f, 0.001f, "%g");
|
|
ImGui::ColorEdit3("Emissive color", fog.emissiveColor, colorEditFlags);
|
|
ImGui::SliderFloat("Noise strength", &fog.noiseStrength, 1.0f, 10.0f, "%g");
|
|
ImGui::SliderFloat("Noise space period", &fog.noiseSpatialPeriod, 0.0f, 200.0f, "%g");
|
|
ImGui::SliderFloat("Noise time period", &fog.noiseTimePeriod, 0.0f, 20.0f, "%g");
|
|
ImGui::Checkbox("Height fog", &fog.isHeightFog);
|
|
ImGui::EndTabItem();
|
|
|
|
ImGui::Separator();
|
|
if(ImGui::Button("Remove"))
|
|
{
|
|
if(i < fogCount - 1)
|
|
{
|
|
memmove(&fogs[i], &fogs[i + 1], (fogCount - 1 - i) * sizeof(fogs[0]));
|
|
}
|
|
fogCount--;
|
|
}
|
|
}
|
|
}
|
|
if(ImGui::BeginTabItem("Debug"))
|
|
{
|
|
if(tr.world->lightGridData != NULL)
|
|
{
|
|
ImGui::Checkbox("Draw ambient light grid", &drawAmbientDebug);
|
|
ImGui::SliderFloat("Voxel sphere scale", &debugSphereScale, 0.25f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "No valid light grid was found for this map");
|
|
}
|
|
|
|
ImGui::NewLine();
|
|
ImGui::SliderInt("Extinction cascade", &debugExtinctionCascadeIndex, 0, 3);
|
|
ImGui::Checkbox("Draw extinction volume", &drawExtinctionDebug);
|
|
ImGui::SliderFloat("Extinction value scale", &debugExtinctionScale, 1.0f, 1000.0f);
|
|
|
|
ImGui::NewLine();
|
|
ImGui::SliderInt("Sun shadow cascade", &debugSunShadowCascadeIndex, 0, 3);
|
|
ImGui::Checkbox("Draw sun shadow volume", &drawSunShadowDebug);
|
|
|
|
ImGui::NewLine();
|
|
ImGui::Checkbox("Lock camera position", &lockCameraPosition);
|
|
ImGui::SliderFloat("Voxel box scale", &debugBoxScale, 0.25f, 1.0f);
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
ImGui::EndTabBar();
|
|
|
|
if(SaveFileDialog())
|
|
{
|
|
SaveFogFile(GetSaveFileDialogPath());
|
|
}
|
|
|
|
if(OpenFileDialog())
|
|
{
|
|
LoadFogFile(GetOpenFileDialogPath());
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
bool VolumetricLight::WantRTASUpdate(const trRefdef_t& scene)
|
|
{
|
|
return crp_volLight->integer != 0;
|
|
}
|
|
|
|
bool VolumetricLight::ShouldDraw()
|
|
{
|
|
if(crp_volLight->integer == 0 ||
|
|
(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) != 0 ||
|
|
!IsViewportFullscreen(backEnd.viewParms) ||
|
|
lockCameraPosition)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VolumetricLight::ShouldDrawDebug()
|
|
{
|
|
if(crp_volLight->integer == 0 ||
|
|
(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) != 0 ||
|
|
!IsViewportFullscreen(backEnd.viewParms))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return drawExtinctionDebug || drawSunShadowDebug || drawAmbientDebug;
|
|
}
|
|
|
|
bool VolumetricLight::LoadFogFile(const char* filePath)
|
|
{
|
|
bool success = false;
|
|
void* data = NULL;
|
|
const int byteCount = ri.FS_ReadFile(filePath, &data);
|
|
if(data != NULL &&
|
|
byteCount > 0 &&
|
|
byteCount <= sizeof(fogs) &&
|
|
(byteCount % sizeof(fogs[0])) == 0)
|
|
{
|
|
memcpy(&fogs[0], data, byteCount);
|
|
fogCount = byteCount / sizeof(fogs[0]);
|
|
success = true;
|
|
}
|
|
if(data != NULL)
|
|
{
|
|
ri.FS_FreeFile(data);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void VolumetricLight::SaveFogFile(const char* filePath)
|
|
{
|
|
if(fogCount > 0)
|
|
{
|
|
FS_EnableCNQ3FolderWrites(qtrue);
|
|
ri.FS_WriteFile(filePath, &fogs[0], fogCount * sizeof(fogs[0]));
|
|
FS_EnableCNQ3FolderWrites(qfalse);
|
|
}
|
|
}
|