2121 lines
77 KiB
C++
2121 lines
77 KiB
C++
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
#if COMPILE_WITH_DEBUG_DRAW
|
|
|
|
#include "DebugDraw.h"
|
|
#include "Engine/Engine/Time.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Level/SceneQuery.h"
|
|
#include "Engine/Engine/EngineService.h"
|
|
#include "Engine/Content/Content.h"
|
|
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
|
#include "Engine/Content/Assets/Shader.h"
|
|
#include "Engine/Content/AssetReference.h"
|
|
#include "Engine/Graphics/GPUContext.h"
|
|
#include "Engine/Graphics/GPUDevice.h"
|
|
#include "Engine/Graphics/GPUPipelineState.h"
|
|
#include "Engine/Graphics/RenderTask.h"
|
|
#include "Engine/Graphics/RenderBuffers.h"
|
|
#include "Engine/Graphics/RenderTools.h"
|
|
#include "Engine/Graphics/DynamicBuffer.h"
|
|
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
|
|
#include "Engine/Graphics/Shaders/GPUShader.h"
|
|
#include "Engine/Animations/AnimationUtils.h"
|
|
#include "Engine/Profiler/Profiler.h"
|
|
#include "Engine/Debug/DebugLog.h"
|
|
#include "Engine/Render2D/Render2D.h"
|
|
#include "Engine/Render2D/FontAsset.h"
|
|
#if USE_EDITOR
|
|
#include "Editor/Editor.h"
|
|
#endif
|
|
|
|
// Debug draw service configuration
|
|
#define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024)
|
|
//
|
|
#define DEBUG_DRAW_SPHERE_LOD0_RESOLUTION 64
|
|
#define DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE 0.2f
|
|
#define DEBUG_DRAW_SPHERE_LOD1_RESOLUTION 16
|
|
#define DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE 0.08f
|
|
#define DEBUG_DRAW_SPHERE_LOD2_RESOLUTION 8
|
|
//
|
|
#define DEBUG_DRAW_CIRCLE_RESOLUTION 32
|
|
#define DEBUG_DRAW_CIRCLE_LINES_COUNT DEBUG_DRAW_CIRCLE_RESOLUTION
|
|
#define DEBUG_DRAW_CIRCLE_VERTICES DEBUG_DRAW_CIRCLE_LINES_COUNT * 2
|
|
//
|
|
#define DEBUG_DRAW_CYLINDER_RESOLUTION 12
|
|
#define DEBUG_DRAW_CYLINDER_VERTICES (DEBUG_DRAW_CYLINDER_RESOLUTION * 4)
|
|
//
|
|
#define DEBUG_DRAW_CONE_RESOLUTION 32
|
|
//
|
|
#define DEBUG_DRAW_ARC_RESOLUTION 32
|
|
//
|
|
#define DEBUG_DRAW_TRIANGLE_SPHERE_RESOLUTION 12
|
|
|
|
struct DebugSphereCache
|
|
{
|
|
Array<Float3, FixedAllocation<(DEBUG_DRAW_SPHERE_LOD0_RESOLUTION + 1) * 3 * 2>> Vertices;
|
|
|
|
void Init(int32 resolution)
|
|
{
|
|
const int32 verticesCount = (resolution + 1) * 3 * 2;
|
|
Vertices.Resize(verticesCount);
|
|
int32 index = 0;
|
|
const float step = TWO_PI / (float)resolution;
|
|
for (float a = 0.0f; a < TWO_PI; a += step)
|
|
{
|
|
// Calculate sines and cosines
|
|
const float sinA = Math::Sin(a);
|
|
const float cosA = Math::Cos(a);
|
|
const float sinB = Math::Sin(a + step);
|
|
const float cosB = Math::Cos(a + step);
|
|
|
|
// XY loop
|
|
Vertices[index++] = Float3(cosA, sinA, 0.0f);
|
|
Vertices[index++] = Float3(cosB, sinB, 0.0f);
|
|
|
|
// XZ loop
|
|
Vertices[index++] = Float3(cosA, 0.0f, sinA);
|
|
Vertices[index++] = Float3(cosB, 0.0f, sinB);
|
|
|
|
// YZ loop
|
|
Vertices[index++] = Float3(0.0f, cosA, sinA);
|
|
Vertices[index++] = Float3(0.0f, cosB, sinB);
|
|
}
|
|
}
|
|
};
|
|
|
|
struct DebugLine
|
|
{
|
|
Float3 Start;
|
|
Float3 End;
|
|
Color32 Color;
|
|
float TimeLeft;
|
|
};
|
|
|
|
struct DebugTriangle
|
|
{
|
|
Float3 V0;
|
|
Float3 V1;
|
|
Float3 V2;
|
|
Color32 Color;
|
|
float TimeLeft;
|
|
};
|
|
|
|
struct DebugText2D
|
|
{
|
|
Array<Char, InlinedAllocation<64>> Text;
|
|
Float2 Position;
|
|
int32 Size;
|
|
Color Color;
|
|
float TimeLeft;
|
|
};
|
|
|
|
struct DebugText3D
|
|
{
|
|
Array<Char, InlinedAllocation<64>> Text;
|
|
Transform Transform;
|
|
bool FaceCamera;
|
|
int32 Size;
|
|
Color Color;
|
|
float TimeLeft;
|
|
};
|
|
|
|
PACK_STRUCT(struct Vertex {
|
|
Float3 Position;
|
|
Color32 Color;
|
|
});
|
|
|
|
PACK_STRUCT(struct alignas(GPU_SHADER_DATA_ALIGNMENT) Data {
|
|
Matrix ViewProjection;
|
|
Float2 Padding;
|
|
float ClipPosZBias;
|
|
uint32 EnableDepthTest;
|
|
});
|
|
|
|
struct PsData
|
|
{
|
|
GPUPipelineState* Depth = nullptr;
|
|
GPUPipelineState* NoDepthTest = nullptr;
|
|
GPUPipelineState* DepthWrite = nullptr;
|
|
GPUPipelineState* NoDepthTestDepthWrite = nullptr;
|
|
|
|
bool Create(GPUPipelineState::Description& desc)
|
|
{
|
|
desc.DepthEnable = true;
|
|
desc.DepthWriteEnable = false;
|
|
|
|
Depth = GPUDevice::Instance->CreatePipelineState();
|
|
if (Depth->Init(desc))
|
|
return true;
|
|
desc.DepthEnable = false;
|
|
NoDepthTest = GPUDevice::Instance->CreatePipelineState();
|
|
if (NoDepthTest->Init(desc))
|
|
return false;
|
|
|
|
desc.DepthWriteEnable = true;
|
|
|
|
NoDepthTestDepthWrite = GPUDevice::Instance->CreatePipelineState();
|
|
if (NoDepthTestDepthWrite->Init(desc))
|
|
return false;
|
|
desc.DepthEnable = true;
|
|
DepthWrite = GPUDevice::Instance->CreatePipelineState();
|
|
return DepthWrite->Init(desc);
|
|
}
|
|
|
|
void Release()
|
|
{
|
|
SAFE_DELETE_GPU_RESOURCE(Depth);
|
|
SAFE_DELETE_GPU_RESOURCE(NoDepthTest);
|
|
SAFE_DELETE_GPU_RESOURCE(DepthWrite);
|
|
SAFE_DELETE_GPU_RESOURCE(NoDepthTestDepthWrite);
|
|
}
|
|
|
|
FORCE_INLINE GPUPipelineState* Get(bool depthWrite, bool depthTest)
|
|
{
|
|
if (depthWrite)
|
|
return depthTest ? DepthWrite : NoDepthTestDepthWrite;
|
|
return depthTest ? Depth : NoDepthTest;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
void UpdateList(float dt, Array<T>& list)
|
|
{
|
|
for (int32 i = 0; i < list.Count() && list.HasItems(); i++)
|
|
{
|
|
list[i].TimeLeft -= dt;
|
|
if (list[i].TimeLeft <= 0)
|
|
{
|
|
list.RemoveAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TeleportList(const Float3& delta, Array<DebugLine>& list)
|
|
{
|
|
for (auto& l : list)
|
|
{
|
|
l.Start += delta;
|
|
l.End += delta;
|
|
}
|
|
}
|
|
|
|
void TeleportList(const Float3& delta, Array<Vertex>& list)
|
|
{
|
|
for (auto& v : list)
|
|
{
|
|
v.Position += delta;
|
|
}
|
|
}
|
|
|
|
void TeleportList(const Float3& delta, Array<DebugTriangle>& list)
|
|
{
|
|
for (auto& v : list)
|
|
{
|
|
v.V0 += delta;
|
|
v.V1 += delta;
|
|
v.V2 += delta;
|
|
}
|
|
}
|
|
|
|
void TeleportList(const Float3& delta, Array<DebugText3D>& list)
|
|
{
|
|
for (auto& v : list)
|
|
{
|
|
v.Transform.Translation += delta;
|
|
}
|
|
}
|
|
|
|
struct DebugDrawData
|
|
{
|
|
Array<DebugLine> DefaultLines;
|
|
Array<Vertex> OneFrameLines;
|
|
Array<DebugTriangle> DefaultTriangles;
|
|
Array<DebugTriangle> OneFrameTriangles;
|
|
Array<DebugTriangle> DefaultWireTriangles;
|
|
Array<DebugTriangle> OneFrameWireTriangles;
|
|
Array<DebugText2D> DefaultText2D;
|
|
Array<DebugText2D> OneFrameText2D;
|
|
Array<DebugText3D> DefaultText3D;
|
|
Array<DebugText3D> OneFrameText3D;
|
|
|
|
inline int32 Count() const
|
|
{
|
|
return LinesCount() + TrianglesCount() + TextCount();
|
|
}
|
|
|
|
inline int32 LinesCount() const
|
|
{
|
|
return DefaultLines.Count() + OneFrameLines.Count();
|
|
}
|
|
|
|
inline int32 TrianglesCount() const
|
|
{
|
|
return DefaultTriangles.Count() + OneFrameTriangles.Count() + DefaultWireTriangles.Count() + OneFrameWireTriangles.Count();
|
|
}
|
|
|
|
inline int32 TextCount() const
|
|
{
|
|
return DefaultText2D.Count() + OneFrameText2D.Count() + DefaultText3D.Count() + OneFrameText3D.Count();
|
|
}
|
|
|
|
inline void Add(const DebugTriangle& t)
|
|
{
|
|
if (t.TimeLeft > 0)
|
|
DefaultTriangles.Add(t);
|
|
else
|
|
OneFrameTriangles.Add(t);
|
|
}
|
|
|
|
inline void AddWire(const DebugTriangle& t)
|
|
{
|
|
if (t.TimeLeft > 0)
|
|
DefaultWireTriangles.Add(t);
|
|
else
|
|
OneFrameWireTriangles.Add(t);
|
|
}
|
|
|
|
inline void Update(float deltaTime)
|
|
{
|
|
UpdateList(deltaTime, DefaultLines);
|
|
UpdateList(deltaTime, DefaultTriangles);
|
|
UpdateList(deltaTime, DefaultWireTriangles);
|
|
UpdateList(deltaTime, DefaultText2D);
|
|
UpdateList(deltaTime, DefaultText3D);
|
|
|
|
OneFrameLines.Clear();
|
|
OneFrameTriangles.Clear();
|
|
OneFrameWireTriangles.Clear();
|
|
OneFrameText2D.Clear();
|
|
OneFrameText3D.Clear();
|
|
}
|
|
|
|
void Teleport(const Float3& delta)
|
|
{
|
|
TeleportList(delta, DefaultLines);
|
|
TeleportList(delta, OneFrameLines);
|
|
TeleportList(delta, DefaultTriangles);
|
|
TeleportList(delta, OneFrameTriangles);
|
|
TeleportList(delta, DefaultWireTriangles);
|
|
TeleportList(delta, OneFrameWireTriangles);
|
|
TeleportList(delta, DefaultText3D);
|
|
TeleportList(delta, OneFrameText3D);
|
|
}
|
|
|
|
inline void Clear()
|
|
{
|
|
DefaultLines.Clear();
|
|
OneFrameLines.Clear();
|
|
DefaultTriangles.Clear();
|
|
OneFrameTriangles.Clear();
|
|
DefaultWireTriangles.Clear();
|
|
OneFrameWireTriangles.Clear();
|
|
DefaultText2D.Clear();
|
|
OneFrameText2D.Clear();
|
|
DefaultText3D.Clear();
|
|
OneFrameText3D.Clear();
|
|
}
|
|
|
|
inline void Release()
|
|
{
|
|
DefaultLines.Resize(0);
|
|
OneFrameLines.Resize(0);
|
|
DefaultTriangles.Resize(0);
|
|
OneFrameTriangles.Resize(0);
|
|
DefaultWireTriangles.Resize(0);
|
|
OneFrameWireTriangles.Resize(0);
|
|
DefaultText2D.Resize(0);
|
|
OneFrameText2D.Resize(0);
|
|
DefaultText3D.Resize(0);
|
|
OneFrameText3D.Resize(0);
|
|
}
|
|
};
|
|
|
|
struct DebugDrawContext
|
|
{
|
|
Vector3 Origin = Vector3::Zero;
|
|
DebugDrawData DebugDrawDefault;
|
|
DebugDrawData DebugDrawDepthTest;
|
|
Float3 LastViewPos = Float3::Zero;
|
|
Matrix LastViewProj = Matrix::Identity;
|
|
};
|
|
|
|
namespace
|
|
{
|
|
DebugDrawContext GlobalContext;
|
|
DebugDrawContext* Context;
|
|
AssetReference<Shader> DebugDrawShader;
|
|
AssetReference<FontAsset> DebugDrawFont;
|
|
PsData DebugDrawPsLinesDefault;
|
|
PsData DebugDrawPsLinesDepthTest;
|
|
PsData DebugDrawPsWireTrianglesDefault;
|
|
PsData DebugDrawPsWireTrianglesDepthTest;
|
|
PsData DebugDrawPsTrianglesDefault;
|
|
PsData DebugDrawPsTrianglesDepthTest;
|
|
DynamicVertexBuffer* DebugDrawVB = nullptr;
|
|
Float3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES];
|
|
Array<Float3> SphereTriangleCache;
|
|
DebugSphereCache SphereCache[3];
|
|
|
|
#if COMPILE_WITH_DEV_ENV
|
|
void OnShaderReloading(Asset* obj)
|
|
{
|
|
DebugDrawPsLinesDefault.Release();
|
|
DebugDrawPsLinesDepthTest.Release();
|
|
DebugDrawPsWireTrianglesDefault.Release();
|
|
DebugDrawPsWireTrianglesDepthTest.Release();
|
|
DebugDrawPsTrianglesDefault.Release();
|
|
DebugDrawPsTrianglesDepthTest.Release();
|
|
}
|
|
|
|
#endif
|
|
};
|
|
|
|
extern int32 BoxTrianglesIndicesCache[];
|
|
|
|
int32 BoxLineIndicesCache[] =
|
|
{
|
|
// @formatter:off
|
|
0, 1,
|
|
0, 3,
|
|
0, 4,
|
|
1, 2,
|
|
1, 5,
|
|
2, 3,
|
|
2, 6,
|
|
3, 7,
|
|
4, 5,
|
|
4, 7,
|
|
5, 6,
|
|
6, 7,
|
|
// @formatter:on
|
|
};
|
|
|
|
struct DebugDrawCall
|
|
{
|
|
int32 StartVertex;
|
|
int32 VertexCount;
|
|
};
|
|
|
|
DebugDrawCall WriteList(int32& vertexCounter, const Array<Vertex>& list)
|
|
{
|
|
DebugDrawCall drawCall;
|
|
drawCall.StartVertex = vertexCounter;
|
|
drawCall.VertexCount = list.Count();
|
|
DebugDrawVB->Write(list.Get(), sizeof(Vertex) * drawCall.VertexCount);
|
|
vertexCounter += drawCall.VertexCount;
|
|
return drawCall;
|
|
}
|
|
|
|
DebugDrawCall WriteList(int32& vertexCounter, const Array<DebugLine>& list)
|
|
{
|
|
DebugDrawCall drawCall;
|
|
drawCall.StartVertex = vertexCounter;
|
|
drawCall.VertexCount = list.Count() * 2;
|
|
vertexCounter += drawCall.VertexCount;
|
|
Vertex* dst = DebugDrawVB->WriteReserve<Vertex>(list.Count() * 2);
|
|
for (int32 i = 0, j = 0; i < list.Count(); i++)
|
|
{
|
|
const DebugLine& l = list.Get()[i];
|
|
dst[j++] = { l.Start, l.Color };
|
|
dst[j++] = { l.End, l.Color };
|
|
}
|
|
return drawCall;
|
|
}
|
|
|
|
DebugDrawCall WriteList(int32& vertexCounter, const Array<DebugTriangle>& list)
|
|
{
|
|
DebugDrawCall drawCall;
|
|
drawCall.StartVertex = vertexCounter;
|
|
drawCall.VertexCount = list.Count() * 3;
|
|
vertexCounter += drawCall.VertexCount;
|
|
Vertex* dst = DebugDrawVB->WriteReserve<Vertex>(list.Count() * 3);
|
|
for (int32 i = 0, j = 0; i < list.Count(); i++)
|
|
{
|
|
const DebugTriangle& l = list.Get()[i];
|
|
dst[j++] = { l.V0, l.Color };
|
|
dst[j++] = { l.V1, l.Color };
|
|
dst[j++] = { l.V2, l.Color };
|
|
}
|
|
return drawCall;
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
DebugDrawCall WriteLists(int32& vertexCounter, const Array<T>& listA, const Array<U>& listB)
|
|
{
|
|
const DebugDrawCall drawCallA = WriteList(vertexCounter, listA);
|
|
const DebugDrawCall drawCallB = WriteList(vertexCounter, listB);
|
|
DebugDrawCall drawCall;
|
|
drawCall.StartVertex = drawCallA.StartVertex;
|
|
drawCall.VertexCount = drawCallA.VertexCount + drawCallB.VertexCount;
|
|
return drawCall;
|
|
}
|
|
|
|
FORCE_INLINE DebugTriangle* AppendTriangles(int32 count, float duration, bool depthTest)
|
|
{
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
|
const int32 startIndex = list->Count();
|
|
list->AddUninitialized(count);
|
|
return list->Get() + startIndex;
|
|
}
|
|
|
|
inline void DrawText3D(const DebugText3D& t, const RenderContext& renderContext, const Float3& viewUp, const Matrix& f, const Matrix& vp, const Viewport& viewport, GPUContext* context, GPUTextureView* target, GPUTextureView* depthBuffer)
|
|
{
|
|
Matrix w, fw, m;
|
|
if (t.FaceCamera)
|
|
{
|
|
Matrix s, ss;
|
|
Matrix::Scaling(t.Transform.Scale.X, s);
|
|
Matrix::CreateWorld(t.Transform.Translation, renderContext.View.Direction, viewUp, ss);
|
|
Matrix::Multiply(s, ss, w);
|
|
}
|
|
else
|
|
t.Transform.GetWorld(w);
|
|
Matrix::Multiply(f, w, fw);
|
|
Matrix::Multiply(fw, vp, m);
|
|
Render2D::Begin(context, target, depthBuffer, viewport, m);
|
|
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
|
Render2D::DrawText(DebugDrawFont->CreateFont((float)t.Size), text, t.Color, Vector2::Zero);
|
|
Render2D::End();
|
|
}
|
|
|
|
class DebugDrawService : public EngineService
|
|
{
|
|
public:
|
|
DebugDrawService()
|
|
: EngineService(TEXT("Debug Draw"), -80)
|
|
{
|
|
}
|
|
|
|
bool Init() override;
|
|
void Update() override;
|
|
void Dispose() override;
|
|
};
|
|
|
|
DebugDrawService DebugDrawServiceInstance;
|
|
|
|
bool DebugDrawService::Init()
|
|
{
|
|
Context = &GlobalContext;
|
|
|
|
// Init wireframe sphere cache
|
|
SphereCache[0].Init(DEBUG_DRAW_SPHERE_LOD0_RESOLUTION);
|
|
SphereCache[1].Init(DEBUG_DRAW_SPHERE_LOD1_RESOLUTION);
|
|
SphereCache[2].Init(DEBUG_DRAW_SPHERE_LOD2_RESOLUTION);
|
|
|
|
// Init wireframe circle cache
|
|
int32 index = 0;
|
|
float step = TWO_PI / (float)DEBUG_DRAW_CIRCLE_RESOLUTION;
|
|
for (float a = 0.0f; a < TWO_PI; a += step)
|
|
{
|
|
// Calculate sines and cosines
|
|
float sinA = Math::Sin(a);
|
|
float cosA = Math::Cos(a);
|
|
float sinB = Math::Sin(a + step);
|
|
float cosB = Math::Cos(a + step);
|
|
|
|
CircleCache[index++] = Float3(cosA, sinA, 0.0f);
|
|
CircleCache[index++] = Float3(cosB, sinB, 0.0f);
|
|
}
|
|
|
|
// Init triangle sphere cache
|
|
{
|
|
const int32 verticalSegments = DEBUG_DRAW_TRIANGLE_SPHERE_RESOLUTION;
|
|
const int32 horizontalSegments = DEBUG_DRAW_TRIANGLE_SPHERE_RESOLUTION * 2;
|
|
|
|
Array<Float3> vertices;
|
|
vertices.Resize((verticalSegments + 1) * (horizontalSegments + 1));
|
|
Array<int> indices;
|
|
indices.Resize((verticalSegments) * (horizontalSegments + 1) * 6);
|
|
|
|
int vertexCount = 0;
|
|
|
|
// Generate the first extremity points
|
|
for (int j = 0; j <= horizontalSegments; j++)
|
|
{
|
|
vertices[vertexCount++] = Float3(0, -1, 0);
|
|
}
|
|
|
|
// Create rings of vertices at progressively higher latitudes
|
|
for (int i = 1; i < verticalSegments; i++)
|
|
{
|
|
const float latitude = ((i * PI / verticalSegments) - PI / 2.0f);
|
|
const float dy = Math::Sin(latitude);
|
|
const float dxz = Math::Cos(latitude);
|
|
|
|
// The first point
|
|
auto& firstHorizontalVertex = vertices[vertexCount++];
|
|
firstHorizontalVertex = Float3(0, dy, dxz);
|
|
|
|
// Create a single ring of vertices at this latitude
|
|
for (int j = 1; j < horizontalSegments; j++)
|
|
{
|
|
const float longitude = (j * 2.0f * PI / horizontalSegments);
|
|
const float dx = Math::Sin(longitude) * dxz;
|
|
const float dz = Math::Cos(longitude) * dxz;
|
|
|
|
vertices[vertexCount++] = Float3(dx, dy, dz);
|
|
}
|
|
|
|
// The last point equal to the first point
|
|
vertices[vertexCount++] = firstHorizontalVertex;
|
|
}
|
|
|
|
// Generate the end extremity points
|
|
for (int j = 0; j <= horizontalSegments; j++)
|
|
{
|
|
vertices[vertexCount++] = Float3(0, 1, 0);
|
|
}
|
|
|
|
// Fill the index buffer with triangles joining each pair of latitude rings
|
|
const int stride = horizontalSegments + 1;
|
|
int indexCount = 0;
|
|
for (int i = 0; i < verticalSegments; i++)
|
|
{
|
|
for (int j = 0; j <= horizontalSegments; j++)
|
|
{
|
|
const int nextI = i + 1;
|
|
const int nextJ = (j + 1) % stride;
|
|
|
|
indices[indexCount++] = (i * stride + j);
|
|
indices[indexCount++] = (nextI * stride + j);
|
|
indices[indexCount++] = (i * stride + nextJ);
|
|
|
|
indices[indexCount++] = (i * stride + nextJ);
|
|
indices[indexCount++] = (nextI * stride + j);
|
|
indices[indexCount++] = (nextI * stride + nextJ);
|
|
}
|
|
}
|
|
|
|
// Create cached unit sphere triangles vertices locations
|
|
SphereTriangleCache.Resize(indices.Count());
|
|
for (int32 i = 0; i < indices.Count();)
|
|
{
|
|
SphereTriangleCache[i] = vertices[indices[i]];
|
|
i++;
|
|
SphereTriangleCache[i] = vertices[indices[i]];
|
|
i++;
|
|
SphereTriangleCache[i] = vertices[indices[i]];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void DebugDrawService::Update()
|
|
{
|
|
// Special case for Null renderer
|
|
if (GPUDevice::Instance->GetRendererType() == RendererType::Null)
|
|
{
|
|
GlobalContext.DebugDrawDefault.Clear();
|
|
GlobalContext.DebugDrawDepthTest.Clear();
|
|
return;
|
|
}
|
|
|
|
PROFILE_CPU();
|
|
|
|
// Update lists
|
|
float deltaTime = Time::Update.DeltaTime.GetTotalSeconds();
|
|
#if USE_EDITOR
|
|
if (!Editor::IsPlayMode)
|
|
deltaTime = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
|
|
#endif
|
|
GlobalContext.DebugDrawDefault.Update(deltaTime);
|
|
GlobalContext.DebugDrawDepthTest.Update(deltaTime);
|
|
|
|
// Lazy-init resources
|
|
if (DebugDrawShader == nullptr)
|
|
{
|
|
DebugDrawShader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/DebugDraw"));
|
|
if (DebugDrawShader == nullptr)
|
|
{
|
|
LOG(Fatal, "Cannot load DebugDraw shader");
|
|
}
|
|
#if COMPILE_WITH_DEV_ENV
|
|
DebugDrawShader->OnReloading.Bind(&OnShaderReloading);
|
|
#endif
|
|
}
|
|
if (DebugDrawPsWireTrianglesDepthTest.Depth == nullptr && DebugDrawShader && DebugDrawShader->IsLoaded())
|
|
{
|
|
bool failed = false;
|
|
const auto shader = DebugDrawShader->GetShader();
|
|
|
|
// Create pipeline states
|
|
GPUPipelineState::Description desc = GPUPipelineState::Description::Default;
|
|
desc.BlendMode = BlendingMode::AlphaBlend;
|
|
desc.CullMode = CullMode::TwoSided;
|
|
desc.VS = shader->GetVS("VS");
|
|
|
|
// Default
|
|
desc.PS = shader->GetPS("PS", 0);
|
|
desc.PrimitiveTopology = PrimitiveTopologyType::Line;
|
|
failed |= DebugDrawPsLinesDefault.Create(desc);
|
|
desc.PS = shader->GetPS("PS", 1);
|
|
desc.PrimitiveTopology = PrimitiveTopologyType::Triangle;
|
|
failed |= DebugDrawPsTrianglesDefault.Create(desc);
|
|
desc.Wireframe = true;
|
|
failed |= DebugDrawPsWireTrianglesDefault.Create(desc);
|
|
|
|
// Depth Test
|
|
desc.Wireframe = false;
|
|
desc.PS = shader->GetPS("PS", 2);
|
|
desc.PrimitiveTopology = PrimitiveTopologyType::Line;
|
|
failed |= DebugDrawPsLinesDepthTest.Create(desc);
|
|
desc.PS = shader->GetPS("PS", 3);
|
|
desc.PrimitiveTopology = PrimitiveTopologyType::Triangle;
|
|
failed |= DebugDrawPsTrianglesDepthTest.Create(desc);
|
|
desc.Wireframe = true;
|
|
failed |= DebugDrawPsWireTrianglesDepthTest.Create(desc);
|
|
|
|
if (failed)
|
|
{
|
|
LOG(Fatal, "Cannot setup DebugDraw service!");
|
|
}
|
|
}
|
|
|
|
// Vertex buffer
|
|
if (DebugDrawVB == nullptr)
|
|
DebugDrawVB = New<DynamicVertexBuffer>((uint32)(DEBUG_DRAW_INITIAL_VB_CAPACITY * sizeof(Vertex)), (uint32)sizeof(Vertex), TEXT("DebugDraw.VB"));
|
|
}
|
|
|
|
void DebugDrawService::Dispose()
|
|
{
|
|
// Clear lists
|
|
GlobalContext.DebugDrawDefault.Release();
|
|
GlobalContext.DebugDrawDepthTest.Release();
|
|
|
|
// Release resources
|
|
SphereTriangleCache.Resize(0);
|
|
DebugDrawPsLinesDefault.Release();
|
|
DebugDrawPsLinesDepthTest.Release();
|
|
DebugDrawPsWireTrianglesDefault.Release();
|
|
DebugDrawPsWireTrianglesDepthTest.Release();
|
|
DebugDrawPsTrianglesDefault.Release();
|
|
DebugDrawPsTrianglesDepthTest.Release();
|
|
SAFE_DELETE(DebugDrawVB);
|
|
DebugDrawShader = nullptr;
|
|
}
|
|
|
|
#if USE_EDITOR
|
|
|
|
void* DebugDraw::AllocateContext()
|
|
{
|
|
auto context = (DebugDrawContext*)Allocator::Allocate(sizeof(DebugDrawContext));
|
|
Memory::ConstructItem(context);
|
|
return context;
|
|
}
|
|
|
|
void DebugDraw::FreeContext(void* context)
|
|
{
|
|
ASSERT(context);
|
|
Memory::DestructItem((DebugDrawContext*)context);
|
|
Allocator::Free(context);
|
|
}
|
|
|
|
void DebugDraw::UpdateContext(void* context, float deltaTime)
|
|
{
|
|
if (!context)
|
|
context = &GlobalContext;
|
|
((DebugDrawContext*)context)->DebugDrawDefault.Update(deltaTime);
|
|
((DebugDrawContext*)context)->DebugDrawDepthTest.Update(deltaTime);
|
|
}
|
|
|
|
void DebugDraw::SetContext(void* context)
|
|
{
|
|
Context = context ? (DebugDrawContext*)context : &GlobalContext;
|
|
}
|
|
|
|
#endif
|
|
|
|
void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTextureView* depthBuffer, bool enableDepthTest)
|
|
{
|
|
PROFILE_GPU_CPU("Debug Draw");
|
|
|
|
// Ensure to have shader loaded and any lines to render
|
|
const int32 debugDrawDepthTestCount = Context->DebugDrawDepthTest.Count();
|
|
const int32 debugDrawDefaultCount = Context->DebugDrawDefault.Count();
|
|
if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || debugDrawDepthTestCount + debugDrawDefaultCount == 0 || DebugDrawPsWireTrianglesDepthTest.Depth == nullptr)
|
|
return;
|
|
if (renderContext.Buffers == nullptr || !DebugDrawVB)
|
|
return;
|
|
auto context = GPUDevice::Instance->GetMainContext();
|
|
const RenderView& view = renderContext.View;
|
|
if (Context->Origin != view.Origin)
|
|
{
|
|
// Teleport existing debug shapes to maintain their location
|
|
Float3 delta = Context->Origin - view.Origin;
|
|
Context->DebugDrawDefault.Teleport(delta);
|
|
Context->DebugDrawDepthTest.Teleport(delta);
|
|
Context->Origin = view.Origin;
|
|
}
|
|
Context->LastViewPos = view.Position;
|
|
Context->LastViewProj = view.Projection;
|
|
TaaJitterRemoveContext taaJitterRemove(view);
|
|
|
|
// Fallback to task buffers
|
|
if (target == nullptr && renderContext.Task)
|
|
target = renderContext.Task->GetOutputView();
|
|
|
|
// Fill vertex buffer and upload data
|
|
DebugDrawCall depthTestLines, defaultLines, depthTestTriangles, defaultTriangles, depthTestWireTriangles, defaultWireTriangles;
|
|
{
|
|
PROFILE_CPU_NAMED("Update Buffer");
|
|
DebugDrawVB->Clear();
|
|
int32 vertexCounter = 0;
|
|
depthTestLines = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultLines, Context->DebugDrawDepthTest.OneFrameLines);
|
|
defaultLines = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultLines, Context->DebugDrawDefault.OneFrameLines);
|
|
depthTestTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultTriangles, Context->DebugDrawDepthTest.OneFrameTriangles);
|
|
defaultTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultTriangles, Context->DebugDrawDefault.OneFrameTriangles);
|
|
depthTestWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultWireTriangles, Context->DebugDrawDepthTest.OneFrameWireTriangles);
|
|
defaultWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultWireTriangles, Context->DebugDrawDefault.OneFrameWireTriangles);
|
|
{
|
|
PROFILE_CPU_NAMED("Flush");
|
|
DebugDrawVB->Flush(context);
|
|
}
|
|
}
|
|
|
|
// Update constant buffer
|
|
const auto cb = DebugDrawShader->GetShader()->GetCB(0);
|
|
Data data;
|
|
Matrix vp;
|
|
Matrix::Multiply(view.View, view.Projection, vp);
|
|
Matrix::Transpose(vp, data.ViewProjection);
|
|
data.ClipPosZBias = -0.2f; // Reduce Z-fighting artifacts (eg. editor grid)
|
|
data.EnableDepthTest = enableDepthTest;
|
|
context->UpdateCB(cb, &data);
|
|
context->BindCB(0, cb);
|
|
auto vb = DebugDrawVB->GetBuffer();
|
|
|
|
// Draw with depth test
|
|
if (depthTestLines.VertexCount + depthTestTriangles.VertexCount + depthTestWireTriangles.VertexCount > 0)
|
|
{
|
|
if (data.EnableDepthTest)
|
|
context->BindSR(0, renderContext.Buffers->DepthBuffer);
|
|
const bool enableDepthWrite = data.EnableDepthTest;
|
|
|
|
context->SetRenderTarget(depthBuffer ? depthBuffer : (data.EnableDepthTest ? nullptr : renderContext.Buffers->DepthBuffer->View()), target);
|
|
|
|
// Lines
|
|
if (depthTestLines.VertexCount)
|
|
{
|
|
auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault;
|
|
context->SetState(state->Get(enableDepthWrite, true));
|
|
context->BindVB(ToSpan(&vb, 1));
|
|
context->Draw(depthTestLines.StartVertex, depthTestLines.VertexCount);
|
|
}
|
|
|
|
// Wire Triangles
|
|
if (depthTestWireTriangles.VertexCount)
|
|
{
|
|
auto state = data.EnableDepthTest ? &DebugDrawPsWireTrianglesDepthTest : &DebugDrawPsWireTrianglesDefault;
|
|
context->SetState(state->Get(enableDepthWrite, true));
|
|
context->BindVB(ToSpan(&vb, 1));
|
|
context->Draw(depthTestWireTriangles.StartVertex, depthTestWireTriangles.VertexCount);
|
|
}
|
|
|
|
// Triangles
|
|
if (depthTestTriangles.VertexCount)
|
|
{
|
|
auto state = data.EnableDepthTest ? &DebugDrawPsTrianglesDepthTest : &DebugDrawPsTrianglesDefault;
|
|
context->SetState(state->Get(enableDepthWrite, true));
|
|
context->BindVB(ToSpan(&vb, 1));
|
|
context->Draw(depthTestTriangles.StartVertex, depthTestTriangles.VertexCount);
|
|
}
|
|
|
|
if (data.EnableDepthTest)
|
|
context->UnBindSR(0);
|
|
}
|
|
|
|
// Draw without depth
|
|
if (defaultLines.VertexCount + defaultTriangles.VertexCount + defaultWireTriangles.VertexCount > 0)
|
|
{
|
|
context->SetRenderTarget(target);
|
|
|
|
// Lines
|
|
if (defaultLines.VertexCount)
|
|
{
|
|
context->SetState(DebugDrawPsLinesDefault.Get(false, false));
|
|
context->BindVB(ToSpan(&vb, 1));
|
|
context->Draw(defaultLines.StartVertex, defaultLines.VertexCount);
|
|
}
|
|
|
|
// Wire Triangles
|
|
if (defaultWireTriangles.VertexCount)
|
|
{
|
|
context->SetState(DebugDrawPsWireTrianglesDefault.Get(false, false));
|
|
context->BindVB(ToSpan(&vb, 1));
|
|
context->Draw(defaultWireTriangles.StartVertex, defaultWireTriangles.VertexCount);
|
|
}
|
|
|
|
// Triangles
|
|
if (defaultTriangles.VertexCount)
|
|
{
|
|
context->SetState(DebugDrawPsTrianglesDefault.Get(false, false));
|
|
context->BindVB(ToSpan(&vb, 1));
|
|
context->Draw(defaultTriangles.StartVertex, defaultTriangles.VertexCount);
|
|
}
|
|
}
|
|
|
|
// Text
|
|
if (Context->DebugDrawDefault.TextCount() + Context->DebugDrawDepthTest.TextCount())
|
|
{
|
|
PROFILE_GPU_CPU_NAMED("Text");
|
|
auto features = Render2D::Features;
|
|
|
|
// Disable vertex snapping when rendering 3D text
|
|
Render2D::Features = (Render2D::RenderingFeatures)((uint32)features & ~(uint32)Render2D::RenderingFeatures::VertexSnapping);
|
|
|
|
if (!DebugDrawFont)
|
|
DebugDrawFont = Content::LoadAsyncInternal<FontAsset>(TEXT("Editor/Fonts/Roboto-Regular"));
|
|
if (DebugDrawFont && DebugDrawFont->IsLoaded())
|
|
{
|
|
Viewport viewport = renderContext.Task->GetViewport();
|
|
|
|
if (Context->DebugDrawDefault.DefaultText2D.Count() + Context->DebugDrawDefault.OneFrameText2D.Count())
|
|
{
|
|
Render2D::Begin(context, target, nullptr, viewport);
|
|
for (auto& t : Context->DebugDrawDefault.DefaultText2D)
|
|
{
|
|
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
|
Render2D::DrawText(DebugDrawFont->CreateFont((float)t.Size), text, t.Color, t.Position);
|
|
}
|
|
for (auto& t : Context->DebugDrawDefault.OneFrameText2D)
|
|
{
|
|
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
|
Render2D::DrawText(DebugDrawFont->CreateFont((float)t.Size), text, t.Color, t.Position);
|
|
}
|
|
Render2D::End();
|
|
}
|
|
|
|
if (Context->DebugDrawDefault.DefaultText3D.Count() + Context->DebugDrawDefault.OneFrameText3D.Count())
|
|
{
|
|
Matrix f;
|
|
Matrix::RotationZ(PI, f);
|
|
Float3 viewUp;
|
|
Float3::Transform(Float3::Up, Quaternion::LookRotation(view.Direction, Float3::Up), viewUp);
|
|
for (auto& t : Context->DebugDrawDefault.DefaultText3D)
|
|
DrawText3D(t, renderContext, viewUp, f, vp, viewport, context, target, nullptr);
|
|
for (auto& t : Context->DebugDrawDefault.OneFrameText3D)
|
|
DrawText3D(t, renderContext, viewUp, f, vp, viewport, context, target, nullptr);
|
|
}
|
|
}
|
|
|
|
Render2D::Features = features;
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool DrawActorsTreeWalk(Actor* a)
|
|
{
|
|
if (a->IsActiveInHierarchy())
|
|
{
|
|
a->OnDebugDraw();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes)
|
|
{
|
|
PROFILE_CPU();
|
|
|
|
if (selectedActors)
|
|
{
|
|
for (int32 i = 0; i < selectedActorsCount; i++)
|
|
{
|
|
auto a = selectedActors[i];
|
|
if (a)
|
|
a->OnDebugDrawSelected();
|
|
}
|
|
}
|
|
|
|
if (drawScenes)
|
|
{
|
|
Function<bool(Actor*)> function = &DrawActorsTreeWalk;
|
|
SceneQuery::TreeExecute(function);
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, float size, float duration, bool depthTest)
|
|
{
|
|
const auto rot = Quaternion::FromDirection(direction.GetNormalized());
|
|
const Vector3 up = (rot * Vector3::Up);
|
|
const Vector3 forward = (rot * Vector3::Forward);
|
|
const Vector3 right = (rot * Vector3::Right);
|
|
const float sizeHalf = size * 0.5f;
|
|
DrawLine(origin, origin + up * sizeHalf + up, Color::Green, duration, depthTest);
|
|
DrawLine(origin, origin + forward * sizeHalf + forward, Color::Blue, duration, depthTest);
|
|
DrawLine(origin, origin + right * sizeHalf + right, Color::Red, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest)
|
|
{
|
|
auto dir = origin + direction;
|
|
if (dir.IsNanOrInfinity())
|
|
return;
|
|
DrawLine(origin, origin + direction, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawLine(origin, origin + direction, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float length, float duration, bool depthTest)
|
|
{
|
|
if (isnan(length) || isinf(length))
|
|
return;
|
|
DrawLine(origin, origin + (direction.GetNormalized() * length), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawRay(const Ray& ray, const Color& color, float length, float duration, bool depthTest)
|
|
{
|
|
if (isnan(length) || isinf(length))
|
|
return;
|
|
DrawLine(ray.Position, ray.Position + (ray.Direction.GetNormalized() * length), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest)
|
|
{
|
|
const Float3 startF = start - Context->Origin, endF = end - Context->Origin;
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { startF, endF, Color32(color), duration };
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
else
|
|
{
|
|
Vertex l = { startF, Color32(color) };
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
l.Position = endF;
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& startColor, const Color& endColor, float duration, bool depthTest)
|
|
{
|
|
const Float3 startF = start - Context->Origin, endF = end - Context->Origin;
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
if (duration > 0)
|
|
{
|
|
// TODO: separate start/end colors for persistent lines
|
|
DebugLine l = { startF, endF, Color32(startColor), duration };
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
else
|
|
{
|
|
Vertex l = { startF, Color32(startColor) };
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
l.Position = endF;
|
|
l.Color = Color32(endColor);
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawLines(const Span<Float3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
if (lines.Length() == 0)
|
|
return;
|
|
if (lines.Length() % 2 != 0)
|
|
{
|
|
DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array");
|
|
return;
|
|
}
|
|
|
|
// Draw lines
|
|
const Float3* p = lines.Get();
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
const Matrix transformF = transform * Matrix::Translation(-Context->Origin);
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { Float3::Zero, Float3::Zero, Color32(color), duration };
|
|
debugDrawData.DefaultLines.EnsureCapacity(debugDrawData.DefaultLines.Count() + lines.Length());
|
|
for (int32 i = 0; i < lines.Length(); i += 2)
|
|
{
|
|
Float3::Transform(*p++, transformF, l.Start);
|
|
Float3::Transform(*p++, transformF, l.End);
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vertex l = { Float3::Zero, Color32(color) };
|
|
debugDrawData.OneFrameLines.EnsureCapacity(debugDrawData.OneFrameLines.Count() + lines.Length() * 2);
|
|
for (int32 i = 0; i < lines.Length(); i += 2)
|
|
{
|
|
Float3::Transform(*p++, transformF, l.Position);
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
Float3::Transform(*p++, transformF, l.Position);
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawLines(const Array<Float3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawLines(Span<Float3>(lines.Get(), lines.Count()), transform, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawLines(const Span<Double3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
if (lines.Length() == 0)
|
|
return;
|
|
if (lines.Length() % 2 != 0)
|
|
{
|
|
DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array");
|
|
return;
|
|
}
|
|
|
|
// Draw lines
|
|
const Double3* p = lines.Get();
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
const Matrix transformF = transform * Matrix::Translation(-Context->Origin);
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { Float3::Zero, Float3::Zero, Color32(color), duration };
|
|
debugDrawData.DefaultLines.EnsureCapacity(debugDrawData.DefaultLines.Count() + lines.Length());
|
|
for (int32 i = 0; i < lines.Length(); i += 2)
|
|
{
|
|
Float3::Transform(*p++, transformF, l.Start);
|
|
Float3::Transform(*p++, transformF, l.End);
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vertex l = { Float3::Zero, Color32(color) };
|
|
debugDrawData.OneFrameLines.EnsureCapacity(debugDrawData.OneFrameLines.Count() + lines.Length() * 2);
|
|
for (int32 i = 0; i < lines.Length(); i += 2)
|
|
{
|
|
Float3::Transform(*p++, transformF, l.Position);
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
Float3::Transform(*p++, transformF, l.Position);
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawLines(const Array<Double3, HeapAllocation>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawLines(Span<Double3>(lines.Get(), lines.Count()), transform, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Color& color, float duration, bool depthTest)
|
|
{
|
|
const Float3 p1F = p1 - Context->Origin, p2F = p2 - Context->Origin, p3F = p3 - Context->Origin, p4F = p4 - Context->Origin;
|
|
|
|
// Find amount of segments to use
|
|
const Float3 d1 = p2F - p1F;
|
|
const Float3 d2 = p3F - p2F;
|
|
const Float3 d3 = p4F - p3F;
|
|
const float len = d1.Length() + d2.Length() + d3.Length();
|
|
const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100);
|
|
const float segmentCountInv = 1.0f / (float)segmentCount;
|
|
|
|
// Draw segmented curve from lines
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { p1F, Float3::Zero, Color32(color), duration };
|
|
debugDrawData.DefaultLines.EnsureCapacity(debugDrawData.DefaultLines.Count() + segmentCount + 2);
|
|
for (int32 i = 0; i <= segmentCount; i++)
|
|
{
|
|
const float t = (float)i * segmentCountInv;
|
|
Float3 end;
|
|
AnimationUtils::Bezier(p1F, p2F, p3F, p4F, t, end);
|
|
l.End = end;
|
|
debugDrawData.DefaultLines.Add(l);
|
|
l.Start = l.End;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vertex l = { p1F, Color32(color) };
|
|
debugDrawData.OneFrameLines.EnsureCapacity(debugDrawData.OneFrameLines.Count() + segmentCount * 2 + 4);
|
|
for (int32 i = 0; i <= segmentCount; i++)
|
|
{
|
|
const float t = (float)i * segmentCountInv;
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
Float3 position;
|
|
AnimationUtils::Bezier(p1F, p2F, p3F, p4F, t, position);
|
|
l.Position = position;
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireBox(const BoundingBox& box, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Get corners
|
|
Vector3 corners[8];
|
|
box.GetCorners(corners);
|
|
for (Vector3& c : corners)
|
|
c -= Context->Origin;
|
|
|
|
// Draw lines
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { Float3::Zero, Float3::Zero, Color32(color), duration };
|
|
for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);)
|
|
{
|
|
l.Start = corners[BoxLineIndicesCache[i++]];
|
|
l.End = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: draw lines as strips to reuse vertices with a single draw call
|
|
Vertex l = { Float3::Zero, Color32(color) };
|
|
for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);)
|
|
{
|
|
l.Position = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
l.Position = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireFrustum(const BoundingFrustum& frustum, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Get corners
|
|
Vector3 corners[8];
|
|
frustum.GetCorners(corners);
|
|
for (Vector3& c : corners)
|
|
c -= Context->Origin;
|
|
|
|
// Draw lines
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { Float3::Zero, Float3::Zero, Color32(color), duration };
|
|
for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);)
|
|
{
|
|
l.Start = corners[BoxLineIndicesCache[i++]];
|
|
l.End = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: draw lines as strips to reuse vertices with a single draw call
|
|
Vertex l = { Float3::Zero, Color32(color) };
|
|
for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);)
|
|
{
|
|
l.Position = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
l.Position = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireBox(const OrientedBoundingBox& box, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Get corners
|
|
Vector3 corners[8];
|
|
box.GetCorners(corners);
|
|
for (Vector3& c : corners)
|
|
c -= Context->Origin;
|
|
|
|
// Draw lines
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { Float3::Zero, Float3::Zero, Color32(color), duration };
|
|
for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);)
|
|
{
|
|
l.Start = corners[BoxLineIndicesCache[i++]];
|
|
l.End = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: draw lines as strips to reuse vertices with a single draw call
|
|
Vertex l = { Float3::Zero, Color32(color) };
|
|
for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);)
|
|
{
|
|
l.Position = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
l.Position = corners[BoxLineIndicesCache[i++]];
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Select LOD
|
|
int32 index;
|
|
const Float3 centerF = sphere.Center - Context->Origin;
|
|
const float radiusF = (float)sphere.Radius;
|
|
const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(centerF, radiusF, Context->LastViewPos, Context->LastViewProj);
|
|
if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * 0.25f)
|
|
index = 0;
|
|
else if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * 0.25f)
|
|
index = 1;
|
|
else
|
|
index = 2;
|
|
auto& cache = SphereCache[index];
|
|
|
|
// Draw lines of the unit sphere after linear transform
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { Float3::Zero, Float3::Zero, Color32(color), duration };
|
|
for (int32 i = 0; i < cache.Vertices.Count();)
|
|
{
|
|
l.Start = centerF + cache.Vertices.Get()[i++] * radiusF;
|
|
l.End = centerF + cache.Vertices.Get()[i++] * radiusF;
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vertex l = { Float3::Zero, Color32(color) };
|
|
for (int32 i = 0; i < cache.Vertices.Count();)
|
|
{
|
|
l.Position = centerF + cache.Vertices.Get()[i++] * radiusF;
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
l.Position = centerF + cache.Vertices.Get()[i++] * radiusF;
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawSphere(const BoundingSphere& sphere, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
|
list->EnsureCapacity(list->Count() + SphereTriangleCache.Count());
|
|
|
|
const Float3 centerF = sphere.Center - Context->Origin;
|
|
const float radiusF = (float)sphere.Radius;
|
|
for (int32 i = 0; i < SphereTriangleCache.Count();)
|
|
{
|
|
t.V0 = centerF + SphereTriangleCache[i++] * radiusF;
|
|
t.V1 = centerF + SphereTriangleCache[i++] * radiusF;
|
|
t.V2 = centerF + SphereTriangleCache[i++] * radiusF;
|
|
list->Add(t);
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawCircle(const Vector3& position, const Float3& normal, float radius, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Create matrix transform for unit circle points
|
|
Matrix world, scale, matrix;
|
|
Float3 right, up;
|
|
if (Float3::Dot(normal, Float3::Up) > 0.99f)
|
|
right = Float3::Right;
|
|
else if (Float3::Dot(normal, Float3::Down) > 0.99f)
|
|
right = Float3::Left;
|
|
else
|
|
Float3::Cross(normal, Float3::Up, right);
|
|
Float3::Cross(right, normal, up);
|
|
Matrix::Scaling(radius, scale);
|
|
const Float3 positionF = position - Context->Origin;
|
|
Matrix::CreateWorld(positionF, normal, up, world);
|
|
Matrix::Multiply(scale, world, matrix);
|
|
|
|
// Draw lines of the unit circle after linear transform
|
|
Float3 prev = Float3::Transform(CircleCache[0], matrix);
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
for (int32 i = 1; i < DEBUG_DRAW_CIRCLE_VERTICES;)
|
|
{
|
|
Float3 cur = Float3::Transform(CircleCache[i++], matrix);
|
|
if (duration > 0)
|
|
{
|
|
DebugLine l = { prev, cur, Color32(color), duration };
|
|
debugDrawData.DefaultLines.Add(l);
|
|
}
|
|
else
|
|
{
|
|
Vertex l = { prev, Color32(color) };
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
l.Position = cur;
|
|
debugDrawData.OneFrameLines.Add(l);
|
|
}
|
|
prev = cur;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawLine(v0, v1, color, duration, depthTest);
|
|
DrawLine(v1, v2, color, duration, depthTest);
|
|
DrawLine(v2, v0, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
t.V0 = v0 - Context->Origin;
|
|
t.V1 = v1 - Context->Origin;
|
|
t.V2 = v2 - Context->Origin;
|
|
if (depthTest)
|
|
Context->DebugDrawDepthTest.Add(t);
|
|
else
|
|
Context->DebugDrawDefault.Add(t);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Span<Float3>& vertices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(vertices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(vertices.Length() / 3, duration, depthTest);
|
|
const Float3 origin = Context->Origin;
|
|
for (int32 i = 0; i < vertices.Length();)
|
|
{
|
|
t.V0 = vertices.Get()[i++] - origin;
|
|
t.V1 = vertices.Get()[i++] - origin;
|
|
t.V2 = vertices.Get()[i++] - origin;
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Span<Float3>& vertices, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(vertices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(vertices.Length() / 3, duration, depthTest);
|
|
const Matrix transformF = transform * Matrix::Translation(-Context->Origin);
|
|
for (int32 i = 0; i < vertices.Length();)
|
|
{
|
|
Float3::Transform(vertices.Get()[i++], transformF, t.V0);
|
|
Float3::Transform(vertices.Get()[i++], transformF, t.V1);
|
|
Float3::Transform(vertices.Get()[i++], transformF, t.V2);
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Array<Float3>& vertices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawTriangles(Span<Float3>(vertices.Get(), vertices.Count()), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Array<Float3>& vertices, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawTriangles(Span<Float3>(vertices.Get(), vertices.Count()), transform, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(indices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(indices.Length() / 3, duration, depthTest);
|
|
const Float3 origin = Context->Origin;
|
|
for (int32 i = 0; i < indices.Length();)
|
|
{
|
|
t.V0 = vertices[indices.Get()[i++]] - origin;
|
|
t.V1 = vertices[indices.Get()[i++]] - origin;
|
|
t.V2 = vertices[indices.Get()[i++]] - origin;
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(indices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(indices.Length() / 3, duration, depthTest);
|
|
const Matrix transformF = transform * Matrix::Translation(-Context->Origin);
|
|
for (int32 i = 0; i < indices.Length();)
|
|
{
|
|
Float3::Transform(vertices[indices.Get()[i++]], transformF, t.V0);
|
|
Float3::Transform(vertices[indices.Get()[i++]], transformF, t.V1);
|
|
Float3::Transform(vertices[indices.Get()[i++]], transformF, t.V2);
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Array<Float3>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawTriangles(Span<Float3>(vertices.Get(), vertices.Count()), Span<int32>(indices.Get(), indices.Count()), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Array<Float3>& vertices, const Array<int32, HeapAllocation>& indices, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawTriangles(Span<Float3>(vertices.Get(), vertices.Count()), Span<int32>(indices.Get(), indices.Count()), transform, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Span<Double3>& vertices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(vertices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(vertices.Length() / 3, duration, depthTest);
|
|
const Double3 origin = Context->Origin;
|
|
for (int32 i = 0; i < vertices.Length();)
|
|
{
|
|
t.V0 = vertices.Get()[i++] - origin;
|
|
t.V1 = vertices.Get()[i++] - origin;
|
|
t.V2 = vertices.Get()[i++] - origin;
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Span<Double3>& vertices, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(vertices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(vertices.Length() / 3, duration, depthTest);
|
|
const Matrix transformF = transform * Matrix::Translation(-Context->Origin);
|
|
for (int32 i = 0; i < vertices.Length();)
|
|
{
|
|
Float3::Transform(vertices.Get()[i++], transformF, t.V0);
|
|
Float3::Transform(vertices.Get()[i++], transformF, t.V1);
|
|
Float3::Transform(vertices.Get()[i++], transformF, t.V2);
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Array<Double3>& vertices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawTriangles(Span<Double3>(vertices.Get(), vertices.Count()), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Array<Double3>& vertices, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawTriangles(Span<Double3>(vertices.Get(), vertices.Count()), transform, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(indices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(indices.Length() / 3, duration, depthTest);
|
|
const Double3 origin = Context->Origin;
|
|
for (int32 i = 0; i < indices.Length();)
|
|
{
|
|
t.V0 = vertices[indices.Get()[i++]] - origin;
|
|
t.V1 = vertices[indices.Get()[i++]] - origin;
|
|
t.V2 = vertices[indices.Get()[i++]] - origin;
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(indices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(indices.Length() / 3, duration, depthTest);
|
|
const Matrix transformF = transform * Matrix::Translation(-Context->Origin);
|
|
for (int32 i = 0; i < indices.Length();)
|
|
{
|
|
Float3::Transform(vertices[indices.Get()[i++]], transformF, t.V0);
|
|
Float3::Transform(vertices[indices.Get()[i++]], transformF, t.V1);
|
|
Float3::Transform(vertices[indices.Get()[i++]], transformF, t.V2);
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Array<Double3>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawTriangles(Span<Double3>(vertices.Get(), vertices.Count()), Span<int32>(indices.Get(), indices.Count()), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTriangles(const Array<Double3>& vertices, const Array<int32, HeapAllocation>& indices, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawTriangles(Span<Double3>(vertices.Get(), vertices.Count()), Span<int32>(indices.Get(), indices.Count()), transform, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangles(const Span<Float3>& vertices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(vertices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(vertices.Length() / 3, duration, depthTest);
|
|
const Float3 origin = Context->Origin;
|
|
for (int32 i = 0; i < vertices.Length();)
|
|
{
|
|
t.V0 = vertices.Get()[i++] - origin;
|
|
t.V1 = vertices.Get()[i++] - origin;
|
|
t.V2 = vertices.Get()[i++] - origin;
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangles(const Array<Float3>& vertices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawWireTriangles(Span<Float3>(vertices.Get(), vertices.Count()), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(indices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(indices.Length() / 3, duration, depthTest);
|
|
const Float3 origin = Context->Origin;
|
|
for (int32 i = 0; i < indices.Length();)
|
|
{
|
|
t.V0 = vertices[indices.Get()[i++]] - origin;
|
|
t.V1 = vertices[indices.Get()[i++]] - origin;
|
|
t.V2 = vertices[indices.Get()[i++]] - origin;
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangles(const Array<Float3>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawWireTriangles(Span<Float3>(vertices.Get(), vertices.Count()), Span<int32>(indices.Get(), indices.Count()), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangles(const Span<Double3>& vertices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(vertices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(vertices.Length() / 3, duration, depthTest);
|
|
const Double3 origin = Context->Origin;
|
|
for (int32 i = 0; i < vertices.Length();)
|
|
{
|
|
t.V0 = vertices.Get()[i++] - origin;
|
|
t.V1 = vertices.Get()[i++] - origin;
|
|
t.V2 = vertices.Get()[i++] - origin;
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangles(const Array<Double3>& vertices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawWireTriangles(Span<Double3>(vertices.Get(), vertices.Count()), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
CHECK(indices.Length() % 3 == 0);
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
auto dst = AppendTriangles(indices.Length() / 3, duration, depthTest);
|
|
const Double3 origin = Context->Origin;
|
|
for (int32 i = 0; i < indices.Length();)
|
|
{
|
|
t.V0 = vertices[indices.Get()[i++]] - origin;
|
|
t.V1 = vertices[indices.Get()[i++]] - origin;
|
|
t.V2 = vertices[indices.Get()[i++]] - origin;
|
|
*dst++ = t;
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireTriangles(const Array<Double3>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color, float duration, bool depthTest)
|
|
{
|
|
DrawWireTriangles(Span<Double3>(vertices.Get(), vertices.Count()), Span<int32>(indices.Get(), indices.Count()), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Check if has no length (just sphere)
|
|
if (length < ZeroTolerance)
|
|
{
|
|
DrawSphere(BoundingSphere(position, radius), color, duration, depthTest);
|
|
}
|
|
else
|
|
{
|
|
const Float3 dir = orientation * Float3::Forward;
|
|
radius = Math::Max(radius, 0.05f);
|
|
length = Math::Max(length, 0.05f);
|
|
const float halfLength = length / 2.0f;
|
|
DrawSphere(BoundingSphere(position + dir * halfLength, radius), color, duration, depthTest);
|
|
DrawSphere(BoundingSphere(position - dir * halfLength, radius), color, duration, depthTest);
|
|
DrawCylinder(position, orientation * Quaternion::Euler(90.0f, 0, 0), radius, length, color, duration, depthTest);
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Check if has no length (just sphere)
|
|
if (length < ZeroTolerance)
|
|
{
|
|
DrawWireSphere(BoundingSphere(position, radius), color, duration, depthTest);
|
|
}
|
|
else
|
|
{
|
|
// Set up
|
|
const int32 resolution = 64;
|
|
const float step = TWO_PI / (float)resolution;
|
|
radius = Math::Max(radius, 0.05f);
|
|
length = Math::Max(length, 0.05f);
|
|
const float halfLength = length / 2.0f;
|
|
Matrix rotation, translation, world;
|
|
Matrix::RotationQuaternion(orientation, rotation);
|
|
const Float3 positionF = position - Context->Origin;
|
|
Matrix::Translation(positionF, translation);
|
|
Matrix::Multiply(rotation, translation, world);
|
|
|
|
// Write vertices
|
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
|
Color32 color32(color);
|
|
if (duration > 0)
|
|
{
|
|
#define DRAW_WIRE_BOX_LINE(x1, y1, z1, x2, y2, z2) debugDrawData.DefaultLines.Add({ Float3::Transform(Float3(x1, y1, z1), world), Float3::Transform(Float3(x2, y2, z2), world), color32, duration });
|
|
for (float a = 0.0f; a < TWO_PI; a += step)
|
|
{
|
|
// Calculate sines and cosines
|
|
float sinA = Math::Sin(a) * radius;
|
|
float cosA = Math::Cos(a) * radius;
|
|
float sinB = Math::Sin(a + step) * radius;
|
|
float cosB = Math::Cos(a + step) * radius;
|
|
|
|
// First XY loop
|
|
DRAW_WIRE_BOX_LINE(cosA, sinA, -halfLength, cosB, sinB, -halfLength);
|
|
|
|
// Second loop
|
|
DRAW_WIRE_BOX_LINE(cosA, sinA, halfLength, cosB, sinB, halfLength);
|
|
|
|
if (a >= PI)
|
|
{
|
|
// First XZ loop
|
|
DRAW_WIRE_BOX_LINE(cosA, 0, sinA - halfLength, cosB, 0, sinB - halfLength);
|
|
|
|
// First YZ loop
|
|
DRAW_WIRE_BOX_LINE(0, cosA, sinA - halfLength, 0, cosB, sinB - halfLength);
|
|
}
|
|
else
|
|
{
|
|
// Second XZ loop
|
|
DRAW_WIRE_BOX_LINE(cosA, 0, sinA + halfLength, cosB, 0, sinB + halfLength);
|
|
|
|
// Second YZ loop
|
|
DRAW_WIRE_BOX_LINE(0, cosA, sinA + halfLength, 0, cosB, sinB + halfLength);
|
|
}
|
|
|
|
// Connection
|
|
if (Math::NearEqual(sinA, radius) || Math::NearEqual(cosA, radius) || Math::NearEqual(sinA, -radius) || Math::NearEqual(cosA, -radius))
|
|
{
|
|
DRAW_WIRE_BOX_LINE(cosA, sinA, -halfLength, cosA, sinA, halfLength);
|
|
}
|
|
}
|
|
#undef DRAW_WIRE_BOX_LINE
|
|
}
|
|
else
|
|
{
|
|
#define DRAW_WIRE_BOX_LINE(x1, y1, z1, x2, y2, z2) debugDrawData.OneFrameLines.Add({ Float3::Transform(Float3(x1, y1, z1), world), color32 }); debugDrawData.OneFrameLines.Add({ Float3::Transform(Float3(x2, y2, z2), world), color32 });
|
|
for (float a = 0.0f; a < TWO_PI; a += step)
|
|
{
|
|
// Calculate sines and cosines
|
|
float sinA = Math::Sin(a) * radius;
|
|
float cosA = Math::Cos(a) * radius;
|
|
float sinB = Math::Sin(a + step) * radius;
|
|
float cosB = Math::Cos(a + step) * radius;
|
|
|
|
// First XY loop
|
|
DRAW_WIRE_BOX_LINE(cosA, sinA, -halfLength, cosB, sinB, -halfLength);
|
|
|
|
// Second loop
|
|
DRAW_WIRE_BOX_LINE(cosA, sinA, halfLength, cosB, sinB, halfLength);
|
|
|
|
if (a >= PI)
|
|
{
|
|
// First XZ loop
|
|
DRAW_WIRE_BOX_LINE(cosA, 0, sinA - halfLength, cosB, 0, sinB - halfLength);
|
|
|
|
// First YZ loop
|
|
DRAW_WIRE_BOX_LINE(0, cosA, sinA - halfLength, 0, cosB, sinB - halfLength);
|
|
}
|
|
else
|
|
{
|
|
// Second XZ loop
|
|
DRAW_WIRE_BOX_LINE(cosA, 0, sinA + halfLength, cosB, 0, sinB + halfLength);
|
|
|
|
// Second YZ loop
|
|
DRAW_WIRE_BOX_LINE(0, cosA, sinA + halfLength, 0, cosB, sinB + halfLength);
|
|
}
|
|
|
|
// Connection
|
|
if (Math::NearEqual(sinA, radius) || Math::NearEqual(cosA, radius) || Math::NearEqual(sinA, -radius) || Math::NearEqual(cosA, -radius))
|
|
{
|
|
DRAW_WIRE_BOX_LINE(cosA, sinA, -halfLength, cosA, sinA, halfLength);
|
|
}
|
|
#undef DRAW_WIRE_BOX_LINE
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void DrawCylinder(Array<DebugTriangle>* list, const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color, float duration)
|
|
{
|
|
// Setup cache
|
|
Float3 CylinderCache[DEBUG_DRAW_CYLINDER_VERTICES];
|
|
const float angleBetweenFacets = TWO_PI / DEBUG_DRAW_CYLINDER_RESOLUTION;
|
|
const float verticalOffset = height * 0.5f;
|
|
int32 index = 0;
|
|
for (int32 i = 0; i < DEBUG_DRAW_CYLINDER_RESOLUTION; i++)
|
|
{
|
|
const float theta = i * angleBetweenFacets;
|
|
const float x = Math::Cos(theta) * radius;
|
|
const float z = Math::Sin(theta) * radius;
|
|
|
|
// Top cap
|
|
CylinderCache[index++] = Float3(x, verticalOffset, z);
|
|
|
|
// Top part of body
|
|
CylinderCache[index++] = Float3(x, verticalOffset, z);
|
|
|
|
// Bottom part of body
|
|
CylinderCache[index++] = Float3(x, -verticalOffset, z);
|
|
|
|
// Bottom cap
|
|
CylinderCache[index++] = Float3(x, -verticalOffset, z);
|
|
}
|
|
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
const Float3 positionF = position - Context->Origin;
|
|
const Matrix world = Matrix::RotationQuaternion(orientation) * Matrix::Translation(positionF);
|
|
|
|
// Write triangles
|
|
for (uint32 i = 0; i < DEBUG_DRAW_CYLINDER_VERTICES; i += 4)
|
|
{
|
|
// Each iteration, the loop advances to the next vertex column
|
|
// Four triangles per column (except for the four degenerate cap triangles)
|
|
|
|
// Top cap triangles
|
|
auto nextIndex = (uint16)((i + 4) % DEBUG_DRAW_CYLINDER_VERTICES);
|
|
if (nextIndex != 0)
|
|
{
|
|
Float3::Transform(CylinderCache[i], world, t.V0);
|
|
Float3::Transform(CylinderCache[nextIndex], world, t.V1);
|
|
Float3::Transform(CylinderCache[0], world, t.V2);
|
|
list->Add(t);
|
|
}
|
|
|
|
// Body triangles
|
|
nextIndex = (uint16)((i + 5) % DEBUG_DRAW_CYLINDER_VERTICES);
|
|
Float3::Transform(CylinderCache[(i + 1)], world, t.V0);
|
|
Float3::Transform(CylinderCache[(i + 2)], world, t.V1);
|
|
Float3::Transform(CylinderCache[nextIndex], world, t.V2);
|
|
list->Add(t);
|
|
|
|
Float3::Transform(CylinderCache[nextIndex], world, t.V0);
|
|
Float3::Transform(CylinderCache[(i + 2)], world, t.V1);
|
|
Float3::Transform(CylinderCache[((i + 6) % DEBUG_DRAW_CYLINDER_VERTICES)], world, t.V2);
|
|
list->Add(t);
|
|
|
|
// Bottom cap triangles
|
|
nextIndex = (uint16)((i + 7) % DEBUG_DRAW_CYLINDER_VERTICES);
|
|
if (nextIndex != 3)
|
|
{
|
|
Float3::Transform(CylinderCache[(i + 3)], world, t.V0);
|
|
Float3::Transform(CylinderCache[3], world, t.V1);
|
|
Float3::Transform(CylinderCache[nextIndex], world, t.V2);
|
|
list->Add(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawCone(Array<DebugTriangle>* list, const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color, float duration)
|
|
{
|
|
const float tolerance = 0.001f;
|
|
const float angle1 = Math::Clamp(angleXY, tolerance, PI - tolerance);
|
|
const float angle2 = Math::Clamp(angleXZ, tolerance, PI - tolerance);
|
|
|
|
const float sinX1 = Math::Sin(0.5f * angle1);
|
|
const float sinY2 = Math::Sin(0.5f * angle2);
|
|
const float sinSqX1 = sinX1 * sinX1;
|
|
const float sinSqY2 = sinY2 * sinY2;
|
|
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
const Float3 positionF = position - Context->Origin;
|
|
const Matrix world = Matrix::RotationQuaternion(orientation) * Matrix::Translation(positionF);
|
|
t.V0 = world.GetTranslation();
|
|
|
|
Float3 vertices[DEBUG_DRAW_CONE_RESOLUTION];
|
|
for (uint32 i = 0; i < DEBUG_DRAW_CONE_RESOLUTION; i++)
|
|
{
|
|
const float alpha = (float)i / (float)DEBUG_DRAW_CONE_RESOLUTION;
|
|
const float azimuth = alpha * TWO_PI;
|
|
|
|
const float phi = Math::Atan2(Math::Sin(azimuth) * sinY2, Math::Cos(azimuth) * sinX1);
|
|
const float sinPhi = Math::Sin(phi);
|
|
const float cosPhi = Math::Cos(phi);
|
|
const float sinSqPhi = sinPhi * sinPhi;
|
|
const float cosSqPhi = cosPhi * cosPhi;
|
|
|
|
const float rSq = sinSqX1 * sinSqY2 / (sinSqX1 * sinSqPhi + sinSqY2 * cosSqPhi);
|
|
const float r = Math::Sqrt(rSq);
|
|
const float s = Math::Sqrt(1 - rSq);
|
|
|
|
Float3 vertex = Float3(2 * s * (r * cosPhi), 2 * s * (r * sinPhi), 1 - 2 * rSq) * radius;
|
|
Float3::Transform(vertex, world, vertices[i]);
|
|
}
|
|
|
|
for (uint32 i = 0; i < DEBUG_DRAW_CONE_RESOLUTION; i++)
|
|
{
|
|
t.V1 = vertices[i];
|
|
t.V2 = vertices[(i + 1) % DEBUG_DRAW_CONE_RESOLUTION];
|
|
list->Add(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color, float duration, bool depthTest)
|
|
{
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
|
::DrawCylinder(list, position, orientation, radius, height, color, duration);
|
|
}
|
|
|
|
void DebugDraw::DrawWireCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color, float duration, bool depthTest)
|
|
{
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultWireTriangles : &Context->DebugDrawDepthTest.OneFrameWireTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultWireTriangles : &Context->DebugDrawDefault.OneFrameWireTriangles;
|
|
::DrawCylinder(list, position, orientation, radius, height, color, duration);
|
|
}
|
|
|
|
void DebugDraw::DrawCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color, float duration, bool depthTest)
|
|
{
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
|
::DrawCone(list, position, orientation, radius, angleXY, angleXZ, color, duration);
|
|
}
|
|
|
|
void DebugDraw::DrawWireCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color, float duration, bool depthTest)
|
|
{
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultWireTriangles : &Context->DebugDrawDepthTest.OneFrameWireTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultWireTriangles : &Context->DebugDrawDefault.OneFrameWireTriangles;
|
|
::DrawCone(list, position, orientation, radius, angleXY, angleXZ, color, duration);
|
|
}
|
|
|
|
void DebugDraw::DrawArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color, float duration, bool depthTest)
|
|
{
|
|
if (angle <= 0)
|
|
return;
|
|
if (angle > TWO_PI)
|
|
angle = TWO_PI;
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
|
const int32 resolution = Math::CeilToInt((float)DEBUG_DRAW_CONE_RESOLUTION / TWO_PI * angle);
|
|
const float angleStep = angle / (float)resolution;
|
|
const Float3 positionF = position - Context->Origin;
|
|
const Matrix world = Matrix::RotationQuaternion(orientation) * Matrix::Translation(positionF);
|
|
float currentAngle = 0.0f;
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
t.V0 = world.GetTranslation();
|
|
Float3 pos(Math::Cos(0.0f) * radius, Math::Sin(0.0f) * radius, 0);
|
|
Float3::Transform(pos, world, t.V2);
|
|
for (int32 i = 0; i < resolution; i++)
|
|
{
|
|
t.V1 = t.V2;
|
|
currentAngle += angleStep;
|
|
pos = Float3(Math::Cos(currentAngle) * radius, Math::Sin(currentAngle) * radius, 0);
|
|
Float3::Transform(pos, world, t.V2);
|
|
list->Add(t);
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawWireArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color, float duration, bool depthTest)
|
|
{
|
|
if (angle <= 0)
|
|
return;
|
|
if (angle > TWO_PI)
|
|
angle = TWO_PI;
|
|
const int32 resolution = Math::CeilToInt((float)DEBUG_DRAW_CONE_RESOLUTION / TWO_PI * angle);
|
|
const float angleStep = angle / (float)resolution;
|
|
const Float3 positionF = position - Context->Origin;
|
|
const Matrix world = Matrix::RotationQuaternion(orientation) * Matrix::Translation(positionF);
|
|
float currentAngle = 0.0f;
|
|
Float3 prevPos(world.GetTranslation());
|
|
if (angle >= TWO_PI)
|
|
{
|
|
prevPos = Float3(Math::Cos(TWO_PI - angleStep) * radius, Math::Sin(TWO_PI - angleStep) * radius, 0);
|
|
Float3::Transform(prevPos, world, prevPos);
|
|
}
|
|
for (int32 i = 0; i <= resolution; i++)
|
|
{
|
|
Float3 pos(Math::Cos(currentAngle) * radius, Math::Sin(currentAngle) * radius, 0);
|
|
Float3::Transform(pos, world, pos);
|
|
DrawLine(prevPos, pos, color, duration, depthTest);
|
|
currentAngle += angleStep;
|
|
prevPos = pos;
|
|
}
|
|
if (angle < TWO_PI)
|
|
DrawLine(prevPos, world.GetTranslation(), color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, float capScale, const Color& color, float duration, bool depthTest)
|
|
{
|
|
Float3 direction, up, right;
|
|
Float3::Transform(Float3::Forward, orientation, direction);
|
|
Float3::Transform(Float3::Up, orientation, up);
|
|
Float3::Transform(Float3::Right, orientation, right);
|
|
const Vector3 end = position + direction * (100.0f * scale);
|
|
const Vector3 capEnd = end - (direction * (100 * Math::Min(capScale, scale * 0.5f)));
|
|
const float arrowSidesRatio = Math::Min(capScale, scale * 0.5f) * 30.0f;
|
|
|
|
DrawLine(position, end, color, duration, depthTest);
|
|
DrawLine(end, capEnd + up * arrowSidesRatio, color, duration, depthTest);
|
|
DrawLine(end, capEnd - up * arrowSidesRatio, color, duration, depthTest);
|
|
DrawLine(end, capEnd + right * arrowSidesRatio, color, duration, depthTest);
|
|
DrawLine(end, capEnd - right * arrowSidesRatio, color, duration, depthTest);
|
|
}
|
|
|
|
void DebugDraw::DrawBox(const BoundingBox& box, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Get corners
|
|
Vector3 corners[8];
|
|
box.GetCorners(corners);
|
|
for (Vector3& c : corners)
|
|
c -= Context->Origin;
|
|
|
|
// Draw triangles
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
|
list->EnsureCapacity(list->Count() + 36);
|
|
for (int i0 = 0; i0 < 36;)
|
|
{
|
|
t.V0 = corners[BoxTrianglesIndicesCache[i0++]];
|
|
t.V1 = corners[BoxTrianglesIndicesCache[i0++]];
|
|
t.V2 = corners[BoxTrianglesIndicesCache[i0++]];
|
|
|
|
list->Add(t);
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawBox(const OrientedBoundingBox& box, const Color& color, float duration, bool depthTest)
|
|
{
|
|
// Get corners
|
|
Vector3 corners[8];
|
|
box.GetCorners(corners);
|
|
for (Vector3& c : corners)
|
|
c -= Context->Origin;
|
|
|
|
// Draw triangles
|
|
DebugTriangle t;
|
|
t.Color = Color32(color);
|
|
t.TimeLeft = duration;
|
|
Array<DebugTriangle>* list;
|
|
if (depthTest)
|
|
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
|
else
|
|
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
|
list->EnsureCapacity(list->Count() + 36);
|
|
for (int i0 = 0; i0 < 36;)
|
|
{
|
|
t.V0 = corners[BoxTrianglesIndicesCache[i0++]];
|
|
t.V1 = corners[BoxTrianglesIndicesCache[i0++]];
|
|
t.V2 = corners[BoxTrianglesIndicesCache[i0++]];
|
|
|
|
list->Add(t);
|
|
}
|
|
}
|
|
|
|
void DebugDraw::DrawText(const StringView& text, const Float2& position, const Color& color, int32 size, float duration)
|
|
{
|
|
if (text.Length() == 0 || size < 4)
|
|
return;
|
|
Array<DebugText2D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText2D : &Context->DebugDrawDefault.OneFrameText2D;
|
|
auto& t = list->AddOne();
|
|
t.Text.Resize(text.Length() + 1);
|
|
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
|
|
t.Text[text.Length()] = 0;
|
|
t.Position = position;
|
|
t.Size = size;
|
|
t.Color = color;
|
|
t.TimeLeft = duration;
|
|
}
|
|
|
|
void DebugDraw::DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size, float duration, float scale)
|
|
{
|
|
if (text.Length() == 0 || size < 4)
|
|
return;
|
|
Array<DebugText3D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText3D : &Context->DebugDrawDefault.OneFrameText3D;
|
|
auto& t = list->AddOne();
|
|
t.Text.Resize(text.Length() + 1);
|
|
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
|
|
t.Text[text.Length()] = 0;
|
|
t.Transform = position - Context->Origin;
|
|
t.Transform.Scale.X = scale;
|
|
t.FaceCamera = true;
|
|
t.Size = size;
|
|
t.Color = color;
|
|
t.TimeLeft = duration;
|
|
}
|
|
|
|
void DebugDraw::DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size, float duration)
|
|
{
|
|
if (text.Length() == 0 || size < 4)
|
|
return;
|
|
Array<DebugText3D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText3D : &Context->DebugDrawDefault.OneFrameText3D;
|
|
auto& t = list->AddOne();
|
|
t.Text.Resize(text.Length() + 1);
|
|
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
|
|
t.Text[text.Length()] = 0;
|
|
t.Transform = transform;
|
|
t.Transform.Translation -= Context->Origin;
|
|
t.FaceCamera = false;
|
|
t.Size = size;
|
|
t.Color = color;
|
|
t.TimeLeft = duration;
|
|
}
|
|
|
|
#endif
|