#ifndef HEADER_VOXEL_UTILITY_H #define HEADER_VOXEL_UTILITY_H #include #include #include #include #include #ifdef DEBUG_ENABLED #include #endif class Mesh; class Object; // Takes elements starting from a given position and moves them at the beginning, // then shrink the array to fit them. Other elements are discarded. template void shift_up(Vector &v, unsigned int pos) { unsigned int j = 0; for (unsigned int i = pos; i < (unsigned int)v.size(); ++i, ++j) { v.write[j] = v[i]; } int remaining = v.size() - pos; v.resize(remaining); } template void shift_up(std::vector &v, unsigned int pos) { unsigned int j = 0; for (unsigned int i = pos; i < v.size(); ++i, ++j) { v[j] = v[i]; } int remaining = v.size() - pos; v.resize(remaining); } // Pops the last element of the vector and place it at the given position. // (The element that was at this position is the one removed). template void unordered_remove(Vector &v, unsigned int pos) { int last = v.size() - 1; v.write[pos] = v[last]; v.resize(last); } // Removes all items satisfying the given predicate. // This can change the size of the container, and original order of items is not preserved. template inline void unordered_remove_if(std::vector &vec, F predicate) { for (unsigned int i = 0; i < vec.size(); ++i) { if (predicate(vec[i])) { vec[i] = vec.back(); vec.pop_back(); // Note: can underflow, but it should be fine since it's incremented right after. // TODO Use a while()? --i; } } } // Removes all items satisfying the given predicate. // This can reduce the size of the container. Items are moved to preserve order. //template //inline void remove_if(std::vector &vec, F predicate) { // unsigned int j = 0; // for (unsigned int i = 0; i < vec.size(); ++i) { // if (predicate(vec[i])) { // continue; // } else { // if (i != j) { // vec[j] = vec[i]; // } // ++j; // } // } // vec.resize(j); //} template void copy_to(PoolVector &to, const Vector &from) { to.resize(from.size()); typename PoolVector::Write w = to.write(); for (unsigned int i = 0; i < from.size(); ++i) { w[i] = from[i]; } } inline String ptr2s(const void *p) { return String::num_uint64((uint64_t)p, 16); } template void raw_copy_to(PoolVector &to, const std::vector &from) { to.resize(from.size()); typename PoolVector::Write w = to.write(); memcpy(w.ptr(), from.data(), from.size() * sizeof(T)); } // TODO Move math funcs under math/ folder and wrap them in a namespace // Trilinear interpolation between corner values of a cube. // Cube points respect the same position as in octree_tables.h template 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) { 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; const float one_min_x_one_min_y = one_min_x * one_min_y; const float x_one_min_y = position.x * one_min_y; T res = one_min_z * (v0 * one_min_x_one_min_y + v1 * x_one_min_y + v4 * one_min_x * position.y); res += position.z * (v3 * one_min_x_one_min_y + v2 * x_one_min_y + v7 * one_min_x * position.y); res += position.x * position.y * (v5 * one_min_z + v6 * position.z); return res; } template inline T min(const T a, const T b) { return a < b ? a : b; } template inline T max(const T a, const T b) { return a > b ? a : b; } template inline T min(const T a, const T b, const T c, const T d) { return min(min(a, b), min(c, d)); } template inline T max(const T a, const T b, const T c, const T d) { return max(max(a, b), max(c, d)); } template inline T clamp(const T x, const T min_value, const T max_value) { if (x < min_value) { return min_value; } if (x >= max_value) { return max_value; } return x; } template inline T squared(const T x) { return x * x; } template inline void sort_min_max(T &a, T &b) { if (a > b) { T temp = a; a = b; b = temp; } } bool is_surface_triangulated(Array surface); bool is_mesh_empty(Ref mesh_ref); template inline void append_array(std::vector &dst, const std::vector &src) { dst.insert(dst.end(), src.begin(), src.end()); } // TODO Rename udiv => floordiv // Performs euclidean division, aka floored division. // This implementation expects a strictly positive divisor. // // x | `/` | udiv // ---------------------- // -6 | -2 | -2 // -5 | -1 | -2 // -4 | -1 | -2 // -3 | -1 | -1 // -2 | 0 | -1 // -1 | 0 | -1 // 0 | 0 | 0 // 1 | 0 | 0 // 2 | 0 | 0 // 3 | 1 | 1 // 4 | 1 | 1 // 5 | 1 | 1 // 6 | 2 | 2 inline int udiv(int x, int d) { #ifdef DEBUG_ENABLED CRASH_COND(d < 0); #endif if (x < 0) { return (x - d + 1) / d; } else { return x / d; } } // TODO Rename `wrapi` // `Math::wrapi` with zero min inline int wrap(int x, int d) { return ((unsigned int)x - (x < 0)) % (unsigned int)d; //return ((x % d) + d) % d; } // Math::wrapf with zero min inline float wrapf(float x, float d) { return Math::is_zero_approx(d) ? 0.f : 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)) { return p_from; } float x = clamp((p_weight - p_from) / (p_to - p_from), 0.0f, 1.0f); return x * x * (3.0f - 2.0f * x); } bool try_call_script(const Object *obj, StringName method_name, const Variant **args, unsigned int argc, Variant *out_ret); inline bool try_call_script(const Object *obj, StringName method_name, Variant arg0, Variant arg1, Variant arg2, Variant *out_ret) { const Variant *args[3] = { &arg0, &arg1, &arg2 }; return try_call_script(obj, method_name, args, 3, out_ret); } #endif // HEADER_VOXEL_UTILITY_H