Merge branch 'master' into depth

# Conflicts:
#	meshers/blocky/voxel_mesher_blocky.cpp
#	meshers/transvoxel/voxel_mesher_transvoxel.cpp
This commit is contained in:
Marc Gilleron 2020-01-05 23:40:44 +00:00
commit 04d15fe843
19 changed files with 326 additions and 104 deletions

View File

@ -56,7 +56,7 @@ void VoxelMesherBlocky::set_occlusion_enabled(bool enable) {
_bake_occlusion = enable;
}
void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &buffer) {
void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
//uint64_t time_before = OS::get_singleton()->get_ticks_usec();
const int channel = VoxelBuffer::CHANNEL_TYPE;
@ -86,9 +86,16 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &bu
// - Slower
// => Could be implemented in a separate class?
const VoxelBuffer &voxels = input.voxels;
#ifdef TOOLS_ENABLED
if (input.lod != 0) {
WARN_PRINT("VoxelMesherBlocky received lod != 0, it is not supported");
}
#endif
// Data must be padded, hence the off-by-one
Vector3i min = Vector3i(get_minimum_padding());
Vector3i max = buffer.get_size() - Vector3i(get_maximum_padding());
Vector3i max = voxels.get_size() - Vector3i(get_maximum_padding());
int index_offsets[MAX_MATERIALS] = { 0 };
@ -101,7 +108,7 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &bu
ERR_FAIL_COND(buffer.get_channel_depth(channel) != VoxelBuffer::DEPTH_8_BIT);
ERR_FAIL_COND(buffer.get_channel_compression(channel) != VoxelBuffer::COMPRESSION_NONE);
uint8_t *type_buffer = buffer.get_channel_raw(channel);
const uint8_t *type_buffer = buffer.get_channel_raw(channel);
/* _
// | \
// /\ \\
@ -124,8 +131,8 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelBuffer &bu
// Build lookup tables so to speed up voxel access.
// These are values to add to an address in order to get given neighbor.
int row_size = buffer.get_size().y;
int deck_size = buffer.get_size().x * row_size;
int row_size = voxels.get_size().y;
int deck_size = voxels.get_size().x * row_size;
int side_neighbor_lut[Cube::SIDE_COUNT];
side_neighbor_lut[Cube::SIDE_LEFT] = row_size;

View File

@ -27,7 +27,7 @@ public:
void set_occlusion_enabled(bool enable);
bool get_occlusion_enabled() const { return _bake_occlusion; }
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels) override;
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
VoxelMesher *clone() override;

View File

@ -49,6 +49,12 @@ Array MeshBuilder::commit(bool wireframe) {
return surface;
}
void MeshBuilder::scale(float scale) {
for (auto it = _positions.begin(); it != _positions.end(); ++it) {
*it *= scale;
}
}
void MeshBuilder::clear() {
_positions.clear();
_normals.clear();

View File

@ -37,6 +37,7 @@ public:
_indices.push_back(i);
}
void scale(float scale);
Array commit(bool wireframe);
void clear();

View File

@ -288,7 +288,15 @@ void foreach_node(OctreeNode *root, Action_T &a, int depth = 0) {
}
}
Array generate_debug_octree_mesh(OctreeNode *root) {
inline void scale_positions(PoolVector3Array &positions, float scale) {
PoolVector3Array::Write w = positions.write();
const uint32_t size = positions.size();
for (unsigned int i = 0; i < size; ++i) {
w[i] *= scale;
}
}
Array generate_debug_octree_mesh(OctreeNode *root, int scale) {
struct GetMaxDepth {
int max_depth;
@ -344,6 +352,10 @@ Array generate_debug_octree_mesh(OctreeNode *root) {
return Array();
}
if (scale != 1) {
scale_positions(arrays.positions, scale);
}
Array surface;
surface.resize(Mesh::ARRAY_MAX);
surface[Mesh::ARRAY_VERTEX] = arrays.positions;
@ -353,7 +365,7 @@ Array generate_debug_octree_mesh(OctreeNode *root) {
return surface;
}
Array generate_debug_dual_grid_mesh(const DualGrid &grid) {
Array generate_debug_dual_grid_mesh(const DualGrid &grid, int scale) {
PoolVector3Array positions;
PoolIntArray indices;
@ -380,6 +392,10 @@ Array generate_debug_dual_grid_mesh(const DualGrid &grid) {
return Array();
}
if (scale != 1) {
scale_positions(positions, scale);
}
Array surface;
surface.resize(Mesh::ARRAY_MAX);
surface[Mesh::ARRAY_VERTEX] = positions;
@ -1464,7 +1480,7 @@ VoxelMesherDMC::SeamMode VoxelMesherDMC::get_seam_mode() const {
return _seam_mode;
}
void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxels) {
void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
// Requirements:
// - Voxel data must be padded
@ -1472,6 +1488,8 @@ void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxel
_stats = {};
const VoxelBuffer &voxels = input.voxels;
if (voxels.is_uniform(VoxelBuffer::CHANNEL_SDF)) {
// That won't produce any polygon
return;
@ -1537,7 +1555,7 @@ void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxel
if (root != nullptr) {
if (_mesh_mode == MESH_DEBUG_OCTREE) {
surface = dmc::generate_debug_octree_mesh(root);
surface = dmc::generate_debug_octree_mesh(root, 1 << input.lod);
} else {
@ -1550,7 +1568,7 @@ void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxel
_stats.dualgrid_derivation_time = OS::get_singleton()->get_ticks_usec() - time_before;
if (_mesh_mode == MESH_DEBUG_DUAL_GRID) {
surface = dmc::generate_debug_dual_grid_mesh(_dual_grid);
surface = dmc::generate_debug_dual_grid_mesh(_dual_grid, 1 << input.lod);
} else {
@ -1576,6 +1594,9 @@ void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxel
if (surface.empty()) {
time_before = OS::get_singleton()->get_ticks_usec();
if (input.lod > 0) {
_mesh_builder.scale(1 << input.lod);
}
surface = _mesh_builder.commit(_mesh_mode == MESH_WIREFRAME);
_stats.commit_time = OS::get_singleton()->get_ticks_usec() - time_before;
}

View File

@ -101,7 +101,7 @@ public:
void set_seam_mode(SeamMode mode);
SeamMode get_seam_mode() const;
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels) override;
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
Dictionary get_statistics() const;

View File

@ -73,7 +73,7 @@ VoxelMesherMC::VoxelMesherMC() {
set_padding(MIN_PADDING, MAX_PADDING);
}
void VoxelMesherMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxels) {
void VoxelMesherMC::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
int channel = VoxelBuffer::CHANNEL_SDF;
@ -85,6 +85,7 @@ void VoxelMesherMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxels
_output_normals.clear();
_output_indices.clear();
const VoxelBuffer &voxels = input.voxels;
build_internal(voxels, channel);
// OS::get_singleton()->print("vertices: %i, normals: %i, indices: %i\n",
// m_output_vertices.size(),

View File

@ -3,6 +3,7 @@
#include "../voxel_mesher.h"
// TODO Remove it.
// Simple marching cubes.
// Implementation is simplified from old Transvoxel code.
class VoxelMesherMC : public VoxelMesher {
@ -19,7 +20,7 @@ public:
VoxelMesherMC();
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels) override;
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
void set_seam_mode(SeamMode mode);
SeamMode get_seam_mode() const;

View File

@ -155,7 +155,20 @@ void VoxelMesherTransvoxel::fill_surface_arrays(Array &arrays) {
arrays[Mesh::ARRAY_INDEX] = indices;
}
void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer &voxels) {
void VoxelMesherTransvoxel::scale_output(float factor) {
for (auto it = _output_vertices.begin(); it != _output_vertices.end(); ++it) {
*it *= factor;
}
for (auto it = _output_extra.begin(); it != _output_extra.end(); ++it) {
Color &c = *it;
c.r *= factor;
c.g *= factor;
c.b *= factor;
}
}
void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
int channel = VoxelBuffer::CHANNEL_SDF;
@ -165,6 +178,7 @@ void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer
// Once capacity is big enough, no more memory should be allocated
clear_output();
const VoxelBuffer &voxels = input.voxels;
ERR_FAIL_COND(voxels.get_channel_depth(channel) != VoxelBuffer::DEPTH_8_BIT);
build_internal(voxels, channel);
@ -175,6 +189,10 @@ void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer
}
Array regular_arrays;
// TODO Bake LOD into the algorithm. It wasn't so far because it wasn't in the API
if (input.lod > 0) {
scale_output(1 << input.lod);
}
fill_surface_arrays(regular_arrays);
output.surfaces.push_back(regular_arrays);
@ -189,6 +207,9 @@ void VoxelMesherTransvoxel::build(VoxelMesher::Output &output, const VoxelBuffer
}
Array transition_arrays;
if (input.lod > 0) {
scale_output(1 << input.lod);
}
fill_surface_arrays(transition_arrays);
output.transition_surfaces[dir].push_back(transition_arrays);
}

View File

@ -15,7 +15,7 @@ public:
VoxelMesherTransvoxel();
void build(VoxelMesher::Output &output, const VoxelBuffer &voxels) override;
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
VoxelMesher *clone() override;
@ -46,6 +46,7 @@ private:
int emit_vertex(Vector3 primary, Vector3 normal, uint16_t border_mask, Vector3 secondary);
void clear_output();
void fill_surface_arrays(Array &arrays);
void scale_output(float factor);
private:
FixedArray<std::vector<ReuseCell>, 2> _cache;

View File

@ -5,7 +5,8 @@ Ref<Mesh> VoxelMesher::build_mesh(Ref<VoxelBuffer> voxels) {
ERR_FAIL_COND_V(voxels.is_null(), Ref<ArrayMesh>());
Output output;
build(output, **voxels);
Input input = { **voxels, 0 };
build(output, input);
if (output.surfaces.empty()) {
return Ref<ArrayMesh>();
@ -21,7 +22,7 @@ Ref<Mesh> VoxelMesher::build_mesh(Ref<VoxelBuffer> voxels) {
return mesh;
}
void VoxelMesher::build(Output &output, const VoxelBuffer &voxels) {
void VoxelMesher::build(Output &output, const Input &input) {
ERR_PRINT("Not implemented");
}

View File

@ -9,6 +9,11 @@
class VoxelMesher : public Reference {
GDCLASS(VoxelMesher, Reference)
public:
struct Input {
const VoxelBuffer &voxels;
int lod = 0;
};
struct Output {
// Each surface correspond to a different material
Vector<Array> surfaces;
@ -17,7 +22,7 @@ public:
unsigned int compression_flags = Mesh::ARRAY_COMPRESS_DEFAULT;
};
virtual void build(Output &output, const VoxelBuffer &voxels);
virtual void build(Output &output, const Input &voxels);
// Get how many neighbor voxels need to be accessed around the meshed area.
// If this is not respected, the mesher might produce seams at the edges, or an error

View File

@ -43,7 +43,7 @@ protected:
Stats _stats;
VOXEL_PROFILER_DECLARE;
VOXEL_PROFILER_DECLARE
};
#endif // VOXEL_STREAM_H

View File

@ -1,24 +1,5 @@
#include "voxel_stream_image.h"
VoxelStreamImage::VoxelStreamImage() :
_channel(VoxelBuffer::CHANNEL_TYPE) {
}
void VoxelStreamImage::set_image(Ref<Image> im) {
_image = im;
}
Ref<Image> VoxelStreamImage::get_image() const {
return _image;
}
void VoxelStreamImage::set_channel(VoxelBuffer::ChannelId channel) {
_channel = channel;
}
VoxelBuffer::ChannelId VoxelStreamImage::get_channel() const {
return _channel;
}
#include "../util/fixed_array.h"
namespace {
@ -39,8 +20,78 @@ inline float get_height_blurred(Image &im, int x, int y) {
return h * 0.2f;
}
float get_constrained_segment_sdf(float p_yp, float p_ya, float p_yb, float p_xb) {
// P
// . B
// . /
// . / y
// ./ |
// A o--x
float s = p_yp >= p_ya ? 1 : -1;
if (Math::absf(p_yp - p_ya) > 1.f && Math::absf(p_yp - p_yb) > 1.f) {
return s;
}
Vector2 p(0, p_yp);
Vector2 a(0, p_ya);
Vector2 b(p_xb, p_yb);
Vector2 closest_point;
// TODO Optimize given the particular case we are in
Vector2 n = b - a;
real_t l2 = n.length_squared();
if (l2 < 1e-20) {
closest_point = a; // Both points are the same, just give any.
} else {
real_t d = n.dot(p - a) / l2;
if (d <= 0.0) {
closest_point = a; // Before first point.
} else if (d >= 1.0) {
closest_point = b; // After first point.
} else {
closest_point = a + n * d; // Inside.
}
}
return s * closest_point.distance_to(p);
}
} // namespace
const char *VoxelStreamImage::SDF_MODE_HINT_STRING = "Vertical,VerticalAverage,Segment";
VoxelStreamImage::VoxelStreamImage() {
}
void VoxelStreamImage::set_image(Ref<Image> im) {
_image = im;
}
Ref<Image> VoxelStreamImage::get_image() const {
return _image;
}
void VoxelStreamImage::set_channel(VoxelBuffer::ChannelId channel) {
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
_channel = channel;
}
VoxelBuffer::ChannelId VoxelStreamImage::get_channel() const {
return _channel;
}
void VoxelStreamImage::set_sdf_mode(SdfMode mode) {
ERR_FAIL_INDEX(mode, SDF_MODE_COUNT);
_sdf_mode = mode;
}
VoxelStreamImage::SdfMode VoxelStreamImage::get_sdf_mode() const {
return _sdf_mode;
}
void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i origin_in_voxels, int lod) {
int ox = origin_in_voxels.x;
@ -52,9 +103,6 @@ void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i orig
image.lock();
int x = 0;
int z = 0;
int bs = out_buffer.get_size().x;
int dirt = 1;
@ -62,22 +110,14 @@ void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i orig
float hbase = 50.0;
float hspan = 200.0;
while (z < bs) {
while (x < bs) {
for (int z = 0; z < out_buffer.get_size().z; ++z) {
for (int x = 0; x < out_buffer.get_size().x; ++x) {
int lx = x << lod;
int lz = z << lod;
if (_channel == VoxelBuffer::CHANNEL_SDF) {
if (_channel == VoxelBuffer::CHANNEL_TYPE) {
float h = get_height_blurred(image, ox + lx, oz + lz) * hspan - hbase;
for (int y = 0; y < bs; ++y) {
int ly = y << lod;
out_buffer.set_voxel_f((oy + ly) - h, x, y, z, _channel);
}
} else {
float h = get_height_repeat(image, ox + lx, oz + lz) * hspan - hbase;
h -= oy;
int ih = int(h);
@ -87,13 +127,107 @@ void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i orig
}
out_buffer.fill_area(dirt, Vector3(x, 0, z), Vector3(x + 1, ih, z + 1), _channel);
}
}
x += 1;
}
z += 1;
x = 0;
}
} else if (_channel == VoxelBuffer::CHANNEL_SDF) {
switch (_sdf_mode) {
case SDF_VERTICAL:
case SDF_VERTICAL_AVERAGE: {
float h;
if (_sdf_mode == SDF_VERTICAL) {
// Only get the height at XZ
h = get_height_repeat(image, ox + lx, oz + lz) * hspan - hbase;
} else {
// Get an average of the heights around XZ
h = get_height_blurred(image, ox + lx, oz + lz) * hspan - hbase;
}
for (int y = 0; y < out_buffer.get_size().y; ++y) {
int ly = y << lod;
float d = (oy + ly) - h;
out_buffer.set_voxel_f(d, x, y, z, _channel);
}
} break;
case SDF_SEGMENT: {
// Calculate distance to 8 segments going from the point at XZ to its neighbor points,
// and pick the smallest distance.
int gx = ox + lx;
int gz = oz + lz;
float h0 = get_height_repeat(image, gx - 1, gz - 1) * hspan - hbase;
float h1 = get_height_repeat(image, gx, gz - 1) * hspan - hbase;
float h2 = get_height_repeat(image, gx + 1, gz - 1) * hspan - hbase;
float h3 = get_height_repeat(image, gx - 1, gz) * hspan - hbase;
float h4 = get_height_repeat(image, gx, gz) * hspan - hbase;
float h5 = get_height_repeat(image, gx + 1, gz) * hspan - hbase;
float h6 = get_height_repeat(image, gx - 1, gz + 1) * hspan - hbase;
float h7 = get_height_repeat(image, gx, gz + 1) * hspan - hbase;
float h8 = get_height_repeat(image, gx + 1, gz + 1) * hspan - hbase;
const float sqrt2 = 1.414213562373095;
for (int y = 0; y < out_buffer.get_size().y; ++y) {
int ly = y << lod;
int gy = oy + ly;
float sdf0 = get_constrained_segment_sdf(gy, h4, h0, sqrt2);
float sdf1 = get_constrained_segment_sdf(gy, h4, h1, 1);
float sdf2 = get_constrained_segment_sdf(gy, h4, h2, sqrt2);
float sdf3 = get_constrained_segment_sdf(gy, h4, h3, 1);
float sdf4 = gy - h4;
float sdf5 = get_constrained_segment_sdf(gy, h4, h5, 1);
float sdf6 = get_constrained_segment_sdf(gy, h4, h6, sqrt2);
float sdf7 = get_constrained_segment_sdf(gy, h4, h7, 1);
float sdf8 = get_constrained_segment_sdf(gy, h4, h8, sqrt2);
float sdf = sdf4;
if (Math::absf(sdf0) < Math::absf(sdf)) {
sdf = sdf0;
}
if (Math::absf(sdf1) < Math::absf(sdf)) {
sdf = sdf1;
}
if (Math::absf(sdf2) < Math::absf(sdf)) {
sdf = sdf2;
}
if (Math::absf(sdf3) < Math::absf(sdf)) {
sdf = sdf3;
}
if (Math::absf(sdf5) < Math::absf(sdf)) {
sdf = sdf5;
}
if (Math::absf(sdf6) < Math::absf(sdf)) {
sdf = sdf6;
}
if (Math::absf(sdf7) < Math::absf(sdf)) {
sdf = sdf7;
}
if (Math::absf(sdf8) < Math::absf(sdf)) {
sdf = sdf8;
}
out_buffer.set_voxel_f(sdf, x, y, z, _channel);
}
} break;
default:
CRASH_NOW();
break;
} // sdf mode
} // channel
} // for x
} // for z
image.unlock();
@ -108,6 +242,14 @@ void VoxelStreamImage::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelStreamImage::set_channel);
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelStreamImage::get_channel);
ClassDB::bind_method(D_METHOD("set_sdf_mode", "mode"), &VoxelStreamImage::set_sdf_mode);
ClassDB::bind_method(D_METHOD("get_sdf_mode"), &VoxelStreamImage::get_sdf_mode);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_image", "get_image");
ADD_PROPERTY(PropertyInfo(Variant::INT, "channel", PROPERTY_HINT_ENUM, VoxelBuffer::CHANNEL_ID_HINT_STRING), "set_channel", "get_channel");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_mode", PROPERTY_HINT_ENUM, SDF_MODE_HINT_STRING), "set_sdf_mode", "get_sdf_mode");
BIND_ENUM_CONSTANT(SDF_VERTICAL);
BIND_ENUM_CONSTANT(SDF_VERTICAL_AVERAGE);
BIND_ENUM_CONSTANT(SDF_SEGMENT);
}

View File

@ -10,12 +10,24 @@ class VoxelStreamImage : public VoxelStream {
public:
VoxelStreamImage();
enum SdfMode {
SDF_VERTICAL = 0, // Lowest quality, fastest
SDF_VERTICAL_AVERAGE,
SDF_SEGMENT,
SDF_MODE_COUNT
};
static const char *SDF_MODE_HINT_STRING;
void set_image(Ref<Image> im);
Ref<Image> get_image() const;
void set_channel(VoxelBuffer::ChannelId channel);
VoxelBuffer::ChannelId get_channel() const;
void set_sdf_mode(SdfMode mode);
SdfMode get_sdf_mode() const;
void emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i origin_in_voxels, int lod);
private:
@ -23,7 +35,10 @@ private:
private:
Ref<Image> _image;
VoxelBuffer::ChannelId _channel;
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_TYPE;
SdfMode _sdf_mode = SDF_VERTICAL_AVERAGE;
};
VARIANT_ENUM_CAST(VoxelStreamImage::SdfMode)
#endif // HEADER_VOXEL_STREAM_IMAGE

View File

@ -201,7 +201,7 @@ void VoxelBlock::set_world(World *world) {
}
void VoxelBlock::set_visible(bool visible) {
if (_visible && visible) {
if (_visible == visible) {
return;
}
_visible = visible;
@ -263,11 +263,12 @@ void VoxelBlock::set_transition_mask(uint8_t m) {
tm |= bits[Cube::SIDE_NEGATIVE_Z] << 4;
tm |= bits[Cube::SIDE_POSITIVE_Z] << 5;
// TODO Use a StringName, VoxelStringNames
_shader_material->set_shader_param("u_transition_mask", tm);
}
for (int dir = 0; dir < Cube::SIDE_COUNT; ++dir) {
DirectMeshInstance &mi = _transition_mesh_instances[dir];
if (diff & (1 << dir) && mi.is_valid()) {
if ((diff & (1 << dir)) && mi.is_valid()) {
mi.set_visible(_visible & _is_transition_visible(dir));
}
}

View File

@ -1080,6 +1080,9 @@ void VoxelLodTerrain::_process() {
Ref<ShaderMaterial> sm;
if (_shader_material_pool.size() > 0) {
sm = _shader_material_pool.back();
// The joys of pooling materials
// TODO Use StringName
sm->set_shader_param("u_transition_mask", 0);
_shader_material_pool.pop_back();
} else {
sm = shader_material->duplicate(false);
@ -1431,7 +1434,7 @@ void VoxelLodTerrain::add_transition_updates_around(Vector3i block_pos, int lod_
Vector3i npos = block_pos + Cube::g_side_normals[dir];
VoxelBlock *nblock = lod.map->get_block(npos);
if (nblock != nullptr && nblock->is_visible()) {
if (nblock != nullptr) {
add_transition_update(nblock);
}
}
@ -1509,6 +1512,35 @@ Dictionary VoxelLodTerrain::get_statistics() const {
return d;
}
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;
float distance = 0.f;
Array hits;
while (distance < max_distance && hits.size() == 0) {
for (int lod_index = 0; lod_index < _lod_count; ++lod_index) {
const Lod &lod = _lods[lod_index];
Vector3i bpos = lod.map->voxel_to_block(Vector3i(pos)) >> lod_index;
const VoxelBlock *block = lod.map->get_block(bpos);
if (block != nullptr && block->is_visible() && block->has_mesh()) {
Dictionary d;
d["position"] = block->position.to_vec3();
d["lod"] = block->lod_index;
d["transition_mask"] = block->get_transition_mask();
hits.append(d);
}
}
distance += step;
pos += dir * step;
}
return hits;
}
void VoxelLodTerrain::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &VoxelLodTerrain::set_stream);
@ -1542,6 +1574,8 @@ void VoxelLodTerrain::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel_tool"), &VoxelLodTerrain::get_voxel_tool);
ClassDB::bind_method(D_METHOD("debug_raycast_block", "origin", "dir"), &VoxelLodTerrain::debug_raycast_block);
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");

View File

@ -80,6 +80,7 @@ public:
};
Dictionary get_statistics() const;
Array debug_raycast_block(Vector3 world_origin, Vector3 world_direction) const;
protected:
static void _bind_methods();

View File

@ -4,34 +4,6 @@
#include "voxel_lod_terrain.h"
#include <core/os/os.h>
static void scale_mesh_data(Vector<Array> &surfaces, float factor) {
for (int i = 0; i < surfaces.size(); ++i) {
Array &surface = surfaces.write[i]; // There is COW here too but should not happen, hopefully
if (surface.empty()) {
continue;
}
PoolVector3Array positions = surface[Mesh::ARRAY_VERTEX]; // Array of Variants here, implicit cast going on
// Now dear COW, let's make sure there is only ONE ref to that PoolVector,
// so you won't trash performance with pointless allocations
surface[Mesh::ARRAY_VERTEX] = Variant();
{
PoolVector3Array::Write w = positions.write();
int len = positions.size();
for (int j = 0; j < len; ++j) {
w[j] = w[j] * factor;
}
}
// Thank you
surface[Mesh::ARRAY_VERTEX] = positions;
}
}
VoxelMeshUpdater::VoxelMeshUpdater(unsigned int thread_count, MeshingParams params) {
print_line("Constructing VoxelMeshUpdater");
@ -103,21 +75,13 @@ void VoxelMeshUpdater::process_blocks_thread_func(
CRASH_COND(block.voxels.is_null());
VoxelMesher::Input input = { **block.voxels, ib.lod };
if (blocky_mesher.is_valid()) {
blocky_mesher->build(output.blocky_surfaces, **block.voxels);
blocky_mesher->build(output.blocky_surfaces, input);
}
if (smooth_mesher.is_valid()) {
smooth_mesher->build(output.smooth_surfaces, **block.voxels);
}
if (ib.lod > 0) {
// TODO Make this optional if the mesher can factor in the upscale already
float factor = 1 << ib.lod;
scale_mesh_data(output.blocky_surfaces.surfaces, factor);
scale_mesh_data(output.smooth_surfaces.surfaces, factor);
for (int i = 0; i < output.smooth_surfaces.transition_surfaces.size(); ++i) {
scale_mesh_data(output.smooth_surfaces.transition_surfaces[i], factor);
}
smooth_mesher->build(output.smooth_surfaces, input);
}
}
}