503 lines
29 KiB
C#
503 lines
29 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using TerraFX.Interop.DirectX;
|
|
|
|
namespace Auspex.Rendering.Direct3D;
|
|
|
|
internal sealed class StrokeShader : IDisposable
|
|
{
|
|
public struct Constants
|
|
{
|
|
public Matrix4x4 ViewProj;
|
|
|
|
public Vector2 RenderTargetSize;
|
|
|
|
public Vector2 PixelToUv;
|
|
}
|
|
|
|
public struct Instance
|
|
{
|
|
public Vector3 World;
|
|
|
|
public float Thickness;
|
|
|
|
public Vector4 Color;
|
|
|
|
public ushort Index;
|
|
|
|
public float Distance;
|
|
|
|
public float DashLength;
|
|
|
|
public float GapLength;
|
|
|
|
public float DashOffset;
|
|
|
|
public float Flags;
|
|
|
|
public Vector3 PrevWorld;
|
|
|
|
public Vector3 NextWorld;
|
|
|
|
public float JoinType;
|
|
|
|
public Vector4 FadeParams;
|
|
}
|
|
|
|
public sealed class Data : HlslShaderData<Instance>
|
|
{
|
|
public sealed class Builder : HlslShaderBuilder<Instance>
|
|
{
|
|
internal Builder(FrameRenderContext ctx, Data data)
|
|
: base(data._buffer.Map(ctx))
|
|
{
|
|
}
|
|
|
|
public void Add(List<Vector3> world, float thickness, Vector4 color, bool closed, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, Vector4? colorEnd = null, float cap = 0f, float join = 0f, AxDxParams p = default(AxDxParams))
|
|
{
|
|
float num = 0f;
|
|
if (colorEnd.HasValue)
|
|
{
|
|
for (int i = 1; i < world.Count; i++)
|
|
{
|
|
num += Vector3.Distance(world[i - 1], world[i]);
|
|
}
|
|
if (closed)
|
|
{
|
|
num += Vector3.Distance(world[world.Count - 1], world[0]);
|
|
}
|
|
}
|
|
float num2 = 0f;
|
|
for (int j = 0; j < world.Count; j++)
|
|
{
|
|
if (j > 0)
|
|
{
|
|
num2 += Vector3.Distance(world[j - 1], world[j]);
|
|
}
|
|
Vector4 vertexColor = ((colorEnd.HasValue && num > 0f) ? Vector4.Lerp(color, colorEnd.Value, num2 / num) : color);
|
|
float flags = 0f;
|
|
if (!closed && cap > 0f)
|
|
{
|
|
if (j == 0)
|
|
{
|
|
flags = cap;
|
|
}
|
|
if (j == world.Count - 1)
|
|
{
|
|
flags = cap;
|
|
}
|
|
}
|
|
AddVertex(world, j, thickness, vertexColor, num2, dashLength, gapLength, dashOffset, flags, closed, join, p);
|
|
}
|
|
if (closed)
|
|
{
|
|
AddCloseVertex(world, thickness, colorEnd, color, num, num2, dashLength, gapLength, dashOffset, join, p);
|
|
}
|
|
}
|
|
|
|
public void Add(List<Vector3> world, ReadOnlySpan<float> thicknesses, Vector4 color, bool closed, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, Vector4? colorEnd = null, float cap = 0f, float join = 0f, AxDxParams p = default(AxDxParams))
|
|
{
|
|
float num = 0f;
|
|
if (colorEnd.HasValue)
|
|
{
|
|
for (int i = 1; i < world.Count; i++)
|
|
{
|
|
num += Vector3.Distance(world[i - 1], world[i]);
|
|
}
|
|
if (closed)
|
|
{
|
|
num += Vector3.Distance(world[world.Count - 1], world[0]);
|
|
}
|
|
}
|
|
float num2 = 0f;
|
|
for (int j = 0; j < world.Count; j++)
|
|
{
|
|
if (j > 0)
|
|
{
|
|
num2 += Vector3.Distance(world[j - 1], world[j]);
|
|
}
|
|
Vector4 vertexColor = ((colorEnd.HasValue && num > 0f) ? Vector4.Lerp(color, colorEnd.Value, num2 / num) : color);
|
|
float flags = 0f;
|
|
if (!closed && cap > 0f)
|
|
{
|
|
if (j == 0)
|
|
{
|
|
flags = cap;
|
|
}
|
|
if (j == world.Count - 1)
|
|
{
|
|
flags = cap;
|
|
}
|
|
}
|
|
AddVertex(world, j, thicknesses[j], vertexColor, num2, dashLength, gapLength, dashOffset, flags, closed, join, p);
|
|
}
|
|
if (closed)
|
|
{
|
|
AddCloseVertex(world, thicknesses[0], colorEnd, color, num, num2, dashLength, gapLength, dashOffset, join, p);
|
|
}
|
|
}
|
|
|
|
private void AddVertex(List<Vector3> world, int i, float thickness, Vector4 vertexColor, float distance, float dashLength, float gapLength, float dashOffset, float flags, bool closed, float join, AxDxParams p)
|
|
{
|
|
bool num = join > 0f && world.Count > 2;
|
|
float joinType = 0f;
|
|
Vector3 prevWorld;
|
|
Vector3 nextWorld;
|
|
if (num)
|
|
{
|
|
if (closed)
|
|
{
|
|
prevWorld = world[(i - 1 + world.Count) % world.Count];
|
|
nextWorld = world[(i + 1) % world.Count];
|
|
joinType = join;
|
|
}
|
|
else
|
|
{
|
|
bool num2 = i == 0;
|
|
bool flag = i == world.Count - 1;
|
|
prevWorld = (num2 ? world[i] : world[i - 1]);
|
|
nextWorld = (flag ? world[i] : world[i + 1]);
|
|
joinType = ((num2 || flag) ? 0f : join);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prevWorld = world[i];
|
|
nextWorld = world[i];
|
|
}
|
|
_inner.Add(new Instance
|
|
{
|
|
World = world[i],
|
|
Thickness = thickness,
|
|
Color = vertexColor,
|
|
Index = (ushort)(i + 1),
|
|
Distance = distance,
|
|
DashLength = dashLength,
|
|
GapLength = gapLength,
|
|
DashOffset = dashOffset,
|
|
Flags = flags,
|
|
PrevWorld = prevWorld,
|
|
NextWorld = nextWorld,
|
|
JoinType = joinType,
|
|
FadeParams = new Vector4(p.OccludedAlpha, p.OcclusionTolerance, p.FadeStart, p.FadeStop)
|
|
});
|
|
}
|
|
|
|
private void AddCloseVertex(List<Vector3> world, float thickness, Vector4? colorEnd, Vector4 color, float totalDistance, float distance, float dashLength, float gapLength, float dashOffset, float join, AxDxParams p)
|
|
{
|
|
distance += Vector3.Distance(world[world.Count - 1], world[0]);
|
|
Vector4 color2 = ((colorEnd.HasValue && totalDistance > 0f) ? Vector4.Lerp(color, colorEnd.Value, distance / totalDistance) : color);
|
|
GpuBuffer<Instance>.Builder inner = _inner;
|
|
Instance item = new Instance
|
|
{
|
|
World = world[0],
|
|
Thickness = thickness,
|
|
Color = color2,
|
|
Index = (ushort)world.Count,
|
|
Distance = distance,
|
|
DashLength = dashLength,
|
|
GapLength = gapLength,
|
|
DashOffset = dashOffset,
|
|
Flags = 0f
|
|
};
|
|
item.PrevWorld = world[world.Count - 1];
|
|
item.NextWorld = ((world.Count > 1) ? world[1] : world[0]);
|
|
item.JoinType = ((join > 0f && world.Count > 2) ? join : 0f);
|
|
item.FadeParams = new Vector4(p.OccludedAlpha, p.OcclusionTolerance, p.FadeStart, p.FadeStop);
|
|
inner.Add(item);
|
|
}
|
|
}
|
|
|
|
public Data(FrameRenderContext ctx, int maxCount, bool dynamic)
|
|
: base("StrokeShader", ctx, maxCount, dynamic)
|
|
{
|
|
}
|
|
|
|
public Builder Map(FrameRenderContext ctx)
|
|
{
|
|
return new Builder(ctx, this);
|
|
}
|
|
|
|
public void DrawSubset(FrameRenderContext ctx, int firstLine, int numLines)
|
|
{
|
|
DrawVertices(ctx, firstLine, numLines);
|
|
}
|
|
|
|
public void DrawAll(FrameRenderContext ctx)
|
|
{
|
|
DrawVertices(ctx, 0, _buffer.CurElements);
|
|
}
|
|
}
|
|
|
|
public const int MAXIMUM_ARC_SEGMENTS = 240;
|
|
|
|
private unsafe ID3D11Buffer* _constantBuffer;
|
|
|
|
private unsafe ID3D11InputLayout* _il;
|
|
|
|
private unsafe ID3D11VertexShader* _vs;
|
|
|
|
private unsafe ID3D11GeometryShader* _gs;
|
|
|
|
private unsafe ID3D11PixelShader* _ps;
|
|
|
|
public unsafe StrokeShader(FrameRenderContext ctx)
|
|
{
|
|
byte[] bytes = Encoding.UTF8.GetBytes("#define FEATHER 2\n#define MITER_LIMIT 4.0\n#define ROUND_JOIN_SEGMENTS 8\n\ncbuffer Constants : register(b0)\n{\n float4x4 viewProj;\n float2 renderTargetSize;\n float2 pixelToUv;\n};\n\n\nTexture2D<float4> _sceneDepth : register(t0);\nSamplerState _occlusionSampler\n{\n Filter = MIN_MAG_MIP_POINT;\n AddressU = CLAMP;\n AddressV = CLAMP;\n};\n\n// fadeParams: x=OccludedAlpha, y=OcclusionTolerance (m), z=FadeStart (m), w=FadeStop (m).\nfloat4 applyShared(float4 color, float3 projPos, float4 fadeParams)\n{\n float2 uv = projPos.xy * pixelToUv;\n float sceneNdcZ = _sceneDepth.Sample(_occlusionSampler, uv).r;\n\n float near = viewProj._m32;\n float shapeWorldZ = near / max(projPos.z, 1e-6);\n float sceneWorldZ = near / max(sceneNdcZ, 1e-6);\n\n float behindMeters = max(shapeWorldZ - sceneWorldZ, 0.0);\n float occlusion = behindMeters <= fadeParams.y ? 1.0 : fadeParams.x;\n\n float distanceFactor = 1.0;\n if (fadeParams.w < 1e10)\n {\n float range = max(fadeParams.w - fadeParams.z, 1e-4);\n distanceFactor = saturate((fadeParams.w - shapeWorldZ) / range);\n }\n\n color.a *= occlusion * distanceFactor;\n return color;\n}\n\nstruct Line\n{\n float3 world : WORLD;\n float thickness : THICKNESS;\n float4 color : COLOR;\n min16uint index : INDEX;\n float distance : DISTANCE;\n float dashLength : DASHLENGTH;\n float gapLength : GAPLENGTH;\n float dashOffset : DASHOFFSET;\n float flags : FLAGS;\n float3 prevWorld : PREVWORLD;\n float3 nextWorld : NEXTWORLD;\n float joinType : JOINTYPE;\n float4 fadeParams : FADEPARAMS;\n};\n\nstruct VSOutput\n{\n float4 projPos : SV_POSITION;\n float thickness : THICKNESS;\n float4 color : COLOR;\n min16uint index : INDEX;\n float distance : DISTANCE;\n float dashLength : DASHLENGTH;\n float gapLength : GAPLENGTH;\n float dashOffset : DASHOFFSET;\n float flags : FLAGS;\n float3 prevWorld : PREVWORLD;\n float3 nextWorld : NEXTWORLD;\n float joinType : JOINTYPE;\n float4 fadeParams : FADEPARAMS;\n};\n\nstruct GSOutput\n{\n float4 projPos : SV_POSITION;\n float4 color : COLOR;\n noperspective float normal : NORMAL;\n float thickness : THICKNESS;\n noperspective float distance : DISTANCE;\n float dashLength : DASHLENGTH;\n float gapLength : GAPLENGTH;\n float dashOffset : DASHOFFSET;\n noperspective float capEdge : CAPEDGE;\n nointerpolation float capType : CAPTYPE;\n noperspective float joinEdge : JOINEDGE;\n nointerpolation float joinTypeOut : JOINTYPEOUT;\n float4 fadeParams : FADEPARAMS;\n};\n\nVSOutput vs(in Line l)\n{\n VSOutput v;\n\n v.thickness = l.thickness + FEATHER / 2;\n v.color = l.color;\n v.index = l.index;\n v.distance = l.distance;\n v.dashLength = l.dashLength;\n v.gapLength = l.gapLength;\n v.dashOffset = l.dashOffset;\n v.flags = l.flags;\n v.prevWorld = l.prevWorld;\n v.nextWorld = l.nextWorld;\n v.joinType = l.joinType;\n v.fadeParams = l.fadeParams;\n\n v.projPos = mul(float4(l.world, 1), viewProj);\n return v;\n}\n\nfloat unscale(inout float4 v)\n{\n float scale = v.w;\n v /= v.w;\n return scale;\n}\n\n// Compute miter offset at a join point.\n// segNormal: normal of the current segment in NDC\n// neighborNormal: normal of the adjacent segment in NDC\n// Returns: miter direction in NDC scaled so that dot(miter, segNormal) == 1\n// outMiterLen: the miter scale factor (1/cos(half_angle))\nbool compute_miter(float2 segNormal, float2 neighborNormal, out float2 miter, out float miterLen)\n{\n float2 m = segNormal + neighborNormal;\n float len2 = dot(m, m);\n if (len2 < 0.0001) {\n miter = segNormal;\n miterLen = 1.0;\n return false;\n }\n miter = m / sqrt(len2);\n float d = dot(miter, segNormal);\n if (abs(d) < 0.001) {\n miter = segNormal;\n miterLen = 1.0;\n return false;\n }\n miterLen = 1.0 / d;\n return true;\n}\n\n// maxvertexcount: 4 (main quad) + 2*(ROUND_JOIN_SEGMENTS+1) (round fan at start)\n[maxvertexcount(22)]\nvoid gs(line VSOutput input[2], inout TriangleStream<GSOutput> output)\n{\n VSOutput start = input[0];\n VSOutput stop = input[1];\n\n if (start.index > stop.index) {\n return;\n }\n\n if (start.projPos.w > stop.projPos.w)\n {\n VSOutput tmp = start;\n start = stop;\n stop = tmp;\n }\n\n float nearPlane = 0.1;\n if (start.projPos.w < nearPlane)\n {\n\t float ratio = (nearPlane - start.projPos.w) / (stop.projPos.w - start.projPos.w);\n\t start.projPos = lerp(start.projPos, stop.projPos, ratio);\n }\n\n float4 p0 = start.projPos;\n float w0 = unscale(p0);\n float4 p1 = stop.projPos;\n float w1 = unscale(p1);\n\n // Segment direction and normal in screen space\n float2 segDir = normalize((p1.xy - p0.xy) * renderTargetSize);\n float2 segN = float2(-segDir.y, segDir.x) / renderTargetSize;\n\n // Cap extension\n float startCapEdge = 0;\n float stopCapEdge = 0;\n float capType = max(start.flags, stop.flags);\n\n if (start.flags > 0.5) {\n float2 ext = segDir * start.thickness * 2.0 / renderTargetSize;\n p0.xy -= ext;\n startCapEdge = -1;\n }\n if (stop.flags > 0.5) {\n float2 ext = segDir * stop.thickness * 2.0 / renderTargetSize;\n p1.xy += ext;\n stopCapEdge = 1;\n }\n\n // Compute join offsets at start vertex\n float2 startOffsetPos = segN * start.thickness; // +normal side offset (NDC)\n float2 startOffsetNeg = -segN * start.thickness; // -normal side offset (NDC)\n float2 startPrevN = segN; // neighbor normal for bevel/round (default to seg normal)\n bool startHasJoin = false;\n float startJoinSign = 0;\n\n if (start.joinType > 0.5) {\n float4 prevProj = mul(float4(start.prevWorld, 1), viewProj);\n if (prevProj.w > 0.01) {\n float2 prevNDC = prevProj.xy / prevProj.w;\n float2 prevDir = normalize((p0.xy - prevNDC) * renderTargetSize);\n float2 prevN = float2(-prevDir.y, prevDir.x) / renderTargetSize;\n startPrevN = prevN;\n\n float2 miter;\n float miterLen;\n bool valid = compute_miter(segN, prevN, miter, miterLen);\n\n if (valid) {\n startHasJoin = true;\n // Determine which side is the outside of the turn\n float cross_z = segDir.x * (-prevDir.y) - segDir.y * (-prevDir.x);\n startJoinSign = sign(cross_z);\n\n if (start.joinType < 1.5 && miterLen <= MITER_LIMIT) {\n // Miter join: extend both sides along the miter\n startOffsetPos = miter * start.thickness * miterLen;\n startOffsetNeg = -miter * start.thickness * miterLen;\n } else if (start.joinType < 1.5) {\n // Miter exceeded limit: fall back to bevel\n startHasJoin = true;\n }\n // Bevel and Round: use segment normal (no miter extension), emit join geometry after quad\n }\n }\n }\n\n // Compute miter offsets at stop vertex (for quad corner placement only)\n float2 stopOffsetPos = segN * stop.thickness;\n float2 stopOffsetNeg = -segN * stop.thickness;\n\n if (stop.joinType > 0.5) {\n float4 nextProj = mul(float4(stop.nextWorld, 1), viewProj);\n if (nextProj.w > 0.01) {\n float2 nextNDC = nextProj.xy / nextProj.w;\n float2 nextDir = normalize((nextNDC - p1.xy) * renderTargetSize);\n float2 nextN = float2(-nextDir.y, nextDir.x) / renderTargetSize;\n\n float2 miter;\n float miterLen;\n bool valid = compute_miter(segN, nextN, miter, miterLen);\n\n if (valid && stop.joinType < 1.5 && miterLen <= MITER_LIMIT) {\n // Miter join: extend quad corners along the miter\n stopOffsetPos = miter * stop.thickness * miterLen;\n stopOffsetNeg = -miter * stop.thickness * miterLen;\n }\n }\n }\n\n // Emit main quad\n GSOutput v;\n v.dashLength = start.dashLength;\n v.gapLength = start.gapLength;\n v.dashOffset = start.dashOffset;\n v.joinEdge = 0;\n v.joinTypeOut = 0;\n v.fadeParams = start.fadeParams;\n\n v.thickness = start.thickness;\n v.color = start.color;\n v.distance = start.distance;\n v.capEdge = startCapEdge;\n v.capType = capType;\n v.normal = 1;\n v.projPos = w0 * float4(p0.xy + startOffsetPos, p0.zw);\n output.Append(v);\n v.normal = -1;\n v.projPos = w0 * float4(p0.xy + startOffsetNeg, p0.zw);\n output.Append(v);\n\n v.thickness = stop.thickness;\n v.color = stop.color;\n v.dashLength = stop.dashLength;\n v.gapLength = stop.gapLength;\n v.dashOffset = stop.dashOffset;\n v.distance = stop.distance;\n v.capEdge = stopCapEdge;\n v.capType = capType;\n v.normal = 1;\n v.projPos = w1 * float4(p1.xy + stopOffsetPos, p1.zw);\n output.Append(v);\n v.normal = -1;\n v.projPos = w1 * float4(p1.xy + stopOffsetNeg, p1.zw);\n output.Append(v);\n\n // Emit bevel/round join geometry at start vertex\n if (startHasJoin && start.joinType > 1.5) {\n output.RestartStrip();\n\n float2 n0 = startPrevN * start.thickness;\n float2 n1 = segN * start.thickness;\n\n if (start.joinType > 2.5) {\n // Round join: emit fan\n float2 outerN0 = startJoinSign > 0 ? n0 : -n0;\n float2 outerN1 = startJoinSign > 0 ? n1 : -n1;\n\n float angle0 = atan2(outerN0.y * renderTargetSize.y, outerN0.x * renderTargetSize.x);\n float angle1 = atan2(outerN1.y * renderTargetSize.y, outerN1.x * renderTargetSize.x);\n float dAngle = angle1 - angle0;\n if (dAngle > 3.14159) dAngle -= 6.28318;\n if (dAngle < -3.14159) dAngle += 6.28318;\n\n int numFan = clamp(abs(dAngle) / 0.3, 2, ROUND_JOIN_SEGMENTS);\n for (int fi = 0; fi <= numFan; fi++) {\n float t = (float)fi / (float)numFan;\n float a = angle0 + dAngle * t;\n float2 fanN = float2(cos(a) / renderTargetSize.x, sin(a) / renderTargetSize.y) * start.thickness;\n\n v.thickness = start.thickness;\n v.color = start.color;\n v.dashLength = start.dashLength;\n v.gapLength = start.gapLength;\n v.dashOffset = start.dashOffset;\n v.distance = start.distance;\n v.capEdge = 0;\n v.capType = 0;\n v.joinEdge = 1;\n v.joinTypeOut = start.joinType;\n\n // Center vertex\n v.normal = 0;\n v.projPos = w0 * float4(p0.xy, p0.zw);\n output.Append(v);\n\n // Outer vertex\n v.normal = 1;\n v.projPos = w0 * float4(p0.xy + fanN, p0.zw);\n output.Append(v);\n }\n } else {\n // Bevel join: single triangle\n float2 outerN0 = startJoinSign > 0 ? n0 : -n0;\n float2 outerN1 = startJoinSign > 0 ? n1 : -n1;\n\n v.thickness = start.thickness;\n v.color = start.color;\n v.dashLength = start.dashLength;\n v.gapLength = start.gapLength;\n v.dashOffset = start.dashOffset;\n v.distance = start.distance;\n v.capEdge = 0;\n v.capType = 0;\n v.joinEdge = 0;\n v.joinTypeOut = 0;\n v.normal = 0;\n\n // Center vertex\n v.projPos = w0 * float4(p0.xy, p0.zw);\n output.Append(v);\n // Prev segment outer edge\n v.normal = 1;\n v.projPos = w0 * float4(p0.xy + outerN0, p0.zw);\n output.Append(v);\n // Current segment outer edge\n v.projPos = w0 * float4(p0.xy + outerN1, p0.zw);\n output.Append(v);\n }\n }\n\n // Note: bevel/round join fill geometry is only emitted at the start vertex\n // to avoid double-drawing (the next segment's start handles the same join).\n}\n\nfloat unfeather(float thickness, float dist)\n{\n float width = thickness - FEATHER;\n float pixel = dist * thickness - width;\n pixel = max(0, pixel);\n pixel /= FEATHER;\n return pixel;\n}\n\nfloat4 ps(GSOutput input) : SV_Target\n{\n if (input.dashLength > 0)\n {\n if (fmod(input.distance + input.dashOffset, input.dashLength + input.gapLength) >= input.dashLength)\n discard;\n }\n float dist;\n if (input.joinTypeOut > 2.5 && abs(input.joinEdge) > 0.001) {\n // Round join: use 2D distance from center for circular edge\n dist = abs(input.normal);\n } else if (input.capType > 1.5 && abs(input.capEdge) > 0.001) {\n dist = length(float2(input.capEdge, input.normal));\n } else {\n dist = abs(input.normal);\n }\n float f = unfeather(input.thickness, dist);\n float4 color = input.color;\n color.a *= exp2(-2.7 * f * f);\n return applyShared(color, input.projPos.xyz, input.fadeParams);\n}");
|
|
TriangleFillShader.CompileShader(bytes, "vs"u8, "vs_5_0"u8, out var blob, "Line VS");
|
|
TriangleFillShader.CompileShader(bytes, "gs"u8, "gs_5_0"u8, out var blob2, "Line GS");
|
|
TriangleFillShader.CompileShader(bytes, "ps"u8, "ps_5_0"u8, out var blob3, "Line PS");
|
|
ID3D11VertexShader* vs = default(ID3D11VertexShader*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), null, &vs));
|
|
_vs = vs;
|
|
ID3D11GeometryShader* gs = default(ID3D11GeometryShader*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreateGeometryShader(blob2->GetBufferPointer(), blob2->GetBufferSize(), null, &gs));
|
|
_gs = gs;
|
|
ID3D11PixelShader* ps = default(ID3D11PixelShader*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreatePixelShader(blob3->GetBufferPointer(), blob3->GetBufferSize(), null, &ps));
|
|
_ps = ps;
|
|
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
|
|
{
|
|
ByteWidth = 128u,
|
|
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
|
|
BindFlags = 4u
|
|
};
|
|
ID3D11Buffer* constantBuffer = default(ID3D11Buffer*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &constantBuffer));
|
|
_constantBuffer = constantBuffer;
|
|
fixed (byte* semanticName = "WORLD"u8)
|
|
{
|
|
fixed (byte* semanticName2 = "THICKNESS"u8)
|
|
{
|
|
fixed (byte* semanticName3 = "COLOR"u8)
|
|
{
|
|
fixed (byte* semanticName4 = "INDEX"u8)
|
|
{
|
|
fixed (byte* semanticName5 = "DISTANCE"u8)
|
|
{
|
|
fixed (byte* semanticName6 = "DASHLENGTH"u8)
|
|
{
|
|
fixed (byte* semanticName7 = "GAPLENGTH"u8)
|
|
{
|
|
fixed (byte* semanticName8 = "DASHOFFSET"u8)
|
|
{
|
|
fixed (byte* semanticName9 = "FLAGS"u8)
|
|
{
|
|
fixed (byte* semanticName10 = "PREVWORLD"u8)
|
|
{
|
|
fixed (byte* semanticName11 = "NEXTWORLD"u8)
|
|
{
|
|
fixed (byte* semanticName12 = "JOINTYPE"u8)
|
|
{
|
|
fixed (byte* semanticName13 = "FADEPARAMS"u8)
|
|
{
|
|
D3D11_INPUT_ELEMENT_DESC* ptr = stackalloc D3D11_INPUT_ELEMENT_DESC[13];
|
|
*ptr = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[1] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName2,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[2] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName3,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[3] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName4,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R16_UINT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[4] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName5,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[5] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName6,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[6] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName7,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[7] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName8,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[8] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName9,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[9] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName10,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[10] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName11,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[11] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName12,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ptr[12] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName13,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
|
|
InstanceDataStepRate = 0u
|
|
};
|
|
ID3D11InputLayout* il = default(ID3D11InputLayout*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreateInputLayout(ptr, 13u, blob->GetBufferPointer(), blob->GetBufferSize(), &il));
|
|
_il = il;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
blob->Release();
|
|
blob2->Release();
|
|
blob3->Release();
|
|
}
|
|
|
|
public unsafe void Dispose()
|
|
{
|
|
if (_constantBuffer != null)
|
|
{
|
|
_constantBuffer->Release();
|
|
_constantBuffer = null;
|
|
}
|
|
if (_il != null)
|
|
{
|
|
_il->Release();
|
|
_il = null;
|
|
}
|
|
if (_vs != null)
|
|
{
|
|
_vs->Release();
|
|
_vs = null;
|
|
}
|
|
if (_gs != null)
|
|
{
|
|
_gs->Release();
|
|
_gs = null;
|
|
}
|
|
if (_ps != null)
|
|
{
|
|
_ps->Release();
|
|
_ps = null;
|
|
}
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public unsafe void UpdateConstants(FrameRenderContext ctx, Constants consts)
|
|
{
|
|
consts.ViewProj = Matrix4x4.Transpose(consts.ViewProj);
|
|
ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u);
|
|
}
|
|
|
|
public unsafe void Bind(FrameRenderContext ctx)
|
|
{
|
|
ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_LINESTRIP);
|
|
ctx.Context->IASetInputLayout(_il);
|
|
ctx.Context->VSSetShader(_vs, null, 0u);
|
|
ID3D11Buffer* constantBuffer = _constantBuffer;
|
|
ctx.Context->VSSetConstantBuffers(0u, 1u, &constantBuffer);
|
|
ctx.Context->GSSetShader(_gs, null, 0u);
|
|
ctx.Context->GSSetConstantBuffers(0u, 1u, &constantBuffer);
|
|
ctx.Context->PSSetShader(_ps, null, 0u);
|
|
ctx.Context->PSSetConstantBuffers(0u, 1u, &constantBuffer);
|
|
}
|
|
|
|
public void Draw(FrameRenderContext ctx, Data data)
|
|
{
|
|
Bind(ctx);
|
|
data.DrawAll(ctx);
|
|
}
|
|
}
|