From 2acf49fb34cfa2b774cf66098565b44fb5914550 Mon Sep 17 00:00:00 2001 From: bjorn Date: Tue, 9 Oct 2018 14:21:45 -0700 Subject: [PATCH] Curve; --- src/api.h | 5 +- src/api/math.c | 9 +++ src/api/types/curve.c | 101 +++++++++++++++++++++++++++++++++ src/math/curve.c | 126 ++++++++++++++++++++++++++++++++++++++++++ src/math/curve.h | 19 +++++++ 5 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 src/api/types/curve.c create mode 100644 src/math/curve.c create mode 100644 src/math/curve.h diff --git a/src/api.h b/src/api.h index 40f2c60e..669842e1 100644 --- a/src/api.h +++ b/src/api.h @@ -28,9 +28,10 @@ extern const luaL_Reg lovrBoxShape[]; extern const luaL_Reg lovrCanvas[]; extern const luaL_Reg lovrCapsuleShape[]; extern const luaL_Reg lovrChannel[]; -extern const luaL_Reg lovrController[]; -extern const luaL_Reg lovrCylinderShape[]; extern const luaL_Reg lovrCollider[]; +extern const luaL_Reg lovrController[]; +extern const luaL_Reg lovrCurve[]; +extern const luaL_Reg lovrCylinderShape[]; extern const luaL_Reg lovrDistanceJoint[]; extern const luaL_Reg lovrFont[]; extern const luaL_Reg lovrHingeJoint[]; diff --git a/src/api/math.c b/src/api/math.c index 14d27d74..99a220ec 100644 --- a/src/api/math.c +++ b/src/api/math.c @@ -3,9 +3,16 @@ #include "math/math.h" #include "math/mat4.h" #include "math/quat.h" +#include "math/curve.h" #include "math/randomGenerator.h" #include "math/transform.h" +static int l_lovrMathNewCurve(lua_State* L) { + Curve* curve = lovrCurveCreate(0); + luax_pushobject(L, curve); + return 1; +} + static int l_lovrMathNewRandomGenerator(lua_State* L) { RandomGenerator* generator = lovrRandomGeneratorCreate(); if (lua_gettop(L) > 0){ @@ -126,6 +133,7 @@ static int l_lovrMathLinearToGamma(lua_State* L) { } static const luaL_Reg lovrMath[] = { + { "newCurve", l_lovrMathNewCurve }, { "newRandomGenerator", l_lovrMathNewRandomGenerator }, { "newTransform", l_lovrMathNewTransform }, { "orientationToDirection", l_lovrMathOrientationToDirection }, @@ -144,6 +152,7 @@ int luaopen_lovr_math(lua_State* L) { lua_newtable(L); luaL_register(L, NULL, lovrMath); luax_atexit(L, lovrMathDestroy); + luax_registertype(L, "Curve", lovrCurve); luax_registertype(L, "RandomGenerator", lovrRandomGenerator); luax_registertype(L, "Transform", lovrTransform); lovrMathInit(); diff --git a/src/api/types/curve.c b/src/api/types/curve.c new file mode 100644 index 00000000..12ce219b --- /dev/null +++ b/src/api/types/curve.c @@ -0,0 +1,101 @@ +#include "api.h" +#include "math/curve.h" + +int l_lovrCurveEvaluate(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + float t = luaL_checknumber(L, 2); + float point[3]; + lovrCurveEvaluate(curve, t, point); + lua_pushnumber(L, point[0]); + lua_pushnumber(L, point[1]); + lua_pushnumber(L, point[2]); + return 3; +} + +int l_lovrCurveRender(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + int n = luaL_optinteger(L, 2, 32); + float t1 = luaL_optnumber(L, 3, 0.f); + float t2 = luaL_optnumber(L, 4, 1.f); + float* points = malloc(3 * n * sizeof(float)); + lovrCurveRender(curve, t1, t2, points, n); + lua_createtable(L, n, 0); + for (int i = 0; i < 3 * n; i++) { + lua_pushnumber(L, points[i]); + lua_rawseti(L, -2, i + 1); + } + free(points); + return 1; +} + +int l_lovrCurveSplit(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + float t1 = luaL_checknumber(L, 2); + float t2 = luaL_checknumber(L, 3); + Curve* subcurve = lovrCurveSplit(curve, t1, t2); + luax_pushobject(L, subcurve); + return 1; +} + +int l_lovrCurveGetPointCount(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + lua_pushinteger(L, lovrCurveGetPointCount(curve)); + return 1; +} + +int l_lovrCurveGetDegree(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + lua_pushinteger(L, lovrCurveGetPointCount(curve) - 1); + return 1; +} + +int l_lovrCurveGetPoint(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + int index = luaL_checkinteger(L, 2) - 1; + lovrAssert(index >= 0 && index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1); + float point[3]; + lovrCurveGetPoint(curve, index, point); + lua_pushnumber(L, point[0]); + lua_pushnumber(L, point[1]); + lua_pushnumber(L, point[2]); + return 3; +} + +int l_lovrCurveSetPoint(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + int index = luaL_checkinteger(L, 2) - 1; + lovrAssert(index >= 0 && index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1); + float point[3] = { luaL_checknumber(L, 3), luaL_checknumber(L, 4), luaL_checknumber(L, 5) }; + lovrCurveSetPoint(curve, index, point); + return 0; +} + +int l_lovrCurveAddPoint(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + float point[3] = { luaL_checknumber(L, 2), luaL_checknumber(L, 3), luaL_checknumber(L, 4) }; + int index = lua_isnoneornil(L, 5) ? lovrCurveGetPointCount(curve) : luaL_checkinteger(L, 5) - 1; + lovrAssert(index >= 0 && index <= lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1); + lovrCurveAddPoint(curve, point, index); + return 0; +} + +int l_lovrCurveRemovePoint(lua_State* L) { + Curve* curve = luax_checktype(L, 1, Curve); + int index = luaL_checkinteger(L, 2) - 1; + lovrAssert(index >= 0 && index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1); + lovrCurveRemovePoint(curve, index); + return 0; +} + +const luaL_Reg lovrCurve[] = { + { "evaluate", l_lovrCurveEvaluate }, + { "render", l_lovrCurveRender }, + { "split", l_lovrCurveSplit }, + { "getPointCount", l_lovrCurveGetPointCount }, + { "getDegree", l_lovrCurveGetDegree }, + { "getPoint", l_lovrCurveGetPoint }, + { "setPoint", l_lovrCurveSetPoint }, + { "addPoint", l_lovrCurveAddPoint }, + { "removePoint", l_lovrCurveRemovePoint }, + { NULL, NULL } +}; diff --git a/src/math/curve.c b/src/math/curve.c new file mode 100644 index 00000000..c692586c --- /dev/null +++ b/src/math/curve.c @@ -0,0 +1,126 @@ +#include "math/curve.h" +#include + +// Explicit curve evaluation, unroll simple cases to avoid pow overhead +static void evaluate(float* P, int n, float t, vec3 p) { + if (n == 2) { + p[0] = P[0] + (P[3] - P[0]) * t; + p[1] = P[1] + (P[4] - P[1]) * t; + p[2] = P[2] + (P[5] - P[2]) * t; + } else if (n == 3) { + float t1 = (1 - t); + float a = t1 * t1; + float b = 2 * t1 * t; + float c = t * t; + p[0] = a * P[0] + b * P[3] + c * P[6]; + p[1] = a * P[1] + b * P[4] + c * P[7]; + p[2] = a * P[2] + b * P[5] + c * P[8]; + } else if (n == 4) { + float t1 = (1 - t); + float a = t1 * t1 * t1; + float b = 3 * t1 * t1 * t; + float c = 3 * t1 * t * t; + float d = t * t * t; + p[0] = a * P[0] + b * P[3] + c * P[6] + d * P[9]; + p[1] = a * P[1] + b * P[4] + c * P[7] + d * P[10]; + p[2] = a * P[2] + b * P[5] + c * P[8] + d * P[11]; + } else { + float b = 1.f; + p[0] = p[1] = p[2] = 0.f; + for (int i = 0; i < n; i++, b *= (float) (n - i) / i) { + float c1 = powf(1 - t, n - (i + 1)); + float c2 = powf(t, i); + p[0] += b * c1 * c2 * P[i * 3 + 0]; + p[1] += b * c1 * c2 * P[i * 3 + 1]; + p[2] += b * c1 * c2 * P[i * 3 + 2]; + } + } +} + +Curve* lovrCurveCreate(int sizeHint) { + Curve* curve = lovrAlloc(Curve, lovrCurveDestroy); + if (!curve) return NULL; + + vec_init(&curve->points); + lovrAssert(!vec_reserve(&curve->points, sizeHint * 3), "Out of memory"); + + return curve; +} + +void lovrCurveDestroy(void* ref) { + Curve* curve = ref; + vec_deinit(&curve->points); + free(curve); +} + +void lovrCurveEvaluate(Curve* curve, float t, vec3 p) { + lovrAssert(curve->points.length >= 6, "Need at least 2 points to evaluate a Curve"); + lovrAssert(t >= 0 && t <= 1, "Curve evaluation interval must be within [0, 1]"); + evaluate(curve->points.data, curve->points.length / 3, t, p); +} + +void lovrCurveRender(Curve* curve, float t1, float t2, vec3 points, int n) { + lovrAssert(curve->points.length >= 6, "Need at least 2 points to render a Curve"); + lovrAssert(t1 >= 0 && t2 <= 1, "Curve render interval must be within [0, 1]"); + float step = 1.f / (n - 1); + for (int i = 0; i < n; i++) { + evaluate(curve->points.data, curve->points.length / 3, t1 + (t2 - t1) * i * step, points + 3 * i); + } +} + +Curve* lovrCurveSplit(Curve* curve, float t1, float t2) { + lovrAssert(curve->points.length >= 6, "Need at least 2 points to split a Curve"); + lovrAssert(t1 >= 0 && t2 <= 1, "Curve split interval must be within [0, 1]"); + + Curve* new = lovrCurveCreate(curve->points.length / 3); + new->points.length = curve->points.length; + + int n = curve->points.length / 3; + + // Right half of split at t1 + for (int i = 0; i < n - 1; i++) { + evaluate(curve->points.data + 3 * i, n - i, t1, new->points.data + 3 * i); + } + + vec3_init(new->points.data + 3 * (n - 1), curve->points.data + 3 * (n - 1)); + + // Split segment at t2, taking left half + float t = (t2 - t1) / (1.f - t1); + for (int i = n - 1; i >= 1; i--) { + evaluate(new->points.data, i + 1, t, new->points.data + 3 * i); + } + + return new; +} + +int lovrCurveGetPointCount(Curve* curve) { + return curve->points.length / 3; +} + +void lovrCurveGetPoint(Curve* curve, int index, vec3 point) { + vec3_init(point, curve->points.data + 3 * index); +} + +void lovrCurveSetPoint(Curve* curve, int index, vec3 point) { + vec3_init(curve->points.data + 3 * index, point); +} + +void lovrCurveAddPoint(Curve* curve, vec3 point, int index) { + + // Reserve enough memory for 3 more points, then record destination once memory is allocated + lovrAssert(!vec_reserve(&curve->points, curve->points.length + 3), "Out of memory"); + float* dest = curve->points.data + index * 3; + + // Shift remaining points over (if any) to create empty space + if (index * 3 != curve->points.length) { + memmove(dest + 3, dest, (curve->points.length - index * 3) * sizeof(float)); + } + + // Fill the empty space with the new point + curve->points.length += 3; + memcpy(dest, point, 3 * sizeof(float)); +} + +void lovrCurveRemovePoint(Curve* curve, int index) { + vec_swapsplice(&curve->points, index * 3, 3); +} diff --git a/src/math/curve.h b/src/math/curve.h new file mode 100644 index 00000000..4106ec11 --- /dev/null +++ b/src/math/curve.h @@ -0,0 +1,19 @@ +#include "math/vec3.h" +#include "util.h" +#include "lib/vec/vec.h" + +typedef struct { + Ref ref; + vec_float_t points; +} Curve; + +Curve* lovrCurveCreate(int sizeHint); +void lovrCurveDestroy(void* ref); +void lovrCurveEvaluate(Curve* curve, float t, vec3 point); +void lovrCurveRender(Curve* curve, float t1, float t2, vec3 points, int n); +Curve* lovrCurveSplit(Curve* curve, float t1, float t2); +int lovrCurveGetPointCount(Curve* curve); +void lovrCurveGetPoint(Curve* curve, int index, vec3 point); +void lovrCurveSetPoint(Curve* curve, int index, vec3 point); +void lovrCurveAddPoint(Curve* curve, vec3 point, int index); +void lovrCurveRemovePoint(Curve* curve, int index);