Merge branch 'master' into depth
# Conflicts: # meshers/blocky/voxel_mesher_blocky.cpp # meshers/transvoxel/voxel_mesher_transvoxel.cpp
This commit is contained in:
commit
04d15fe843
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
_indices.push_back(i);
|
||||
}
|
||||
|
||||
void scale(float scale);
|
||||
Array commit(bool wireframe);
|
||||
void clear();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -43,7 +43,7 @@ protected:
|
||||
|
||||
Stats _stats;
|
||||
|
||||
VOXEL_PROFILER_DECLARE;
|
||||
VOXEL_PROFILER_DECLARE
|
||||
};
|
||||
|
||||
#endif // VOXEL_STREAM_H
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user