Streaming/LOD can be set to follow the editor camera instead of being centered on world origin
parent
659e45fe32
commit
418cf0e630
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -62,7 +62,6 @@ public:
|
|||
unsigned int view_distance = 128;
|
||||
bool require_collisions = false;
|
||||
bool require_visuals = true;
|
||||
bool is_default = false;
|
||||
};
|
||||
|
||||
enum VolumeType {
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue