Skip to content

Commit dfb1175

Browse files
authored
Merge pull request stride3d#32 from Doprez/wireframe-debug
Wireframe testing
2 parents 5066fc3 + 7ae1742 commit dfb1175

File tree

5 files changed

+390
-0
lines changed

5 files changed

+390
-0
lines changed

sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/Components/Colliders/_ColliderComponent.cs

+2
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,7 @@ public float Mass
3131
public ContainerComponent? Container { get; internal set; }
3232

3333
internal abstract void AddToCompoundBuilder(IGame game, ref CompoundBuilder builder, RigidPose localPose);
34+
35+
3436
}
3537
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using Stride.BepuPhysics.Components.Colliders;
2+
using Stride.Core;
3+
using Stride.Core.Annotations;
4+
using Stride.Core.Mathematics;
5+
using Stride.Engine;
6+
using Stride.Graphics;
7+
using Stride.Rendering;
8+
9+
namespace Stride.BepuPhysics.Effects.RenderFeatures;
10+
11+
public class SinglePassWireframeRenderFeature : RootRenderFeature
12+
{
13+
DynamicEffectInstance shader;
14+
MutablePipelineState pipelineState;
15+
16+
/// <summary>
17+
/// Adjust scale a bit of wireframe model to prevent z-fighting
18+
/// </summary>
19+
[DataMember(10)]
20+
[DataMemberRange(0.0f, 0.1f, 0.001f, 0.002f, 4)]
21+
public float ScaleAdjust = 0.001f;
22+
23+
public override Type SupportedRenderObjectType => typeof(RenderMesh);
24+
25+
public SinglePassWireframeRenderFeature()
26+
{
27+
SortKey = 255;
28+
}
29+
30+
protected override void InitializeCore()
31+
{
32+
base.InitializeCore();
33+
34+
// initialize shader
35+
shader = new DynamicEffectInstance("SinglePassWireframeShader");
36+
shader.Initialize(Context.Services);
37+
38+
// create the pipeline state and set properties that won't change
39+
pipelineState = new MutablePipelineState(Context.GraphicsDevice);
40+
pipelineState.State.SetDefaults();
41+
pipelineState.State.InputElements = VertexPositionNormalTexture.Layout.CreateInputElements();
42+
pipelineState.State.BlendState = BlendStates.AlphaBlend;
43+
pipelineState.State.RasterizerState.CullMode = CullMode.None;
44+
}
45+
46+
public override void Prepare(RenderDrawContext context)
47+
{
48+
base.Prepare(context);
49+
}
50+
51+
public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage)
52+
{
53+
shader.UpdateEffect(context.GraphicsDevice);
54+
55+
foreach (var renderNode in renderViewStage.SortedRenderNodes)
56+
{
57+
var renderMesh = renderNode.RenderObject as RenderMesh;
58+
if (renderMesh == null)
59+
{
60+
continue;
61+
}
62+
63+
// get wireframe script
64+
//WireframeScript wireframeScript = null;
65+
//if (renderMesh.Source is ModelComponent)
66+
//{
67+
// wireframeScript = (renderMesh.Source as ModelComponent).Entity.Get<WireframeScript>();
68+
//}
69+
//
70+
//if (wireframeScript == null || !wireframeScript.Enabled)
71+
//{
72+
// continue;
73+
//}
74+
// get collider data and pass the buffers to the shader
75+
var collider = renderMesh.Source as ColliderComponent;
76+
77+
MeshDraw drawData = renderMesh.ActiveMeshDraw;
78+
79+
// bind VB
80+
for (int slot = 0; slot < drawData.VertexBuffers.Length; slot++)
81+
{
82+
var vertexBuffer = drawData.VertexBuffers[slot];
83+
context.CommandList.SetVertexBuffer(slot, vertexBuffer.Buffer, vertexBuffer.Offset, vertexBuffer.Stride);
84+
}
85+
86+
// set shader parameters
87+
shader.Parameters.Set(TransformationKeys.WorldViewProjection, renderMesh.World * renderView.ViewProjection); // matrix
88+
shader.Parameters.Set(TransformationKeys.WorldScale, new Vector3(ScaleAdjust + 1.0f)); // increase size to avoid z-fight
89+
shader.Parameters.Set(SinglePassWireframeShaderKeys.Viewport, new Vector4(context.RenderContext.RenderView.ViewSize, 0, 0));
90+
shader.Parameters.Set(SinglePassWireframeShaderKeys.LineWidth, 2f);
91+
shader.Parameters.Set(SinglePassWireframeShaderKeys.LineColor, (Vector3)Color.Red);
92+
93+
// prepare pipeline state
94+
pipelineState.State.RootSignature = shader.RootSignature;
95+
pipelineState.State.EffectBytecode = shader.Effect.Bytecode;
96+
pipelineState.State.PrimitiveType = drawData.PrimitiveType;
97+
98+
pipelineState.State.Output.CaptureState(context.CommandList);
99+
pipelineState.Update();
100+
101+
context.CommandList.SetIndexBuffer(drawData.IndexBuffer.Buffer, drawData.IndexBuffer.Offset, drawData.IndexBuffer.Is32Bit);
102+
context.CommandList.SetPipelineState(pipelineState.CurrentState);
103+
104+
// apply the effect
105+
shader.Apply(context.GraphicsContext);
106+
107+
if (drawData.IndexBuffer != null)
108+
{
109+
context.CommandList.DrawIndexed(drawData.DrawCount, drawData.StartLocation);
110+
}
111+
else
112+
{
113+
context.CommandList.Draw(drawData.DrawCount, drawData.StartLocation);
114+
}
115+
}
116+
}
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
shader SinglePassWireframeShader : ShaderBase, Transformation, Texturing, PositionStream4
2+
{
3+
noperspective stage stream float4 EdgeA;
4+
noperspective stage stream float4 EdgeB;
5+
stage stream float3 Normal : NORMAL;
6+
stage stream uint Case;
7+
8+
static const uint infoA[] = { 0, 0, 0, 0, 1, 1, 2 };
9+
static const uint infoB[] = { 1, 1, 2, 0, 2, 1, 2 };
10+
static const uint infoAd[] = { 2, 2, 1, 1, 0, 0, 0 };
11+
static const uint infoBd[] = { 2, 2, 1, 2, 0, 2, 1 };
12+
static const uint infoEdge0[] = { 0, 2, 0, 0, 0, 0, 2 };
13+
14+
float LineWidth;
15+
float3 LineColor;
16+
17+
cbuffer PerView
18+
{
19+
stage float4 Viewport;
20+
}
21+
22+
float4 projToWorld(in float4 pos, in float3 normal)
23+
{
24+
float3 scaling = normal * (WorldScale - 1);
25+
return mul(pos + float4(scaling.x, scaling.y, scaling.z, 0), WorldViewProjection);
26+
}
27+
28+
float2 projToWindow(in float4 pos)
29+
{
30+
return float2(
31+
Viewport.x * 0.5 * (1 + (pos.x / pos.w)) + Viewport.z,
32+
Viewport.y * 0.5 * (1 - (pos.y / pos.w)) + Viewport.w
33+
);
34+
}
35+
36+
float evalMinDistanceToEdges()
37+
{
38+
float dist;
39+
40+
if (streams.Case == 0)
41+
{
42+
// the easy case, the 3 distances of the fragment to the 3 edges
43+
// is already computed, get the min
44+
45+
dist = min(min(streams.EdgeA.x, streams.EdgeA.y), streams.EdgeA.z);
46+
}
47+
else
48+
{
49+
// the tricky case, compute the distances and get the min from 2D lines
50+
// given from the geometry shader
51+
52+
float2 af = streams.Position.xy - streams.EdgeA.xy;
53+
float sqaf = dot(af, af);
54+
float afCosA = dot(af, streams.EdgeA.zw);
55+
dist = abs(sqaf - afCosA * afCosA);
56+
57+
float2 bf = streams.Position.xy - streams.EdgeB.xy;
58+
float sqbf = dot(bf, bf);
59+
float bfCosB = dot(bf, streams.EdgeB.zw);
60+
dist = min(dist, abs(sqbf - bfCosB * bfCosB));
61+
62+
// only need to care about the 3rd edge for some cases
63+
if (streams.Case == 1 || streams.Case == 2 || streams.Case == 4)
64+
{
65+
float afCosA0 = dot(af, normalize(streams.EdgeB.xy - streams.EdgeA.xy));
66+
dist = min(dist, abs(sqaf - afCosA0 * afCosA0));
67+
}
68+
69+
dist = sqrt(dist);
70+
}
71+
72+
return dist;
73+
}
74+
75+
// geometry shader
76+
[maxvertexcount(3)]
77+
void GSMain(triangle Input input[3], inout TriangleStream<Output> triangleStream)
78+
{
79+
// project to world
80+
float4 positionWS[3];
81+
positionWS[0] = projToWorld(input[0].Position, input[0].Normal);
82+
positionWS[1] = projToWorld(input[1].Position, input[1].Normal);
83+
positionWS[2] = projToWorld(input[2].Position, input[2].Normal);
84+
85+
// Compute the case from the positions of point in space.
86+
87+
uint vertexCase
88+
= (positionWS[0].z < 0 ? 1 : 0) * 4
89+
+ (positionWS[1].z < 0 ? 1 : 0) * 2
90+
+ (positionWS[2].z < 0 ? 1 : 0);
91+
92+
// Compute the case from the positions of point in space.
93+
if (vertexCase == 7)
94+
{
95+
return;
96+
}
97+
98+
float2 points[3];
99+
points[0] = projToWindow(positionWS[0]);
100+
points[1] = projToWindow(positionWS[1]);
101+
points[2] = projToWindow(positionWS[2]);
102+
103+
float3 vertExclude = float3(0, 0, 0);
104+
float excludeEdgeLength = LineWidth + 100;
105+
106+
// general computation
107+
if (vertexCase == 0)
108+
{
109+
// compute the edges vectors of transformed triangle
110+
float2 edges[3];
111+
edges[0] = points[1] - points[0];
112+
edges[1] = points[2] - points[1];
113+
edges[2] = points[0] - points[2];
114+
115+
// store the length of the edges
116+
float lengths[3];
117+
lengths[0] = length(edges[0]);
118+
lengths[1] = length(edges[1]);
119+
lengths[2] = length(edges[2]);
120+
121+
float3 edgesWS[3];
122+
edgesWS[0] = input[1].Position.xyz - input[0].Position.xyz;
123+
edgesWS[1] = input[2].Position.xyz - input[1].Position.xyz;
124+
edgesWS[2] = input[0].Position.xyz - input[2].Position.xyz;
125+
126+
float lengthsWS[3];
127+
lengthsWS[0] = length(edgesWS[0]);
128+
lengthsWS[1] = length(edgesWS[1]);
129+
lengthsWS[2] = length(edgesWS[2]);
130+
131+
float maxLength = max(max(lengthsWS[0], lengthsWS[1]), lengthsWS[2]);
132+
133+
vertExclude.x = lengthsWS[0] == maxLength ? 1 : 0;
134+
vertExclude.y = lengthsWS[1] == maxLength ? 1 : 0;
135+
vertExclude.z = lengthsWS[2] == maxLength ? 1 : 0;
136+
137+
// compute the cos angle of each vertices
138+
float cosAngles[3];
139+
cosAngles[0] = dot(-edges[2], edges[0]) / (lengths[2] * lengths[0]);
140+
cosAngles[1] = dot(-edges[0], edges[1]) / (lengths[0] * lengths[1]);
141+
cosAngles[2] = dot(-edges[1], edges[2]) / (lengths[1] * lengths[2]);
142+
143+
// the height for each vertices of triangle
144+
float heights[3];
145+
heights[1] = lengths[0] * sqrt(1 - cosAngles[0] * cosAngles[0]);
146+
heights[2] = lengths[1] * sqrt(1 - cosAngles[1] * cosAngles[1]);
147+
heights[0] = lengths[2] * sqrt(1 - cosAngles[2] * cosAngles[2]);
148+
149+
float edgeSigns[3];
150+
edgeSigns[0] = (edges[0] > 0 ? 1 : -1);
151+
edgeSigns[1] = (edges[1] > 0 ? 1 : -1);
152+
edgeSigns[2] = (edges[2] > 0 ? 1 : -1);
153+
154+
float edgeOffsets[3];
155+
edgeOffsets[0] = lengths[0] * (0.5 - 0.5 * edgeSigns[0]);
156+
edgeOffsets[1] = lengths[1] * (0.5 - 0.5 * edgeSigns[1]);
157+
edgeOffsets[2] = lengths[2] * (0.5 - 0.5 * edgeSigns[2]);
158+
159+
// vertex 0
160+
streams = input[0];
161+
streams.Case = vertexCase;
162+
streams.Position = input[0].Position;
163+
streams.EdgeA[0] = vertExclude.x * excludeEdgeLength;
164+
streams.EdgeA[1] = heights[0];
165+
streams.EdgeA[2] = vertExclude.z * excludeEdgeLength;
166+
streams.EdgeB[0] = edgeOffsets[0];
167+
streams.EdgeB[1] = edgeOffsets[1] + edgeSigns[1] * cosAngles[1] * lengths[0];
168+
streams.EdgeB[2] = edgeOffsets[2] + edgeSigns[2] * lengths[2];
169+
triangleStream.Append(streams);
170+
171+
// vertex 1
172+
streams = input[1];
173+
streams.Case = vertexCase;
174+
streams.Position = input[1].Position;
175+
streams.EdgeA[0] = vertExclude.x * excludeEdgeLength;
176+
streams.EdgeA[1] = vertExclude.y * excludeEdgeLength;
177+
streams.EdgeA[2] = heights[1];
178+
streams.EdgeB[0] = edgeOffsets[0] + edgeSigns[0] * lengths[0];
179+
streams.EdgeB[1] = edgeOffsets[1];
180+
streams.EdgeB[2] = edgeOffsets[2] * edgeSigns[2] * cosAngles[2] * lengths[1];
181+
triangleStream.Append(streams);
182+
183+
// vertex 2
184+
streams = input[2];
185+
streams.Case = vertexCase;
186+
streams.Position = input[2].Position;
187+
streams.EdgeA[0] = heights[2];
188+
streams.EdgeA[1] = vertExclude.y * excludeEdgeLength;
189+
streams.EdgeA[2] = vertExclude.z * excludeEdgeLength;
190+
streams.EdgeB[0] = edgeOffsets[0] + edgeSigns[0] * cosAngles[0] * lengths[2];
191+
streams.EdgeB[1] = edgeOffsets[1] + edgeSigns[1] * lengths[1];
192+
streams.EdgeB[2] = edgeOffsets[2];
193+
triangleStream.Append(streams);
194+
195+
triangleStream.RestartStrip();
196+
}
197+
else
198+
{
199+
// Else need some tricky computations
200+
201+
for (int i = 0; i < 3; i++)
202+
{
203+
streams = input[i];
204+
205+
streams.EdgeA = float4(0, 0, 0, 0);
206+
streams.EdgeB = float4(0, 0, 0, 0);
207+
208+
streams.Case = vertexCase;
209+
streams.EdgeA.xy = points[infoA[vertexCase]];
210+
streams.EdgeB.xy = points[infoB[vertexCase]];
211+
212+
streams.EdgeA.zw = normalize(streams.EdgeA.xy - points[infoAd[vertexCase]]);
213+
streams.EdgeB.zw = normalize(streams.EdgeB.xy - points[infoBd[vertexCase]]);
214+
215+
triangleStream.Append(streams);
216+
}
217+
218+
triangleStream.RestartStrip();
219+
}
220+
}
221+
222+
// vertex shader
223+
stage override void VSMain()
224+
{
225+
streams.ShadingPosition = projToWorld(streams.Position, streams.Normal);
226+
}
227+
228+
// pixel shader
229+
stage override void PSMain()
230+
{
231+
float dist = evalMinDistanceToEdges();
232+
if (dist > 0.5 * LineWidth)
233+
{
234+
// too far from edge
235+
discard;
236+
}
237+
238+
streams.ColorTarget = float4(LineColor, 1);
239+
}
240+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// <auto-generated>
2+
// Do not edit this file yourself!
3+
//
4+
// This code was generated by Stride Shader Mixin Code Generator.
5+
// To generate it yourself, please install Stride.VisualStudio.Package .vsix
6+
// and re-save the associated .sdfx.
7+
// </auto-generated>
8+
9+
using System;
10+
using Stride.Core;
11+
using Stride.Rendering;
12+
using Stride.Graphics;
13+
using Stride.Shaders;
14+
using Stride.Core.Mathematics;
15+
using Buffer = Stride.Graphics.Buffer;
16+
17+
namespace Stride.Rendering
18+
{
19+
public static partial class SinglePassWireframeShaderKeys
20+
{
21+
public static readonly ValueParameterKey<float> LineWidth = ParameterKeys.NewValue<float>();
22+
public static readonly ValueParameterKey<Vector3> LineColor = ParameterKeys.NewValue<Vector3>();
23+
public static readonly ValueParameterKey<Vector4> Viewport = ParameterKeys.NewValue<Vector4>();
24+
}
25+
}

0 commit comments

Comments
 (0)