723 lines
22 KiB
C++

/*
Copyright (c) 2013 yvt
This file is part of OpenSpades.
OpenSpades is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
*/
#include <new>
#include <cstdlib>
#include "Math.h"
#include <Core/Debug.h>
#include <Core/ThreadLocalStorage.h>
namespace spades {
namespace {
std::random_device r_device;
std::mt19937_64 global_rng{r_device()};
std::mutex global_rng_mutex;
} // namespace
/** Thread-local random number generator. */
class LocalRNG {
public:
using result_type = std::uint64_t;
LocalRNG() {
auto sampleRandom = [] {
std::lock_guard<std::mutex> lock{global_rng_mutex};
return global_rng();
};
do {
s[0] = sampleRandom();
} while (s[0] == 0);
do {
s[1] = sampleRandom();
} while (s[1] == 0);
}
result_type operator()() {
uint64_t x = s[0];
uint64_t y = s[1];
s[0] = y;
x ^= x << 23; // a
s[1] = x ^ y ^ (x >> 17) ^ (y >> 26); // b, c
return s[1] + y;
}
float SampleFloat() { return std::uniform_real_distribution<float>{}(*this); }
template <class T = int> inline T SampleInt(T a, T b) {
return std::uniform_int_distribution<T>{a, b}(*this);
}
static constexpr result_type min() { return 0; }
static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
private:
std::uint64_t s[2];
};
namespace {
AutoDeletedThreadLocalStorage<LocalRNG> thread_local_rng;
LocalRNG &GetThreadLocalRNG() {
LocalRNG *rng = thread_local_rng.GetPointer();
if (!rng) {
thread_local_rng = rng = new LocalRNG();
}
return *rng;
}
} // namespace
std::uint_fast64_t SampleRandom() { return GetThreadLocalRNG()(); }
float SampleRandomFloat() {
return std::uniform_real_distribution<float>{}(GetThreadLocalRNG());
}
template <class T> T SampleRandomInt(T a, T b) {
return std::uniform_int_distribution<T>{a, b}(GetThreadLocalRNG());
}
// Note: `uniform_int_distribution` does not accept `char` nor `unsigned char`
// (N4659 29.6.1.1 [rand.req.genl])
template short SampleRandomInt(short a, short b);
template unsigned short SampleRandomInt(unsigned short a, unsigned short b);
template int SampleRandomInt(int a, int b);
template unsigned int SampleRandomInt(unsigned int a, unsigned int b);
template long SampleRandomInt(long a, long b);
template unsigned long SampleRandomInt(unsigned long a, unsigned long b);
template long long SampleRandomInt(long long a, long long b);
template unsigned long long SampleRandomInt(unsigned long long a, unsigned long long b);
void Matrix4Multiply(const float a[16], const float b[16], float out[16]) {
out[0] = b[0] * a[0] + b[1] * a[4] + b[2] * a[8] + b[3] * a[12];
out[1] = b[0] * a[1] + b[1] * a[5] + b[2] * a[9] + b[3] * a[13];
out[2] = b[0] * a[2] + b[1] * a[6] + b[2] * a[10] + b[3] * a[14];
out[3] = b[0] * a[3] + b[1] * a[7] + b[2] * a[11] + b[3] * a[15];
out[4] = b[4] * a[0] + b[5] * a[4] + b[6] * a[8] + b[7] * a[12];
out[5] = b[4] * a[1] + b[5] * a[5] + b[6] * a[9] + b[7] * a[13];
out[6] = b[4] * a[2] + b[5] * a[6] + b[6] * a[10] + b[7] * a[14];
out[7] = b[4] * a[3] + b[5] * a[7] + b[6] * a[11] + b[7] * a[15];
out[8] = b[8] * a[0] + b[9] * a[4] + b[10] * a[8] + b[11] * a[12];
out[9] = b[8] * a[1] + b[9] * a[5] + b[10] * a[9] + b[11] * a[13];
out[10] = b[8] * a[2] + b[9] * a[6] + b[10] * a[10] + b[11] * a[14];
out[11] = b[8] * a[3] + b[9] * a[7] + b[10] * a[11] + b[11] * a[15];
out[12] = b[12] * a[0] + b[13] * a[4] + b[14] * a[8] + b[15] * a[12];
out[13] = b[12] * a[1] + b[13] * a[5] + b[14] * a[9] + b[15] * a[13];
out[14] = b[12] * a[2] + b[13] * a[6] + b[14] * a[10] + b[15] * a[14];
out[15] = b[12] * a[3] + b[13] * a[7] + b[14] * a[11] + b[15] * a[15];
}
Matrix4::Matrix4(float *elms) {
for (int i = 0; i < 16; i++)
m[i] = elms[i];
}
Matrix4::Matrix4(float m00, float m10, float m20, float m30, float m01, float m11, float m21,
float m31, float m02, float m12, float m22, float m32, float m03, float m13,
float m23, float m33) {
m[0] = m00;
m[1] = m10;
m[2] = m20;
m[3] = m30;
m[4] = m01;
m[5] = m11;
m[6] = m21;
m[7] = m31;
m[8] = m02;
m[9] = m12;
m[10] = m22;
m[11] = m32;
m[12] = m03;
m[13] = m13;
m[14] = m23;
m[15] = m33;
}
Matrix4 Matrix4::operator*(const spades::Matrix4 &other) const {
Matrix4 out;
Matrix4Multiply(m, other.m, out.m);
return Matrix4(out);
}
Vector4 operator*(const Matrix4 &mat, const Vector4 &v) {
float x, y, z, w;
x = mat.m[0] * v.x;
y = mat.m[1] * v.x;
z = mat.m[2] * v.x;
w = mat.m[3] * v.x;
x += mat.m[4] * v.y;
y += mat.m[5] * v.y;
z += mat.m[6] * v.y;
w += mat.m[7] * v.y;
x += mat.m[8] * v.z;
y += mat.m[9] * v.z;
z += mat.m[10] * v.z;
w += mat.m[11] * v.z;
x += mat.m[12] * v.w;
y += mat.m[13] * v.w;
z += mat.m[14] * v.w;
w += mat.m[15] * v.w;
return MakeVector4(x, y, z, w);
}
Vector4 operator*(const Matrix4 &mat, const Vector3 &v) {
return mat * MakeVector4(v.x, v.y, v.z, 1.f);
}
Vector3 Matrix4::GetOrigin() const { return MakeVector3(m[12], m[13], m[14]); }
Vector3 Matrix4::GetAxis(int axis) const {
switch (axis) {
case 0: return MakeVector3(m[0], m[1], m[2]);
case 1: return MakeVector3(m[4], m[5], m[6]);
default: SPAssert(false);
case 2: return MakeVector3(m[8], m[9], m[10]);
}
}
Matrix4 Matrix4::FromAxis(spades::Vector3 a1, spades::Vector3 a2, spades::Vector3 a3,
spades::Vector3 origin) {
return Matrix4(a1.x, a1.y, a1.z, 0.f, a2.x, a2.y, a2.z, 0.f, a3.x, a3.y, a3.z, 0.f,
origin.x, origin.y, origin.z, 1.f);
}
Matrix4 Matrix4::Identity() { return Matrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
Matrix4 Matrix4::Translate(spades::Vector3 v) { return Translate(v.x, v.y, v.z); }
Matrix4 Matrix4::Translate(float x, float y, float z) {
return Matrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1);
}
Matrix4 Matrix4::Rotate(spades::Vector3 axis, float theta) {
axis = axis.Normalize();
float c = cosf(theta), s = sinf(theta);
float ic = 1.f - c;
float x = axis.x, y = axis.y, z = axis.z;
return Matrix4(x * x * ic + c, x * y * ic + z * s, x * z * ic - y * s, 0,
x * y * ic - z * s, y * y * ic + c, y * z * ic + x * s, 0,
x * z * ic + y * s, y * z * ic - x * s, z * z * ic + c, 0, 0, 0, 0, 1);
}
Matrix4 Matrix4::Scale(float f) { return Scale(f, f, f); }
Matrix4 Matrix4::Scale(spades::Vector3 v) { return Scale(v.x, v.y, v.z); }
Matrix4 Matrix4::Scale(float x, float y, float z) {
return Matrix4(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1);
}
Matrix4 Matrix4::Transposed() const {
return Matrix4(m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14],
m[3], m[7], m[11], m[15]);
}
static void inverseMatrix4(float *matrix) {
// based on three.js's getInverse, which is in turn based on
// http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
float n11 = matrix[0], n12 = matrix[4], n13 = matrix[8], n14 = matrix[12];
float n21 = matrix[1], n22 = matrix[5], n23 = matrix[9], n24 = matrix[13];
float n31 = matrix[2], n32 = matrix[6], n33 = matrix[10], n34 = matrix[14];
float n41 = matrix[3], n42 = matrix[7], n43 = matrix[11], n44 = matrix[15];
matrix[0] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 -
n23 * n32 * n44 + n22 * n33 * n44;
matrix[4] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 +
n13 * n32 * n44 - n12 * n33 * n44;
matrix[8] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 -
n13 * n22 * n44 + n12 * n23 * n44;
matrix[12] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 +
n13 * n22 * n34 - n12 * n23 * n34;
matrix[1] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 +
n23 * n31 * n44 - n21 * n33 * n44;
matrix[5] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 -
n13 * n31 * n44 + n11 * n33 * n44;
matrix[9] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 +
n13 * n21 * n44 - n11 * n23 * n44;
matrix[13] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 -
n13 * n21 * n34 + n11 * n23 * n34;
matrix[2] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 -
n22 * n31 * n44 + n21 * n32 * n44;
matrix[6] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 +
n12 * n31 * n44 - n11 * n32 * n44;
matrix[10] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 -
n12 * n21 * n44 + n11 * n22 * n44;
matrix[14] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 +
n12 * n21 * n34 - n11 * n22 * n34;
matrix[3] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 +
n22 * n31 * n43 - n21 * n32 * n43;
matrix[7] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 -
n12 * n31 * n43 + n11 * n32 * n43;
matrix[11] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 +
n12 * n21 * n43 - n11 * n22 * n43;
matrix[15] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 -
n12 * n21 * n33 + n11 * n22 * n33;
float det = n11 * matrix[0] + n21 * matrix[4] + n31 * matrix[8] + n41 * matrix[12];
float idet = 1.0f / det;
for (int i = 0; i < 16; ++i) {
matrix[i] *= idet;
}
}
Matrix4 Matrix4::Inversed() const {
Matrix4 mm = *this;
inverseMatrix4(mm.m);
return mm;
}
Matrix4 Matrix4::InversedFast() const {
Matrix4 out = Matrix4::Identity();
Vector3 axis1 = GetAxis(0);
Vector3 axis2 = GetAxis(1);
Vector3 axis3 = GetAxis(2);
Vector3 norm1 = axis1 / axis1.GetPoweredLength();
Vector3 norm2 = axis2 / axis2.GetPoweredLength();
Vector3 norm3 = axis3 / axis3.GetPoweredLength();
out.m[0] = norm1.x;
out.m[1] = norm2.x;
out.m[2] = norm3.x;
out.m[4] = norm1.y;
out.m[5] = norm2.y;
out.m[6] = norm3.y;
out.m[8] = norm1.z;
out.m[9] = norm2.z;
out.m[10] = norm3.z;
Vector3 s = (out * GetOrigin()).GetXYZ();
out.m[12] = -s.x;
out.m[13] = -s.y;
out.m[14] = -s.z;
return out;
}
AABB3::operator OBB3() const {
Vector3 siz = max - min;
return OBB3(
Matrix4(siz.x, 0, 0, 0, 0, siz.y, 0, 0, 0, 0, siz.z, 0, min.x, min.y, min.z, 1));
}
bool OBB3::RayCast(spades::Vector3 start, spades::Vector3 dir, spades::Vector3 *hitPos) {
// inside?
if (*this && start) {
*hitPos = start;
return true;
}
Vector3 normX = {m.m[0], m.m[1], m.m[2]};
Vector3 normY = {m.m[4], m.m[5], m.m[6]};
Vector3 normZ = {m.m[8], m.m[9], m.m[10]};
// subtract offset
Vector3 origin = {m.m[12], m.m[13], m.m[14]};
start -= origin;
Vector3 end = start + dir;
float dotX = Vector3::Dot(dir, normX);
float dotY = Vector3::Dot(dir, normY);
float dotZ = Vector3::Dot(dir, normZ);
// x-plane hit test
if (dotX != 0.f) {
float startp = Vector3::Dot(start, normX);
float endp = Vector3::Dot(end, normX);
float boxp = Vector3::Dot(normX, normX);
float hit; // 0=start, 1=end
if (startp < endp) {
hit = startp / (startp - endp);
} else {
hit = (boxp - startp) / (endp - startp);
}
if (hit >= 0.f) {
*hitPos = start + dir * hit;
float yd = Vector3::Dot(*hitPos, normY);
float zd = Vector3::Dot(*hitPos, normZ);
if (yd >= 0 && zd >= 0 && yd <= normY.GetPoweredLength() &&
zd <= normZ.GetPoweredLength()) {
// hit x-plane
*hitPos += origin;
return true;
}
}
}
// y-plane hit test
if (dotY != 0.f) {
float startp = Vector3::Dot(start, normY);
float endp = Vector3::Dot(end, normY);
float boxp = Vector3::Dot(normY, normY);
float hit; // 0=start, 1=end
if (startp < endp) {
hit = startp / (startp - endp);
} else {
hit = (boxp - startp) / (endp - startp);
}
if (hit >= 0.f) {
*hitPos = start + dir * hit;
float xd = Vector3::Dot(*hitPos, normX);
float zd = Vector3::Dot(*hitPos, normZ);
if (xd >= 0 && zd >= 0 && xd <= normX.GetPoweredLength() &&
zd <= normZ.GetPoweredLength()) {
// hit y-plane
*hitPos += origin;
return true;
}
}
}
// z-plane hit test
if (dotZ != 0.f) {
float startp = Vector3::Dot(start, normZ);
float endp = Vector3::Dot(end, normZ);
float boxp = Vector3::Dot(normZ, normZ);
float hit; // 0=start, 1=end
if (startp < endp) {
hit = startp / (startp - endp);
} else {
hit = (boxp - startp) / (endp - startp);
}
if (hit >= 0.f) {
*hitPos = start + dir * hit;
float xd = Vector3::Dot(*hitPos, normX);
float yd = Vector3::Dot(*hitPos, normY);
if (xd >= 0 && yd >= 0 && xd <= normX.GetPoweredLength() &&
yd <= normY.GetPoweredLength()) {
// hit z-plane
*hitPos += origin;
return true;
}
}
}
return false;
}
bool OBB3::operator&&(const spades::Vector3 &v) const {
Matrix4 rmat = m.InversedFast();
Vector3 r = (rmat * v).GetXYZ();
return r.x >= 0.f && r.y >= 0.f && r.z >= 0.f && r.x < 1.f && r.y < 1.f && r.z < 1.f;
}
float OBB3::GetDistanceTo(const spades::Vector3 &v) const {
if (*this && v) {
return 0.f;
}
Matrix4 rmat = m.InversedFast();
Vector3 r = (rmat * v).GetXYZ();
Vector3 pt = r;
// find nearest point
r.x = std::max(r.x, 0.f);
r.y = std::max(r.y, 0.f);
r.z = std::max(r.z, 0.f);
r.x = std::min(r.x, 1.f);
r.y = std::min(r.y, 1.f);
r.z = std::min(r.z, 1.f);
// compute difference in local space
pt -= r;
// scale to global space
float len;
len = pt.x * pt.x * m.GetAxis(0).GetPoweredLength();
len += pt.y * pt.y * m.GetAxis(1).GetPoweredLength();
len += pt.z * pt.z * m.GetAxis(2).GetPoweredLength();
return len;
}
AABB3 OBB3::GetBoundingAABB() const {
Vector3 orig = m.GetOrigin();
Vector3 axis1 = m.GetAxis(0);
Vector3 axis2 = m.GetAxis(1);
Vector3 axis3 = m.GetAxis(2);
AABB3 ab(orig.x, orig.y, orig.z, 0, 0, 0);
ab += orig + axis1;
ab += orig + axis2;
ab += orig + axis1 + axis2;
ab += orig + axis3;
ab += orig + axis3 + axis1;
ab += orig + axis3 + axis2;
ab += orig + axis3 + axis1 + axis2;
return ab;
}
Vector3 Line3::Project(Vector3 v, bool supposeUnbounded) {
Vector3 delta = v2 - v1;
Vector3 direction = delta.Normalize();
float length = delta.GetLength();
float positionOnLine = Vector3::Dot(v - v1, direction);
if ((end1 & !supposeUnbounded) && positionOnLine < 0.0f) {
positionOnLine = 0.0f;
};
if ((end2 & !supposeUnbounded) && positionOnLine > length) {
positionOnLine = length;
};
return v1 + direction * positionOnLine;
}
float Line3::GetDistanceTo(Vector3 v, bool supposeUnbounded) {
return (v - Project(v, supposeUnbounded)).GetLength();
}
std::string Replace(const std::string &text, const std::string &before,
const std::string &after) {
std::string out;
size_t pos = 0;
while (pos < text.size()) {
size_t newPos = text.find(before, pos);
if (newPos == std::string::npos) {
out.append(text, pos, text.size() - pos);
break;
}
out.append(text, pos, newPos - pos);
out += after;
pos = newPos + before.size();
}
return out;
}
std::vector<std::string> Split(const std::string &str, const std::string &sep) {
std::vector<std::string> strs;
size_t pos = 0;
while (pos < str.size()) {
size_t newPos = str.find(sep, pos);
if (newPos == std::string::npos) {
strs.push_back(str.substr(pos));
break;
}
strs.push_back(str.substr(pos, newPos - pos));
pos = newPos + sep.size();
}
return strs;
}
std::vector<std::string> SplitIntoLines(const std::string &str) {
std::vector<std::string> strs;
size_t pos = 0;
char newLineChar = 0;
std::string buf;
for (pos = 0; pos < str.size(); pos++) {
if (str[pos] == 13 || str[pos] == 10) {
if (newLineChar == 0)
newLineChar = str[pos];
if (str[pos] != newLineChar) {
continue;
}
strs.push_back(buf);
buf.clear();
continue;
}
buf += str[pos];
}
strs.push_back(buf);
return strs;
}
std::string EscapeControlCharacters(const std::string &str) {
std::string out;
out.reserve(str.size() * 2);
for (size_t i = 0; i < str.size(); i++) {
char c = str[i];
if ((c >= 0 && c < 0x20 && c != 10 && c != 13 && c != 9)) {
out += '<';
switch (c) {
case 0x00: out += "NUL"; break;
case 0x01: out += "SOH"; break;
case 0x02: out += "STX"; break;
case 0x03: out += "ETX"; break;
case 0x04: out += "EOT"; break;
case 0x05: out += "ENQ"; break;
case 0x06: out += "ACK"; break;
case 0x07: out += "BEL"; break;
case 0x08: out += "BS"; break;
case 0x09: out += "TAB"; break;
case 0x0a: out += "LF"; break;
case 0x0b: out += "VT"; break;
case 0x0c: out += "FF"; break;
case 0x0d: out += "CR"; break;
case 0x0e: out += "SO"; break;
case 0x0f: out += "SI"; break;
case 0x10: out += "DLE"; break;
case 0x11: out += "DC1"; break;
case 0x12: out += "DC2"; break;
case 0x13: out += "DC3"; break;
case 0x14: out += "DC4"; break;
case 0x15: out += "NAK"; break;
case 0x16: out += "SYN"; break;
case 0x17: out += "ETB"; break;
case 0x18: out += "CAN"; break;
case 0x19: out += "EM"; break;
case 0x1a: out += "SUB"; break;
case 0x1b: out += "ESC"; break;
case 0x1c: out += "FS"; break;
case 0x1d: out += "GS"; break;
case 0x1e: out += "RS"; break;
case 0x1f: out += "US"; break;
}
out += '>';
} else {
out += c;
}
}
return out;
}
std::string TrimSpaces(const std::string &str) {
size_t pos = str.find_first_not_of(" \t\n\r");
if (pos == std::string::npos)
return std::string();
size_t po2 = str.find_last_not_of(" \t\n\r");
SPAssert(po2 != std::string::npos);
SPAssert(po2 >= pos);
return str.substr(pos, po2 - pos + 1);
}
bool PlaneCullTest(const Plane3 &plane, const AABB3 &box) {
Vector3 testVertex;
// find the vertex with the greatest distance value
if (plane.n.x >= 0.f) {
if (plane.n.y >= 0.f) {
if (plane.n.z >= 0.f) {
testVertex = box.max;
} else {
testVertex = MakeVector3(box.max.x, box.max.y, box.min.z);
}
} else {
if (plane.n.z >= 0.f) {
testVertex = MakeVector3(box.max.x, box.min.y, box.max.z);
} else {
testVertex = MakeVector3(box.max.x, box.min.y, box.min.z);
}
}
} else {
if (plane.n.y >= 0.f) {
if (plane.n.z >= 0.f) {
testVertex = MakeVector3(box.min.x, box.max.y, box.max.z);
} else {
testVertex = MakeVector3(box.min.x, box.max.y, box.min.z);
}
} else {
if (plane.n.z >= 0.f) {
testVertex = MakeVector3(box.min.x, box.min.y, box.max.z);
} else {
testVertex = box.min;
}
}
}
return plane.GetDistanceTo(testVertex) >= 0.f;
}
bool EqualsIgnoringCase(const std::string &a, const std::string &b) {
if (a.size() != b.size())
return false;
if (&a == &b)
return true;
for (size_t siz = a.size(), i = 0; i < siz; i++) {
char x = a[i], y = b[i];
if (tolower(x) != tolower(y))
return false;
}
return true;
}
uint32_t GetCodePointFromUTF8String(const std::string &str, size_t start, size_t *outNumBytes) {
if (start >= str.size()) {
if (outNumBytes)
*outNumBytes = 0;
return 0;
}
if ((str[start] & 0x80) == 0) {
if (outNumBytes)
*outNumBytes = 1;
return (uint32_t)str[start];
}
// WARNING: start <= str.size() - 2 is bad (this leads to security holes)
uint32_t ret;
if ((str[start] & 0xe0) == 0xc0 && start + 2 <= str.size()) {
if (outNumBytes)
*outNumBytes = 2;
ret = (str[start] & 0x1f) << 6;
ret |= (str[start + 1] & 0x3f);
return ret;
}
if ((str[start] & 0xf0) == 0xe0 && start + 3 <= str.size()) {
if (outNumBytes)
*outNumBytes = 3;
ret = (str[start] & 0xf) << 12;
ret |= (str[start + 1] & 0x3f) << 6;
ret |= (str[start + 2] & 0x3f);
return ret;
}
if ((str[start] & 0xf8) == 0xf0 && start + 4 <= str.size()) {
if (outNumBytes)
*outNumBytes = 4;
ret = (str[start] & 0x7) << 18;
ret |= (str[start + 1] & 0x3f) << 12;
ret |= (str[start + 2] & 0x3f) << 6;
ret |= (str[start + 3] & 0x3f);
return ret;
}
if ((str[start] & 0xfc) == 0xf8 && start + 5 <= str.size()) {
if (outNumBytes)
*outNumBytes = 5;
ret = (str[start] & 0x3) << 24;
ret |= (str[start + 1] & 0x3f) << 18;
ret |= (str[start + 2] & 0x3f) << 12;
ret |= (str[start + 3] & 0x3f) << 6;
ret |= (str[start + 4] & 0x3f);
return ret;
}
if ((str[start] & 0xfe) == 0xfc && start + 6 <= str.size()) {
if (outNumBytes)
*outNumBytes = 6;
ret = (str[start] & 0x1) << 30;
ret |= (str[start + 1] & 0x3f) << 24;
ret |= (str[start + 2] & 0x3f) << 18;
ret |= (str[start + 3] & 0x3f) << 12;
ret |= (str[start + 4] & 0x3f) << 6;
ret |= (str[start + 5] & 0x3f);
return ret;
}
// invalid UTF8
if (outNumBytes)
*outNumBytes = 1;
return (uint32_t)str[start];
}
float SmoothStep(float v) { return v * v * (3.f - 2.f * v); }
float Mix(float a, float b, float frac) { return a + (b - a) * frac; }
Vector2 Mix(const Vector2 &a, const Vector2 &b, float frac) { return a + (b - a) * frac; }
Vector3 Mix(const Vector3 &a, const Vector3 &b, float frac) { return a + (b - a) * frac; }
}