/*************************************************************************/ /* terrain_chunk_default.cpp */ /*************************************************************************/ /* This file is part of: */ /* PANDEMONIUM ENGINE */ /* https://github.com/Relintai/pandemonium_engine */ /*************************************************************************/ /* Copyright (c) 2022-present Péter Magyar. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "terrain_chunk_default.h" #include "../../defines.h" #include "servers/physics_server.h" #include "servers/rendering_server.h" #include "../../../opensimplex/open_simplex_noise.h" #include "../../meshers/default/terrain_mesher_default.h" #include "../terrain_world.h" #include "../jobs/terrain_job.h" #include "terrain_world_default.h" #include "../jobs/terrain_light_job.h" #include "../jobs/terrain_prop_job.h" #include "../jobs/terrain_terrain_job.h" #include "scene/resources/world_3d.h" const String TerrainChunkDefault::BINDING_STRING_BUILD_FLAGS = "Use Isolevel,Use Lighting,Use AO,Use RAO,Generate AO,Generate RAO,Bake Lights,Create Collider,Create Lods"; _FORCE_INLINE_ int TerrainChunkDefault::get_build_flags() const { return _build_flags; } _FORCE_INLINE_ void TerrainChunkDefault::set_build_flags(const int flags) { _build_flags = flags; } bool TerrainChunkDefault::get_lights_dirty() const { return _lights_dirty; } void TerrainChunkDefault::set_lights_dirty(const bool value) { _lights_dirty = value; } int TerrainChunkDefault::get_lod_num() const { return _lod_num; } void TerrainChunkDefault::set_lod_num(const int value) { _lod_num = value; } int TerrainChunkDefault::get_current_lod_level() const { return _current_lod_level; } void TerrainChunkDefault::set_current_lod_level(const int value) { _current_lod_level = value; if ((_build_flags & BUILD_FLAG_CREATE_LODS) == 0) return; if (_current_lod_level < 0) _current_lod_level = 0; int lod_num = mesh_rid_get_count(MESH_INDEX_TERRAIN, MESH_TYPE_INDEX_MESH_INSTANCE); if (_current_lod_level > lod_num) _current_lod_level = lod_num; for (int i = 0; i < lod_num; ++i) { bool vis = false; if (i == _current_lod_level) vis = true; RID rid = mesh_rid_get_index(MESH_INDEX_TERRAIN, MESH_TYPE_INDEX_MESH_INSTANCE, i); if (rid != RID()) RenderingServer::get_singleton()->instance_set_visible(rid, vis); rid = mesh_rid_get_index(MESH_INDEX_PROP, MESH_TYPE_INDEX_MESH_INSTANCE, i); if (rid != RID()) RenderingServer::get_singleton()->instance_set_visible(rid, vis); } } void TerrainChunkDefault::emit_build_finished() { emit_signal("mesh_generation_finished", this); if (_voxel_world != NULL) { _voxel_world->on_chunk_mesh_generation_finished(this); } } //Meshes Dictionary TerrainChunkDefault::mesh_rids_get() { return _rids; } void TerrainChunkDefault::mesh_rids_set(const Dictionary &rids) { _rids = rids; } RID TerrainChunkDefault::mesh_rid_get(const int mesh_index, const int mesh_type_index) { if (!_rids.has(mesh_index)) return RID(); Dictionary m = _rids[mesh_index]; if (!m.has(mesh_type_index)) return RID(); Variant v = m[mesh_type_index]; if (v.get_type() != Variant::RID) return RID(); return v; } void TerrainChunkDefault::mesh_rid_set(const int mesh_index, const int mesh_type_index, RID value) { if (!_rids.has(mesh_index)) _rids[mesh_index] = Dictionary(); Dictionary m = _rids[mesh_index]; if (!m.has(mesh_type_index)) { m[mesh_type_index] = value; _rids[mesh_index] = m; return; } Variant v = m[mesh_type_index]; ERR_FAIL_COND(v.get_type() != Variant::RID); m[mesh_type_index] = value; _rids[mesh_index] = m; } RID TerrainChunkDefault::mesh_rid_get_index(const int mesh_index, const int mesh_type_index, const int index) { if (!_rids.has(mesh_index)) return RID(); Dictionary m = _rids[mesh_index]; if (!m.has(mesh_type_index)) return RID(); Variant v = m[mesh_type_index]; if (v.get_type() != Variant::ARRAY) return RID(); Array arr = v; ERR_FAIL_INDEX_V(index, arr.size(), RID()); return arr[index]; } void TerrainChunkDefault::mesh_rid_set_index(const int mesh_index, const int mesh_type_index, const int index, RID value) { if (!_rids.has(mesh_index)) _rids[mesh_index] = Dictionary(); Dictionary m = _rids[mesh_index]; if (!m.has(mesh_type_index)) { Array arr; arr.resize(index + 1); arr[index] = value; m[mesh_type_index] = arr; _rids[mesh_index] = m; return; } Variant v = m[mesh_type_index]; ERR_FAIL_COND(v.get_type() != Variant::ARRAY); Array arr = m[mesh_type_index]; if (arr.size() <= index) arr.resize(index + 1); arr[index] = value; m[mesh_type_index] = arr; _rids[mesh_index] = m; } int TerrainChunkDefault::mesh_rid_get_count(const int mesh_index, const int mesh_type_index) { if (!_rids.has(mesh_index)) return 0; Dictionary m = _rids[mesh_index]; if (!m.has(mesh_type_index)) return 0; Variant v = m[mesh_type_index]; if (v.get_type() != Variant::ARRAY) return 0; Array arr = v; return arr.size(); } void TerrainChunkDefault::mesh_rids_clear(const int mesh_index, const int mesh_type_index) { if (!_rids.has(mesh_index)) return; Dictionary m = _rids[mesh_index]; if (!m.has(mesh_type_index)) return; m.erase(mesh_type_index); } Array TerrainChunkDefault::meshes_get(const int mesh_index, const int mesh_type_index) { if (!_rids.has(mesh_index)) return Array(); Dictionary m = _rids[mesh_index]; if (!m.has(mesh_type_index)) return Array(); Variant v = m[mesh_type_index]; if (v.get_type() != Variant::ARRAY) return Array(); return v; } void TerrainChunkDefault::meshes_set(const int mesh_index, const int mesh_type_index, const Array &meshes) { if (!_rids.has(mesh_index)) _rids[mesh_index] = Dictionary(); Dictionary m = _rids[mesh_index]; m[mesh_type_index] = meshes; _rids[mesh_index] = m; } bool TerrainChunkDefault::meshes_has(const int mesh_index, const int mesh_type_index) { if (!_rids.has(mesh_index)) return false; Dictionary m = _rids[mesh_index]; if (!m.has(mesh_type_index)) return false; return true; } void TerrainChunkDefault::rids_clear() { _rids.clear(); } void TerrainChunkDefault::rids_free() { List keys; _rids.get_key_list(&keys); for (List::Element *E = keys.front(); E; E = E->next()) { Variant v = E->get(); if (v.get_type() != Variant::INT) continue; free_index(v); } } void TerrainChunkDefault::meshes_create(const int mesh_index, const int mesh_count) { ERR_FAIL_COND(_voxel_world == NULL); ERR_FAIL_COND(!get_library().is_valid()); if (!_rids.has(mesh_index)) _rids[mesh_index] = Dictionary(); Dictionary m = _rids[mesh_index]; ERR_FAIL_COND(m.has(MESH_TYPE_INDEX_MESH)); ERR_FAIL_COND(m.has(MESH_TYPE_INDEX_MESH_INSTANCE)); Array am; Array ami; for (int i = 0; i < mesh_count; ++i) { RID mesh_instance_rid = RS::get_singleton()->instance_create(); if (get_voxel_world()->get_world_3d().is_valid()) RS::get_singleton()->instance_set_scenario(mesh_instance_rid, get_voxel_world()->get_world_3d()->get_scenario()); RID mesh_rid = RS::get_singleton()->mesh_create(); RS::get_singleton()->instance_set_base(mesh_instance_rid, mesh_rid); RS::get_singleton()->instance_set_transform(mesh_instance_rid, get_transform()); if (i != 0) RS::get_singleton()->instance_set_visible(mesh_instance_rid, false); am.push_back(mesh_rid); ami.push_back(mesh_instance_rid); } m[MESH_TYPE_INDEX_MESH] = am; m[MESH_TYPE_INDEX_MESH_INSTANCE] = ami; _rids[mesh_index] = m; } void TerrainChunkDefault::meshes_free(const int mesh_index) { if (!_rids.has(mesh_index)) return; Dictionary m = _rids[mesh_index]; RID rid; if (m.has(MESH_TYPE_INDEX_MESH)) { Array a = m[MESH_TYPE_INDEX_MESH]; for (int i = 0; i < a.size(); ++i) { RID r = a[i]; if (r != rid) { RS::get_singleton()->free(r); } } } if (m.has(MESH_TYPE_INDEX_MESH_INSTANCE)) { Array a = m[MESH_TYPE_INDEX_MESH_INSTANCE]; for (int i = 0; i < a.size(); ++i) { RID r = a[i]; if (r != rid) { RS::get_singleton()->free(r); } } } m.erase(MESH_TYPE_INDEX_MESH); m.erase(MESH_TYPE_INDEX_MESH_INSTANCE); } void TerrainChunkDefault::colliders_create(const int mesh_index, const int layer_mask) { ERR_FAIL_COND(_voxel_world == NULL); ERR_FAIL_COND(PhysicsServer::get_singleton()->is_flushing_queries()); //ERR_FAIL_COND(!get_voxel_world()->is_inside_tree()); if (!_rids.has(mesh_index)) _rids[mesh_index] = Dictionary(); Dictionary m = _rids[mesh_index]; ERR_FAIL_COND(m.has(MESH_TYPE_INDEX_BODY)); ERR_FAIL_COND(m.has(MESH_TYPE_INDEX_SHAPE)); RID shape_rid = PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONCAVE_POLYGON); RID body_rid = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC); PhysicsServer::get_singleton()->body_set_collision_layer(body_rid, layer_mask); PhysicsServer::get_singleton()->body_set_collision_mask(body_rid, layer_mask); PhysicsServer::get_singleton()->body_add_shape(body_rid, shape_rid); PhysicsServer::get_singleton()->body_set_state(body_rid, PhysicsServer::BODY_STATE_TRANSFORM, get_transform()); if (get_voxel_world()->is_inside_tree() && get_voxel_world()->is_inside_world()) { Ref world = get_voxel_world()->get_world_3d(); if (world.is_valid() && world->get_space() != RID()) PhysicsServer::get_singleton()->body_set_space(body_rid, world->get_space()); } m[MESH_TYPE_INDEX_BODY] = body_rid; m[MESH_TYPE_INDEX_SHAPE] = shape_rid; _rids[mesh_index] = m; } void TerrainChunkDefault::colliders_create_area(const int mesh_index, const int layer_mask) { ERR_FAIL_COND(_voxel_world == NULL); ERR_FAIL_COND(PhysicsServer::get_singleton()->is_flushing_queries()); if (!_rids.has(mesh_index)) _rids[mesh_index] = Dictionary(); Dictionary m = _rids[mesh_index]; ERR_FAIL_COND(m.has(MESH_TYPE_INDEX_AREA)); ERR_FAIL_COND(m.has(MESH_TYPE_INDEX_SHAPE)); RID shape_rid = PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONCAVE_POLYGON); RID area_rid = PhysicsServer::get_singleton()->area_create(); PhysicsServer::get_singleton()->area_attach_object_instance_id(area_rid, _voxel_world->get_instance_id()); PhysicsServer::get_singleton()->area_set_param(area_rid, PhysicsServer::AREA_PARAM_GRAVITY, 9.8); PhysicsServer::get_singleton()->area_set_param(area_rid, PhysicsServer::AREA_PARAM_GRAVITY_VECTOR, Vector3(0, -1, 0)); //PhysicsServer::get_singleton()->area_set_monitor_callback(area_rid, this, "_body_area_inout"); //PhysicsServer::get_singleton()->area_set_area_monitor_callback(area_rid, this, "_body_area_area_inout"); //PhysicsServer::get_singleton()->area_set_monitorable(area_rid, true); PhysicsServer::get_singleton()->area_set_collision_layer(area_rid, layer_mask); PhysicsServer::get_singleton()->area_set_collision_mask(area_rid, layer_mask); if (get_voxel_world()->is_inside_tree() && get_voxel_world()->is_inside_world()) { Ref world = get_voxel_world()->get_world_3d(); if (world.is_valid() && world->get_space() != RID()) PhysicsServer::get_singleton()->area_set_space(area_rid, world->get_space()); } PhysicsServer::get_singleton()->area_add_shape(area_rid, shape_rid, get_transform()); m[MESH_TYPE_INDEX_AREA] = area_rid; m[MESH_TYPE_INDEX_SHAPE] = shape_rid; _rids[mesh_index] = m; } void TerrainChunkDefault::colliders_free(const int mesh_index) { if (!_rids.has(mesh_index)) return; Dictionary m = _rids[mesh_index]; RID rid; if (m.has(MESH_TYPE_INDEX_SHAPE)) { RID r = m[MESH_TYPE_INDEX_SHAPE]; PhysicsServer::get_singleton()->free(r); } if (m.has(MESH_TYPE_INDEX_BODY)) { RID r = m[MESH_TYPE_INDEX_BODY]; PhysicsServer::get_singleton()->free(r); } m.erase(MESH_TYPE_INDEX_SHAPE); m.erase(MESH_TYPE_INDEX_BODY); _rids[mesh_index] = m; } void TerrainChunkDefault::free_index(const int mesh_index) { meshes_free(mesh_index); colliders_free(mesh_index); } void TerrainChunkDefault::update_transforms() { RID empty_rid; Transform t = get_transform(); List keys; _rids.get_key_list(&keys); for (List::Element *E = keys.front(); E; E = E->next()) { Variant v = E->get(); if (v.get_type() != Variant::INT) continue; Dictionary d = _rids[v]; if (d.has(MESH_TYPE_INDEX_MESH_INSTANCE)) { Array arr = d[MESH_TYPE_INDEX_MESH_INSTANCE]; for (int i = 0; i < arr.size(); ++i) { RID rid = arr[i]; if (rid != empty_rid) RS::get_singleton()->instance_set_transform(rid, get_transform()); } } if (d.has(MESH_TYPE_INDEX_BODY)) { RID rid = d[MESH_TYPE_INDEX_BODY]; if (rid != empty_rid) PhysicsServer::get_singleton()->body_set_state(rid, PhysicsServer::BODY_STATE_TRANSFORM, t); } if (d.has(MESH_TYPE_INDEX_AREA)) { RID rid = d[MESH_TYPE_INDEX_AREA]; if (rid != empty_rid) PhysicsServer::get_singleton()->area_set_shape_transform(rid, 0, t); } } for (int i = 0; i < collider_get_count(); ++i) { PhysicsServer::get_singleton()->body_set_state(collider_get_body(i), PhysicsServer::BODY_STATE_TRANSFORM, get_transform() * collider_get_transform(i)); } if (_debug_mesh_instance != RID()) { RS::get_singleton()->instance_set_transform(_debug_mesh_instance, get_transform()); } } //Lights Ref TerrainChunkDefault::get_light(const int index) { ERR_FAIL_INDEX_V(index, _lights.size(), Ref()); return _lights.get(index); } int TerrainChunkDefault::get_light_count() const { return _lights.size(); } void TerrainChunkDefault::debug_mesh_allocate() { if (_debug_mesh_rid == RID()) { _debug_mesh_rid = RenderingServer::get_singleton()->mesh_create(); } if (_debug_mesh_instance == RID()) { _debug_mesh_instance = RenderingServer::get_singleton()->instance_create(); if (get_voxel_world() && get_voxel_world()->get_world_3d().is_valid()) { RS::get_singleton()->instance_set_scenario(_debug_mesh_instance, get_voxel_world()->get_world_3d()->get_scenario()); } RS::get_singleton()->instance_set_base(_debug_mesh_instance, _debug_mesh_rid); RS::get_singleton()->instance_set_transform(_debug_mesh_instance, get_transform()); RS::get_singleton()->instance_set_visible(_debug_mesh_instance, true); } } void TerrainChunkDefault::debug_mesh_free() { if (_debug_mesh_instance != RID()) { RenderingServer::get_singleton()->free(_debug_mesh_instance); } if (_debug_mesh_rid != RID()) { RenderingServer::get_singleton()->free(_debug_mesh_rid); } } bool TerrainChunkDefault::debug_mesh_has() { return _debug_mesh_rid != RID(); } void TerrainChunkDefault::debug_mesh_clear() { if (_debug_mesh_rid != RID()) { RenderingServer::get_singleton()->mesh_clear(_debug_mesh_rid); } } void TerrainChunkDefault::debug_mesh_array_clear() { _debug_mesh_array.resize(0); } void TerrainChunkDefault::debug_mesh_add_vertices_to(const PoolVector3Array &arr) { _debug_mesh_array.append_array(arr); if (_debug_mesh_array.size() % 2 == 1) { _debug_mesh_array.append(_debug_mesh_array[_debug_mesh_array.size() - 1]); } } void TerrainChunkDefault::debug_mesh_send() { debug_mesh_allocate(); debug_mesh_clear(); if (_debug_mesh_array.size() == 0) return; SceneTree *st = SceneTree::get_singleton(); Array arr; arr.resize(RenderingServer::ARRAY_MAX); arr[RenderingServer::ARRAY_VERTEX] = _debug_mesh_array; RenderingServer::get_singleton()->mesh_add_surface_from_arrays(_debug_mesh_rid, RenderingServer::PRIMITIVE_LINES, arr); if (st) { RenderingServer::get_singleton()->mesh_surface_set_material(_debug_mesh_rid, 0, SceneTree::get_singleton()->get_debug_collision_material()->get_rid()); } debug_mesh_array_clear(); } void TerrainChunkDefault::draw_cross_voxels(Vector3 pos) { pos *= _voxel_scale; int size = _debug_mesh_array.size(); _debug_mesh_array.resize(_debug_mesh_array.size() + 6); _debug_mesh_array.set(size, pos + Vector3(0, 0, -0.2)); _debug_mesh_array.set(size + 1, pos + Vector3(0, 0, 0.2)); _debug_mesh_array.set(size + 2, pos + Vector3(0, -0.2, 0)); _debug_mesh_array.set(size + 3, pos + Vector3(0, 0.2, 0)); _debug_mesh_array.set(size + 4, pos + Vector3(-0.2, 0, 0)); _debug_mesh_array.set(size + 5, pos + Vector3(0.2, 0, 0)); } void TerrainChunkDefault::draw_cross_voxels_fill(Vector3 pos, float fill) { pos *= _voxel_scale; int size = _debug_mesh_array.size(); _debug_mesh_array.resize(_debug_mesh_array.size() + 6); _debug_mesh_array.set(size, pos + Vector3(0, 0, -0.2 * fill)); _debug_mesh_array.set(size + 1, pos + Vector3(0, 0, 0.2 * fill)); _debug_mesh_array.set(size + 2, pos + Vector3(0, -0.2 * fill, 0)); _debug_mesh_array.set(size + 3, pos + Vector3(0, 0.2 * fill, 0)); _debug_mesh_array.set(size + 4, pos + Vector3(-0.2 * fill, 0, 0)); _debug_mesh_array.set(size + 5, pos + Vector3(0.2 * fill, 0, 0)); } void TerrainChunkDefault::draw_debug_voxels(int max, Color color) { if (!debug_mesh_has()) { debug_mesh_allocate(); } //debug_mesh_array_clear(); //_debug_drawer->begin(Mesh::PRIMITIVE_LINES); int a = 0; int64_t sx = static_cast(_size_x); int64_t sz = static_cast(_size_z); for (int z = 0; z < sz; ++z) { for (int x = 0; x < sx; ++x) { int type = get_voxel(x, z, TerrainChunkDefault::DEFAULT_CHANNEL_TYPE); if (type == 0) { continue; } draw_cross_voxels_fill(Vector3(x, get_voxel(x, z, TerrainChunkDefault::DEFAULT_CHANNEL_ISOLEVEL), z), get_voxel(x, z, TerrainChunkDefault::DEFAULT_CHANNEL_ISOLEVEL) / 255.0 * get_voxel_scale() * 2.0); ++a; if (a > max) { break; } } } debug_mesh_send(); } void TerrainChunkDefault::draw_debug_voxel_lights() { if (!debug_mesh_has()) { debug_mesh_allocate(); } //debug_mesh_array_clear(); //_debug_drawer->begin(Mesh::PrimitiveType::PRIMITIVE_LINES); for (int i = 0; i < _lights.size(); ++i) { Ref v = _lights[i]; int pos_x = v->get_world_position_x() - (_size_x * _position_x); int pos_z = v->get_world_position_z() - (_size_z * _position_z); draw_cross_voxels_fill(Vector3(pos_x, 0, pos_z), 1.0); } debug_mesh_send(); } void TerrainChunkDefault::draw_debug_mdr_colliders() { if (!debug_mesh_has()) { debug_mesh_allocate(); } for (int i = 0; i < collider_get_count(); ++i) { Ref shape = collider_get_shape(i); if (!shape.is_valid()) continue; Transform t = collider_get_transform(i); shape->add_vertices_to_array(_debug_mesh_array, t); } } void TerrainChunkDefault::_visibility_changed(bool visible) { if (visible) { set_current_lod_level(_current_lod_level); return; } int lod_num = mesh_rid_get_count(MESH_INDEX_TERRAIN, MESH_TYPE_INDEX_MESH_INSTANCE); for (int i = 0; i < lod_num; ++i) { RID rid = mesh_rid_get_index(MESH_INDEX_TERRAIN, MESH_TYPE_INDEX_MESH_INSTANCE, i); if (rid != RID()) RenderingServer::get_singleton()->instance_set_visible(rid, false); rid = mesh_rid_get_index(MESH_INDEX_LIQUID, MESH_TYPE_INDEX_MESH_INSTANCE, i); if (rid != RID()) RenderingServer::get_singleton()->instance_set_visible(rid, false); rid = mesh_rid_get_index(MESH_INDEX_PROP, MESH_TYPE_INDEX_MESH_INSTANCE, i); if (rid != RID()) RenderingServer::get_singleton()->instance_set_visible(rid, false); } } void TerrainChunkDefault::_exit_tree() { TerrainChunk::_exit_tree(); if (!_is_generating) { rids_free(); rids_clear(); } } void TerrainChunkDefault::_world_transform_changed() { TerrainChunk::_world_transform_changed(); update_transforms(); } //Lights void TerrainChunkDefault::_bake_lights() { clear_baked_lights(); for (int i = 0; i < _lights.size(); ++i) { bake_light(_lights.get(i)); } } void TerrainChunkDefault::_bake_light(Ref light) { ERR_FAIL_COND(!light.is_valid()); Color color = light->get_color(); int size = light->get_size(); int local_x = light->get_world_position_x() - (_position_x * _size_x); int local_y = light->get_world_position_y(); int local_z = light->get_world_position_z() - (_position_z * _size_z); ERR_FAIL_COND(size < 0); int64_t dsx = static_cast(_data_size_x); int64_t dsy = static_cast(_world_height); int64_t dsz = static_cast(_data_size_z); uint8_t *channel_color_r = channel_get(TerrainChunkDefault::DEFAULT_CHANNEL_LIGHT_COLOR_R); uint8_t *channel_color_g = channel_get(TerrainChunkDefault::DEFAULT_CHANNEL_LIGHT_COLOR_G); uint8_t *channel_color_b = channel_get(TerrainChunkDefault::DEFAULT_CHANNEL_LIGHT_COLOR_B); ERR_FAIL_COND(channel_color_r == NULL || channel_color_g == NULL || channel_color_b == NULL); for (int y = local_y - size; y <= local_y + size; ++y) { if (y < 0 || y >= dsy) continue; for (int z = local_z - size; z <= local_z + size; ++z) { if (z < 0 || z >= dsz) continue; for (int x = local_x - size; x <= local_x + size; ++x) { if (x < 0 || x >= dsx) continue; int lx = x - local_x; int ly = y - local_y; int lz = z - local_z; float str = size - (((float)lx * lx + ly * ly + lz * lz)); str /= size; if (str < 0) continue; int index = get_data_index(x, z); int r = color.r * str * 255.0; int g = color.g * str * 255.0; int b = color.b * str * 255.0; r += channel_color_r[index]; g += channel_color_g[index]; b += channel_color_b[index]; if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; channel_color_r[index] = r; channel_color_g[index] = g; channel_color_b[index] = b; } } } } void TerrainChunkDefault::_clear_baked_lights() { channel_fill(0, DEFAULT_CHANNEL_LIGHT_COLOR_R); channel_fill(0, DEFAULT_CHANNEL_LIGHT_COLOR_G); channel_fill(0, DEFAULT_CHANNEL_LIGHT_COLOR_B); } void TerrainChunkDefault::_world_light_added(const Ref &light) { _lights.push_back(light); set_lights_dirty(true); } void TerrainChunkDefault::_world_light_removed(const Ref &light) { int index = _lights.find(light); if (index != -1) { _lights.remove(index); set_lights_dirty(true); } } void TerrainChunkDefault::free_chunk() { rids_free(); } void TerrainChunkDefault::_finalize_build() { ERR_FAIL_COND(!_library.is_valid()); #if TOOLS_ENABLED if (_debug_mesh_array.size() > 0) { debug_mesh_send(); } #endif set_current_lod_level(get_current_lod_level()); call_deferred("update_transforms"); } TerrainChunkDefault::TerrainChunkDefault() { _abort_build = false; _enabled = true; _lod_num = 3; _current_lod_level = 0; _build_flags = BUILD_FLAG_CREATE_COLLIDER | BUILD_FLAG_CREATE_LODS; } TerrainChunkDefault::~TerrainChunkDefault() { _abort_build = true; _lights.clear(); debug_mesh_free(); rids_free(); rids_clear(); } void TerrainChunkDefault::_channel_setup() { channel_set_count(MAX_DEFAULT_CHANNELS); } void TerrainChunkDefault::_bind_methods() { ClassDB::bind_method(D_METHOD("get_build_flags"), &TerrainChunkDefault::get_build_flags); ClassDB::bind_method(D_METHOD("set_build_flags", "value"), &TerrainChunkDefault::set_build_flags); ADD_PROPERTY(PropertyInfo(Variant::INT, "build_flags", PROPERTY_HINT_FLAGS, BINDING_STRING_BUILD_FLAGS, 0), "set_build_flags", "get_build_flags"); ClassDB::bind_method(D_METHOD("get_lights_dirty"), &TerrainChunkDefault::get_lights_dirty); ClassDB::bind_method(D_METHOD("set_lights_dirty", "value"), &TerrainChunkDefault::set_lights_dirty); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lights_dirty", PROPERTY_HINT_NONE, "", 0), "set_lights_dirty", "get_lights_dirty"); ClassDB::bind_method(D_METHOD("get_lod_num"), &TerrainChunkDefault::get_lod_num); ClassDB::bind_method(D_METHOD("set_lod_num"), &TerrainChunkDefault::set_lod_num); ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_num", PROPERTY_HINT_NONE, "", 0), "set_lod_num", "get_lod_num"); ClassDB::bind_method(D_METHOD("get_current_lod_level"), &TerrainChunkDefault::get_current_lod_level); ClassDB::bind_method(D_METHOD("set_current_lod_level"), &TerrainChunkDefault::set_current_lod_level); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_lod_level"), "set_current_lod_level", "get_current_lod_level"); //Meshes ClassDB::bind_method(D_METHOD("get_mesh_rids"), &TerrainChunkDefault::mesh_rids_get); ClassDB::bind_method(D_METHOD("set_mesh_rids", "rids"), &TerrainChunkDefault::mesh_rids_set); ClassDB::bind_method(D_METHOD("clear_rids"), &TerrainChunkDefault::rids_clear); ClassDB::bind_method(D_METHOD("mesh_rid_get", "mesh_index", "mesh_type_index"), &TerrainChunkDefault::mesh_rid_get); ClassDB::bind_method(D_METHOD("mesh_rid_set", "mesh_index", "mesh_type_index", "value"), &TerrainChunkDefault::mesh_rid_set); ClassDB::bind_method(D_METHOD("mesh_rid_get_index", "mesh_index", "mesh_type_index", "index"), &TerrainChunkDefault::mesh_rid_get_index); ClassDB::bind_method(D_METHOD("mesh_rid_set_index", "mesh_index", "mesh_type_index", "index", "value"), &TerrainChunkDefault::mesh_rid_set_index); ClassDB::bind_method(D_METHOD("mesh_rid_get_count", "mesh_index", "mesh_type_index"), &TerrainChunkDefault::mesh_rid_get_count); ClassDB::bind_method(D_METHOD("mesh_rids_clear", "mesh_index", "mesh_type_index"), &TerrainChunkDefault::mesh_rids_clear); ClassDB::bind_method(D_METHOD("meshes_get", "mesh_index", "mesh_type_index"), &TerrainChunkDefault::meshes_get); ClassDB::bind_method(D_METHOD("meshes_set", "mesh_index", "mesh_type_index", "meshes"), &TerrainChunkDefault::meshes_set); ClassDB::bind_method(D_METHOD("meshes_has", "mesh_index", "mesh_type_index"), &TerrainChunkDefault::meshes_has); ClassDB::bind_method(D_METHOD("rids_free"), &TerrainChunkDefault::rids_free); ClassDB::bind_method(D_METHOD("free_index", "mesh_index"), &TerrainChunkDefault::free_index); ClassDB::bind_method(D_METHOD("meshes_create", "mesh_index", "mesh_count"), &TerrainChunkDefault::meshes_create); ClassDB::bind_method(D_METHOD("meshes_free", "mesh_index"), &TerrainChunkDefault::meshes_free); ClassDB::bind_method(D_METHOD("create_colliders", "mesh_index", "layer_mask"), &TerrainChunkDefault::colliders_create, DEFVAL(1)); ClassDB::bind_method(D_METHOD("free_colliders", "mesh_index"), &TerrainChunkDefault::colliders_free); //Lights ClassDB::bind_method(D_METHOD("get_light", "index"), &TerrainChunkDefault::get_light); ClassDB::bind_method(D_METHOD("get_light_count"), &TerrainChunkDefault::get_light_count); //Debug ClassDB::bind_method(D_METHOD("debug_mesh_allocate"), &TerrainChunkDefault::debug_mesh_allocate); ClassDB::bind_method(D_METHOD("debug_mesh_free"), &TerrainChunkDefault::debug_mesh_free); ClassDB::bind_method(D_METHOD("debug_mesh_has"), &TerrainChunkDefault::debug_mesh_has); ClassDB::bind_method(D_METHOD("debug_mesh_clear"), &TerrainChunkDefault::debug_mesh_clear); ClassDB::bind_method(D_METHOD("debug_mesh_array_clear"), &TerrainChunkDefault::debug_mesh_array_clear); ClassDB::bind_method(D_METHOD("debug_mesh_add_vertices_to", "arr"), &TerrainChunkDefault::debug_mesh_add_vertices_to); ClassDB::bind_method(D_METHOD("debug_mesh_send"), &TerrainChunkDefault::debug_mesh_send); ClassDB::bind_method(D_METHOD("draw_cross_voxels", "max"), &TerrainChunkDefault::draw_cross_voxels); ClassDB::bind_method(D_METHOD("draw_cross_voxels_fill", "max", "fill"), &TerrainChunkDefault::draw_cross_voxels_fill); ClassDB::bind_method(D_METHOD("draw_debug_voxels", "max", "color"), &TerrainChunkDefault::draw_debug_voxels, DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_debug_voxel_lights"), &TerrainChunkDefault::draw_debug_voxel_lights); ClassDB::bind_method(D_METHOD("draw_debug_mdr_colliders"), &TerrainChunkDefault::draw_debug_mdr_colliders); //Free ClassDB::bind_method(D_METHOD("free_chunk"), &TerrainChunkDefault::free_chunk); //etc ClassDB::bind_method(D_METHOD("emit_build_finished"), &TerrainChunkDefault::emit_build_finished); //virtuals ClassDB::bind_method(D_METHOD("_channel_setup"), &TerrainChunkDefault::_channel_setup); ClassDB::bind_method(D_METHOD("_visibility_changed", "visible"), &TerrainChunkDefault::_visibility_changed); //lights ClassDB::bind_method(D_METHOD("_bake_lights"), &TerrainChunkDefault::_bake_lights); ClassDB::bind_method(D_METHOD("_bake_light", "light"), &TerrainChunkDefault::_bake_light); ClassDB::bind_method(D_METHOD("_clear_baked_lights"), &TerrainChunkDefault::_clear_baked_lights); ClassDB::bind_method(D_METHOD("_world_light_added", "light"), &TerrainChunkDefault::_world_light_added); ClassDB::bind_method(D_METHOD("_world_light_removed", "light"), &TerrainChunkDefault::_world_light_removed); ClassDB::bind_method(D_METHOD("_finalize_build"), &TerrainChunkDefault::_finalize_build); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_TYPE); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_ISOLEVEL); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_LIQUID_TYPE); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_LIQUID_ISOLEVEL); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_LIGHT_COLOR_R); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_LIGHT_COLOR_G); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_LIGHT_COLOR_B); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_AO); BIND_ENUM_CONSTANT(DEFAULT_CHANNEL_RANDOM_AO); BIND_ENUM_CONSTANT(MAX_DEFAULT_CHANNELS); BIND_CONSTANT(MESH_INDEX_TERRAIN); BIND_CONSTANT(MESH_INDEX_PROP); BIND_CONSTANT(MESH_INDEX_LIQUID); BIND_CONSTANT(MESH_INDEX_CLUTTER); BIND_CONSTANT(MESH_TYPE_INDEX_MESH); BIND_CONSTANT(MESH_TYPE_INDEX_MESH_INSTANCE); BIND_CONSTANT(MESH_TYPE_INDEX_SHAPE); BIND_CONSTANT(MESH_TYPE_INDEX_BODY); BIND_ENUM_CONSTANT(BUILD_FLAG_USE_ISOLEVEL); BIND_ENUM_CONSTANT(BUILD_FLAG_USE_LIGHTING); BIND_ENUM_CONSTANT(BUILD_FLAG_USE_AO); BIND_ENUM_CONSTANT(BUILD_FLAG_USE_RAO); BIND_ENUM_CONSTANT(BUILD_FLAG_GENERATE_AO); BIND_ENUM_CONSTANT(BUILD_FLAG_AUTO_GENERATE_RAO); BIND_ENUM_CONSTANT(BUILD_FLAG_BAKE_LIGHTS); BIND_ENUM_CONSTANT(BUILD_FLAG_CREATE_COLLIDER); BIND_ENUM_CONSTANT(BUILD_FLAG_CREATE_LODS); }