Show terrain in editor, with option to turn it off. A scripted stream will turn it off by default.
This commit is contained in:
parent
27180e11e6
commit
23ce65d789
1
SCsub
1
SCsub
@ -19,6 +19,7 @@ files = [
|
|||||||
"math/*.cpp",
|
"math/*.cpp",
|
||||||
"edition/*.cpp",
|
"edition/*.cpp",
|
||||||
"editor/graph/*.cpp",
|
"editor/graph/*.cpp",
|
||||||
|
"editor/terrain/*.cpp",
|
||||||
"thirdparty/lz4/*.c"
|
"thirdparty/lz4/*.c"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
49
editor/terrain/voxel_terrain_editor_plugin.cpp
Normal file
49
editor/terrain/voxel_terrain_editor_plugin.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "voxel_terrain_editor_plugin.h"
|
||||||
|
#include "../../terrain/voxel_lod_terrain.h"
|
||||||
|
#include "../../terrain/voxel_terrain.h"
|
||||||
|
|
||||||
|
VoxelTerrainEditorPlugin::VoxelTerrainEditorPlugin(EditorNode *p_node) {
|
||||||
|
_restart_stream_button = memnew(Button);
|
||||||
|
_restart_stream_button->set_text(TTR("Re-generate"));
|
||||||
|
_restart_stream_button->connect("pressed", this, "_on_restart_stream_button_pressed");
|
||||||
|
_restart_stream_button->hide();
|
||||||
|
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, _restart_stream_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoxelTerrainEditorPlugin::handles(Object *p_object) const {
|
||||||
|
VoxelTerrain *terrain = Object::cast_to<VoxelTerrain>(p_object);
|
||||||
|
if (terrain != nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
VoxelLodTerrain *terrain2 = Object::cast_to<VoxelLodTerrain>(p_object);
|
||||||
|
return terrain2 != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelTerrainEditorPlugin::edit(Object *p_object) {
|
||||||
|
_node = Object::cast_to<Node>(p_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelTerrainEditorPlugin::make_visible(bool visible) {
|
||||||
|
_restart_stream_button->set_visible(visible);
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
_node = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelTerrainEditorPlugin::_on_restart_stream_button_pressed() {
|
||||||
|
ERR_FAIL_COND(_node == nullptr);
|
||||||
|
VoxelTerrain *terrain = Object::cast_to<VoxelTerrain>(_node);
|
||||||
|
if (terrain != nullptr) {
|
||||||
|
terrain->restart_stream();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VoxelLodTerrain *terrain2 = Object::cast_to<VoxelLodTerrain>(_node);
|
||||||
|
ERR_FAIL_COND(terrain2 == nullptr);
|
||||||
|
terrain2->restart_stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelTerrainEditorPlugin::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("_on_restart_stream_button_pressed"),
|
||||||
|
&VoxelTerrainEditorPlugin::_on_restart_stream_button_pressed);
|
||||||
|
}
|
26
editor/terrain/voxel_terrain_editor_plugin.h
Normal file
26
editor/terrain/voxel_terrain_editor_plugin.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef VOXEL_TERRAIN_EDITOR_PLUGIN_H
|
||||||
|
#define VOXEL_TERRAIN_EDITOR_PLUGIN_H
|
||||||
|
|
||||||
|
#include <editor/editor_plugin.h>
|
||||||
|
|
||||||
|
class Button;
|
||||||
|
|
||||||
|
class VoxelTerrainEditorPlugin : public EditorPlugin {
|
||||||
|
GDCLASS(VoxelTerrainEditorPlugin, EditorPlugin)
|
||||||
|
public:
|
||||||
|
VoxelTerrainEditorPlugin(EditorNode *p_node);
|
||||||
|
|
||||||
|
bool handles(Object *p_object) const override;
|
||||||
|
void edit(Object *p_object) override;
|
||||||
|
void make_visible(bool visible) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _on_restart_stream_button_pressed();
|
||||||
|
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
Button *_restart_stream_button = nullptr;
|
||||||
|
Node *_node = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VOXEL_TERRAIN_EDITOR_PLUGIN_H
|
@ -340,6 +340,8 @@ Ref<Resource> VoxelGeneratorGraph::duplicate(bool p_subresources) const {
|
|||||||
d->_graph.copy_from(_graph, p_subresources);
|
d->_graph.copy_from(_graph, p_subresources);
|
||||||
// Program not copied, as it may contain pointers to the resources we are duplicating
|
// Program not copied, as it may contain pointers to the resources we are duplicating
|
||||||
|
|
||||||
|
d->compile();
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +592,6 @@ bool VoxelGeneratorGraph::_set(const StringName &p_name, const Variant &p_value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool VoxelGeneratorGraph::_get(const StringName &p_name, Variant &r_ret) const {
|
bool VoxelGeneratorGraph::_get(const StringName &p_name, Variant &r_ret) const {
|
||||||
|
|
||||||
const String name = p_name;
|
const String name = p_name;
|
||||||
|
|
||||||
struct L {
|
struct L {
|
||||||
@ -637,7 +638,6 @@ bool VoxelGeneratorGraph::_get(const StringName &p_name, Variant &r_ret) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelGeneratorGraph::_get_property_list(List<PropertyInfo> *p_list) const {
|
void VoxelGeneratorGraph::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
|
||||||
p_list->push_back(PropertyInfo(Variant::INT, "bounds/type", PROPERTY_HINT_ENUM, "None,Vertical,Box"));
|
p_list->push_back(PropertyInfo(Variant::INT, "bounds/type", PROPERTY_HINT_ENUM, "None,Vertical,Box"));
|
||||||
|
|
||||||
switch (_bounds.type) {
|
switch (_bounds.type) {
|
||||||
|
@ -744,6 +744,10 @@ float VoxelGraphRuntime::generate_single(const Vector3i &position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Interval VoxelGraphRuntime::analyze_range(Vector3i min_pos, Vector3i max_pos) {
|
Interval VoxelGraphRuntime::analyze_range(Vector3i min_pos, Vector3i max_pos) {
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
ERR_FAIL_COND_V_MSG(_sdf_output_address == -1, Interval(), "The graph has no SDF output");
|
||||||
|
#endif
|
||||||
|
|
||||||
ArraySlice<float> min_memory(_memory, 0, _memory.size() / 2);
|
ArraySlice<float> min_memory(_memory, 0, _memory.size() / 2);
|
||||||
ArraySlice<float> max_memory(_memory, _memory.size() / 2, _memory.size());
|
ArraySlice<float> max_memory(_memory, _memory.size() / 2, _memory.size());
|
||||||
min_memory[0] = min_pos.x;
|
min_memory[0] = min_pos.x;
|
||||||
@ -755,7 +759,6 @@ Interval VoxelGraphRuntime::analyze_range(Vector3i min_pos, Vector3i max_pos) {
|
|||||||
|
|
||||||
uint32_t pc = 0;
|
uint32_t pc = 0;
|
||||||
while (pc < _program.size()) {
|
while (pc < _program.size()) {
|
||||||
|
|
||||||
const uint8_t opid = _program[pc++];
|
const uint8_t opid = _program[pc++];
|
||||||
|
|
||||||
switch (opid) {
|
switch (opid) {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "edition/voxel_tool_terrain.h"
|
#include "edition/voxel_tool_terrain.h"
|
||||||
#include "editor/editor_plugin.h"
|
#include "editor/editor_plugin.h"
|
||||||
#include "editor/graph/voxel_graph_editor_plugin.h"
|
#include "editor/graph/voxel_graph_editor_plugin.h"
|
||||||
|
#include "editor/terrain/voxel_terrain_editor_plugin.h"
|
||||||
#include "generators/graph/voxel_generator_graph.h"
|
#include "generators/graph/voxel_generator_graph.h"
|
||||||
#include "generators/graph/voxel_graph_node_db.h"
|
#include "generators/graph/voxel_graph_node_db.h"
|
||||||
#include "generators/voxel_generator_flat.h"
|
#include "generators/voxel_generator_flat.h"
|
||||||
@ -79,6 +80,7 @@ void register_voxel_types() {
|
|||||||
VoxelDebug::create_debug_box_mesh();
|
VoxelDebug::create_debug_box_mesh();
|
||||||
|
|
||||||
EditorPlugins::add_by_type<VoxelGraphEditorPlugin>();
|
EditorPlugins::add_by_type<VoxelGraphEditorPlugin>();
|
||||||
|
EditorPlugins::add_by_type<VoxelTerrainEditorPlugin>();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "voxel_stream.h"
|
#include "voxel_stream.h"
|
||||||
#include "../voxel_string_names.h"
|
#include "../voxel_string_names.h"
|
||||||
|
#include <core/script_language.h>
|
||||||
|
|
||||||
VoxelStream::VoxelStream() {
|
VoxelStream::VoxelStream() {
|
||||||
}
|
}
|
||||||
@ -63,6 +64,11 @@ VoxelStream::Stats VoxelStream::get_statistics() const {
|
|||||||
return _stats;
|
return _stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VoxelStream::has_script() const {
|
||||||
|
Ref<Script> s = get_script();
|
||||||
|
return s.is_valid();
|
||||||
|
}
|
||||||
|
|
||||||
void VoxelStream::_bind_methods() {
|
void VoxelStream::_bind_methods() {
|
||||||
// TODO Make these proper virtual, it confuses C# bindings
|
// TODO Make these proper virtual, it confuses C# bindings
|
||||||
// Note: C++ inheriting classes don't need to re-bind these, because they are bindings that call the actual virtual methods
|
// Note: C++ inheriting classes don't need to re-bind these, because they are bindings that call the actual virtual methods
|
||||||
|
@ -47,6 +47,8 @@ public:
|
|||||||
|
|
||||||
Stats get_statistics() const;
|
Stats get_statistics() const;
|
||||||
|
|
||||||
|
virtual bool has_script() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
@ -74,8 +74,14 @@ Vector3 VoxelStreamFile::_get_block_size() const {
|
|||||||
return Vector3i(1 << get_block_size_po2()).to_vec3();
|
return Vector3i(1 << get_block_size_po2()).to_vec3();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelStreamFile::_bind_methods() {
|
bool VoxelStreamFile::has_script() const {
|
||||||
|
if (_fallback_stream.is_valid()) {
|
||||||
|
return _fallback_stream->has_script();
|
||||||
|
}
|
||||||
|
return VoxelStream::has_script();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelStreamFile::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_save_fallback_output", "enabled"), &VoxelStreamFile::set_save_fallback_output);
|
ClassDB::bind_method(D_METHOD("set_save_fallback_output", "enabled"), &VoxelStreamFile::set_save_fallback_output);
|
||||||
ClassDB::bind_method(D_METHOD("get_save_fallback_output"), &VoxelStreamFile::get_save_fallback_output);
|
ClassDB::bind_method(D_METHOD("get_save_fallback_output"), &VoxelStreamFile::get_save_fallback_output);
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ public:
|
|||||||
|
|
||||||
int get_used_channels_mask() const override;
|
int get_used_channels_mask() const override;
|
||||||
|
|
||||||
|
bool has_script() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
@ -7,6 +7,12 @@ VoxelDataLoader::VoxelDataLoader(unsigned int thread_count, Ref<VoxelStream> str
|
|||||||
PRINT_VERBOSE("Constructing VoxelDataLoader");
|
PRINT_VERBOSE("Constructing VoxelDataLoader");
|
||||||
CRASH_COND(stream.is_null());
|
CRASH_COND(stream.is_null());
|
||||||
|
|
||||||
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
|
// In the editor, we want extra safety.
|
||||||
|
// Duplicate all data so modifications done by the user won't impact running threads.
|
||||||
|
stream = stream->duplicate(true);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO I'm not sure it's worth to configure more than one thread for voxel streams
|
// TODO I'm not sure it's worth to configure more than one thread for voxel streams
|
||||||
|
|
||||||
FixedArray<Mgr::BlockProcessingFunc, Mgr::MAX_JOBS> processors;
|
FixedArray<Mgr::BlockProcessingFunc, Mgr::MAX_JOBS> processors;
|
||||||
|
@ -82,11 +82,18 @@ VoxelLodTerrain::~VoxelLodTerrain() {
|
|||||||
String VoxelLodTerrain::get_configuration_warning() const {
|
String VoxelLodTerrain::get_configuration_warning() const {
|
||||||
if (_stream.is_valid()) {
|
if (_stream.is_valid()) {
|
||||||
Ref<Script> script = _stream->get_script();
|
Ref<Script> script = _stream->get_script();
|
||||||
if (script.is_valid() && !script->is_tool()) {
|
if (script.is_valid()) {
|
||||||
return TTR("The custom stream is not tool, the editor won't be able to use it.");
|
if (script->is_tool()) {
|
||||||
|
// TODO This is very annoying. Probably needs an issue or proposal in Godot so we can handle this properly?
|
||||||
|
return TTR("Be careful! Don't edit your custom stream while it's running, it can cause crashes. "
|
||||||
|
"Turn off `run_stream_in_editor` before doing so.");
|
||||||
|
} else {
|
||||||
|
return TTR("The custom stream is not tool, the editor won't be able to use it.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!(_stream->get_used_channels_mask() & (1 << VoxelBuffer::CHANNEL_SDF))) {
|
if (!(_stream->get_used_channels_mask() & (1 << VoxelBuffer::CHANNEL_SDF))) {
|
||||||
return TTR("VoxelLodTerrain supports only stream channel \"Sdf\" (smooth).");
|
return TTR("VoxelLodTerrain supports only stream channel \"Sdf\" (smooth), "
|
||||||
|
"but `get_used_channels_mask()` tells it's providing none of these.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return String();
|
return String();
|
||||||
@ -113,44 +120,46 @@ unsigned int VoxelLodTerrain::get_block_size_pow2() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::set_stream(Ref<VoxelStream> p_stream) {
|
void VoxelLodTerrain::set_stream(Ref<VoxelStream> p_stream) {
|
||||||
|
|
||||||
if (p_stream == _stream) {
|
if (p_stream == _stream) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_stream.is_valid()) {
|
|
||||||
if (_stream->is_connected(CoreStringNames::get_singleton()->changed, this, "_on_stream_params_changed")) {
|
|
||||||
_stream->disconnect(CoreStringNames::get_singleton()->changed, this, "_on_stream_params_changed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_stream = p_stream;
|
_stream = p_stream;
|
||||||
|
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
if (_stream.is_valid()) {
|
if (_stream.is_valid()) {
|
||||||
_stream->connect(CoreStringNames::get_singleton()->changed, this, "_on_stream_params_changed");
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
|
if (_stream->has_script()) {
|
||||||
|
// Safety check. It's too easy to break threads by making a script reload.
|
||||||
|
// You can turn it back on, but be careful.
|
||||||
|
_run_stream_in_editor = false;
|
||||||
|
_change_notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
_on_stream_params_changed();
|
_on_stream_params_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::_on_stream_params_changed() {
|
void VoxelLodTerrain::_on_stream_params_changed() {
|
||||||
|
|
||||||
stop_streamer();
|
stop_streamer();
|
||||||
|
|
||||||
bool was_updater_running = _block_updater != nullptr;
|
const bool was_updater_running = _block_updater != nullptr;
|
||||||
stop_updater();
|
stop_updater();
|
||||||
|
|
||||||
Ref<VoxelStreamFile> file_stream = _stream;
|
Ref<VoxelStreamFile> file_stream = _stream;
|
||||||
if (file_stream.is_valid()) {
|
if (file_stream.is_valid()) {
|
||||||
|
const int stream_block_size_po2 = file_stream->get_block_size_po2();
|
||||||
int stream_block_size_po2 = file_stream->get_block_size_po2();
|
|
||||||
_set_block_size_po2(stream_block_size_po2);
|
_set_block_size_po2(stream_block_size_po2);
|
||||||
|
|
||||||
int stream_lod_count = file_stream->get_lod_count();
|
const int stream_lod_count = file_stream->get_lod_count();
|
||||||
_set_lod_count(min(stream_lod_count, get_lod_count()));
|
_set_lod_count(min(stream_lod_count, get_lod_count()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_stream.is_valid()) {
|
reset_maps();
|
||||||
|
|
||||||
|
if (_stream.is_valid() && (!Engine::get_singleton()->is_editor_hint() || _run_stream_in_editor)) {
|
||||||
start_streamer();
|
start_streamer();
|
||||||
}
|
}
|
||||||
if (was_updater_running) {
|
if (was_updater_running) {
|
||||||
@ -168,7 +177,6 @@ void VoxelLodTerrain::_on_stream_params_changed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::set_block_size_po2(unsigned int p_block_size_po2) {
|
void VoxelLodTerrain::set_block_size_po2(unsigned int p_block_size_po2) {
|
||||||
|
|
||||||
ERR_FAIL_COND(p_block_size_po2 < 1);
|
ERR_FAIL_COND(p_block_size_po2 < 1);
|
||||||
ERR_FAIL_COND(p_block_size_po2 > 32);
|
ERR_FAIL_COND(p_block_size_po2 > 32);
|
||||||
|
|
||||||
@ -182,20 +190,7 @@ void VoxelLodTerrain::set_block_size_po2(unsigned int p_block_size_po2) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool updater_was_running = _block_updater != nullptr;
|
_on_stream_params_changed();
|
||||||
|
|
||||||
stop_streamer();
|
|
||||||
stop_updater();
|
|
||||||
|
|
||||||
_set_block_size_po2(p_block_size_po2);
|
|
||||||
reset_maps();
|
|
||||||
|
|
||||||
if (_stream.is_valid()) {
|
|
||||||
start_streamer();
|
|
||||||
}
|
|
||||||
if (updater_was_running) {
|
|
||||||
start_updater();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::_set_block_size_po2(int p_block_size_po2) {
|
void VoxelLodTerrain::_set_block_size_po2(int p_block_size_po2) {
|
||||||
@ -240,10 +235,8 @@ int VoxelLodTerrain::get_view_distance() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::set_view_distance(int p_distance_in_voxels) {
|
void VoxelLodTerrain::set_view_distance(int p_distance_in_voxels) {
|
||||||
|
|
||||||
ERR_FAIL_COND(p_distance_in_voxels <= 0);
|
ERR_FAIL_COND(p_distance_in_voxels <= 0);
|
||||||
ERR_FAIL_COND(p_distance_in_voxels > 8192);
|
ERR_FAIL_COND(p_distance_in_voxels > 8192);
|
||||||
|
|
||||||
// Note: this is a hint distance, the terrain will attempt to have this radius filled with loaded voxels.
|
// Note: this is a hint distance, the terrain will attempt to have this radius filled with loaded voxels.
|
||||||
// It is possible for blocks to still load beyond that distance.
|
// It is possible for blocks to still load beyond that distance.
|
||||||
_view_distance_voxels = p_distance_in_voxels;
|
_view_distance_voxels = p_distance_in_voxels;
|
||||||
@ -264,7 +257,6 @@ Spatial *VoxelLodTerrain::get_viewer() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::start_updater() {
|
void VoxelLodTerrain::start_updater() {
|
||||||
|
|
||||||
ERR_FAIL_COND(_block_updater != nullptr);
|
ERR_FAIL_COND(_block_updater != nullptr);
|
||||||
|
|
||||||
// TODO Thread-safe way to change those parameters
|
// TODO Thread-safe way to change those parameters
|
||||||
@ -275,7 +267,6 @@ void VoxelLodTerrain::start_updater() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::stop_updater() {
|
void VoxelLodTerrain::stop_updater() {
|
||||||
|
|
||||||
struct ResetMeshStateAction {
|
struct ResetMeshStateAction {
|
||||||
void operator()(VoxelBlock *block) {
|
void operator()(VoxelBlock *block) {
|
||||||
if (block->get_mesh_state() == VoxelBlock::MESH_UPDATE_SENT) {
|
if (block->get_mesh_state() == VoxelBlock::MESH_UPDATE_SENT) {
|
||||||
@ -292,7 +283,6 @@ void VoxelLodTerrain::stop_updater() {
|
|||||||
_blocks_pending_main_thread_update.clear();
|
_blocks_pending_main_thread_update.clear();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < _lods.size(); ++i) {
|
for (unsigned int i = 0; i < _lods.size(); ++i) {
|
||||||
|
|
||||||
Lod &lod = _lods[i];
|
Lod &lod = _lods[i];
|
||||||
lod.blocks_pending_update.clear();
|
lod.blocks_pending_update.clear();
|
||||||
|
|
||||||
@ -304,7 +294,6 @@ void VoxelLodTerrain::stop_updater() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::start_streamer() {
|
void VoxelLodTerrain::start_streamer() {
|
||||||
|
|
||||||
ERR_FAIL_COND(_stream_thread != nullptr);
|
ERR_FAIL_COND(_stream_thread != nullptr);
|
||||||
ERR_FAIL_COND(_stream.is_null());
|
ERR_FAIL_COND(_stream.is_null());
|
||||||
|
|
||||||
@ -312,7 +301,6 @@ void VoxelLodTerrain::start_streamer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::stop_streamer() {
|
void VoxelLodTerrain::stop_streamer() {
|
||||||
|
|
||||||
if (_stream_thread) {
|
if (_stream_thread) {
|
||||||
memdelete(_stream_thread);
|
memdelete(_stream_thread);
|
||||||
_stream_thread = nullptr;
|
_stream_thread = nullptr;
|
||||||
@ -325,7 +313,6 @@ void VoxelLodTerrain::stop_streamer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::set_lod_split_scale(float p_lod_split_scale) {
|
void VoxelLodTerrain::set_lod_split_scale(float p_lod_split_scale) {
|
||||||
|
|
||||||
if (p_lod_split_scale == _lod_split_scale) {
|
if (p_lod_split_scale == _lod_split_scale) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -333,7 +320,6 @@ void VoxelLodTerrain::set_lod_split_scale(float p_lod_split_scale) {
|
|||||||
_lod_split_scale = CLAMP(p_lod_split_scale, VoxelConstants::MINIMUM_LOD_SPLIT_SCALE, VoxelConstants::MAXIMUM_LOD_SPLIT_SCALE);
|
_lod_split_scale = CLAMP(p_lod_split_scale, VoxelConstants::MINIMUM_LOD_SPLIT_SCALE, VoxelConstants::MAXIMUM_LOD_SPLIT_SCALE);
|
||||||
|
|
||||||
for (Map<Vector3i, OctreeItem>::Element *E = _lod_octrees.front(); E; E = E->next()) {
|
for (Map<Vector3i, OctreeItem>::Element *E = _lod_octrees.front(); E; E = E->next()) {
|
||||||
|
|
||||||
OctreeItem &item = E->value();
|
OctreeItem &item = E->value();
|
||||||
item.octree.set_split_scale(_lod_split_scale);
|
item.octree.set_split_scale(_lod_split_scale);
|
||||||
|
|
||||||
@ -347,7 +333,6 @@ float VoxelLodTerrain::get_lod_split_scale() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::set_lod_count(int p_lod_count) {
|
void VoxelLodTerrain::set_lod_count(int p_lod_count) {
|
||||||
|
|
||||||
ERR_FAIL_COND(p_lod_count >= (int)VoxelConstants::MAX_LOD);
|
ERR_FAIL_COND(p_lod_count >= (int)VoxelConstants::MAX_LOD);
|
||||||
ERR_FAIL_COND(p_lod_count < 1);
|
ERR_FAIL_COND(p_lod_count < 1);
|
||||||
|
|
||||||
@ -357,7 +342,6 @@ void VoxelLodTerrain::set_lod_count(int p_lod_count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::_set_lod_count(int p_lod_count) {
|
void VoxelLodTerrain::_set_lod_count(int p_lod_count) {
|
||||||
|
|
||||||
CRASH_COND(p_lod_count >= (int)VoxelConstants::MAX_LOD);
|
CRASH_COND(p_lod_count >= (int)VoxelConstants::MAX_LOD);
|
||||||
CRASH_COND(p_lod_count < 1);
|
CRASH_COND(p_lod_count < 1);
|
||||||
|
|
||||||
@ -376,25 +360,31 @@ void VoxelLodTerrain::_set_lod_count(int p_lod_count) {
|
|||||||
void VoxelLodTerrain::reset_maps() {
|
void VoxelLodTerrain::reset_maps() {
|
||||||
// Clears all blocks and reconfigures maps to account for new LOD count and block sizes
|
// Clears all blocks and reconfigures maps to account for new LOD count and block sizes
|
||||||
|
|
||||||
for (int lod_index = 0; lod_index < (int)_lods.size(); ++lod_index) {
|
// Don't reset while streaming, the result can be dirty?
|
||||||
|
//CRASH_COND(_stream_thread != nullptr);
|
||||||
|
|
||||||
|
for (int lod_index = 0; lod_index < (int)_lods.size(); ++lod_index) {
|
||||||
Lod &lod = _lods[lod_index];
|
Lod &lod = _lods[lod_index];
|
||||||
|
|
||||||
// Instance new maps if we have more lods, or clear them otherwise
|
// Instance new maps if we have more lods, or clear them otherwise
|
||||||
if (lod_index < get_lod_count()) {
|
if (lod_index < get_lod_count()) {
|
||||||
|
|
||||||
if (lod.map.is_null()) {
|
if (lod.map.is_null()) {
|
||||||
lod.map.instance();
|
lod.map.instance();
|
||||||
}
|
}
|
||||||
lod.map->create(get_block_size_pow2(), lod_index);
|
lod.map->create(get_block_size_pow2(), lod_index);
|
||||||
|
|
||||||
} else {
|
lod.last_view_distance_blocks = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
if (lod.map.is_valid()) {
|
if (lod.map.is_valid()) {
|
||||||
lod.map.unref();
|
lod.map.unref();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset previous state caches to force rebuilding the view area
|
||||||
|
_last_octree_region_box = Rect3i();
|
||||||
|
_lod_octrees.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int VoxelLodTerrain::get_lod_count() const {
|
int VoxelLodTerrain::get_lod_count() const {
|
||||||
@ -437,9 +427,7 @@ Vector3 VoxelLodTerrain::voxel_to_block_position(Vector3 vpos, int lod_index) co
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::_notification(int p_what) {
|
void VoxelLodTerrain::_notification(int p_what) {
|
||||||
|
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
|
|
||||||
case NOTIFICATION_ENTER_TREE:
|
case NOTIFICATION_ENTER_TREE:
|
||||||
if (_block_updater == nullptr) {
|
if (_block_updater == nullptr) {
|
||||||
start_updater();
|
start_updater();
|
||||||
@ -448,9 +436,7 @@ void VoxelLodTerrain::_notification(int p_what) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NOTIFICATION_PROCESS:
|
case NOTIFICATION_PROCESS:
|
||||||
if (!Engine::get_singleton()->is_editor_hint()) {
|
_process();
|
||||||
_process();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NOTIFICATION_EXIT_TREE:
|
case NOTIFICATION_EXIT_TREE:
|
||||||
@ -496,9 +482,7 @@ void VoxelLodTerrain::_notification(int p_what) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::get_viewer_pos_and_direction(Vector3 &out_pos, Vector3 &out_direction) const {
|
void VoxelLodTerrain::get_viewer_pos_and_direction(Vector3 &out_pos, Vector3 &out_direction) const {
|
||||||
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
|
|
||||||
// TODO Use editor's camera here
|
// TODO Use editor's camera here
|
||||||
out_pos = Vector3();
|
out_pos = Vector3();
|
||||||
out_direction = Vector3(0, -1, 0);
|
out_direction = Vector3(0, -1, 0);
|
||||||
@ -507,13 +491,11 @@ void VoxelLodTerrain::get_viewer_pos_and_direction(Vector3 &out_pos, Vector3 &ou
|
|||||||
// TODO Have option to use viewport camera
|
// TODO Have option to use viewport camera
|
||||||
Spatial *viewer = get_viewer();
|
Spatial *viewer = get_viewer();
|
||||||
if (viewer) {
|
if (viewer) {
|
||||||
|
|
||||||
Transform gt = viewer->get_global_transform();
|
Transform gt = viewer->get_global_transform();
|
||||||
out_pos = gt.origin;
|
out_pos = gt.origin;
|
||||||
out_direction = -gt.basis.get_axis(Vector3::AXIS_Z);
|
out_direction = -gt.basis.get_axis(Vector3::AXIS_Z);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// TODO Just remember last viewer pos
|
// TODO Just remember last viewer pos
|
||||||
out_pos = (_lods[0].last_viewer_block_pos << _lods[0].map->get_block_size_pow2()).to_vec3();
|
out_pos = (_lods[0].last_viewer_block_pos << _lods[0].map->get_block_size_pow2()).to_vec3();
|
||||||
out_direction = Vector3(0, -1, 0);
|
out_direction = Vector3(0, -1, 0);
|
||||||
@ -591,7 +573,6 @@ bool VoxelLodTerrain::check_block_mesh_updated(VoxelBlock *block) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::send_block_data_requests() {
|
void VoxelLodTerrain::send_block_data_requests() {
|
||||||
|
|
||||||
VoxelDataLoader::Input input;
|
VoxelDataLoader::Input input;
|
||||||
|
|
||||||
Vector3 viewer_pos;
|
Vector3 viewer_pos;
|
||||||
@ -665,7 +646,7 @@ void VoxelLodTerrain::_process() {
|
|||||||
// TODO Could it actually be enough to have a rolling update on all blocks?
|
// TODO Could it actually be enough to have a rolling update on all blocks?
|
||||||
|
|
||||||
// This should be the same distance relatively to each LOD
|
// This should be the same distance relatively to each LOD
|
||||||
int block_region_extent = get_block_region_extent();
|
const int block_region_extent = get_block_region_extent();
|
||||||
|
|
||||||
// Ignore last lod because it can extend a little beyond due to the view distance setting.
|
// Ignore last lod because it can extend a little beyond due to the view distance setting.
|
||||||
// Instead, those blocks are unloaded by the octree forest management.
|
// Instead, those blocks are unloaded by the octree forest management.
|
||||||
@ -828,7 +809,6 @@ void VoxelLodTerrain::_process() {
|
|||||||
|
|
||||||
// TODO Maintain a vector to make iteration faster?
|
// TODO Maintain a vector to make iteration faster?
|
||||||
for (Map<Vector3i, OctreeItem>::Element *E = _lod_octrees.front(); E; E = E->next()) {
|
for (Map<Vector3i, OctreeItem>::Element *E = _lod_octrees.front(); E; E = E->next()) {
|
||||||
|
|
||||||
VOXEL_PROFILE_SCOPE(profile_process_update_octrees_item);
|
VOXEL_PROFILE_SCOPE(profile_process_update_octrees_item);
|
||||||
|
|
||||||
OctreeItem &item = E->value();
|
OctreeItem &item = E->value();
|
||||||
@ -836,7 +816,6 @@ void VoxelLodTerrain::_process() {
|
|||||||
Vector3i block_offset_lod0 = block_pos_maxlod << (get_lod_count() - 1);
|
Vector3i block_offset_lod0 = block_pos_maxlod << (get_lod_count() - 1);
|
||||||
|
|
||||||
struct OctreeActions {
|
struct OctreeActions {
|
||||||
|
|
||||||
VoxelLodTerrain *self = nullptr;
|
VoxelLodTerrain *self = nullptr;
|
||||||
Vector3i block_offset_lod0;
|
Vector3i block_offset_lod0;
|
||||||
unsigned int blocked_count = 0;
|
unsigned int blocked_count = 0;
|
||||||
@ -1076,12 +1055,10 @@ void VoxelLodTerrain::_process() {
|
|||||||
input.exclusive_region_extent = get_block_region_extent();
|
input.exclusive_region_extent = get_block_region_extent();
|
||||||
|
|
||||||
for (int lod_index = 0; lod_index < get_lod_count(); ++lod_index) {
|
for (int lod_index = 0; lod_index < get_lod_count(); ++lod_index) {
|
||||||
|
|
||||||
VOXEL_PROFILE_SCOPE(profile_process_send_mesh_updates_lod);
|
VOXEL_PROFILE_SCOPE(profile_process_send_mesh_updates_lod);
|
||||||
Lod &lod = _lods[lod_index];
|
Lod &lod = _lods[lod_index];
|
||||||
|
|
||||||
for (unsigned int i = 0; i < lod.blocks_pending_update.size(); ++i) {
|
for (unsigned int i = 0; i < lod.blocks_pending_update.size(); ++i) {
|
||||||
|
|
||||||
VOXEL_PROFILE_SCOPE(profile_process_send_mesh_updates_block);
|
VOXEL_PROFILE_SCOPE(profile_process_send_mesh_updates_block);
|
||||||
Vector3i block_pos = lod.blocks_pending_update[i];
|
Vector3i block_pos = lod.blocks_pending_update[i];
|
||||||
|
|
||||||
@ -1140,7 +1117,6 @@ void VoxelLodTerrain::_process() {
|
|||||||
_stats.updated_blocks = output.blocks.size();
|
_stats.updated_blocks = output.blocks.size();
|
||||||
|
|
||||||
for (int i = 0; i < output.blocks.size(); ++i) {
|
for (int i = 0; i < output.blocks.size(); ++i) {
|
||||||
|
|
||||||
VOXEL_PROFILE_SCOPE(profile_process_receive_mesh_updates_block_schedule);
|
VOXEL_PROFILE_SCOPE(profile_process_receive_mesh_updates_block_schedule);
|
||||||
const VoxelMeshUpdater::OutputBlock &ob = output.blocks[i];
|
const VoxelMeshUpdater::OutputBlock &ob = output.blocks[i];
|
||||||
|
|
||||||
@ -1162,7 +1138,6 @@ void VoxelLodTerrain::_process() {
|
|||||||
// hopefully Vulkan will allow us to upload graphical resources without stalling rendering as they upload?
|
// hopefully Vulkan will allow us to upload graphical resources without stalling rendering as they upload?
|
||||||
|
|
||||||
for (; queue_index < _blocks_pending_main_thread_update.size() && os.get_ticks_msec() < timeout; ++queue_index) {
|
for (; queue_index < _blocks_pending_main_thread_update.size() && os.get_ticks_msec() < timeout; ++queue_index) {
|
||||||
|
|
||||||
VOXEL_PROFILE_SCOPE(profile_process_receive_mesh_updates_block_update);
|
VOXEL_PROFILE_SCOPE(profile_process_receive_mesh_updates_block_update);
|
||||||
|
|
||||||
const VoxelMeshUpdater::OutputBlock &ob = _blocks_pending_main_thread_update[queue_index];
|
const VoxelMeshUpdater::OutputBlock &ob = _blocks_pending_main_thread_update[queue_index];
|
||||||
@ -1319,13 +1294,11 @@ void VoxelLodTerrain::flush_pending_lod_edits() {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct ScheduleSaveAction {
|
struct ScheduleSaveAction {
|
||||||
|
|
||||||
std::vector<VoxelDataLoader::InputBlock> &blocks_to_save;
|
std::vector<VoxelDataLoader::InputBlock> &blocks_to_save;
|
||||||
std::vector<Ref<ShaderMaterial> > &shader_materials;
|
std::vector<Ref<ShaderMaterial> > &shader_materials;
|
||||||
bool with_copy;
|
bool with_copy;
|
||||||
|
|
||||||
void operator()(VoxelBlock *block) {
|
void operator()(VoxelBlock *block) {
|
||||||
|
|
||||||
Ref<ShaderMaterial> sm = block->get_shader_material();
|
Ref<ShaderMaterial> sm = block->get_shader_material();
|
||||||
if (sm.is_valid()) {
|
if (sm.is_valid()) {
|
||||||
shader_materials.push_back(sm);
|
shader_materials.push_back(sm);
|
||||||
@ -1524,6 +1497,32 @@ Dictionary VoxelLodTerrain::get_statistics() const {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoxelLodTerrain::set_run_stream_in_editor(bool enable) {
|
||||||
|
if (enable == _run_stream_in_editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_run_stream_in_editor = enable;
|
||||||
|
|
||||||
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
|
if (_run_stream_in_editor) {
|
||||||
|
_on_stream_params_changed();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// This is expected to block the main thread until the streaming thread is done.
|
||||||
|
stop_streamer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoxelLodTerrain::is_stream_running_in_editor() const {
|
||||||
|
return _run_stream_in_editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelLodTerrain::restart_stream() {
|
||||||
|
_on_stream_params_changed();
|
||||||
|
}
|
||||||
|
|
||||||
void VoxelLodTerrain::_b_save_modified_blocks() {
|
void VoxelLodTerrain::_b_save_modified_blocks() {
|
||||||
save_all_modified_blocks(true);
|
save_all_modified_blocks(true);
|
||||||
}
|
}
|
||||||
@ -1662,12 +1661,15 @@ void VoxelLodTerrain::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("get_voxel_tool"), &VoxelLodTerrain::get_voxel_tool);
|
ClassDB::bind_method(D_METHOD("get_voxel_tool"), &VoxelLodTerrain::get_voxel_tool);
|
||||||
ClassDB::bind_method(D_METHOD("save_modified_blocks"), &VoxelLodTerrain::_b_save_modified_blocks);
|
ClassDB::bind_method(D_METHOD("save_modified_blocks"), &VoxelLodTerrain::_b_save_modified_blocks);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_run_stream_in_editor"), &VoxelLodTerrain::set_run_stream_in_editor);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_stream_running_in_editor"), &VoxelLodTerrain::is_stream_running_in_editor);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("debug_raycast_block", "origin", "dir"), &VoxelLodTerrain::debug_raycast_block);
|
ClassDB::bind_method(D_METHOD("debug_raycast_block", "origin", "dir"), &VoxelLodTerrain::debug_raycast_block);
|
||||||
ClassDB::bind_method(D_METHOD("debug_get_block_info", "block_pos", "lod"), &VoxelLodTerrain::debug_get_block_info);
|
ClassDB::bind_method(D_METHOD("debug_get_block_info", "block_pos", "lod"), &VoxelLodTerrain::debug_get_block_info);
|
||||||
ClassDB::bind_method(D_METHOD("debug_get_octrees"), &VoxelLodTerrain::debug_get_octrees);
|
ClassDB::bind_method(D_METHOD("debug_get_octrees"), &VoxelLodTerrain::debug_get_octrees);
|
||||||
ClassDB::bind_method(D_METHOD("debug_print_sdf_top_down", "center", "extents"), &VoxelLodTerrain::_b_debug_print_sdf_top_down);
|
ClassDB::bind_method(D_METHOD("debug_print_sdf_top_down", "center", "extents"), &VoxelLodTerrain::_b_debug_print_sdf_top_down);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("_on_stream_params_changed"), &VoxelLodTerrain::_on_stream_params_changed);
|
//ClassDB::bind_method(D_METHOD("_on_stream_params_changed"), &VoxelLodTerrain::_on_stream_params_changed);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VoxelStream"), "set_stream", "get_stream");
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VoxelStream"), "set_stream", "get_stream");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "view_distance"), "set_view_distance", "get_view_distance");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "view_distance"), "set_view_distance", "get_view_distance");
|
||||||
@ -1677,4 +1679,6 @@ void VoxelLodTerrain::_bind_methods() {
|
|||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material", "get_material");
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material", "get_material");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_collisions"), "set_generate_collisions", "get_generate_collisions");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_collisions"), "set_generate_collisions", "get_generate_collisions");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_lod_count"), "set_collision_lod_count", "get_collision_lod_count");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_lod_count"), "set_collision_lod_count", "get_collision_lod_count");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "run_stream_in_editor"),
|
||||||
|
"set_run_stream_in_editor", "is_stream_running_in_editor");
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,11 @@ public:
|
|||||||
|
|
||||||
Dictionary get_statistics() const;
|
Dictionary get_statistics() const;
|
||||||
|
|
||||||
|
void set_run_stream_in_editor(bool enable);
|
||||||
|
bool is_stream_running_in_editor() const;
|
||||||
|
|
||||||
|
void restart_stream();
|
||||||
|
|
||||||
Array debug_raycast_block(Vector3 world_origin, Vector3 world_direction) const;
|
Array debug_raycast_block(Vector3 world_origin, Vector3 world_direction) const;
|
||||||
Dictionary debug_get_block_info(Vector3 fbpos, int lod_index) const;
|
Dictionary debug_get_block_info(Vector3 fbpos, int lod_index) const;
|
||||||
Array debug_get_octrees() const;
|
Array debug_get_octrees() const;
|
||||||
@ -119,13 +124,14 @@ private:
|
|||||||
void _b_save_modified_blocks();
|
void _b_save_modified_blocks();
|
||||||
Array _b_debug_print_sdf_top_down(Vector3 center, Vector3 extents) const;
|
Array _b_debug_print_sdf_top_down(Vector3 center, Vector3 extents) const;
|
||||||
|
|
||||||
|
private:
|
||||||
struct OctreeItem {
|
struct OctreeItem {
|
||||||
LodOctree octree;
|
LodOctree octree;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This terrain type is a sparse grid of octrees.
|
// This terrain type is a sparse grid of octrees.
|
||||||
// Indexed by a grid coordinate whose step is the size of the highest-LOD block
|
// Indexed by a grid coordinate whose step is the size of the highest-LOD block.
|
||||||
// This octree doesn't hold any data... hence bool.
|
// Not using a pointer because Map storage is stable.
|
||||||
Map<Vector3i, OctreeItem> _lod_octrees;
|
Map<Vector3i, OctreeItem> _lod_octrees;
|
||||||
Rect3i _last_octree_region_box;
|
Rect3i _last_octree_region_box;
|
||||||
|
|
||||||
@ -168,6 +174,8 @@ private:
|
|||||||
float _lod_split_scale = 0.f;
|
float _lod_split_scale = 0.f;
|
||||||
unsigned int _view_distance_voxels = 512;
|
unsigned int _view_distance_voxels = 512;
|
||||||
|
|
||||||
|
bool _run_stream_in_editor = true;
|
||||||
|
|
||||||
Stats _stats;
|
Stats _stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,8 +27,6 @@ VoxelTerrain::VoxelTerrain() {
|
|||||||
_stream_thread = nullptr;
|
_stream_thread = nullptr;
|
||||||
_block_updater = nullptr;
|
_block_updater = nullptr;
|
||||||
|
|
||||||
_run_in_editor = false;
|
|
||||||
|
|
||||||
Ref<VoxelLibrary> library;
|
Ref<VoxelLibrary> library;
|
||||||
library.instance();
|
library.instance();
|
||||||
set_voxel_library(library);
|
set_voxel_library(library);
|
||||||
@ -53,11 +51,18 @@ VoxelTerrain::~VoxelTerrain() {
|
|||||||
String VoxelTerrain::get_configuration_warning() const {
|
String VoxelTerrain::get_configuration_warning() const {
|
||||||
if (_stream.is_valid()) {
|
if (_stream.is_valid()) {
|
||||||
Ref<Script> script = _stream->get_script();
|
Ref<Script> script = _stream->get_script();
|
||||||
if (script.is_valid() && !script->is_tool()) {
|
if (script.is_valid()) {
|
||||||
return TTR("The custom stream is not tool, the editor won't be able to use it.");
|
if (script->is_tool()) {
|
||||||
|
// TODO This is very annoying. Probably needs an issue or proposal in Godot so we can handle this properly?
|
||||||
|
return TTR("Be careful! Don't edit your custom stream while it's running, "
|
||||||
|
"it can cause crashes. Turn off `run_stream_in_editor` before doing so.");
|
||||||
|
} else {
|
||||||
|
return TTR("The custom stream is not tool, the editor won't be able to use it.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!(_stream->get_used_channels_mask() & ((1 << VoxelBuffer::CHANNEL_TYPE) | (1 << VoxelBuffer::CHANNEL_SDF)))) {
|
if (!(_stream->get_used_channels_mask() & ((1 << VoxelBuffer::CHANNEL_TYPE) | (1 << VoxelBuffer::CHANNEL_SDF)))) {
|
||||||
return TTR("VoxelTerrain supports only stream channels \"Type\" or \"Sdf\".");
|
return TTR("VoxelTerrain supports only stream channels \"Type\" or \"Sdf\", "
|
||||||
|
"but `get_used_channels_mask()` tells it's providing none of these.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return String();
|
return String();
|
||||||
@ -98,17 +103,20 @@ void VoxelTerrain::set_stream(Ref<VoxelStream> p_stream) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_stream.is_valid()) {
|
|
||||||
if (_stream->is_connected(CoreStringNames::get_singleton()->changed, this, "_on_stream_params_changed")) {
|
|
||||||
_stream->disconnect(CoreStringNames::get_singleton()->changed, this, "_on_stream_params_changed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_stream = p_stream;
|
_stream = p_stream;
|
||||||
|
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
if (_stream.is_valid()) {
|
if (_stream.is_valid()) {
|
||||||
_stream->connect(CoreStringNames::get_singleton()->changed, this, "_on_stream_params_changed");
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
|
if (_stream->has_script()) {
|
||||||
|
// Safety check. It's too easy to break threads by making a script reload.
|
||||||
|
// You can turn it back on, but be careful.
|
||||||
|
_run_stream_in_editor = false;
|
||||||
|
_change_notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
_on_stream_params_changed();
|
_on_stream_params_changed();
|
||||||
}
|
}
|
||||||
@ -131,20 +139,7 @@ void VoxelTerrain::set_block_size_po2(unsigned int p_block_size_po2) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool updater_was_running = _block_updater != nullptr;
|
_on_stream_params_changed();
|
||||||
|
|
||||||
stop_streamer();
|
|
||||||
stop_updater();
|
|
||||||
|
|
||||||
reset_map();
|
|
||||||
_set_block_size_po2(p_block_size_po2);
|
|
||||||
|
|
||||||
if (_stream.is_valid()) {
|
|
||||||
start_streamer();
|
|
||||||
}
|
|
||||||
if (updater_was_running) {
|
|
||||||
start_updater();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelTerrain::_set_block_size_po2(int p_block_size_po2) {
|
void VoxelTerrain::_set_block_size_po2(int p_block_size_po2) {
|
||||||
@ -155,6 +150,10 @@ unsigned int VoxelTerrain::get_block_size_pow2() const {
|
|||||||
return _map->get_block_size_pow2();
|
return _map->get_block_size_pow2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoxelTerrain::restart_stream() {
|
||||||
|
_on_stream_params_changed();
|
||||||
|
}
|
||||||
|
|
||||||
void VoxelTerrain::_on_stream_params_changed() {
|
void VoxelTerrain::_on_stream_params_changed() {
|
||||||
stop_streamer();
|
stop_streamer();
|
||||||
|
|
||||||
@ -163,12 +162,14 @@ void VoxelTerrain::_on_stream_params_changed() {
|
|||||||
|
|
||||||
Ref<VoxelStreamFile> file_stream = _stream;
|
Ref<VoxelStreamFile> file_stream = _stream;
|
||||||
if (file_stream.is_valid()) {
|
if (file_stream.is_valid()) {
|
||||||
|
|
||||||
int stream_block_size_po2 = file_stream->get_block_size_po2();
|
int stream_block_size_po2 = file_stream->get_block_size_po2();
|
||||||
_set_block_size_po2(stream_block_size_po2);
|
_set_block_size_po2(stream_block_size_po2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_stream.is_valid()) {
|
// The whole map might change, so regenerate it
|
||||||
|
reset_map();
|
||||||
|
|
||||||
|
if (_stream.is_valid() && (!Engine::get_singleton()->is_editor_hint() || _run_stream_in_editor)) {
|
||||||
start_streamer();
|
start_streamer();
|
||||||
}
|
}
|
||||||
if (was_updater_running) {
|
if (was_updater_running) {
|
||||||
@ -176,10 +177,6 @@ void VoxelTerrain::_on_stream_params_changed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_configuration_warning();
|
update_configuration_warning();
|
||||||
|
|
||||||
// The whole map might change, so make all area dirty
|
|
||||||
// TODO Actually, we should regenerate the whole map, not just update all its blocks
|
|
||||||
make_all_view_dirty_deferred();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<VoxelLibrary> VoxelTerrain::get_voxel_library() const {
|
Ref<VoxelLibrary> VoxelTerrain::get_voxel_library() const {
|
||||||
@ -370,6 +367,7 @@ Dictionary VoxelTerrain::get_statistics() const {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
void VoxelTerrain::make_all_view_dirty_deferred() {
|
void VoxelTerrain::make_all_view_dirty_deferred() {
|
||||||
|
// We do this because we query blocks only when the viewer area changes.
|
||||||
// This trick will regenerate all chunks in view, according to the view distance found during block updates.
|
// This trick will regenerate all chunks in view, according to the view distance found during block updates.
|
||||||
// The point of doing this instead of immediately scheduling updates is that it will
|
// The point of doing this instead of immediately scheduling updates is that it will
|
||||||
// always use an up-to-date view distance, which is not necessarily loaded yet on initialization.
|
// always use an up-to-date view distance, which is not necessarily loaded yet on initialization.
|
||||||
@ -439,10 +437,16 @@ void VoxelTerrain::stop_streamer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelTerrain::reset_map() {
|
void VoxelTerrain::reset_map() {
|
||||||
|
// Don't reset while streaming, the result can be dirty
|
||||||
|
CRASH_COND(_stream_thread != nullptr);
|
||||||
|
|
||||||
_map->for_all_blocks([this](VoxelBlock *block) {
|
_map->for_all_blocks([this](VoxelBlock *block) {
|
||||||
emit_block_unloaded(block);
|
emit_block_unloaded(block);
|
||||||
});
|
});
|
||||||
_map->create(get_block_size_pow2(), 0);
|
_map->create(get_block_size_pow2(), 0);
|
||||||
|
|
||||||
|
// To force queries to happen again, because we only listen for viewer position changes
|
||||||
|
make_all_view_dirty_deferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int get_border_index(int x, int max) {
|
inline int get_border_index(int x, int max) {
|
||||||
@ -639,9 +643,7 @@ void VoxelTerrain::_notification(int p_what) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NOTIFICATION_PROCESS:
|
case NOTIFICATION_PROCESS:
|
||||||
if (!Engine::get_singleton()->is_editor_hint() || _run_in_editor) {
|
_process();
|
||||||
_process();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NOTIFICATION_EXIT_TREE:
|
case NOTIFICATION_EXIT_TREE:
|
||||||
@ -806,7 +808,7 @@ void VoxelTerrain::_process() {
|
|||||||
_last_view_distance_blocks = _view_distance_blocks;
|
_last_view_distance_blocks = _view_distance_blocks;
|
||||||
_last_viewer_block_pos = viewer_block_pos;
|
_last_viewer_block_pos = viewer_block_pos;
|
||||||
|
|
||||||
// It's possible the user didn't set a stream yet
|
// It's possible the user didn't set a stream yet, or it is turned off
|
||||||
if (_stream_thread != nullptr) {
|
if (_stream_thread != nullptr) {
|
||||||
send_block_data_requests();
|
send_block_data_requests();
|
||||||
}
|
}
|
||||||
@ -1097,6 +1099,28 @@ Ref<VoxelTool> VoxelTerrain::get_voxel_tool() {
|
|||||||
return vt;
|
return vt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoxelTerrain::set_run_stream_in_editor(bool enable) {
|
||||||
|
if (enable == _run_stream_in_editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_run_stream_in_editor = enable;
|
||||||
|
|
||||||
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
|
if (_run_stream_in_editor) {
|
||||||
|
_on_stream_params_changed();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// This is expected to block the main thread until the streaming thread is done.
|
||||||
|
stop_streamer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoxelTerrain::is_stream_running_in_editor() const {
|
||||||
|
return _run_stream_in_editor;
|
||||||
|
}
|
||||||
|
|
||||||
Vector3 VoxelTerrain::_b_voxel_to_block(Vector3 pos) {
|
Vector3 VoxelTerrain::_b_voxel_to_block(Vector3 pos) {
|
||||||
return Vector3i(_map->voxel_to_block(pos)).to_vec3();
|
return Vector3i(_map->voxel_to_block(pos)).to_vec3();
|
||||||
}
|
}
|
||||||
@ -1153,7 +1177,10 @@ void VoxelTerrain::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("save_modified_blocks"), &VoxelTerrain::_b_save_modified_blocks);
|
ClassDB::bind_method(D_METHOD("save_modified_blocks"), &VoxelTerrain::_b_save_modified_blocks);
|
||||||
ClassDB::bind_method(D_METHOD("save_block"), &VoxelTerrain::_b_save_block);
|
ClassDB::bind_method(D_METHOD("save_block"), &VoxelTerrain::_b_save_block);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("_on_stream_params_changed"), &VoxelTerrain::_on_stream_params_changed);
|
ClassDB::bind_method(D_METHOD("set_run_stream_in_editor"), &VoxelTerrain::set_run_stream_in_editor);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_stream_running_in_editor"), &VoxelTerrain::is_stream_running_in_editor);
|
||||||
|
|
||||||
|
//ClassDB::bind_method(D_METHOD("_on_stream_params_changed"), &VoxelTerrain::_on_stream_params_changed);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VoxelStream"),
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VoxelStream"),
|
||||||
"set_stream", "get_stream");
|
"set_stream", "get_stream");
|
||||||
@ -1163,6 +1190,8 @@ void VoxelTerrain::_bind_methods() {
|
|||||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewer_path"), "set_viewer_path", "get_viewer_path");
|
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewer_path"), "set_viewer_path", "get_viewer_path");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_collisions"),
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_collisions"),
|
||||||
"set_generate_collisions", "get_generate_collisions");
|
"set_generate_collisions", "get_generate_collisions");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "run_stream_in_editor"),
|
||||||
|
"set_run_stream_in_editor", "is_stream_running_in_editor");
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo(VoxelStringNames::get_singleton()->block_loaded,
|
ADD_SIGNAL(MethodInfo(VoxelStringNames::get_singleton()->block_loaded,
|
||||||
PropertyInfo(Variant::VECTOR3, "position"),
|
PropertyInfo(Variant::VECTOR3, "position"),
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
#ifndef VOXEL_TERRAIN_H
|
#ifndef VOXEL_TERRAIN_H
|
||||||
#define VOXEL_TERRAIN_H
|
#define VOXEL_TERRAIN_H
|
||||||
|
|
||||||
#include "../math/rect3i.h"
|
|
||||||
#include "../math/vector3i.h"
|
|
||||||
#include "../util/zprofiling.h"
|
#include "../util/zprofiling.h"
|
||||||
#include "voxel_data_loader.h"
|
#include "voxel_data_loader.h"
|
||||||
|
#include "voxel_map.h"
|
||||||
#include "voxel_mesh_updater.h"
|
#include "voxel_mesh_updater.h"
|
||||||
|
|
||||||
#include <scene/3d/spatial.h>
|
#include <scene/3d/spatial.h>
|
||||||
@ -55,6 +54,11 @@ public:
|
|||||||
Ref<VoxelMap> get_storage() const { return _map; }
|
Ref<VoxelMap> get_storage() const { return _map; }
|
||||||
Ref<VoxelTool> get_voxel_tool();
|
Ref<VoxelTool> get_voxel_tool();
|
||||||
|
|
||||||
|
void set_run_stream_in_editor(bool enable);
|
||||||
|
bool is_stream_running_in_editor() const;
|
||||||
|
|
||||||
|
void restart_stream();
|
||||||
|
|
||||||
struct Stats {
|
struct Stats {
|
||||||
VoxelMeshUpdater::Stats updater;
|
VoxelMeshUpdater::Stats updater;
|
||||||
VoxelDataLoader::Stats stream;
|
VoxelDataLoader::Stats stream;
|
||||||
@ -135,7 +139,7 @@ private:
|
|||||||
int _last_view_distance_blocks;
|
int _last_view_distance_blocks;
|
||||||
|
|
||||||
bool _generate_collisions = true;
|
bool _generate_collisions = true;
|
||||||
bool _run_in_editor;
|
bool _run_stream_in_editor = true;
|
||||||
|
|
||||||
Ref<Material> _materials[VoxelMesherBlocky::MAX_MATERIALS];
|
Ref<Material> _materials[VoxelMesherBlocky::MAX_MATERIALS];
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user