This commit is contained in:
bjorn 2018-10-09 14:21:45 -07:00 committed by Bjorn Swenson
parent 01bed22050
commit 2acf49fb34
5 changed files with 258 additions and 2 deletions

View File

@ -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[];

View File

@ -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();

101
src/api/types/curve.c Normal file
View File

@ -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 }
};

126
src/math/curve.c Normal file
View File

@ -0,0 +1,126 @@
#include "math/curve.h"
#include <math.h>
// 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);
}

19
src/math/curve.h Normal file
View File

@ -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);