pandemonium_engine/modules/terraman/world/default/terrain_chunk_default.cpp

1005 lines
31 KiB
C++

/*************************************************************************/
/* 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<Variant> keys;
_rids.get_key_list(&keys);
for (List<Variant>::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<World3D> 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<World3D> 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<Variant> keys;
_rids.get_key_list(&keys);
for (List<Variant>::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<TerrainLight> TerrainChunkDefault::get_light(const int index) {
ERR_FAIL_INDEX_V(index, _lights.size(), Ref<TerrainLight>());
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<int64_t>(_size_x);
int64_t sz = static_cast<int64_t>(_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<TerrainLight> 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> 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<TerrainLight> 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<int64_t>(_data_size_x);
int64_t dsy = static_cast<int64_t>(_world_height);
int64_t dsz = static_cast<int64_t>(_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<TerrainLight> &light) {
_lights.push_back(light);
set_lights_dirty(true);
}
void TerrainChunkDefault::_world_light_removed(const Ref<TerrainLight> &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);
}