922f361cb0
- Made a bunch of changes to comply with Godot 4 API - Use Godot's Vector3i and add the missing stuff with helper functions - Transvoxel uses custom attributes API, the old way would not work - Wrap MeshOptimizer in a unique namespace (see build script why) - Added clang-format file for the module as some rules now differ - Prevent thirdparty code and lookup tables from being clang-formatted - Very likely full of runtime bugs that need fixing
256 lines
6.8 KiB
C++
256 lines
6.8 KiB
C++
#include "voxel_debug.h"
|
|
#include "../util/fixed_array.h"
|
|
#include "../util/godot/direct_mesh_instance.h"
|
|
#include "../util/godot/direct_multimesh_instance.h"
|
|
|
|
#include <scene/resources/mesh.h>
|
|
|
|
namespace VoxelDebug {
|
|
|
|
FixedArray<Ref<Mesh>, ID_COUNT> g_wirecubes;
|
|
bool g_finalized = false;
|
|
|
|
template <typename T> void raw_copy_to(Vector<T> &dst, const T *src, unsigned int count) {
|
|
dst.resize(count);
|
|
memcpy(dst.ptrw(), src, count * sizeof(T));
|
|
}
|
|
|
|
static Color get_color(ColorID id) {
|
|
switch (id) {
|
|
case ID_VOXEL_BOUNDS:
|
|
return Color(1, 1, 1);
|
|
case ID_OCTREE_BOUNDS:
|
|
return Color(0.5, 0.5, 0.5);
|
|
case ID_VOXEL_GRAPH_DEBUG_BOUNDS:
|
|
return Color(1.0, 1.0, 0.0);
|
|
case ID_WHITE:
|
|
return Color(1, 1, 1);
|
|
default:
|
|
CRASH_NOW_MSG("Unexpected index");
|
|
}
|
|
return Color();
|
|
}
|
|
|
|
Ref<Mesh> get_wirecube(ColorID id) {
|
|
CRASH_COND(g_finalized);
|
|
|
|
Ref<Mesh> &wirecube = g_wirecubes[id];
|
|
|
|
if (wirecube.is_null()) {
|
|
const Vector3 positions_raw[] = { Vector3(0, 0, 0), Vector3(1, 0, 0), Vector3(1, 0, 1), Vector3(0, 0, 1),
|
|
Vector3(0, 1, 0), Vector3(1, 1, 0), Vector3(1, 1, 1), Vector3(0, 1, 1) };
|
|
PackedVector3Array positions;
|
|
raw_copy_to(positions, positions_raw, 8);
|
|
|
|
Color white(1.0, 1.0, 1.0);
|
|
PackedColorArray colors;
|
|
colors.resize(positions.size());
|
|
for (int i = 0; i < colors.size(); ++i) {
|
|
colors.write[i] = white;
|
|
}
|
|
|
|
const int indices_raw[] = { 0, 1, 1, 2, 2, 3, 3, 0,
|
|
|
|
4, 5, 5, 6, 6, 7, 7, 4,
|
|
|
|
0, 4, 1, 5, 2, 6, 3, 7 };
|
|
PackedInt32Array indices;
|
|
raw_copy_to(indices, indices_raw, 24);
|
|
|
|
Array arrays;
|
|
arrays.resize(Mesh::ARRAY_MAX);
|
|
arrays[Mesh::ARRAY_VERTEX] = positions;
|
|
arrays[Mesh::ARRAY_COLOR] = colors;
|
|
arrays[Mesh::ARRAY_INDEX] = indices;
|
|
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
|
|
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, arrays);
|
|
|
|
Ref<StandardMaterial3D> mat;
|
|
mat.instantiate();
|
|
mat->set_albedo(get_color(id));
|
|
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
|
|
mesh->surface_set_material(0, mat);
|
|
|
|
wirecube = mesh;
|
|
}
|
|
|
|
return wirecube;
|
|
}
|
|
|
|
void free_resources() {
|
|
for (unsigned int i = 0; i < g_wirecubes.size(); ++i) {
|
|
g_wirecubes[i].unref();
|
|
}
|
|
g_finalized = true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class DebugRendererItem {
|
|
public:
|
|
DebugRendererItem() {
|
|
_mesh_instance.create();
|
|
// TODO When shadow casting is on, directional shadows completely break.
|
|
// The reason is still unknown.
|
|
// It should be off anyways, but it's rather concerning.
|
|
_mesh_instance.set_cast_shadows_setting(RenderingServer::SHADOW_CASTING_SETTING_OFF);
|
|
}
|
|
|
|
void set_mesh(Ref<Mesh> mesh) {
|
|
if (_mesh != mesh) {
|
|
_mesh = mesh;
|
|
_mesh_instance.set_mesh(mesh);
|
|
}
|
|
}
|
|
|
|
void set_transform(Transform3D t) {
|
|
if (_transform != t) {
|
|
_transform = t;
|
|
_mesh_instance.set_transform(t);
|
|
}
|
|
}
|
|
|
|
void set_visible(bool visible) {
|
|
if (_visible != visible) {
|
|
_visible = visible;
|
|
_mesh_instance.set_visible(visible);
|
|
}
|
|
}
|
|
|
|
void set_world(World3D *world) {
|
|
_mesh_instance.set_world(world);
|
|
}
|
|
|
|
private:
|
|
Transform3D _transform;
|
|
bool _visible = true;
|
|
Ref<Mesh> _mesh;
|
|
DirectMeshInstance _mesh_instance;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DebugRenderer::~DebugRenderer() {
|
|
clear();
|
|
}
|
|
|
|
void DebugRenderer::clear() {
|
|
for (auto it = _items.begin(); it != _items.end(); ++it) {
|
|
memdelete(*it);
|
|
}
|
|
_items.clear();
|
|
_mm_renderer.clear();
|
|
}
|
|
|
|
void DebugRenderer::set_world(World3D *world) {
|
|
_world = world;
|
|
for (auto it = _items.begin(); it != _items.end(); ++it) {
|
|
(*it)->set_world(world);
|
|
}
|
|
_mm_renderer.set_world(world);
|
|
}
|
|
|
|
void DebugRenderer::begin() {
|
|
CRASH_COND(_inside_block);
|
|
CRASH_COND(_world == nullptr);
|
|
_current = 0;
|
|
_inside_block = true;
|
|
_mm_renderer.begin();
|
|
}
|
|
|
|
void DebugRenderer::draw_box(const Transform3D &t, ColorID color) {
|
|
// Pick an existing item, or create one
|
|
DebugRendererItem *item;
|
|
if (_current >= _items.size()) {
|
|
item = memnew(DebugRendererItem);
|
|
item->set_world(_world);
|
|
_items.push_back(item);
|
|
} else {
|
|
item = _items[_current];
|
|
}
|
|
|
|
item->set_mesh(get_wirecube(color));
|
|
item->set_transform(t);
|
|
item->set_visible(true);
|
|
|
|
++_current;
|
|
}
|
|
|
|
void DebugRenderer::draw_box_mm(const Transform3D &t, Color8 color) {
|
|
_mm_renderer.draw_box(t, color);
|
|
}
|
|
|
|
void DebugRenderer::end() {
|
|
CRASH_COND(!_inside_block);
|
|
// Hide exceeding items
|
|
for (unsigned int i = _current; i < _items.size(); ++i) {
|
|
DebugRendererItem *item = _items[i];
|
|
item->set_visible(false);
|
|
}
|
|
_inside_block = false;
|
|
_mm_renderer.end();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DebugMultiMeshRenderer::DebugMultiMeshRenderer() {
|
|
_multimesh_instance.create();
|
|
// TODO When shadow casting is on, directional shadows completely break.
|
|
// The reason is still unknown.
|
|
// It should be off anyways, but it's rather concerning.
|
|
_multimesh_instance.set_cast_shadows_setting(RenderingServer::SHADOW_CASTING_SETTING_OFF);
|
|
_multimesh.instantiate();
|
|
Ref<Mesh> wirecube = get_wirecube(ID_WHITE);
|
|
_multimesh->set_mesh(wirecube);
|
|
_multimesh->set_transform_format(MultiMesh::TRANSFORM_3D);
|
|
// TODO Optimize: Godot needs to bring back 8-bit color attributes on multimesh, 32-bit colors are too much
|
|
//_multimesh->set_color_format(MultiMesh::COLOR_8BIT);
|
|
_multimesh->set_use_colors(true);
|
|
_multimesh->set_use_custom_data(false);
|
|
_multimesh_instance.set_multimesh(_multimesh);
|
|
_material.instantiate();
|
|
_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
|
|
_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
|
|
_multimesh_instance.set_material_override(_material);
|
|
}
|
|
|
|
void DebugMultiMeshRenderer::set_world(World3D *world) {
|
|
_multimesh_instance.set_world(world);
|
|
_world = world;
|
|
}
|
|
|
|
void DebugMultiMeshRenderer::begin() {
|
|
ERR_FAIL_COND(_inside_block);
|
|
ERR_FAIL_COND(_world == nullptr);
|
|
_inside_block = true;
|
|
}
|
|
|
|
void DebugMultiMeshRenderer::draw_box(const Transform3D &t, Color8 color) {
|
|
_items.push_back(DirectMultiMeshInstance::TransformAndColor32{ t, color });
|
|
}
|
|
|
|
void DebugMultiMeshRenderer::end() {
|
|
ERR_FAIL_COND(!_inside_block);
|
|
_inside_block = false;
|
|
|
|
DirectMultiMeshInstance::make_transform_and_color32_3d_bulk_array(to_span_const(_items), _bulk_array);
|
|
if (_items.size() != static_cast<unsigned int>(_multimesh->get_instance_count())) {
|
|
_multimesh->set_instance_count(_items.size());
|
|
}
|
|
|
|
// Apparently Godot doesn't like empty bulk arrays, it breaks RasterizerStorageGLES3
|
|
if (_items.size() > 0) {
|
|
//_multimesh->set_as_bulk_array(_bulk_array);
|
|
RenderingServer::get_singleton()->multimesh_set_buffer(_multimesh->get_rid(), _bulk_array);
|
|
}
|
|
|
|
_items.clear();
|
|
}
|
|
|
|
void DebugMultiMeshRenderer::clear() {
|
|
_items.clear();
|
|
_multimesh->set_instance_count(0);
|
|
}
|
|
|
|
} // namespace VoxelDebug
|