some set up for camera, work started on own vector/matrix structures
This commit is contained in:
parent
e429cee207
commit
e2c6bf1b24
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12
dub.json
12
dub.json
@ -9,8 +9,8 @@
|
||||
"targetPath": "build",
|
||||
"sourceFiles-linux": ["build/libvma.a", "build/libstb_image.a", "build/libm3d.a"],
|
||||
"sourceFiles-windows": [],
|
||||
"importPaths": ["src/gears", "src/shared", "src/generated", "external/xxhash", "external/dplug/math", "external/inteli"],
|
||||
"sourcePaths": ["src/gears", "src/shared", "src/generated", "external/xxhash", "external/dplug/math", "external/inteli"],
|
||||
"importPaths": ["src/gears", "src/shared", "src/generated", "external/xxhash", "external/gfm", "external/inteli"],
|
||||
"sourcePaths": ["src/gears", "src/shared", "src/generated", "external/xxhash", "external/gfm", "external/inteli"],
|
||||
"libs-linux": ["xcb", "X11", "X11-xcb", "vulkan", "stdc++"],
|
||||
"libs-windows": [],
|
||||
"preGenerateCommands-linux": ["./build.sh", "build/Packer"],
|
||||
@ -22,8 +22,8 @@
|
||||
"targetType": "executable",
|
||||
"targetPath": "build",
|
||||
"targetName": "Packer",
|
||||
"importPaths": ["src/packer", "src/shared", "src/generated", "external/xxhash", "external/dplug/math", "external/inteli"],
|
||||
"sourcePaths": ["src/packer", "src/shared", "src/generated", "external/xxhash", "external/dplug/math", "external/inteli"],
|
||||
"importPaths": ["src/packer", "src/shared", "src/generated", "external/xxhash", "external/gfm", "external/inteli"],
|
||||
"sourcePaths": ["src/packer", "src/shared", "src/generated", "external/xxhash", "external/gfm", "external/inteli"],
|
||||
"sourceFiles-linux": ["build/libstb_image.a", "build/libm3d.a"],
|
||||
"preGenerateCommands-linux": ["./build.sh"],
|
||||
"postGenerateCommands-linux": [],
|
||||
@ -35,8 +35,8 @@
|
||||
"targetType": "executable",
|
||||
"targetPath": "build",
|
||||
"targetName": "Codegen",
|
||||
"importPaths": ["src/codegen", "src/shared", "external/xxhash", "external/dplug/math", "external/inteli"],
|
||||
"sourcePaths": ["src/codegen", "src/shared", "external/xxhash", "external/dplug/math", "external/inteli"],
|
||||
"importPaths": ["src/codegen", "src/shared", "external/xxhash", "external/gfm", "external/inteli"],
|
||||
"sourcePaths": ["src/codegen", "src/shared", "external/xxhash", "external/gfm", "external/inteli"],
|
||||
"sourceFiles-linux": ["build/libstb_image.a"],
|
||||
"preGenerateCommands-linux": ["./build.sh"],
|
||||
"preGenerateCommands-windows": [],
|
||||
|
||||
12
external/dplug/math/package.d
vendored
12
external/dplug/math/package.d
vendored
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Math package: rectangles, vectors, matrices.
|
||||
*
|
||||
* Copyright: Copyright Guillaume Piolat 2021.
|
||||
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
||||
* Note: this is part of the former gfm:math package, hence containing copyright from many GFM contributors.
|
||||
*/
|
||||
module dplug.math;
|
||||
|
||||
public import dplug.math.vector,
|
||||
dplug.math.box,
|
||||
dplug.math.matrix;
|
||||
@ -1,20 +1,15 @@
|
||||
/**
|
||||
* N-dimensional half-open interval [a, b[.
|
||||
*
|
||||
* Copyright: Copyright Guillaume Piolat 2015-2021.
|
||||
* Copyright Ahmet Sait 2021.
|
||||
* Copyright Ryan Roden-Corrent 2016.
|
||||
* Copyright Nathan Sashihara 2018.
|
||||
* Copyright Colden Cullen 2014.
|
||||
*
|
||||
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
||||
This module implements a generic axis-aligned bounding box (AABB).
|
||||
*/
|
||||
module dplug.math.box;
|
||||
module gfm.math.box;
|
||||
|
||||
import std.math,
|
||||
std.traits;
|
||||
std.traits,
|
||||
std.conv,
|
||||
std.string;
|
||||
|
||||
import dplug.math.vector;
|
||||
import gfm.math.vector,
|
||||
gfm.math.funcs;
|
||||
|
||||
/// N-dimensional half-open interval [a, b[.
|
||||
struct Box(T, int N)
|
||||
@ -317,52 +312,6 @@ struct Box(T, int N)
|
||||
return Box(min + offset, max + offset);
|
||||
}
|
||||
|
||||
/// Scale the box by factor `scale`, and round the result to integer if needed.
|
||||
@nogc Box scaleByFactor(float scale) const nothrow
|
||||
{
|
||||
Box res;
|
||||
static if (isFloatingPoint!T)
|
||||
{
|
||||
res.min.x = min.x * scale;
|
||||
res.min.y = min.y * scale;
|
||||
res.max.x = max.x * scale;
|
||||
res.max.y = max.y * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
res.min.x = cast(T)( round(min.x * scale) );
|
||||
res.min.y = cast(T)( round(min.y * scale) );
|
||||
res.max.x = cast(T)( round(max.x * scale) );
|
||||
res.max.y = cast(T)( round(max.y * scale) );
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static if (N == 2) // useful for UI that have horizontal and vertical scale
|
||||
{
|
||||
/// Scale the box by factor `scaleX` horizontally and `scaleY` vetically.
|
||||
/// Round the result to integer if needed.
|
||||
@nogc Box scaleByFactor(float scaleX, float scaleY) const nothrow
|
||||
{
|
||||
Box res;
|
||||
static if (isFloatingPoint!T)
|
||||
{
|
||||
res.min.x = min.x * scaleX;
|
||||
res.min.y = min.y * scaleY;
|
||||
res.max.x = max.x * scaleX;
|
||||
res.max.y = max.y * scaleY;
|
||||
}
|
||||
else
|
||||
{
|
||||
res.min.x = cast(T)( round(min.x * scaleX) );
|
||||
res.min.y = cast(T)( round(min.y * scaleY) );
|
||||
res.max.x = cast(T)( round(max.x * scaleX) );
|
||||
res.max.y = cast(T)( round(max.y * scaleY) );
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static if (N >= 2)
|
||||
{
|
||||
/// Translate this Box by `x`, `y`.
|
||||
@ -404,7 +353,7 @@ struct Box(T, int N)
|
||||
/// Returns: Expanded box.
|
||||
@nogc Box expand(bound_t point) pure const nothrow
|
||||
{
|
||||
import vector = dplug.math.vector;
|
||||
import vector = gfm.math.vector;
|
||||
return Box(vector.minByElem(min, point), vector.maxByElem(max, point));
|
||||
}
|
||||
|
||||
@ -661,9 +610,6 @@ unittest
|
||||
assert(rectangle(1, 2, 3, 4) == box2i(1, 2, 4, 6));
|
||||
assert(rectanglef(1, 2, 3, 4) == box2f(1, 2, 4, 6));
|
||||
assert(rectangled(1, 2, 3, 4) == box2d(1, 2, 4, 6));
|
||||
|
||||
assert(rectangle(10, 10, 20, 20).scaleByFactor(1.5f) == rectangle(15, 15, 30, 30));
|
||||
assert(rectangle(10, 10, 20, 20).scaleByFactor(1.5f, 2.0f) == rectangle(15, 20, 30, 40));
|
||||
}
|
||||
|
||||
/// True if `T` is a kind of Box
|
||||
@ -687,3 +633,27 @@ unittest
|
||||
static assert(is(DimensionType!box3d == double));
|
||||
}
|
||||
|
||||
private
|
||||
{
|
||||
static string generateLoopCode(string formatString, int N)() pure nothrow
|
||||
{
|
||||
string result;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
string index = ctIntToString(i);
|
||||
// replace all @ by indices
|
||||
result ~= formatString.replace("@", index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Speed-up CTFE conversions
|
||||
static string ctIntToString(int n) pure nothrow
|
||||
{
|
||||
static immutable string[16] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
||||
if (n < 10)
|
||||
return table[n];
|
||||
else
|
||||
return to!string(n);
|
||||
}
|
||||
}
|
||||
486
external/gfm/math/funcs.d
vendored
Normal file
486
external/gfm/math/funcs.d
vendored
Normal file
@ -0,0 +1,486 @@
|
||||
/**
|
||||
Useful math functions and range-based statistic computations.
|
||||
|
||||
If you need real statistics, consider using the $(WEB github.com/dsimcha/dstats,Dstats) library.
|
||||
*/
|
||||
module gfm.math.funcs;
|
||||
|
||||
import std.math,
|
||||
std.traits,
|
||||
std.range,
|
||||
std.math;
|
||||
|
||||
import inteli.xmmintrin;
|
||||
|
||||
import gfm.math.vector : Vector;
|
||||
|
||||
/// Convert from radians to degrees.
|
||||
@nogc T degrees(T)(in T x) pure nothrow
|
||||
if (isFloatingPoint!T || (is(T : Vector!(U, n), U, int n) && isFloatingPoint!U))
|
||||
{
|
||||
static if (is(T : Vector!(U, n), U, int n))
|
||||
return x * U(180 / PI);
|
||||
else
|
||||
return x * T(180 / PI);
|
||||
}
|
||||
|
||||
/// Convert from degrees to radians.
|
||||
@nogc T radians(T)(in T x) pure nothrow
|
||||
if (isFloatingPoint!T || (is(T : Vector!(U, n), U, int n) && isFloatingPoint!U))
|
||||
{
|
||||
static if (is(T : Vector!(U, n), U, int n))
|
||||
return x * U(PI / 180);
|
||||
else
|
||||
return x * T(PI / 180);
|
||||
}
|
||||
|
||||
/// Linear interpolation, akin to GLSL's mix.
|
||||
@nogc S lerp(S, T)(S a, S b, T t) pure nothrow
|
||||
if (is(typeof(t * b + (1 - t) * a) : S))
|
||||
{
|
||||
return t * b + (1 - t) * a;
|
||||
}
|
||||
|
||||
/// Clamp x in [min, max], akin to GLSL's clamp.
|
||||
@nogc T clamp(T)(T x, T min, T max) pure nothrow
|
||||
{
|
||||
if (x < min)
|
||||
return min;
|
||||
else if (x > max)
|
||||
return max;
|
||||
else
|
||||
return x;
|
||||
}
|
||||
|
||||
/// Integer truncation.
|
||||
@nogc long ltrunc(real x) nothrow // may be pure but trunc isn't pure
|
||||
{
|
||||
return cast(long)(trunc(x));
|
||||
}
|
||||
|
||||
/// Integer flooring.
|
||||
@nogc long lfloor(real x) nothrow // may be pure but floor isn't pure
|
||||
{
|
||||
return cast(long)(floor(x));
|
||||
}
|
||||
|
||||
/// Returns: Fractional part of x.
|
||||
@nogc T fract(T)(real x) nothrow
|
||||
{
|
||||
return x - lfloor(x);
|
||||
}
|
||||
|
||||
/// Safe asin: input clamped to [-1, 1]
|
||||
@nogc T safeAsin(T)(T x) pure nothrow
|
||||
{
|
||||
return asin(clamp!T(x, -1, 1));
|
||||
}
|
||||
|
||||
/// Safe acos: input clamped to [-1, 1]
|
||||
@nogc T safeAcos(T)(T x) pure nothrow
|
||||
{
|
||||
return acos(clamp!T(x, -1, 1));
|
||||
}
|
||||
|
||||
/// Same as GLSL step function.
|
||||
/// 0.0 is returned if x < edge, and 1.0 is returned otherwise.
|
||||
@nogc T step(T)(T edge, T x) pure nothrow
|
||||
{
|
||||
return (x < edge) ? 0 : 1;
|
||||
}
|
||||
|
||||
/// Same as GLSL smoothstep function.
|
||||
/// See: http://en.wikipedia.org/wiki/Smoothstep
|
||||
@nogc T smoothStep(T)(T a, T b, T t) pure nothrow
|
||||
{
|
||||
if (t <= a)
|
||||
return 0;
|
||||
else if (t >= b)
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
T x = (t - a) / (b - a);
|
||||
return x * x * (3 - 2 * x);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns: true of i is a power of 2.
|
||||
@nogc bool isPowerOf2(T)(T i) pure nothrow if (isIntegral!T)
|
||||
{
|
||||
assert(i >= 0);
|
||||
return (i != 0) && ((i & (i - 1)) == 0);
|
||||
}
|
||||
|
||||
/// Integer log2
|
||||
@nogc int ilog2(T)(T i) nothrow if (isIntegral!T)
|
||||
{
|
||||
assert(i > 0);
|
||||
assert(isPowerOf2(i));
|
||||
import core.bitop : bsr;
|
||||
return bsr(i);
|
||||
}
|
||||
|
||||
/// Computes next power of 2.
|
||||
@nogc int nextPowerOf2(int i) pure nothrow
|
||||
{
|
||||
int v = i - 1;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
assert(isPowerOf2(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Computes next power of 2.
|
||||
@nogc long nextPowerOf2(long i) pure nothrow
|
||||
{
|
||||
long v = i - 1;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v |= v >> 32;
|
||||
v++;
|
||||
assert(isPowerOf2(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Computes sin(x)/x accurately.
|
||||
/// See_also: $(WEB www.plunk.org/~hatch/rightway.php)
|
||||
@nogc T sinOverX(T)(T x) pure nothrow
|
||||
{
|
||||
if (1 + x * x == 1)
|
||||
return 1;
|
||||
else
|
||||
return sin(x) / x;
|
||||
}
|
||||
|
||||
|
||||
/// Signed integer modulo a/b where the remainder is guaranteed to be in [0..b[,
|
||||
/// even if a is negative. Only support positive dividers.
|
||||
@nogc T moduloWrap(T)(T a, T b) pure nothrow if (isSigned!T)
|
||||
{
|
||||
assert(b > 0);
|
||||
if (a >= 0)
|
||||
a = a % b;
|
||||
else
|
||||
{
|
||||
auto rem = a % b;
|
||||
x = (rem == 0) ? 0 : (-rem + b);
|
||||
}
|
||||
|
||||
assert(x >= 0 && x < b);
|
||||
return x;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert(nextPowerOf2(13) == 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the root of a linear polynomial a + b x = 0
|
||||
* Returns: Number of roots.
|
||||
*/
|
||||
@nogc int solveLinear(T)(T a, T b, out T root) pure nothrow if (isFloatingPoint!T)
|
||||
{
|
||||
if (b == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
root = -a / b;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the root roots of a quadratic polynomial a + b x + c x^2 = 0
|
||||
* Params:
|
||||
* a = Coefficient.
|
||||
* b = Coefficient.
|
||||
* c = Coefficient.
|
||||
* outRoots = array of root results, should have room for at least 2 elements.
|
||||
* Returns: Number of roots in outRoots.
|
||||
*/
|
||||
@nogc int solveQuadratic(T)(T a, T b, T c, T[] outRoots) pure nothrow if (isFloatingPoint!T)
|
||||
{
|
||||
assert(outRoots.length >= 2);
|
||||
if (c == 0)
|
||||
return solveLinear(a, b, outRoots[0]);
|
||||
|
||||
T delta = b * b - 4 * a * c;
|
||||
if (delta < 0.0 )
|
||||
return 0;
|
||||
|
||||
delta = sqrt(delta);
|
||||
T oneOver2a = 0.5 / a;
|
||||
|
||||
outRoots[0] = oneOver2a * (-b - delta);
|
||||
outRoots[1] = oneOver2a * (-b + delta);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the roots of a cubic polynomial a + b x + c x^2 + d x^3 = 0
|
||||
* Params:
|
||||
* a = Coefficient.
|
||||
* b = Coefficient.
|
||||
* c = Coefficient.
|
||||
* d = Coefficient.
|
||||
* outRoots = array of root results, should have room for at least 2 elements.
|
||||
* Returns: Number of roots in outRoots.
|
||||
* See_also: $(WEB www.codeguru.com/forum/archive/index.php/t-265551.html)
|
||||
*/
|
||||
@nogc int solveCubic(T)(T a, T b, T c, T d, T[] outRoots) pure nothrow if (isFloatingPoint!T)
|
||||
{
|
||||
assert(outRoots.length >= 3);
|
||||
if (d == 0)
|
||||
return solveQuadratic(a, b, c, outRoots);
|
||||
|
||||
// adjust coefficients
|
||||
T a1 = c / d,
|
||||
a2 = b / d,
|
||||
a3 = a / d;
|
||||
|
||||
T Q = (a1 * a1 - 3 * a2) / 9,
|
||||
R = (2 * a1 * a1 * a1 - 9 * a1 * a2 + 27 * a3) / 54;
|
||||
|
||||
T Qcubed = Q * Q * Q;
|
||||
T d2 = Qcubed - R * R;
|
||||
|
||||
if (d2 >= 0)
|
||||
{
|
||||
// 3 real roots
|
||||
if (Q < 0.0)
|
||||
return 0;
|
||||
T P = R / sqrt(Qcubed);
|
||||
|
||||
assert(-1 <= P && P <= 1);
|
||||
T theta = acos(P);
|
||||
T sqrtQ = sqrt(Q);
|
||||
|
||||
outRoots[0] = -2 * sqrtQ * cos(theta / 3) - a1 / 3;
|
||||
outRoots[1] = -2 * sqrtQ * cos((theta + 2 * T(PI)) / 3) - a1 / 3;
|
||||
outRoots[2] = -2 * sqrtQ * cos((theta + 4 * T(PI)) / 3) - a1 / 3;
|
||||
return 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1 real root
|
||||
T e = (sqrt(-d) + abs(R)) ^^ cast(T)(1.0 / 3.0);
|
||||
if (R > 0)
|
||||
e = -e;
|
||||
outRoots[0] = e + Q / e - a1 / 3.0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roots of a quartic polynomial a + b x + c x^2 + d x^3 + e x^4 = 0
|
||||
*
|
||||
* Returns number of roots. roots slice should have room for up to 4 elements.
|
||||
* Bugs: doesn't pass unit-test!
|
||||
* See_also: $(WEB mathworld.wolfram.com/QuarticEquation.html)
|
||||
*/
|
||||
@nogc int solveQuartic(T)(T a, T b, T c, T d, T e, T[] roots) pure nothrow if (isFloatingPoint!T)
|
||||
{
|
||||
assert(roots.length >= 4);
|
||||
|
||||
if (e == 0)
|
||||
return solveCubic(a, b, c, d, roots);
|
||||
|
||||
// Adjust coefficients
|
||||
T a0 = a / e,
|
||||
a1 = b / e,
|
||||
a2 = c / e,
|
||||
a3 = d / e;
|
||||
|
||||
// Find a root for the following cubic equation:
|
||||
// y^3 - a2 y^2 + (a1 a3 - 4 a0) y + (4 a2 a0 - a1 ^2 - a3^2 a0) = 0
|
||||
// aka Resolvent cubic
|
||||
T b0 = 4 * a2 * a0 - a1 * a1 - a3 * a3 * a0;
|
||||
T b1 = a1 * a3 - 4 * a0;
|
||||
T b2 = -a2;
|
||||
T[3] resolventCubicRoots;
|
||||
int numRoots = solveCubic!T(b0, b1, b2, 1, resolventCubicRoots[]);
|
||||
assert(numRoots == 3);
|
||||
T y = resolventCubicRoots[0];
|
||||
if (y < resolventCubicRoots[1]) y = resolventCubicRoots[1];
|
||||
if (y < resolventCubicRoots[2]) y = resolventCubicRoots[2];
|
||||
|
||||
// Compute R, D & E
|
||||
T R = 0.25f * a3 * a3 - a2 + y;
|
||||
if (R < 0.0)
|
||||
return 0;
|
||||
R = sqrt(R);
|
||||
|
||||
T D = void,
|
||||
E = void;
|
||||
if (R == 0)
|
||||
{
|
||||
T d1 = 0.75f * a3 * a3 - 2 * a2;
|
||||
T d2 = 2 * sqrt(y * y - 4 * a0);
|
||||
D = sqrt(d1 + d2) * 0.5f;
|
||||
E = sqrt(d1 - d2) * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
T Rsquare = R * R;
|
||||
T Rrec = 1 / R;
|
||||
T d1 = 0.75f * a3 * a3 - Rsquare - 2 * a2;
|
||||
T d2 = 0.25f * Rrec * (4 * a3 * a2 - 8 * a1 - a3 * a3 * a3);
|
||||
D = sqrt(d1 + d2) * 0.5f;
|
||||
E = sqrt(d1 - d2) * 0.5f;
|
||||
}
|
||||
|
||||
// Compute the 4 roots
|
||||
a3 *= -0.25f;
|
||||
R *= 0.5f;
|
||||
|
||||
roots[0] = a3 + R + D;
|
||||
roots[1] = a3 + R - D;
|
||||
roots[2] = a3 - R + E;
|
||||
roots[3] = a3 - R - E;
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
unittest
|
||||
{
|
||||
bool arrayContainsRoot(double[] arr, double root)
|
||||
{
|
||||
foreach(e; arr)
|
||||
if (abs(e - root) < 1e-7)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// test quadratic
|
||||
{
|
||||
double[3] roots;
|
||||
int numRoots = solveCubic!double(-2, -3 / 2.0, 3 / 4.0, 1 / 4.0, roots[]);
|
||||
assert(numRoots == 3);
|
||||
assert(arrayContainsRoot(roots[], -4));
|
||||
assert(arrayContainsRoot(roots[], -1));
|
||||
assert(arrayContainsRoot(roots[], 2));
|
||||
}
|
||||
|
||||
// test quartic
|
||||
{
|
||||
double[4] roots;
|
||||
int numRoots = solveQuartic!double(0, -2, -1, 2, 1, roots[]);
|
||||
|
||||
assert(numRoots == 4);
|
||||
assert(arrayContainsRoot(roots[], -2));
|
||||
assert(arrayContainsRoot(roots[], -1));
|
||||
assert(arrayContainsRoot(roots[], 0));
|
||||
assert(arrayContainsRoot(roots[], 1));
|
||||
}
|
||||
}
|
||||
|
||||
/// Arithmetic mean.
|
||||
double average(R)(R r) if (isInputRange!R)
|
||||
{
|
||||
if (r.empty)
|
||||
return double.nan;
|
||||
|
||||
typeof(r.front()) sum = 0;
|
||||
long count = 0;
|
||||
foreach(e; r)
|
||||
{
|
||||
sum += e;
|
||||
++count;
|
||||
}
|
||||
return sum / count;
|
||||
}
|
||||
|
||||
/// Minimum of a range.
|
||||
double minElement(R)(R r) if (isInputRange!R)
|
||||
{
|
||||
// do like Javascript for an empty range
|
||||
if (r.empty)
|
||||
return double.infinity;
|
||||
|
||||
return minmax!("<", R)(r);
|
||||
}
|
||||
|
||||
/// Maximum of a range.
|
||||
double maxElement(R)(R r) if (isInputRange!R)
|
||||
{
|
||||
// do like Javascript for an empty range
|
||||
if (r.empty)
|
||||
return -double.infinity;
|
||||
|
||||
return minmax!(">", R)(r);
|
||||
}
|
||||
|
||||
/// Variance of a range.
|
||||
double variance(R)(R r) if (isForwardRange!R)
|
||||
{
|
||||
if (r.empty)
|
||||
return double.nan;
|
||||
|
||||
auto avg = average(r.save); // getting the average
|
||||
|
||||
typeof(avg) sum = 0;
|
||||
long count = 0;
|
||||
foreach(e; r)
|
||||
{
|
||||
sum += (e - avg) ^^ 2;
|
||||
++count;
|
||||
}
|
||||
if (count <= 1)
|
||||
return 0.0;
|
||||
else
|
||||
return (sum / (count - 1.0)); // using sample std deviation as estimator
|
||||
}
|
||||
|
||||
/// Standard deviation of a range.
|
||||
double standardDeviation(R)(R r) if (isForwardRange!R)
|
||||
{
|
||||
return sqrt(variance(r));
|
||||
}
|
||||
|
||||
private
|
||||
{
|
||||
typeof(R.front()) minmax(string op, R)(R r) if (isInputRange!R)
|
||||
{
|
||||
assert(!r.empty);
|
||||
auto best = r.front();
|
||||
r.popFront();
|
||||
foreach(e; r)
|
||||
{
|
||||
mixin("if (e " ~ op ~ " best) best = e;");
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
/// SSE approximation of reciprocal square root.
|
||||
@nogc T inverseSqrt(T)(T x) pure nothrow if (isFloatingPoint!T)
|
||||
{
|
||||
static if (is(T == float))
|
||||
{
|
||||
__m128 V = _mm_set_ss(x);
|
||||
V = _mm_rsqrt_ss(V);
|
||||
return _mm_cvtss_f32(V);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1 / sqrt(x);
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert(abs( inverseSqrt!float(1) - 1) < 1e-3 );
|
||||
assert(abs( inverseSqrt!double(1) - 1) < 1e-3 );
|
||||
}
|
||||
@ -1,21 +1,16 @@
|
||||
/**
|
||||
* Custom sized 2D Matrices.
|
||||
*
|
||||
* Copyright: Copyright Guillaume Piolat 2015-2021.
|
||||
* Copyright Aleksandr Druzhinin 2016-2020.
|
||||
* Copyright Nathan Sashihara 2018.
|
||||
* Copyright Thibaut Charles 2018.
|
||||
*
|
||||
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
||||
*/
|
||||
module dplug.math.matrix;
|
||||
/// Custom sized 2-dimension Matrices
|
||||
module gfm.math.matrix;
|
||||
|
||||
import std.math,
|
||||
std.typetuple,
|
||||
std.traits,
|
||||
std.typecons;
|
||||
std.string,
|
||||
std.typecons,
|
||||
std.conv;
|
||||
|
||||
import dplug.math.vector;
|
||||
import gfm.math.vector,
|
||||
gfm.math.shapes,
|
||||
gfm.math.quaternion;
|
||||
|
||||
/// Generic non-resizeable matrix with R rows and C columns.
|
||||
/// Intended for 3D use (size 3x3 and 4x4).
|
||||
@ -187,6 +182,15 @@ struct Matrix(T, int R, int C)
|
||||
return rows[i];
|
||||
}
|
||||
|
||||
/// Covnerts to pretty string.
|
||||
string toString() const nothrow
|
||||
{
|
||||
try
|
||||
return format("%s", v);
|
||||
catch (Exception e)
|
||||
assert(false); // should not happen since format is right
|
||||
}
|
||||
|
||||
/// Matrix * scalar multiplication.
|
||||
@nogc Matrix opBinary(string op)(T factor) pure const nothrow if (op == "*")
|
||||
{
|
||||
@ -318,6 +322,85 @@ struct Matrix(T, int R, int C)
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Convert 3x3 rotation matrix to quaternion.
|
||||
/// See_also: 3D Math Primer for Graphics and Game Development.
|
||||
@nogc U opCast(U)() pure const nothrow if (isQuaternionInstantiation!U
|
||||
&& is(U._T : _T)
|
||||
&& (_R == 3) && (_C == 3))
|
||||
{
|
||||
T fourXSquaredMinus1 = c[0][0] - c[1][1] - c[2][2];
|
||||
T fourYSquaredMinus1 = c[1][1] - c[0][0] - c[2][2];
|
||||
T fourZSquaredMinus1 = c[2][2] - c[0][0] - c[1][1];
|
||||
T fourWSquaredMinus1 = c[0][0] + c[1][1] + c[2][2];
|
||||
|
||||
int biggestIndex = 0;
|
||||
T fourBiggestSquaredMinus1 = fourWSquaredMinus1;
|
||||
|
||||
if(fourXSquaredMinus1 > fourBiggestSquaredMinus1)
|
||||
{
|
||||
fourBiggestSquaredMinus1 = fourXSquaredMinus1;
|
||||
biggestIndex = 1;
|
||||
}
|
||||
|
||||
if(fourYSquaredMinus1 > fourBiggestSquaredMinus1)
|
||||
{
|
||||
fourBiggestSquaredMinus1 = fourYSquaredMinus1;
|
||||
biggestIndex = 2;
|
||||
}
|
||||
|
||||
if(fourZSquaredMinus1 > fourBiggestSquaredMinus1)
|
||||
{
|
||||
fourBiggestSquaredMinus1 = fourZSquaredMinus1;
|
||||
biggestIndex = 3;
|
||||
}
|
||||
|
||||
T biggestVal = sqrt(fourBiggestSquaredMinus1 + 1) / 2;
|
||||
T mult = 1 / (biggestVal * 4);
|
||||
|
||||
U quat;
|
||||
switch(biggestIndex)
|
||||
{
|
||||
case 1:
|
||||
quat.w = (c[1][2] - c[2][1]) * mult;
|
||||
quat.x = biggestVal;
|
||||
quat.y = (c[0][1] + c[1][0]) * mult;
|
||||
quat.z = (c[2][0] + c[0][2]) * mult;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
quat.w = (c[2][0] - c[0][2]) * mult;
|
||||
quat.x = (c[0][1] + c[1][0]) * mult;
|
||||
quat.y = biggestVal;
|
||||
quat.z = (c[1][2] + c[2][1]) * mult;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
quat.w = (c[0][1] - c[1][0]) * mult;
|
||||
quat.x = (c[2][0] + c[0][2]) * mult;
|
||||
quat.y = (c[1][2] + c[2][1]) * mult;
|
||||
quat.z = biggestVal;
|
||||
break;
|
||||
|
||||
default: // biggestIndex == 0
|
||||
quat.w = biggestVal;
|
||||
quat.x = (c[1][2] - c[2][1]) * mult;
|
||||
quat.y = (c[2][0] - c[0][2]) * mult;
|
||||
quat.z = (c[0][1] - c[1][0]) * mult;
|
||||
break;
|
||||
}
|
||||
|
||||
return quat;
|
||||
}
|
||||
|
||||
/// Converts a 4x4 rotation matrix to quaternion.
|
||||
@nogc U opCast(U)() pure const nothrow if (isQuaternionInstantiation!U
|
||||
&& is(U._T : _T)
|
||||
&& (_R == 4) && (_C == 4))
|
||||
{
|
||||
auto m3 = cast(mat3!T)(this);
|
||||
return cast(U)(m3);
|
||||
}
|
||||
|
||||
static if (isSquare && isFloatingPoint!T && R == 1)
|
||||
{
|
||||
/// Returns an inverted copy of this matrix
|
||||
@ -614,6 +697,19 @@ struct Matrix(T, int R, int C)
|
||||
Z.x, Z.y, Z.z, -dot(Z, eye),
|
||||
0, 0, 0, 1);
|
||||
}
|
||||
|
||||
/// Extract frustum from a 4x4 matrice.
|
||||
@nogc Frustum!T frustum() pure const nothrow
|
||||
{
|
||||
auto left = Plane!T(row(3) + row(0));
|
||||
auto right = Plane!T(row(3) - row(0));
|
||||
auto top = Plane!T(row(3) - row(1));
|
||||
auto bottom = Plane!T(row(3) + row(1));
|
||||
auto near = Plane!T(row(3) + row(2));
|
||||
auto far = Plane!T(row(3) - row(2));
|
||||
return Frustum!T(left, right, top, bottom, near, far);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
9
external/gfm/math/package.d
vendored
Normal file
9
external/gfm/math/package.d
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
module gfm.math;
|
||||
|
||||
public import gfm.math.funcs,
|
||||
gfm.math.vector,
|
||||
gfm.math.box,
|
||||
gfm.math.matrix,
|
||||
gfm.math.quaternion,
|
||||
gfm.math.shapes,
|
||||
gfm.math.simplerng;
|
||||
339
external/gfm/math/quaternion.d
vendored
Normal file
339
external/gfm/math/quaternion.d
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
///
|
||||
module gfm.math.quaternion;
|
||||
|
||||
import std.math,
|
||||
std.string;
|
||||
|
||||
import gfm.math.vector,
|
||||
gfm.math.matrix,
|
||||
funcs = gfm.math.funcs;
|
||||
|
||||
/// Quaternion implementation.
|
||||
/// Holds a rotation + angle in a proper but wild space.
|
||||
struct Quaternion(T)
|
||||
{
|
||||
public
|
||||
{
|
||||
union
|
||||
{
|
||||
Vector!(T, 4u) v;
|
||||
struct
|
||||
{
|
||||
T x, y, z, w;
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a Quaternion from a value.
|
||||
@nogc this(U)(U x) pure nothrow if (isAssignable!U)
|
||||
{
|
||||
opAssign!U(x);
|
||||
}
|
||||
|
||||
/// Constructs a Quaternion from coordinates.
|
||||
/// Warning: order of coordinates is different from storage.
|
||||
@nogc this(T qw, T qx, T qy, T qz) pure nothrow
|
||||
{
|
||||
x = qx;
|
||||
y = qy;
|
||||
z = qz;
|
||||
w = qw;
|
||||
}
|
||||
|
||||
/// Constructs a Quaternion from axis + angle.
|
||||
@nogc static Quaternion fromAxis(Vector!(T, 3) axis, T angle) pure nothrow
|
||||
{
|
||||
Quaternion q = void;
|
||||
axis.normalize();
|
||||
T cos_a = cos(angle / 2);
|
||||
T sin_a = sin(angle / 2);
|
||||
q.x = sin_a * axis.x;
|
||||
q.y = sin_a * axis.y;
|
||||
q.z = sin_a * axis.z;
|
||||
q.w = cos_a;
|
||||
return q; // should be normalized
|
||||
}
|
||||
|
||||
/// Constructs a Quaternion from Euler angles.
|
||||
/// All paramers given in radians.
|
||||
/// Roll->X axis, Pitch->Y axis, Yaw->Z axis
|
||||
/// See_also: $(LINK https://www.cs.princeton.edu/~gewang/projects/darth/stuff/quat_faq.html)
|
||||
@nogc static Quaternion fromEulerAngles(T roll, T pitch, T yaw) pure nothrow
|
||||
{
|
||||
Quaternion q = void;
|
||||
T sinPitch = sin(pitch / 2);
|
||||
T cosPitch = cos(pitch / 2);
|
||||
T sinYaw = sin(yaw / 2);
|
||||
T cosYaw = cos(yaw / 2);
|
||||
T sinRoll = sin(roll / 2);
|
||||
T cosRoll = cos(roll / 2);
|
||||
T cosPitchCosYaw = cosPitch * cosYaw;
|
||||
T sinPitchSinYaw = sinPitch * sinYaw;
|
||||
q.x = sinRoll * cosPitchCosYaw - cosRoll * sinPitchSinYaw;
|
||||
q.y = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw;
|
||||
q.z = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw;
|
||||
q.w = cosRoll * cosPitchCosYaw + sinRoll * sinPitchSinYaw;
|
||||
return q;
|
||||
}
|
||||
|
||||
/// Converts a quaternion to Euler angles.
|
||||
/// TODO: adds a EulerAngles type.
|
||||
/// Returns: A vector which contains roll-pitch-yaw as x-y-z.
|
||||
@nogc vec3!T toEulerAngles() pure const nothrow
|
||||
{
|
||||
mat3!T m = cast(mat3!T)(this);
|
||||
|
||||
T pitch, yaw, roll;
|
||||
T s = sqrt(m.c[0][0] * m.c[0][0] + m.c[1][0] * m.c[1][0]);
|
||||
if (s > T.epsilon)
|
||||
{
|
||||
pitch = - atan2(m.c[2][0], s);
|
||||
yaw = atan2(m.c[1][0], m.c[0][0]);
|
||||
roll = atan2(m.c[2][1], m.c[2][2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
pitch = m.c[2][0] < 0.0f ? T(PI) /2 : -T(PI) / 2;
|
||||
yaw = -atan2(m.c[0][1], m.c[1][1]);
|
||||
roll = 0.0f;
|
||||
}
|
||||
return vec3!T(roll, pitch, yaw);
|
||||
}
|
||||
|
||||
/// Assign from another Quaternion.
|
||||
@nogc ref Quaternion opAssign(U)(U u) pure nothrow if (isQuaternionInstantiation!U && is(U._T : T))
|
||||
{
|
||||
v = u.v;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Assign from a vector of 4 elements.
|
||||
@nogc ref Quaternion opAssign(U)(U u) pure nothrow if (is(U : Vector!(T, 4u)))
|
||||
{
|
||||
v = u;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Converts to a pretty string.
|
||||
string toString() const nothrow
|
||||
{
|
||||
try
|
||||
return format("%s", v);
|
||||
catch (Exception e)
|
||||
assert(false); // should not happen since format is right
|
||||
}
|
||||
|
||||
/// Normalizes a quaternion.
|
||||
@nogc void normalize() pure nothrow
|
||||
{
|
||||
v.normalize();
|
||||
}
|
||||
|
||||
/// Returns: Normalized quaternion.
|
||||
@nogc Quaternion normalized() pure const nothrow
|
||||
{
|
||||
Quaternion res = void;
|
||||
res.v = v.normalized();
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Inverses a quaternion in-place.
|
||||
@nogc void inverse() pure nothrow
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
/// Returns: Inverse of quaternion.
|
||||
@nogc Quaternion inversed() pure const nothrow
|
||||
{
|
||||
Quaternion res = void;
|
||||
res.v = v;
|
||||
res.inverse();
|
||||
return res;
|
||||
}
|
||||
|
||||
@nogc ref Quaternion opOpAssign(string op, U)(U q) pure nothrow
|
||||
if (is(U : Quaternion) && (op == "*"))
|
||||
{
|
||||
T nx = w * q.x + x * q.w + y * q.z - z * q.y,
|
||||
ny = w * q.y + y * q.w + z * q.x - x * q.z,
|
||||
nz = w * q.z + z * q.w + x * q.y - y * q.x,
|
||||
nw = w * q.w - x * q.x - y * q.y - z * q.z;
|
||||
x = nx;
|
||||
y = ny;
|
||||
z = nz;
|
||||
w = nw;
|
||||
return this;
|
||||
}
|
||||
|
||||
@nogc ref Quaternion opOpAssign(string op, U)(U operand) pure nothrow if (isConvertible!U)
|
||||
{
|
||||
Quaternion conv = operand;
|
||||
return opOpAssign!op(conv);
|
||||
}
|
||||
|
||||
@nogc Quaternion opBinary(string op, U)(U operand) pure const nothrow
|
||||
if (is(U: Quaternion) || (isConvertible!U))
|
||||
{
|
||||
Quaternion temp = this;
|
||||
return temp.opOpAssign!op(operand);
|
||||
}
|
||||
|
||||
/// Compare two Quaternions.
|
||||
bool opEquals(U)(U other) pure const if (is(U : Quaternion))
|
||||
{
|
||||
return v == other.v;
|
||||
}
|
||||
|
||||
/// Compare Quaternion and other types.
|
||||
bool opEquals(U)(U other) pure const nothrow if (isConvertible!U)
|
||||
{
|
||||
Quaternion conv = other;
|
||||
return opEquals(conv);
|
||||
}
|
||||
|
||||
/// Convert to a 3x3 rotation matrix.
|
||||
/// TODO: check out why we can't do is(Unqual!U == mat3!T)
|
||||
@nogc U opCast(U)() pure const nothrow if (isMatrixInstantiation!U
|
||||
&& is(U._T : _T)
|
||||
&& (U._R == 3) && (U._C == 3))
|
||||
{
|
||||
// do not assume that quaternion is normalized
|
||||
T norm = x*x + y*y + z*z + w*w;
|
||||
T s = (norm > 0) ? 2 / norm : 0;
|
||||
|
||||
T xx = x * x * s, xy = x * y * s, xz = x * z * s, xw = x * w * s,
|
||||
yy = y * y * s, yz = y * z * s, yw = y * w * s,
|
||||
zz = z * z * s, zw = z * w * s;
|
||||
return mat3!(U._T)
|
||||
(
|
||||
1 - (yy + zz) , (xy - zw) , (xz + yw) ,
|
||||
(xy + zw) , 1 - (xx + zz) , (yz - xw) ,
|
||||
(xz - yw) , (yz + xw) , 1 - (xx + yy)
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts a to a 4x4 rotation matrix.
|
||||
/// Bugs: check why we can't do is(Unqual!U == mat4!T)
|
||||
@nogc U opCast(U)() pure const nothrow if (isMatrixInstantiation!U
|
||||
&& is(U._T : _T)
|
||||
&& (U._R == 4) && (U._C == 4))
|
||||
{
|
||||
auto m3 = cast(mat3!(U._T))(this);
|
||||
return cast(U)(m3);
|
||||
}
|
||||
|
||||
/// Workaround Vector not being constructable through CTFE
|
||||
@nogc static Quaternion identity() pure nothrow @property
|
||||
{
|
||||
Quaternion q;
|
||||
q.x = q.y = q.z = 0;
|
||||
q.w = 1;
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
private
|
||||
{
|
||||
alias T _T;
|
||||
|
||||
template isAssignable(T)
|
||||
{
|
||||
enum bool isAssignable =
|
||||
is(typeof(
|
||||
{
|
||||
T x;
|
||||
Quaternion v = x;
|
||||
}()));
|
||||
}
|
||||
|
||||
// define types that can be converted to Quaternion, but are not Quaternion
|
||||
template isConvertible(T)
|
||||
{
|
||||
enum bool isConvertible = (!is(T : Quaternion)) && isAssignable!T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template isQuaternionInstantiation(U)
|
||||
{
|
||||
private static void isQuaternion(T)(Quaternion!T x)
|
||||
{
|
||||
}
|
||||
|
||||
enum bool isQuaternionInstantiation = is(typeof(isQuaternion(U.init)));
|
||||
}
|
||||
|
||||
alias Quaternion!float quatf;///
|
||||
alias Quaternion!double quatd;///
|
||||
|
||||
/// Linear interpolation, for quaternions.
|
||||
@nogc Quaternion!T lerp(T)(Quaternion!T a, Quaternion!T b, float t) pure nothrow
|
||||
{
|
||||
Quaternion!T res = void;
|
||||
res.v = funcs.lerp(a.v, b.v, t);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Nlerp of quaternions
|
||||
/// Returns: Nlerp of quaternions.
|
||||
/// See_also: $(WEB keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/, Math Magician – Lerp, Slerp, and Nlerp)
|
||||
@nogc Quaternion!T Nlerp(T)(Quaternion!T a, Quaternion!T b, float t) pure nothrow
|
||||
{
|
||||
assert(t >= 0 && t <= 1); // else probably doesn't make sense
|
||||
Quaternion!T res = void;
|
||||
res.v = funcs.lerp(a.v, b.v, t);
|
||||
res.v.normalize();
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Slerp of quaternions
|
||||
/// Returns: Slerp of quaternions. Slerp is more expensive than Nlerp.
|
||||
/// See_also: "Understanding Slerp, Then Not Using It"
|
||||
@nogc Quaternion!T slerp(T)(Quaternion!T a, Quaternion!T b, T t) pure nothrow
|
||||
{
|
||||
assert(t >= 0 && t <= 1); // else probably doesn't make sense
|
||||
|
||||
Quaternion!T res = void;
|
||||
|
||||
T dotProduct = dot(a.v, b.v);
|
||||
|
||||
// spherical lerp always has 2 potential paths
|
||||
// here we always take the shortest
|
||||
if (dotProduct < 0)
|
||||
{
|
||||
b.v *= -1;
|
||||
dotProduct = dot(a.v, b.v);
|
||||
}
|
||||
|
||||
immutable T threshold = 10 * T.epsilon; // idMath uses 1e-6f for 32-bits float precision
|
||||
if ((1 - dotProduct) > threshold) // if small difference, use lerp
|
||||
return lerp(a, b, t);
|
||||
|
||||
T theta_0 = funcs.safeAcos(dotProduct); // angle between this and other
|
||||
T theta = theta_0 * t; // angle between this and result
|
||||
|
||||
vec3!T v2 = dot(b.v, a.v * dotProduct);
|
||||
v2.normalize();
|
||||
|
||||
res.v = dot(b.v, a.v * dotProduct);
|
||||
res.v.normalize();
|
||||
|
||||
res.v = a.v * cos(theta) + res.v * sin(theta);
|
||||
return res;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
quatf a = quatf.fromAxis(vec3f(1, 0, 0), 1);
|
||||
quatf b = quatf.fromAxis(vec3f(0, 1, 0), 0);
|
||||
a = a * b;
|
||||
|
||||
quatf c = lerp(a, b, 0.5f);
|
||||
quatf d = Nlerp(a, b, 0.1f);
|
||||
quatf e = slerp(a, b, 0.0f);
|
||||
quatd f = quatd(1.0, 4, 5.0, 6.0);
|
||||
quatf g = quatf.fromEulerAngles(-0.1f, 1.2f, -0.3f);
|
||||
vec3f ga = g.toEulerAngles();
|
||||
}
|
||||
645
external/gfm/math/shapes.d
vendored
Normal file
645
external/gfm/math/shapes.d
vendored
Normal file
@ -0,0 +1,645 @@
|
||||
/**
|
||||
This module implements some abstract geometric shapes:
|
||||
$(UL
|
||||
$(LI Line segments.)
|
||||
$(LI Triangle.)
|
||||
$(LI Circles/spheres.)
|
||||
$(LI Rays)
|
||||
$(LI Planes)
|
||||
$(LI Frustum)
|
||||
)
|
||||
*/
|
||||
module gfm.math.shapes;
|
||||
|
||||
import std.math,
|
||||
std.traits;
|
||||
|
||||
import gfm.math.vector,
|
||||
gfm.math.box;
|
||||
|
||||
/// A Segment is 2 points.
|
||||
/// When considered like a vector, it represents the arrow from a to b.
|
||||
struct Segment(T, int N)
|
||||
{
|
||||
public
|
||||
{
|
||||
alias Vector!(T, N) point_t;
|
||||
point_t a, b;
|
||||
|
||||
static if (N == 3 && isFloatingPoint!T)
|
||||
{
|
||||
/// Segment vs plane intersection.
|
||||
/// See_also: "Geometry for Computer Graphics: Formulae, Examples
|
||||
/// and Proofs", Vince (2005), p. 62
|
||||
/// Returns: $(D true) if the segment crosses the plane exactly
|
||||
/// once, or $(D false) if the segment is parallel to or does
|
||||
/// not reach the plane
|
||||
/// Params:
|
||||
/// plane = The plane to intersect.
|
||||
/// intersection = Point of intersection.
|
||||
/// progress = Set to the point's progress between endpoints
|
||||
/// $(D a) at 0.0 and $(D b) at 1.0, or T.infinity
|
||||
/// if parallel to the plane
|
||||
@nogc bool intersect(Plane!T plane, out point_t intersection, out T progress) pure const nothrow
|
||||
{
|
||||
// direction vector from a to b
|
||||
point_t dir = b-a;
|
||||
|
||||
// dot product will be 0 if angle to plane is 0
|
||||
T dp = dot(plane.n, dir);
|
||||
if (abs(dp) < T.epsilon)
|
||||
{
|
||||
progress = T.infinity;
|
||||
return false; // parallel to plane
|
||||
}
|
||||
|
||||
// relative distance along segment where intersection happens
|
||||
progress = -(dot(plane.n, a) - plane.d) / dp;
|
||||
|
||||
intersection = progress*dir + a;
|
||||
return progress >= 0 && progress <= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias Segment!(float, 2) seg2f; /// 2D float segment.
|
||||
alias Segment!(float, 3) seg3f; /// 3D float segment.
|
||||
alias Segment!(double, 2) seg2d; /// 2D double segment.
|
||||
alias Segment!(double, 3) seg3d; /// 3D double segment.
|
||||
alias Segment!(int, 2) seg2i; /// 2D integer segment.
|
||||
alias Segment!(int, 3) seg3i; /// 3D integer segment.
|
||||
|
||||
/// A Triangle is 3 points.
|
||||
struct Triangle(T, int N)
|
||||
{
|
||||
public
|
||||
{
|
||||
alias Vector!(T, N) point_t;
|
||||
point_t a, b, c;
|
||||
|
||||
static if (N == 2)
|
||||
{
|
||||
/// Returns: Area of a 2D triangle.
|
||||
@nogc T area() pure const nothrow
|
||||
{
|
||||
return abs(signedArea());
|
||||
}
|
||||
|
||||
/// Returns: Signed area of a 2D triangle.
|
||||
@nogc T signedArea() pure const nothrow
|
||||
{
|
||||
return ((b.x * a.y - a.x * b.y)
|
||||
+ (c.x * b.y - b.x * c.y)
|
||||
+ (a.x * c.y - c.x * a.y)) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static if (N == 3 && isFloatingPoint!T)
|
||||
{
|
||||
/// Returns: Triangle normal.
|
||||
@nogc Vector!(T, 3) computeNormal() pure const nothrow
|
||||
{
|
||||
return cross(b - a, c - a).normalized();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias Triangle!(float, 2) triangle2f; /// 2D float triangle.
|
||||
alias Triangle!(float, 3) triangle3f; /// 3D float triangle.
|
||||
alias Triangle!(double, 2) triangle2d; /// 2D double triangle.
|
||||
alias Triangle!(double, 3) triangle3d; /// 3D double triangle.
|
||||
|
||||
/// A Sphere is a point + a radius.
|
||||
struct Sphere(T, int N)
|
||||
{
|
||||
public nothrow
|
||||
{
|
||||
alias Vector!(T, N) point_t;
|
||||
point_t center;
|
||||
T radius;
|
||||
|
||||
/// Creates a sphere from a point and a radius.
|
||||
@nogc this(in point_t center_, T radius_) pure nothrow
|
||||
{
|
||||
center = center_;
|
||||
radius = radius_;
|
||||
}
|
||||
|
||||
/// Sphere contains point test.
|
||||
/// Returns: true if the point is inside the sphere.
|
||||
@nogc bool contains(in Sphere s) pure const nothrow
|
||||
{
|
||||
if (s.radius > radius)
|
||||
return false;
|
||||
|
||||
T innerRadius = radius - s.radius;
|
||||
return squaredDistanceTo(s.center) < innerRadius * innerRadius;
|
||||
}
|
||||
|
||||
/// Sphere vs point Euclidean distance squared.
|
||||
@nogc T squaredDistanceTo(point_t p) pure const nothrow
|
||||
{
|
||||
return center.squaredDistanceTo(p);
|
||||
}
|
||||
|
||||
/// Sphere vs sphere intersection.
|
||||
/// Returns: true if the spheres intersect.
|
||||
@nogc bool intersects(Sphere s) pure const nothrow
|
||||
{
|
||||
T outerRadius = radius + s.radius;
|
||||
return squaredDistanceTo(s.center) < outerRadius * outerRadius;
|
||||
}
|
||||
|
||||
static if (isFloatingPoint!T)
|
||||
{
|
||||
/// Sphere vs point Euclidean distance.
|
||||
@nogc T distanceTo(point_t p) pure const nothrow
|
||||
{
|
||||
return center.distanceTo(p);
|
||||
}
|
||||
|
||||
static if(N == 2)
|
||||
{
|
||||
/// Returns: Circle area.
|
||||
@nogc T area() pure const nothrow
|
||||
{
|
||||
return T(PI) * (radius * radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias Sphere!(float, 2) sphere2f; /// 2D float sphere (ie. a circle).
|
||||
alias Sphere!(float, 3) sphere3f; /// 3D float sphere.
|
||||
alias Sphere!(double, 2) sphere2d; /// 2D double sphere (ie. a circle).
|
||||
alias Sphere!(double, 3) sphere3d; /// 3D double sphere (ie. a circle).
|
||||
|
||||
|
||||
/// A Ray ir a point + a direction.
|
||||
struct Ray(T, int N)
|
||||
{
|
||||
nothrow:
|
||||
public
|
||||
{
|
||||
alias Vector!(T, N) point_t;
|
||||
point_t orig;
|
||||
point_t dir;
|
||||
|
||||
/// Returns: A point further along the ray direction.
|
||||
@nogc point_t progress(T t) pure const nothrow
|
||||
{
|
||||
return orig + dir * t;
|
||||
}
|
||||
|
||||
static if (N == 3 && isFloatingPoint!T)
|
||||
{
|
||||
/// Ray vs triangle intersection.
|
||||
/// See_also: "Fast, Minimum Storage Ray/Triangle intersection", Mommer & Trumbore (1997)
|
||||
/// Returns: Barycentric coordinates, the intersection point is at $(D (1 - u - v) * A + u * B + v * C).
|
||||
@nogc bool intersect(Triangle!(T, 3) triangle, out T t, out T u, out T v) pure const nothrow
|
||||
{
|
||||
point_t edge1 = triangle.b - triangle.a;
|
||||
point_t edge2 = triangle.c - triangle.a;
|
||||
point_t pvec = cross(dir, edge2);
|
||||
T det = dot(edge1, pvec);
|
||||
if (abs(det) < T.epsilon)
|
||||
return false; // no intersection
|
||||
T invDet = 1 / det;
|
||||
|
||||
// calculate distance from triangle.a to ray origin
|
||||
point_t tvec = orig - triangle.a;
|
||||
|
||||
// calculate U parameter and test bounds
|
||||
u = dot(tvec, pvec) * invDet;
|
||||
if (u < 0 || u > 1)
|
||||
return false;
|
||||
|
||||
// prepare to test V parameter
|
||||
point_t qvec = cross(tvec, edge1);
|
||||
|
||||
// calculate V parameter and test bounds
|
||||
v = dot(dir, qvec) * invDet;
|
||||
if (v < 0.0 || u + v > 1.0)
|
||||
return false;
|
||||
|
||||
// calculate t, ray intersects triangle
|
||||
t = dot(edge2, qvec) * invDet;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Ray vs plane intersection.
|
||||
/// See_also: "Geometry for Computer Graphics: Formulae, Examples
|
||||
/// and Proofs", Vince (2005), p. 62
|
||||
/// Returns: $(D true) if the ray crosses the plane exactly once,
|
||||
/// or $(D false) if the ray is parallel to or points away from
|
||||
/// the plane
|
||||
/// Params:
|
||||
/// plane = Plane to intersect.
|
||||
/// intersection = Point of intersection.
|
||||
/// distance = set to the point's distance along the ray, or
|
||||
/// T.infinity if parallel to the plane
|
||||
@nogc bool intersect(Plane!T plane, out point_t intersection, out T distance) pure const nothrow
|
||||
{
|
||||
// dot product will be 0 if angle to plane is 0
|
||||
T dp = dot(plane.n, dir);
|
||||
if (abs(dp) < T.epsilon)
|
||||
{
|
||||
distance = T.infinity;
|
||||
return false; // parallel to plane
|
||||
}
|
||||
|
||||
// distance along ray where intersection happens
|
||||
distance = -(dot(plane.n, orig) - plane.d) / dp;
|
||||
|
||||
intersection = distance*dir + orig;
|
||||
return distance >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias Ray!(float, 2) ray2f; /// 2D float ray.
|
||||
alias Ray!(float, 3) ray3f; /// 3D float ray.
|
||||
alias Ray!(double, 2) ray2d; /// 2D double ray.
|
||||
alias Ray!(double, 3) ray3d; /// 3D double ray.
|
||||
|
||||
|
||||
/// 3D plane.
|
||||
/// See_also: Flipcode article by Nate Miller $(WEB www.flipcode.com/archives/Plane_Class.shtml).
|
||||
struct Plane(T) if (isFloatingPoint!T)
|
||||
{
|
||||
public
|
||||
{
|
||||
vec3!T n; /// Normal (always stored normalized).
|
||||
T d;
|
||||
|
||||
/// Create from four coordinates.
|
||||
@nogc this(vec4!T abcd) pure nothrow
|
||||
{
|
||||
n = vec3!T(abcd.x, abcd.y, abcd.z).normalized();
|
||||
d = abcd.w;
|
||||
}
|
||||
|
||||
/// Create from a point and a normal.
|
||||
@nogc this(vec3!T origin, vec3!T normal) pure nothrow
|
||||
{
|
||||
n = normal.normalized();
|
||||
d = -dot(origin, n);
|
||||
}
|
||||
|
||||
/// Create from 3 non-aligned points.
|
||||
@nogc this(vec3!T A, vec3!T B, vec3!T C) pure nothrow
|
||||
{
|
||||
this(C, cross(B - A, C - A));
|
||||
}
|
||||
|
||||
/// Assign a plane with another plane.
|
||||
@nogc ref Plane opAssign(Plane other) pure nothrow
|
||||
{
|
||||
n = other.n;
|
||||
d = other.d;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Returns: signed distance between a point and the plane.
|
||||
@nogc T signedDistanceTo(vec3!T point) pure const nothrow
|
||||
{
|
||||
return dot(n, point) + d;
|
||||
}
|
||||
|
||||
/// Returns: absolute distance between a point and the plane.
|
||||
@nogc T distanceTo(vec3!T point) pure const nothrow
|
||||
{
|
||||
return abs(signedDistanceTo(point));
|
||||
}
|
||||
|
||||
/// Returns: true if the point is in front of the plane.
|
||||
@nogc bool isFront(vec3!T point) pure const nothrow
|
||||
{
|
||||
return signedDistanceTo(point) >= 0;
|
||||
}
|
||||
|
||||
/// Returns: true if the point is in the back of the plane.
|
||||
@nogc bool isBack(vec3!T point) pure const nothrow
|
||||
{
|
||||
return signedDistanceTo(point) < 0;
|
||||
}
|
||||
|
||||
/// Returns: true if the point is on the plane, with a given epsilon.
|
||||
@nogc bool isOn(vec3!T point, T epsilon) pure const nothrow
|
||||
{
|
||||
T sd = signedDistanceTo(point);
|
||||
return (-epsilon < sd) && (sd < epsilon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias Plane!float planef; /// 3D float plane.
|
||||
alias Plane!double planed; /// 3D double plane.
|
||||
|
||||
unittest
|
||||
{
|
||||
auto p = planed(vec4d(1.0, 2.0, 3.0, 4.0));
|
||||
auto p2 = planed(vec3d(1.0, 0.0, 0.0),
|
||||
vec3d(0.0, 1.0, 0.0),
|
||||
vec3d(0.0, 0.0, 1.0));
|
||||
|
||||
assert(p2.isOn(vec3d(1.0, 0.0, 0.0), 1e-7));
|
||||
assert(p2.isFront(vec3d(1.0, 1.0, 1.0)));
|
||||
assert(p2.isBack(vec3d(0.0, 0.0, 0.0)));
|
||||
}
|
||||
|
||||
/// Plane intersection tests
|
||||
@nogc pure nothrow unittest
|
||||
{
|
||||
void testR(planed p, ray3d r, bool shouldIntersect, double expectedDistance, vec3d expectedPoint = vec3d.init) pure nothrow @nogc
|
||||
{
|
||||
vec3d point;
|
||||
double distance;
|
||||
assert(r.intersect(p, point, distance) == shouldIntersect);
|
||||
assert(approxEqual(distance, expectedDistance));
|
||||
if (shouldIntersect)
|
||||
assert(approxEqual(point.v[], expectedPoint.v[]));
|
||||
}
|
||||
// ray facing plane
|
||||
testR(planed(vec4d(1.0, 0.0, 0.0, 1.0)), ray3d(vec3d(2.0, 3.0, 4.0), vec3d(-1.0, 0.0, 0.0)),
|
||||
true, 1.0, vec3d(1.0, 3.0, 4.0));
|
||||
testR(planed(vec4d(1.0, 1.0, 1.0, -sqrt(3.0))), ray3d(vec3d(1.0, 1.0, 1.0), vec3d(-1.0, -1.0, -1.0).normalized()),
|
||||
true, 2.0*sqrt(3.0), vec3d(-1.0, -1.0, -1.0));
|
||||
// ray facing away
|
||||
testR(planed(vec4d(1.0, 0.0, 0.0, 1.0)), ray3d(vec3d(2.0, 3.0, 4.0), vec3d(1.0, 0.0, 0.0)),
|
||||
false, -1.0);
|
||||
testR(planed(vec4d(1.0, 0.0, 0.0, 5.0)), ray3d(vec3d(2.0, 3.0, 4.0), vec3d(-1.0, 0.0, 0.0)),
|
||||
false, -3.0);
|
||||
// ray parallel
|
||||
testR(planed(vec4d(0.0, 0.0, 1.0, 1.0)), ray3d(vec3d(1.0, 2.0, 3.0), vec3d(1.0, 0.0, 0.0)),
|
||||
false, double.infinity);
|
||||
|
||||
void testS(planed p, seg3d s, bool shouldIntersect, double expectedProgress, vec3d expectedPoint = vec3d.init) pure nothrow @nogc
|
||||
{
|
||||
vec3d point;
|
||||
double progress;
|
||||
assert(s.intersect(p, point, progress) == shouldIntersect);
|
||||
assert(approxEqual(progress, expectedProgress));
|
||||
if (shouldIntersect)
|
||||
assert(approxEqual(point.v[], expectedPoint.v[]));
|
||||
}
|
||||
// segment crossing plane
|
||||
testS(planed(vec4d(1.0, 0.0, 0.0, 2.0)), seg3d(vec3d(1.0, 2.0, 3.0), vec3d(3.0, 4.0, 5.0)),
|
||||
true, 0.5, vec3d(2.0, 3.0, 4.0));
|
||||
// segment too short
|
||||
testS(planed(vec4d(1.0, 0.0, 0.0, 0.0)), seg3d(vec3d(1.0, 2.0, 3.0), vec3d(3.0, 4.0, 5.0)),
|
||||
false, -0.5);
|
||||
}
|
||||
|
||||
|
||||
/// 3D frustum.
|
||||
/// See_also: Flipcode article by Dion Picco $(WEB www.flipcode.com/archives/Frustum_Culling.shtml).
|
||||
/// Bugs: verify proper signedness of half-spaces
|
||||
struct Frustum(T) if (isFloatingPoint!T)
|
||||
{
|
||||
public
|
||||
{
|
||||
enum int LEFT = 0,
|
||||
RIGHT = 1,
|
||||
TOP = 2,
|
||||
BOTTOM = 3,
|
||||
NEAR = 4,
|
||||
FAR = 5;
|
||||
|
||||
Plane!T[6] planes;
|
||||
|
||||
/// Create a frustum from 6 planes.
|
||||
@nogc this(Plane!T left, Plane!T right, Plane!T top, Plane!T bottom, Plane!T near, Plane!T far) pure nothrow
|
||||
{
|
||||
planes[LEFT] = left;
|
||||
planes[RIGHT] = right;
|
||||
planes[TOP] = top;
|
||||
planes[BOTTOM] = bottom;
|
||||
planes[NEAR] = near;
|
||||
planes[FAR] = far;
|
||||
}
|
||||
|
||||
enum : int
|
||||
{
|
||||
OUTSIDE, /// object is outside the frustum
|
||||
INTERSECT, /// object intersects with the frustum
|
||||
INSIDE /// object is inside the frustum
|
||||
}
|
||||
|
||||
/// Point vs frustum intersection.
|
||||
@nogc bool contains(vec3!T point) pure const nothrow
|
||||
{
|
||||
for(int i = 0; i < 6; ++i)
|
||||
{
|
||||
T distance = planes[i].signedDistanceTo(point);
|
||||
|
||||
if(distance < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Sphere vs frustum intersection.
|
||||
/// Returns: Frustum.OUTSIDE, Frustum.INTERSECT or Frustum.INSIDE.
|
||||
@nogc int contains(Sphere!(T, 3) sphere) pure const nothrow
|
||||
{
|
||||
// calculate our distances to each of the planes
|
||||
for(int i = 0; i < 6; ++i)
|
||||
{
|
||||
// find the distance to this plane
|
||||
T distance = planes[i].signedDistanceTo(sphere.center);
|
||||
|
||||
if(distance < -sphere.radius)
|
||||
return OUTSIDE;
|
||||
|
||||
else if (distance < sphere.radius)
|
||||
return INTERSECT;
|
||||
}
|
||||
|
||||
// otherwise we are fully in view
|
||||
return INSIDE;
|
||||
}
|
||||
|
||||
/// AABB vs frustum intersection.
|
||||
/// Returns: Frustum.OUTSIDE, Frustum.INTERSECT or Frustum.INSIDE.
|
||||
@nogc int contains(box3!T box) pure const nothrow
|
||||
{
|
||||
vec3!T[8] corners;
|
||||
int totalIn = 0;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
for (int j = 0; j < 2; ++j)
|
||||
for (int k = 0; k < 2; ++k)
|
||||
{
|
||||
auto x = i == 0 ? box.min.x : box.max.x;
|
||||
auto y = j == 0 ? box.min.y : box.max.y;
|
||||
auto z = k == 0 ? box.min.z : box.max.z;
|
||||
corners[i*4 + j*2 + k] = vec3!T(x, y, z);
|
||||
}
|
||||
|
||||
// test all 8 corners against the 6 sides
|
||||
// if all points are behind 1 specific plane, we are out
|
||||
// if we are in with all points, then we are fully in
|
||||
for(int p = 0; p < 6; ++p)
|
||||
{
|
||||
int inCount = 8;
|
||||
int ptIn = 1;
|
||||
|
||||
for(int i = 0; i < 8; ++i)
|
||||
{
|
||||
// test this point against the planes
|
||||
if (planes[p].isBack(corners[i]))
|
||||
{
|
||||
ptIn = 0;
|
||||
--inCount;
|
||||
}
|
||||
}
|
||||
|
||||
// were all the points outside of plane p?
|
||||
if (inCount == 0)
|
||||
return OUTSIDE;
|
||||
|
||||
// check if they were all on the right side of the plane
|
||||
totalIn += ptIn;
|
||||
}
|
||||
|
||||
// so if totalIn is 6, then all are inside the view
|
||||
if(totalIn == 6)
|
||||
return INSIDE;
|
||||
|
||||
// we must be partly in then otherwise
|
||||
return INTERSECT;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
seg2f se;
|
||||
triangle3f tr;
|
||||
Frustum!double frust;
|
||||
planed pl;
|
||||
}
|
||||
|
||||
/// True if `T` is a kind of Segment
|
||||
enum isSegment(T) = is(T : Segment!U, U...);
|
||||
|
||||
/// True if `T` is a kind of Triangle
|
||||
enum isTriangle(T) = is(T : Triangle!U, U...);
|
||||
|
||||
/// True if `T` is a kind of Sphere
|
||||
enum isSphere(T) = is(T : Sphere!U, U...);
|
||||
|
||||
/// True if `T` is a kind of Ray
|
||||
enum isRay(T) = is(T : Ray!U, U...);
|
||||
|
||||
/// True if `T` is a kind of Plane
|
||||
enum isPlane(T) = is(T : Plane!U, U);
|
||||
|
||||
/// True if `T` is a kind of Frustum
|
||||
enum isFrustum(T) = is(T : Frustum!U, U);
|
||||
|
||||
/// True if `T` is a kind of 2 dimensional Segment
|
||||
enum isSegment2D(T) = is(T : Segment!(U, 2), U);
|
||||
|
||||
/// True if `T` is a kind of 2 dimensional Triangle
|
||||
enum isTriangle2D(T) = is(T : Triangle!(U, 2), U);
|
||||
|
||||
/// True if `T` is a kind of 2 dimensional Sphere
|
||||
enum isSphere2D(T) = is(T : Sphere!(U, 2), U);
|
||||
|
||||
/// True if `T` is a kind of 2 dimensional Ray
|
||||
enum isRay2D(T) = is(T : Ray!(U, 2), U);
|
||||
|
||||
/// True if `T` is a kind of 3 dimensional Segment
|
||||
enum isSegment3D(T) = is(T : Segment!(U, 3), U);
|
||||
|
||||
/// True if `T` is a kind of 3 dimensional Triangle
|
||||
enum isTriangle3D(T) = is(T : Triangle!(U, 3), U);
|
||||
|
||||
/// True if `T` is a kind of 3 dimensional Sphere
|
||||
enum isSphere3D(T) = is(T : Sphere!(U, 3), U);
|
||||
|
||||
/// True if `T` is a kind of 3 dimensional Ray
|
||||
enum isRay3D(T) = is(T : Ray!(U, 3), U);
|
||||
|
||||
unittest
|
||||
{
|
||||
enum ShapeType
|
||||
{
|
||||
segment,
|
||||
triangle,
|
||||
sphere,
|
||||
ray,
|
||||
plane,
|
||||
frustum
|
||||
}
|
||||
|
||||
void test(T, ShapeType type, int dim)()
|
||||
{
|
||||
with (ShapeType)
|
||||
{
|
||||
static assert(isSegment!T == (type == segment ));
|
||||
static assert(isTriangle!T == (type == triangle));
|
||||
static assert(isSphere!T == (type == sphere ));
|
||||
static assert(isRay!T == (type == ray ));
|
||||
static assert(isPlane!T == (type == plane ));
|
||||
static assert(isFrustum!T == (type == frustum ));
|
||||
|
||||
static assert(isSegment2D!T == (type == segment && dim == 2));
|
||||
static assert(isTriangle2D!T == (type == triangle && dim == 2));
|
||||
static assert(isSphere2D!T == (type == sphere && dim == 2));
|
||||
static assert(isRay2D!T == (type == ray && dim == 2));
|
||||
|
||||
static assert(isSegment3D!T == (type == segment && dim == 3));
|
||||
static assert(isTriangle3D!T == (type == triangle && dim == 3));
|
||||
static assert(isSphere3D!T == (type == sphere && dim == 3));
|
||||
static assert(isRay3D!T == (type == ray && dim == 3));
|
||||
}
|
||||
}
|
||||
|
||||
with (ShapeType)
|
||||
{
|
||||
// test case type #dimensions
|
||||
test!(seg2f , segment , 2);
|
||||
test!(seg3d , segment , 3);
|
||||
test!(triangle2d , triangle, 2);
|
||||
test!(triangle3f , triangle, 3);
|
||||
test!(sphere2d , sphere , 2);
|
||||
test!(Sphere!(uint, 3), sphere , 3);
|
||||
test!(ray2f , ray , 2);
|
||||
test!(Ray!(real, 3) , ray , 3);
|
||||
test!(planed , plane , 0); // ignore dimension (always 3D)
|
||||
test!(Plane!float , plane , 0);
|
||||
test!(Frustum!double , frustum , 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the numeric type used to measure a shape's dimensions.
|
||||
alias DimensionType(T : Segment!U, U...) = U[0];
|
||||
/// ditto
|
||||
alias DimensionType(T : Triangle!U, U...) = U[0];
|
||||
/// ditto
|
||||
alias DimensionType(T : Sphere!U, U...) = U[0];
|
||||
/// ditto
|
||||
alias DimensionType(T : Ray!U, U...) = U[0];
|
||||
/// ditto
|
||||
alias DimensionType(T : Plane!U, U) = U;
|
||||
/// ditto
|
||||
alias DimensionType(T : Frustum!U, U) = U;
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
static assert(is(DimensionType!seg2i == int));
|
||||
static assert(is(DimensionType!triangle3d == double));
|
||||
static assert(is(DimensionType!sphere2d == double));
|
||||
static assert(is(DimensionType!ray3f == float));
|
||||
static assert(is(DimensionType!planed == double));
|
||||
static assert(is(DimensionType!(Frustum!real) == real));
|
||||
}
|
||||
485
external/gfm/math/simplerng.d
vendored
Normal file
485
external/gfm/math/simplerng.d
vendored
Normal file
@ -0,0 +1,485 @@
|
||||
/**
|
||||
Translation of SimpleRNG.
|
||||
Removed the builtin RNG to use std.random, but kept the distribution functions.
|
||||
John D. Cook confirmed this code as public domain.
|
||||
|
||||
Authors: John D. Cook.
|
||||
See_also: $(WEB www.johndcook.com/cpp_random_number_generation.html)
|
||||
*/
|
||||
module gfm.math.simplerng;
|
||||
|
||||
public import std.random;
|
||||
import std.math;
|
||||
|
||||
/// Returns: Normal (Gaussian) random sample.
|
||||
/// See_also: Box-Muller algorithm.
|
||||
double randNormal(RNG)(ref RNG rng, double mean = 0.0, double standardDeviation = 1.0)
|
||||
{
|
||||
assert(standardDeviation > 0);
|
||||
double u1;
|
||||
|
||||
do
|
||||
{
|
||||
u1 = uniform01(rng);
|
||||
} while (u1 == 0); // u1 must not be zero
|
||||
double u2 = uniform01(rng);
|
||||
double r = sqrt(-2.0 * log(u1));
|
||||
double theta = 2.0 * double(PI) * u2;
|
||||
return mean + standardDeviation * r * sin(theta);
|
||||
}
|
||||
|
||||
/// Returns: Exponential random sample with specified mean.
|
||||
double randExponential(RNG)(ref RNG rng, double mean = 1.0)
|
||||
{
|
||||
assert(mean > 0);
|
||||
return -mean*log(uniform01(rng));
|
||||
}
|
||||
|
||||
/// Returns: Gamma random sample.
|
||||
/// See_also: "A Simple Method for Generating Gamma Variables"
|
||||
/// by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software
|
||||
/// Vol 26, No 3, September 2000, pages 363-372.
|
||||
double randGamma(RNG)(ref RNG rng, double shape, double scale)
|
||||
{
|
||||
double d, c, x, xsquared, v, u;
|
||||
|
||||
if (shape >= 1.0)
|
||||
{
|
||||
d = shape - 1.0/3.0;
|
||||
c = 1.0/sqrt(9.0*d);
|
||||
for (;;)
|
||||
{
|
||||
do
|
||||
{
|
||||
x = randNormal(rng);
|
||||
v = 1.0 + c*x;
|
||||
}
|
||||
while (v <= 0.0);
|
||||
v = v*v*v;
|
||||
u = uniform01(rng);
|
||||
xsquared = x*x;
|
||||
if (u < 1.0 -.0331*xsquared*xsquared || log(u) < 0.5*xsquared + d*(1.0 - v + log(v)))
|
||||
return scale*d*v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(shape > 0);
|
||||
double g = randGamma(rng, shape+1.0, 1.0);
|
||||
double w = uniform01(rng);
|
||||
return scale*g*pow(w, 1.0/shape);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns: Chi-square sample.
|
||||
double randChiSquare(RNG)(ref RNG rng, double degreesOfFreedom)
|
||||
{
|
||||
// A chi squared distribution with n degrees of freedom
|
||||
// is a gamma distribution with shape n/2 and scale 2.
|
||||
return randGamma(rng, 0.5 * degreesOfFreedom, 2.0);
|
||||
}
|
||||
|
||||
/// Returns: Inverse-gamma sample.
|
||||
double randInverseGamma(RNG)(ref RNG rng, double shape, double scale)
|
||||
{
|
||||
// If X is gamma(shape, scale) then
|
||||
// 1/Y is inverse gamma(shape, 1/scale)
|
||||
return 1.0 / randGamma(rng, shape, 1.0 / scale);
|
||||
}
|
||||
|
||||
/// Returns: Weibull sample.
|
||||
double randWeibull(RNG)(ref RNG rng, double shape, double scale)
|
||||
{
|
||||
assert(shape > 0 && scale > 0);
|
||||
return scale * pow(-log(uniform01(rng)), 1.0 / shape);
|
||||
}
|
||||
|
||||
/// Returns: Cauchy sample.
|
||||
double randCauchy(RNG)(ref RNG rng, double median, double scale)
|
||||
{
|
||||
assert(scale > 0);
|
||||
double p = uniform01(rng);
|
||||
|
||||
// Apply inverse of the Cauchy distribution function to a uniform
|
||||
return median + scale*tan(double(PI)*(p - 0.5));
|
||||
}
|
||||
|
||||
/// Returns: Student-t sample.
|
||||
/// See_also: Seminumerical Algorithms by Knuth.
|
||||
double randStudentT(RNG)(ref RNG rng, double degreesOfFreedom)
|
||||
{
|
||||
assert(degreesOfFreedom > 0);
|
||||
|
||||
|
||||
double y1 = getNormal(rng);
|
||||
double y2 = getChiSquare(rng, degreesOfFreedom);
|
||||
return y1 / sqrt(y2 / degreesOfFreedom);
|
||||
}
|
||||
|
||||
/// Returns: Laplace distribution random sample (also known as the double exponential distribution).
|
||||
double randLaplace(RNG)(ref RNG rng, double mean, double scale)
|
||||
{
|
||||
double u = uniform01(rng);
|
||||
return (u < 0.5) ? (mean + scale*log(2.0*u))
|
||||
: (mean - scale*log(2*(1-u)));
|
||||
}
|
||||
|
||||
/// Returns: Log-normal sample.
|
||||
double randLogNormal(RNG)(ref RNG rng, double mu, double sigma)
|
||||
{
|
||||
return exp(getNormal(rng, mu, sigma));
|
||||
}
|
||||
|
||||
/// Returns: Beta sample.
|
||||
double randBeta(RNG)(ref RNG rng, double a, double b)
|
||||
{
|
||||
assert(a > 0 && b > 0);
|
||||
|
||||
// There are more efficient methods for generating beta samples.
|
||||
// However such methods are a little more efficient and much more complicated.
|
||||
// For an explanation of why the following method works, see
|
||||
// http://www.johndcook.com/distribution_chart.html#gamma_beta
|
||||
|
||||
double u = getGamma(rng, a, 1.0);
|
||||
double v = getGamma(rng, b, 1.0);
|
||||
return u / (u + v);
|
||||
}
|
||||
|
||||
/// Returns: Poisson sample.
|
||||
int randPoisson(RNG)(ref RNG rng, double lambda)
|
||||
{
|
||||
return (lambda < 30.0) ? poissonSmall(rng, lambda) : poissonLarge(rng, lambda);
|
||||
}
|
||||
|
||||
private
|
||||
{
|
||||
int poissonSmall(RNG)(ref RNG rng, double lambda)
|
||||
{
|
||||
// Algorithm due to Donald Knuth, 1969.
|
||||
double p = 1.0, L = exp(-lambda);
|
||||
int k = 0;
|
||||
do
|
||||
{
|
||||
k++;
|
||||
p *= uniform01(rng);
|
||||
}
|
||||
while (p > L);
|
||||
return k - 1;
|
||||
}
|
||||
|
||||
int poissonLarge(RNG)(ref RNG rng, double lambda)
|
||||
{
|
||||
// "Rejection method PA" from "The Computer Generation of Poisson Random Variables" by A. C. Atkinson
|
||||
// Journal of the Royal Statistical Society Series C (Applied Statistics) Vol. 28, No. 1. (1979)
|
||||
// The article is on pages 29-35. The algorithm given here is on page 32.
|
||||
|
||||
double c = 0.767 - 3.36/lambda;
|
||||
double beta = double(PI)/sqrt(3.0*lambda);
|
||||
double alpha = beta*lambda;
|
||||
double k = log(c) - lambda - log(beta);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
double u = uniform01(rng);
|
||||
double x = (alpha - log((1.0 - u)/u))/beta;
|
||||
int n = cast(int)(floor(x + 0.5));
|
||||
if (n < 0)
|
||||
continue;
|
||||
double v = uniform01(rng);
|
||||
double y = alpha - beta*x;
|
||||
double temp = 1.0 + exp(y);
|
||||
double lhs = y + log(v/(temp*temp));
|
||||
double rhs = k + n*log(lambda) - logFactorial(n);
|
||||
if (lhs <= rhs)
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
double logFactorial(int n) nothrow
|
||||
{
|
||||
assert(n >= 0);
|
||||
if (n > 254)
|
||||
{
|
||||
double x = n + 1;
|
||||
return (x - 0.5)*log(x) - x + 0.5*log(2*double(PI)) + 1.0/(12.0*x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return LOG_FACTORIAL[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static immutable double[255] LOG_FACTORIAL =
|
||||
[
|
||||
0.000000000000000,
|
||||
0.000000000000000,
|
||||
0.693147180559945,
|
||||
1.791759469228055,
|
||||
3.178053830347946,
|
||||
4.787491742782046,
|
||||
6.579251212010101,
|
||||
8.525161361065415,
|
||||
10.604602902745251,
|
||||
12.801827480081469,
|
||||
15.104412573075516,
|
||||
17.502307845873887,
|
||||
19.987214495661885,
|
||||
22.552163853123421,
|
||||
25.191221182738683,
|
||||
27.899271383840894,
|
||||
30.671860106080675,
|
||||
33.505073450136891,
|
||||
36.395445208033053,
|
||||
39.339884187199495,
|
||||
42.335616460753485,
|
||||
45.380138898476908,
|
||||
48.471181351835227,
|
||||
51.606675567764377,
|
||||
54.784729398112319,
|
||||
58.003605222980518,
|
||||
61.261701761002001,
|
||||
64.557538627006323,
|
||||
67.889743137181526,
|
||||
71.257038967168000,
|
||||
74.658236348830158,
|
||||
78.092223553315307,
|
||||
81.557959456115029,
|
||||
85.054467017581516,
|
||||
88.580827542197682,
|
||||
92.136175603687079,
|
||||
95.719694542143202,
|
||||
99.330612454787428,
|
||||
102.968198614513810,
|
||||
106.631760260643450,
|
||||
110.320639714757390,
|
||||
114.034211781461690,
|
||||
117.771881399745060,
|
||||
121.533081515438640,
|
||||
125.317271149356880,
|
||||
129.123933639127240,
|
||||
132.952575035616290,
|
||||
136.802722637326350,
|
||||
140.673923648234250,
|
||||
144.565743946344900,
|
||||
148.477766951773020,
|
||||
152.409592584497350,
|
||||
156.360836303078800,
|
||||
160.331128216630930,
|
||||
164.320112263195170,
|
||||
168.327445448427650,
|
||||
172.352797139162820,
|
||||
176.395848406997370,
|
||||
180.456291417543780,
|
||||
184.533828861449510,
|
||||
188.628173423671600,
|
||||
192.739047287844900,
|
||||
196.866181672889980,
|
||||
201.009316399281570,
|
||||
205.168199482641200,
|
||||
209.342586752536820,
|
||||
213.532241494563270,
|
||||
217.736934113954250,
|
||||
221.956441819130360,
|
||||
226.190548323727570,
|
||||
230.439043565776930,
|
||||
234.701723442818260,
|
||||
238.978389561834350,
|
||||
243.268849002982730,
|
||||
247.572914096186910,
|
||||
251.890402209723190,
|
||||
256.221135550009480,
|
||||
260.564940971863220,
|
||||
264.921649798552780,
|
||||
269.291097651019810,
|
||||
273.673124285693690,
|
||||
278.067573440366120,
|
||||
282.474292687630400,
|
||||
286.893133295426990,
|
||||
291.323950094270290,
|
||||
295.766601350760600,
|
||||
300.220948647014100,
|
||||
304.686856765668720,
|
||||
309.164193580146900,
|
||||
313.652829949878990,
|
||||
318.152639620209300,
|
||||
322.663499126726210,
|
||||
327.185287703775200,
|
||||
331.717887196928470,
|
||||
336.261181979198450,
|
||||
340.815058870798960,
|
||||
345.379407062266860,
|
||||
349.954118040770250,
|
||||
354.539085519440790,
|
||||
359.134205369575340,
|
||||
363.739375555563470,
|
||||
368.354496072404690,
|
||||
372.979468885689020,
|
||||
377.614197873918670,
|
||||
382.258588773060010,
|
||||
386.912549123217560,
|
||||
391.575988217329610,
|
||||
396.248817051791490,
|
||||
400.930948278915760,
|
||||
405.622296161144900,
|
||||
410.322776526937280,
|
||||
415.032306728249580,
|
||||
419.750805599544780,
|
||||
424.478193418257090,
|
||||
429.214391866651570,
|
||||
433.959323995014870,
|
||||
438.712914186121170,
|
||||
443.475088120918940,
|
||||
448.245772745384610,
|
||||
453.024896238496130,
|
||||
457.812387981278110,
|
||||
462.608178526874890,
|
||||
467.412199571608080,
|
||||
472.224383926980520,
|
||||
477.044665492585580,
|
||||
481.872979229887900,
|
||||
486.709261136839360,
|
||||
491.553448223298010,
|
||||
496.405478487217580,
|
||||
501.265290891579240,
|
||||
506.132825342034830,
|
||||
511.008022665236070,
|
||||
515.890824587822520,
|
||||
520.781173716044240,
|
||||
525.679013515995050,
|
||||
530.584288294433580,
|
||||
535.496943180169520,
|
||||
540.416924105997740,
|
||||
545.344177791154950,
|
||||
550.278651724285620,
|
||||
555.220294146894960,
|
||||
560.169054037273100,
|
||||
565.124881094874350,
|
||||
570.087725725134190,
|
||||
575.057539024710200,
|
||||
580.034272767130800,
|
||||
585.017879388839220,
|
||||
590.008311975617860,
|
||||
595.005524249382010,
|
||||
600.009470555327430,
|
||||
605.020105849423770,
|
||||
610.037385686238740,
|
||||
615.061266207084940,
|
||||
620.091704128477430,
|
||||
625.128656730891070,
|
||||
630.172081847810200,
|
||||
635.221937855059760,
|
||||
640.278183660408100,
|
||||
645.340778693435030,
|
||||
650.409682895655240,
|
||||
655.484856710889060,
|
||||
660.566261075873510,
|
||||
665.653857411105950,
|
||||
670.747607611912710,
|
||||
675.847474039736880,
|
||||
680.953419513637530,
|
||||
686.065407301994010,
|
||||
691.183401114410800,
|
||||
696.307365093814040,
|
||||
701.437263808737160,
|
||||
706.573062245787470,
|
||||
711.714725802289990,
|
||||
716.862220279103440,
|
||||
722.015511873601330,
|
||||
727.174567172815840,
|
||||
732.339353146739310,
|
||||
737.509837141777440,
|
||||
742.685986874351220,
|
||||
747.867770424643370,
|
||||
753.055156230484160,
|
||||
758.248113081374300,
|
||||
763.446610112640200,
|
||||
768.650616799717000,
|
||||
773.860102952558460,
|
||||
779.075038710167410,
|
||||
784.295394535245690,
|
||||
789.521141208958970,
|
||||
794.752249825813460,
|
||||
799.988691788643450,
|
||||
805.230438803703120,
|
||||
810.477462875863580,
|
||||
815.729736303910160,
|
||||
820.987231675937890,
|
||||
826.249921864842800,
|
||||
831.517780023906310,
|
||||
836.790779582469900,
|
||||
842.068894241700490,
|
||||
847.352097970438420,
|
||||
852.640365001133090,
|
||||
857.933669825857460,
|
||||
863.231987192405430,
|
||||
868.535292100464630,
|
||||
873.843559797865740,
|
||||
879.156765776907600,
|
||||
884.474885770751830,
|
||||
889.797895749890240,
|
||||
895.125771918679900,
|
||||
900.458490711945270,
|
||||
905.796028791646340,
|
||||
911.138363043611210,
|
||||
916.485470574328820,
|
||||
921.837328707804890,
|
||||
927.193914982476710,
|
||||
932.555207148186240,
|
||||
937.921183163208070,
|
||||
943.291821191335660,
|
||||
948.667099599019820,
|
||||
954.046996952560450,
|
||||
959.431492015349480,
|
||||
964.820563745165940,
|
||||
970.214191291518320,
|
||||
975.612353993036210,
|
||||
981.015031374908400,
|
||||
986.422203146368590,
|
||||
991.833849198223450,
|
||||
997.249949600427840,
|
||||
1002.670484599700300,
|
||||
1008.095434617181700,
|
||||
1013.524780246136200,
|
||||
1018.958502249690200,
|
||||
1024.396581558613400,
|
||||
1029.838999269135500,
|
||||
1035.285736640801600,
|
||||
1040.736775094367400,
|
||||
1046.192096209724900,
|
||||
1051.651681723869200,
|
||||
1057.115513528895000,
|
||||
1062.583573670030100,
|
||||
1068.055844343701400,
|
||||
1073.532307895632800,
|
||||
1079.012946818975000,
|
||||
1084.497743752465600,
|
||||
1089.986681478622400,
|
||||
1095.479742921962700,
|
||||
1100.976911147256000,
|
||||
1106.478169357800900,
|
||||
1111.983500893733000,
|
||||
1117.492889230361000,
|
||||
1123.006317976526100,
|
||||
1128.523770872990800,
|
||||
1134.045231790853000,
|
||||
1139.570684729984800,
|
||||
1145.100113817496100,
|
||||
1150.633503306223700,
|
||||
1156.170837573242400,
|
||||
];
|
||||
|
||||
unittest
|
||||
{
|
||||
Xorshift32 rng;
|
||||
rng.seed(unpredictableSeed());
|
||||
|
||||
double x = randNormal!Xorshift32(rng, 0.0, 1.0);
|
||||
x = randExponential!Xorshift32(rng);
|
||||
x = randGamma!Xorshift32(rng, 1.2, 1.0);
|
||||
x = randGamma!Xorshift32(rng, 0.8, 2.0);
|
||||
x = randChiSquare!Xorshift32(rng, 2.0);
|
||||
x = randInverseGamma!Xorshift32(rng, 1.1, 0.7);
|
||||
x = randWeibull!Xorshift32(rng, 3.0, 0.7);
|
||||
x = randCauchy!Xorshift32(rng, 5.0, 1.4);
|
||||
}
|
||||
@ -1,25 +1,13 @@
|
||||
/**
|
||||
* N-dimensional small vector math.
|
||||
*
|
||||
* Copyright: Copyright Guillaume Piolat 2021.
|
||||
* Copyright Chance Snow 2021.
|
||||
* Copyright Aleksandr Druzhinin 2018.
|
||||
* Copyright Nathan Sashihara 2018.
|
||||
* Copyright Ryan Roden-Corrent 2016.
|
||||
* Copyright Steven Dwy 2015.
|
||||
* Copyright Martin Nowak 2015.
|
||||
* Copyright Tanel Tagaväli 2015.
|
||||
*
|
||||
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
||||
*/
|
||||
module dplug.math.vector;
|
||||
|
||||
/// N-dimension vector mathematical object
|
||||
module gfm.math.vector;
|
||||
|
||||
import std.traits,
|
||||
std.math,
|
||||
std.array;
|
||||
std.conv,
|
||||
std.array,
|
||||
std.string;
|
||||
|
||||
import inteli.emmintrin;
|
||||
import gfm.math.funcs;
|
||||
|
||||
/**
|
||||
* Generic 1D small vector.
|
||||
@ -160,6 +148,15 @@ nothrow:
|
||||
return v.ptr;
|
||||
}
|
||||
|
||||
/// Converts to a pretty string.
|
||||
string toString() const nothrow
|
||||
{
|
||||
try
|
||||
return format("%s", v);
|
||||
catch (Exception e)
|
||||
assert(false); // should not happen since format is right
|
||||
}
|
||||
|
||||
@nogc bool opEquals(U)(U other) pure const nothrow
|
||||
if (is(U : Vector))
|
||||
{
|
||||
@ -555,6 +552,31 @@ alias vec4!int vec4i; ///
|
||||
alias vec4!float vec4f; ///
|
||||
alias vec4!double vec4d; ///
|
||||
|
||||
private
|
||||
{
|
||||
static string generateLoopCode(string formatString, int N)() pure nothrow
|
||||
{
|
||||
string result;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
string index = ctIntToString(i);
|
||||
// replace all @ by indices
|
||||
result ~= formatString.replace("@", index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Speed-up CTFE conversions
|
||||
static string ctIntToString(int n) pure nothrow
|
||||
{
|
||||
static immutable string[16] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
||||
if (n < 10)
|
||||
return table[n];
|
||||
else
|
||||
return to!string(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Element-wise minimum.
|
||||
@nogc Vector!(T, N) minByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
|
||||
@ -730,6 +752,8 @@ unittest
|
||||
assert(!__traits(compiles, h.xx = h.yy));
|
||||
vec4ub j;
|
||||
|
||||
assert(lerp(vec2f(-10, -1), vec2f(10, 1), 0.5) == vec2f(0, 0));
|
||||
|
||||
// larger vectors
|
||||
alias Vector!(float, 5) vec5f;
|
||||
vec5f l = vec5f(1, 2.0f, 3.0, 4u, 5.0L);
|
||||
@ -744,80 +768,3 @@ unittest
|
||||
assert(absByElem(vec3i(-1, 0, 2)) == vec3i(1, 0, 2));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// SSE approximation of reciprocal square root.
|
||||
@nogc T inverseSqrt(T)(T x) pure nothrow if (isFloatingPoint!T)
|
||||
{
|
||||
static if (is(T == float))
|
||||
{
|
||||
__m128 V = _mm_set_ss(x);
|
||||
V = _mm_rsqrt_ss(V);
|
||||
return _mm_cvtss_f32(V);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1 / sqrt(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package
|
||||
{
|
||||
// This generates small loops for Vector, Matrix, and Box.
|
||||
// Time has shown such sort of manually unrolled code works best on both DMD and LDC.
|
||||
|
||||
static string generateLoopCode(string formatString, int N)() pure nothrow
|
||||
{
|
||||
string result;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
string index = ctIntToString(i);
|
||||
// replace all @ by indices
|
||||
|
||||
int after = 0;
|
||||
int cur = 0;
|
||||
for (; cur < formatString.length; ++cur)
|
||||
{
|
||||
char ch = formatString[cur];
|
||||
if (ch == '@')
|
||||
{
|
||||
if (cur > after)
|
||||
result ~= formatString[after..cur];
|
||||
result ~= index;
|
||||
after = cur+1;
|
||||
}
|
||||
}
|
||||
if (cur > after)
|
||||
result ~= formatString[after..cur];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Speed-up CTFE conversions, replacement for std.conv
|
||||
// Doesn't do the negatives.
|
||||
static string ctIntToString(int n) pure nothrow
|
||||
{
|
||||
static immutable string[16] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
||||
if (n < 10)
|
||||
return table[n];
|
||||
else
|
||||
{
|
||||
char[10] r;
|
||||
for (int k = 0; k < 10; ++k)
|
||||
{
|
||||
r[9-k] = cast(char)('0' + n % 10);
|
||||
n /= 10;
|
||||
if (n == 0)
|
||||
return r[9-k..$].idup;
|
||||
}
|
||||
return r.idup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert(ctIntToString(132) == "132");
|
||||
assert(ctIntToString(2147483647) == "2147483647");
|
||||
}
|
||||
@ -4,14 +4,14 @@ import renderer : Destroy;
|
||||
import renderer;
|
||||
import util;
|
||||
import platform;
|
||||
import dplug.math;
|
||||
import math;
|
||||
|
||||
struct Camera
|
||||
{
|
||||
f32 speed = 3.0;
|
||||
Vec3 pos = Vec3(0.0, 0.0, 3.0);
|
||||
Vec3 front = Vec3(0.0, 0.0, -1.0);
|
||||
Vec3 up = Vec3(0.0, 1.0, 0.0);
|
||||
Vec3 velocity = Vec3(0.0);
|
||||
Vec3 pos = Vec3(0.0, 0.0, 0.0);
|
||||
f32 yaw = 0.0;
|
||||
f32 pitch = 0.0;
|
||||
}
|
||||
|
||||
struct Game
|
||||
@ -32,6 +32,9 @@ struct Game
|
||||
|
||||
Camera camera;
|
||||
|
||||
u16 prev_x;
|
||||
u16 prev_y;
|
||||
|
||||
Model model;
|
||||
}
|
||||
|
||||
@ -89,44 +92,80 @@ InitGame(PlatformWindow* window)
|
||||
}
|
||||
|
||||
void
|
||||
Update(Game* g, Camera* cam)
|
||||
ProcessInputs(Game* g, Camera* cam)
|
||||
{
|
||||
f32 speed = cam.speed * g.delta;
|
||||
|
||||
foreach(i; 0 .. g.window.input_count)
|
||||
{
|
||||
bool pressed = g.window.inputs[i].pressed;
|
||||
switch(g.window.inputs[i].key)
|
||||
{
|
||||
case KBI.W:
|
||||
case Input.W: cam.velocity.z = pressed ? -1.0 : 0.0; break;
|
||||
case Input.S: cam.velocity.z = pressed ? +1.0 : 0.0; break;
|
||||
case Input.A: cam.velocity.x = pressed ? -1.0 : 0.0; break;
|
||||
case Input.D: cam.velocity.x = pressed ? +1.0 : 0.0; break;
|
||||
case Input.MouseMotion:
|
||||
{
|
||||
cam.pos += speed * cam.front;
|
||||
} break;
|
||||
case KBI.A:
|
||||
{
|
||||
cam.pos -= speed * cam.front;
|
||||
} break;
|
||||
case KBI.S:
|
||||
{
|
||||
cam.pos -= cross(cam.front, cam.up).normalized() * speed;
|
||||
} break;
|
||||
case KBI.D:
|
||||
{
|
||||
cam.pos += cross(cam.front, cam.up).normalized() * speed;
|
||||
f32 x = cast(f32)(g.window.inputs[i].x) - cast(f32)(g.prev_x);
|
||||
f32 y = cast(f32)(g.window.inputs[i].y) - cast(f32)(g.prev_y);
|
||||
|
||||
cam.yaw += x / 200.0;
|
||||
cam.pitch -= y / 200.0;
|
||||
|
||||
g.prev_x = g.window.inputs[i].x;
|
||||
g.prev_y = g.window.inputs[i].y;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Update(Game* g, Camera* cam)
|
||||
{
|
||||
Mat4 rotation = RotationMatrix(cam);
|
||||
//cam.pos += (rotation * Vec4(cam.velocity * 0.5, 0.0)).xyz * g.delta;
|
||||
|
||||
Extent ext = GetExtent(&g.rd);
|
||||
|
||||
g.globals.view_matrix = ViewMatrix(cam);
|
||||
g.globals.projection_matrix = Mat4Identity();
|
||||
//g.globals.projection_matrix.perspective(Radians(70.0), cast(f32)(ext.x) / cast(f32)(ext.y), 10000.0, 0.1);
|
||||
//g.globals.projection_matrix.rows[1][1] *= -1.0;
|
||||
|
||||
//g.globals.view_projection = g.globals.projection_matrix * g.globals.view_matrix;
|
||||
|
||||
g.globals.res.x = cast(f32)ext.x;
|
||||
g.globals.res.y = cast(f32)ext.y;
|
||||
|
||||
//g.globals.projection_matrix = g.globals.projection_matrix.transposed();
|
||||
//g.globals.view_matrix = g.globals.view_matrix.transposed();
|
||||
//g.globals.view_projection = g.globals.view_projection.transposed();
|
||||
}
|
||||
|
||||
pragma(inline): Mat4
|
||||
RotationMatrix(Camera* cam)
|
||||
{
|
||||
//Quat pitch_rotation = Quat.fromAxis(Vec3(1.0, 0.0, 0.0), cam.pitch);
|
||||
//Quat yaw_rotation = Quat.fromAxis(Vec3(0.0, -1.0, 0.0), cam.yaw);
|
||||
return Mat4Identity(); //cast(Mat4)(yaw_rotation) * cast(Mat4)(pitch_rotation);
|
||||
}
|
||||
|
||||
pragma(inline): Mat4
|
||||
ViewMatrix(Camera* cam)
|
||||
{
|
||||
//Mat4 translation = Mat4.translation(cam.pos);
|
||||
//Mat4 rotation = RotationMatrix(cam);
|
||||
return Mat4Identity(); //(translation * rotation).inverse();
|
||||
}
|
||||
|
||||
void
|
||||
Cycle(Game* g)
|
||||
{
|
||||
g.delta = DeltaTime(&g.timer);
|
||||
|
||||
Update(g, &g.camera);
|
||||
ProcessInputs(g, &g.camera);
|
||||
|
||||
g.globals.projection_matrix = Mat4.identity;
|
||||
g.globals.view_matrix = Mat4.lookAt(g.camera.pos, g.camera.pos + g.camera.front, g.camera.up);
|
||||
Update(g, &g.camera);
|
||||
|
||||
BeginFrame(&g.rd);
|
||||
|
||||
@ -134,11 +173,11 @@ Cycle(Game* g)
|
||||
|
||||
SetUniform(&g.rd, &g.globals);
|
||||
|
||||
DrawRect(&g.rd, 150.0, 300.0, 500.0, 700.0, Vec4(0.0, 0.0, 1.0, 1.0));
|
||||
//DrawRect(&g.rd, 150.0, 300.0, 500.0, 700.0, Vec4(0.0, 0.0, 1.0, 1.0));
|
||||
|
||||
PrepComputeDrawImage(&g.rd);
|
||||
//PrepComputeDrawImage(&g.rd);
|
||||
|
||||
Dispatch(&g.rd);
|
||||
//Dispatch(&g.rd);
|
||||
|
||||
BeginRender(&g.rd);
|
||||
|
||||
@ -146,11 +185,11 @@ Cycle(Game* g)
|
||||
|
||||
BindUIBuffers(&g.rd);
|
||||
|
||||
DrawUI(&g.rd);
|
||||
//DrawUI(&g.rd);
|
||||
|
||||
Bind(&g.rd, &g.triangle_pipeline);
|
||||
|
||||
Draw(&g.rd, 3, 1);
|
||||
//Draw(&g.rd, 3, 1);
|
||||
|
||||
Bind(&g.rd, &g.pbr_pipeline);
|
||||
|
||||
|
||||
@ -6,9 +6,34 @@ import platform;
|
||||
import game;
|
||||
import util;
|
||||
import core.simd;
|
||||
import math;
|
||||
|
||||
// TODO:
|
||||
// 1. Remove bindless (informed to be perf death)
|
||||
// 2. Set up VK_AMD_shader_info
|
||||
|
||||
void main()
|
||||
{
|
||||
Mat4 mat1 = Mat4Identity();
|
||||
Mat4 mat2 = Mat4Identity();
|
||||
mat1.v[0] = 2.0;
|
||||
mat1.v[4] = 3.0;
|
||||
mat1.v[7] = 25.0;
|
||||
mat1.v[11] = 55.0;
|
||||
|
||||
mat2.v[2] = 13.0;
|
||||
mat2.v[5] = 2.0;
|
||||
mat2.v[9] = 23.0;
|
||||
mat2.v[10] = 15.0;
|
||||
mat2.v[11] = 34.0;
|
||||
|
||||
mat2 = mat1 * mat2;
|
||||
|
||||
Logf("%s", mat2.vec[0].v);
|
||||
Logf("%s", mat2.vec[1].v);
|
||||
Logf("%s", mat2.vec[2].v);
|
||||
Logf("%s", mat2.vec[3].v);
|
||||
|
||||
PlatformWindow window = CreateWindow("Video Game", 1920, 1080);
|
||||
|
||||
Game g = InitGame(&window);
|
||||
|
||||
@ -3,9 +3,11 @@ import includes;
|
||||
import std.stdio;
|
||||
import core.memory;
|
||||
|
||||
enum KeyboardInput
|
||||
enum Input
|
||||
{
|
||||
None,
|
||||
|
||||
// Keyboard
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine,
|
||||
@ -22,9 +24,14 @@ enum KeyboardInput
|
||||
Enter, Space,
|
||||
Tilde, Esc,
|
||||
Semicolon, Quote, LeftBrace, RightBrace,
|
||||
|
||||
// Mouse
|
||||
MouseMotion,
|
||||
|
||||
LeftClick, MiddleClick, RightClick,
|
||||
};
|
||||
|
||||
alias KBI = KeyboardInput;
|
||||
alias KBI = Input;
|
||||
|
||||
version(linux)
|
||||
{
|
||||
@ -32,8 +39,19 @@ import core.sys.posix.dlfcn;
|
||||
|
||||
struct InputEvent
|
||||
{
|
||||
KeyboardInput key;
|
||||
Input key;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool pressed;
|
||||
};
|
||||
struct
|
||||
{
|
||||
u16 x;
|
||||
u16 y;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
struct PlatformWindow
|
||||
@ -171,6 +189,8 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
|
||||
|
||||
xcb_map_window(window.conn, window.window);
|
||||
|
||||
xcb_grab_pointer(window.conn, 1, window.window, 0, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, window.window, XCB_NONE, XCB_CURRENT_TIME);
|
||||
|
||||
i32 stream_result = xcb_flush(window.conn);
|
||||
assert(stream_result > 0, "xcb_flush failure");
|
||||
|
||||
@ -180,6 +200,8 @@ PlatformWindow CreateWindow(string name, u16 width, u16 height)
|
||||
void
|
||||
HandleEvents(PlatformWindow* window)
|
||||
{
|
||||
window.input_count = 0;
|
||||
|
||||
xcb_generic_event_t* e;
|
||||
|
||||
do
|
||||
@ -226,6 +248,40 @@ HandleEvents(PlatformWindow* window)
|
||||
window.input_count += 1;
|
||||
}
|
||||
} break;
|
||||
case XCB_BUTTON_PRESS:
|
||||
case XCB_BUTTON_RELEASE:
|
||||
{
|
||||
xcb_button_press_event_t* mouse_event = cast(xcb_button_press_event_t*)e;
|
||||
bool pressed = e.response_type == XCB_BUTTON_PRESS;
|
||||
Input input = Input.None;
|
||||
|
||||
switch (mouse_event.detail)
|
||||
{
|
||||
case XCB_BUTTON_INDEX_1: input = Input.LeftClick; break;
|
||||
case XCB_BUTTON_INDEX_2: input = Input.MiddleClick; break;
|
||||
case XCB_BUTTON_INDEX_3: input = Input.RightClick; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (input != Input.None)
|
||||
{
|
||||
window.inputs[window.input_count].key = input;
|
||||
window.inputs[window.input_count].pressed = pressed;
|
||||
window.input_count += 1;
|
||||
}
|
||||
} break;
|
||||
case XCB_MOTION_NOTIFY:
|
||||
{
|
||||
xcb_motion_notify_event_t* move_event = cast(xcb_motion_notify_event_t*)e;
|
||||
|
||||
if (move_event.event_x > 0 || move_event.event_y > 0)
|
||||
{
|
||||
window.inputs[window.input_count].key = Input.MouseMotion;
|
||||
window.inputs[window.input_count].x = move_event.event_x;
|
||||
window.inputs[window.input_count].y = move_event.event_y;
|
||||
window.input_count += 1;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -256,7 +312,7 @@ Function LoadFunction(Library lib, string name)
|
||||
return fn;
|
||||
};
|
||||
|
||||
KeyboardInput
|
||||
Input
|
||||
ConvertInput(u64 x_key)
|
||||
{
|
||||
switch (x_key)
|
||||
|
||||
@ -7,8 +7,10 @@ import vulkan;
|
||||
import vulkan : Destroy, Init, Draw, DrawIndexed, Bind, BindUIBuffers, BeginRender, SetUniform, PrepComputeDrawImage, Dispatch, FinishFrame, BeginFrame;
|
||||
import assets;
|
||||
import std.math.traits : isNaN;
|
||||
import u = util : Logf;
|
||||
import util : Logf;
|
||||
import util;
|
||||
import platform;
|
||||
import math;
|
||||
|
||||
alias Shader = VkShaderModule;
|
||||
alias Pipeline = PipelineHandle;
|
||||
@ -51,8 +53,9 @@ alias BT = BufferType;
|
||||
|
||||
struct GlobalUniforms
|
||||
{
|
||||
Mat4 view_matrix = Mat4.identity;
|
||||
Mat4 projection_matrix = Mat4.identity;
|
||||
Mat4 view_matrix = Mat4Identity();
|
||||
Mat4 projection_matrix = Mat4Identity();
|
||||
Mat4 view_projection = Mat4Identity();
|
||||
Vec2 res;
|
||||
}
|
||||
|
||||
@ -129,6 +132,12 @@ extern(C) struct Material
|
||||
f32 shininess = 0.0;
|
||||
}
|
||||
|
||||
struct Extent
|
||||
{
|
||||
u32 x;
|
||||
u32 y;
|
||||
}
|
||||
|
||||
struct UIVertex
|
||||
{
|
||||
Vec2 p0;
|
||||
@ -154,6 +163,7 @@ struct MeshPart
|
||||
|
||||
struct Model
|
||||
{
|
||||
Vec3 pos = Vec3(0.0, 0.0, 0.0);
|
||||
Buffer vertex_buffer;
|
||||
Buffer index_buffer;
|
||||
MeshPart[] parts;
|
||||
@ -167,12 +177,12 @@ struct Model
|
||||
Renderer
|
||||
InitRenderer(PlatformWindow* window)
|
||||
{
|
||||
u.Result!(Vulkan) vk_result = Init(window, u.MB(24), u.MB(32));
|
||||
Result!(Vulkan) vk_result = Init(window, MB(24), MB(32));
|
||||
assert(vk_result.ok, "Init failure: Unable to initialize Vulkan");
|
||||
|
||||
Renderer rd = {
|
||||
arena: CreateArena(u.MB(16)),
|
||||
temp_arena: CreateArena(u.MB(16)),
|
||||
arena: CreateArena(MB(16)),
|
||||
temp_arena: CreateArena(MB(16)),
|
||||
vk: vk_result.value,
|
||||
window: window,
|
||||
ui_vertex_buf: GetUIVertexBuffer(&vk_result.value),
|
||||
@ -187,7 +197,10 @@ DrawModel(Renderer* rd, Model* model)
|
||||
{
|
||||
BindBuffers(&rd.vk, &model.index_buffer, &model.vertex_buffer);
|
||||
|
||||
rd.push_const.model_matrix = Mat4.identity;
|
||||
rd.push_const.model_matrix = Mat4Identity();
|
||||
//rd.push_const.model_matrix.translate(Vec3(1.0, 0.0, 0.0));
|
||||
//rd.push_const.model_matrix = rd.push_const.model_matrix.transposed();
|
||||
|
||||
foreach(i, part; model.parts)
|
||||
{
|
||||
rd.push_const.mat_id = part.mat;
|
||||
@ -196,6 +209,12 @@ DrawModel(Renderer* rd, Model* model)
|
||||
}
|
||||
}
|
||||
|
||||
pragma(inline): Extent
|
||||
GetExtent(Renderer* rd)
|
||||
{
|
||||
return Extent(rd.vk.swapchain_extent.width, rd.vk.swapchain_extent.height);
|
||||
}
|
||||
|
||||
pragma(inline): void
|
||||
CopyVertex(Vec4* dst, m3dv_t* src)
|
||||
{
|
||||
|
||||
@ -1,25 +1,3 @@
|
||||
import aliases;
|
||||
import u = util;
|
||||
import util;
|
||||
|
||||
void
|
||||
RunTests()
|
||||
{
|
||||
TestHashTable();
|
||||
}
|
||||
|
||||
void
|
||||
TestHashTable()
|
||||
{
|
||||
auto ht = u.CreateHashTable!(u64, u64)(8);
|
||||
|
||||
u64 value = 55;
|
||||
ht[3] = value;
|
||||
auto res = ht[3];
|
||||
assert(res.ok && value == res.value, "hash table failure");
|
||||
|
||||
res = ~ht[3];
|
||||
assert(res.ok && res.value == value, "hash table delete failure");
|
||||
|
||||
res = ~ht[3];
|
||||
assert(!res.ok, "value not deleted from hash table");
|
||||
}
|
||||
|
||||
@ -5,12 +5,13 @@ import std.stdio;
|
||||
import std.algorithm.comparison;
|
||||
import core.stdc.string : strcmp, memcpy;
|
||||
import std.format : sformat;
|
||||
import u = util : HashTable, Result, Logf, Log, MB, Delete;
|
||||
import util;
|
||||
import alloc;
|
||||
import platform;
|
||||
import ap = assets;
|
||||
import assets;
|
||||
import renderer;
|
||||
import std.math.rounding : Ceil = ceil;
|
||||
import math;
|
||||
|
||||
bool g_VLAYER_SUPPORT = false;
|
||||
bool g_DEBUG_PRINTF = false;
|
||||
@ -138,7 +139,7 @@ struct Vulkan
|
||||
u32 frame_index;
|
||||
u32 semaphore_index;
|
||||
|
||||
u.SLList!(SI) cleanup_list;
|
||||
SLList!(SI) cleanup_list;
|
||||
|
||||
PlatformWindow* window;
|
||||
|
||||
@ -223,9 +224,9 @@ struct QueueInfo
|
||||
i32 gfx_index, tfer_index;
|
||||
VkQueue gfx_queue, tfer_queue;
|
||||
bool single_queue;
|
||||
};
|
||||
}
|
||||
|
||||
u.Result!(Vulkan)
|
||||
Result!(Vulkan)
|
||||
Init(PlatformWindow* window, u64 permanent_mem, u64 frame_mem)
|
||||
{
|
||||
bool success = true;
|
||||
@ -261,7 +262,7 @@ Init(PlatformWindow* window, u64 permanent_mem, u64 frame_mem)
|
||||
if (success) InitBuffers(&vk);
|
||||
if (success) success = InitConversionPipeline(&vk);
|
||||
|
||||
u.Result!(Vulkan) result = {
|
||||
Result!(Vulkan) result = {
|
||||
ok: success,
|
||||
value: vk,
|
||||
};
|
||||
@ -518,7 +519,7 @@ BeginRender(Vulkan* vk)
|
||||
sType: VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
imageView: vk.draw_image.view,
|
||||
imageLayout: vk.draw_image.layout,
|
||||
loadOp: VK_ATTACHMENT_LOAD_OP_LOAD, // CLEAR instead of LOAD if wanting to clear colors, also clearColor value (or whatever)
|
||||
loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, // CLEAR instead of LOAD if wanting to clear colors, also clearColor value (or whatever)
|
||||
storeOp: VK_ATTACHMENT_STORE_OP_STORE,
|
||||
};
|
||||
|
||||
@ -1262,7 +1263,7 @@ CreateGraphicsPipeline(Vulkan* vk, GfxPipelineInfo* build_info)
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_info = {
|
||||
sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
cullMode: VK_CULL_MODE_BACK_BIT,
|
||||
//cullMode: VK_CULL_MODE_BACK_BIT,
|
||||
polygonMode: VK_POLYGON_MODE_FILL,
|
||||
lineWidth: 1.0,
|
||||
frontFace: VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||
@ -1342,8 +1343,8 @@ CreateGraphicsPipeline(Vulkan* vk, GfxPipelineInfo* build_info)
|
||||
|
||||
Arena* arena = &vk.frame_arenas[0];
|
||||
|
||||
u8[] vert_bytes = ap.LoadAssetData(arena, build_info.vertex_shader);
|
||||
u8[] frag_bytes = ap.LoadAssetData(arena, build_info.frag_shader);
|
||||
u8[] vert_bytes = LoadAssetData(arena, build_info.vertex_shader);
|
||||
u8[] frag_bytes = LoadAssetData(arena, build_info.frag_shader);
|
||||
|
||||
assert(vert_bytes && frag_bytes, "Unable to load shaders");
|
||||
|
||||
@ -1422,7 +1423,7 @@ CreateComputePipeline(Vulkan* vk, CompPipelineInfo* comp_info)
|
||||
},
|
||||
};
|
||||
|
||||
u8[] comp_bytes = ap.LoadAssetData(&vk.frame_arenas[0], comp_info.shader);
|
||||
u8[] comp_bytes = LoadAssetData(&vk.frame_arenas[0], comp_info.shader);
|
||||
assert(comp_bytes != null, "Unable to load compute shader data");
|
||||
|
||||
Result!(Shader) comp_module = BuildShader(vk, comp_bytes);
|
||||
@ -1453,10 +1454,6 @@ CreateComputePipeline(Vulkan* vk, CompPipelineInfo* comp_info)
|
||||
void
|
||||
SetUniform(Vulkan* vk, GlobalUniforms* globals)
|
||||
{
|
||||
globals.res.x = vk.swapchain_extent.width;
|
||||
globals.res.y = vk.swapchain_extent.height;
|
||||
globals.projection_matrix = Mat4.perspective(1.57, cast(f32)(globals.res.y) / cast(f32)(globals.res.y), 0.1, 100.0);
|
||||
|
||||
vk.global_buf.data[0] = *globals;
|
||||
|
||||
VkDescriptorBufferInfo buffer_info = {
|
||||
@ -1493,7 +1490,7 @@ Destroy(Vulkan* vk)
|
||||
{
|
||||
vkDeviceWaitIdle(vk.device);
|
||||
|
||||
alias N = u.Node!(SI);
|
||||
alias N = Node!(SI);
|
||||
assert(vk.cleanup_list.first != null, "node null");
|
||||
for(N* node = vk.cleanup_list.first; node != null; node = node.next)
|
||||
{
|
||||
@ -1685,7 +1682,7 @@ InitDescriptors(Vulkan* vk)
|
||||
{
|
||||
foreach(i; cast(u64)DT.min .. cast(u64)DT.max)
|
||||
{
|
||||
vk.desc_bindings[i].lookup_table = u.CreateHashTable!(string, u32)(8);
|
||||
vk.desc_bindings[i].lookup_table = CreateHashTable!(string, u32)(8);
|
||||
|
||||
u32 DESC_MAX_BINDINGS = 512;
|
||||
vk.desc_bindings[i].free = AllocArray!(u32)(&vk.arena, DESC_MAX_BINDINGS);
|
||||
@ -2555,7 +2552,7 @@ CheckQueueProperties(Arena *arena, VkPhysicalDevice device, VkSurfaceKHR surface
|
||||
VkQueueFamilyProperties[] properties = AllocArray!(VkQueueFamilyProperties)(arena, count);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &count, properties.ptr);
|
||||
|
||||
if (count == 1 && properties[0].queueCount == 1 && u.BitEq(properties[0].queueFlags, T_BIT | C_BIT | G_BIT))
|
||||
if (count == 1 && properties[0].queueCount == 1 && BitEq(properties[0].queueFlags, T_BIT | C_BIT | G_BIT))
|
||||
{
|
||||
current.gfx_index = current.tfer_index = 0;
|
||||
current.single_queue = true;
|
||||
@ -2569,13 +2566,13 @@ CheckQueueProperties(Arena *arena, VkPhysicalDevice device, VkSurfaceKHR surface
|
||||
b32 surface_support;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(device, cast(u32)i, surface, &surface_support);
|
||||
|
||||
if (current.gfx_index < 0 && surface_support && u.BitEq(prop.queueFlags, G_BIT))
|
||||
if (current.gfx_index < 0 && surface_support && BitEq(prop.queueFlags, G_BIT))
|
||||
{
|
||||
current.gfx_index = cast(i32)i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (u.BitEq(prop.queueFlags, T_BIT | S_BIT) && !u.BitEq(prop.queueFlags, G_BIT | C_BIT))
|
||||
if (BitEq(prop.queueFlags, T_BIT | S_BIT) && !BitEq(prop.queueFlags, G_BIT | C_BIT))
|
||||
{
|
||||
sparse = true;
|
||||
tfer_only = true;
|
||||
@ -2583,21 +2580,21 @@ CheckQueueProperties(Arena *arena, VkPhysicalDevice device, VkSurfaceKHR surface
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(sparse && tfer_only) && u.BitEq(prop.queueFlags, T_BIT | S_BIT))
|
||||
if (!(sparse && tfer_only) && BitEq(prop.queueFlags, T_BIT | S_BIT))
|
||||
{
|
||||
sparse = true;
|
||||
current.tfer_index = cast(i32)i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sparse && !u.BitEq(prop.queueFlags, T_BIT) && u.BitEq(prop.queueFlags, C_BIT))
|
||||
if (!sparse && !BitEq(prop.queueFlags, T_BIT) && BitEq(prop.queueFlags, C_BIT))
|
||||
{
|
||||
tfer_only = true;
|
||||
current.tfer_index = cast(i32)i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sparse && !tfer_only && u.BitEq(prop.queueFlags, C_BIT))
|
||||
if (!sparse && !tfer_only && BitEq(prop.queueFlags, C_BIT))
|
||||
{
|
||||
current.tfer_index = cast(i32)i;
|
||||
}
|
||||
@ -2615,9 +2612,9 @@ CheckQueueProperties(Arena *arena, VkPhysicalDevice device, VkSurfaceKHR surface
|
||||
pragma(inline): void
|
||||
Push(Vulkan* vk, StepInitialized step)
|
||||
{
|
||||
u.Node!(SI)* node = Alloc!(u.Node!(SI));
|
||||
Node!(SI)* node = Alloc!(Node!(SI));
|
||||
node.value = step;
|
||||
u.PushFront(&vk.cleanup_list, node, null);
|
||||
PushFront(&vk.cleanup_list, node, null);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2928,3 +2925,18 @@ EnableVLayers(Vulkan* vk)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintShaderDisassembly(Vulkan* vk, Pipeline* pipeline, VkShaderStageFlagBits stage)
|
||||
{
|
||||
version(AMD_GPU)
|
||||
{
|
||||
u64 size;
|
||||
VkResult result = vkGetShaderInfoAMD(vk.device, pipeline.handle, stage, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, null);
|
||||
if (result == VK_SUCCESS)
|
||||
{
|
||||
u8[] buf = AllocArray!(u8)(vk.frame_arenas[vk.frame_index], size);
|
||||
vkGetShaderInfoAMD(vk.device, pipeline.handle, stage, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, buf.ptr);
|
||||
Logf("DISASSEMBLY:\n%r", buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
public import includes;
|
||||
import p = platform;
|
||||
import platform;
|
||||
import vulkan : Vulkan, VULKAN_LIBS;
|
||||
|
||||
// Global Functions
|
||||
@ -43,6 +43,11 @@ version(Windows)
|
||||
PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = null;
|
||||
};
|
||||
|
||||
debug
|
||||
{
|
||||
PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD = null;
|
||||
}
|
||||
|
||||
|
||||
// Device Functions
|
||||
|
||||
@ -112,14 +117,14 @@ PFN_vkQueueWaitIdle vkQueueWaitIdle = null;
|
||||
bool
|
||||
LoadGlobalFunctions()
|
||||
{
|
||||
p.Library lib;
|
||||
p.Function fn;
|
||||
Library lib;
|
||||
Function fn;
|
||||
foreach(name; VULKAN_LIBS)
|
||||
{
|
||||
lib = p.LoadLibrary(name);
|
||||
lib = LoadLibrary(name);
|
||||
if (lib.ptr)
|
||||
{
|
||||
fn = p.LoadFunction(lib, "vkGetInstanceProcAddr");
|
||||
fn = LoadFunction(lib, "vkGetInstanceProcAddr");
|
||||
vkGetInstanceProcAddr = cast(PFN_vkGetInstanceProcAddr)fn.ptr;
|
||||
}
|
||||
}
|
||||
@ -205,6 +210,11 @@ LoadDeviceFunctions(Vulkan* vk)
|
||||
vkWaitSemaphores = cast(PFN_vkWaitSemaphores)vkGetDeviceProcAddr(vk.device, "vkWaitSemaphores");
|
||||
vkQueueWaitIdle = cast(PFN_vkQueueWaitIdle)vkGetDeviceProcAddr(vk.device, "vkQueueWaitIdle");
|
||||
|
||||
version(AMD_GPU)
|
||||
{
|
||||
vkGetShaderInfoAMD = cast(PFN_vkGetShaderInfoAMD)vkGetShaderInfoAMD(vk.device, "vkGetShaderInfoAMD");
|
||||
}
|
||||
|
||||
assert(vkCreateSwapchainKHR != null, "LoadDeviceFunctions failure: function pointer is null");
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ mat4 y_matrix = mat4(
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = G.projection_matrix * G.view_matrix * PC.model_matrix * in_pos;
|
||||
gl_Position = G.projection_matrix * G.view_matrix * PC.model_matrix * vec4(in_pos.rgb, 1.0);
|
||||
|
||||
vec4 col = Materials[nonuniformEXT(PC.mat_id)].diffuse;
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
layout (set = 0, binding = 0) uniform GlobalUniforms {
|
||||
mat4 view_matrix;
|
||||
mat4 projection_matrix;
|
||||
mat4 view_projection;
|
||||
vec2 res;
|
||||
} G;
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import core.memory;
|
||||
import std.stdint;
|
||||
import dplug.math;
|
||||
import std.math.trigonometry;
|
||||
import math;
|
||||
|
||||
debug
|
||||
{
|
||||
@ -30,10 +29,10 @@ alias b32 = uint;
|
||||
alias intptr = intptr_t;
|
||||
alias uintptr = uintptr_t;
|
||||
|
||||
alias Vec2 = vec2f;
|
||||
alias Vec3 = vec3f;
|
||||
alias Vec4 = vec4f;
|
||||
alias Vec2 = Vector!(f32, 2);
|
||||
alias Vec3 = Vector!(f32, 3);
|
||||
alias Vec4 = Vector!(f32, 4);
|
||||
|
||||
alias Mat2 = mat2f;
|
||||
alias Mat3 = mat3f;
|
||||
alias Mat4 = mat4f;
|
||||
alias Mat2 = Matrix!(f32, 2);
|
||||
alias Mat3 = Matrix!(f32, 3);
|
||||
alias Mat4 = Matrix!(f32, 4);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import aliases;
|
||||
import m = math;
|
||||
import math;
|
||||
import std.stdio;
|
||||
import core.stdc.string : memset;
|
||||
import core.memory;
|
||||
@ -66,7 +66,7 @@ AllocAlign(Arena* arena, u64 size, u64 alignment)
|
||||
|
||||
uintptr mem_pos = cast(uintptr)arena.mem;
|
||||
uintptr current = mem_pos + arena.pos;
|
||||
uintptr offset = m.AlignPow2(current, alignment) - mem_pos;
|
||||
uintptr offset = AlignPow2(current, alignment) - mem_pos;
|
||||
|
||||
if (offset+size <= arena.length)
|
||||
{
|
||||
|
||||
@ -1,6 +1,599 @@
|
||||
import aliases;
|
||||
import util;
|
||||
import std.math;
|
||||
import std.traits;
|
||||
import inteli;
|
||||
import std.meta;
|
||||
import std.format;
|
||||
import std.stdio;
|
||||
|
||||
T AlignPow2(T)(T v, T a)
|
||||
{
|
||||
return (v + a - 1) & ~(a - 1);
|
||||
};
|
||||
}
|
||||
|
||||
f32 Radians(f32 deg)
|
||||
{
|
||||
return deg * (PI / 180.0);
|
||||
}
|
||||
|
||||
enum IsVector(T) = is(T : Vector!U, U...);
|
||||
|
||||
struct Vector(T, int N)
|
||||
{
|
||||
static assert(N > 0 && N <= 4);
|
||||
|
||||
enum _N = N;
|
||||
alias T _T;
|
||||
|
||||
union
|
||||
{
|
||||
T[N] v;
|
||||
struct
|
||||
{
|
||||
T x;
|
||||
alias x r;
|
||||
|
||||
static if (N > 1)
|
||||
{
|
||||
T y;
|
||||
alias y g;
|
||||
}
|
||||
|
||||
static if (N > 2)
|
||||
{
|
||||
T z;
|
||||
alias z b;
|
||||
}
|
||||
|
||||
static if (N > 3)
|
||||
{
|
||||
T w;
|
||||
alias w a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nothrow @nogc pure:
|
||||
|
||||
this(Args...)(Args args)
|
||||
{
|
||||
static if (args.length == 1)
|
||||
{
|
||||
opAssign!(Args[0])(args[0]);
|
||||
}
|
||||
else static if (args.length == N)
|
||||
{
|
||||
mixin(GenerateLoop!("v[@] = args[@];", N)());
|
||||
}
|
||||
else static if (args.length == 2 && N == 4)
|
||||
{
|
||||
v[0] = args[0];
|
||||
v[1] = args[0];
|
||||
v[2] = args[0];
|
||||
v[3] = args[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false, "Invalid Vector constructor");
|
||||
}
|
||||
}
|
||||
|
||||
ref Vector opAssign(U)(U x) if (isAssignable!(T, U))
|
||||
{
|
||||
mixin(GenerateLoop!("v[@] = x;", N)());
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Vector opAssign(U)(U arr) if (isStaticArray!(U) && isAssignable!(T, typeof(arr[0])) && arr.length == N)
|
||||
{
|
||||
mixin(GenerateLoop!("v[@] = arr[@];", N)());
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Vector opAssign(U)(U arr) if (isDynamicArray!(U) && isAssignable!(T, typeof(arr[0])))
|
||||
{
|
||||
mixin(GenerateLoop!("v[@] = arr[@];", N)());
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Vector opAssign(U)(U u) if (is(U : Vector))
|
||||
{
|
||||
v[] = u.v[];
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Vector opAssign(U)(U x) if (IsVector!(U) && isAssignable!(T, U._T) && (!is(U: Vector)) && (U._N == _N))
|
||||
{
|
||||
mixin(GenerateLoop!("v[@] = x.v[@];", N)());
|
||||
return this;
|
||||
}
|
||||
|
||||
inout(T)* ptr() inout @property
|
||||
{
|
||||
return v.ptr;
|
||||
}
|
||||
|
||||
bool opEquals(U)(U other) if (IsConvertible!(U))
|
||||
{
|
||||
Vector conv = other;
|
||||
return opEquals(conv);
|
||||
}
|
||||
|
||||
Vector opEquals(U)(U other) if (is(U: Vector))
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
foreach(i; 0 .. N)
|
||||
{
|
||||
if (v[i] != other.v[i])
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int opDollar()
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
T[] opSlice()
|
||||
{
|
||||
return v[];
|
||||
}
|
||||
|
||||
Vector opUnary(string op)() if (op == "+" || op == "-" || op == "~" || op == "!")
|
||||
{
|
||||
Vector result;
|
||||
mixin(GenerateLoop!("res.v[@] = " ~ op ~ " v[@];", N)());
|
||||
return res;
|
||||
}
|
||||
|
||||
ref Vector opOpAssign(string op, U)(U value) if (is(U: Vector))
|
||||
{
|
||||
mixin(GenerateLoop!("v[@] " ~ op ~ "= value.v[@];", N)());
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Vector opOpAssign(string op, U)(U value) if (IsConvertible!(U))
|
||||
{
|
||||
Vector conv = value;
|
||||
return opOpAssign!(op)(conv);
|
||||
}
|
||||
|
||||
@property auto opDispatch(string op, U = void)() if (ValidSwizzle!(op) && op.length <= 4)
|
||||
{
|
||||
Vector!(T, op.length) result;
|
||||
enum index_tuple = SwizzleTuple!(op);
|
||||
static foreach(i, index; index_tuple)
|
||||
{
|
||||
result.v[i] = v[index];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@property void opDispatch(string op, U)(U x) if ((op.length > 1) && ValidUniqueSwizzle!(op) && is(typeof(Vector!(T, op.length)(x))))
|
||||
{
|
||||
Vector!(T, op.length) conv = x;
|
||||
enum index_tuple = SwizzleTuple!(op);
|
||||
static foreach(i, index; index_tuple)
|
||||
{
|
||||
v[index] = conv[i];
|
||||
}
|
||||
}
|
||||
|
||||
Vector opBinary(string op, U)(U operand) if (is(U: Vector) || (IsConvertible!(U)))
|
||||
{
|
||||
Vector result;
|
||||
|
||||
static if (is(U: T))
|
||||
{
|
||||
mixin(GenerateLoop!("result.v[@] = cast(T)(v[@] " ~ op ~ " operand);", N)());
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector other = operand;
|
||||
mixin(GenerateLoop!("result.v[@] = cast(T)(v[@] " ~ op ~ " other.v[@]);", N)());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ref T opIndex(size_t i)
|
||||
{
|
||||
return v[i];
|
||||
}
|
||||
|
||||
ref const(T) opIndex(size_t i) const
|
||||
{
|
||||
return v[i];
|
||||
}
|
||||
|
||||
T opIndexAssign(U : T)(U x, size_t i)
|
||||
{
|
||||
return v[i] = x;
|
||||
}
|
||||
|
||||
U opCast(U)() if (IsVector!(U) && (U._N == _N))
|
||||
{
|
||||
U result;
|
||||
mixin(GenerateLoop!("res.v[@] = cast(U._T)v[@];", N)());
|
||||
return result;
|
||||
}
|
||||
|
||||
template IsConvertible(T)
|
||||
{
|
||||
enum bool IsConvertible = (!is(T : Vector)) && is(typeof({ T x; Vector v = x; }()));
|
||||
}
|
||||
|
||||
template SwizzleIndex(char c)
|
||||
{
|
||||
static if ((c == 'x' || c == 'r') && N > 0)
|
||||
enum SwizzleIndex = 0;
|
||||
else static if ((c == 'y' || c == 'g') && N > 1)
|
||||
enum SwizzleIndex = 1;
|
||||
else static if ((c == 'z' || c == 'b') && N > 2)
|
||||
enum SwizzleIndex = 2;
|
||||
else static if ((c == 'w' || c == 'a') && N > 3)
|
||||
enum SwizzleIndex = 3;
|
||||
else
|
||||
enum SwizzleIndex = -1;
|
||||
}
|
||||
|
||||
template SwizzleSet(char c)
|
||||
{
|
||||
static if (c == 'x' || c == 'y' || c == 'z' || c == 'w')
|
||||
enum SwizzleSet = 0;
|
||||
else static if (c == 'r' || c == 'g' || c == 'b' || c == 'a')
|
||||
enum SwizzleSet = 1;
|
||||
else
|
||||
enum SwizzleSet = -1;
|
||||
}
|
||||
|
||||
template SwizzleTuple(string op)
|
||||
{
|
||||
enum op_length = op.length;
|
||||
static if (op.length == 0)
|
||||
enum SwizzleTuple = [];
|
||||
else
|
||||
enum SwizzleTuple = [ SwizzleIndex!(op[0])] ~ SwizzleTuple!(op[1 .. op.length]);
|
||||
}
|
||||
|
||||
template SearchString(char c, string s)
|
||||
{
|
||||
static if (s.length == 0)
|
||||
{
|
||||
enum bool result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum string tail = s[1 .. s.length];
|
||||
enum bool result = (s[0] == c) || SearchString!(c, tail).result;
|
||||
}
|
||||
}
|
||||
|
||||
template UniqueChars(string s)
|
||||
{
|
||||
static if (s.length == 1)
|
||||
{
|
||||
enum bool result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum tail = s[1 .. s.length];
|
||||
enum bool result = !(SearchString!(s[0], tail).result) && UniqueChars!(tail).result;
|
||||
}
|
||||
}
|
||||
|
||||
template ValidSwizzle(string op, int last_swizzle = -1)
|
||||
{
|
||||
static if (op.length == 0)
|
||||
{
|
||||
enum bool ValidSwizzle = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum length = op.length;
|
||||
enum int swizzle_set = SwizzleSet!(op[0]);
|
||||
enum bool valid_swizzle_set = (last_swizzle == -1 || (swizzle_set == last_swizzle));
|
||||
enum bool ValidSwizzle = (SwizzleIndex!(op[0]) != -1) && valid_swizzle_set && ValidSwizzle!(op[1 .. length], swizzle_set);
|
||||
}
|
||||
}
|
||||
|
||||
template ValidUniqueSwizzle(string op)
|
||||
{
|
||||
static if (ValidSwizzle!(op))
|
||||
{
|
||||
enum ValidUniqueSwizzle = UniqueChars!(op).result;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum ValidUniqueSwizzle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Matrix(T, int D)
|
||||
{
|
||||
static assert(D > 0 && D <= 4);
|
||||
|
||||
alias Vector!(T, D) MatrixVec;
|
||||
alias T _T;
|
||||
|
||||
enum N = D*D;
|
||||
|
||||
union
|
||||
{
|
||||
T[N] v;
|
||||
MatrixVec[D] vec;
|
||||
}
|
||||
|
||||
// TODO: setup @nogc nothrow
|
||||
|
||||
this(U...)(U values)
|
||||
{
|
||||
static if ((U.length == N) && allSatisfy!(IsTypeAssignable, U))
|
||||
{
|
||||
static foreach(i, x; values)
|
||||
{
|
||||
v[i] = x;
|
||||
}
|
||||
}
|
||||
else static if ((U.length == 1) && (isAssignable!(U[0])) && (!is(U[0] : Matrix)))
|
||||
{
|
||||
v[] = values[0];
|
||||
}
|
||||
else static assert(false, "Cannot construct matrix with provided parameters");
|
||||
}
|
||||
|
||||
this(U)(T x)
|
||||
{
|
||||
static foreach(i; 0 .. N)
|
||||
{
|
||||
v[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
@property inout(T)* ptr() inout
|
||||
{
|
||||
return v.ptr;
|
||||
}
|
||||
|
||||
ref Matrix opAssign(U : T)(U x)
|
||||
{
|
||||
static foreach(i; 0 .. N)
|
||||
{
|
||||
v[i] = x;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Matrix opAssign(U : Matrix)(U x)
|
||||
{
|
||||
static foreach(i; 0 .. N)
|
||||
{
|
||||
v[i] = x.v[i];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Matrix opAssign(U)(U x) if (IsMatrixInstantiation!(U) && is(U._T : _T) && (!is(U: Matrix) && (U.N != N)))
|
||||
{
|
||||
static foreach(i; 0 .. N)
|
||||
{
|
||||
v[i] = x.v[i];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Matrix opBinary(string op)(T scalar) if (op == "*")
|
||||
{
|
||||
Matrix result;
|
||||
|
||||
static foreach(i; 0 .. N)
|
||||
{
|
||||
result.v[i] = v[i] * scalar;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MatrixVec opBinary(string op)(T factor) if (op == "*")
|
||||
{
|
||||
Matrix result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static if (D == 4)
|
||||
{
|
||||
Matrix opBinary(string op, U)(U x) if (is(U: Matrix!(T, D)) && is(T: f32))
|
||||
{
|
||||
Matrix result;
|
||||
|
||||
auto res = &result;
|
||||
auto l = &this;
|
||||
auto r = &x;
|
||||
|
||||
asm pure @trusted @nogc nothrow
|
||||
{
|
||||
mov R8, l;
|
||||
mov R9, r;
|
||||
mov R10, res;
|
||||
|
||||
movups XMM0, vec.offsetof[R8];
|
||||
movups XMM1, x.vec.offsetof[R9];
|
||||
movups XMM2, x.vec.offsetof[R9]+16;
|
||||
movups XMM3, x.vec.offsetof[R9]+32;
|
||||
movups XMM4, x.vec.offsetof[R9]+48;
|
||||
|
||||
movups XMM5, XMM1;
|
||||
shufps XMM5, XMM5, 0; // XMM5 = vec.xxxx;
|
||||
mulps XMM5, XMM0;
|
||||
movups XMM6, XMM5; // XMM6 = col1;
|
||||
|
||||
movups XMM5, XMM2;
|
||||
shufps XMM5, XMM5, 0;
|
||||
mulps XMM5, XMM0;
|
||||
movups XMM7, XMM5; // XMM7 = col2;
|
||||
|
||||
movups XMM5, XMM3;
|
||||
shufps XMM5, XMM5, 0;
|
||||
mulps XMM5, XMM0;
|
||||
movups XMM8, XMM5; // XMM8 = col3;
|
||||
|
||||
movups XMM5, XMM3;
|
||||
shufps XMM5, XMM5, 0;
|
||||
mulps XMM5, XMM0;
|
||||
movups XMM9, XMM5; // XMM9 = col4;
|
||||
|
||||
movups XMM0, vec.offsetof[R8]+16;
|
||||
|
||||
movups XMM5, XMM1;
|
||||
shufps XMM5, XMM5, 85; // XMM5 = vec.yyyy;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM6, XMM10;
|
||||
|
||||
movups XMM5, XMM2;
|
||||
shufps XMM5, XMM5, 85;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM7, XMM10;
|
||||
|
||||
movups XMM5, XMM3;
|
||||
shufps XMM5, XMM5, 85;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM8, XMM10;
|
||||
|
||||
movups XMM5, XMM4;
|
||||
shufps XMM5, XMM5, 85;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM9, XMM10;
|
||||
|
||||
movups XMM0, vec.offsetof[R8]+32;
|
||||
|
||||
movups XMM5, XMM1;
|
||||
shufps XMM5, XMM5, 170; // XMM5 = vec.zzzz;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM6, XMM10;
|
||||
|
||||
movups XMM5, XMM2;
|
||||
shufps XMM5, XMM5, 170;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM7, XMM10;
|
||||
|
||||
movups XMM5, XMM3;
|
||||
shufps XMM5, XMM5, 170;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM8, XMM10;
|
||||
|
||||
movups XMM5, XMM4;
|
||||
shufps XMM5, XMM5, 170;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM9, XMM10;
|
||||
|
||||
movups XMM0, vec.offsetof[R8]+48;
|
||||
|
||||
movups XMM5, XMM1;
|
||||
shufps XMM5, XMM5, 255; // XMM5 = vec.wwww;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM6, XMM10;
|
||||
|
||||
movups XMM5, XMM2;
|
||||
shufps XMM5, XMM5, 255;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM7, XMM10;
|
||||
|
||||
movups XMM5, XMM3;
|
||||
shufps XMM5, XMM5, 255;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM8, XMM10;
|
||||
|
||||
movups XMM5, XMM4;
|
||||
shufps XMM5, XMM5, 255;
|
||||
movups XMM10, XMM0;
|
||||
mulps XMM10, XMM5;
|
||||
addps XMM9, XMM10;
|
||||
|
||||
movups result.vec.offsetof[R10]+00, XMM6;
|
||||
movups result.vec.offsetof[R10]+16, XMM7;
|
||||
movups result.vec.offsetof[R10]+32, XMM8;
|
||||
movups result.vec.offsetof[R10]+48, XMM9;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template IsTypeAssignable(U)
|
||||
{
|
||||
enum bool IsTypeAssignable = std.traits.isAssignable!(T, U);
|
||||
}
|
||||
|
||||
template IsMatrixInstantiation(U)
|
||||
{
|
||||
static void IsMatrix(T, int D)(Matrix!(T, D) x) {}
|
||||
|
||||
enum bool IsMatrixInstantiation = is(typeof(IsMatrix(U.init)));
|
||||
}
|
||||
}
|
||||
|
||||
nothrow @nogc pragma(inline): Mat4
|
||||
Mat4Identity()
|
||||
{
|
||||
return Mat4(
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
);
|
||||
}
|
||||
|
||||
nothrow @nogc pragma(inline): Mat3
|
||||
Mat3Identity()
|
||||
{
|
||||
return Mat3(
|
||||
1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 1.0
|
||||
);
|
||||
}
|
||||
|
||||
nothrow @nogc pragma(inline): Mat2
|
||||
Mat2Identity()
|
||||
{
|
||||
return Mat2(
|
||||
1.0, 0.0,
|
||||
0.0, 1.0
|
||||
);
|
||||
}
|
||||
|
||||
T SquaredMagnitude(T)(T v) if (IsVector!(T))
|
||||
{
|
||||
T sum_squares = 0;
|
||||
mixin(GenerateLoop!("sum_squares += v.v[@] * v.v[@];", v._N)());
|
||||
return sum_squares;
|
||||
}
|
||||
|
||||
T SquaredDistTo(T)(T l, T r) if (IsVector!(T))
|
||||
{
|
||||
return SquaredMagnitude(r - l);
|
||||
}
|
||||
|
||||
|
||||
@ -3,14 +3,23 @@ import xxhash3;
|
||||
import includes;
|
||||
import std.stdio;
|
||||
import core.stdc.string : memset;
|
||||
import a = alloc;
|
||||
import alloc;
|
||||
import core.simd;
|
||||
import std.conv;
|
||||
import std.string;
|
||||
|
||||
void
|
||||
Logf(Args...)(string fmt, Args args)
|
||||
{
|
||||
try
|
||||
{
|
||||
writefln(fmt, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
assert(false, "Incompatible format type");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Log(string str)
|
||||
@ -47,7 +56,7 @@ ConvertColor(Vec4 *dst, u32 src)
|
||||
{
|
||||
if (src == 0)
|
||||
{
|
||||
dst.v[] = 0.0;
|
||||
dst.rgb = 0.0;
|
||||
dst.a = 1.0;
|
||||
}
|
||||
else
|
||||
@ -232,8 +241,8 @@ struct HashTable(K, V)
|
||||
HashTable!(K, V)
|
||||
CreateHashTable(K, V)(u64 size)
|
||||
{
|
||||
auto nil = a.Alloc!(Node!(KVPair!(K, V)));
|
||||
auto lists = a.AllocArray!(SLList!(KVPair!(K, V)))(size);
|
||||
auto nil = Alloc!(Node!(KVPair!(K, V)));
|
||||
auto lists = AllocArray!(SLList!(KVPair!(K, V)))(size);
|
||||
|
||||
HashTable!(K, V) table = {
|
||||
lists: lists,
|
||||
@ -278,7 +287,7 @@ Push(K, V)(HashTable!(K, V)* ht, K key, V value)
|
||||
}
|
||||
else
|
||||
{
|
||||
node = a.Alloc!(N);
|
||||
node = Alloc!(N);
|
||||
}
|
||||
|
||||
node.next = ht.nil;
|
||||
@ -519,3 +528,33 @@ DeltaTime(Timer* t)
|
||||
t.prev = time;
|
||||
return cast(f32)(step) / cast(f32)(t.cpu_freq);
|
||||
}
|
||||
|
||||
static string
|
||||
IntToStr(int n) nothrow pure @safe
|
||||
{
|
||||
string result;
|
||||
|
||||
static immutable string[] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
||||
if (n < table.length)
|
||||
{
|
||||
result = table[n];
|
||||
}
|
||||
else
|
||||
{
|
||||
result = to!string(n);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static string
|
||||
GenerateLoop(string format_string, int N)() nothrow pure @safe
|
||||
{
|
||||
string result;
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
result ~= format_string.replace("@", IntToStr(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user