Streaming/LOD can be set to follow the editor camera instead of being centered on world origin

master
Marc Gilleron 2020-12-19 23:16:10 +00:00
parent 659e45fe32
commit 418cf0e630
6 changed files with 69 additions and 44 deletions

View File

@ -13,16 +13,20 @@ Semver is not yet in place, so each version can have breaking changes, although
- General
- Introduction of Voxel Server, which shares threaded tasks among all voxel nodes
- Voxel data is no longer copied when sent to processing threads, reducing high memory spikes in some scenarios
- Added a utility class to load MagicaVoxel `.vox` files
- Added a utility class to load `.vox` files created with MagicaVoxel (scripts only)
- Voxel nodes can be moved, scaled and rotated
- Voxel nodes can be limited to specific bounds, rather than being infinitely paging volumes (multiples of block size)
- Meshers are now resources so you can choose and configure them per terrain
- Editor
- Streaming/LOD can be set to follow the editor camera instead of being centered on world origin
- Added About window
- Smooth voxels
- Shaders now have access to the transform of each block, useful for triplanar mapping on moving volumes
- Blocky voxels
- Introduced a second blocky mesher dedicated to colored cubes, with greedy meshing and palette support
- Introduced a second blocky mesher dedicated to colored cubes, with greedy meshing and palette support (scripts only)
- Replaced `transparent` property with `transparency_index` for more control on the culling of transparent faces
- Breaking changes
@ -43,7 +47,6 @@ Semver is not yet in place, so each version can have breaking changes, although
---------------------------
- General
- Terrain nodes now render in the editor, unless scripts are involved (can be changed with an option)
- Added per-voxel and per-block metadata, which are saved by file streams along with voxel data
- `StringName` is now used when possible to call script functions, to reduce overhead
- Exposed block serializer to allow encoding voxels for network or files from script
@ -51,6 +54,9 @@ Semver is not yet in place, so each version can have breaking changes, although
- The module only prints debug logs if the engine is in verbose mode
- `VoxelTerrain` now emit signals when blocks are loaded and unloaded
- Editor
- Terrain nodes now render in the editor, unless scripts are involved (can be changed with an option)
- Blocky voxels
- Added collision masks to blocky voxels, which takes effect with `VoxelBoxMover` and voxel raycasts
- Added random tick API for blocky terrains

View File

