Use `real_t` in most utils, use 32-bit floats in meshers.

This is a first step to supporting double-precision floats in Godot.
Meshers don't need doubles because vertices, normals and UVs should
never span large enough areas while needing such amount of precision.
Besides, it costs a lot more memory and processing.
master
Marc Gilleron 2022-02-14 00:11:41 +00:00
parent 83e4454789
commit 48081faf03
34 changed files with 849 additions and 410 deletions

View File

@ -30,14 +30,14 @@ namespace zylann::voxel::Cube {
//
// Ordered as per the cube corners diagram
const Vector3 g_corner_position[CORNER_COUNT] = { Vector3(1, 0, 0), //
Vector3(0, 0, 0), //
Vector3(0, 0, 1), //
Vector3(1, 0, 1), //
Vector3(1, 1, 0), //
Vector3(0, 1, 0), //
Vector3(0, 1, 1), //
Vector3(1, 1, 1) };
const Vector3f g_corner_position[CORNER_COUNT] = { Vector3f(1, 0, 0), //
Vector3f(0, 0, 0), //
Vector3f(0, 0, 1), //
Vector3f(1, 0, 1), //
Vector3f(1, 1, 0), //
Vector3f(0, 1, 0), //
Vector3f(0, 1, 1), //
Vector3f(1, 1, 1) };
const int g_side_quad_triangles[SIDE_COUNT][6] = {
{ 0, 2, 1, 0, 3, 2 }, // LEFT (+x)

View File

@ -1,8 +1,8 @@
#ifndef CUBE_TABLES_H
#define CUBE_TABLES_H
#include "../util/math/vector3f.h"
#include "../util/math/vector3i.h"
#include <core/math/vector3.h>
namespace zylann::voxel::Cube {
@ -60,7 +60,7 @@ enum Corner {
CORNER_COUNT
};
extern const Vector3 g_corner_position[CORNER_COUNT];
extern const Vector3f g_corner_position[CORNER_COUNT];
extern const int g_side_quad_triangles[SIDE_COUNT][6];

View File

@ -23,29 +23,29 @@ struct SdfOperation16bit {
};
struct SdfUnion {
inline float operator()(float a, float b) const {
inline real_t operator()(real_t a, real_t b) const {
return zylann::math::sdf_union(a, b);
}
};
struct SdfSubtract {
inline float operator()(float a, float b) const {
inline real_t operator()(real_t a, real_t b) const {
return zylann::math::sdf_subtract(a, b);
}
};
struct SdfSet {
inline float operator()(float a, float b) const {
inline real_t operator()(real_t a, real_t b) const {
return b;
}
};
struct SdfSphere {
Vector3 center;
float radius;
float scale;
real_t radius;
real_t scale;
inline float operator()(Vector3 pos) const {
inline real_t operator()(Vector3 pos) const {
return scale * zylann::math::sdf_sphere(pos, center, radius);
}
};

View File

@ -41,7 +41,7 @@ float get_sdf_interpolated(const Volume_F &f, Vector3 pos) {
const float s011 = f(Vector3i(c.x, c.y + 1, c.z + 1));
const float s111 = f(Vector3i(c.x + 1, c.y + 1, c.z + 1));
return math::interpolate(s000, s100, s101, s001, s010, s110, s111, s011, math::fract(pos));
return math::interpolate(s000, s100, s101, s001, s010, s110, s111, s011, to_vec3f(math::fract(pos)));
}
// Binary search can be more accurate than linear regression because the SDF can be inaccurate in the first place.

View File

@ -134,14 +134,14 @@ void VoxelBlockyLibrary::set_voxel(unsigned int idx, Ref<VoxelBlockyModel> voxel
}
template <typename F>
static void rasterize_triangle_barycentric(Vector2 a, Vector2 b, Vector2 c, F output_func) {
static void rasterize_triangle_barycentric(Vector2f a, Vector2f b, Vector2f c, F output_func) {
// Slower than scanline method, but looks better
// Grow the triangle a tiny bit, to help against floating point error
const Vector2 m = 0.333333 * (a + b + c);
a += 0.001 * (a - m);
b += 0.001 * (b - m);
c += 0.001 * (c - m);
const Vector2f m = 0.333333f * (a + b + c);
a += 0.001f * (a - m);
b += 0.001f * (b - m);
c += 0.001f * (c - m);
using namespace math;
@ -151,11 +151,11 @@ static void rasterize_triangle_barycentric(Vector2 a, Vector2 b, Vector2 c, F ou
const int max_y = (int)Math::ceil(max(max(a.y, b.y), c.y));
// We test against points centered on grid cells
const Vector2 offset(0.5, 0.5);
const Vector2f offset(0.5, 0.5);
for (int y = min_y; y < max_y; ++y) {
for (int x = min_x; x < max_x; ++x) {
if (Geometry2D::is_point_in_triangle(Vector2(x, y) + offset, a, b, c)) {
if (is_point_in_triangle(Vector2f(x, y) + offset, a, b, c)) {
output_func(x, y);
}
}
@ -221,39 +221,39 @@ void VoxelBlockyLibrary::generate_side_culling_matrix() {
model_data.contributes_to_ao = true;
for (uint16_t side = 0; side < Cube::SIDE_COUNT; ++side) {
const std::vector<Vector3> &positions = model_data.model.side_positions[side];
const std::vector<Vector3f> &positions = model_data.model.side_positions[side];
const std::vector<int> &indices = model_data.model.side_indices[side];
ERR_FAIL_COND(indices.size() % 3 != 0);
std::bitset<RASTER_SIZE * RASTER_SIZE> bitmap;
for (unsigned int j = 0; j < indices.size(); j += 3) {
const Vector3 va = positions[indices[j]];
const Vector3 vb = positions[indices[j + 1]];
const Vector3 vc = positions[indices[j + 2]];
const Vector3f va = positions[indices[j]];
const Vector3f vb = positions[indices[j + 1]];
const Vector3f vc = positions[indices[j + 2]];
// Convert 3D vertices into 2D
Vector2 a, b, c;
Vector2f a, b, c;
switch (side) {
case Cube::SIDE_NEGATIVE_X:
case Cube::SIDE_POSITIVE_X:
a = Vector2(va.y, va.z);
b = Vector2(vb.y, vb.z);
c = Vector2(vc.y, vc.z);
a = Vector2f(va.y, va.z);
b = Vector2f(vb.y, vb.z);
c = Vector2f(vc.y, vc.z);
break;
case Cube::SIDE_NEGATIVE_Y:
case Cube::SIDE_POSITIVE_Y:
a = Vector2(va.x, va.z);
b = Vector2(vb.x, vb.z);
c = Vector2(vc.x, vc.z);
a = Vector2f(va.x, va.z);
b = Vector2f(vb.x, vb.z);
c = Vector2f(vc.x, vc.z);
break;
case Cube::SIDE_NEGATIVE_Z:
case Cube::SIDE_POSITIVE_Z:
a = Vector2(va.x, va.y);
b = Vector2(vb.x, vb.y);
c = Vector2(vc.x, vc.y);
a = Vector2f(va.x, va.y);
b = Vector2f(vb.x, vb.y);
c = Vector2f(vc.x, vc.y);
break;
default:

View File

@ -1,4 +1,5 @@
#include "voxel_blocky_model.h"
#include "../../util/godot/funcs.h"
#include "../../util/macros.h"
#include "voxel_blocky_library.h"
#include "voxel_mesher_blocky.h" // TODO Only required because of MAX_MATERIALS... could be enough inverting that dependency
@ -39,7 +40,7 @@ bool VoxelBlockyModel::_set(const StringName &p_name, const Variant &p_value) {
Cube::Side side = name_to_side(s);
if (side != Cube::SIDE_COUNT) {
Vector2 v = p_value;
set_cube_uv_side(side, v);
set_cube_uv_side(side, Vector2f(v.x, v.y));
return true;
}
}
@ -54,7 +55,8 @@ bool VoxelBlockyModel::_get(const StringName &p_name, Variant &r_ret) const {
String s = name.substr(VOXEL_ARRAY_LENGTH("cube_tiles/") - 1, name.length());
Cube::Side side = name_to_side(s);
if (side != Cube::SIDE_COUNT) {
r_ret = _cube_tiles[side];
const Vector2f f = _cube_tiles[side];
r_ret = Vector2(f.x, f.y);
return true;
}
}
@ -152,7 +154,7 @@ void VoxelBlockyModel::set_random_tickable(bool rt) {
_random_tickable = rt;
}
void VoxelBlockyModel::set_cube_uv_side(int side, Vector2 tile_pos) {
void VoxelBlockyModel::set_cube_uv_side(int side, Vector2f tile_pos) {
_cube_tiles[side] = tile_pos;
}
@ -192,11 +194,11 @@ static void bake_cube_geometry(
const float sy = 1.0;
for (unsigned int side = 0; side < Cube::SIDE_COUNT; ++side) {
std::vector<Vector3> &positions = baked_data.model.side_positions[side];
std::vector<Vector3f> &positions = baked_data.model.side_positions[side];
positions.resize(4);
for (unsigned int i = 0; i < 4; ++i) {
int corner = Cube::g_side_corners[side][i];
Vector3 p = Cube::g_corner_position[corner];
Vector3f p = Cube::g_corner_position[corner];
if (p.y > 0.9) {
p.y = sy;
}
@ -214,11 +216,11 @@ static void bake_cube_geometry(
// Winding is the same as the one chosen in Cube:: vertices
// I am confused. I read in at least 3 OpenGL tutorials that texture coordinates start at bottom-left (0,0).
// But even though Godot is said to follow OpenGL's convention, the engine starts at top-left!
const Vector2 uv[4] = {
Vector2(e, 1.f - e),
Vector2(1.f - e, 1.f - e),
Vector2(1.f - e, e),
Vector2(e, e),
const Vector2f uv[4] = {
Vector2f(e, 1.f - e),
Vector2f(1.f - e, 1.f - e),
Vector2f(1.f - e, e),
Vector2f(e, e),
};
const float atlas_size = (float)p_atlas_size;
@ -227,7 +229,7 @@ static void bake_cube_geometry(
for (unsigned int side = 0; side < Cube::SIDE_COUNT; ++side) {
baked_data.model.side_uvs[side].resize(4);
std::vector<Vector2> &uvs = baked_data.model.side_uvs[side];
std::vector<Vector2f> &uvs = baked_data.model.side_uvs[side];
for (unsigned int i = 0; i < 4; ++i) {
uvs[i] = (config.get_cube_tile(side) + uv[i]) * s;
}
@ -270,19 +272,20 @@ static void bake_mesh_geometry(VoxelBlockyModel &config, VoxelBlockyModel::Baked
ERR_FAIL_COND(normals.size() == 0);
struct L {
static uint8_t get_sides(Vector3 pos) {
static uint8_t get_sides(Vector3f pos) {
uint8_t mask = 0;
const real_t tolerance = 0.001;
mask |= Math::is_equal_approx(pos.x, real_t(0.0), tolerance) << Cube::SIDE_NEGATIVE_X;
mask |= Math::is_equal_approx(pos.x, real_t(1.0), tolerance) << Cube::SIDE_POSITIVE_X;
mask |= Math::is_equal_approx(pos.y, real_t(0.0), tolerance) << Cube::SIDE_NEGATIVE_Y;
mask |= Math::is_equal_approx(pos.y, real_t(1.0), tolerance) << Cube::SIDE_POSITIVE_Y;
mask |= Math::is_equal_approx(pos.z, real_t(0.0), tolerance) << Cube::SIDE_NEGATIVE_Z;
mask |= Math::is_equal_approx(pos.z, real_t(1.0), tolerance) << Cube::SIDE_POSITIVE_Z;
const float tolerance = 0.001;
mask |= Math::is_equal_approx(pos.x, 0.f, tolerance) << Cube::SIDE_NEGATIVE_X;
mask |= Math::is_equal_approx(pos.x, 1.f, tolerance) << Cube::SIDE_POSITIVE_X;
mask |= Math::is_equal_approx(pos.y, 0.f, tolerance) << Cube::SIDE_NEGATIVE_Y;
mask |= Math::is_equal_approx(pos.y, 1.f, tolerance) << Cube::SIDE_POSITIVE_Y;
mask |= Math::is_equal_approx(pos.z, 0.f, tolerance) << Cube::SIDE_NEGATIVE_Z;
mask |= Math::is_equal_approx(pos.z, 1.f, tolerance) << Cube::SIDE_POSITIVE_Z;
return mask;
}
static bool get_triangle_side(const Vector3 &a, const Vector3 &b, const Vector3 &c, Cube::SideAxis &out_side) {
static bool get_triangle_side(
const Vector3f &a, const Vector3f &b, const Vector3f &c, Cube::SideAxis &out_side) {
const uint8_t m = get_sides(a) & get_sides(b) & get_sides(c);
if (m == 0) {
// At least one of the points doesn't belong to a face
@ -327,32 +330,32 @@ static void bake_mesh_geometry(VoxelBlockyModel &config, VoxelBlockyModel::Baked
FixedArray<HashMap<int, int>, Cube::SIDE_COUNT> added_side_indices;
HashMap<int, int> added_regular_indices;
FixedArray<Vector3, 3> tri_positions;
FixedArray<Vector3f, 3> tri_positions;
VoxelBlockyModel::BakedData::Model &model = baked_data.model;
for (int i = 0; i < indices.size(); i += 3) {
Cube::SideAxis side;
tri_positions[0] = positions[indices[i]];
tri_positions[1] = positions[indices[i + 1]];
tri_positions[2] = positions[indices[i + 2]];
tri_positions[0] = to_vec3f(positions[indices[i]]);
tri_positions[1] = to_vec3f(positions[indices[i + 1]]);
tri_positions[2] = to_vec3f(positions[indices[i + 2]]);
FixedArray<float, 4> tangent;
if (tangents_empty && bake_tangents) {
//If tangents are empty then we calculate them
Vector2 delta_uv1 = uvs[indices[i + 1]] - uvs[indices[i]];
Vector2 delta_uv2 = uvs[indices[i + 2]] - uvs[indices[i]];
Vector3 delta_pos1 = tri_positions[1] - tri_positions[0];
Vector3 delta_pos2 = tri_positions[2] - tri_positions[0];
float r = 1.0f / (delta_uv1[0] * delta_uv2[1] - delta_uv1[1] * delta_uv2[0]);
Vector3 t = (delta_pos1 * delta_uv2[1] - delta_pos2 * delta_uv1[1]) * r;
Vector3 bt = (delta_pos2 * delta_uv1[0] - delta_pos1 * delta_uv2[0]) * r;
const Vector2f delta_uv1 = to_vec2f(uvs[indices[i + 1]] - uvs[indices[i]]);
const Vector2f delta_uv2 = to_vec2f(uvs[indices[i + 2]] - uvs[indices[i]]);
const Vector3f delta_pos1 = tri_positions[1] - tri_positions[0];
const Vector3f delta_pos2 = tri_positions[2] - tri_positions[0];
const float r = 1.0f / (delta_uv1[0] * delta_uv2[1] - delta_uv1[1] * delta_uv2[0]);
const Vector3f t = (delta_pos1 * delta_uv2[1] - delta_pos2 * delta_uv1[1]) * r;
const Vector3f bt = (delta_pos2 * delta_uv1[0] - delta_pos1 * delta_uv2[0]) * r;
tangent[0] = t[0];
tangent[1] = t[1];
tangent[2] = t[2];
tangent[3] = (bt.dot(normals[indices[i]].cross(t))) < 0 ? -1.0f : 1.0f;
tangent[3] = (bt.dot(to_vec3f(normals[indices[i]]).cross(t))) < 0 ? -1.0f : 1.0f;
}
if (L::get_triangle_side(tri_positions[0], tri_positions[1], tri_positions[2], side)) {
@ -369,7 +372,7 @@ static void bake_mesh_geometry(VoxelBlockyModel &config, VoxelBlockyModel::Baked
model.side_indices[side].push_back(next_side_index);
model.side_positions[side].push_back(tri_positions[j]);
model.side_uvs[side].push_back(uvs[indices[i + j]]);
model.side_uvs[side].push_back(to_vec2f(uvs[indices[i + j]]));
if (bake_tangents) {
if (tangents_empty) {
@ -410,8 +413,8 @@ static void bake_mesh_geometry(VoxelBlockyModel &config, VoxelBlockyModel::Baked
if (existing_dst_index == nullptr) {
model.indices.push_back(next_regular_index);
model.positions.push_back(tri_positions[j]);
model.normals.push_back(normals[indices[i + j]]);
model.uvs.push_back(uvs[indices[i + j]]);
model.normals.push_back(to_vec3f(normals[indices[i + j]]));
model.uvs.push_back(to_vec2f(uvs[indices[i + j]]));
if (bake_tangents) {
if (tangents_empty) {

View File

@ -3,6 +3,8 @@
#include "../../constants/cube_tables.h"
#include "../../util/fixed_array.h"
#include "../../util/math/vector2f.h"
#include "../../util/math/vector3f.h"
#include <scene/resources/mesh.h>
#include <vector>
@ -27,16 +29,16 @@ public:
// while the configuration that produced the data can be changed by the user at any time.
struct BakedData {
struct Model {
std::vector<Vector3> positions;
std::vector<Vector3> normals;
std::vector<Vector2> uvs;
std::vector<Vector3f> positions;
std::vector<Vector3f> normals;
std::vector<Vector2f> uvs;
std::vector<int> indices;
std::vector<float> tangents;
// Model sides:
// They are separated because this way we can occlude them easily.
// Due to these defining cube side triangles, normals are known already.
FixedArray<std::vector<Vector3>, Cube::SIDE_COUNT> side_positions;
FixedArray<std::vector<Vector2>, Cube::SIDE_COUNT> side_uvs;
FixedArray<std::vector<Vector3f>, Cube::SIDE_COUNT> side_positions;
FixedArray<std::vector<Vector2f>, Cube::SIDE_COUNT> side_uvs;
FixedArray<std::vector<int>, Cube::SIDE_COUNT> side_indices;
FixedArray<std::vector<float>, Cube::SIDE_COUNT> side_tangents;
@ -131,7 +133,7 @@ public:
return _collision_mask;
}
Vector2 get_cube_tile(int side) const {
Vector2f get_cube_tile(int side) const {
return _cube_tiles[side];
}
@ -168,7 +170,7 @@ private:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void set_cube_uv_side(int side, Vector2 tile_pos);
void set_cube_uv_side(int side, Vector2f tile_pos);
static void _bind_methods();
@ -193,7 +195,7 @@ private:
Color _color;
GeometryType _geometry_type;
FixedArray<Vector2, Cube::SIDE_COUNT> _cube_tiles;
FixedArray<Vector2f, Cube::SIDE_COUNT> _cube_tiles;
Ref<Mesh> _custom_mesh;
std::vector<AABB> _collision_aabbs;
bool _random_tickable = false;

View File

@ -2,6 +2,7 @@
#include "../../constants/cube_tables.h"
#include "../../storage/voxel_buffer.h"
#include "../../util/funcs.h"
#include "../../util/godot/funcs.h"
#include "../../util/span.h"
#include <core/os/os.h>
@ -141,7 +142,7 @@ void generate_blocky_mesh(
// Sides
for (unsigned int side = 0; side < Cube::SIDE_COUNT; ++side) {
const std::vector<Vector3> &side_positions = voxel.model.side_positions[side];
const std::vector<Vector3f> &side_positions = voxel.model.side_positions[side];
const unsigned int vertex_count = side_positions.size();
if (vertex_count == 0) {
@ -190,18 +191,18 @@ void generate_blocky_mesh(
}
}
const std::vector<Vector2> &side_uvs = voxel.model.side_uvs[side];
const std::vector<Vector2f> &side_uvs = voxel.model.side_uvs[side];
const std::vector<float> &side_tangents = voxel.model.side_tangents[side];
// Subtracting 1 because the data is padded
Vector3 pos(x - 1, y - 1, z - 1);
Vector3f pos(x - 1, y - 1, z - 1);
// Append vertices of the faces in one go, don't use push_back
{
const int append_index = arrays.positions.size();
arrays.positions.resize(arrays.positions.size() + vertex_count);
Vector3 *w = arrays.positions.data() + append_index;
Vector3f *w = arrays.positions.data() + append_index;
for (unsigned int i = 0; i < vertex_count; ++i) {
w[i] = side_positions[i] + pos;
}
@ -210,7 +211,7 @@ void generate_blocky_mesh(
{
const int append_index = arrays.uvs.size();
arrays.uvs.resize(arrays.uvs.size() + vertex_count);
memcpy(arrays.uvs.data() + append_index, side_uvs.data(), vertex_count * sizeof(Vector2));
memcpy(arrays.uvs.data() + append_index, side_uvs.data(), vertex_count * sizeof(Vector2f));
}
if (side_tangents.size() > 0) {
@ -223,9 +224,9 @@ void generate_blocky_mesh(
{
const int append_index = arrays.normals.size();
arrays.normals.resize(arrays.normals.size() + vertex_count);
Vector3 *w = arrays.normals.data() + append_index;
Vector3f *w = arrays.normals.data() + append_index;
for (unsigned int i = 0; i < vertex_count; ++i) {
w[i] = Cube::g_side_normals[side];
w[i] = to_vec3f(Cube::g_side_normals[side]);
}
}
@ -237,7 +238,7 @@ void generate_blocky_mesh(
if (bake_occlusion) {
for (unsigned int i = 0; i < vertex_count; ++i) {
Vector3 v = side_positions[i];
Vector3f v = side_positions[i];
// General purpose occlusion colouring.
// TODO Optimize for cubes
@ -290,15 +291,15 @@ void generate_blocky_mesh(
if (voxel.model.positions.size() != 0) {
// TODO Get rid of push_backs
const std::vector<Vector3> &positions = voxel.model.positions;
const std::vector<Vector3f> &positions = voxel.model.positions;
const unsigned int vertex_count = positions.size();
const Color modulate_color = voxel.color;
const std::vector<Vector3> &normals = voxel.model.normals;
const std::vector<Vector2> &uvs = voxel.model.uvs;
const std::vector<Vector3f> &normals = voxel.model.normals;
const std::vector<Vector2f> &uvs = voxel.model.uvs;
const std::vector<float> &tangents = voxel.model.tangents;
const Vector3 pos(x - 1, y - 1, z - 1);
const Vector3f pos(x - 1, y - 1, z - 1);
if (tangents.size() > 0) {
const int append_index = arrays.tangents.size();
@ -485,9 +486,9 @@ void VoxelMesherBlocky::build(VoxelMesher::Output &output, const VoxelMesher::In
PackedColorArray colors;
PackedInt32Array indices;
raw_copy_to(positions, arrays.positions);
raw_copy_to(uvs, arrays.uvs);
raw_copy_to(normals, arrays.normals);
copy_to(positions, arrays.positions);
copy_to(uvs, arrays.uvs);
copy_to(normals, arrays.normals);
raw_copy_to(colors, arrays.colors);
raw_copy_to(indices, arrays.indices);

View File

@ -42,9 +42,9 @@ public:
// Using std::vector because they make this mesher twice as fast than Godot Vectors.
// See why: https://github.com/godotengine/godot/issues/24731
struct Arrays {
std::vector<Vector3> positions;
std::vector<Vector3> normals;
std::vector<Vector2> uvs;
std::vector<Vector3f> positions;
std::vector<Vector3f> normals;
std::vector<Vector2f> uvs;
std::vector<Color> colors;
std::vector<int> indices;
std::vector<float> tangents;

View File

@ -1,6 +1,7 @@
#include "voxel_mesher_cubes.h"
#include "../../storage/voxel_buffer.h"
#include "../../util/funcs.h"
#include "../../util/godot/funcs.h"
#include "../../util/profiling.h"
#include <core/math/geometry_2d.h>
@ -135,27 +136,27 @@ void build_voxel_mesh_as_simple_cubes(
const int vx1 = vx0 + 1;
const int vy1 = vy0 + 1;
Vector3 v0;
Vector3f v0;
v0[xa] = vx0;
v0[ya] = vy0;
v0[za] = d;
Vector3 v1;
Vector3f v1;
v1[xa] = vx1;
v1[ya] = vy0;
v1[za] = d;
Vector3 v2;
Vector3f v2;
v2[xa] = vx0;
v2[ya] = vy1;
v2[za] = d;
Vector3 v3;
Vector3f v3;
v3[xa] = vx1;
v3[ya] = vy1;
v3[za] = d;
Vector3 n;
Vector3f n;
n[za] = side == FACE_SIDE_FRONT ? -1 : 1;
// 2-----3
@ -319,27 +320,27 @@ void build_voxel_mesh_as_greedy_cubes(
const uint8_t material_index = colorf.a < 0.999f;
VoxelMesherCubes::Arrays &arrays = out_arrays_per_material[material_index];
Vector3 v0;
Vector3f v0;
v0[xa] = fx;
v0[ya] = fy;
v0[za] = d;
Vector3 v1;
Vector3f v1;
v1[xa] = rx;
v1[ya] = fy;
v1[za] = d;
Vector3 v2;
Vector3f v2;
v2[xa] = fx;
v2[ya] = ry;
v2[za] = d;
Vector3 v3;
Vector3f v3;
v3[xa] = rx;
v3[ya] = ry;
v3[za] = d;
Vector3 n;
Vector3f n;
n[za] = m.side == FACE_SIDE_FRONT ? -1 : 1;
// 2-----3
@ -518,27 +519,27 @@ void build_voxel_mesh_as_greedy_cubes_atlased(
const uint8_t material_index = m.material_index;
VoxelMesherCubes::Arrays &arrays = out_arrays_per_material[material_index];
Vector3 v0;
Vector3f v0;
v0[xa] = fx;
v0[ya] = fy;
v0[za] = d;
Vector3 v1;
Vector3f v1;
v1[xa] = rx;
v1[ya] = fy;
v1[za] = d;
Vector3 v2;
Vector3f v2;
v2[xa] = fx;
v2[ya] = ry;
v2[za] = d;
Vector3 v3;
Vector3f v3;
v3[xa] = rx;
v3[ya] = ry;
v3[za] = d;
Vector3 n;
Vector3f n;
n[za] = m.side == FACE_SIDE_FRONT ? -1 : 1;
// 2-----3
@ -642,26 +643,26 @@ Ref<Image> make_greedy_atlas(
// tmp.instance();
// tmp->create(im.size_x, im.size_y, false, debug_im->get_format());
// tmp->fill(Color(Math::randf(), Math::randf(), Math::randf()));
// debug_im->blit_rect(tmp, Rect2(0, 0, tmp->get_width(), tmp->get_height()), Vector2(dst_pos));
// debug_im->blit_rect(tmp, Rect2(0, 0, tmp->get_width(), tmp->get_height()), Vector2f(dst_pos));
// }
// debug_im->save_png("debug_atlas_packing.png");
// Update UVs
const Vector2 uv_scale(1.f / float(result_size.x), 1.f / float(result_size.y));
const Vector2f uv_scale(1.f / float(result_size.x), 1.f / float(result_size.y));
for (unsigned int i = 0; i < atlas_data.images.size(); ++i) {
const VoxelMesherCubes::GreedyAtlasData::ImageInfo &im = atlas_data.images[i];
VoxelMesherCubes::Arrays &surface = surfaces[im.surface_index];
ERR_FAIL_COND_V(im.first_vertex_index + 4 > surface.uvs.size(), Ref<Image>());
const unsigned int vi = im.first_vertex_index;
const Vector2 pos(result_points[i]);
const Vector2f pos(to_vec2f(result_points[i]));
// 2-----3
// | |
// | |
// 0-----1
surface.uvs[vi] = pos * uv_scale;
surface.uvs[vi + 1] = (pos + Vector2(im.size_x, 0)) * uv_scale;
surface.uvs[vi + 2] = (pos + Vector2(0, im.size_y)) * uv_scale;
surface.uvs[vi + 3] = (pos + Vector2(im.size_x, im.size_y)) * uv_scale;
surface.uvs[vi + 1] = (pos + Vector2f(im.size_x, 0)) * uv_scale;
surface.uvs[vi + 2] = (pos + Vector2f(0, im.size_y)) * uv_scale;
surface.uvs[vi + 3] = (pos + Vector2f(im.size_x, im.size_y)) * uv_scale;
}
// Create image
@ -903,8 +904,8 @@ void VoxelMesherCubes::build(VoxelMesher::Output &output, const VoxelMesher::Inp
PackedVector3Array normals;
PackedInt32Array indices;
raw_copy_to(positions, arrays.positions);
raw_copy_to(normals, arrays.normals);
copy_to(positions, arrays.positions);
copy_to(normals, arrays.normals);
raw_copy_to(indices, arrays.indices);
mesh_arrays[Mesh::ARRAY_VERTEX] = positions;
@ -918,7 +919,7 @@ void VoxelMesherCubes::build(VoxelMesher::Output &output, const VoxelMesher::Inp
}
if (arrays.uvs.size() > 0) {
PackedVector2Array uvs;
raw_copy_to(uvs, arrays.uvs);
copy_to(uvs, arrays.uvs);
mesh_arrays[Mesh::ARRAY_TEX_UV] = uvs;
}
}

View File

@ -1,8 +1,11 @@
#ifndef VOXEL_MESHER_CUBES_H
#define VOXEL_MESHER_CUBES_H
#include "../../util/math/vector2f.h"
#include "../../util/math/vector3f.h"
#include "../voxel_mesher.h"
#include "voxel_color_palette.h"
#include <vector>
namespace zylann::voxel {
@ -62,10 +65,10 @@ public:
// Using std::vector because they make this mesher twice as fast than Godot Vectors.
// See why: https://github.com/godotengine/godot/issues/24731
struct Arrays {
std::vector<Vector3> positions;
std::vector<Vector3> normals;
std::vector<Vector3f> positions;
std::vector<Vector3f> normals;
std::vector<Color> colors;
std::vector<Vector2> uvs;
std::vector<Vector2f> uvs;
std::vector<int> indices;
void clear() {

View File

@ -3,13 +3,13 @@
#include "../../storage/voxel_buffer_internal.h"
#include "../../util/math/funcs.h"
#include <core/math/vector3.h>
#include "../../util/math/vector3f.h"
namespace zylann::voxel::dmc {
struct HermiteValue {
float sdf; // Signed "distance" to surface
Vector3 gradient; // Derivation of the density
Vector3f gradient; // Derivation of the density
HermiteValue() : sdf(1.0) {}
};
@ -27,7 +27,7 @@ inline HermiteValue get_hermite_value(
v.sdf = voxels.get_voxel_f(x, y, z, VoxelBufferInternal::CHANNEL_SDF);
Vector3 gradient;
Vector3f gradient;
gradient.x = get_isolevel_clamped(voxels, x + 1, y, z) - get_isolevel_clamped(voxels, x - 1, y, z);
gradient.y = get_isolevel_clamped(voxels, x, y + 1, z) - get_isolevel_clamped(voxels, x, y - 1, z);
@ -38,7 +38,7 @@ inline HermiteValue get_hermite_value(
return v;
}
inline HermiteValue get_interpolated_hermite_value(const VoxelBufferInternal &voxels, Vector3 pos) {
inline HermiteValue get_interpolated_hermite_value(const VoxelBufferInternal &voxels, Vector3f pos) {
int x0 = static_cast<int>(pos.x);
int y0 = static_cast<int>(pos.y);
int z0 = static_cast<int>(pos.z);
@ -64,7 +64,7 @@ inline HermiteValue get_interpolated_hermite_value(const VoxelBufferInternal &vo
HermiteValue v6 = get_hermite_value(voxels, x1, y1, z1);
HermiteValue v7 = get_hermite_value(voxels, x0, y1, z1);
Vector3 rpos = pos - Vector3(x0, y0, z0);
Vector3f rpos = pos - Vector3f(x0, y0, z0);
HermiteValue v;
v.sdf = math::interpolate(v0.sdf, v1.sdf, v2.sdf, v3.sdf, v4.sdf, v5.sdf, v6.sdf, v7.sdf, rpos);

View File

@ -1,5 +1,6 @@
#include "mesh_builder.h"
#include "../../util/funcs.h"
#include "../../util/godot/funcs.h"
#include <scene/resources/mesh.h>
namespace zylann::voxel::dmc {
@ -33,8 +34,8 @@ Array MeshBuilder::commit(bool wireframe) {
PackedVector3Array normals;
PackedInt32Array indices;
raw_copy_to(positions, _positions);
raw_copy_to(normals, _normals);
copy_to(positions, _positions);
copy_to(normals, _normals);
raw_copy_to(indices, _indices);
clear();

View File

@ -1,8 +1,9 @@
#ifndef MESH_BUILDER_H
#define MESH_BUILDER_H
#include <core/math/vector3.h>
#include "../../util/math/vector3f.h"
#include <core/templates/map.h>
#include <core/variant/array.h>
#include <vector>
namespace zylann::voxel::dmc {
@ -12,10 +13,10 @@ class MeshBuilder {
public:
MeshBuilder() : _reused_vertices(0) {}
inline void add_vertex(Vector3 position, Vector3 normal) {
inline void add_vertex(Vector3f position, Vector3f normal) {
int i = 0;
Map<Vector3, int>::Element *e = _position_to_index.find(position);
Map<Vector3f, int>::Element *e = _position_to_index.find(position);
if (e) {
i = e->get();
@ -41,10 +42,10 @@ public:
}
private:
std::vector<Vector3> _positions;
std::vector<Vector3> _normals;
std::vector<Vector3f> _positions;
std::vector<Vector3f> _normals;
std::vector<int> _indices;
Map<Vector3, int> _position_to_index;
Map<Vector3f, int> _position_to_index;
int _reused_vertices;
};

View File

@ -1,8 +1,10 @@
#include "voxel_mesher_dmc.h"
#include "../../constants/cube_tables.h"
#include "../../util/godot/funcs.h"
#include "marching_cubes_tables.h"
#include "mesh_builder.h"
#include "octree_tables.h"
#include <core/os/time.h>
// Dual marching cubes
@ -28,7 +30,7 @@ struct VoxelAccess {
return dmc::get_hermite_value(buffer, x + offset.x, y + offset.y, z + offset.z);
}
inline HermiteValue get_interpolated_hermite_value(Vector3 pos) const {
inline HermiteValue get_interpolated_hermite_value(Vector3f pos) const {
pos.x += offset.x;
pos.y += offset.y;
pos.z += offset.z;
@ -93,28 +95,28 @@ bool can_split(Vector3i node_origin, int node_size, const VoxelAccess &voxels, f
Vector3i(origin.x + hstep, /**/ origin.y + hstep, /**/ origin.z + hstep) // 26
};
Vector3 positions_ratio[19] = { Vector3(0.5, 0.0, 0.0), //
Vector3(1.0, 0.0, 0.5), //
Vector3(0.5, 0.0, 1.0), //
Vector3(0.0, 0.0, 0.5), //
Vector3f positions_ratio[19] = { Vector3f(0.5, 0.0, 0.0), //
Vector3f(1.0, 0.0, 0.5), //
Vector3f(0.5, 0.0, 1.0), //
Vector3f(0.0, 0.0, 0.5), //
Vector3(0.0, 0.5, 0.0), //
Vector3(1.0, 0.5, 0.0), //
Vector3(1.0, 0.5, 1.0), //
Vector3(0.0, 0.5, 1.0), //
Vector3f(0.0, 0.5, 0.0), //
Vector3f(1.0, 0.5, 0.0), //
Vector3f(1.0, 0.5, 1.0), //
Vector3f(0.0, 0.5, 1.0), //
Vector3(0.5, 1.0, 0.0), //
Vector3(1.0, 1.0, 0.5), //
Vector3(0.5, 1.0, 1.0), //
Vector3(0.0, 1.0, 0.5), //
Vector3f(0.5, 1.0, 0.0), //
Vector3f(1.0, 1.0, 0.5), //
Vector3f(0.5, 1.0, 1.0), //
Vector3f(0.0, 1.0, 0.5), //
Vector3(0.5, 0.0, 0.5), //
Vector3(0.5, 0.5, 0.0), //
Vector3(1.0, 0.5, 0.5), //
Vector3(0.5, 0.5, 1.0), //
Vector3(0.0, 0.5, 0.5), //
Vector3(0.5, 1.0, 0.5), //
Vector3(0.5, 0.5, 0.5) };
Vector3f(0.5, 0.0, 0.5), //
Vector3f(0.5, 0.5, 0.0), //
Vector3f(1.0, 0.5, 0.5), //
Vector3f(0.5, 0.5, 1.0), //
Vector3f(0.0, 0.5, 0.5), //
Vector3f(0.5, 1.0, 0.5), //
Vector3f(0.5, 0.5, 0.5) };
float error = 0.0;
@ -138,8 +140,8 @@ bool can_split(Vector3i node_origin, int node_size, const VoxelAccess &voxels, f
return false;
}
inline Vector3 get_center(const OctreeNode *node) {
return Vector3(node->origin) + 0.5 * Vector3(node->size, node->size, node->size);
inline Vector3f get_center(const OctreeNode *node) {
return to_vec3f(node->origin) + 0.5 * Vector3f(node->size, node->size, node->size);
}
class OctreeBuilderTopDown {
@ -300,7 +302,7 @@ Array generate_debug_octree_mesh(OctreeNode *root, int scale) {
void operator()(OctreeNode *node, int depth) {
float shrink = depth * 0.005;
Vector3 o = Vector3(node->origin) + Vector3(shrink, shrink, shrink);
Vector3f o = to_vec3f(node->origin) + Vector3f(shrink, shrink, shrink);
float s = node->size - 2.0 * shrink;
Color col(1.0, (float)depth / (float)max_depth, 0.0);
@ -308,7 +310,8 @@ Array generate_debug_octree_mesh(OctreeNode *root, int scale) {
int vi = arrays->positions.size();
for (int i = 0; i < Cube::CORNER_COUNT; ++i) {
arrays->positions.push_back(o + s * Cube::g_corner_position[i]);
const Vector3f pf = o + s * Cube::g_corner_position[i];
arrays->positions.push_back(Vector3(pf.x, pf.y, pf.z));
arrays->colors.push_back(col);
}
@ -357,7 +360,8 @@ Array generate_debug_dual_grid_mesh(const DualGrid &grid, int scale) {
for (int j = 0; j < 8; ++j) {
// Vector3 p = Vector3(g_octant_position[j][0], g_octant_position[j][1], g_octant_position[j][2]);
// Vector3 n = (Vector3(0.5, 0.5, 0.5) - p).normalized();
positions.push_back(cell.corners[j]); // + n * 0.01);
const Vector3f pf = cell.corners[j]; // + n * 0.01);
positions.push_back(Vector3(pf.x, pf.y, pf.z));
}
for (int j = 0; j < Cube::EDGE_COUNT; ++j) {
@ -406,177 +410,177 @@ inline bool is_border_front(const OctreeNode *node, int root_size) {
return node->origin.z + node->size == root_size;
}
inline Vector3 get_center_back(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_back(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size * 0.5;
p.y += node->size * 0.5;
return p;
}
inline Vector3 get_center_front(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_front(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size * 0.5;
p.y += node->size * 0.5;
p.z += node->size;
return p;
}
inline Vector3 get_center_left(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_left(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.y += node->size * 0.5;
p.z += node->size * 0.5;
return p;
}
inline Vector3 get_center_right(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_right(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
p.y += node->size * 0.5;
p.z += node->size * 0.5;
return p;
}
inline Vector3 get_center_top(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_top(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size * 0.5;
p.y += node->size;
p.z += node->size * 0.5;
return p;
}
inline Vector3 get_center_bottom(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_bottom(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size * 0.5;
p.z += node->size * 0.5;
return p;
}
inline Vector3 get_center_back_top(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_back_top(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size * 0.5;
p.y += node->size;
return p;
}
inline Vector3 get_center_back_bottom(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_back_bottom(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size * 0.5;
return p;
}
inline Vector3 get_center_front_top(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_front_top(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size * 0.5;
p.y += node->size;
p.z += node->size;
return p;
}
inline Vector3 get_center_front_bottom(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_front_bottom(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size * 0.5;
p.z += node->size;
return p;
}
inline Vector3 get_center_left_top(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_left_top(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.y += node->size;
p.z += node->size * 0.5;
return p;
}
inline Vector3 get_center_left_bottom(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_left_bottom(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.z += node->size * 0.5;
return p;
}
inline Vector3 get_center_right_top(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_right_top(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
p.y += node->size;
p.z += node->size * 0.5;
return p;
}
inline Vector3 get_center_right_bottom(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_right_bottom(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
p.z += node->size * 0.5;
return p;
}
inline Vector3 get_center_back_left(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_back_left(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.y += node->size * 0.5;
return p;
}
inline Vector3 get_center_front_left(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_front_left(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.y += node->size * 0.5;
p.z += node->size;
return p;
}
inline Vector3 get_center_back_right(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_back_right(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
p.y += node->size * 0.5;
return p;
}
inline Vector3 get_center_front_right(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_center_front_right(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
p.y += node->size * 0.5;
p.z += node->size;
return p;
}
inline Vector3 get_corner1(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_corner1(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
return p;
}
inline Vector3 get_corner2(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_corner2(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
p.z += node->size;
return p;
}
inline Vector3 get_corner3(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_corner3(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.z += node->size;
return p;
}
inline Vector3 get_corner4(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_corner4(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.y += node->size;
return p;
}
inline Vector3 get_corner5(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_corner5(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
p.y += node->size;
return p;
}
inline Vector3 get_corner6(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_corner6(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.x += node->size;
p.y += node->size;
p.z += node->size;
return p;
}
inline Vector3 get_corner7(const OctreeNode *node) {
Vector3 p = node->origin;
inline Vector3f get_corner7(const OctreeNode *node) {
Vector3f p = to_vec3f(node->origin);
p.y += node->size;
p.z += node->size;
return p;
@ -607,8 +611,8 @@ private:
void face_proc_xz(OctreeNode *n0, OctreeNode *n1);
};
inline void add_cell(DualGrid &grid, const Vector3 c0, const Vector3 c1, const Vector3 c2, const Vector3 c3,
const Vector3 c4, const Vector3 c5, const Vector3 c6, const Vector3 c7) {
inline void add_cell(DualGrid &grid, const Vector3f c0, const Vector3f c1, const Vector3f c2, const Vector3f c3,
const Vector3f c4, const Vector3f c5, const Vector3f c6, const Vector3f c7) {
DualCell cell;
cell.corners[0] = c0;
cell.corners[1] = c1;
@ -655,7 +659,7 @@ void DualGridGenerator::create_border_cells(const OctreeNode *n0, const OctreeNo
// Generate back bottom corner cells
if (is_border_left(n0)) {
add_cell(grid, n0->origin, get_center_back_bottom(n0), get_center_bottom(n0),
add_cell(grid, to_vec3f(n0->origin), get_center_back_bottom(n0), get_center_bottom(n0),
get_center_left_bottom(n0), get_center_back_left(n0), get_center_back(n0), get_center(n0),
get_center_left(n0));
}
@ -1032,8 +1036,8 @@ void DualGridGenerator::node_proc(OctreeNode *node) {
vert_proc(children[0], children[1], children[2], children[3], children[4], children[5], children[6], children[7]);
}
inline Vector3 interpolate(
const Vector3 &v0, const Vector3 &v1, const HermiteValue &val0, const HermiteValue &val1, Vector3 &out_normal) {
inline Vector3f interpolate(const Vector3f &v0, const Vector3f &v1, const HermiteValue &val0, const HermiteValue &val1,
Vector3f &out_normal) {
if (Math::abs(val0.sdf - SURFACE_ISO_LEVEL) <= FLT_EPSILON) {
out_normal = val0.gradient;
out_normal.normalize();
@ -1059,7 +1063,7 @@ inline Vector3 interpolate(
return v0 + mu * (v1 - v0);
}
void polygonize_cell_marching_squares(const Vector3 *cube_corners, const HermiteValue *cube_values, float max_distance,
void polygonize_cell_marching_squares(const Vector3f *cube_corners, const HermiteValue *cube_values, float max_distance,
MeshBuilder &mesh_builder, const int *corner_map) {
// Note:
// Using Ogre's implementation directly resulted in inverted result, because it expects density values instead of
@ -1086,8 +1090,8 @@ void polygonize_cell_marching_squares(const Vector3 *cube_corners, const Hermite
int edge = MarchingCubes::ms_edges[square_index];
// Find the intersection vertices.
Vector3 intersection_points[8];
Vector3 intersection_normals[8];
Vector3f intersection_points[8];
Vector3f intersection_normals[8];
intersection_points[0] = cube_corners[corner_map[0]];
intersection_points[2] = cube_corners[corner_map[1]];
@ -1161,8 +1165,8 @@ static const int g_corner_map_bottom[4] = { 3, 2, 1, 0 };
} // namespace MarchingSquares
void add_marching_squares_skirts(const Vector3 *corners, const HermiteValue *values, MeshBuilder &mesh_builder,
Vector3 min_pos, Vector3 max_pos) {
void add_marching_squares_skirts(const Vector3f *corners, const HermiteValue *values, MeshBuilder &mesh_builder,
Vector3f min_pos, Vector3f max_pos) {
float max_distance = 0.2f; // Max distance to the isosurface
if (corners[0].z == min_pos.z) {
@ -1191,7 +1195,7 @@ void add_marching_squares_skirts(const Vector3 *corners, const HermiteValue *val
}
}
void polygonize_cell_marching_cubes(const Vector3 *corners, const HermiteValue *values, MeshBuilder &mesh_builder) {
void polygonize_cell_marching_cubes(const Vector3f *corners, const HermiteValue *values, MeshBuilder &mesh_builder) {
unsigned char case_index = 0;
for (int i = 0; i < 8; ++i) {
@ -1208,8 +1212,8 @@ void polygonize_cell_marching_cubes(const Vector3 *corners, const HermiteValue *
}
// Find the intersection vertices
Vector3 intersection_points[12];
Vector3 intersection_normals[12];
Vector3f intersection_points[12];
Vector3f intersection_normals[12];
if (edge & 1) {
intersection_points[0] = interpolate(corners[0], corners[1], values[0], values[1], intersection_normals[0]);
}
@ -1264,7 +1268,7 @@ void polygonize_cell_marching_cubes(const Vector3 *corners, const HermiteValue *
void polygonize_dual_cell(
const DualCell &cell, const VoxelAccess &voxels, MeshBuilder &mesh_builder, bool skirts_enabled) {
const Vector3 *corners = cell.corners;
const Vector3f *corners = cell.corners;
HermiteValue values[8];
if (cell.has_values) {
@ -1279,7 +1283,7 @@ void polygonize_dual_cell(
if (skirts_enabled) {
add_marching_squares_skirts(
corners, values, mesh_builder, Vector3(), (voxels.buffer.get_size() + voxels.offset));
corners, values, mesh_builder, Vector3f(), to_vec3f(voxels.buffer.get_size() + voxels.offset));
}
}
@ -1292,14 +1296,14 @@ inline void polygonize_dual_grid(
void polygonize_volume_directly(const VoxelBufferInternal &voxels, Vector3i min, Vector3i size,
MeshBuilder &mesh_builder, bool skirts_enabled) {
Vector3 corners[8];
Vector3f corners[8];
HermiteValue values[8];
const Vector3i max = min + size;
const Vector3 minf = min;
const Vector3f minf = to_vec3f(min);
const Vector3 min_vertex_pos = Vector3();
const Vector3 max_vertex_pos = voxels.get_size() - 2 * min;
const Vector3f min_vertex_pos = Vector3f();
const Vector3f max_vertex_pos = to_vec3f(voxels.get_size() - 2 * min);
for (int z = min.z; z < max.z; ++z) {
for (int x = min.x; x < max.x; ++x) {
@ -1313,14 +1317,14 @@ void polygonize_volume_directly(const VoxelBufferInternal &voxels, Vector3i min,
values[6] = get_hermite_value(voxels, x + 1, y + 1, z + 1);
values[7] = get_hermite_value(voxels, x, y + 1, z + 1);
corners[0] = Vector3(x, y, z);
corners[1] = Vector3(x + 1, y, z);
corners[2] = Vector3(x + 1, y, z + 1);
corners[3] = Vector3(x, y, z + 1);
corners[4] = Vector3(x, y + 1, z);
corners[5] = Vector3(x + 1, y + 1, z);
corners[6] = Vector3(x + 1, y + 1, z + 1);
corners[7] = Vector3(x, y + 1, z + 1);
corners[0] = Vector3f(x, y, z);
corners[1] = Vector3f(x + 1, y, z);
corners[2] = Vector3f(x + 1, y, z + 1);
corners[3] = Vector3f(x, y, z + 1);
corners[4] = Vector3f(x, y + 1, z);
corners[5] = Vector3f(x + 1, y + 1, z);
corners[6] = Vector3f(x + 1, y + 1, z + 1);
corners[7] = Vector3f(x, y + 1, z + 1);
for (int i = 0; i < 8; ++i) {
corners[i] -= minf;

View File

@ -43,11 +43,11 @@ struct OctreeNode {
};
struct DualCell {
Vector3 corners[8];
Vector3f corners[8];
HermiteValue values[8];
bool has_values = false;
inline void set_corner(int i, Vector3 vertex, HermiteValue value) {
inline void set_corner(int i, Vector3f vertex, HermiteValue value) {
CRASH_COND(i < 0 || i >= 8);
corners[i] = vertex;
values[i] = value;
@ -115,7 +115,7 @@ protected:
private:
struct Parameters {
real_t geometric_error = 0.1;
float geometric_error = 0.1;
MeshMode mesh_mode = MESH_NORMAL;
SimplifyMode simplify_mode = SIMPLIFY_OCTREE_BOTTOM_UP;
SeamMode seam_mode = SEAM_NONE;
@ -135,10 +135,10 @@ private:
static thread_local Cache _cache;
struct Stats {
real_t octree_build_time = 0;
real_t dualgrid_derivation_time = 0;
real_t meshing_time = 0;
real_t commit_time = 0;
float octree_build_time = 0;
float dualgrid_derivation_time = 0;
float meshing_time = 0;
float commit_time = 0;
};
Stats _stats;

View File

@ -1,6 +1,7 @@
#include "transvoxel.h"
#include "../../constants/cube_tables.h"
#include "../../util/funcs.h"
#include "../../util/godot/funcs.h"
#include "../../util/profiling.h"
#include "transvoxel_tables.cpp"
@ -13,7 +14,7 @@ inline uint8_t sign_f(float v) {
return v < 0.f;
}
Vector3 get_border_offset(const Vector3 pos, const int lod_index, const Vector3i block_size) {
Vector3f get_border_offset(const Vector3f pos, const int lod_index, const Vector3i block_size) {
// When transition meshes are inserted between blocks of different LOD, we need to make space for them.
// Secondary vertex positions can be calculated by linearly transforming positions inside boundary cells
// so that the full-size cell is scaled to a smaller size that allows space for between one and three
@ -21,7 +22,7 @@ Vector3 get_border_offset(const Vector3 pos, const int lod_index, const Vector3i
// entire block. This can be accomplished by computing offsets (Δx, Δy, Δz) for the coordinates (x, y, z)
// in any boundary cell.
Vector3 delta;
Vector3f delta;
const float p2k = 1 << lod_index; // 2 ^ lod
const float p2mk = 1.f / p2k; // 2 ^ (-lod)
@ -45,14 +46,14 @@ Vector3 get_border_offset(const Vector3 pos, const int lod_index, const Vector3i
return delta;
}
inline Vector3 project_border_offset(Vector3 delta, Vector3 normal) {
inline Vector3f project_border_offset(Vector3f delta, Vector3f normal) {
// Secondary position can be obtained with the following formula:
//
// | x | | 1 - nx² , -nx * ny , -nx * nz | | Δx |
// | y | + | -nx * ny , 1 - ny² , -ny * nz | * | Δy |
// | z | | -nx * nz , -ny * nz , 1 - nz² | | Δz |
//
return Vector3((1 - normal.x * normal.x) * delta.x /**/ - normal.y * normal.x * delta.y /* */ -
return Vector3f((1 - normal.x * normal.x) * delta.x /**/ - normal.y * normal.x * delta.y /* */ -
normal.z * normal.x * delta.z,
/**/ -normal.x * normal.y * delta.x + (1 - normal.y * normal.y) * delta.y /* */ -
normal.z * normal.y * delta.z,
@ -60,9 +61,9 @@ inline Vector3 project_border_offset(Vector3 delta, Vector3 normal) {
(1 - normal.z * normal.z) * delta.z);
}
inline Vector3 get_secondary_position(
const Vector3 primary, const Vector3 normal, const int lod_index, const Vector3i block_size) {
Vector3 delta = get_border_offset(primary, lod_index, block_size);
inline Vector3f get_secondary_position(
const Vector3f primary, const Vector3f normal, const int lod_index, const Vector3i block_size) {
Vector3f delta = get_border_offset(primary, lod_index, block_size);
delta = project_border_offset(delta, normal);
return primary + delta;
}
@ -91,18 +92,18 @@ inline uint8_t get_border_mask(const Vector3i &pos, const Vector3i &block_size)
return mask;
}
inline Vector3 normalized_not_null(Vector3 n) {
real_t lengthsq = n.length_squared();
inline Vector3f normalized_not_null(Vector3f n) {
const float lengthsq = n.length_squared();
if (lengthsq == 0) {
return Vector3(0, 1, 0);
return Vector3f(0, 1, 0);
} else {
real_t length = Math::sqrt(lengthsq);
return Vector3(n.x / length, n.y / length, n.z / length);
const float length = Math::sqrt(lengthsq);
return Vector3f(n.x / length, n.y / length, n.z / length);
}
}
inline Vector3i dir_to_prev_vec(uint8_t dir) {
//return g_corner_dirs[mask] - Vector3(1,1,1);
//return g_corner_dirs[mask] - Vector3f(1,1,1);
return Vector3i(-(dir & 1), -((dir >> 1) & 1), -((dir >> 2) & 1));
}
@ -123,7 +124,7 @@ inline float sdf_as_float(double v) {
}
template <typename Sdf_T>
inline Vector3 get_corner_gradient(unsigned int data_index, Span<const Sdf_T> sdf_data, const Vector3i block_size) {
inline Vector3f get_corner_gradient(unsigned int data_index, Span<const Sdf_T> sdf_data, const Vector3i block_size) {
const unsigned int n010 = 1; // Y+1
const unsigned int n100 = block_size.y; // X+1
const unsigned int n001 = block_size.y * block_size.x; // Z+1
@ -136,7 +137,7 @@ inline Vector3 get_corner_gradient(unsigned int data_index, Span<const Sdf_T> sd
const float pz = sdf_as_float(sdf_data[data_index + n001]);
//get_gradient_normal(nx, px, ny, py, nz, pz, cell_samples[i]);
return Vector3(nx - px, ny - py, nz - pz);
return Vector3f(nx - px, ny - py, nz - pz);
}
inline uint32_t pack_bytes(const FixedArray<uint8_t, 4> &a) {
@ -144,20 +145,21 @@ inline uint32_t pack_bytes(const FixedArray<uint8_t, 4> &a) {
}
void add_texture_data(
std::vector<Vector2> &uv, unsigned int packed_indices, FixedArray<uint8_t, MAX_TEXTURE_BLENDS> weights) {
std::vector<Vector2f> &uv, unsigned int packed_indices, FixedArray<uint8_t, MAX_TEXTURE_BLENDS> weights) {
struct IntUV {
uint32_t x;
uint32_t y;
};
static_assert(sizeof(IntUV) == sizeof(Vector2), "Expected same binary size");
uv.push_back(Vector2());
static_assert(sizeof(IntUV) == sizeof(Vector2f), "Expected same binary size");
uv.push_back(Vector2f());
IntUV &iuv = *(reinterpret_cast<IntUV *>(&uv.back()));
//print_line(String("{0}, {1}, {2}, {3}").format(varray(weights[0], weights[1], weights[2], weights[3])));
iuv.x = packed_indices;
iuv.y = pack_bytes(weights);
}
template <unsigned int NVoxels> struct CellTextureDatas {
template <unsigned int NVoxels>
struct CellTextureDatas {
uint32_t packed_indices = 0;
FixedArray<uint8_t, MAX_TEXTURE_BLENDS> indices;
FixedArray<FixedArray<uint8_t, MAX_TEXTURE_BLENDS>, NVoxels> weights;
@ -259,17 +261,21 @@ inline void get_cell_texture_data(CellTextureDatas<NVoxels> &cell_textures,
}
}
template <typename Sdf_T> inline Sdf_T get_isolevel() = delete;
template <typename Sdf_T>
inline Sdf_T get_isolevel() = delete;
template <> inline uint8_t get_isolevel<uint8_t>() {
template <>
inline uint8_t get_isolevel<uint8_t>() {
return 128;
}
template <> inline uint16_t get_isolevel<uint16_t>() {
template <>
inline uint16_t get_isolevel<uint16_t>() {
return 32768;
}
template <> inline float get_isolevel<float>() {
template <>
inline float get_isolevel<float>() {
return 0.f;
}
@ -516,14 +522,14 @@ void build_regular_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData texture_i
//const int ti1 = 0x100 - t;
//const Vector3i primary = p0 * ti0 + p1 * ti1;
const Vector3 primaryf = Vector3(p0) * t0 + Vector3(p1) * t1;
const Vector3 cg0 = get_corner_gradient<Sdf_T>(
const Vector3f primaryf = to_vec3f(p0) * t0 + to_vec3f(p1) * t1;
const Vector3f cg0 = get_corner_gradient<Sdf_T>(
corner_data_indices[v0], sdf_data, block_size_with_padding);
const Vector3 cg1 = get_corner_gradient<Sdf_T>(
const Vector3f cg1 = get_corner_gradient<Sdf_T>(
corner_data_indices[v1], sdf_data, block_size_with_padding);
const Vector3 normal = normalized_not_null(cg0 * t0 + cg1 * t1);
const Vector3f normal = normalized_not_null(cg0 * t0 + cg1 * t1);
Vector3 secondary;
Vector3f secondary;
uint16_t border_mask = cell_border_mask;
if (cell_border_mask > 0) {
@ -559,12 +565,12 @@ void build_regular_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData texture_i
// This cell owns the vertex, so it should be created.
const Vector3i primary = p1;
const Vector3 primaryf = primary;
const Vector3 cg1 =
const Vector3f primaryf = to_vec3f(primary);
const Vector3f cg1 =
get_corner_gradient<Sdf_T>(corner_data_indices[v1], sdf_data, block_size_with_padding);
const Vector3 normal = normalized_not_null(cg1);
const Vector3f normal = normalized_not_null(cg1);
Vector3 secondary;
Vector3f secondary;
uint16_t border_mask = cell_border_mask;
if (cell_border_mask > 0) {
@ -606,13 +612,13 @@ void build_regular_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData texture_i
const unsigned int vi = t == 0 ? v1 : v0;
const Vector3i primary = t == 0 ? p1 : p0;
const Vector3 primaryf = primary;
const Vector3 cg = get_corner_gradient<Sdf_T>(
const Vector3f primaryf = to_vec3f(primary);
const Vector3f cg = get_corner_gradient<Sdf_T>(
corner_data_indices[vi], sdf_data, block_size_with_padding);
const Vector3 normal = normalized_not_null(cg);
const Vector3f normal = normalized_not_null(cg);
// TODO This bit of code is repeated several times, factor it?
Vector3 secondary;
Vector3f secondary;
uint16_t border_mask = cell_border_mask;
if (cell_border_mask > 0) {
@ -893,7 +899,7 @@ void build_transition_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData textur
CRASH_COND(case_code > 511);
// TODO We may not need all of them!
FixedArray<Vector3, 13> cell_gradients;
FixedArray<Vector3f, 13> cell_gradients;
for (unsigned int i = 0; i < 9; ++i) {
const unsigned int di = cell_data_indices[i];
@ -904,7 +910,7 @@ void build_transition_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData textur
const float py = sdf_as_float(sdf_data[di + n010]);
const float pz = sdf_as_float(sdf_data[di + n001]);
cell_gradients[i] = Vector3(nx - px, ny - py, nz - pz);
cell_gradients[i] = Vector3f(nx - px, ny - py, nz - pz);
}
cell_gradients[0x9] = cell_gradients[0];
cell_gradients[0xA] = cell_gradients[2];
@ -1002,17 +1008,17 @@ void build_transition_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData textur
const Vector3i p0 = cell_positions[index_vertex_a];
const Vector3i p1 = cell_positions[index_vertex_b];
const Vector3 n0 = cell_gradients[index_vertex_a];
const Vector3 n1 = cell_gradients[index_vertex_b];
const Vector3f n0 = cell_gradients[index_vertex_a];
const Vector3f n1 = cell_gradients[index_vertex_b];
//Vector3i primary = p0 * ti0 + p1 * ti1;
const Vector3 primaryf = Vector3(p0) * t0 + Vector3(p1) * t1;
const Vector3 normal = normalized_not_null(n0 * t0 + n1 * t1);
const Vector3f primaryf = to_vec3f(p0) * t0 + to_vec3f(p1) * t1;
const Vector3f normal = normalized_not_null(n0 * t0 + n1 * t1);
const bool fullres_side = (index_vertex_a < 9 || index_vertex_b < 9);
uint16_t border_mask = cell_border_mask;
Vector3 secondary;
Vector3f secondary;
if (fullres_side) {
secondary = get_secondary_position(primaryf, normal, 0, block_size_scaled);
border_mask |=
@ -1075,13 +1081,13 @@ void build_transition_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData textur
// Going to create a new vertex
const Vector3i primary = cell_positions[cell_index];
const Vector3 primaryf = primary;
const Vector3 normal = normalized_not_null(cell_gradients[cell_index]);
const Vector3f primaryf = to_vec3f(primary);
const Vector3f normal = normalized_not_null(cell_gradients[cell_index]);
const bool fullres_side = (cell_index < 9);
uint16_t border_mask = cell_border_mask;
Vector3 secondary;
Vector3f secondary;
if (fullres_side) {
secondary = get_secondary_position(primaryf, normal, 0, block_size_scaled);
border_mask |= get_border_mask(primary, block_size_scaled) << 6;
@ -1264,6 +1270,9 @@ DefaultTextureIndicesData build_regular_mesh(const VoxelBufferInternal &voxels,
sdf_data, indices_data, weights_data, voxels.get_size(), lod_index, texturing_mode, cache, output);
} break;
// TODO Remove support for 32-bit SDF in Transvoxel?
// I don't think it's worth it. And it could reduce executable size significantly
// (the optimized obj size for just transvoxel.cpp is 1.2 Mb on Windows)
case VoxelBufferInternal::DEPTH_32_BIT: {
Span<const float> sdf_data = sdf_data_raw.reinterpret_cast_to<const float>();
build_regular_mesh<float>(

View File

@ -3,11 +3,11 @@
#include "../../storage/voxel_buffer_internal.h"
#include "../../util/fixed_array.h"
#include "../../util/math/vector2f.h"
#include "../../util/math/vector3f.h"
#include "../../util/math/vector3i.h"
#include <core/math/color.h>
#include <core/math/vector2.h>
#include <core/math/vector3.h>
#include <vector>
namespace zylann::voxel::transvoxel {
@ -30,10 +30,10 @@ enum TexturingMode {
};
struct MeshArrays {
std::vector<Vector3> vertices;
std::vector<Vector3> normals;
std::vector<Vector3f> vertices;
std::vector<Vector3f> normals;
std::vector<Color> lod_data;
std::vector<Vector2> texturing_data;
std::vector<Vector2f> texturing_data;
std::vector<int> indices;
void clear() {
@ -44,7 +44,7 @@ struct MeshArrays {
indices.clear();
}
int add_vertex(Vector3 primary, Vector3 normal, uint16_t border_mask, Vector3 secondary) {
int add_vertex(Vector3f primary, Vector3f normal, uint16_t border_mask, Vector3f secondary) {
int vi = vertices.size();
vertices.push_back(primary);
normals.push_back(normal);

View File

@ -2,6 +2,7 @@
#include "../../storage/voxel_buffer.h"
#include "../../thirdparty/meshoptimizer/meshoptimizer.h"
#include "../../util/funcs.h"
#include "../../util/godot/funcs.h"
#include "../../util/profiling.h"
#include "transvoxel_tables.cpp"
@ -32,7 +33,7 @@ void VoxelMesherTransvoxel::fill_surface_arrays(Array &arrays, const transvoxel:
PackedFloat32Array texturing_data; // 2*4*uint8 as 2*float32
PackedInt32Array indices;
raw_copy_to(vertices, src.vertices);
copy_to(vertices, src.vertices);
//raw_copy_to(lod_data, src.lod_data);
lod_data.resize(src.lod_data.size() * 4);
@ -43,7 +44,7 @@ void VoxelMesherTransvoxel::fill_surface_arrays(Array &arrays, const transvoxel:
arrays.resize(Mesh::ARRAY_MAX);
arrays[Mesh::ARRAY_VERTEX] = vertices;
if (src.normals.size() != 0) {
raw_copy_to(normals, src.normals);
copy_to(normals, src.normals);
arrays[Mesh::ARRAY_NORMAL] = normals;
}
if (src.texturing_data.size() != 0) {
@ -94,7 +95,7 @@ static void simplify(const transvoxel::MeshArrays &src_mesh, transvoxel::MeshArr
// TODO See build script about the `zylannmeshopt::` namespace
const unsigned int lod_index_count = zylannmeshopt::meshopt_simplify(&lod_indices[0],
reinterpret_cast<const unsigned int *>(src_mesh.indices.data()), src_mesh.indices.size(),
&src_mesh.vertices[0].x, src_mesh.vertices.size(), sizeof(Vector3), target_index_count,
&src_mesh.vertices[0].x, src_mesh.vertices.size(), sizeof(Vector3f), target_index_count,
p_error_threshold, &lod_error);
lod_indices.resize(lod_index_count);

View File

@ -114,7 +114,8 @@ void DirectMultiMeshInstance::make_transform_3d_bulk_array(
if (static_cast<unsigned int>(bulk_array.size()) != bulk_array_size) {
bulk_array.resize(bulk_array_size);
}
CRASH_COND(transforms.size() * sizeof(Transform3D) / sizeof(float) != static_cast<size_t>(bulk_array.size()));
// Note, the actual size of `Transform3D` can be twice if `real_t` is `double`.
CRASH_COND(transforms.size() * sizeof(Transform3D) / sizeof(real_t) != static_cast<size_t>(bulk_array.size()));
//memcpy(w.ptr(), _transform_cache.data(), bulk_array.size() * sizeof(float));
// Nope, you can't memcpy that, nonono. It's said to be for performance, but doesnt specify why.
@ -138,7 +139,9 @@ void DirectMultiMeshInstance::make_transform_and_color8_3d_bulk_array(
if (static_cast<unsigned int>(bulk_array.size()) != bulk_array_size) {
bulk_array.resize(bulk_array_size);
}
CRASH_COND(data.size() * sizeof(TransformAndColor8) / sizeof(float) != static_cast<size_t>(bulk_array.size()));
// Note, the actual size of `Transform3D` can be twice if `real_t` is `double`.
CRASH_COND(data.size() * (sizeof(Transform3D) / sizeof(real_t) + sizeof(Color8) / sizeof(float)) !=
static_cast<size_t>(bulk_array.size()));
float *w = bulk_array.ptrw();
for (size_t i = 0; i < data.size(); ++i) {
@ -160,7 +163,10 @@ void DirectMultiMeshInstance::make_transform_and_color32_3d_bulk_array(
if (static_cast<unsigned int>(bulk_array.size()) != bulk_array_size) {
bulk_array.resize(bulk_array_size);
}
CRASH_COND(data.size() * sizeof(TransformAndColor32) / sizeof(float) != static_cast<size_t>(bulk_array.size()));
// Note, the actual size of `Transform3D` can be twice if `real_t` is `double`.
// `Color` still uses `float` no matter the setting.
CRASH_COND(data.size() * (sizeof(Transform3D) / sizeof(real_t) + sizeof(Color) / sizeof(float)) !=
static_cast<size_t>(bulk_array.size()));
float *w = bulk_array.ptrw();
for (size_t i = 0; i < data.size(); ++i) {

View File

@ -256,4 +256,42 @@ void set_nodes_owner_except_root(Node *root, Node *owner) {
}
}
void copy_to(Vector<Vector3> &dst, const std::vector<Vector3f> &src) {
dst.resize(src.size());
// resize can fail in case allocation was not possible
ERR_FAIL_COND(dst.size() != static_cast<size_t>(src.size()));
#ifdef REAL_T_IS_DOUBLE
// Convert floats to doubles
const unsigned int count = dst.size() * Vector3f::AXIS_COUNT;
real_t *dst_w = reinterpret_cast<real_t *>(dst.ptrw());
const float *src_r = reinterpret_cast<const float *>(src.data());
for (unsigned int i = 0; i < count; ++i) {
dst_w[i] = src_r[i];
}
#else
static_assert(sizeof(Vector3) == sizeof(Vector3f));
memcpy(dst.ptrw(), src.data(), src.size() * sizeof(Vector3f));
#endif
}
void copy_to(Vector<Vector2> &dst, const std::vector<Vector2f> &src) {
dst.resize(src.size());
// resize can fail in case allocation was not possible
ERR_FAIL_COND(dst.size() != static_cast<size_t>(src.size()));
#ifdef REAL_T_IS_DOUBLE
// Convert floats to doubles
const unsigned int count = dst.size() * Vector2f::AXIS_COUNT;
real_t *dst_w = reinterpret_cast<real_t *>(dst.ptrw());
const float *src_r = reinterpret_cast<const float *>(src.data());
for (unsigned int i = 0; i < count; ++i) {
dst_w[i] = src_r[i];
}
#else
static_assert(sizeof(Vector2) == sizeof(Vector2f));
memcpy(dst.ptrw(), src.data(), src.size() * sizeof(Vector2f));
#endif
}
} // namespace zylann

View File

@ -1,9 +1,13 @@
#ifndef VOXEL_UTILITY_GODOT_FUNCS_H
#define VOXEL_UTILITY_GODOT_FUNCS_H
#include "../math/vector2f.h"
#include "../math/vector3f.h"
#include "../span.h"
#include <core/object/ref_counted.h>
#include <core/variant/variant.h>
#include <memory>
class Mesh;
@ -92,6 +96,25 @@ struct RefHasher {
}
};
void copy_to(Vector<Vector3> &dst, const std::vector<Vector3f> &src);
void copy_to(Vector<Vector2> &dst, const std::vector<Vector2f> &src);
inline Vector2f to_vec2f(Vector2i v) {
return Vector2f(v.x, v.y);
}
inline Vector2f to_vec2f(Vector2 v) {
return Vector2f(v.x, v.y);
}
inline Vector3f to_vec3f(Vector3i v) {
return Vector3f(v.x, v.y, v.z);
}
inline Vector3f to_vec3f(Vector3 v) {
return Vector3f(v.x, v.y, v.z);
}
} // namespace zylann
#endif // VOXEL_UTILITY_GODOT_FUNCS_H

View File

@ -1,6 +1,8 @@
#ifndef VOXEL_MATH_FUNCS_H
#define VOXEL_MATH_FUNCS_H
#include "vector2f.h"
#include "vector3f.h"
#include <core/math/vector3.h>
namespace zylann::math {
@ -21,7 +23,7 @@ namespace zylann::math {
//
template <typename T>
inline T interpolate(const T v0, const T v1, const T v2, const T v3, const T v4, const T v5, const T v6, const T v7,
Vector3 position) {
Vector3f position) {
const float one_min_x = 1.f - position.x;
const float one_min_y = 1.f - position.y;
const float one_min_z = 1.f - position.z;
@ -75,6 +77,25 @@ inline T max(const T a, const T b, const T c, const T d, const T e, const T f, c
return max(max(a, b, c, d), max(e, f, g, h));
}
// template versions require explicit types.
// float versions do not require casting all the time, so optional double-precision support with `real_t` is easier.
inline float minf(float a, float b) {
return a < b ? a : b;
}
inline double minf(double a, double b) {
return a < b ? a : b;
}
inline float maxf(float a, float b) {
return a > b ? a : b;
}
inline float maxf(double a, double b) {
return a > b ? a : b;
}
template <typename T>
inline T clamp(const T x, const T min_value, const T max_value) {
// TODO Optimization: clang can optimize a min/max implementation. Worth changing to that?
@ -87,6 +108,10 @@ inline T clamp(const T x, const T min_value, const T max_value) {
return x;
}
inline float clampf(float x, float min_value, float max_value) {
return fmin(fmax(x, min_value), max_value);
}
template <typename T>
inline T squared(const T x) {
return x * x;
@ -139,6 +164,10 @@ inline float wrapf(float x, float d) {
return Math::is_zero_approx(d) ? 0.f : x - (d * Math::floor(x / d));
}
inline double wrapf(double x, double d) {
return Math::is_zero_approx(d) ? 0.0 : x - (d * Math::floor(x / d));
}
// Similar to Math::smoothstep but doesn't use macro to clamp
inline float smoothstep(float p_from, float p_to, float p_weight) {
if (Math::is_equal_approx(p_from, p_to)) {
@ -148,10 +177,22 @@ inline float smoothstep(float p_from, float p_to, float p_weight) {
return x * x * (3.0f - 2.0f * x);
}
inline double smoothstep(double p_from, double p_to, double p_weight) {
if (Math::is_equal_approx(p_from, p_to)) {
return p_from;
}
double x = clamp((p_weight - p_from) / (p_to - p_from), 0.0, 1.0);
return x * x * (3.0 - 2.0 * x);
}
inline float fract(float x) {
return x - Math::floor(x);
}
inline double fract(double x) {
return x - Math::floor(x);
}
inline Vector3 fract(const Vector3 &p) {
return Vector3(fract(p.x), fract(p.y), fract(p.z));
}
@ -214,6 +255,21 @@ inline bool has_nan(const Vector3 &v) {
// return i & (i - 1);
// }
// Float version of Geometry::is_point_in_triangle()
inline bool is_point_in_triangle(const Vector2f &s, const Vector2f &a, const Vector2f &b, const Vector2f &c) {
const Vector2f an = a - s;
const Vector2f bn = b - s;
const Vector2f cn = c - s;
const bool orientation = an.cross(bn) > 0;
if ((bn.cross(cn) > 0) != orientation) {
return false;
}
return (cn.cross(an) > 0) == orientation;
}
} // namespace zylann::math
#endif // VOXEL_MATH_FUNCS_H

View File

@ -9,12 +9,12 @@ namespace zylann::math {
// For interval arithmetic
struct Interval {
// Both inclusive
float min;
float max;
real_t min;
real_t max;
inline Interval() : min(0), max(0) {}
inline Interval(float p_min, float p_max) : min(p_min), max(p_max) {
inline Interval(real_t p_min, real_t p_max) : min(p_min), max(p_max) {
#if DEBUG_ENABLED
CRASH_COND(p_min > p_max);
#endif
@ -22,23 +22,23 @@ struct Interval {
inline Interval(const Interval &other) : min(other.min), max(other.max) {}
inline static Interval from_single_value(float p_val) {
inline static Interval from_single_value(real_t p_val) {
return Interval(p_val, p_val);
}
inline static Interval from_unordered_values(float a, float b) {
inline static Interval from_unordered_values(real_t a, real_t b) {
return Interval(math::min(a, b), math::max(a, b));
}
inline static Interval from_infinity() {
return Interval(-std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
return Interval(-std::numeric_limits<real_t>::infinity(), std::numeric_limits<real_t>::infinity());
}
inline static Interval from_union(const Interval a, const Interval b) {
return Interval(math::min(a.min, b.min), math::max(a.max, b.max));
}
inline bool contains(float v) const {
inline bool contains(real_t v) const {
return v >= min && v <= max;
}
@ -46,7 +46,7 @@ struct Interval {
return min == max;
}
inline void add_point(float x) {
inline void add_point(real_t x) {
if (x < min) {
min = x;
} else if (x > max) {
@ -54,7 +54,7 @@ struct Interval {
}
}
inline Interval padded(float e) const {
inline Interval padded(real_t e) const {
return Interval(min - e, max + e);
}
@ -63,7 +63,7 @@ struct Interval {
add_point(other.max);
}
inline float length() const {
inline real_t length() const {
return max - min;
}
@ -75,7 +75,7 @@ struct Interval {
return min != other.min || max != other.max;
}
inline Interval operator+(float x) const {
inline Interval operator+(real_t x) const {
return Interval{ min + x, max + x };
}
@ -87,7 +87,7 @@ struct Interval {
*this = *this + other;
}
inline Interval operator-(float x) const {
inline Interval operator-(real_t x) const {
return Interval{ min - x, max - x };
}
@ -99,9 +99,9 @@ struct Interval {
return Interval{ -max, -min };
}
inline Interval operator*(float x) const {
const float a = min * x;
const float b = max * x;
inline Interval operator*(real_t x) const {
const real_t a = min * x;
const real_t b = max * x;
if (a < b) {
return Interval(a, b);
} else {
@ -112,14 +112,14 @@ struct Interval {
inline Interval operator*(const Interval &other) const {
// Note, if the two operands have the same source (i.e you are doing x^2), this may lead to suboptimal results.
// You may then prefer using a more dedicated function.
const float a = min * other.min;
const float b = min * other.max;
const float c = max * other.min;
const float d = max * other.max;
const real_t a = min * other.min;
const real_t b = min * other.max;
const real_t c = max * other.min;
const real_t d = max * other.max;
return Interval{ math::min(a, b, c, d), math::max(a, b, c, d) };
}
inline void operator*=(float x) {
inline void operator*=(real_t x) {
*this = *this * x;
}
@ -136,19 +136,19 @@ struct Interval {
// TODO May need something more precise
return Interval::from_infinity();
}
const float a = min / other.min;
const float b = min / other.max;
const float c = max / other.min;
const float d = max / other.max;
const real_t a = min / other.min;
const real_t b = min / other.max;
const real_t c = max / other.min;
const real_t d = max / other.max;
return Interval{ math::min(a, b, c, d), math::max(a, b, c, d) };
}
inline Interval operator/(float x) const {
inline Interval operator/(real_t x) const {
// TODO Implement proper division by interval
return *this * (1.f / x);
return *this * (1.0 / x);
}
inline void operator/=(float x) {
inline void operator/=(real_t x) {
*this = *this / x;
}
};
@ -164,7 +164,7 @@ struct Interval3 {
Interval z;
};
inline Interval operator*(float b, const Interval &a) {
inline Interval operator*(real_t b, const Interval &a) {
return a * b;
}
@ -178,11 +178,11 @@ inline Interval max_interval(const Interval &a, const Interval &b) {
return Interval(max(a.min, b.min), max(a.max, b.max));
}
inline Interval min_interval(const Interval &a, const float b) {
inline Interval min_interval(const Interval &a, const real_t b) {
return Interval(min(a.min, b), min(a.max, b));
}
inline Interval max_interval(const Interval &a, const float b) {
inline Interval max_interval(const Interval &a, const real_t b) {
return Interval(max(a.min, b), max(a.max, b));
}
@ -216,14 +216,14 @@ inline Interval lerp(const Interval &a, const Interval &b, const Interval &t) {
return Interval(Math::lerp(a.min, b.min, t.min), Math::lerp(a.max, b.max, t.min));
}
const float v0 = a.min + t.min * (b.min - a.min);
const float v1 = a.max + t.min * (b.min - a.max);
const float v2 = a.min + t.max * (b.min - a.min);
const float v3 = a.max + t.max * (b.min - a.max);
const float v4 = a.min + t.min * (b.max - a.min);
const float v5 = a.max + t.min * (b.max - a.max);
const float v6 = a.min + t.max * (b.max - a.min);
const float v7 = a.max + t.max * (b.max - a.max);
const real_t v0 = a.min + t.min * (b.min - a.min);
const real_t v1 = a.max + t.min * (b.min - a.max);
const real_t v2 = a.min + t.max * (b.min - a.min);
const real_t v3 = a.max + t.max * (b.min - a.max);
const real_t v4 = a.min + t.min * (b.max - a.min);
const real_t v5 = a.max + t.min * (b.max - a.max);
const real_t v6 = a.min + t.max * (b.max - a.min);
const real_t v7 = a.max + t.max * (b.max - a.max);
return Interval(min(v0, v1, v2, v3, v4, v5, v6, v7), max(v0, v1, v2, v3, v4, v5, v6, v7));
}
@ -348,13 +348,13 @@ inline Interval wrapf(const Interval &x, const Interval &d) {
return x - (d * floor(x / d));
}
inline Interval smoothstep(float p_from, float p_to, Interval p_weight) {
inline Interval smoothstep(real_t p_from, real_t p_to, Interval p_weight) {
if (Math::is_equal_approx(p_from, p_to)) {
return Interval::from_single_value(p_from);
}
// Smoothstep is monotonic
float v0 = smoothstep(p_from, p_to, p_weight.min);
float v1 = smoothstep(p_from, p_to, p_weight.max);
real_t v0 = smoothstep(p_from, p_to, p_weight.min);
real_t v1 = smoothstep(p_from, p_to, p_weight.max);
if (v0 <= v1) {
return Interval(v0, v1);
} else {
@ -379,7 +379,7 @@ inline Interval squared(const Interval &x) {
}
// Prefer this instead of doing polynomials with a single interval, this will provide a more optimal result
inline Interval polynomial_second_degree(const Interval x, float a, float b, float c) {
inline Interval polynomial_second_degree(const Interval x, real_t a, real_t b, real_t c) {
// a*x*x + b*x + c
if (a == 0.f) {
@ -390,14 +390,14 @@ inline Interval polynomial_second_degree(const Interval x, float a, float b, flo
}
}
const float parabola_x = -b / (2.f * a);
const real_t parabola_x = -b / (2.f * a);
const float y0 = a * x.min * x.min + b * x.min + c;
const float y1 = a * x.max * x.max + b * x.max + c;
const real_t y0 = a * x.min * x.min + b * x.min + c;
const real_t y1 = a * x.max * x.max + b * x.max + c;
if (x.min < parabola_x && x.max > parabola_x) {
// The interval includes the tip
const float parabola_y = a * parabola_x * parabola_x + b * parabola_x + c;
const real_t parabola_y = a * parabola_x * parabola_x + b * parabola_x + c;
if (a < 0) {
return Interval(min(y0, y1), parabola_y);
} else {
@ -417,8 +417,8 @@ inline Interval polynomial_second_degree(const Interval x, float a, float b, flo
// Prefer this over x*x*x, this will provide a more optimal result
inline Interval cubed(const Interval &x) {
// x^3 is monotonic ascending
const float minv = x.min * x.min * x.min;
const float maxv = x.max * x.max * x.max;
const real_t minv = x.min * x.min * x.min;
const real_t maxv = x.max * x.max * x.max;
return Interval{ minv, maxv };
}

View File

@ -12,7 +12,7 @@ SdfAffectingArguments sdf_subtract_side(Interval a, Interval b) {
return SDF_BOTH;
}
SdfAffectingArguments sdf_polynomial_smooth_subtract_side(Interval a, Interval b, float s) {
SdfAffectingArguments sdf_polynomial_smooth_subtract_side(Interval a, Interval b, real_t s) {
// | \ \ \ |
// ---1---x--x--x-------3--- b.max
// | \ \ \ |
@ -42,7 +42,7 @@ SdfAffectingArguments sdf_union_side(Interval a, Interval b) {
return SDF_BOTH;
}
SdfAffectingArguments sdf_polynomial_smooth_union_side(Interval a, Interval b, float s) {
SdfAffectingArguments sdf_polynomial_smooth_union_side(Interval a, Interval b, real_t s) {
if (a.max + s < b.min) {
return SDF_ONLY_A;
}
@ -53,7 +53,7 @@ SdfAffectingArguments sdf_polynomial_smooth_union_side(Interval a, Interval b, f
}
template <typename F>
inline Interval sdf_smooth_op(Interval b, Interval a, float s, F smooth_op_func) {
inline Interval sdf_smooth_op(Interval b, Interval a, real_t s, F smooth_op_func) {
// Smooth union and subtract are a generalization of `min(a, b)` and `max(-a, b)`, with a smooth junction.
// That junction runs in a diagonal crossing zero (with equation `y = -x`).
// Areas on the two sides of the junction are monotonic, i.e their derivatives should never cross zero,
@ -71,10 +71,10 @@ inline Interval sdf_smooth_op(Interval b, Interval a, float s, F smooth_op_func)
// | \ |
// a.min a.max
const float v0 = smooth_op_func(b.min, a.min, s);
const float v1 = smooth_op_func(b.max, a.min, s);
const float v2 = smooth_op_func(b.min, a.max, s);
const float v3 = smooth_op_func(b.max, a.max, s);
const real_t v0 = smooth_op_func(b.min, a.min, s);
const real_t v1 = smooth_op_func(b.max, a.min, s);
const real_t v2 = smooth_op_func(b.min, a.max, s);
const real_t v3 = smooth_op_func(b.max, a.max, s);
const Vector2 diag_b_min(-b.min, b.min);
const Vector2 diag_b_max(-b.max, b.max);
@ -87,14 +87,14 @@ inline Interval sdf_smooth_op(Interval b, Interval a, float s, F smooth_op_func)
if (crossing_left || crossing_top) {
const bool crossing_right = (diag_a_max.y > b.min && diag_a_max.y < b.max);
float v4;
real_t v4;
if (crossing_left) {
v4 = smooth_op_func(diag_a_min.y, diag_a_min.x, s);
} else {
v4 = smooth_op_func(diag_b_max.y, diag_b_max.x, s);
}
float v5;
real_t v5;
if (crossing_right) {
v5 = smooth_op_func(diag_a_max.y, diag_a_max.x, s);
} else {
@ -108,16 +108,18 @@ inline Interval sdf_smooth_op(Interval b, Interval a, float s, F smooth_op_func)
return Interval(min(v0, v1, v2, v3), max(v0, v1, v2, v3));
}
Interval sdf_smooth_union(Interval p_b, Interval p_a, float p_s) {
Interval sdf_smooth_union(Interval p_b, Interval p_a, real_t p_s) {
// TODO Not tested
// Had to use a lambda because otherwise it's ambiguous
return sdf_smooth_op(
p_b, p_a, p_s, [](float b, float a, float s) { return zylann::math::sdf_smooth_union(b, a, s); });
return sdf_smooth_op(p_b, p_a, p_s, [](real_t b, real_t a, real_t s) { //
return zylann::math::sdf_smooth_union(b, a, s);
});
}
Interval sdf_smooth_subtract(Interval p_b, Interval p_a, float p_s) {
return sdf_smooth_op(
p_b, p_a, p_s, [](float b, float a, float s) { return zylann::math::sdf_smooth_subtract(b, a, s); });
Interval sdf_smooth_subtract(Interval p_b, Interval p_a, real_t p_s) {
return sdf_smooth_op(p_b, p_a, p_s, [](real_t b, real_t a, real_t s) { //
return zylann::math::sdf_smooth_subtract(b, a, s);
});
}
} //namespace zylann::math

View File

@ -9,9 +9,9 @@ namespace zylann::math {
// Signed-distance-field functions.
// For more, see https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
inline float sdf_box(const Vector3 pos, const Vector3 extents) {
inline real_t sdf_box(const Vector3 pos, const Vector3 extents) {
Vector3 d = pos.abs() - extents;
return min(max(d.x, max(d.y, d.z)), 0.f) + Vector3(max(d.x, 0.f), max(d.y, 0.f), max(d.z, 0.f)).length();
return minf(maxf(d.x, maxf(d.y, d.z)), 0.f) + Vector3(maxf(d.x, 0.f), maxf(d.y, 0.f), maxf(d.z, 0.f)).length();
}
inline Interval sdf_box(const Interval &x, const Interval &y, const Interval &z, const Interval &sx, const Interval &sy,
@ -23,11 +23,11 @@ inline Interval sdf_box(const Interval &x, const Interval &y, const Interval &z,
get_length(max_interval(dx, 0.f), max_interval(dy, 0.f), max_interval(dz, 0.f));
}
inline float sdf_sphere(Vector3 pos, Vector3 center, float radius) {
inline real_t sdf_sphere(Vector3 pos, Vector3 center, real_t radius) {
return pos.distance_to(center) - radius;
}
inline float sdf_torus(float x, float y, float z, float r0, float r1) {
inline real_t sdf_torus(real_t x, real_t y, real_t z, real_t r0, real_t r1) {
Vector2 q = Vector2(Vector2(x, z).length() - r0, y);
return q.length() - r1;
}
@ -38,23 +38,23 @@ inline Interval sdf_torus(
return get_length(qx, y) - r1;
}
inline float sdf_union(float a, float b) {
inline real_t sdf_union(real_t a, real_t b) {
return min(a, b);
}
// Subtracts SDF b from SDF a
inline float sdf_subtract(float a, float b) {
inline real_t sdf_subtract(real_t a, real_t b) {
return max(a, -b);
}
inline float sdf_smooth_union(float a, float b, float s) {
float h = clamp(0.5f + 0.5f * (b - a) / s, 0.0f, 1.0f);
inline real_t sdf_smooth_union(real_t a, real_t b, real_t s) {
const real_t h = clampf(0.5f + 0.5f * (b - a) / s, 0.0f, 1.0f);
return Math::lerp(b, a, h) - s * h * (1.0f - h);
}
// Inverted a and b because it subtracts SDF a from SDF b
inline float sdf_smooth_subtract(float b, float a, float s) {
float h = clamp(0.5f - 0.5f * (b + a) / s, 0.0f, 1.0f);
inline real_t sdf_smooth_subtract(real_t b, real_t a, real_t s) {
const real_t h = clampf(0.5f - 0.5f * (b + a) / s, 0.0f, 1.0f);
return Math::lerp(b, -a, h) + s * h * (1.0f - h);
}
@ -67,10 +67,10 @@ inline Interval sdf_subtract(Interval a, Interval b) {
return max_interval(a, -b);
}
Interval sdf_smooth_union(Interval p_b, Interval p_a, float p_s);
Interval sdf_smooth_union(Interval p_b, Interval p_a, real_t p_s);
// Does b - a
Interval sdf_smooth_subtract(Interval p_b, Interval p_a, float p_s);
Interval sdf_smooth_subtract(Interval p_b, Interval p_a, real_t p_s);
enum SdfAffectingArguments { //
SDF_ONLY_A,
@ -82,10 +82,10 @@ enum SdfAffectingArguments { //
// for a - b
SdfAffectingArguments sdf_subtract_side(Interval a, Interval b);
// for a - b
SdfAffectingArguments sdf_polynomial_smooth_subtract_side(Interval a, Interval b, float s);
SdfAffectingArguments sdf_polynomial_smooth_subtract_side(Interval a, Interval b, real_t s);
SdfAffectingArguments sdf_union_side(Interval a, Interval b);
SdfAffectingArguments sdf_polynomial_smooth_union_side(Interval a, Interval b, float s);
SdfAffectingArguments sdf_polynomial_smooth_union_side(Interval a, Interval b, real_t s);
} // namespace zylann::math

81
util/math/vector2f.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef ZYLANN_VECTOR2F_H
#define ZYLANN_VECTOR2F_H
#include <core/error/error_macros.h>
namespace zylann {
// 32-bit float precision 2D vector.
// Because Godot's `Vector2` uses `real_t`, so when `real_t` is `double` it forces some things to use double-precision
// vectors while they dont need that amount of precision.
struct Vector2f {
static const unsigned int AXIS_X = 0;
static const unsigned int AXIS_Y = 1;
static const unsigned int AXIS_COUNT = 2;
union {
struct {
float x;
float y;
};
float coords[2];
};
Vector2f() : x(0), y(0) {}
Vector2f(float p_x, float p_y) : x(p_x), y(p_y) {}
Vector2f(const Vector2f &other) : x(other.x), y(other.y) {}
float cross(const Vector2f &p_other) const {
return x * p_other.y - y * p_other.x;
}
inline const float &operator[](const unsigned int p_axis) const {
#ifdef DEBUG_ENABLED
CRASH_COND(p_axis >= AXIS_COUNT);
#endif
return coords[p_axis];
}
inline float &operator[](const unsigned int p_axis) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_axis >= AXIS_COUNT);
#endif
return coords[p_axis];
}
inline Vector2f &operator+=(const Vector2f &p_v) {
x += p_v.x;
y += p_v.y;
return *this;
}
inline Vector2f operator+(const Vector2f &p_v) const {
return Vector2f(x + p_v.x, y + p_v.y);
}
inline Vector2f operator-(const Vector2f &p_v) const {
return Vector2f(x - p_v.x, y - p_v.y);
}
inline Vector2f operator*(const Vector2f &p_v) const {
return Vector2f(x * p_v.x, y * p_v.y);
}
inline Vector2f &operator*=(const float p_scalar) {
x *= p_scalar;
y *= p_scalar;
return *this;
}
inline Vector2f operator*(const float p_scalar) const {
return Vector2f(x * p_scalar, y * p_scalar);
}
};
inline Vector2f operator*(float p_scalar, const Vector2f &v) {
return v * p_scalar;
}
} // namespace zylann
#endif // ZYLANN_VECTOR2F_H

183
util/math/vector3f.h Normal file
View File

@ -0,0 +1,183 @@
#ifndef ZYLANN_VECTOR3F_H
#define ZYLANN_VECTOR3F_H
#include <core/error/error_macros.h>
#include <core/math/math_funcs.h>
namespace zylann {
// 32-bit float precision 3D vector.
// Because Godot's `Vector3` uses `real_t`, so when `real_t` is `double` it forces some things to use double-precision
// vectors while they dont need that amount of precision. This is also a problem for some third-party libraries
// that do not support `double` as a result.
struct Vector3f {
static const unsigned int AXIS_X = 0;
static const unsigned int AXIS_Y = 1;
static const unsigned int AXIS_Z = 2;
static const unsigned int AXIS_COUNT = 3;
union {
struct {
float x;
float y;
float z;
};
float coords[3];
};
Vector3f() : x(0), y(0), z(0) {}
Vector3f(float p_x, float p_y, float p_z) : x(p_x), y(p_y), z(p_z) {}
Vector3f(const Vector3f &other) : x(other.x), y(other.y), z(other.z) {}
inline float length_squared() const {
return x * x + y * y + z * z;
}
inline float length() const {
return Math::sqrt(length_squared());
}
inline float distance_squared_to(const Vector3f &p_to) const {
return (p_to - *this).length_squared();
}
inline Vector3f cross(const Vector3f &p_with) const {
const Vector3f ret( //
(y * p_with.z) - (z * p_with.y), //
(z * p_with.x) - (x * p_with.z), //
(x * p_with.y) - (y * p_with.x));
return ret;
}
inline float dot(const Vector3f &p_with) const {
return x * p_with.x + y * p_with.y + z * p_with.z;
}
inline void normalize() {
const float lengthsq = length_squared();
if (lengthsq == 0) {
x = y = z = 0;
} else {
const float length = Math::sqrt(lengthsq);
x /= length;
y /= length;
z /= length;
}
}
inline Vector3f normalized() const {
Vector3f v = *this;
v.normalize();
return v;
}
inline const float &operator[](const unsigned int p_axis) const {
#ifdef DEBUG_ENABLED
CRASH_COND(p_axis >= AXIS_COUNT);
#endif
return coords[p_axis];
}
inline float &operator[](const unsigned int p_axis) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_axis >= AXIS_COUNT);
#endif
return coords[p_axis];
}
inline Vector3f &operator+=(const Vector3f &p_v) {
x += p_v.x;
y += p_v.y;
z += p_v.z;
return *this;
}
inline Vector3f operator+(const Vector3f &p_v) const {
return Vector3f(x + p_v.x, y + p_v.y, z + p_v.z);
}
inline Vector3f &operator-=(const Vector3f &p_v) {
x -= p_v.x;
y -= p_v.y;
z -= p_v.z;
return *this;
}
inline Vector3f operator-(const Vector3f &p_v) const {
return Vector3f(x - p_v.x, y - p_v.y, z - p_v.z);
}
inline Vector3f &operator*=(const Vector3f &p_v) {
x *= p_v.x;
y *= p_v.y;
z *= p_v.z;
return *this;
}
inline Vector3f operator*(const Vector3f &p_v) const {
return Vector3f(x * p_v.x, y * p_v.y, z * p_v.z);
}
inline Vector3f &operator/=(const Vector3f &p_v) {
x /= p_v.x;
y /= p_v.y;
z /= p_v.z;
return *this;
}
inline Vector3f operator/(const Vector3f &p_v) const {
return Vector3f(x / p_v.x, y / p_v.y, z / p_v.z);
}
inline Vector3f &operator*=(const float p_scalar) {
x *= p_scalar;
y *= p_scalar;
z *= p_scalar;
return *this;
}
inline Vector3f operator*(const float p_scalar) const {
return Vector3f(x * p_scalar, y * p_scalar, z * p_scalar);
}
inline Vector3f &operator/=(const float p_scalar) {
x /= p_scalar;
y /= p_scalar;
z /= p_scalar;
return *this;
}
inline Vector3f operator/(const float p_scalar) const {
return Vector3f(x / p_scalar, y / p_scalar, z / p_scalar);
}
inline Vector3f operator-() const {
return Vector3f(-x, -y, -z);
}
inline bool operator==(const Vector3f &p_v) const {
return x == p_v.x && y == p_v.y && z == p_v.z;
}
inline bool operator!=(const Vector3f &p_v) const {
return x != p_v.x || y != p_v.y || z != p_v.z;
}
inline bool operator<(const Vector3f &p_v) const {
if (x == p_v.x) {
if (y == p_v.y) {
return z < p_v.z;
}
return y < p_v.y;
}
return x < p_v.x;
}
};
inline Vector3f operator*(float p_scalar, const Vector3f &v) {
return v * p_scalar;
}
} // namespace zylann
#endif // ZYLANN_VECTOR3F_H

View File

@ -156,6 +156,7 @@ public:
bool is_valid() const;
// Queries
// TODO Double-precision support. FastNoise2 doesn't have it yet, so it's all `float` for now.
float get_noise_2d_single(Vector2 pos) const;
float get_noise_3d_single(Vector3 pos) const;

View File

@ -98,14 +98,14 @@ public:
void set_rotation_type_3d(RotationType3D type);
RotationType3D get_rotation_type_3d() const;
inline float get_noise_2d(float x, float y) const {
inline float get_noise_2d(real_t x, real_t y) const {
if (_warp_noise.is_valid()) {
_warp_noise->warp_2d(x, y);
}
return _fn.GetNoise(x, y);
}
inline float get_noise_3d(float x, float y, float z) const {
inline float get_noise_3d(real_t x, real_t y, real_t z) const {
if (_warp_noise.is_valid()) {
_warp_noise->warp_3d(x, y, z);
}
@ -126,10 +126,10 @@ private:
void _on_warp_noise_changed();
float _b_get_noise_2d(float x, float y) {
float _b_get_noise_2d(real_t x, real_t y) {
return get_noise_2d(x, y);
}
float _b_get_noise_3d(float x, float y, float z) {
float _b_get_noise_3d(real_t x, real_t y, real_t z) {
return get_noise_3d(x, y, z);
}

View File

@ -76,11 +76,11 @@ public:
// These are inline to ensure inlining actually happens. If they were bound directly to the script API,
// it means they would need to have an address, in which case I'm not sure they would be inlined?
inline void warp_2d(float &x, float &y) const {
inline void warp_2d(real_t &x, real_t &y) const {
return _fn.DomainWarp(x, y);
}
inline void warp_3d(float &x, float &y, float &z) const {
inline void warp_3d(real_t &x, real_t &y, real_t &z) const {
return _fn.DomainWarp(x, y, z);
}

View File

@ -158,13 +158,19 @@ Interval fnl_single_opensimplex2(
// https://www.wolframalpha.com/input/?i=max+d%2Fdx+32.69428253173828125+*+x+*+%28%280.6-x%5E2%29%5E4%29+from+-0.6+to+0.6
// But empiric measures have shown it around 8. Discontinuities do exist in this noise though,
// which makes this measuring harder
return get_noise_range_3d([&fn, seed](float x, float y, float z) { return fn.SingleOpenSimplex2(seed, x, y, z); },
return get_noise_range_3d(
[&fn, seed](real_t x, real_t y, real_t z) { //
return fn.SingleOpenSimplex2(seed, x, y, z);
},
p_x, p_y, p_z, 4.23718f);
}
Interval fnl_single_opensimplex2s(
const fast_noise_lite::FastNoiseLite &fn, int seed, Interval p_x, Interval p_y, Interval p_z) {
return get_noise_range_3d([&fn, seed](float x, float y, float z) { return fn.SingleOpenSimplex2(seed, x, y, z); },
return get_noise_range_3d(
[&fn, seed](real_t x, real_t y, real_t z) { //
return fn.SingleOpenSimplex2(seed, x, y, z);
},
// Max derivative found from empiric tests
p_x, p_y, p_z, 2.5f);
}
@ -179,21 +185,30 @@ Interval fnl_single_cellular(const FastNoiseLite &noise, Interval x, Interval y,
Interval fnl_single_perlin(
const fast_noise_lite::FastNoiseLite &fn, int seed, Interval p_x, Interval p_y, Interval p_z) {
return get_noise_range_3d([&fn, seed](float x, float y, float z) { return fn.SinglePerlin(seed, x, y, z); },
return get_noise_range_3d(
[&fn, seed](real_t x, real_t y, real_t z) { //
return fn.SinglePerlin(seed, x, y, z);
},
// Max derivative found from empiric tests
p_x, p_y, p_z, 3.2f);
}
Interval fnl_single_value_cubic(
const fast_noise_lite::FastNoiseLite &fn, int seed, Interval p_x, Interval p_y, Interval p_z) {
return get_noise_range_3d([&fn, seed](float x, float y, float z) { return fn.SingleValueCubic(seed, x, y, z); },
return get_noise_range_3d(
[&fn, seed](real_t x, real_t y, real_t z) { //
return fn.SingleValueCubic(seed, x, y, z);
},
// Max derivative found from empiric tests
p_x, p_y, p_z, 1.2f);
}
Interval fnl_single_value(
const fast_noise_lite::FastNoiseLite &fn, int seed, Interval p_x, Interval p_y, Interval p_z) {
return get_noise_range_3d([&fn, seed](float x, float y, float z) { return fn.SingleValue(seed, x, y, z); },
return get_noise_range_3d(
[&fn, seed](real_t x, real_t y, real_t z) { //
return fn.SingleValue(seed, x, y, z);
},
// Max derivative found from empiric tests
p_x, p_y, p_z, 3.0f);
}
@ -318,7 +333,10 @@ Interval get_fnl_range_3d(const FastNoiseLite &noise, Interval x, Interval y, In
math::Interval2 get_fnl_gradient_range_2d(const FastNoiseLiteGradient &noise, Interval x, Interval y) {
// TODO More precise analysis
const float amp = Math::abs(noise.get_amplitude());
return math::Interval2{ Interval{ x.min - amp, x.max + amp }, Interval{ y.min - amp, y.max + amp } };
return math::Interval2{ //
Interval{ x.min - amp, x.max + amp }, //
Interval{ y.min - amp, y.max + amp }
};
}
math::Interval3 get_fnl_gradient_range_3d(const FastNoiseLiteGradient &noise, Interval x, Interval y, Interval z) {

View File

@ -18,15 +18,15 @@ inline math::Interval get_noise_range_2d(
// We can use that number to find a bounding range within our rectangular interval.
const float max_derivative_half_diagonal = 0.5f * max_derivative * Math_SQRT2;
const float mid_x = 0.5 * (x.min + x.max);
const float mid_y = 0.5 * (y.min + y.max);
const real_t mid_x = 0.5 * (x.min + x.max);
const real_t mid_y = 0.5 * (y.min + y.max);
const float mid_value = noise_func(mid_x, mid_y);
const float diag = Math::sqrt(math::squared(x.length()) + math::squared(y.length()));
const real_t diag = Math::sqrt(math::squared(x.length()) + math::squared(y.length()));
return math::Interval( //
math::max(mid_value - max_derivative_half_diagonal * diag, -1.f),
math::min(mid_value + max_derivative_half_diagonal * diag, 1.f));
math::maxf(mid_value - max_derivative_half_diagonal * diag, -1.f),
math::minf(mid_value + max_derivative_half_diagonal * diag, 1.f));
}
template <typename Noise_F>
@ -34,16 +34,16 @@ inline math::Interval get_noise_range_3d(Noise_F noise_func, const math::Interva
const math::Interval &z, float max_derivative) {
const float max_derivative_half_diagonal = 0.5f * max_derivative * Math_SQRT2;
const float mid_x = 0.5 * (x.min + x.max);
const float mid_y = 0.5 * (y.min + y.max);
const float mid_z = 0.5 * (z.min + z.max);
const real_t mid_x = 0.5 * (x.min + x.max);
const real_t mid_y = 0.5 * (y.min + y.max);
const real_t mid_z = 0.5 * (z.min + z.max);
const float mid_value = noise_func(mid_x, mid_y, mid_z);
const float diag = Math::sqrt(math::squared(x.length()) + math::squared(y.length()) + math::squared(z.length()));
const real_t diag = Math::sqrt(math::squared(x.length()) + math::squared(y.length()) + math::squared(z.length()));
return math::Interval( //
math::max(mid_value - max_derivative_half_diagonal * diag, -1.f),
math::min(mid_value + max_derivative_half_diagonal * diag, 1.f));
math::maxf(mid_value - max_derivative_half_diagonal * diag, -1.f),
math::minf(mid_value + max_derivative_half_diagonal * diag, 1.f));
}
} // namespace zylann

View File

@ -8,14 +8,19 @@ using namespace math;
Interval get_osn_octave_range_2d(const OpenSimplexNoise &noise, const Interval &p_x, const Interval &p_y, int octave) {
return get_noise_range_2d(
[octave, &noise](float x, float y) { return noise._get_octave_noise_2d(octave, x, y); }, p_x, p_y, 2.35f);
[octave, &noise](real_t x, real_t y) { //
return noise._get_octave_noise_2d(octave, x, y);
},
p_x, p_y, 2.35f);
}
Interval get_osn_octave_range_3d(
const OpenSimplexNoise &noise, const Interval &p_x, const Interval &p_y, const Interval &p_z, int octave) {
return get_noise_range_3d(
[octave, &noise](float x, float y, float z) { return noise._get_octave_noise_3d(octave, x, y, z); }, p_x,
p_y, p_z, 2.5f);
[octave, &noise](real_t x, real_t y, real_t z) { //
return noise._get_octave_noise_3d(octave, x, y, z);
},
p_x, p_y, p_z, 2.5f);
}
Interval get_osn_range_2d(const OpenSimplexNoise &noise, Interval x, Interval y) {