X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Futil%2Fnumeric.h;h=6f82a18c1b4d858019e29d8f5a9c0da064b6a3a1;hb=d5456da;hp=5ed4d0e999d1fc03e987d9dd109534a04027b944;hpb=9f031a67594162a53b07acbfbc65faf8c4938e99;p=minetest.git diff --git a/src/util/numeric.h b/src/util/numeric.h index 5ed4d0e99..6f82a18c1 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -1,6 +1,6 @@ /* -Minetest-c55 -Copyright (C) 2010-2012 celeron55, Perttu Ahola +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,37 +17,28 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef UTIL_NUMERIC_HEADER -#define UTIL_NUMERIC_HEADER +#pragma once -#include "../irrlichttypes.h" -#include "../irr_v2d.h" -#include "../irr_v3d.h" -#include "../irr_aabb3d.h" -#include +#include "basic_macros.h" +#include "irrlichttypes.h" +#include "irr_v2d.h" +#include "irr_v3d.h" +#include "irr_aabb3d.h" +#include -// Calculate the borders of a "d-radius" cube -void getFacePositions(core::list &list, u16 d); +#define rangelim(d, min, max) ((d) < (min) ? (min) : ((d) > (max) ? (max) : (d))) +#define myfloor(x) ((x) < 0.0 ? (int)(x) - 1 : (int)(x)) +// The naive swap performs better than the xor version +#define SWAP(t, x, y) do { \ + t temp = x; \ + x = y; \ + y = temp; \ +} while (0) -class IndentationRaiser -{ -public: - IndentationRaiser(u16 *indentation) - { - m_indentation = indentation; - (*m_indentation)++; - } - ~IndentationRaiser() - { - (*m_indentation)--; - } -private: - u16 *m_indentation; -}; inline s16 getContainerPos(s16 p, s16 d) { - return (p>=0 ? p : p-d+1) / d; + return (p >= 0 ? p : p - d + 1) / d; } inline v2s16 getContainerPos(v2s16 p, s16 d) @@ -84,6 +75,26 @@ inline v3s16 getContainerPos(v3s16 p, v3s16 d) ); } +inline void getContainerPosWithOffset(s16 p, s16 d, s16 &container, s16 &offset) +{ + container = (p >= 0 ? p : p - d + 1) / d; + offset = p & (d - 1); +} + +inline void getContainerPosWithOffset(const v2s16 &p, s16 d, v2s16 &container, v2s16 &offset) +{ + getContainerPosWithOffset(p.X, d, container.X, offset.X); + getContainerPosWithOffset(p.Y, d, container.Y, offset.Y); +} + +inline void getContainerPosWithOffset(const v3s16 &p, s16 d, v3s16 &container, v3s16 &offset) +{ + getContainerPosWithOffset(p.X, d, container.X, offset.X); + getContainerPosWithOffset(p.Y, d, container.Y, offset.Y); + getContainerPosWithOffset(p.Z, d, container.Z, offset.Z); +} + + inline bool isInArea(v3s16 p, s16 d) { return ( @@ -110,109 +121,136 @@ inline bool isInArea(v3s16 p, v3s16 d) ); } -inline s16 rangelim(s16 i, s16 max) +inline void sortBoxVerticies(v3s16 &p1, v3s16 &p2) { + if (p1.X > p2.X) + SWAP(s16, p1.X, p2.X); + if (p1.Y > p2.Y) + SWAP(s16, p1.Y, p2.Y); + if (p1.Z > p2.Z) + SWAP(s16, p1.Z, p2.Z); +} + +inline v3s16 componentwise_min(const v3s16 &a, const v3s16 &b) +{ + return v3s16(MYMIN(a.X, b.X), MYMIN(a.Y, b.Y), MYMIN(a.Z, b.Z)); +} + +inline v3s16 componentwise_max(const v3s16 &a, const v3s16 &b) { - if(i < 0) - return 0; - if(i > max) - return max; - return i; + return v3s16(MYMAX(a.X, b.X), MYMAX(a.Y, b.Y), MYMAX(a.Z, b.Z)); } -#define rangelim(d, min, max) ((d) < (min) ? (min) : ((d)>(max)?(max):(d))) -inline v3s16 arealim(v3s16 p, s16 d) +/** Returns \p f wrapped to the range [-360, 360] + * + * See test.cpp for example cases. + * + * \note This is also used in cases where degrees wrapped to the range [0, 360] + * is innapropriate (e.g. pitch needs negative values) + * + * \internal functionally equivalent -- although precision may vary slightly -- + * to fmodf((f), 360.0f) however empirical tests indicate that this approach is + * faster. + */ +inline float modulo360f(float f) { - if(p.X < 0) - p.X = 0; - if(p.Y < 0) - p.Y = 0; - if(p.Z < 0) - p.Z = 0; - if(p.X > d-1) - p.X = d-1; - if(p.Y > d-1) - p.Y = d-1; - if(p.Z > d-1) - p.Z = d-1; - return p; + int sign; + int whole; + float fraction; + + if (f < 0) { + f = -f; + sign = -1; + } else { + sign = 1; + } + + whole = f; + + fraction = f - whole; + whole %= 360; + + return sign * (whole + fraction); } -/* - See test.cpp for example cases. - wraps degrees to the range of -360...360 - NOTE: Wrapping to 0...360 is not used because pitch needs negative values. -*/ -inline float wrapDegrees(float f) -{ - // Take examples of f=10, f=720.5, f=-0.5, f=-360.5 - // This results in - // 10, 720, -1, -361 - int i = floor(f); - // 0, 2, 0, -1 - int l = i / 360; - // NOTE: This would be used for wrapping to 0...360 - // 0, 2, -1, -2 - /*if(i < 0) - l -= 1;*/ - // 0, 720, 0, -360 - int k = l * 360; - // 10, 0.5, -0.5, -0.5 - f -= float(k); - return f; -} - -/* Wrap to 0...360 */ +/** Returns \p f wrapped to the range [0, 360] + */ inline float wrapDegrees_0_360(float f) { - // Take examples of f=10, f=720.5, f=-0.5, f=-360.5 - // This results in - // 10, 720, -1, -361 - int i = floor(f); - // 0, 2, 0, -1 - int l = i / 360; - // Wrap to 0...360 - // 0, 2, -1, -2 - if(i < 0) - l -= 1; - // 0, 720, 0, -360 - int k = l * 360; - // 10, 0.5, -0.5, -0.5 - f -= float(k); - return f; -} - -/* Wrap to -180...180 */ + float value = modulo360f(f); + return value < 0 ? value + 360 : value; +} + + +/** Returns \p v3f wrapped to the range [0, 360] + */ +inline v3f wrapDegrees_0_360_v3f(v3f v) +{ + v3f value_v3f; + value_v3f.X = modulo360f(v.X); + value_v3f.Y = modulo360f(v.Y); + value_v3f.Z = modulo360f(v.Z); + + // Now that values are wrapped, use to get values for certain ranges + value_v3f.X = value_v3f.X < 0 ? value_v3f.X + 360 : value_v3f.X; + value_v3f.Y = value_v3f.Y < 0 ? value_v3f.Y + 360 : value_v3f.Y; + value_v3f.Z = value_v3f.Z < 0 ? value_v3f.Z + 360 : value_v3f.Z; + return value_v3f; +} + + +/** Returns \p f wrapped to the range [-180, 180] + */ inline float wrapDegrees_180(float f) { - f += 180; - f = wrapDegrees_0_360(f); - f -= 180; - return f; + float value = modulo360f(f + 180); + if (value < 0) + value += 360; + return value - 180; } /* Pseudo-random (VC++ rand() sucks) */ -int myrand(void); -void mysrand(unsigned seed); -#define MYRAND_MAX 32767 - +#define MYRAND_RANGE 0xffffffff +u32 myrand(); +void mysrand(unsigned int seed); +void myrand_bytes(void *out, size_t len); int myrand_range(int min, int max); /* Miscellaneous functions */ +inline u32 get_bits(u32 x, u32 pos, u32 len) +{ + u32 mask = (1 << len) - 1; + return (x >> pos) & mask; +} + +inline void set_bits(u32 *x, u32 pos, u32 len, u32 val) +{ + u32 mask = (1 << len) - 1; + *x &= ~(mask << pos); + *x |= (val & mask) << pos; +} + +inline u32 calc_parity(u32 v) +{ + v ^= v >> 16; + v ^= v >> 8; + v ^= v >> 4; + v &= 0xf; + return (0x6996 >> v) & 1; +} + +u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed); + bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 camera_fov, f32 range, f32 *distance_ptr=NULL); -/* - Some helper stuff -*/ -#define MYMIN(a,b) ((a)<(b)?(a):(b)) -#define MYMAX(a,b) ((a)>(b)?(a):(b)) +s16 adjustDist(s16 dist, float zoom_fov); /* Returns nearest 32-bit integer for given floating point number. @@ -220,7 +258,12 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, */ inline s32 myround(f32 f) { - return floor(f + 0.5); + return (s32)(f < 0.f ? (f - 0.5f) : (f + 0.5f)); +} + +inline constexpr f32 sqr(f32 f) +{ + return f * f; } /* @@ -228,11 +271,21 @@ inline s32 myround(f32 f) */ inline v3s16 floatToInt(v3f p, f32 d) { - v3s16 p2( - (p.X + (p.X>0 ? d/2 : -d/2))/d, - (p.Y + (p.Y>0 ? d/2 : -d/2))/d, - (p.Z + (p.Z>0 ? d/2 : -d/2))/d); - return p2; + return v3s16( + (p.X + (p.X > 0 ? d / 2 : -d / 2)) / d, + (p.Y + (p.Y > 0 ? d / 2 : -d / 2)) / d, + (p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d); +} + +/* + Returns integer position of node in given double precision position + */ +inline v3s16 doubleToInt(v3d p, double d) +{ + return v3s16( + (p.X + (p.X > 0 ? d / 2 : -d / 2)) / d, + (p.Y + (p.Y > 0 ? d / 2 : -d / 2)) / d, + (p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d); } /* @@ -240,34 +293,32 @@ inline v3s16 floatToInt(v3f p, f32 d) */ inline v3f intToFloat(v3s16 p, f32 d) { - v3f p2( + return v3f( (f32)p.X * d, (f32)p.Y * d, (f32)p.Z * d ); - return p2; } // Random helper. Usually d=BS -inline core::aabbox3d getNodeBox(v3s16 p, float d) -{ - return core::aabbox3d( - (float)p.X * d - 0.5*d, - (float)p.Y * d - 0.5*d, - (float)p.Z * d - 0.5*d, - (float)p.X * d + 0.5*d, - (float)p.Y * d + 0.5*d, - (float)p.Z * d + 0.5*d +inline aabb3f getNodeBox(v3s16 p, float d) +{ + return aabb3f( + (float)p.X * d - 0.5f * d, + (float)p.Y * d - 0.5f * d, + (float)p.Z * d - 0.5f * d, + (float)p.X * d + 0.5f * d, + (float)p.Y * d + 0.5f * d, + (float)p.Z * d + 0.5f * d ); } + class IntervalLimiter { public: - IntervalLimiter(): - m_accumulator(0) - { - } + IntervalLimiter() = default; + /* dtime: time from last call to this method wanted_interval: interval wanted @@ -278,15 +329,17 @@ class IntervalLimiter bool step(float dtime, float wanted_interval) { m_accumulator += dtime; - if(m_accumulator < wanted_interval) + if (m_accumulator < wanted_interval) return false; m_accumulator -= wanted_interval; return true; } -protected: - float m_accumulator; + +private: + float m_accumulator = 0.0f; }; + /* Splits a list into "pages". For example, the list [1,2,3,4,5] split into two pages would be [1,2,3],[4,5]. This function computes the @@ -302,34 +355,80 @@ class IntervalLimiter */ inline void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxindex) { - if(length < 1 || pagecount < 1 || page < 1 || page > pagecount) - { + if (length < 1 || pagecount < 1 || page < 1 || page > pagecount) { // Special cases or invalid parameters minindex = maxindex = 0; - } - else if(pagecount <= length) - { + } else if(pagecount <= length) { // Less pages than entries in the list: // Each page contains at least one entry minindex = (length * (page-1) + (pagecount-1)) / pagecount; maxindex = (length * page + (pagecount-1)) / pagecount; - } - else - { + } else { // More pages than entries in the list: // Make sure the empty pages are at the end - if(page < length) - { + if (page < length) { minindex = page-1; maxindex = page; - } - else - { + } else { minindex = 0; maxindex = 0; } } } -#endif +inline float cycle_shift(float value, float by = 0, float max = 1) +{ + if (value + by < 0) return value + by + max; + if (value + by > max) return value + by - max; + return value + by; +} + +inline bool is_power_of_two(u32 n) +{ + return n != 0 && (n & (n - 1)) == 0; +} + +// Compute next-higher power of 2 efficiently, e.g. for power-of-2 texture sizes. +// Public Domain: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +inline u32 npot2(u32 orig) { + orig--; + orig |= orig >> 1; + orig |= orig >> 2; + orig |= orig >> 4; + orig |= orig >> 8; + orig |= orig >> 16; + return orig + 1; +} + +// Gradual steps towards the target value in a wrapped (circular) system +// using the shorter of both ways +template +inline void wrappedApproachShortest(T ¤t, const T target, const T stepsize, + const T maximum) +{ + T delta = target - current; + if (delta < 0) + delta += maximum; + + if (delta > stepsize && maximum - delta > stepsize) { + current += (delta < maximum / 2) ? stepsize : -stepsize; + if (current >= maximum) + current -= maximum; + } else { + current = target; + } +} + +void setPitchYawRollRad(core::matrix4 &m, const v3f &rot); +inline void setPitchYawRoll(core::matrix4 &m, const v3f &rot) +{ + setPitchYawRollRad(m, rot * core::DEGTORAD64); +} + +v3f getPitchYawRollRad(const core::matrix4 &m); + +inline v3f getPitchYawRoll(const core::matrix4 &m) +{ + return getPitchYawRollRad(m) * core::RADTODEG64; +}