VoxelTerrain can be transformed

This commit is contained in:
Marc Gilleron 2020-10-25 00:55:57 +01:00
parent 850874893c
commit 85bb3d7b28
5 changed files with 55 additions and 18 deletions

View File

@ -20,6 +20,8 @@ private:
static void _bind_methods();
};
// TODO Need to review VoxelTool to account for transformed volumes
// High-level generic voxel edition utility.
// Ease of use comes at cost.
// It's not a class to instantiate alone, get it from the voxel objects you want to work with

View File

@ -223,7 +223,7 @@ void VoxelBlock::set_shader_material(Ref<ShaderMaterial> material) {
void VoxelBlock::set_transition_mask(uint8_t m) {
CRASH_COND(m >= (1 << Cube::SIDE_COUNT));
uint8_t diff = _transition_mask ^ m;
const uint8_t diff = _transition_mask ^ m;
if (diff == 0) {
return;
}

View File

@ -242,7 +242,7 @@ void VoxelLodTerrain::set_view_distance(int p_distance_in_voxels) {
_view_distance_voxels = p_distance_in_voxels;
}
Spatial *VoxelLodTerrain::get_viewer() const {
const Spatial *VoxelLodTerrain::get_viewer() const {
if (!is_inside_tree()) {
return nullptr;
}
@ -463,7 +463,7 @@ void VoxelLodTerrain::_notification(int p_what) {
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
bool visible = is_visible();
const bool visible = is_visible();
for (unsigned int lod_index = 0; lod_index < _lods.size(); ++lod_index) {
if (_lods[lod_index].map.is_valid()) {
_lods[lod_index].map->for_all_blocks([visible](VoxelBlock *block) {
@ -479,13 +479,15 @@ void VoxelLodTerrain::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
const Transform transform = get_global_transform();
VoxelServer::get_singleton()->set_volume_transform(_volume_id, transform);
if (!is_inside_tree()) {
// The transform and other properties can be set by the scene loader,
// before we enter the tree
return;
}
Transform transform = get_global_transform();
for (unsigned int lod_index = 0; lod_index < _lods.size(); ++lod_index) {
if (_lods[lod_index].map.is_valid()) {
_lods[lod_index].map->for_all_blocks([&transform](VoxelBlock *block) {
@ -508,11 +510,12 @@ void VoxelLodTerrain::get_viewer_pos_and_direction(Vector3 &out_pos, Vector3 &ou
} else {
// TODO Have option to use viewport camera
Spatial *viewer = get_viewer();
const Spatial *viewer = get_viewer();
if (viewer) {
Transform gt = viewer->get_global_transform();
out_pos = gt.origin;
out_direction = -gt.basis.get_axis(Vector3::AXIS_Z);
const Transform world_to_local = get_global_transform().affine_inverse();
out_pos = world_to_local.xform(gt.origin);
out_direction = world_to_local.basis.xform(-gt.basis.get_axis(Vector3::AXIS_Z));
} else {
// TODO Just remember last viewer pos
@ -623,8 +626,7 @@ void VoxelLodTerrain::_process() {
return;
}
// Get viewer location
// TODO Transform to local (Spatial Transform)
// Get viewer location in voxel space
Vector3 viewer_pos;
Vector3 viewer_direction;
get_viewer_pos_and_direction(viewer_pos, viewer_direction);
@ -1550,10 +1552,11 @@ AABB VoxelLodTerrain::_b_get_voxel_bounds() const {
// DEBUG LAND
Array VoxelLodTerrain::debug_raycast_block(Vector3 world_origin, Vector3 world_direction) const {
Vector3 pos = world_origin;
Vector3 dir = world_direction;
float max_distance = 256;
float step = 2.f;
const Transform world_to_local = get_global_transform().affine_inverse();
Vector3 pos = world_to_local.xform(world_origin);
const Vector3 dir = world_to_local.basis.xform(world_direction);
const float max_distance = 256;
const float step = 2.f;
float distance = 0.f;
Array hits;

View File

@ -110,7 +110,7 @@ protected:
void _process();
private:
Spatial *get_viewer() const;
const Spatial *get_viewer() const;
void immerge_block(Vector3i block_pos, int lod_index);
void start_updater();

View File

@ -20,6 +20,8 @@ VoxelTerrain::VoxelTerrain() {
// Godot may create and destroy dozens of instances of all node types on startup,
// due to how ClassDB gets its default values.
set_notify_transform(true);
// Infinite by default
_bounds_in_voxels = Rect3i::from_center_extents(Vector3i(0), Vector3i(VoxelConstants::MAX_VOLUME_EXTENT));
@ -743,7 +745,21 @@ void VoxelTerrain::_notification(int p_what) {
_map->for_all_blocks(SetParentVisibilityAction(is_visible()));
break;
// TODO Listen for transform changes
case NOTIFICATION_TRANSFORM_CHANGED: {
const Transform transform = get_global_transform();
VoxelServer::get_singleton()->set_volume_transform(_volume_id, transform);
if (!is_inside_tree()) {
// The transform and other properties can be set by the scene loader,
// before we enter the tree
return;
}
_map->for_all_blocks([&transform](VoxelBlock *block) {
block->set_parent_transform(transform);
});
} break;
default:
break;
@ -819,6 +835,7 @@ void VoxelTerrain::_process() {
std::vector<size_t> unpaired_viewer_indexes;
{
// Our node doesn't have bounds yet, so for now viewers are always paired.
// TODO Update: the node has bounds now, need to change this
// Destroyed viewers
for (size_t i = 0; i < _paired_viewers.size(); ++i) {
@ -830,8 +847,16 @@ void VoxelTerrain::_process() {
}
}
const Transform local_to_world_transform = get_global_transform();
const Transform world_to_local_transform = local_to_world_transform.affine_inverse();
// Note, this does not support non-uniform scaling
// TODO There is probably a better way to do this
const float view_distance_scale = world_to_local_transform.basis.xform(Vector3(1, 0, 0)).length();
// New viewers and updates
VoxelServer::get_singleton()->for_each_viewer([this](const VoxelServer::Viewer &viewer, uint32_t viewer_id) {
VoxelServer::get_singleton()->for_each_viewer([this, view_distance_scale, world_to_local_transform](
const VoxelServer::Viewer &viewer, uint32_t viewer_id) {
size_t i;
if (!try_get_paired_viewer_index(viewer_id, i)) {
PairedViewer p;
@ -844,9 +869,13 @@ void VoxelTerrain::_process() {
p.prev_state = p.state;
const unsigned int view_distance_voxels =
static_cast<unsigned int>(static_cast<float>(viewer.view_distance) * view_distance_scale);
const Vector3 local_position = world_to_local_transform.xform(viewer.world_position);
p.state.view_distance_blocks =
min(viewer.view_distance >> get_block_size_pow2(), _max_view_distance_blocks);
p.state.block_position = _map->voxel_to_block(Vector3i(viewer.world_position));
min(view_distance_voxels >> get_block_size_pow2(), _max_view_distance_blocks);
p.state.block_position = _map->voxel_to_block(Vector3i(local_position));
p.state.requires_collisions = VoxelServer::get_singleton()->is_viewer_requiring_collisions(viewer_id);
p.state.requires_meshes = VoxelServer::get_singleton()->is_viewer_requiring_visuals(viewer_id);
});
@ -1152,6 +1181,8 @@ void VoxelTerrain::_process() {
const uint32_t timeout = os.get_ticks_msec() + VoxelConstants::MAIN_THREAD_MESHING_BUDGET_MS;
size_t queue_index = 0;
const Transform local_to_world_transform = get_global_transform();
// The following is done on the main thread because Godot doesn't really support multithreaded Mesh allocation.
// This also proved to be very slow compared to the meshing process itself...
// hopefully Vulkan will allow us to upload graphical resources without stalling rendering as they upload?
@ -1233,6 +1264,7 @@ void VoxelTerrain::_process() {
block->set_collision_mesh(collidable_surfaces, get_tree()->is_debugging_collisions_hint(), this);
}
block->set_parent_visible(is_visible());
block->set_parent_transform(local_to_world_transform);
}
shift_up(_reception_buffers.mesh_output, queue_index);