181 lines
6.5 KiB
C#
181 lines
6.5 KiB
C#
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
using System;
|
|
using FlaxEngine;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace FlaxEditor.Gizmo
|
|
{
|
|
/// <summary>
|
|
/// Draws a grid to feel better world origin position and the world units.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEditor.Gizmo.GizmoBase" />
|
|
[HideInEditor]
|
|
public class GridGizmo : GizmoBase
|
|
{
|
|
[HideInEditor]
|
|
private sealed class Renderer : PostProcessEffect
|
|
{
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct Data
|
|
{
|
|
public Matrix WorldMatrix;
|
|
public Matrix ViewProjectionMatrix;
|
|
public Float4 GridColor;
|
|
public Float3 ViewPos;
|
|
public float Far;
|
|
public Float3 Padding;
|
|
public float GridSize;
|
|
}
|
|
|
|
private static readonly uint[] _triangles =
|
|
{
|
|
0, 2, 1, // Face front
|
|
1, 3, 0,
|
|
};
|
|
|
|
private GPUBuffer[] _vbs = new GPUBuffer[1];
|
|
private GPUBuffer _vertexBuffer;
|
|
private GPUBuffer _indexBuffer;
|
|
private GPUPipelineState _psGrid;
|
|
private Shader _shader;
|
|
|
|
public Renderer()
|
|
{
|
|
UseSingleTarget = true;
|
|
Location = PostProcessEffectLocation.Default;
|
|
_shader = FlaxEngine.Content.LoadAsyncInternal<Shader>("Shaders/Editor/Grid");
|
|
}
|
|
|
|
~Renderer()
|
|
{
|
|
Destroy(ref _psGrid);
|
|
Destroy(ref _vertexBuffer);
|
|
Destroy(ref _indexBuffer);
|
|
_shader = null;
|
|
}
|
|
|
|
public override unsafe void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)
|
|
{
|
|
if (_shader == null)
|
|
return;
|
|
Profiler.BeginEventGPU("Editor Grid");
|
|
|
|
var options = Editor.Instance.Options.Options;
|
|
Float3 camPos = renderContext.View.WorldPosition;
|
|
float gridSize = renderContext.View.Far + 20000;
|
|
|
|
// Lazy-init resources
|
|
if (_vertexBuffer == null)
|
|
{
|
|
_vertexBuffer = new GPUBuffer();
|
|
var desc = GPUBufferDescription.Vertex(sizeof(Float3), 4);
|
|
_vertexBuffer.Init(ref desc);
|
|
}
|
|
if (_indexBuffer == null)
|
|
{
|
|
_indexBuffer = new GPUBuffer();
|
|
fixed (uint* ptr = _triangles)
|
|
{
|
|
var desc = GPUBufferDescription.Index(sizeof(uint), _triangles.Length, new IntPtr(ptr));
|
|
_indexBuffer.Init(ref desc);
|
|
}
|
|
}
|
|
if (_psGrid == null)
|
|
{
|
|
_psGrid = new GPUPipelineState();
|
|
var desc = GPUPipelineState.Description.Default;
|
|
desc.BlendMode = BlendingMode.AlphaBlend;
|
|
desc.CullMode = CullMode.TwoSided;
|
|
desc.VS = _shader.GPU.GetVS("VS_Grid");
|
|
desc.PS = _shader.GPU.GetPS("PS_Grid");
|
|
_psGrid.Init(ref desc);
|
|
}
|
|
|
|
// Update vertices of the plane
|
|
// TODO: perf this operation in a Vertex Shader
|
|
float y = 1.5f; // Add small bias to reduce Z-fighting with geometry at scene origin
|
|
var vertices = new Float3[]
|
|
{
|
|
new Float3(-gridSize + camPos.X, y, -gridSize + camPos.Z),
|
|
new Float3(gridSize + camPos.X, y, gridSize + camPos.Z),
|
|
new Float3(-gridSize + camPos.X, y, gridSize + camPos.Z),
|
|
new Float3(gridSize + camPos.X, y, -gridSize + camPos.Z),
|
|
};
|
|
fixed (Float3* ptr = vertices)
|
|
{
|
|
context.UpdateBuffer(_vertexBuffer, new IntPtr(ptr), (uint)(sizeof(Float3) * vertices.Length));
|
|
}
|
|
|
|
// Update constant buffer data
|
|
var cb = _shader.GPU.GetCB(0);
|
|
if (cb != IntPtr.Zero)
|
|
{
|
|
var data = new Data();
|
|
Matrix.Multiply(ref renderContext.View.View, ref renderContext.View.Projection, out var viewProjection);
|
|
data.WorldMatrix = Matrix.Identity;
|
|
Matrix.Transpose(ref viewProjection, out data.ViewProjectionMatrix);
|
|
data.ViewPos = renderContext.View.WorldPosition;
|
|
data.GridColor = options.Viewport.ViewportGridColor;
|
|
data.Far = renderContext.View.Far;
|
|
data.GridSize = options.Viewport.ViewportGridViewDistance;
|
|
context.UpdateCB(cb, new IntPtr(&data));
|
|
}
|
|
|
|
// Draw geometry using custom Pixel Shader and Vertex Shader
|
|
context.BindCB(0, cb);
|
|
context.BindIB(_indexBuffer);
|
|
_vbs[0] = _vertexBuffer;
|
|
context.BindVB(_vbs);
|
|
context.SetState(_psGrid);
|
|
context.SetRenderTarget(renderContext.Buffers.DepthBuffer.View(), input.View());
|
|
context.DrawIndexed((uint)_triangles.Length);
|
|
|
|
Profiler.EndEventGPU();
|
|
}
|
|
}
|
|
|
|
private Renderer _renderer;
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether this <see cref="GridGizmo"/> is enabled.
|
|
/// </summary>
|
|
public bool Enabled
|
|
{
|
|
get => _renderer.Enabled;
|
|
set
|
|
{
|
|
if (_renderer.Enabled != value)
|
|
{
|
|
_renderer.Enabled = value;
|
|
EnabledChanged?.Invoke(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Occurs when enabled state gets changed.
|
|
/// </summary>
|
|
public event Action<GridGizmo> EnabledChanged;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="GridGizmo"/> class.
|
|
/// </summary>
|
|
/// <param name="owner">The gizmos owner.</param>
|
|
public GridGizmo(IGizmoOwner owner)
|
|
: base(owner)
|
|
{
|
|
_renderer = new Renderer();
|
|
owner.RenderTask.AddCustomPostFx(_renderer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Destructor.
|
|
/// </summary>
|
|
~GridGizmo()
|
|
{
|
|
FlaxEngine.Object.Destroy(ref _renderer);
|
|
}
|
|
}
|
|
}
|