diff --git a/Content/models/emitter_fluid_50k.wiscene b/Content/models/emitter_fluid_50k.wiscene index 7a891bf1f..889fa0db9 100644 Binary files a/Content/models/emitter_fluid_50k.wiscene and b/Content/models/emitter_fluid_50k.wiscene differ diff --git a/Editor/ColliderWindow.cpp b/Editor/ColliderWindow.cpp index beb108044..c1ca6dd3f 100644 --- a/Editor/ColliderWindow.cpp +++ b/Editor/ColliderWindow.cpp @@ -37,6 +37,7 @@ void ColliderWindow::Create(EditorComponent* _editor) shapeCombo.SetPos(XMFLOAT2(x, y)); shapeCombo.AddItem("Sphere", (uint64_t)ColliderComponent::Shape::Sphere); shapeCombo.AddItem("Capsule", (uint64_t)ColliderComponent::Shape::Capsule); + shapeCombo.AddItem("Plane", (uint64_t)ColliderComponent::Shape::Plane); shapeCombo.OnSelect([=](wi::gui::EventArgs args) { wi::scene::Scene& scene = editor->GetCurrentScene(); ColliderComponent* collider = scene.colliders.GetComponent(entity); diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index dbed48855..77c7d42aa 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -1001,7 +1001,7 @@ void EditorComponent::Update(float dt) optionsWnd.RefreshEntityTree(); } - else + else if(translator.selected.empty()) { // Check for interactive grass (hair particle that is child of hovered object: for (size_t i = 0; i < scene.hairs.GetCount(); ++i) diff --git a/Editor/IconDefinitions.h b/Editor/IconDefinitions.h index 6c9cdb6fa..27fc62d87 100644 --- a/Editor/IconDefinitions.h +++ b/Editor/IconDefinitions.h @@ -54,6 +54,7 @@ #define ICON_PAUSE ICON_FA_PAUSE #define ICON_STOP ICON_FA_STOP #define ICON_PEN ICON_FA_PEN +#define ICON_FILTER ICON_FA_FILTER #define ICON_DARK ICON_FA_MOON #define ICON_BRIGHT ICON_FA_SUN diff --git a/Editor/OptionsWindow.cpp b/Editor/OptionsWindow.cpp index 8bc692e10..abf9bf923 100644 --- a/Editor/OptionsWindow.cpp +++ b/Editor/OptionsWindow.cpp @@ -289,7 +289,7 @@ void OptionsWindow::Create(EditorComponent* _editor) filterCombo.Create("Filter: "); - filterCombo.AddItem("All ", (uint64_t)Filter::All); + filterCombo.AddItem("All " ICON_FILTER, (uint64_t)Filter::All); filterCombo.AddItem("Transform " ICON_TRANSFORM, (uint64_t)Filter::Transform); filterCombo.AddItem("Material " ICON_MATERIAL, (uint64_t)Filter::Material); filterCombo.AddItem("Mesh " ICON_MESH, (uint64_t)Filter::Mesh); diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index 529a6eb2d..1839d991e 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -515,6 +515,10 @@ struct ShaderEntity { return GetConeAngleCos(); } + inline float3 GetColliderTip() + { + return shadowAtlasMulAdd.xyz; + } #else // Application-side: @@ -572,6 +576,10 @@ struct ShaderEntity { SetConeAngleCos(value); } + inline void SetColliderTip(float3 value) + { + shadowAtlasMulAdd = float4(value.x, value.y, value.z, 0); + } #endif // __cplusplus }; @@ -607,13 +615,21 @@ struct ShaderFrustum #endif // __cplusplus }; -static const uint ENTITY_TYPE_DIRECTIONALLIGHT = 0; -static const uint ENTITY_TYPE_POINTLIGHT = 1; -static const uint ENTITY_TYPE_SPOTLIGHT = 2; -static const uint ENTITY_TYPE_DECAL = 100; -static const uint ENTITY_TYPE_ENVMAP = 101; -static const uint ENTITY_TYPE_FORCEFIELD_POINT = 200; -static const uint ENTITY_TYPE_FORCEFIELD_PLANE = 201; +enum SHADER_ENTITY_TYPE +{ + ENTITY_TYPE_DIRECTIONALLIGHT, + ENTITY_TYPE_POINTLIGHT, + ENTITY_TYPE_SPOTLIGHT, + ENTITY_TYPE_DECAL, + ENTITY_TYPE_ENVMAP, + ENTITY_TYPE_FORCEFIELD_POINT, + ENTITY_TYPE_FORCEFIELD_PLANE, + ENTITY_TYPE_COLLIDER_SPHERE, + ENTITY_TYPE_COLLIDER_CAPSULE, + ENTITY_TYPE_COLLIDER_PLANE, + + ENTITY_TYPE_COUNT +}; static const uint ENTITY_FLAG_LIGHT_STATIC = 1 << 0; @@ -688,7 +704,7 @@ struct FrameCB uint forcefieldarray_count; // indexing into entity array uint envprobearray_offset; // indexing into entity array - uint envprobearray_count; // indexing into entity array + uint envprobearray_count; // indexing into entity array uint temporalaa_samplerotation; float blue_noise_phase; diff --git a/WickedEngine/shaders/emittedparticle_simulateCS.hlsl b/WickedEngine/shaders/emittedparticle_simulateCS.hlsl index 1a93e0dc1..96e91573a 100644 --- a/WickedEngine/shaders/emittedparticle_simulateCS.hlsl +++ b/WickedEngine/shaders/emittedparticle_simulateCS.hlsl @@ -21,8 +21,8 @@ RWByteAddressBuffer vertexBuffer_COL : register(u9); RWStructuredBuffer culledIndirectionBuffer : register(u10); RWStructuredBuffer culledIndirectionBuffer2 : register(u11); -#define SPH_FLOOR_COLLISION -#define SPH_BOX_COLLISION +//#define SPH_FLOOR_COLLISION +//#define SPH_BOX_COLLISION [numthreads(THREADCOUNT_SIMULATION, 1, 1)] @@ -40,29 +40,105 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) Particle particle = particleBuffer[particleIndex]; uint v0 = particleIndex * 4; + const float lifeLerp = 1 - particle.life / particle.maxLife; + const float particleSize = lerp(particle.sizeBeginEnd.x, particle.sizeBeginEnd.y, lifeLerp); + + // integrate: + particle.force += xParticleGravity; + particle.velocity += particle.force * dt; + particle.position += particle.velocity * dt; + + // reset force for next frame: + particle.force = 0; + + // drag: + particle.velocity *= xParticleDrag; + if (particle.life > 0) { - // simulate: + // process forces and colliders: for (uint i = 0; i < GetFrame().forcefieldarray_count; ++i) { - ShaderEntity forceField = load_entity(GetFrame().forcefieldarray_offset + i); + ShaderEntity entity = load_entity(GetFrame().forcefieldarray_offset + i); [branch] - if (forceField.layerMask & xEmitterLayerMask) + if (entity.layerMask & xEmitterLayerMask) { - float3 dir = forceField.position - particle.position; - float dist; - if (forceField.GetType() == ENTITY_TYPE_FORCEFIELD_POINT) // point-based force field - { - dist = length(dir); - } - else // planar force field - { - dist = dot(forceField.GetDirection(), dir); - dir = forceField.GetDirection(); - } + const float range = entity.GetRange(); + const uint type = entity.GetType(); - particle.force += dir * forceField.GetGravity() * (1 - saturate(dist / forceField.GetRange())); + if (type == ENTITY_TYPE_COLLIDER_CAPSULE) + { + float3 A = entity.position; + float3 B = entity.GetColliderTip(); + float3 N = normalize(A - B); + A -= N * range; + B += N * range; + float3 C = closest_point_on_segment(A, B, particle.position); + float3 dir = C - particle.position; + float dist = length(dir); + dir /= dist; + dist = dist - range - particleSize; + if (dist < 0) + { + particle.velocity = reflect(particle.velocity, dir); + float3 offset_velocity = dir * dist; + particle.position = particle.position + offset_velocity; + particle.velocity += offset_velocity; + } + } + else + { + float3 dir = entity.position - particle.position; + float dist = length(dir); + dir /= dist; + + switch (type) + { + case ENTITY_TYPE_FORCEFIELD_POINT: + particle.force += dir * entity.GetGravity() * (1 - saturate(dist / range)); + break; + case ENTITY_TYPE_FORCEFIELD_PLANE: + particle.force += entity.GetDirection() * entity.GetGravity() * (1 - saturate(dist / range)); + break; + case ENTITY_TYPE_COLLIDER_SPHERE: + dist = dist - range - particleSize; + if (dist < 0) + { + particle.velocity = reflect(particle.velocity, dir); + float3 offset_velocity = dir * dist; + particle.position = particle.position + offset_velocity; + particle.velocity += offset_velocity; + } + break; + case ENTITY_TYPE_COLLIDER_PLANE: + dir = normalize(entity.GetDirection()); + dist = plane_point_distance(entity.position, dir, particle.position); + if (dist < 0) + { + dir *= -1; + dist = abs(dist); + } + dist = dist - particleSize; + if (dist < 0) + { + float4x4 planeProjection = load_entitymatrix(entity.GetMatrixIndex()); + const float3 clipSpacePos = mul(planeProjection, float4(particle.position, 1)).xyz; + const float3 uvw = clipspace_to_uv(clipSpacePos.xyz); + [branch] + if (is_saturated(uvw)) + { + particle.velocity = reflect(particle.velocity, dir); + float3 offset_velocity = -dir * dist; + particle.position = particle.position + offset_velocity; + particle.velocity += offset_velocity; + } + } + break; + default: + break; + } + } } } @@ -82,9 +158,6 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) float surfaceLinearDepth = compute_lineardepth(depth0); float surfaceThickness = 1.5f; - float lifeLerp = 1 - particle.life / particle.maxLife; - float particleSize = lerp(particle.sizeBeginEnd.x, particle.sizeBeginEnd.y, lifeLerp); - // check if particle is colliding with the depth buffer, but not completely behind it: if ((pos2D.w + particleSize > surfaceLinearDepth) && (pos2D.w - particleSize < surfaceLinearDepth + surfaceThickness)) { @@ -106,20 +179,6 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) } #endif // DEPTHCOLLISIONS - // integrate: - particle.force += xParticleGravity; - particle.velocity += particle.force * dt; - particle.position += particle.velocity * dt; - - // reset force for next frame: - particle.force = 0; - - // drag: - particle.velocity *= xParticleDrag; - - float lifeLerp = 1 - particle.life / particle.maxLife; - float particleSize = lerp(particle.sizeBeginEnd.x, particle.sizeBeginEnd.y, lifeLerp); - [branch] if (xEmitterOptions & EMITTER_OPTION_BIT_SPH_ENABLED) { diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli index 342c0f5b2..aaf26bf30 100644 --- a/WickedEngine/shaders/globals.hlsli +++ b/WickedEngine/shaders/globals.hlsli @@ -1020,6 +1020,13 @@ inline float dither(in float2 pixel) return ditherMask8(pixel); } + + +float plane_point_distance(float3 planeOrigin, float3 planeNormal, float3 P) +{ + return dot(planeNormal, P - planeOrigin); +} + // o : ray origin // d : ray direction // center : sphere center diff --git a/WickedEngine/shaders/hairparticle_simulateCS.hlsl b/WickedEngine/shaders/hairparticle_simulateCS.hlsl index 8a9a2af70..937840929 100644 --- a/WickedEngine/shaders/hairparticle_simulateCS.hlsl +++ b/WickedEngine/shaders/hairparticle_simulateCS.hlsl @@ -116,30 +116,82 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn float3 tip = base + normal * len; - // Accumulate forces: + // Accumulate forces, apply colliders: float3 force = 0; for (uint i = 0; i < GetFrame().forcefieldarray_count; ++i) { - ShaderEntity forceField = load_entity(GetFrame().forcefieldarray_offset + i); + ShaderEntity entity = load_entity(GetFrame().forcefieldarray_offset + i); [branch] - if (forceField.layerMask & xHairLayerMask) + if (entity.layerMask & xHairLayerMask) { - //float3 dir = forceField.position - PointOnLineSegmentNearestPoint(base, tip, forceField.position); - float3 dir = forceField.position - tip; - float dist; - if (forceField.GetType() == ENTITY_TYPE_FORCEFIELD_POINT) // point-based force field - { - //dist = length(dir); - dist = length(forceField.position - closest_point_on_segment(base, tip, forceField.position)); - } - else // planar force field - { - dist = dot(forceField.GetDirection(), dir); - dir = forceField.GetDirection(); - } + const float range = entity.GetRange(); + const uint type = entity.GetType(); - force += dir * forceField.GetGravity() * (1 - saturate(dist / forceField.GetRange())); + if (type == ENTITY_TYPE_COLLIDER_CAPSULE) + { + float3 A = entity.position; + float3 B = entity.GetColliderTip(); + float3 N = normalize(A - B); + A -= N * range; + B += N * range; + float3 C = closest_point_on_segment(A, B, tip); + float3 dir = C- tip; + float dist = length(dir); + dir /= dist; + dist = dist - range - len; + if (dist < 0) + { + tip = tip - dir * dist; + } + } + else + { + float3 closest_point = closest_point_on_segment(base, tip, entity.position); + float3 dir = entity.position - closest_point; + float dist = length(dir); + dir /= dist; + + switch (type) + { + case ENTITY_TYPE_FORCEFIELD_POINT: + force += dir * entity.GetGravity() * (1 - saturate(dist / range)); + break; + case ENTITY_TYPE_FORCEFIELD_PLANE: + force += entity.GetDirection() * entity.GetGravity() * (1 - saturate(dist / range)); + break; + case ENTITY_TYPE_COLLIDER_SPHERE: + dist = dist - range - len; + if (dist < 0) + { + tip = tip - dir * dist; + } + break; + case ENTITY_TYPE_COLLIDER_PLANE: + dir = normalize(entity.GetDirection()); + dist = plane_point_distance(entity.position, dir, closest_point); + if (dist < 0) + { + dir *= -1; + dist = abs(dist); + } + dist = dist - len; + if (dist < 0) + { + float4x4 planeProjection = load_entitymatrix(entity.GetMatrixIndex()); + const float3 clipSpacePos = mul(planeProjection, float4(closest_point, 1)).xyz; + const float3 uvw = clipspace_to_uv(clipSpacePos.xyz); + [branch] + if (is_saturated(uvw)) + { + tip = tip + dir * dist; + } + } + break; + default: + break; + } + } } } diff --git a/WickedEngine/wiApplication.cpp b/WickedEngine/wiApplication.cpp index a9ec2ec1d..16c26b8ea 100644 --- a/WickedEngine/wiApplication.cpp +++ b/WickedEngine/wiApplication.cpp @@ -404,19 +404,25 @@ namespace wi params.cursor = wi::font::Draw(infodisplay_str, params, cmd); - if (infoDisplay.vram_usage) + // VRAM: { GraphicsDevice::MemoryUsage vram = graphicsDevice->GetMemoryUsage(); + bool warn = false; if (vram.usage > vram.budget) { params.color = wi::Color::Error(); + warn = true; } else if (float(vram.usage) / float(vram.budget) > 0.9f) { params.color = wi::Color::Warning(); + warn = true; + } + if (infoDisplay.vram_usage || warn) + { + params.cursor = wi::font::Draw("VRAM usage: " + std::to_string(vram.usage / 1024 / 1024) + "MB / " + std::to_string(vram.budget / 1024 / 1024) + "MB\n", params, cmd); + params.color = wi::Color::White(); } - params.cursor = wi::font::Draw("VRAM usage: " + std::to_string(vram.usage / 1024 / 1024) + "MB / " + std::to_string(vram.budget / 1024 / 1024) + "MB\n", params, cmd); - params.color = wi::Color::White(); } // Write warnings below: diff --git a/WickedEngine/wiMath.h b/WickedEngine/wiMath.h index 82f5e5324..c6794cb6b 100644 --- a/WickedEngine/wiMath.h +++ b/WickedEngine/wiMath.h @@ -253,6 +253,11 @@ namespace wi::math XMVECTOR GetClosestPointToLine(const XMVECTOR& A, const XMVECTOR& B, const XMVECTOR& P, bool segmentClamp = false); float GetPointSegmentDistance(const XMVECTOR& point, const XMVECTOR& segmentA, const XMVECTOR& segmentB); + inline float GetPlanePointDistance(const XMVECTOR& planeOrigin, const XMVECTOR& planeNormal, const XMVECTOR& point) + { + return XMVectorGetX(XMVector3Dot(planeNormal, point - planeOrigin)); + } + float GetAngle(const XMFLOAT2& a, const XMFLOAT2& b); float GetAngle(const XMFLOAT3& a, const XMFLOAT3& b, const XMFLOAT3& axis); void ConstructTriangleEquilateral(float radius, XMFLOAT4& A, XMFLOAT4& B, XMFLOAT4& C); diff --git a/WickedEngine/wiPrimitive.cpp b/WickedEngine/wiPrimitive.cpp index 83ca4b866..33fddba33 100644 --- a/WickedEngine/wiPrimitive.cpp +++ b/WickedEngine/wiPrimitive.cpp @@ -251,8 +251,13 @@ namespace wi::primitive } bool Sphere::intersects(const Sphere& b, float& dist, XMFLOAT3& direction) const { - dist = wi::math::Distance(center, b.center); - XMStoreFloat3(&direction, (XMLoadFloat3(¢er) - XMLoadFloat3(&b.center)) / dist); + XMVECTOR A = XMLoadFloat3(¢er); + XMVECTOR B = XMLoadFloat3(&b.center); + XMVECTOR DIR = A - B; + XMVECTOR DIST = XMVector3Length(DIR); + DIR = DIR / DIST; + XMStoreFloat3(&direction, DIR); + dist = XMVectorGetX(DIST); dist = dist - radius - b.radius; return dist < 0; } @@ -266,8 +271,8 @@ namespace wi::primitive XMVECTOR A = XMLoadFloat3(&b.base); XMVECTOR B = XMLoadFloat3(&b.tip); XMVECTOR N = XMVector3Normalize(A - B); - A += N * b.radius; - B -= N * b.radius; + A -= N * b.radius; + B += N * b.radius; XMVECTOR C = XMLoadFloat3(¢er); dist = wi::math::GetPointSegmentDistance(C, A, B); dist = dist - radius - b.radius; @@ -278,8 +283,8 @@ namespace wi::primitive XMVECTOR A = XMLoadFloat3(&b.base); XMVECTOR B = XMLoadFloat3(&b.tip); XMVECTOR N = XMVector3Normalize(A - B); - A += N * b.radius; - B -= N * b.radius; + A -= N * b.radius; + B += N * b.radius; XMVECTOR C = XMLoadFloat3(¢er); dist = wi::math::GetPointSegmentDistance(C, A, B); XMStoreFloat3(&direction, (C - wi::math::ClosestPointOnLineSegment(A, B, C)) / dist); diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index eecb20c31..16ad92faa 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -3214,7 +3214,7 @@ void UpdatePerFrameData( frameCB.lightarray_offset = frameCB.envprobearray_offset + frameCB.envprobearray_count; frameCB.lightarray_count = (uint)vis.visibleLights.size(); frameCB.forcefieldarray_offset = frameCB.lightarray_offset + frameCB.lightarray_count; - frameCB.forcefieldarray_count = (uint)vis.scene->forces.GetCount(); + frameCB.forcefieldarray_count = uint(vis.scene->forces.GetCount() + vis.scene->colliders.GetCount()); frameCB.envprobe_mipcount = 0; frameCB.envprobe_mipcount_rcp = 1.0f; @@ -3681,6 +3681,57 @@ void UpdateRenderData( entityCounter++; } + // Write colliders into entity array: + for (size_t i = 0; i < vis.scene->colliders.GetCount(); ++i) + { + if (entityCounter == SHADER_ENTITY_COUNT) + { + assert(0); // too many entities! + entityCounter--; + break; + } + ShaderEntity shaderentity = {}; + + const ColliderComponent& collider = vis.scene->colliders[i]; + + shaderentity.layerMask = ~0u; + + Entity entity = vis.scene->colliders.GetEntity(i); + const LayerComponent* layer = vis.scene->layers.GetComponent(entity); + if (layer != nullptr) + { + shaderentity.layerMask = layer->layerMask; + } + + switch (collider.shape) + { + case ColliderComponent::Shape::Sphere: + shaderentity.SetType(ENTITY_TYPE_COLLIDER_SPHERE); + shaderentity.position = collider.sphere.center; + shaderentity.SetRange(collider.sphere.radius); + break; + case ColliderComponent::Shape::Capsule: + shaderentity.SetType(ENTITY_TYPE_COLLIDER_CAPSULE); + shaderentity.position = collider.capsule.base; + shaderentity.SetColliderTip(collider.capsule.tip); + shaderentity.SetRange(collider.capsule.radius); + break; + case ColliderComponent::Shape::Plane: + shaderentity.SetType(ENTITY_TYPE_COLLIDER_PLANE); + shaderentity.position = collider.planeOrigin; + shaderentity.SetDirection(collider.planeNormal); + shaderentity.SetIndices(matrixCounter, ~0u); + std::memcpy(&matrixArray[matrixCounter++], &collider.planeProjection, sizeof(collider.planeProjection)); + break; + default: + assert(0); + break; + } + + std::memcpy(entityArray + entityCounter, &shaderentity, sizeof(ShaderEntity)); + entityCounter++; + } + // Write force fields into entity array: for (size_t i = 0; i < vis.scene->forces.GetCount(); ++i) { @@ -5103,7 +5154,36 @@ void DrawDebugWorld( DrawSphere(collider.sphere, XMFLOAT4(1, 0, 1, 1)); break; case ColliderComponent::Shape::Capsule: - DrawCapsule(collider.capsule, XMFLOAT4(1, 1, 0, 1)); + DrawCapsule(collider.capsule, XMFLOAT4(1, 0, 1, 1)); + break; + case ColliderComponent::Shape::Plane: + { + RenderableLine line; + line.color_start = XMFLOAT4(1, 0, 1, 1); + line.color_end = XMFLOAT4(1, 0, 1, 1); + XMMATRIX planeMatrix = XMMatrixInverse(nullptr, XMLoadFloat4x4(&collider.planeProjection)); + XMVECTOR P0 = XMVector3Transform(XMVectorSet(-1, 0, -1, 1), planeMatrix); + XMVECTOR P1 = XMVector3Transform(XMVectorSet(1, 0, -1, 1), planeMatrix); + XMVECTOR P2 = XMVector3Transform(XMVectorSet(1, 0, 1, 1), planeMatrix); + XMVECTOR P3 = XMVector3Transform(XMVectorSet(-1, 0, 1, 1), planeMatrix); + XMStoreFloat3(&line.start, P0); + XMStoreFloat3(&line.end, P1); + DrawLine(line); + XMStoreFloat3(&line.start, P1); + XMStoreFloat3(&line.end, P2); + DrawLine(line); + XMStoreFloat3(&line.start, P2); + XMStoreFloat3(&line.end, P3); + DrawLine(line); + XMStoreFloat3(&line.start, P3); + XMStoreFloat3(&line.end, P0); + DrawLine(line); + XMVECTOR O = XMLoadFloat3(&collider.planeOrigin); + XMVECTOR N = XMLoadFloat3(&collider.planeNormal); + XMStoreFloat3(&line.start, O); + XMStoreFloat3(&line.end, O + N); + DrawLine(line); + } break; } } diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index dcd3db593..ca652f2a0 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -3196,6 +3196,20 @@ namespace wi::scene tail -= N * collider.capsule.radius; XMStoreFloat3(&collider.capsule.base, offset); XMStoreFloat3(&collider.capsule.tip, tail); + + if (collider.shape == ColliderComponent::Shape::Plane) + { + collider.planeOrigin = collider.sphere.center; + XMVECTOR N = XMVectorSet(0, 1, 0, 0); + N = XMVector3Normalize(XMVector3TransformNormal(N, W)); + XMStoreFloat3(&collider.planeNormal, N); + + XMMATRIX PLANE = XMMatrixScaling(collider.radius, 1, collider.radius); + PLANE = PLANE * XMMatrixTranslationFromVector(XMLoadFloat3(&collider.offset)); + PLANE = PLANE * W; + PLANE = XMMatrixInverse(nullptr, PLANE); + XMStoreFloat4x4(&collider.planeProjection, PLANE); + } } } void Scene::RunSpringUpdateSystem(wi::jobsystem::context& ctx) @@ -3347,6 +3361,31 @@ namespace wi::scene case ColliderComponent::Shape::Capsule: tail_sphere.intersects(collider.capsule, dist, direction); break; + case ColliderComponent::Shape::Plane: + dist = wi::math::GetPlanePointDistance(XMLoadFloat3(&collider.planeOrigin), XMLoadFloat3(&collider.planeNormal), tail_next); + direction = collider.planeNormal; + if (dist < 0) + { + direction.x *= -1; + direction.y *= -1; + direction.z *= -1; + dist = std::abs(dist); + } + dist = dist - tail_sphere.radius; + if (dist < 0) + { + XMMATRIX planeProjection = XMLoadFloat4x4(&collider.planeProjection); + XMVECTOR clipSpacePos = XMVector3Transform(tail_next, planeProjection); + XMVECTOR uvw = clipSpacePos * XMVectorSet(0.5f, -0.5f, 0.5f, 1) + XMVectorSet(0.5f, 0.5f, 0.5f, 0); + XMVECTOR uvw_sat = XMVectorSaturate(uvw); + if (std::abs(XMVectorGetX(uvw) - XMVectorGetX(uvw_sat)) > std::numeric_limits::epsilon()) + dist = 1; // force no collision + else if (std::abs(XMVectorGetY(uvw) - XMVectorGetY(uvw_sat)) > std::numeric_limits::epsilon()) + dist = 1; // force no collision + else if (std::abs(XMVectorGetZ(uvw) - XMVectorGetZ(uvw_sat)) > std::numeric_limits::epsilon()) + dist = 1; // force no collision + } + break; } if (dist < 0) diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index fc7a5f4dd..19942ab4e 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -1327,6 +1327,7 @@ namespace wi::scene { Sphere, Capsule, + Plane, }; Shape shape; @@ -1337,6 +1338,9 @@ namespace wi::scene // Non-serialized attributes: wi::primitive::Sphere sphere; wi::primitive::Capsule capsule; + XMFLOAT3 planeOrigin = {}; + XMFLOAT3 planeNormal = {}; + XMFLOAT4X4 planeProjection = wi::math::IDENTITY_MATRIX; void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index dff8682de..af5bd9b1e 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 24; + const int revision = 25; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);