@ -5,6 +5,7 @@
#include "../about_window.h"
#include "../graph/voxel_graph_node_inspector_wrapper.h"
#include <scene/3d/camera.h>
#include <scene/gui/menu_button.h>
VoxelTerrainEditorPlugin::VoxelTerrainEditorPlugin(EditorNode *p_node) {
@ -13,6 +14,13 @@ VoxelTerrainEditorPlugin::VoxelTerrainEditorPlugin(EditorNode *p_node) {
menu_button->get_popup()->add_item(TTR("Re-generate"), MENU_RESTART_STREAM);
menu_button->get_popup()->add_item(TTR("Re-mesh"), MENU_REMESH);
menu_button->get_popup()->add_separator();
menu_button->get_popup()->add_item(TTR("Stream follow camera"), MENU_STREAM_FOLLOW_CAMERA);
{
const int i = menu_button->get_popup()->get_item_index(MENU_STREAM_FOLLOW_CAMERA);
menu_button->get_popup()->set_item_as_checkable(i, true);
menu_button->get_popup()->set_item_checked(i, _editor_viewer_follows_camera);
}
menu_button->get_popup()->add_separator();
menu_button->get_popup()->add_item(TTR("About Voxel Tools..."), MENU_ABOUT);
menu_button->get_popup()->connect("id_pressed", this, "_on_menu_item_selected");
menu_button->hide();
@ -25,6 +33,19 @@ VoxelTerrainEditorPlugin::VoxelTerrainEditorPlugin(EditorNode *p_node) {
base_control->add_child(_about_window);
}
void VoxelTerrainEditorPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
_editor_viewer_id = VoxelServer::get_singleton()->add_viewer();
VoxelServer::get_singleton()->set_viewer_distance(_editor_viewer_id, 512);
break;
case NOTIFICATION_EXIT_TREE:
VoxelServer::get_singleton()->remove_viewer(_editor_viewer_id);
break;
}
}
// Things the plugin doesn't directly work on, but still handles to keep things visible.
// This is basically a hack because it's not easy to express that with EditorPlugin API.
// The use case being, as long as we edit an object NESTED within a voxel terrain, we should keep things visible.
@ -107,6 +128,17 @@ void VoxelTerrainEditorPlugin::make_visible(bool visible) {
// So we'll need to check if _node is null all over the place
}
bool VoxelTerrainEditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
VoxelServer::get_singleton()->set_viewer_distance(_editor_viewer_id, p_camera->get_zfar());
_editor_camera_last_position = p_camera->get_global_transform().origin;
if (_editor_viewer_follows_camera) {
VoxelServer::get_singleton()->set_viewer_position(_editor_viewer_id, _editor_camera_last_position);
}
return false;
}
void VoxelTerrainEditorPlugin::_on_menu_item_selected(int id) {
switch (id) {
case MENU_RESTART_STREAM:
@ -119,6 +151,17 @@ void VoxelTerrainEditorPlugin::_on_menu_item_selected(int id) {
_node->remesh_all_blocks();
break;
case MENU_STREAM_FOLLOW_CAMERA: {
_editor_viewer_follows_camera = !_editor_viewer_follows_camera;
const int i = _menu_button->get_popup()->get_item_index(MENU_STREAM_FOLLOW_CAMERA);
_menu_button->get_popup()->set_item_checked(i, _editor_viewer_follows_camera);
if (_editor_viewer_follows_camera) {
VoxelServer::get_singleton()->set_viewer_position(_editor_viewer_id, _editor_camera_last_position);
}
} break;
case MENU_ABOUT:
_about_window->popup_centered();
break;

View File

@ -15,6 +15,10 @@ public:
bool handles(Object *p_object) const override;
void edit(Object *p_object) override;
void make_visible(bool visible) override;
bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) override;
protected:
void _notification(int p_what);
private:
void set_node(VoxelNode *node);
@ -28,11 +32,16 @@ private:
enum MenuID {
MENU_RESTART_STREAM,
MENU_REMESH,
MENU_STREAM_FOLLOW_CAMERA,
MENU_ABOUT
};
VoxelNode *_node = nullptr;
uint32_t _editor_viewer_id = -1;
Vector3 _editor_camera_last_position;
bool _editor_viewer_follows_camera = false;
MenuButton *_menu_button = nullptr;
VoxelAboutWindow *_about_window = nullptr;
};

View File

@ -56,13 +56,6 @@ VoxelServer::VoxelServer() {
_meshing_thread_pool.set_priority_update_period(64);
_meshing_thread_pool.set_batch_count(1);
if (Engine::get_singleton()->is_editor_hint()) {
// Default viewer
const uint32_t default_viewer_id = add_viewer();
Viewer &default_viewer = _world.viewers.get(default_viewer_id);
default_viewer.is_default = true;
}
// Init world
_world.shared_priority_dependency = gd_make_shared<PriorityDependencyShared>();
@ -297,30 +290,11 @@ void VoxelServer::remove_volume(uint32_t volume_id) {
}
uint32_t VoxelServer::add_viewer() {
if (Engine::get_singleton()->is_editor_hint()) {
// Remove default viewer if any
_world.viewers.for_each_with_id([this](Viewer &viewer, uint32_t id) {
if (viewer.is_default) {
// Safe because StructDB does not reallocate the data structure on removal
_world.viewers.destroy(id);
}
});
}
return _world.viewers.create(Viewer());
}
void VoxelServer::remove_viewer(uint32_t viewer_id) {
_world.viewers.destroy(viewer_id);
if (Engine::get_singleton()->is_editor_hint()) {
// Re-add default viewer
if (_world.viewers.count() == 0) {
const uint32_t default_viewer_id = add_viewer();
Viewer &default_viewer = _world.viewers.get(default_viewer_id);
default_viewer.is_default = true;
}
}
}
void VoxelServer::set_viewer_position(uint32_t viewer_id, Vector3 position) {

View File

@ -62,7 +62,6 @@ public:
unsigned int view_distance = 128;
bool require_collisions = false;
bool require_visuals = true;
bool is_default = false;
};
enum VolumeType {

View File

@ -507,22 +507,16 @@ void VoxelLodTerrain::_notification(int p_what) {
}
Vector3 VoxelLodTerrain::get_local_viewer_pos() const {
if (Engine::get_singleton()->is_editor_hint()) {
// TODO Use editor's camera here
return Vector3();
Vector3 pos = (_lods[0].last_viewer_block_pos << _lods[0].map.get_block_size_pow2()).to_vec3();
} else {
Vector3 pos = (_lods[0].last_viewer_block_pos << _lods[0].map.get_block_size_pow2()).to_vec3();
// TODO Support for multiple viewers, this is a placeholder implementation
VoxelServer::get_singleton()->for_each_viewer([&pos](const VoxelServer::Viewer &viewer, uint32_t viewer_id) {
pos = viewer.world_position;
});
// TODO Support for multiple viewers, this is a placeholder implementation
VoxelServer::get_singleton()->for_each_viewer([&pos](const VoxelServer::Viewer &viewer, uint32_t viewer_id) {
pos = viewer.world_position;
});
const Transform world_to_local = get_global_transform().affine_inverse();
pos = world_to_local.xform(pos);
return pos;
}
const Transform world_to_local = get_global_transform().affine_inverse();
pos = world_to_local.xform(pos);
return pos;
}
void VoxelLodTerrain::try_schedule_loading_with_neighbors(const Vector3i &p_bpos, int lod_index) {