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