/* Copyright (c) 2019-2021 Péter Magyar 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 "terra_world.h" #include "core/version.h" #include "terra_chunk.h" #include "terra_structure.h" #include "../defines.h" #if PROPS_PRESENT #include "../../props/props/prop_data.h" #include "../../props/props/prop_data_entry.h" #include "../../props/props/prop_data_light.h" #include "../../props/props/prop_data_prop.h" #include "../../props/props/prop_data_scene.h" #endif #if MESH_DATA_RESOURCE_PRESENT #include "../../mesh_data_resource/props/prop_data_mesh_data.h" #endif const String TerraWorld::BINDING_STRING_CHANNEL_TYPE_INFO = "Type,Isolevel,Liquid,Liquid Level"; bool TerraWorld::get_editable() const { return _editable; } void TerraWorld::set_editable(const bool value) { _editable = value; } int TerraWorld::get_chunk_size_x() const { return _chunk_size_x; } void TerraWorld::set_chunk_size_x(const int value) { _chunk_size_x = value; } int TerraWorld::get_chunk_size_z() const { return _chunk_size_z; } void TerraWorld::set_chunk_size_z(const int value) { _chunk_size_z = value; } int TerraWorld::get_data_margin_start() const { return _data_margin_start; } void TerraWorld::set_data_margin_start(const int value) { _data_margin_start = value; } int TerraWorld::get_data_margin_end() const { return _data_margin_end; } void TerraWorld::set_data_margin_end(const int value) { _data_margin_end = value; } int TerraWorld::get_current_seed() const { return _current_seed; } void TerraWorld::set_current_seed(const int value) { _current_seed = value; } _FORCE_INLINE_ float TerraWorld::get_world_height() const { return _world_height; } void TerraWorld::set_world_height(const float value) { _world_height = value; } int TerraWorld::get_max_concurrent_generations() { return _max_concurrent_generations; } void TerraWorld::set_max_concurrent_generations(const int value) { _max_concurrent_generations = value; } Ref TerraWorld::get_library() { return _library; } void TerraWorld::set_library(const Ref &library) { _library = library; for (int i = 0; i < chunk_get_count(); ++i) { Ref c = chunk_get_index(i); if (!c.is_valid()) continue; c->set_library(_library); } } Ref TerraWorld::get_level_generator() const { return _level_generator; } void TerraWorld::set_level_generator(const Ref &level_generator) { _level_generator = level_generator; } float TerraWorld::get_voxel_scale() const { return _voxel_scale; } void TerraWorld::set_voxel_scale(const float value) { _voxel_scale = value; for (int i = 0; i < chunk_get_count(); ++i) { Ref c = chunk_get_index(i); if (!c.is_valid()) continue; c->set_voxel_scale(_voxel_scale); } } int TerraWorld::get_chunk_spawn_range() const { return _chunk_spawn_range; } void TerraWorld::set_chunk_spawn_range(const int value) { _chunk_spawn_range = value; } NodePath TerraWorld::get_player_path() const { return _player_path; } void TerraWorld::set_player_path(const NodePath &player_path) { _player_path = player_path; } Spatial *TerraWorld::get_player() const { return _player; } void TerraWorld::set_player(Spatial *player) { _player = player; } void TerraWorld::set_player_bind(Node *player) { set_player(Object::cast_to(player)); } Ref TerraWorld::world_area_get(const int index) const { ERR_FAIL_INDEX_V(index, _world_areas.size(), Ref()); return _world_areas.get(index); } void TerraWorld::world_area_add(const Ref &area) { _world_areas.push_back(area); } void TerraWorld::world_area_remove(const int index) { ERR_FAIL_INDEX(index, _world_areas.size()); _world_areas.remove(index); } void TerraWorld::world_areas_clear() { _world_areas.clear(); } int TerraWorld::world_area_get_count() const { return _world_areas.size(); } //Terra Structures Ref TerraWorld::voxel_structure_get(const int index) const { ERR_FAIL_INDEX_V(index, _voxel_structures.size(), Ref()); return _voxel_structures.get(index); } void TerraWorld::voxel_structure_add(const Ref &structure) { _voxel_structures.push_back(structure); } void TerraWorld::voxel_structure_remove(const Ref &structure) { if (!structure.is_valid()) return; int index = _voxel_structures.find(structure); if (index != -1) _voxel_structures.remove(index); } void TerraWorld::voxel_structure_remove_index(const int index) { ERR_FAIL_INDEX(index, _voxel_structures.size()); _voxel_structures.remove(index); } void TerraWorld::voxel_structures_clear() { _voxel_structures.clear(); } int TerraWorld::voxel_structure_get_count() const { return _voxel_structures.size(); } void TerraWorld::voxel_structure_add_at_position(Ref structure, const Vector3 &world_position) { ERR_FAIL_COND(!structure.is_valid()); structure->set_position_x(static_cast(world_position.x / _voxel_scale)); structure->set_position_y(static_cast(world_position.y / _voxel_scale)); structure->set_position_z(static_cast(world_position.z / _voxel_scale)); voxel_structure_add(structure); } Vector TerraWorld::voxel_structures_get() { VARIANT_ARRAY_GET(_voxel_structures); } void TerraWorld::voxel_structures_set(const Vector &structures) { voxel_structures_clear(); for (int i = 0; i < structures.size(); ++i) { Ref structure = Ref(structures[i]); voxel_structure_add(structure); } } void TerraWorld::chunk_add(Ref chunk, const int x, const int z) { ERR_FAIL_COND(!chunk.is_valid()); ERR_FAIL_COND_MSG(chunk->get_voxel_world() != NULL && chunk->get_voxel_world() != this, "Chunk is already owned by an another world!"); IntPos pos(x, z); //ERR_FAIL_COND(_chunks.has(pos)); chunk->set_voxel_world(this); chunk->set_position(x, z); chunk->world_transform_changed(); if (!_chunks.has(pos)) _chunks.set(pos, chunk); if (_chunks_vector.find(chunk) == -1) _chunks_vector.push_back(chunk); if (is_inside_tree()) chunk->enter_tree(); if (has_method("_chunk_added")) call("_chunk_added", chunk); } bool TerraWorld::chunk_has(const int x, const int z) const { return _chunks.has(IntPos(x, z)); } Ref TerraWorld::chunk_get(const int x, const int z) { IntPos pos(x, z); if (_chunks.has(pos)) return _chunks.get(pos); return Ref(); } Ref TerraWorld::chunk_remove(const int x, const int z) { IntPos pos(x, z); if (!_chunks.has(pos)) return NULL; Ref chunk = _chunks.get(pos); for (int i = 0; i < _chunks_vector.size(); ++i) { if (_chunks_vector.get(i) == chunk) { _chunks_vector.remove(i); break; } } chunk->exit_tree(); ERR_FAIL_COND_V(!_chunks.erase(pos), NULL); //_chunks.erase(pos); return chunk; } Ref TerraWorld::chunk_remove_index(const int index) { ERR_FAIL_INDEX_V(index, _chunks_vector.size(), NULL); Ref chunk = _chunks_vector.get(index); _chunks_vector.remove(index); _chunks.erase(IntPos(chunk->get_position_x(), chunk->get_position_z())); chunk->exit_tree(); return chunk; } Ref TerraWorld::chunk_get_index(const int index) { ERR_FAIL_INDEX_V(index, _chunks_vector.size(), NULL); return _chunks_vector.get(index); } int TerraWorld::chunk_get_count() const { return _chunks_vector.size(); } void TerraWorld::chunks_clear() { for (int i = 0; i < _chunks_vector.size(); ++i) { _chunks_vector.get(i)->exit_tree(); } _chunks_vector.clear(); _chunks.clear(); _generation_queue.clear(); _generating.clear(); } Ref TerraWorld::chunk_get_or_create(int x, int z) { Ref chunk = chunk_get(x, z); if (!chunk.is_valid()) { chunk = chunk_create(x, z); } return chunk; } Ref TerraWorld::chunk_create(const int x, const int z) { Ref c = call("_create_chunk", x, z, Ref()); generation_queue_add_to(c); return c; } void TerraWorld::chunk_setup(Ref chunk) { ERR_FAIL_COND(!chunk.is_valid()); call("_create_chunk", chunk->get_position_x(), chunk->get_position_z(), chunk); } Ref TerraWorld::_create_chunk(const int x, const int z, Ref chunk) { if (!chunk.is_valid()) { chunk.instance(); } //no meshers here ERR_FAIL_COND_V(!chunk.is_valid(), NULL); chunk->set_name("Chunk[" + String::num(x) + "," + String::num(z) + "]"); chunk->set_voxel_world(this); chunk->set_position(x, z); chunk->set_world_height(_world_height); chunk->set_library(_library); chunk->set_voxel_scale(_voxel_scale); chunk->set_size(_chunk_size_x, _chunk_size_z, _data_margin_start, _data_margin_end); //chunk->set_translation(Vector3(x * _chunk_size_x * _voxel_scale, y * _chunk_size_y * _voxel_scale, z * _chunk_size_z * _voxel_scale)); chunk_add(chunk, x, z); return chunk; } void TerraWorld::chunk_generate(Ref chunk) { ERR_FAIL_COND(!chunk.is_valid()); if (has_method("_prepare_chunk_for_generation")) call("_prepare_chunk_for_generation", chunk); call("_generate_chunk", chunk); chunk->build(); } Vector TerraWorld::chunks_get() { VARIANT_ARRAY_GET(_chunks_vector); } void TerraWorld::chunks_set(const Vector &chunks) { if (is_inside_tree()) { for (int i = 0; i < _chunks_vector.size(); ++i) { Ref chunk = Ref(_chunks_vector[i]); if (chunks.find(chunk) == -1) { chunk_remove_index(i); _generation_queue.erase(chunk); _generating.erase(chunk); --i; } } //add the difference for (int i = 0; i < chunks.size(); ++i) { Ref chunk = Ref(chunks[i]); if (!chunk.is_valid()) continue; if (_chunks_vector.find(chunk) != -1) continue; chunk_add(chunk, chunk->get_position_x(), chunk->get_position_z()); } } else { _chunks_vector.clear(); for (int i = 0; i < chunks.size(); ++i) { Ref chunk = Ref(chunks[i]); _chunks_vector.push_back(chunk); } } } int TerraWorld::_get_channel_index_info(const TerraWorld::ChannelTypeInfo channel_type) { return -1; } void TerraWorld::_set_voxel_with_tool(const bool mode_add, const Vector3 hit_position, const Vector3 hit_normal, const int selected_voxel, const int isolevel) { Vector3 pos; if (mode_add) { pos = (hit_position + (Vector3(0.1, 0.1, 0.1) * hit_normal * get_voxel_scale())); } else { pos = (hit_position + (Vector3(0.1, 0.1, 0.1) * -hit_normal * get_voxel_scale())); } int channel_type = get_channel_index_info(TerraWorld::CHANNEL_TYPE_INFO_TYPE); int channel_isolevel = get_channel_index_info(TerraWorld::CHANNEL_TYPE_INFO_ISOLEVEL); if (channel_isolevel == -1) { set_voxel_at_world_position(pos, selected_voxel, channel_type); } else { set_voxel_at_world_position(pos, selected_voxel, channel_type, false); set_voxel_at_world_position(pos, isolevel, channel_isolevel); } } bool TerraWorld::can_chunk_do_build_step() { if (_max_frame_chunk_build_steps == 0) { return true; } return _num_frame_chunk_build_steps++ < _max_frame_chunk_build_steps; } bool TerraWorld::is_position_walkable(const Vector3 &p_pos) { int x = static_cast(Math::floor(p_pos.x / (_chunk_size_x * _voxel_scale))); int z = static_cast(Math::floor(p_pos.z / (_chunk_size_z * _voxel_scale))); Ref c = chunk_get(x, z); if (!c.is_valid()) return false; return !c->get_is_generating(); } void TerraWorld::on_chunk_mesh_generation_finished(Ref p_chunk) { call_deferred("emit_signal", "chunk_mesh_generation_finished", p_chunk); } void TerraWorld::generation_queue_add_to(const Ref &chunk) { ERR_FAIL_COND(!chunk.is_valid()); set_process_internal(true); _generation_queue.push_back(chunk); } Ref TerraWorld::generation_queue_get_index(int index) { ERR_FAIL_INDEX_V(index, _generation_queue.size(), NULL); return _generation_queue.get(index); } void TerraWorld::generation_queue_remove_index(int index) { ERR_FAIL_INDEX(index, _generation_queue.size()); _generation_queue.remove(index); } int TerraWorld::generation_queue_get_size() const { return _generation_queue.size(); } void TerraWorld::generation_add_to(const Ref &chunk) { ERR_FAIL_COND(!chunk.is_valid()); _generating.push_back(chunk); } Ref TerraWorld::generation_get_index(const int index) { ERR_FAIL_INDEX_V(index, _generating.size(), NULL); return _generating.get(index); } void TerraWorld::generation_remove_index(const int index) { ERR_FAIL_INDEX(index, _generating.size()); _generating.remove(index); } int TerraWorld::generation_get_size() const { return _generating.size(); } #if PROPS_PRESENT void TerraWorld::prop_add(Transform transform, const Ref &prop, const bool apply_voxel_scale) { ERR_FAIL_COND(!prop.is_valid()); if (apply_voxel_scale) { transform = transform.scaled(Vector3(get_voxel_scale(), get_voxel_scale(), get_voxel_scale())); } Vector3 wp; wp = transform.xform(wp); Ref chunk = get_or_create_chunk_at_world_position(wp); chunk->prop_add(transform, prop); int count = prop->get_prop_count(); for (int i = 0; i < count; ++i) { Ref entry = prop->get_prop(i); if (!entry.is_valid()) continue; Transform t = transform * entry->get_transform(); wp = t.xform(Vector3()); chunk = get_or_create_chunk_at_world_position(wp); Ref prop_entry_data = entry; if (prop_entry_data.is_valid()) { Ref p = prop_entry_data->get_prop(); if (!p.is_valid()) continue; prop_add(t, p, false); continue; } Ref scene_data = entry; if (scene_data.is_valid()) { Ref sc = scene_data->get_scene(); if (!sc.is_valid()) continue; Node *n = sc->instance(); add_child(n); n->set_owner(this); Spatial *sp = Object::cast_to(n); if (sp) { sp->set_transform(t); } continue; } Ref light_data = entry; if (light_data.is_valid()) { Ref light; light.instance(); light->set_world_position(wp.x / get_voxel_scale(), wp.y / get_voxel_scale(), wp.z / get_voxel_scale()); light->set_color(light_data->get_light_color()); light->set_size(light_data->get_light_size()); light_add(light); continue; } #if MESH_DATA_RESOURCE_PRESENT Ref mesh_data = entry; if (mesh_data.is_valid()) { Ref mdr = mesh_data->get_mesh(); if (!mdr.is_valid()) continue; chunk->mesh_data_resource_add(t, mdr, mesh_data->get_texture(), Color(1, 1, 1, 1), false); continue; } #endif } } #endif //Lights void TerraWorld::light_add(const Ref &light) { _lights.push_back(light); for (int i = 0; i < _chunks_vector.size(); ++i) { Ref chunk = _chunks_vector[i]; if (chunk.is_valid()) { chunk->world_light_added(light); } } } Ref TerraWorld::light_get(const int index) { ERR_FAIL_INDEX_V(index, _lights.size(), Ref()); return _lights.get(index); } void TerraWorld::light_remove(const int index) { ERR_FAIL_INDEX(index, _lights.size()); Ref light = _lights[index]; for (int i = 0; i < _chunks_vector.size(); ++i) { Ref chunk = _chunks_vector[i]; if (chunk.is_valid()) { chunk->world_light_removed(light); } } } int TerraWorld::light_get_count() const { return _lights.size(); } void TerraWorld::lights_clear() { for (int i = 0; i < _lights.size(); ++i) { Ref light = _lights[i]; if (!light.is_valid()) continue; for (int j = 0; j < _chunks_vector.size(); ++j) { Ref chunk = _chunks_vector[j]; if (chunk.is_valid()) { chunk->world_light_removed(light); } } } _lights.clear(); } Vector TerraWorld::lights_get() { VARIANT_ARRAY_GET(_lights); } void TerraWorld::lights_set(const Vector &chunks) { lights_clear(); for (int i = 0; i < chunks.size(); ++i) { Ref light = Ref(chunks[i]); light_add(light); } } uint8_t TerraWorld::get_voxel_at_world_position(const Vector3 &world_position, const int channel_index) { Vector3 pos = world_position / get_voxel_scale(); //Note: floor is needed to handle negative numbers properly int x = static_cast(Math::floor(pos.x / get_chunk_size_x())); int z = static_cast(Math::floor(pos.z / get_chunk_size_z())); int bx = static_cast(Math::floor(pos.x)) % get_chunk_size_x(); int bz = static_cast(Math::floor(pos.z)) % get_chunk_size_z(); if (bx < 0) { bx += get_chunk_size_x(); } if (bz < 0) { bz += get_chunk_size_z(); } Ref chunk = chunk_get(x, z); if (chunk.is_valid()) return chunk->get_voxel(bx, bz, channel_index); return 0; } void TerraWorld::set_voxel_at_world_position(const Vector3 &world_position, const uint8_t data, const int channel_index, const bool rebuild) { Vector3 pos = world_position / get_voxel_scale(); //Note: floor is needed to handle negative numbers properly int x = static_cast(Math::floor(pos.x / get_chunk_size_x())); int z = static_cast(Math::floor(pos.z / get_chunk_size_z())); int bx = static_cast(Math::floor(pos.x)) % get_chunk_size_x(); int bz = static_cast(Math::floor(pos.z)) % get_chunk_size_z(); if (bx < 0) { bx += get_chunk_size_x(); } if (bz < 0) { bz += get_chunk_size_z(); } if (get_data_margin_end() > 0) { if (bx == 0) { Ref chunk = chunk_get_or_create(x - 1, z); chunk->set_voxel(data, get_chunk_size_x(), bz, channel_index); if (rebuild) chunk->build(); } if (bz == 0) { Ref chunk = chunk_get_or_create(x, z - 1); chunk->set_voxel(data, bx, get_chunk_size_z(), channel_index); if (rebuild) chunk->build(); } } if (get_data_margin_start() > 0) { if (bx == get_chunk_size_x() - 1) { Ref chunk = chunk_get_or_create(x + 1, z); chunk->set_voxel(data, -1, bz, channel_index); if (rebuild) chunk->build(); } if (bz == get_chunk_size_z() - 1) { Ref chunk = chunk_get_or_create(x, z + 1); chunk->set_voxel(data, bx, -1, channel_index); if (rebuild) chunk->build(); } } Ref chunk = chunk_get_or_create(x, z); chunk->set_voxel(data, bx, bz, channel_index); if (rebuild) chunk->build(); } Ref TerraWorld::get_chunk_at_world_position(const Vector3 &world_position) { Vector3 pos = world_position / get_voxel_scale(); //Note: floor is needed to handle negative numbers proiberly int x = static_cast(Math::floor(pos.x / get_chunk_size_x())); int z = static_cast(Math::floor(pos.z / get_chunk_size_z())); return chunk_get(x, z); } Ref TerraWorld::get_or_create_chunk_at_world_position(const Vector3 &world_position) { Vector3 pos = world_position / get_voxel_scale(); //Note: floor is needed to handle negative numbers proiberly int x = static_cast(Math::floor(pos.x / get_chunk_size_x())); int z = static_cast(Math::floor(pos.z / get_chunk_size_z())); return chunk_get_or_create(x, z); } void TerraWorld::set_voxel_with_tool(const bool mode_add, const Vector3 hit_position, const Vector3 hit_normal, const int selected_voxel, const int isolevel) { call("_set_voxel_with_tool", mode_add, hit_position, hit_normal, selected_voxel, isolevel); } int TerraWorld::get_channel_index_info(const TerraWorld::ChannelTypeInfo channel_type) { return call("_get_channel_index_info", channel_type); } TerraWorld::TerraWorld() { _editable = false; _is_priority_generation = true; _max_concurrent_generations = 3; _chunk_size_x = 16; _chunk_size_z = 16; _current_seed = 0; _data_margin_start = 0; _data_margin_end = 0; _world_height = 256; _voxel_scale = 1; _chunk_spawn_range = 4; _player = NULL; _max_frame_chunk_build_steps = 0; _num_frame_chunk_build_steps = 0; } TerraWorld ::~TerraWorld() { _chunks.clear(); _chunks_vector.clear(); _world_areas.clear(); _voxel_structures.clear(); _library.unref(); _level_generator.unref(); _player = NULL; _generation_queue.clear(); _generating.clear(); _lights.clear(); } void TerraWorld::_generate_chunk(Ref chunk) { ERR_FAIL_COND(!chunk.is_valid()); if (_level_generator.is_valid()) _level_generator->generate_chunk(chunk); for (int i = 0; i < _voxel_structures.size(); ++i) { Ref structure = _voxel_structures.get(i); if (!structure.is_valid()) continue; if (structure->get_use_aabb()) { if (structure->get_chunk_aabb().has_point(Vector3(chunk->get_position_x(), 1, chunk->get_position_z()))) structure->write_to_chunk(chunk); } else { structure->write_to_chunk(chunk); } } } void TerraWorld::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { set_player_bind(get_node_or_null(get_player_path())); set_process_internal(true); set_physics_process_internal(true); set_notify_transform(true); if (_library.is_valid()) _library->refresh_rects(); for (int i = 0; i < _chunks_vector.size(); ++i) { Ref chunk = _chunks_vector[i]; if (chunk.is_valid()) { chunk_setup(chunk); chunk->build(); } } } break; case NOTIFICATION_INTERNAL_PROCESS: { _num_frame_chunk_build_steps = 0; for (int i = 0; i < _chunks_vector.size(); ++i) { Ref chunk = _chunks_vector[i]; ERR_CONTINUE(!chunk.is_valid()); if (chunk->get_process()) { chunk->process(get_process_delta_time()); } if (chunk->get_is_generating()) { chunk->generation_process(get_process_delta_time()); } } #if VERSION_MAJOR > 3 if (_is_priority_generation && _generation_queue.is_empty() && _generating.is_empty()) { #else if (_is_priority_generation && _generation_queue.empty() && _generating.empty()) { #endif _is_priority_generation = false; call("_generation_finished"); emit_signal("generation_finished"); return; } for (int i = 0; i < _generating.size(); ++i) { Ref chunk = _generating.get(i); if (!chunk.is_valid() || !chunk->get_is_generating()) { _generating.remove(i); --i; continue; } } if (_generating.size() >= _max_concurrent_generations) return; if (_generation_queue.size() == 0) return; while (_generating.size() < _max_concurrent_generations && _generation_queue.size() != 0) { Ref chunk = _generation_queue.get(0); _generation_queue.remove(0); ERR_FAIL_COND(!chunk.is_valid()); _generating.push_back(chunk); chunk_generate(chunk); } } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { for (int i = 0; i < _chunks_vector.size(); ++i) { Ref chunk = _chunks_vector[i]; ERR_CONTINUE(!chunk.is_valid()); if (chunk->get_process()) { chunk->physics_process(get_physics_process_delta_time()); } if (chunk->get_is_generating()) { chunk->generation_physics_process(get_physics_process_delta_time()); } } } break; case NOTIFICATION_EXIT_TREE: { for (int i = 0; i < _chunks_vector.size(); ++i) { Ref chunk = _chunks_vector[i]; if (chunk.is_valid()) { if (chunk->get_voxel_world() == this) { chunk->exit_tree(); chunk->set_voxel_world(NULL); } } } } break; case NOTIFICATION_TRANSFORM_CHANGED: { for (int i = 0; i < _chunks_vector.size(); ++i) { Ref chunk = _chunks_vector[i]; if (chunk.is_valid()) { chunk->world_transform_changed(); } } } break; } } void TerraWorld::_bind_methods() { ADD_SIGNAL(MethodInfo("chunk_mesh_generation_finished", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerraChunk"))); ClassDB::bind_method(D_METHOD("get_editable"), &TerraWorld::get_editable); ClassDB::bind_method(D_METHOD("set_editable", "value"), &TerraWorld::set_editable); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "get_editable"); ClassDB::bind_method(D_METHOD("get_chunk_size_x"), &TerraWorld::get_chunk_size_x); ClassDB::bind_method(D_METHOD("set_chunk_size_x", "value"), &TerraWorld::set_chunk_size_x); ADD_PROPERTY(PropertyInfo(Variant::INT, "chunk_size_x"), "set_chunk_size_x", "get_chunk_size_x"); ClassDB::bind_method(D_METHOD("get_chunk_size_z"), &TerraWorld::get_chunk_size_z); ClassDB::bind_method(D_METHOD("set_chunk_size_z", "value"), &TerraWorld::set_chunk_size_z); ADD_PROPERTY(PropertyInfo(Variant::INT, "chunk_size_z"), "set_chunk_size_z", "get_chunk_size_z"); ClassDB::bind_method(D_METHOD("get_data_margin_start"), &TerraWorld::get_data_margin_start); ClassDB::bind_method(D_METHOD("set_data_margin_start", "value"), &TerraWorld::set_data_margin_start); ADD_PROPERTY(PropertyInfo(Variant::INT, "data_margin_start"), "set_data_margin_start", "get_data_margin_start"); ClassDB::bind_method(D_METHOD("get_data_margin_end"), &TerraWorld::get_data_margin_end); ClassDB::bind_method(D_METHOD("set_data_margin_end", "value"), &TerraWorld::set_data_margin_end); ADD_PROPERTY(PropertyInfo(Variant::INT, "data_margin_end"), "set_data_margin_end", "get_data_margin_end"); ClassDB::bind_method(D_METHOD("get_world_height"), &TerraWorld::get_world_height); ClassDB::bind_method(D_METHOD("set_world_height", "height"), &TerraWorld::set_world_height); ADD_PROPERTY(PropertyInfo(Variant::REAL, "world_height"), "set_world_height", "get_world_height"); ClassDB::bind_method(D_METHOD("get_max_concurrent_generations"), &TerraWorld::get_max_concurrent_generations); ClassDB::bind_method(D_METHOD("set_max_concurrent_generations", "height"), &TerraWorld::set_max_concurrent_generations); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_concurrent_generations"), "set_max_concurrent_generations", "get_max_concurrent_generations"); ClassDB::bind_method(D_METHOD("get_current_seed"), &TerraWorld::get_current_seed); ClassDB::bind_method(D_METHOD("set_current_seed", "value"), &TerraWorld::set_current_seed); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_seed"), "set_current_seed", "get_current_seed"); ClassDB::bind_method(D_METHOD("get_library"), &TerraWorld::get_library); ClassDB::bind_method(D_METHOD("set_library", "library"), &TerraWorld::set_library); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "TerramanLibrary"), "set_library", "get_library"); ClassDB::bind_method(D_METHOD("get_level_generator"), &TerraWorld::get_level_generator); ClassDB::bind_method(D_METHOD("set_level_generator", "level_generator"), &TerraWorld::set_level_generator); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "level_generator", PROPERTY_HINT_RESOURCE_TYPE, "TerramanLevelGenerator"), "set_level_generator", "get_level_generator"); ClassDB::bind_method(D_METHOD("get_voxel_scale"), &TerraWorld::get_voxel_scale); ClassDB::bind_method(D_METHOD("set_voxel_scale", "value"), &TerraWorld::set_voxel_scale); ADD_PROPERTY(PropertyInfo(Variant::REAL, "voxel_scale"), "set_voxel_scale", "get_voxel_scale"); ClassDB::bind_method(D_METHOD("get_chunk_spawn_range"), &TerraWorld::get_chunk_spawn_range); ClassDB::bind_method(D_METHOD("set_chunk_spawn_range", "value"), &TerraWorld::set_chunk_spawn_range); ADD_PROPERTY(PropertyInfo(Variant::INT, "chunk_spawn_range"), "set_chunk_spawn_range", "get_chunk_spawn_range"); ClassDB::bind_method(D_METHOD("get_player_path"), &TerraWorld::get_player_path); ClassDB::bind_method(D_METHOD("set_player_path", "value"), &TerraWorld::set_player_path); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "player_path"), "set_player_path", "get_player_path"); ClassDB::bind_method(D_METHOD("get_player"), &TerraWorld::get_player); ClassDB::bind_method(D_METHOD("set_player", "player"), &TerraWorld::set_player_bind); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "player", PROPERTY_HINT_RESOURCE_TYPE, "Spatial", 0), "set_player", "get_player"); ClassDB::bind_method(D_METHOD("world_area_get", "index"), &TerraWorld::world_area_get); ClassDB::bind_method(D_METHOD("world_area_add", "area"), &TerraWorld::world_area_add); ClassDB::bind_method(D_METHOD("world_area_remove", "index"), &TerraWorld::world_area_remove); ClassDB::bind_method(D_METHOD("world_areas_clear"), &TerraWorld::world_areas_clear); ClassDB::bind_method(D_METHOD("world_area_get_count"), &TerraWorld::world_area_get_count); ClassDB::bind_method(D_METHOD("voxel_structure_get", "index"), &TerraWorld::voxel_structure_get); ClassDB::bind_method(D_METHOD("voxel_structure_add", "structure"), &TerraWorld::voxel_structure_add); ClassDB::bind_method(D_METHOD("voxel_structure_remove", "structure"), &TerraWorld::voxel_structure_remove); ClassDB::bind_method(D_METHOD("voxel_structure_remove_index", "index"), &TerraWorld::voxel_structure_remove_index); ClassDB::bind_method(D_METHOD("voxel_structures_clear"), &TerraWorld::voxel_structures_clear); ClassDB::bind_method(D_METHOD("voxel_structure_get_count"), &TerraWorld::voxel_structure_get_count); ClassDB::bind_method(D_METHOD("voxel_structure_add_at_position", "structure", "world_position"), &TerraWorld::voxel_structure_add_at_position); ClassDB::bind_method(D_METHOD("voxel_structures_get"), &TerraWorld::voxel_structures_get); ClassDB::bind_method(D_METHOD("voxel_structures_set"), &TerraWorld::voxel_structures_set); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "17/17:TerraStructure", PROPERTY_USAGE_DEFAULT, "TerraStructure"), "voxel_structures_set", "voxel_structures_get"); BIND_VMETHOD(MethodInfo("_chunk_added", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerraChunk"))); ClassDB::bind_method(D_METHOD("chunk_add", "chunk", "x", "z"), &TerraWorld::chunk_add); ClassDB::bind_method(D_METHOD("chunk_has", "x", "z"), &TerraWorld::chunk_has); ClassDB::bind_method(D_METHOD("chunk_get", "x", "z"), &TerraWorld::chunk_get); ClassDB::bind_method(D_METHOD("chunk_remove", "x", "z"), &TerraWorld::chunk_remove); ClassDB::bind_method(D_METHOD("chunk_remove_index", "index"), &TerraWorld::chunk_remove_index); ClassDB::bind_method(D_METHOD("chunk_get_index", "index"), &TerraWorld::chunk_get_index); ClassDB::bind_method(D_METHOD("chunk_get_count"), &TerraWorld::chunk_get_count); ClassDB::bind_method(D_METHOD("chunks_clear"), &TerraWorld::chunks_clear); ClassDB::bind_method(D_METHOD("chunks_get"), &TerraWorld::chunks_get); ClassDB::bind_method(D_METHOD("chunks_set"), &TerraWorld::chunks_set); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "17/17:TerraChunk", PROPERTY_USAGE_DEFAULT, "TerraChunk"), "chunks_set", "chunks_get"); ClassDB::bind_method(D_METHOD("generation_queue_add_to", "chunk"), &TerraWorld::generation_queue_add_to); ClassDB::bind_method(D_METHOD("generation_queue_get_index", "index"), &TerraWorld::generation_queue_get_index); ClassDB::bind_method(D_METHOD("generation_queue_remove_index", "index"), &TerraWorld::generation_queue_remove_index); ClassDB::bind_method(D_METHOD("generation_queue_get_size"), &TerraWorld::generation_queue_get_size); ClassDB::bind_method(D_METHOD("generation_add_to", "chunk"), &TerraWorld::generation_add_to); ClassDB::bind_method(D_METHOD("generation_get_index", "index"), &TerraWorld::generation_get_index); ClassDB::bind_method(D_METHOD("generation_remove_index", "index"), &TerraWorld::generation_remove_index); ClassDB::bind_method(D_METHOD("generation_get_size"), &TerraWorld::generation_get_size); ADD_SIGNAL(MethodInfo("generation_finished")); BIND_VMETHOD(MethodInfo("_generation_finished")); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerraChunk"), "_create_chunk", PropertyInfo(Variant::INT, "x"), PropertyInfo(Variant::INT, "y"), PropertyInfo(Variant::INT, "z"), PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerraChunk"))); BIND_VMETHOD(MethodInfo("_prepare_chunk_for_generation", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerraChunk"))); BIND_VMETHOD(MethodInfo("_generate_chunk", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerraChunk"))); ClassDB::bind_method(D_METHOD("chunk_get_or_create", "x", "z"), &TerraWorld::chunk_get_or_create); ClassDB::bind_method(D_METHOD("chunk_create", "x", "z"), &TerraWorld::chunk_create); ClassDB::bind_method(D_METHOD("chunk_setup", "chunk"), &TerraWorld::chunk_setup); ClassDB::bind_method(D_METHOD("_create_chunk", "x", "z", "chunk"), &TerraWorld::_create_chunk); ClassDB::bind_method(D_METHOD("_generate_chunk", "chunk"), &TerraWorld::_generate_chunk); ClassDB::bind_method(D_METHOD("can_chunk_do_build_step"), &TerraWorld::can_chunk_do_build_step); ClassDB::bind_method(D_METHOD("is_position_walkable", "position"), &TerraWorld::is_position_walkable); ClassDB::bind_method(D_METHOD("on_chunk_mesh_generation_finished", "chunk"), &TerraWorld::on_chunk_mesh_generation_finished); #if PROPS_PRESENT ClassDB::bind_method(D_METHOD("prop_add", "transform", "prop", "apply_voxel_scale"), &TerraWorld::prop_add, DEFVAL(true)); #endif //Lights ClassDB::bind_method(D_METHOD("light_add", "light"), &TerraWorld::light_add); ClassDB::bind_method(D_METHOD("light_get", "index"), &TerraWorld::light_get); ClassDB::bind_method(D_METHOD("light_remove", "index"), &TerraWorld::light_remove); ClassDB::bind_method(D_METHOD("light_get_count"), &TerraWorld::light_get_count); ClassDB::bind_method(D_METHOD("lights_clear"), &TerraWorld::lights_clear); ClassDB::bind_method(D_METHOD("lights_get"), &TerraWorld::lights_get); ClassDB::bind_method(D_METHOD("lights_set", "chunks"), &TerraWorld::lights_set); ClassDB::bind_method(D_METHOD("get_voxel_at_world_position", "world_position", "channel_index"), &TerraWorld::get_voxel_at_world_position); ClassDB::bind_method(D_METHOD("set_voxel_at_world_position", "world_position", "data", "channel_index", "rebuild"), &TerraWorld::set_voxel_at_world_position, DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_chunk_at_world_position", "world_position"), &TerraWorld::get_chunk_at_world_position); ClassDB::bind_method(D_METHOD("get_or_create_chunk_at_world_position", "world_position"), &TerraWorld::get_or_create_chunk_at_world_position); BIND_VMETHOD(MethodInfo("_get_channel_index_info", PropertyInfo(Variant::INT, "channel_type", PROPERTY_HINT_ENUM, BINDING_STRING_CHANNEL_TYPE_INFO))); ClassDB::bind_method(D_METHOD("get_channel_index_info", "channel_type"), &TerraWorld::get_channel_index_info); ClassDB::bind_method(D_METHOD("_get_channel_index_info", "channel_type"), &TerraWorld::_get_channel_index_info); BIND_VMETHOD(MethodInfo("_set_voxel_with_tool", PropertyInfo(Variant::BOOL, "mode_add"), PropertyInfo(Variant::VECTOR3, "hit_position"), PropertyInfo(Variant::VECTOR3, "hit_normal"), PropertyInfo(Variant::INT, "selected_voxel"), PropertyInfo(Variant::INT, "isolevel"))); ClassDB::bind_method(D_METHOD("set_voxel_with_tool", "mode_add", "hit_position", "hit_normal", "selected_voxel", "isolevel"), &TerraWorld::set_voxel_with_tool); ClassDB::bind_method(D_METHOD("_set_voxel_with_tool", "mode_add", "hit_position", "hit_normal", "selected_voxel", "isolevel"), &TerraWorld::_set_voxel_with_tool); BIND_ENUM_CONSTANT(CHANNEL_TYPE_INFO_TYPE); BIND_ENUM_CONSTANT(CHANNEL_TYPE_INFO_ISOLEVEL); BIND_ENUM_CONSTANT(CHANNEL_TYPE_INFO_LIQUID_FLOW); }