#include "dc_terrain_utils.h"
#include "dc_mesher.h"
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/classes/mesh.hpp>
#include <algorithm>
#include <cmath>
namespace godot {
void DCTerrainUtils::_bind_methods() {
ClassDB::bind_static_method("DCTerrainUtils",
D_METHOD("mesh_from_generator", "generator", "origin", "voxel_size", "grid_size"),
&DCTerrainUtils::mesh_from_generator);
ClassDB::bind_static_method("DCTerrainUtils",
D_METHOD("mesh_from_generator_auto", "generator", "voxel_size"),
&DCTerrainUtils::mesh_from_generator_auto);
}
Ref<ArrayMesh> DCTerrainUtils::mesh_from_generator(Ref<DCTerrainGenerator> generator,
Vector3 origin, float voxel_size, int grid_size) {
Ref<ArrayMesh> arr_mesh;
arr_mesh.instantiate();
if (!generator.is_valid()) {
UtilityFunctions::printerr("DCTerrainUtils: generator is null");
return arr_mesh;
}
DCGenerator* gen = generator->get_native_generator();
if (!gen) {
UtilityFunctions::printerr("DCTerrainUtils: native generator is null");
return arr_mesh;
}
float org[3] = { origin.x, origin.y, origin.z };
DCMesherConfig config;
config.auto_smooth = true;
config.smooth_angle_deg = 30.0f;
config.qef_bias = 0.01f;
DCMeshData mesh = dc_mesh_from_generator(*gen, org, voxel_size, grid_size, config);
if (mesh.positions.empty() || mesh.indices.empty()) {
UtilityFunctions::print("DCTerrainUtils: generator produced empty mesh");
return arr_mesh;
}
// Convert DCMeshData → Godot arrays
int vert_count = (int)mesh.positions.size() / 3;
int tri_count = (int)mesh.indices.size() / 3;
PackedVector3Array vertices;
vertices.resize(vert_count);
for (int i = 0; i < vert_count; i++) {
vertices[i] = Vector3(mesh.positions[i * 3],
mesh.positions[i * 3 + 1],
mesh.positions[i * 3 + 2]);
}
PackedVector3Array normals;
if (!mesh.normals.empty()) {
normals.resize(vert_count);
for (int i = 0; i < vert_count; i++) {
normals[i] = Vector3(mesh.normals[i * 3],
mesh.normals[i * 3 + 1],
mesh.normals[i * 3 + 2]);
}
}
PackedInt32Array indices;
indices.resize((int)mesh.indices.size());
for (int i = 0; i < (int)mesh.indices.size(); i++) {
indices[i] = mesh.indices[i];
}
Array arrays;
arrays.resize(Mesh::ARRAY_MAX);
arrays[Mesh::ARRAY_VERTEX] = vertices;
if (!normals.is_empty()) {
arrays[Mesh::ARRAY_NORMAL] = normals;
}
arrays[Mesh::ARRAY_INDEX] = indices;
arr_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
UtilityFunctions::print("DCTerrainUtils: meshed ", vert_count, " verts, ", tri_count, " tris");
return arr_mesh;
}
Ref<ArrayMesh> DCTerrainUtils::mesh_from_generator_auto(Ref<DCTerrainGenerator> generator,
float voxel_size) {
if (!generator.is_valid()) {
UtilityFunctions::printerr("DCTerrainUtils: generator is null");
Ref<ArrayMesh> empty;
empty.instantiate();
return empty;
}
DCGenerator* gen = generator->get_native_generator();
if (!gen) {
UtilityFunctions::printerr("DCTerrainUtils: native generator is null");
Ref<ArrayMesh> empty;
empty.instantiate();
return empty;
}
float bmin[3], bmax[3];
if (!gen->get_bounds(bmin, bmax)) {
UtilityFunctions::printerr("DCTerrainUtils: generator has no bounds, use mesh_from_generator() with explicit origin/size");
Ref<ArrayMesh> empty;
empty.instantiate();
return empty;
}
// Add 10% padding
float extent[3] = { bmax[0] - bmin[0], bmax[1] - bmin[1], bmax[2] - bmin[2] };
float max_extent = std::max({extent[0], extent[1], extent[2]});
float pad = max_extent * 0.1f;
Vector3 origin(bmin[0] - pad, bmin[1] - pad, bmin[2] - pad);
int raw_size = (int)std::ceil((max_extent + 2 * pad) / voxel_size);
// Round up to next power of 2 — octree requires power-of-2 grid size
int grid_size = 1;
while (grid_size < raw_size) grid_size <<= 1;
grid_size = std::min(grid_size, 256); // cap at 256 to avoid OOM
UtilityFunctions::print("DCTerrainUtils: auto bounds, grid_size=", grid_size,
" voxel_size=", voxel_size);
return mesh_from_generator(generator, origin, voxel_size, grid_size);
}
} // namespace godot