Convert instancer MMI nodes to RS

This commit is contained in:
aidandavey 2025-10-05 21:30:16 +01:00
parent 8cade44ba9
commit 4bdb15c476
4 changed files with 93 additions and 130 deletions

View File

@ -1,7 +1,7 @@
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors. // Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
#include <godot_cpp/classes/resource_saver.hpp> #include <godot_cpp/classes/resource_saver.hpp>
#include <godot_cpp/classes/world3d.hpp>
#include "constants.h" #include "constants.h"
#include "logger.h" #include "logger.h"
#include "terrain_3d_instancer.h" #include "terrain_3d_instancer.h"
@ -111,15 +111,8 @@ void Terrain3DInstancer::_update_mmi_by_region(const Terrain3DRegion *p_region,
Vector2i region_loc = p_region->get_location(); Vector2i region_loc = p_region->get_location();
Dictionary mesh_inst_dict = p_region->get_instances(); Dictionary mesh_inst_dict = p_region->get_instances();
// Create MMI container if needed (always, per region) // Get or create mesh dict
String rname("Region" + Util::location_to_string(region_loc)); MeshMMIDict &mesh_mmi_dict = _mmi_nodes[region_loc];
if (_mmi_containers.count(region_loc) == 0) {
LOG(DEBUG, "Creating new region MMI container Terrain3D/MMI/", rname);
Node3D *node = memnew(Node3D);
node->set_name(rname);
_mmi_containers[region_loc] = node;
_terrain->get_mmi_parent()->add_child(node, true);
}
// Verify mesh id is valid, enabled, and has MeshInstance3Ds // Verify mesh id is valid, enabled, and has MeshInstance3Ds
Ref<Terrain3DMeshAsset> ma = _terrain->get_assets()->get_mesh_asset(p_mesh_id); Ref<Terrain3DMeshAsset> ma = _terrain->get_assets()->get_mesh_asset(p_mesh_id);
@ -142,6 +135,7 @@ void Terrain3DInstancer::_update_mmi_by_region(const Terrain3DRegion *p_region,
// Process cells // Process cells
Dictionary cell_inst_dict = mesh_inst_dict[p_mesh_id]; Dictionary cell_inst_dict = mesh_inst_dict[p_mesh_id];
Array cell_locations = cell_inst_dict.keys(); Array cell_locations = cell_inst_dict.keys();
RID shadow_impostor_source_mm;
for (int c = 0; c < cell_locations.size(); c++) { for (int c = 0; c < cell_locations.size(); c++) {
Vector2i cell = cell_locations[c]; Vector2i cell = cell_locations[c];
@ -179,8 +173,7 @@ void Terrain3DInstancer::_update_mmi_by_region(const Terrain3DRegion *p_region,
// Get or create mesh dict (defined here as cleanup above might invalidate it) // Get or create mesh dict (defined here as cleanup above might invalidate it)
MeshMMIDict &mesh_mmi_dict = _mmi_nodes[region_loc]; MeshMMIDict &mesh_mmi_dict = _mmi_nodes[region_loc];
Ref<MultiMesh> shadow_impostor_source_mm;
for (int lod = ma->get_last_lod(); lod >= Terrain3DMeshAsset::SHADOW_LOD_ID; lod--) { for (int lod = ma->get_last_lod(); lod >= Terrain3DMeshAsset::SHADOW_LOD_ID; lod--) {
// Don't create shadow MMI if not needed // Don't create shadow MMI if not needed
if (lod == Terrain3DMeshAsset::SHADOW_LOD_ID && shadow_lod_disabled) { if (lod == Terrain3DMeshAsset::SHADOW_LOD_ID && shadow_lod_disabled) {
@ -195,38 +188,23 @@ void Terrain3DInstancer::_update_mmi_by_region(const Terrain3DRegion *p_region,
// Get or create MMI - [] creates key if missing // Get or create MMI - [] creates key if missing
Vector2i mesh_key(p_mesh_id, lod); Vector2i mesh_key(p_mesh_id, lod);
CellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key]; CellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key];
MultiMeshInstance3D *mmi = cell_mmi_dict[cell]; // null if missing RID &mmi = cell_mmi_dict[cell].first; // null if missing
if (!mmi) { if (!mmi.is_valid()) {
mmi = memnew(MultiMeshInstance3D); mmi = RS->instance_create();
LOG(EXTREME, "No MMI found, Created new MultiMeshInstance3D for cell ", cell, ": ", ptr_to_str(mmi)); RS->instance_set_scenario(mmi, _terrain->get_world_3d()->get_scenario());
// Node name is MMI3D_Cell##_##_Mesh#_LOD#
String cstring = "_C" + Util::location_to_string(cell).trim_prefix("_");
String mstring = "_M" + String::num_int64(p_mesh_id);
String lstring = "_L" + ((lod == Terrain3DMeshAsset::SHADOW_LOD_ID) ? "S" : String::num_int64(lod));
mmi->set_name("MMI3D" + cstring + mstring + lstring);
cell_mmi_dict[cell] = mmi;
//Attach to tree
Node *node_container = _terrain->get_mmi_parent()->get_node_internal(rname);
if (!node_container) {
LOG(ERROR, rname, " isn't attached to the tree.");
memdelete(mmi);
cell_mmi_dict.erase(cell);
continue;
}
node_container->add_child(mmi, true);
modified = true; // New MMI needs full update modified = true; // New MMI needs full update
} }
// Always update MMI propertiess // Always update MMI propertiess
if (ma->is_highlighted()) { if (ma->is_highlighted()) {
mmi->set_material_override(ma->get_highlight_material()); RS->instance_geometry_set_material_override(mmi, ma->get_highlight_material().is_valid() ? ma->get_highlight_material()->get_rid() : RID());
mmi->set_material_overlay(Ref<Material>()); RS->instance_geometry_set_material_overlay(mmi, RID());
} else { } else {
mmi->set_material_override(ma->get_material_override()); RS->instance_geometry_set_material_override(mmi, ma->get_material_override().is_valid() ? ma->get_material_override()->get_rid() : RID());
mmi->set_material_overlay(ma->get_material_overlay()); RS->instance_geometry_set_material_overlay(mmi, ma->get_material_overlay().is_valid() ? ma->get_material_overlay()->get_rid() : RID());
} }
mmi->set_cast_shadows_setting(ma->get_lod_cast_shadows(lod)); RS->instance_geometry_set_cast_shadows_setting(mmi, ma->get_lod_cast_shadows(lod));
RS->instance_set_layer_mask(mmi, ma->get_visibility_layers());
_set_mmi_lod_ranges(mmi, ma, lod); _set_mmi_lod_ranges(mmi, ma, lod);
// Reposition MMI to region location // Reposition MMI to region location
@ -235,40 +213,37 @@ void Terrain3DInstancer::_update_mmi_by_region(const Terrain3DRegion *p_region,
real_t vertex_spacing = _terrain->get_vertex_spacing(); real_t vertex_spacing = _terrain->get_vertex_spacing();
t.origin.x += region_loc.x * region_size * vertex_spacing; t.origin.x += region_loc.x * region_size * vertex_spacing;
t.origin.z += region_loc.y * region_size * vertex_spacing; t.origin.z += region_loc.y * region_size * vertex_spacing;
mmi->set_as_top_level(true);
mmi->set_global_transform(t);
RS->instance_set_transform(mmi, t);
RID &mm = cell_mmi_dict[cell].second;
// Only recreate MultiMesh if modified/no existing (with override for shadow) // Only recreate MultiMesh if modified/no existing (with override for shadow)
// Always update shadow MMI (source may have changed) // Always update shadow MMI (source may have changed)
if (modified || mmi->get_multimesh().is_null() || if (modified || !mm.is_valid() ||
lod == Terrain3DMeshAsset::SHADOW_LOD_ID) { lod == Terrain3DMeshAsset::SHADOW_LOD_ID) {
// Subtract previous instance count of this MMI // Subtract previous instance count for this cell
if (lod == 0) { if (mm.is_valid() && lod == ma->get_last_lod()) {
if (mmi->get_multimesh().is_valid()) { ma->update_instance_count(-RS->multimesh_get_instance_count(mm));
ma->update_instance_count(-mmi->get_multimesh()->get_instance_count());
}
} }
Ref<MultiMesh> mm;
if (lod == Terrain3DMeshAsset::SHADOW_LOD_ID) { if (lod == Terrain3DMeshAsset::SHADOW_LOD_ID) {
// Reuse impostor LOD MM as shadow impostor // Reuse impostor LOD MM as shadow impostor
mm = shadow_impostor_source_mm; mm = shadow_impostor_source_mm;
if (mm.is_null()) { if (!mm.is_valid()) {
LOG(ERROR, "Shadow MM is null for cell ", cell, " lod ", lod, " xforms: ", xforms.size()); LOG(ERROR, "Shadow MM is null for cell ", cell, " lod ", lod, " xforms: ", xforms.size());
continue; continue;
} }
} else { } else {
mm = _create_multimesh(p_mesh_id, lod, xforms, colors); mm = _create_multimesh(p_mesh_id, lod, xforms, colors);
} }
if (mm.is_null()) { if (!mm.is_valid()) {
LOG(ERROR, "Null MM for cell ", cell, " lod ", lod, " xforms: ", xforms.size()); LOG(ERROR, "Null MM for cell ", cell, " lod ", lod, " xforms: ", xforms.size());
continue; continue;
} }
mmi->set_multimesh(mm); RS->instance_set_base(mmi, mm);
// Add current instance count of this new MMI // Add current instance count for this cell
if (lod == 0) { if (lod == 0) {
ma->update_instance_count(mm->get_instance_count()); ma->update_instance_count(RS->multimesh_get_instance_count(mm));
} }
// Clear modified only for visible LODs // Clear modified only for visible LODs
@ -277,40 +252,43 @@ void Terrain3DInstancer::_update_mmi_by_region(const Terrain3DRegion *p_region,
} }
} else { } else {
// Needed to update generated mesh changes // Needed to update generated mesh changes
mmi->get_multimesh()->set_mesh(ma->get_mesh(lod)); RS->multimesh_set_mesh(mm, ma->get_mesh(lod)->get_rid());
} }
// Capture source MM from shadow impostor LOD // Capture source MM from shadow impostor LOD
if (lod == ma->get_shadow_impostor()) { if (lod == ma->get_shadow_impostor()) {
shadow_impostor_source_mm = mmi->get_multimesh(); shadow_impostor_source_mm = RS->multimesh_get_mesh(mm);
} }
} // End for LOD loop } // End for LOD loop
// Set all LOD mmi AABB to match LOD0 to ensure no gaps between transitions. // Set all LOD mmi AABB to match LOD0 to ensure no gaps between transitions.
AABB mmi_custom_aabb; AABB mm_custom_aabb;
for (int lod = 0; lod <= ma->get_last_lod(); lod++) { for (int lod = 0; lod <= ma->get_last_lod(); lod++) {
Vector2i mesh_key(p_mesh_id, lod); Vector2i mesh_key(p_mesh_id, lod);
CellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key]; CellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key];
MultiMeshInstance3D *mmi = cell_mmi_dict[cell]; RID &mmi = cell_mmi_dict[cell].first;
if (mmi) { RID &mm = cell_mmi_dict[cell].second;
if (mm.is_valid()) {
if (lod == 0) { if (lod == 0) {
mmi_custom_aabb = mmi->get_aabb(); mm_custom_aabb = RS->multimesh_get_aabb(mm);
} else { } else {
mmi->set_custom_aabb(mmi_custom_aabb); RS->multimesh_set_custom_aabb(mm, mm_custom_aabb);
} }
RS->instance_set_custom_aabb(mmi, mm_custom_aabb);
} }
} }
if (ma->get_shadow_impostor() > 0) { if (ma->get_shadow_impostor() > 0) {
Vector2i mesh_key(p_mesh_id, Terrain3DMeshAsset::SHADOW_LOD_ID); Vector2i mesh_key(p_mesh_id, Terrain3DMeshAsset::SHADOW_LOD_ID);
CellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key]; CellMMIDict &cell_mmi_dict = mesh_mmi_dict[mesh_key];
MultiMeshInstance3D *mmi = cell_mmi_dict[cell]; //RID &mmi = cell_mmi_dict[cell].first;
if (mmi) { RID &mm = cell_mmi_dict[cell].second;
mmi->set_custom_aabb(mmi_custom_aabb); if (mm.is_valid()) {
RS->multimesh_set_custom_aabb(mm, mm_custom_aabb);
} }
} }
} }
} }
void Terrain3DInstancer::_set_mmi_lod_ranges(MultiMeshInstance3D *p_mmi, const Ref<Terrain3DMeshAsset> &p_ma, const int p_lod) { void Terrain3DInstancer::_set_mmi_lod_ranges(RID p_mmi, const Ref<Terrain3DMeshAsset> &p_ma, const int p_lod) {
if (!p_mmi || p_ma.is_null()) { if (!p_mmi || p_ma.is_null()) {
return; return;
} }
@ -326,17 +304,12 @@ void Terrain3DInstancer::_set_mmi_lod_ranges(MultiMeshInstance3D *p_mmi, const R
if (margin > 0.f) { if (margin > 0.f) {
lod_begin = MAX(lod_begin < 0.001f ? 0.f : lod_begin - margin, 0.f); lod_begin = MAX(lod_begin < 0.001f ? 0.f : lod_begin - margin, 0.f);
lod_end = MAX(lod_end < 0.001f ? 0.f : lod_end + margin, 0.f); lod_end = MAX(lod_end < 0.001f ? 0.f : lod_end + margin, 0.f);
p_mmi->set_visibility_range_begin_margin(lod_begin < 0.001f ? 0.f : margin); real_t begin_margin = lod_begin < 0.001f ? 0.f : margin;
p_mmi->set_visibility_range_end_margin(lod_end < 0.001f ? 0.f : margin); real_t end_margin = lod_end < 0.001f ? 0.f : margin;
p_mmi->set_visibility_range_fade_mode(GeometryInstance3D::VISIBILITY_RANGE_FADE_SELF); RS->instance_geometry_set_visibility_range(p_mmi, lod_begin, lod_end, begin_margin, end_margin, RenderingServer::VISIBILITY_RANGE_FADE_SELF);
} else { } else {
// Fade mode unset (Godot default) RS->instance_geometry_set_visibility_range(p_mmi, lod_begin, lod_end, 0.f, 0.f, RenderingServer::VISIBILITY_RANGE_FADE_DISABLED);
p_mmi->set_visibility_range_begin_margin(0.f);
p_mmi->set_visibility_range_end_margin(0.f);
p_mmi->set_visibility_range_fade_mode(GeometryInstance3D::VISIBILITY_RANGE_FADE_DISABLED);
} }
p_mmi->set_visibility_range_begin(lod_begin);
p_mmi->set_visibility_range_end(lod_end);
} }
void Terrain3DInstancer::_update_vertex_spacing(const real_t p_vertex_spacing) { void Terrain3DInstancer::_update_vertex_spacing(const real_t p_vertex_spacing) {
@ -441,37 +414,25 @@ void Terrain3DInstancer::_destroy_mmi_by_cell(const Vector2i &p_region_loc, cons
if (cell_mmi_dict.count(p_cell) == 0) { if (cell_mmi_dict.count(p_cell) == 0) {
continue; continue;
} }
MultiMeshInstance3D *mmi = cell_mmi_dict[p_cell];
if (ma.is_valid() && mmi && mmi->get_multimesh().is_valid()) { RID &mmi = cell_mmi_dict[p_cell].first;
if (lod == 0) { RID &mm = cell_mmi_dict[p_cell].second;
ma->update_instance_count(-mmi->get_multimesh()->get_instance_count()); if (ma.is_valid() && mm.is_valid()) {
if (lod == ma->get_last_lod()) {
ma->update_instance_count(-RS->multimesh_get_instance_count(mm));
} }
} }
LOG(EXTREME, "Freeing ", ptr_to_str(mmi), " and erasing mmi cell ", p_cell);
remove_from_tree(mmi); LOG(EXTREME, "Freeing ", mmi, " and erasing mmi cell ", p_cell);
memdelete_safely(mmi); RS->free_rid(mmi);
RS->free_rid(mm);
cell_mmi_dict.erase(p_cell); cell_mmi_dict.erase(p_cell);
if (cell_mmi_dict.empty()) { if (cell_mmi_dict.empty()) {
LOG(EXTREME, "Removing mesh ", mesh_key, " from cell MMI dictionary"); LOG(EXTREME, "Removing mesh ", mesh_key, " from cell MMI dictionary");
mesh_mmi_dict.erase(mesh_key); // invalidates cell_mmi_dict mesh_mmi_dict.erase(mesh_key); // invalidates cell_mmi_dict
} }
} }
// Clean up region if we've removed the last MMI and cell
if (mesh_mmi_dict.empty()) {
LOG(EXTREME, "Removing region ", p_region_loc, " from mesh MMI dictionary");
if (_mmi_containers.count(p_region_loc) > 0) {
Node *node = _mmi_containers[p_region_loc];
if (node && node->get_child_count() == 0) {
LOG(EXTREME, "Removing ", node->get_name());
_mmi_containers.erase(p_region_loc);
remove_from_tree(node);
memdelete_safely(node);
}
}
// This invalidates mesh_mmi_dict here and for calling functions
_mmi_nodes.erase(p_region_loc);
}
} }
void Terrain3DInstancer::_backup_region(const Ref<Terrain3DRegion> &p_region) { void Terrain3DInstancer::_backup_region(const Ref<Terrain3DRegion> &p_region) {
@ -485,8 +446,8 @@ void Terrain3DInstancer::_backup_region(const Ref<Terrain3DRegion> &p_region) {
} }
} }
Ref<MultiMesh> Terrain3DInstancer::_create_multimesh(const int p_mesh_id, const int p_lod, const TypedArray<Transform3D> &p_xforms, const PackedColorArray &p_colors) const { RID Terrain3DInstancer::_create_multimesh(const int p_mesh_id, const int p_lod, const TypedArray<Transform3D> &p_xforms, const PackedColorArray &p_colors) const {
Ref<MultiMesh> mm; RID mm;
IS_INIT(mm); IS_INIT(mm);
Ref<Terrain3DMeshAsset> mesh_asset = _terrain->get_assets()->get_mesh_asset(p_mesh_id); Ref<Terrain3DMeshAsset> mesh_asset = _terrain->get_assets()->get_mesh_asset(p_mesh_id);
if (mesh_asset.is_null()) { if (mesh_asset.is_null()) {
@ -498,17 +459,13 @@ Ref<MultiMesh> Terrain3DInstancer::_create_multimesh(const int p_mesh_id, const
LOG(ERROR, "No LOD ", p_lod, " for mesh id ", p_mesh_id, " found. Max: ", mesh_asset->get_lod_count()); LOG(ERROR, "No LOD ", p_lod, " for mesh id ", p_mesh_id, " found. Max: ", mesh_asset->get_lod_count());
return mm; return mm;
} }
mm.instantiate(); mm = RS->multimesh_create();
mm->set_transform_format(MultiMesh::TRANSFORM_3D); RS->multimesh_allocate_data(mm, p_xforms.size(), RenderingServer::MULTIMESH_TRANSFORM_3D, true, false, false);
mm->set_use_colors(true); RS->multimesh_set_mesh(mm, mesh->get_rid());
mm->set_mesh(mesh); for (int i = 0; i < p_xforms.size(); i++) {
if (p_xforms.size() > 0) { RS->multimesh_instance_set_transform(mm, i, p_xforms[i]);
mm->set_instance_count(p_xforms.size()); if (i < p_colors.size()) {
for (int i = 0; i < p_xforms.size(); i++) { RS->multimesh_instance_set_color(mm, i, p_colors[i]);
mm->set_instance_transform(i, p_xforms[i]);
if (i < p_colors.size()) {
mm->set_instance_color(i, p_colors[i]);
}
} }
} }
return mm; return mm;
@ -1337,20 +1294,16 @@ void Terrain3DInstancer::update_mmis(const int p_mesh_id, const Vector2i &p_regi
} }
void Terrain3DInstancer::dump_mmis() { void Terrain3DInstancer::dump_mmis() {
LOG(WARN, "Dumping MMI tree and node containers"); LOG(WARN, "Dumping MMI tree");
LOG(MESG, "_mmi_containers size: ", int(_mmi_containers.size()));
for (auto &it : _mmi_containers) {
LOG(MESG, "_mmi_containers region: ", it.first, ", node ptr: ", ptr_to_str(it.second));
}
LOG(MESG, "_mmi tree: "); LOG(MESG, "_mmi tree: ");
_terrain->get_mmi_parent()->print_tree(); _terrain->get_mmi_parent()->print_tree();
LOG(MESG, "_mmi_nodes size: ", int(_mmi_nodes.size())); LOG(MESG, "_mmi_nodes size: ", int(_mmi_nodes.size()));
for (auto &i : _mmi_nodes) { for (auto &i : _mmi_nodes) {
LOG(MESG, "_mmi_nodes region: ", i.first, ", dict ptr: ", ptr_to_str(&i.second)); //LOG(MESG, "_mmi_nodes region: ", i.first, ", dict ptr: ", ptr_to_str(&i.second));
for (auto &j : i.second) { for (auto &j : i.second) {
LOG(MESG, "mesh_mmi_dict mesh: ", j.first, ", dict ptr: ", ptr_to_str(&j.second)); //LOG(MESG, "mesh_mmi_dict mesh: ", j.first, ", dict ptr: ", &j.second.first);
for (auto &k : j.second) { for (auto &k : j.second) {
LOG(MESG, "cell_mmi_dict cell: ", k.first, ", mmi ptr: ", ptr_to_str(k.second)); //LOG(MESG, "cell_mmi_dict cell: ", k.first, ", mmi ptr: ", (k.second));
} }
} }
} }

View File

@ -28,16 +28,12 @@ private:
// MM Resources stored in Terrain3DRegion::_instances as // MM Resources stored in Terrain3DRegion::_instances as
// Region::_instances{mesh_id:int} -> cell{v2i} -> [ TypedArray<Transform3D>, PackedColorArray, modified:bool ] // Region::_instances{mesh_id:int} -> cell{v2i} -> [ TypedArray<Transform3D>, PackedColorArray, modified:bool ]
// MMI Objects attached to tree, freed in destructor, stored as // MMI Objects, freed in destructor, stored as
// _mmi_nodes{region_loc} -> mesh{v2i(mesh_id,lod)} -> cell{v2i} -> MultiMeshInstance3D // _mmi_nodes{region_loc} -> mesh{v2i(mesh_id,lod)} -> cell{v2i} -> RID
using CellMMIDict = std::unordered_map<Vector2i, MultiMeshInstance3D *, Vector2iHash>; using CellMMIDict = std::unordered_map<Vector2i, std::pair<RID, RID>, Vector2iHash>;
using MeshMMIDict = std::unordered_map<Vector2i, CellMMIDict, Vector2iHash>; using MeshMMIDict = std::unordered_map<Vector2i, CellMMIDict, Vector2iHash>;
std::unordered_map<Vector2i, MeshMMIDict, Vector2iHash> _mmi_nodes; std::unordered_map<Vector2i, MeshMMIDict, Vector2iHash> _mmi_nodes;
// Region MMI containers named Terrain3D/MMI/Region* are stored here as
// _mmi_containers{region_loc} -> Node3D
std::unordered_map<Vector2i, Node3D *, Vector2iHash> _mmi_containers;
// MMI Updates tracked in a unique Set of <region_location, mesh_id> // MMI Updates tracked in a unique Set of <region_location, mesh_id>
// <V2I_MAX, -2> means destroy first, then update everything // <V2I_MAX, -2> means destroy first, then update everything
// <V2I_MAX, -1> means update everything // <V2I_MAX, -1> means update everything
@ -51,13 +47,13 @@ private:
uint32_t _get_density_count(const real_t p_density); uint32_t _get_density_count(const real_t p_density);
void _process_updates(); void _process_updates();
void _update_mmi_by_region(const Terrain3DRegion *p_region, const int p_mesh_id); void _update_mmi_by_region(const Terrain3DRegion *p_region, const int p_mesh_id);
void _set_mmi_lod_ranges(MultiMeshInstance3D *p_mmi, const Ref<Terrain3DMeshAsset> &p_ma, const int p_lod); void _set_mmi_lod_ranges(RID p_mmi, const Ref<Terrain3DMeshAsset> &p_ma, const int p_lod);
void _update_vertex_spacing(const real_t p_vertex_spacing); void _update_vertex_spacing(const real_t p_vertex_spacing);
void _destroy_mmi_by_mesh(const int p_mesh_id); void _destroy_mmi_by_mesh(const int p_mesh_id);
void _destroy_mmi_by_location(const Vector2i &p_region_loc, const int p_mesh_id); void _destroy_mmi_by_location(const Vector2i &p_region_loc, const int p_mesh_id);
void _destroy_mmi_by_cell(const Vector2i &p_region_loc, const int p_mesh_id, const Vector2i p_cell, const int p_lod = INT32_MAX); void _destroy_mmi_by_cell(const Vector2i &p_region_loc, const int p_mesh_id, const Vector2i p_cell, const int p_lod = INT32_MAX);
void _backup_region(const Ref<Terrain3DRegion> &p_region); void _backup_region(const Ref<Terrain3DRegion> &p_region);
Ref<MultiMesh> _create_multimesh(const int p_mesh_id, const int p_lod, const TypedArray<Transform3D> &p_xforms = TypedArray<Transform3D>(), const PackedColorArray &p_colors = PackedColorArray()) const; RID _create_multimesh(const int p_mesh_id, const int p_lod, const TypedArray<Transform3D> &p_xforms = TypedArray<Transform3D>(), const PackedColorArray &p_colors = PackedColorArray()) const;
Vector2i _get_cell(const Vector3 &p_global_position, const int p_region_size) const; Vector2i _get_cell(const Vector3 &p_global_position, const int p_region_size) const;
Array _get_usable_height(const Vector3 &p_global_position, const Vector2 &p_slope_range, const bool p_on_collision, const real_t p_raycast_start) const; Array _get_usable_height(const Vector3 &p_global_position, const Vector2 &p_slope_range, const bool p_on_collision, const real_t p_raycast_start) const;

View File

@ -132,6 +132,7 @@ void Terrain3DMeshAsset::clear() {
_height_offset = 0.f; _height_offset = 0.f;
_density = 10.f; _density = 10.f;
_cast_shadows = SHADOWS_ON; _cast_shadows = SHADOWS_ON;
_visibility_layers = 1;
_material_override.unref(); _material_override.unref();
_material_overlay.unref(); _material_overlay.unref();
_last_lod = MAX_LOD_COUNT - 1; _last_lod = MAX_LOD_COUNT - 1;
@ -377,6 +378,13 @@ ShadowCasting Terrain3DMeshAsset::get_lod_cast_shadows(const int p_lod_id) const
return _cast_shadows; return _cast_shadows;
} }
inline void Terrain3DMeshAsset::set_visibility_layers(const int p_layers) {
LOG(INFO, _name, ": Setting visibility layers: ", p_layers);
_visibility_layers = p_layers;
LOG(DEBUG, "Emitting instancer_setting_changed, ID: ", _id);
emit_signal("instancer_setting_changed", _id);
}
void Terrain3DMeshAsset::set_material_override(const Ref<Material> &p_material) { void Terrain3DMeshAsset::set_material_override(const Ref<Material> &p_material) {
SET_IF_DIFF(_material_override, p_material); SET_IF_DIFF(_material_override, p_material);
LOG(INFO, _name, ": Setting material override: ", p_material); LOG(INFO, _name, ": Setting material override: ", p_material);
@ -562,6 +570,8 @@ void Terrain3DMeshAsset::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_density"), &Terrain3DMeshAsset::get_density); ClassDB::bind_method(D_METHOD("get_density"), &Terrain3DMeshAsset::get_density);
ClassDB::bind_method(D_METHOD("set_cast_shadows", "mode"), &Terrain3DMeshAsset::set_cast_shadows); ClassDB::bind_method(D_METHOD("set_cast_shadows", "mode"), &Terrain3DMeshAsset::set_cast_shadows);
ClassDB::bind_method(D_METHOD("get_cast_shadows"), &Terrain3DMeshAsset::get_cast_shadows); ClassDB::bind_method(D_METHOD("get_cast_shadows"), &Terrain3DMeshAsset::get_cast_shadows);
ClassDB::bind_method(D_METHOD("set_visibility_layers", "layers"), &Terrain3DMeshAsset::set_visibility_layers);
ClassDB::bind_method(D_METHOD("get_visibility_layers"), &Terrain3DMeshAsset::get_visibility_layers);
ClassDB::bind_method(D_METHOD("set_material_override", "material"), &Terrain3DMeshAsset::set_material_override); ClassDB::bind_method(D_METHOD("set_material_override", "material"), &Terrain3DMeshAsset::set_material_override);
ClassDB::bind_method(D_METHOD("get_material_override"), &Terrain3DMeshAsset::get_material_override); ClassDB::bind_method(D_METHOD("get_material_override"), &Terrain3DMeshAsset::get_material_override);
ClassDB::bind_method(D_METHOD("set_material_overlay", "material"), &Terrain3DMeshAsset::set_material_overlay); ClassDB::bind_method(D_METHOD("set_material_overlay", "material"), &Terrain3DMeshAsset::set_material_overlay);
@ -615,6 +625,7 @@ void Terrain3DMeshAsset::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height_offset", PROPERTY_HINT_RANGE, "-20.0,20.0,.005"), "set_height_offset", "get_height_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height_offset", PROPERTY_HINT_RANGE, "-20.0,20.0,.005"), "set_height_offset", "get_height_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "density", PROPERTY_HINT_RANGE, ".01,10.0,.005"), "set_density", "get_density"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "density", PROPERTY_HINT_RANGE, ".01,10.0,.005"), "set_density", "get_density");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadows", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows", "get_cast_shadows"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadows", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows", "get_cast_shadows");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_layers", PROPERTY_HINT_LAYERS_3D_RENDER), "set_visibility_layers", "get_visibility_layers");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material_override", "get_material_override"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material_override", "get_material_override");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_overlay", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material_overlay", "get_material_overlay"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_overlay", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material_overlay", "get_material_overlay");

View File

@ -4,7 +4,7 @@
#define TERRAIN3D_MESH_ASSET_CLASS_H #define TERRAIN3D_MESH_ASSET_CLASS_H
#include <godot_cpp/classes/array_mesh.hpp> #include <godot_cpp/classes/array_mesh.hpp>
#include <godot_cpp/classes/geometry_instance3d.hpp> //#include <godot_cpp/classes/geometry_instance3d.hpp>
#include <godot_cpp/classes/material.hpp> #include <godot_cpp/classes/material.hpp>
#include <godot_cpp/classes/packed_scene.hpp> #include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/resource.hpp> #include <godot_cpp/classes/resource.hpp>
@ -13,10 +13,10 @@
#include "constants.h" #include "constants.h"
#include "terrain_3d_asset_resource.h" #include "terrain_3d_asset_resource.h"
using ShadowCasting = GeometryInstance3D::ShadowCastingSetting; using ShadowCasting = RenderingServer::ShadowCastingSetting;
constexpr ShadowCasting SHADOWS_ON = GeometryInstance3D::SHADOW_CASTING_SETTING_ON; constexpr ShadowCasting SHADOWS_ON = RenderingServer::SHADOW_CASTING_SETTING_ON;
constexpr ShadowCasting SHADOWS_OFF = GeometryInstance3D::SHADOW_CASTING_SETTING_OFF; constexpr ShadowCasting SHADOWS_OFF = RenderingServer::SHADOW_CASTING_SETTING_OFF;
constexpr ShadowCasting SHADOWS_ONLY = GeometryInstance3D::SHADOW_CASTING_SETTING_SHADOWS_ONLY; constexpr ShadowCasting SHADOWS_ONLY = RenderingServer::SHADOW_CASTING_SETTING_SHADOWS_ONLY;
class Terrain3DMeshAsset : public Terrain3DAssetResource { class Terrain3DMeshAsset : public Terrain3DAssetResource {
GDCLASS(Terrain3DMeshAsset, Terrain3DAssetResource); GDCLASS(Terrain3DMeshAsset, Terrain3DAssetResource);
@ -42,6 +42,7 @@ private:
real_t _height_offset = 0.f; real_t _height_offset = 0.f;
real_t _density = 10.f; real_t _density = 10.f;
ShadowCasting _cast_shadows = SHADOWS_ON; ShadowCasting _cast_shadows = SHADOWS_ON;
int _visibility_layers = 1;
Ref<Material> _material_override; Ref<Material> _material_override;
Ref<Material> _material_overlay; Ref<Material> _material_overlay;
int _last_lod = MAX_LOD_COUNT - 1; int _last_lod = MAX_LOD_COUNT - 1;
@ -96,6 +97,8 @@ public:
void set_cast_shadows(const ShadowCasting p_cast_shadows); void set_cast_shadows(const ShadowCasting p_cast_shadows);
ShadowCasting get_cast_shadows() const { return _cast_shadows; }; ShadowCasting get_cast_shadows() const { return _cast_shadows; };
ShadowCasting get_lod_cast_shadows(const int p_lod_id) const; ShadowCasting get_lod_cast_shadows(const int p_lod_id) const;
void set_visibility_layers(const int p_layers);
int get_visibility_layers() const { return _visibility_layers; }
void set_material_override(const Ref<Material> &p_material); void set_material_override(const Ref<Material> &p_material);
Ref<Material> get_material_override() const { return _material_override; } Ref<Material> get_material_override() const { return _material_override; }
void set_material_overlay(const Ref<Material> &p_material); void set_material_overlay(const Ref<Material> &p_material);