From 67f592d5ac3ec13da3481e75b22e8a73d196cd65 Mon Sep 17 00:00:00 2001 From: turanszkij Date: Thu, 13 Sep 2018 18:38:43 +0100 Subject: [PATCH] mainly hair particle updates --- Editor/Editor.cpp | 24 +++ Editor/EmitterWindow.cpp | 4 +- Editor/HairParticleWindow.cpp | 17 ++- Editor/HairParticleWindow.h | 1 + Editor/WorldWindow.cpp | 8 + Editor/WorldWindow.h | 1 + WickedEngine/ShaderInterop_HairParticle.h | 20 +++ WickedEngine/WickedEngine_SHADERS.vcxproj | 4 + .../WickedEngine_SHADERS.vcxproj.filters | 3 + WickedEngine/WickedEngine_SHARED.vcxitems | 1 + .../WickedEngine_SHARED.vcxitems.filters | 3 + WickedEngine/emittedparticle_emitCS.hlsl | 6 +- WickedEngine/hairparticleHF.hlsli | 12 +- WickedEngine/hairparticlePS_deferred.hlsl | 1 + WickedEngine/hairparticlePS_forward.hlsl | 1 + WickedEngine/hairparticlePS_simplest.hlsl | 4 +- WickedEngine/hairparticlePS_tiledforward.hlsl | 1 + WickedEngine/hairparticleVS.hlsl | 14 +- WickedEngine/hairparticle_simulateCS.hlsl | 109 ++++++++++++++ WickedEngine/wiHairParticle.cpp | 141 ++++++++++++++---- WickedEngine/wiHairParticle.h | 29 +--- WickedEngine/wiRenderer.cpp | 34 ++++- WickedEngine/wiSceneSystem.cpp | 28 +++- WickedEngine/wiSceneSystem.h | 1 + 24 files changed, 381 insertions(+), 86 deletions(-) create mode 100644 WickedEngine/ShaderInterop_HairParticle.h create mode 100644 WickedEngine/hairparticle_simulateCS.hlsl diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index 7042662e9..fac7fb3c2 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -1694,6 +1694,24 @@ void EditorComponent::Render() { wiRenderer::AddRenderableBox(decal->world, XMFLOAT4(0.5f, 0, 0.5f, 0.5f)); } + + const EnvironmentProbeComponent* probe = scene.probes.GetComponent(hovered.entity); + if (probe != nullptr) + { + const AABB& aabb = *scene.aabb_probes.GetComponent(hovered.entity); + + XMFLOAT4X4 hoverBox; + XMStoreFloat4x4(&hoverBox, aabb.getAsBoxMatrix()); + wiRenderer::AddRenderableBox(hoverBox, XMFLOAT4(0.5f, 0.5f, 0.5f, 0.5f)); + } + + const wiHairParticle* hair = scene.hairs.GetComponent(hovered.entity); + if (hair != nullptr) + { + XMFLOAT4X4 hoverBox; + XMStoreFloat4x4(&hoverBox, hair->aabb.getAsBoxMatrix()); + wiRenderer::AddRenderableBox(hoverBox, XMFLOAT4(0, 0.5f, 0, 0.5f)); + } } } @@ -1739,6 +1757,12 @@ void EditorComponent::Render() selectedAABB = AABB::Merge(selectedAABB, aabb); } + const wiHairParticle* hair = scene.hairs.GetComponent(picked->entity); + if (hair != nullptr) + { + selectedAABB = AABB::Merge(selectedAABB, hair->aabb); + } + } } diff --git a/Editor/EmitterWindow.cpp b/Editor/EmitterWindow.cpp index 1a89d4305..f4799fc67 100644 --- a/Editor/EmitterWindow.cpp +++ b/Editor/EmitterWindow.cpp @@ -253,7 +253,7 @@ EmitterWindow::EmitterWindow(wiGUI* gui) : GUI(gui) emitScalingSlider->SetTooltip("Set the scaling of the particles based on their lifetime."); emitterWindow->AddWidget(emitScalingSlider); - emitLifeSlider = new wiSlider(0.0f, 1000.0f, 1.0f, 100000, "Life span: "); + emitLifeSlider = new wiSlider(0.0f, 100.0f, 1.0f, 10000, "Life span: "); emitLifeSlider->SetSize(XMFLOAT2(360, 30)); emitLifeSlider->SetPos(XMFLOAT2(x, y += step)); emitLifeSlider->OnSlide([&](wiEventArgs args) { @@ -264,7 +264,7 @@ EmitterWindow::EmitterWindow(wiGUI* gui) : GUI(gui) } }); emitLifeSlider->SetEnabled(false); - emitLifeSlider->SetTooltip("Set the lifespan of the emitted particles."); + emitLifeSlider->SetTooltip("Set the lifespan of the emitted particles (in seconds)."); emitterWindow->AddWidget(emitLifeSlider); emitRandomnessSlider = new wiSlider(0.0f, 1.0f, 1.0f, 100000, "Randomness: "); diff --git a/Editor/HairParticleWindow.cpp b/Editor/HairParticleWindow.cpp index 45aade731..6d07bbf21 100644 --- a/Editor/HairParticleWindow.cpp +++ b/Editor/HairParticleWindow.cpp @@ -55,6 +55,20 @@ HairParticleWindow::HairParticleWindow(wiGUI* gui) : GUI(gui) meshComboBox->SetTooltip("Choose an animation clip..."); hairWindow->AddWidget(meshComboBox); + lengthSlider = new wiSlider(0, 4, 1, 100000, "Particle Length: "); + lengthSlider->SetSize(XMFLOAT2(360, 30)); + lengthSlider->SetPos(XMFLOAT2(x, y += step * 2)); + lengthSlider->OnSlide([&](wiEventArgs args) { + auto hair = GetHair(); + if (hair != nullptr) + { + hair->length = args.fValue; + } + }); + lengthSlider->SetEnabled(false); + lengthSlider->SetTooltip("Set hair strand length"); + hairWindow->AddWidget(lengthSlider); + countSlider = new wiSlider(0, 100000, 1000, 100000, "Particle Count: "); countSlider->SetSize(XMFLOAT2(360, 30)); countSlider->SetPos(XMFLOAT2(x, y += step * 2)); @@ -62,7 +76,7 @@ HairParticleWindow::HairParticleWindow(wiGUI* gui) : GUI(gui) auto hair = GetHair(); if (hair != nullptr) { - hair->particleCount = (size_t)args.iValue; + hair->particleCount = (uint32_t)args.iValue; } }); countSlider->SetEnabled(false); @@ -115,6 +129,7 @@ void HairParticleWindow::SetEntity(Entity entity) if (hair != nullptr) { + lengthSlider->SetValue(hair->length); countSlider->SetValue((float)hair->particleCount); } else diff --git a/Editor/HairParticleWindow.h b/Editor/HairParticleWindow.h index 3ac900d02..20d108db6 100644 --- a/Editor/HairParticleWindow.h +++ b/Editor/HairParticleWindow.h @@ -30,6 +30,7 @@ public: wiButton* addButton; wiComboBox* meshComboBox; + wiSlider* lengthSlider; wiSlider* countSlider; wiButton* generateButton; diff --git a/Editor/WorldWindow.cpp b/Editor/WorldWindow.cpp index 65e6b5e11..cbfbadb86 100644 --- a/Editor/WorldWindow.cpp +++ b/Editor/WorldWindow.cpp @@ -73,6 +73,14 @@ WorldWindow::WorldWindow(wiGUI* gui) : GUI(gui) }); worldWindow->AddWidget(cloudSpeedSlider); + windSpeedSlider = new wiSlider(0.001f, 0.2f, 0.1f, 10000, "Wind Speed: "); + windSpeedSlider->SetSize(XMFLOAT2(100, 30)); + windSpeedSlider->SetPos(XMFLOAT2(x, y += step)); + windSpeedSlider->OnSlide([&](wiEventArgs args) { + wiRenderer::GetScene().windDirection = XMFLOAT3(args.fValue, 0, 0); + }); + worldWindow->AddWidget(windSpeedSlider); + skyButton = new wiButton("Load Sky"); skyButton->SetTooltip("Load a skybox cubemap texture..."); diff --git a/Editor/WorldWindow.h b/Editor/WorldWindow.h index c360c75c5..fd0156308 100644 --- a/Editor/WorldWindow.h +++ b/Editor/WorldWindow.h @@ -24,6 +24,7 @@ public: wiSlider* cloudinessSlider; wiSlider* cloudScaleSlider; wiSlider* cloudSpeedSlider; + wiSlider* windSpeedSlider; wiButton* skyButton; wiColorPicker* ambientColorPicker; wiColorPicker* horizonColorPicker; diff --git a/WickedEngine/ShaderInterop_HairParticle.h b/WickedEngine/ShaderInterop_HairParticle.h new file mode 100644 index 000000000..76cf20fd0 --- /dev/null +++ b/WickedEngine/ShaderInterop_HairParticle.h @@ -0,0 +1,20 @@ +#ifndef _SHADERINTEROP_HAIRPARTICLE_H_ +#define _SHADERINTEROP_HAIRPARTICLE_H_ + +#include "ShaderInterop.h" + +#define THREADCOUNT_SIMULATEHAIR 256 + +static const uint particleBuffer_stride = 16 + 4 + 4; // pos, normal, tangent + +CBUFFER(HairParticleCB, CBSLOT_OTHER_HAIRPARTICLE) +{ + float4x4 xWorld; + float4 xColor; + float xLength; + float LOD0; + float LOD1; + float LOD2; +}; + +#endif // _SHADERINTEROP_HAIRPARTICLE_H_ diff --git a/WickedEngine/WickedEngine_SHADERS.vcxproj b/WickedEngine/WickedEngine_SHADERS.vcxproj index 7f9f01727..e559a5d2e 100644 --- a/WickedEngine/WickedEngine_SHADERS.vcxproj +++ b/WickedEngine/WickedEngine_SHADERS.vcxproj @@ -302,6 +302,10 @@ Pixel + + Compute + 5.0 + Pixel diff --git a/WickedEngine/WickedEngine_SHADERS.vcxproj.filters b/WickedEngine/WickedEngine_SHADERS.vcxproj.filters index 6c2d6588f..7e98f230c 100644 --- a/WickedEngine/WickedEngine_SHADERS.vcxproj.filters +++ b/WickedEngine/WickedEngine_SHADERS.vcxproj.filters @@ -789,6 +789,9 @@ CS + + CS + diff --git a/WickedEngine/WickedEngine_SHARED.vcxitems b/WickedEngine/WickedEngine_SHARED.vcxitems index 62d20658e..dde93101f 100644 --- a/WickedEngine/WickedEngine_SHARED.vcxitems +++ b/WickedEngine/WickedEngine_SHARED.vcxitems @@ -234,6 +234,7 @@ + diff --git a/WickedEngine/WickedEngine_SHARED.vcxitems.filters b/WickedEngine/WickedEngine_SHARED.vcxitems.filters index 7445b59e5..98a88b451 100644 --- a/WickedEngine/WickedEngine_SHARED.vcxitems.filters +++ b/WickedEngine/WickedEngine_SHARED.vcxitems.filters @@ -1134,6 +1134,9 @@ ENGINE\System + + ENGINE\Graphics\GPUMapping + diff --git a/WickedEngine/emittedparticle_emitCS.hlsl b/WickedEngine/emittedparticle_emitCS.hlsl index df351cdf0..544eefcb8 100644 --- a/WickedEngine/emittedparticle_emitCS.hlsl +++ b/WickedEngine/emittedparticle_emitCS.hlsl @@ -24,7 +24,9 @@ void main(uint3 DTid : SV_DispatchThreadID) { // we can emit: - const float3 randoms = randomTex.SampleLevel(sampler_linear_wrap, float2((float)DTid.x / (float)THREADCOUNT_EMIT, g_xFrame_Time + xEmitterRandomness), 0).rgb; + const float global_seed = g_xFrame_Time + xEmitterRandomness; + const float thread_seed = (float)((DTid.x + g_xFrame_FrameCount) % THREADCOUNT_EMIT) / (float)THREADCOUNT_EMIT; + const float3 randoms = randomTex.SampleLevel(sampler_point_wrap, float2(thread_seed, global_seed), 0).rgb; #ifdef EMIT_FROM_MESH // random triangle on emitter surface: @@ -63,7 +65,7 @@ void main(uint3 DTid : SV_DispatchThreadID) } // random barycentric coords: - float f = randoms.x; + float f = randoms.z; float g = randoms.y; [flatten] if (f + g > 1) diff --git a/WickedEngine/hairparticleHF.hlsli b/WickedEngine/hairparticleHF.hlsli index af984a3b6..1c5bdb6ee 100644 --- a/WickedEngine/hairparticleHF.hlsli +++ b/WickedEngine/hairparticleHF.hlsli @@ -1,17 +1,6 @@ #ifndef _HAIRPARTICLE_HF_ #define _HAIRPARTICLE_HF_ - -CBUFFER(HairParticleCB, CBSLOT_OTHER_HAIRPARTICLE) -{ - float4x4 xWorld; - float4 xColor; - float LOD0; - float LOD1; - float LOD2; - float __pad1; -} - struct VertexToPixel { float4 pos : SV_POSITION; @@ -21,6 +10,7 @@ struct VertexToPixel float fade : DITHERFADE; float4 pos2D : SCREENPOSITION; float4 pos2DPrev : SCREENPOSITIONPREV; + float3 color : COLOR; }; #endif // _HAIRPARTICLE_HF_ diff --git a/WickedEngine/hairparticlePS_deferred.hlsl b/WickedEngine/hairparticlePS_deferred.hlsl index 0e9d1a6f1..c2719bed0 100644 --- a/WickedEngine/hairparticlePS_deferred.hlsl +++ b/WickedEngine/hairparticlePS_deferred.hlsl @@ -10,6 +10,7 @@ GBUFFEROutputType main(VertexToPixel input) #endif float4 color = texture_0.Sample(sampler_linear_clamp, input.tex); + color.rgb *= input.color; ALPHATEST(color.a) color = DEGAMMA(color); float emissive = 0; diff --git a/WickedEngine/hairparticlePS_forward.hlsl b/WickedEngine/hairparticlePS_forward.hlsl index 0b91b508a..6f1e3e9d9 100644 --- a/WickedEngine/hairparticlePS_forward.hlsl +++ b/WickedEngine/hairparticlePS_forward.hlsl @@ -10,6 +10,7 @@ GBUFFEROutputType_Thin main(VertexToPixel input) #endif float4 color = texture_0.Sample(sampler_linear_clamp, input.tex); + color.rgb *= input.color; ALPHATEST(color.a) float opacity = 1; // keep edge diffuse shading color.rgb = DEGAMMA(color.rgb); diff --git a/WickedEngine/hairparticlePS_simplest.hlsl b/WickedEngine/hairparticlePS_simplest.hlsl index 3bca133a3..a20d54300 100644 --- a/WickedEngine/hairparticlePS_simplest.hlsl +++ b/WickedEngine/hairparticlePS_simplest.hlsl @@ -1,7 +1,7 @@ #include "globals.hlsli" #include "hairparticleHF.hlsli" -float4 main() : SV_TARGET +float4 main(VertexToPixel input) : SV_TARGET { - return float4(xColor.rgb, 1.0f); + return float4(input.color, 1.0f); } \ No newline at end of file diff --git a/WickedEngine/hairparticlePS_tiledforward.hlsl b/WickedEngine/hairparticlePS_tiledforward.hlsl index 3efe3ad65..3f083384d 100644 --- a/WickedEngine/hairparticlePS_tiledforward.hlsl +++ b/WickedEngine/hairparticlePS_tiledforward.hlsl @@ -9,6 +9,7 @@ GBUFFEROutputType_Thin main(VertexToPixel input) { float4 color = texture_0.Sample(sampler_linear_clamp, input.tex); + color.rgb *= input.color; color.a *= 1.0 - input.fade; clip(color.a - 1.0f / 256.0f); // cancel heaviest overdraw for the alpha composition effect float opacity = 1; diff --git a/WickedEngine/hairparticleVS.hlsl b/WickedEngine/hairparticleVS.hlsl index 3017287a5..c35c91006 100644 --- a/WickedEngine/hairparticleVS.hlsl +++ b/WickedEngine/hairparticleVS.hlsl @@ -1,5 +1,6 @@ #include "globals.hlsli" #include "hairparticleHF.hlsli" +#include "ShaderInterop_HairParticle.h" static const float hairPopDistanceThreshold = 0.9f; @@ -20,7 +21,6 @@ static const float3 HAIRPATCH[] = { float3(0, 1, 1), }; -static const uint particleBuffer_stride = 16 + 4 + 4; // pos, normal, tangent RAWBUFFER(particleBuffer, 0); VertexToPixel main(uint fakeIndex : SV_VERTEXID) @@ -39,7 +39,7 @@ VertexToPixel main(uint fakeIndex : SV_VERTEXID) // convert the raw loaded particle data: float3 pos = posLen.xyz; - float len = posLen.w; + float len = posLen.w * xLength; float3 normal; uint rand; normal.x = (normalRand >> 0) & 0x000000FF; @@ -72,16 +72,16 @@ VertexToPixel main(uint fakeIndex : SV_VERTEXID) float3 windPrev = sin(g_xFrame_TimePrev + (pos.x + pos.y + pos.z))*g_xFrame_WindDirection.xyz * patchPos.y * 0.03f; // transform particle by the emitter object matrix: - pos.xyz = mul(float4(pos.xyz, 1), xWorld).xyz; - normal = mul(normal, (float3x3)xWorld); - tangent = mul(tangent, (float3x3)xWorld); + pos.xyz = mul(xWorld, float4(pos.xyz, 1)).xyz; + normal = mul((float3x3)xWorld, normal); + tangent = mul((float3x3)xWorld, tangent); // rotate the patch into the tangent space of the emitting triangle: float3x3 TBN = float3x3(tangent, normal, cross(normal, tangent)); patchPos = mul(patchPos, TBN); // inset to the emitter a bit, to avoid disconnect: - pos.xyz -= normal*0.1*len; + pos.xyz -= normal * 0.1*len; // copy to output: @@ -99,5 +99,7 @@ VertexToPixel main(uint fakeIndex : SV_VERTEXID) Out.pos2D = Out.pos; Out.pos2DPrev = mul(float4(savedPos + windPrev, 1), g_xFrame_MainCamera_PrevVP); + Out.color = xColor.rgb; + return Out; } diff --git a/WickedEngine/hairparticle_simulateCS.hlsl b/WickedEngine/hairparticle_simulateCS.hlsl new file mode 100644 index 000000000..cce89864f --- /dev/null +++ b/WickedEngine/hairparticle_simulateCS.hlsl @@ -0,0 +1,109 @@ +#include "globals.hlsli" +#include "ShaderInterop_HairParticle.h" + +RWRAWBUFFER(particleBuffer, 0); + +RAWBUFFER(targetBuffer, 0); + +#define NUM_LDS_FORCEFIELDS 32 +struct LDS_ForceField +{ + uint type; + float3 position; + float gravity; + float range_inverse; + float3 normal; +}; +groupshared LDS_ForceField forceFields[NUM_LDS_FORCEFIELDS]; + +[numthreads(THREADCOUNT_SIMULATEHAIR, 1, 1)] +void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) +{ + // Load the forcefields into LDS: + uint numForceFields = min(g_xFrame_ForceFieldArrayCount, NUM_LDS_FORCEFIELDS); + if (Gid < numForceFields) + { + uint forceFieldID = g_xFrame_ForceFieldArrayOffset + Gid; + ShaderEntityType forceField = EntityArray[forceFieldID]; + + forceFields[Gid].type = (uint)forceField.type; + forceFields[Gid].position = forceField.positionWS; + forceFields[Gid].gravity = forceField.energy; + forceFields[Gid].range_inverse = forceField.range; + forceFields[Gid].normal = forceField.directionWS; + } + + GroupMemoryBarrierWithGroupSync(); + + + const uint instanceID = DTid.x; + + // Fetch from particle buffer: + const uint fetchAddress = instanceID * particleBuffer_stride; + float4 posLen = asfloat(particleBuffer.Load4(fetchAddress)); + uint normalRand = particleBuffer.Load(fetchAddress + 16); + + // Decompress particle properties: + float3 pos = posLen.xyz; + float len = posLen.w * xLength; + float3 normal; + normal.x = (normalRand >> 0) & 0x000000FF; + normal.y = (normalRand >> 8) & 0x000000FF; + normal.z = (normalRand >> 16) & 0x000000FF; + normal = normal / 255.0f * 2 - 1; + + // Fetch from target buffer + uint uTarget = targetBuffer.Load(instanceID * 4); + + // Decompress target: + float3 target; + target.x = (uTarget >> 0) & 0x000000FF; + target.y = (uTarget >> 8) & 0x000000FF; + target.z = (uTarget >> 16) & 0x000000FF; + target = target / 255.0f * 2 - 1; + + // transform particle by the emitter object matrix: + pos.xyz = mul(xWorld, float4(pos.xyz, 1)).xyz; + normal = mul((float3x3)xWorld, normal); + target = mul((float3x3)xWorld, target); + + // Accumulate forces: + float3 force = 0; + for (uint i = 0; i < numForceFields; ++i) + { + LDS_ForceField forceField = forceFields[i]; + + float3 dir = forceField.position - pos; + float dist; + if (forceField.type == ENTITY_TYPE_FORCEFIELD_POINT) // point-based force field + { + dist = length(dir); + } + else // planar force field + { + dist = dot(forceField.normal, dir); + dir = forceField.normal; + } + + force += dir * forceField.gravity * (1 - saturate(dist * forceField.range_inverse)); + } + + // Apply forces: + normal += force * g_xFrame_DeltaTime; + + // Apply rest: + normal = lerp(normal, target, 0.1f); + + // Transform back to mesh space and renormalize: + normal = mul(normal, (float3x3)xWorld); // transposed mul! + normal = normalize(normal); + + + // Compress normal and store: + uint uNormal = 0; + uNormal |= (uint)((normal.x * 0.5f + 0.5f) * 255.0f) << 0; + uNormal |= (uint)((normal.y * 0.5f + 0.5f) * 255.0f) << 8; + uNormal |= (uint)((normal.z * 0.5f + 0.5f) * 255.0f) << 16; + uNormal |= normalRand & 0xFF000000; + particleBuffer.Store(fetchAddress + 16, uNormal); +} diff --git a/WickedEngine/wiHairParticle.cpp b/WickedEngine/wiHairParticle.cpp index dabf46df1..d4c68b624 100644 --- a/WickedEngine/wiHairParticle.cpp +++ b/WickedEngine/wiHairParticle.cpp @@ -9,6 +9,7 @@ #include "ShaderInterop.h" #include "wiTextureHelper.h" #include "wiSceneSystem.h" +#include "ShaderInterop_HairParticle.h" using namespace std; using namespace wiGraphicsTypes; @@ -19,11 +20,13 @@ namespace wiSceneSystem VertexShader *wiHairParticle::vs = nullptr; PixelShader *wiHairParticle::ps[]; PixelShader *wiHairParticle::ps_simplest = nullptr; +ComputeShader *wiHairParticle::cs_simulate = nullptr; DepthStencilState wiHairParticle::dss_default, wiHairParticle::dss_equal, wiHairParticle::dss_rejectopaque_keeptransparent; RasterizerState wiHairParticle::rs, wiHairParticle::ncrs, wiHairParticle::wirers; BlendState wiHairParticle::bs[2]; GraphicsPSO wiHairParticle::PSO[SHADERTYPE_COUNT][2]; GraphicsPSO wiHairParticle::PSO_wire; +ComputePSO wiHairParticle::CPSO_simulate; int wiHairParticle::LOD[3]; void wiHairParticle::CleanUpStatic() @@ -33,6 +36,9 @@ void wiHairParticle::CleanUpStatic() { SAFE_DELETE(ps[i]); } + + SAFE_DELETE(ps_simplest); + SAFE_DELETE(cs_simulate); } void wiHairParticle::LoadShaders() { @@ -115,16 +121,27 @@ void wiHairParticle::LoadShaders() SAFE_INIT(ps_simplest); ps_simplest = static_cast(wiResourceManager::GetShaderManager()->add(wiRenderer::SHADERPATH + "hairparticlePS_simplest.cso", wiResourceManager::PIXELSHADER)); - GraphicsPSODesc desc; - desc.vs = vs; - desc.ps = ps_simplest; - desc.bs = &bs[0]; - desc.rs = &wirers; - desc.dss = &dss_default; - desc.numRTs = 1; - desc.RTFormats[0] = wiRenderer::RTFormat_hdr; - desc.DSFormat = wiRenderer::DSFormat_full; - device->CreateGraphicsPSO(&desc, &PSO_wire); + { + GraphicsPSODesc desc; + desc.vs = vs; + desc.ps = ps_simplest; + desc.bs = &bs[0]; + desc.rs = &wirers; + desc.dss = &dss_default; + desc.numRTs = 1; + desc.RTFormats[0] = wiRenderer::RTFormat_hdr; + desc.DSFormat = wiRenderer::DSFormat_full; + device->CreateGraphicsPSO(&desc, &PSO_wire); + } + + SAFE_INIT(cs_simulate); + cs_simulate = static_cast(wiResourceManager::GetShaderManager()->add(wiRenderer::SHADERPATH + "hairparticle_simulateCS.cso", wiResourceManager::COMPUTESHADER)); + + { + ComputePSODesc desc; + desc.cs = cs_simulate; + device->CreateComputePSO(&desc, &CPSO_simulate); + } } void wiHairParticle::SetUpStatic() { @@ -224,11 +241,20 @@ void wiHairParticle::Settings(int l0,int l1,int l2) void wiHairParticle::Generate(const MeshComponent& mesh) { + struct Patch + { + XMFLOAT4 posLen; + UINT normalRand; + UINT tangent; + }; + static_assert(sizeof(Patch) == particleBuffer_stride, "Mismatch!"); + std::vector points(particleCount); + std::vector targets(particleCount); - // Now the distribution is completely uniform. TODO: bring back distribution, but make it more intuitive to set up! + // Now the distribution is uniform. TODO: bring back weight-based distribution, but make it more intuitive to set up! (vertex colors?) - for (UINT i = 0; i < particleCount; ++i) + for (uint32_t i = 0; i < particleCount; ++i) { int tri = wiRandom::getRandom(0, (int)((mesh.indices.size() - 1) / 3)); @@ -246,45 +272,102 @@ void wiHairParticle::Generate(const MeshComponent& mesh) float f = wiRandom::getRandom(0, 1000) * 0.001f; float g = wiRandom::getRandom(0, 1000) * 0.001f; + if (f + g > 1) + { + f = 1 - f; + g = 1 - g; + } XMVECTOR P = XMVectorBaryCentric(p0, p1, p2, f, g); XMVECTOR N = XMVectorBaryCentric(n0, n1, n2, f, g); - XMVECTOR T = XMVector3Normalize(XMVectorSubtract(p0, p1)); + XMVECTOR T = XMVector3Normalize(XMVectorSubtract(i % 2 == 0 ? p0 : p2, p1)); - XMStoreFloat4(&points[i].posLen, P); - points[i].posLen.w = 1.0f; - - XMFLOAT3 normal, tangent; + XMFLOAT3 position, normal, tangent; + XMStoreFloat3(&position, P); XMStoreFloat3(&normal, N); XMStoreFloat3(&tangent, T); - points[i].normalRand = wiMath::CompressNormal(normal); + points[i].posLen = XMFLOAT4(position.x, position.y, position.z, 1.0f); + + uint32_t uNormal = wiMath::CompressNormal(normal); + points[i].normalRand = uNormal; + points[i].normalRand |= ((uint32_t)wiRandom::getRandom(0, 256)) << 24; + points[i].tangent = wiMath::CompressNormal(tangent); + + targets[i] = uNormal; } cb.reset(new GPUBuffer); particleBuffer.reset(new GPUBuffer); + targetBuffer.reset(new GPUBuffer); GPUBufferDesc bd; - ZeroMemory(&bd, sizeof(bd)); - bd.Usage = USAGE_IMMUTABLE; - bd.ByteWidth = (UINT)(sizeof(Patch) * particleCount); - bd.BindFlags = BIND_VERTEX_BUFFER | BIND_SHADER_RESOURCE; + bd.Usage = USAGE_DEFAULT; + bd.ByteWidth = sizeof(Patch) * particleCount; + bd.BindFlags = BIND_SHADER_RESOURCE | BIND_UNORDERED_ACCESS; bd.CPUAccessFlags = 0; bd.MiscFlags = RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + SubresourceData data = {}; data.pSysMem = points.data(); wiRenderer::GetDevice()->CreateBuffer(&bd, &data, particleBuffer.get()); - ZeroMemory(&bd, sizeof(bd)); + bd.BindFlags = BIND_SHADER_RESOURCE; + bd.ByteWidth = sizeof(uint32_t) * particleCount; + data.pSysMem = targets.data(); + wiRenderer::GetDevice()->CreateBuffer(&bd, &data, targetBuffer.get()); + bd.Usage = USAGE_DYNAMIC; - bd.ByteWidth = sizeof(ConstantBuffer); + bd.ByteWidth = sizeof(HairParticleCB); bd.BindFlags = BIND_CONSTANT_BUFFER; bd.CPUAccessFlags = CPU_ACCESS_WRITE; + bd.MiscFlags = 0; wiRenderer::GetDevice()->CreateBuffer(&bd, nullptr, cb.get()); } +void wiHairParticle::UpdateRenderData(const MaterialComponent& material, GRAPHICSTHREAD threadID) +{ + if (cb == nullptr) + { + return; + } + + GraphicsDevice* device = wiRenderer::GetDevice(); + device->EventBegin("HairParticle - UpdateRenderData", threadID); + + device->BindComputePSO(&CPSO_simulate, threadID); + + HairParticleCB hcb; + hcb.xWorld = world; + hcb.xColor = material.baseColor; + hcb.xLength = length; + hcb.LOD0 = (float)LOD[0]; + hcb.LOD1 = (float)LOD[1]; + hcb.LOD2 = (float)LOD[2]; + device->UpdateBuffer(cb.get(), &hcb, threadID); + + device->BindConstantBuffer(CS, cb.get(), CB_GETBINDSLOT(HairParticleCB), threadID); + + GPUResource* res[] = { + targetBuffer.get() + }; + device->BindResources(CS, res, 0, ARRAYSIZE(res), threadID); + + GPUResource* uavs[] = { + particleBuffer.get() + }; + device->BindUAVs(CS, uavs, 0, ARRAYSIZE(uavs), threadID); + + device->Dispatch((UINT)ceilf((float)particleCount / (float)THREADCOUNT_SIMULATEHAIR), 1, 1, threadID); + + device->UnbindResources(0, ARRAYSIZE(res), threadID); + device->UnbindUAVs(0, ARRAYSIZE(uavs), threadID); + + device->EventEnd(threadID); +} + void wiHairParticle::Draw(CameraComponent* camera, const MaterialComponent& material, SHADERTYPE shaderType, bool transparent, GRAPHICSTHREAD threadID) const { if (cb == nullptr) @@ -315,15 +398,7 @@ void wiHairParticle::Draw(CameraComponent* camera, const MaterialComponent& mate device->BindResources(VS, res, TEXSLOT_ONDEMAND0, ARRAYSIZE(res), threadID); } - ConstantBuffer gcb; - gcb.mWorld = XMMatrixTranspose(XMLoadFloat4x4(&world)); - gcb.color = material.baseColor; - gcb.LOD0 = (float)LOD[0]; - gcb.LOD1 = (float)LOD[1]; - gcb.LOD2 = (float)LOD[2]; - device->UpdateBuffer(cb.get(), &gcb, threadID); - - device->BindConstantBuffer(VS, cb.get(), CB_GETBINDSLOT(ConstantBuffer), threadID); + device->BindConstantBuffer(VS, cb.get(), CB_GETBINDSLOT(HairParticleCB), threadID); device->BindResource(VS, particleBuffer.get(), 0, threadID); diff --git a/WickedEngine/wiHairParticle.h b/WickedEngine/wiHairParticle.h index 4318b3f43..e9880cbec 100644 --- a/WickedEngine/wiHairParticle.h +++ b/WickedEngine/wiHairParticle.h @@ -4,6 +4,7 @@ #include "ShaderInterop.h" #include "wiECS.h" #include "wiSceneSystem_Decl.h" +#include "wiIntersectables.h" class wiArchive; @@ -12,52 +13,38 @@ namespace wiSceneSystem class wiHairParticle { -public: - struct Patch - { - XMFLOAT4 posLen; - UINT normalRand; - UINT tangent; - }; private: - CBUFFER(ConstantBuffer, CBSLOT_OTHER_HAIRPARTICLE) - { - XMMATRIX mWorld; - XMFLOAT4 color; - float LOD0; - float LOD1; - float LOD2; - float __pad1; - }; - std::unique_ptr cb; std::unique_ptr particleBuffer; + std::unique_ptr targetBuffer; static wiGraphicsTypes::VertexShader *vs; static wiGraphicsTypes::PixelShader *ps[SHADERTYPE_COUNT]; static wiGraphicsTypes::PixelShader *ps_simplest; + static wiGraphicsTypes::ComputeShader *cs_simulate; static wiGraphicsTypes::DepthStencilState dss_default, dss_equal, dss_rejectopaque_keeptransparent; static wiGraphicsTypes::RasterizerState rs, ncrs, wirers; static wiGraphicsTypes::BlendState bs[2]; // opaque, transparent static wiGraphicsTypes::GraphicsPSO PSO[SHADERTYPE_COUNT][2]; // shadertype * transparency static wiGraphicsTypes::GraphicsPSO PSO_wire; + static wiGraphicsTypes::ComputePSO CPSO_simulate; static int LOD[3]; public: static void LoadShaders(); -public: - void Generate(const MeshComponent& mesh); - void Draw(wiSceneSystem::CameraComponent* camera, const MaterialComponent& material, SHADERTYPE shaderType, bool transparent, GRAPHICSTHREAD threadID) const; + void UpdateRenderData(const MaterialComponent& material, GRAPHICSTHREAD threadID); + void Draw(CameraComponent* camera, const MaterialComponent& material, SHADERTYPE shaderType, bool transparent, GRAPHICSTHREAD threadID) const; static void CleanUpStatic(); static void SetUpStatic(); static void Settings(int lod0,int lod1,int lod2); float length = 1.0f; - size_t particleCount = 0; + uint32_t particleCount = 0; wiECS::Entity meshID = wiECS::INVALID_ENTITY; XMFLOAT4X4 world; + AABB aabb; }; } diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 2b99df0c7..a35c1071a 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -3477,6 +3477,20 @@ void wiRenderer::UpdateRenderData(GRAPHICSTHREAD threadID) emitter.UpdateRenderData(transform, material, mesh, threadID); } + // Hair particle systems simulation: + for (size_t i = 0; i < scene.hairs.GetCount(); ++i) + { + wiHairParticle& hair = scene.hairs[i]; + + if (getCamera()->frustum.CheckBox(hair.aabb)) + { + Entity entity = scene.hairs.GetEntity(i); + const MaterialComponent& material = *scene.materials.GetComponent(entity); + + hair.UpdateRenderData(material, threadID); + } + } + // Compute water simulation: if (ocean != nullptr) { @@ -5326,10 +5340,14 @@ void wiRenderer::DrawWorld(CameraComponent* camera, bool tessellation, GRAPHICST for (size_t i = 0; i < scene.hairs.GetCount(); ++i) { const wiHairParticle& hair = scene.hairs[i]; - Entity entity = scene.hairs.GetEntity(i); - const MaterialComponent& material = *scene.materials.GetComponent(entity); - hair.Draw(camera, material, shaderType, false, threadID); + if (camera->frustum.CheckBox(hair.aabb)) + { + Entity entity = scene.hairs.GetEntity(i); + const MaterialComponent& material = *scene.materials.GetComponent(entity); + + hair.Draw(camera, material, shaderType, false, threadID); + } } } @@ -5366,10 +5384,14 @@ void wiRenderer::DrawWorldTransparent(CameraComponent* camera, SHADERTYPE shader for (size_t i = 0; i < scene.hairs.GetCount(); ++i) { const wiHairParticle& hair = scene.hairs[i]; - Entity entity = scene.hairs.GetEntity(i); - const MaterialComponent& material = *scene.materials.GetComponent(entity); - hair.Draw(camera, material, shaderType, true, threadID); + if (camera->frustum.CheckBox(hair.aabb)) + { + Entity entity = scene.hairs.GetEntity(i); + const MaterialComponent& material = *scene.materials.GetComponent(entity); + + hair.Draw(camera, material, shaderType, true, threadID); + } } } diff --git a/WickedEngine/wiSceneSystem.cpp b/WickedEngine/wiSceneSystem.cpp index 7e85d8ff7..1c7f0cb9e 100644 --- a/WickedEngine/wiSceneSystem.cpp +++ b/WickedEngine/wiSceneSystem.cpp @@ -785,7 +785,7 @@ namespace wiSceneSystem RunLightUpdateSystem(*cameras.GetComponent(wiRenderer::getCameraID()), transforms, aabb_lights, lights, sunDirection, sunColor); - RunParticleUpdateSystem(transforms, emitters, hairs, dt); + RunParticleUpdateSystem(transforms, meshes, emitters, hairs, dt); } void Scene::Clear() @@ -1083,7 +1083,7 @@ namespace wiSceneSystem transform.Translate(position); transform.UpdateTransform(); - materials.Create(entity); + materials.Create(entity).blendFlag = BLENDMODE_ALPHA; return entity; } @@ -1811,6 +1811,7 @@ namespace wiSceneSystem } void RunParticleUpdateSystem( const ComponentManager& transforms, + const ComponentManager& meshes, ComponentManager& emitters, ComponentManager& hairs, float dt @@ -1828,6 +1829,29 @@ namespace wiSceneSystem Entity entity = hairs.GetEntity(i); const TransformComponent& transform = *transforms.GetComponent(entity); hair.world = transform.world; + + if (hair.meshID != INVALID_ENTITY) + { + const MeshComponent* mesh = meshes.GetComponent(hair.meshID); + + if (mesh != nullptr) + { + XMFLOAT3 min = mesh->aabb.getMin(); + XMFLOAT3 max = mesh->aabb.getMax(); + + max.x += hair.length; + max.y += hair.length; + max.z += hair.length; + + min.x -= hair.length; + min.y -= hair.length; + min.z -= hair.length; + + hair.aabb.create(min, max); + hair.aabb = hair.aabb.get(hair.world); + } + } + } } diff --git a/WickedEngine/wiSceneSystem.h b/WickedEngine/wiSceneSystem.h index 98cf6c490..b6f69397e 100644 --- a/WickedEngine/wiSceneSystem.h +++ b/WickedEngine/wiSceneSystem.h @@ -849,6 +849,7 @@ namespace wiSceneSystem ); void RunParticleUpdateSystem( const wiECS::ComponentManager& transforms, + const wiECS::ComponentManager& meshes, wiECS::ComponentManager& emitters, wiECS::ComponentManager& hairs, float dt