add curve module
parent
1e8f7c57e9
commit
47c3add134
20
bind/v4k.lua
20
bind/v4k.lua
|
@ -1514,6 +1514,14 @@ ffi.cdef([[
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3* samples;
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3* points;
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void curve_add(curve_t *c, vec3 p);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void curve_add(curve_t *c, vec3 p);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: void curve_add(curve_t *c, vec3 p);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:API vec3 curve_eval(curve_t *c, float dt, unsigned *color);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC vec3 curve_eval(curve_t *c, float dt, unsigned *color);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: vec3 curve_eval(curve_t *c, float dt, unsigned *color);
|
||||||
//lcpp INF [0000] test: macro name but used as C declaration in:API int (test)(const char *file, int line, const char *expr, bool result);
|
//lcpp INF [0000] test: macro name but used as C declaration in:API int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
//lcpp INF [0000] test: macro name but used as C declaration in:STATIC int (test)(const char *file, int line, const char *expr, bool result);
|
//lcpp INF [0000] test: macro name but used as C declaration in:STATIC int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
//lcpp INF [0000] test: macro name but used as C declaration in: int (test)(const char *file, int line, const char *expr, bool result);
|
//lcpp INF [0000] test: macro name but used as C declaration in: int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
|
@ -3212,6 +3220,18 @@ float duration;
|
||||||
float tween_update(tween_t *tw, float dt);
|
float tween_update(tween_t *tw, float dt);
|
||||||
void tween_reset(tween_t *tw);
|
void tween_reset(tween_t *tw);
|
||||||
void tween_destroy(tween_t *tw);
|
void tween_destroy(tween_t *tw);
|
||||||
|
typedef struct curve_t {
|
||||||
|
float* lengths;
|
||||||
|
unsigned* colors;
|
||||||
|
vec3* samples;
|
||||||
|
vec3* points;
|
||||||
|
int* indices;
|
||||||
|
} curve_t;
|
||||||
|
curve_t curve();
|
||||||
|
void curve_add(curve_t *c, vec3 p);
|
||||||
|
void curve_finish(curve_t *c, int num_points);
|
||||||
|
vec3 curve_eval(curve_t *c, float dt, unsigned *color);
|
||||||
|
void curve_destroy(curve_t *c);
|
||||||
void* thread( int (*thread_func)(void* user_data), void* user_data );
|
void* thread( int (*thread_func)(void* user_data), void* user_data );
|
||||||
void thread_destroy( void *thd );
|
void thread_destroy( void *thd );
|
||||||
int argc();
|
int argc();
|
||||||
|
|
|
@ -2,204 +2,14 @@
|
||||||
|
|
||||||
#include "v4k.h"
|
#include "v4k.h"
|
||||||
|
|
||||||
// [ref] https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
|
|
||||||
|
|
||||||
vec3 catmull( vec3 p0, vec3 p1, vec3 p2, vec3 p3, float t ) {
|
|
||||||
float t2 = t*t;
|
|
||||||
float t3 = t*t*t;
|
|
||||||
|
|
||||||
vec3 c;
|
|
||||||
c.x = 0.5 * ((2 * p1.x) + (-p0.x + p2.x) * t + (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 + (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
|
|
||||||
c.y = 0.5 * ((2 * p1.y) + (-p0.y + p2.y) * t + (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 + (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
|
|
||||||
c.z = 0.5 * ((2 * p1.z) + (-p0.z + p2.z) * t + (2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z) * t2 + (-p0.z + 3 * p1.z - 3 * p2.z + p3.z) * t3);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [ref] https://gamedevnotesblog.wordpress.com/2017/09/08/constant-time-algorithm-for-parametric-curves/
|
|
||||||
// - rlyeh, public domain. based on pseudo-code by Matrefeytontias
|
|
||||||
|
|
||||||
typedef struct curve {
|
|
||||||
array(float) lengths;
|
|
||||||
array(unsigned) colors;
|
|
||||||
array(vec3) samples;
|
|
||||||
array(vec3) points;
|
|
||||||
array(int) indices;
|
|
||||||
} curve;
|
|
||||||
|
|
||||||
void curve_add(curve *c, vec3 pt) {
|
|
||||||
// @fixme: do not add dupes into list
|
|
||||||
array_push(c->points, pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void curve_finish(curve *c, int k ) {
|
|
||||||
assert( k > 0 );
|
|
||||||
|
|
||||||
c->lengths = 0;
|
|
||||||
c->samples = 0;
|
|
||||||
c->indices = 0;
|
|
||||||
c->colors = 0;
|
|
||||||
|
|
||||||
// refit points[N] to samples[K]
|
|
||||||
int N = array_count(c->points);
|
|
||||||
for( int i = 0; i < N; ++i) {
|
|
||||||
printf("P%d) ", i); print3(c->points[i]); puts("");
|
|
||||||
}
|
|
||||||
if( k < N ) {
|
|
||||||
|
|
||||||
// truncate: expected k-points lesser or equal than existing N points
|
|
||||||
for( int i = 0; i <= k; ++i ) {
|
|
||||||
float s = (float)i / k;
|
|
||||||
int t = s * (N-1);
|
|
||||||
array_push(c->samples, c->points[t]);
|
|
||||||
|
|
||||||
float p = fmod(i, N-1) / (N-1); // [0..1)
|
|
||||||
int is_control_point = p <= 0 || p >= 1;
|
|
||||||
array_push(c->colors, is_control_point ? ORANGE: BLUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// interpolate: expected k-points greater than existing N-points
|
|
||||||
//
|
|
||||||
--N;
|
|
||||||
printf("k%d, N%d\n", k, N);
|
|
||||||
int upper = N - (k%N);
|
|
||||||
int lower = (k%N);
|
|
||||||
if(upper < lower)
|
|
||||||
k += upper;
|
|
||||||
else
|
|
||||||
k -= lower;
|
|
||||||
printf("k%d, N%d\n", k, N);
|
|
||||||
//k -= 2; // begin/end points do not compute below
|
|
||||||
|
|
||||||
//array_push(c->samples, c->points[0]);
|
|
||||||
int points_per_segment = (k / N);
|
|
||||||
++N;
|
|
||||||
|
|
||||||
int looped = len3sq(sub3(c->points[0], *array_back(c->points))) < 0.1;
|
|
||||||
|
|
||||||
for( int i = 0; i <= k; ++i ) {
|
|
||||||
#if 1
|
|
||||||
int point = i % points_per_segment;
|
|
||||||
float p = point / (float)points_per_segment; // [0..1)
|
|
||||||
#else
|
|
||||||
float p = fmod(i, N) / N; // [0..1)
|
|
||||||
#endif
|
|
||||||
int t = i / points_per_segment;
|
|
||||||
|
|
||||||
// linear
|
|
||||||
vec3 l = mix3(c->points[t], c->points[t+(i!=k)], p);
|
|
||||||
|
|
||||||
// printf("%d) %d>%d %f\n", i, t, t+(i!=k), p);
|
|
||||||
ASSERT(p <= 1);
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
// catmull
|
|
||||||
int p0 = t - 1;
|
|
||||||
int p1 = t + 0;
|
|
||||||
int p2 = t + 1;
|
|
||||||
int p3 = t + 2;
|
|
||||||
if( looped )
|
|
||||||
{
|
|
||||||
int M = N-1;
|
|
||||||
if(p0<0) p0+=M; else if(p0>=M) p0-=M;
|
|
||||||
if(p1<0) p1+=M; else if(p1>=M) p1-=M;
|
|
||||||
if(p2<0) p2+=M; else if(p2>=M) p2-=M;
|
|
||||||
if(p3<0) p3+=M; else if(p3>=M) p3-=M;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int M = N-1;
|
|
||||||
if(p0<0) p0=0; else if(p0>=M) p0=M;
|
|
||||||
if(p1<0) p1=0; else if(p1>=M) p1=M;
|
|
||||||
if(p2<0) p2=0; else if(p2>=M) p2=M;
|
|
||||||
if(p3<0) p3=0; else if(p3>=M) p3=M;
|
|
||||||
}
|
|
||||||
vec3 m = catmull(c->points[p0],c->points[p1],c->points[p2],c->points[p3],p);
|
|
||||||
l = m;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
array_push(c->samples, l);
|
|
||||||
|
|
||||||
int is_control_point = p <= 0 || p >= 1;
|
|
||||||
array_push(c->colors, is_control_point ? ORANGE: BLUE);
|
|
||||||
}
|
|
||||||
//array_push(c->samples, *array_back(c->points));
|
|
||||||
}
|
|
||||||
|
|
||||||
array_push(c->lengths, 0 );
|
|
||||||
for( int i = 1; i <= k; ++i ) {
|
|
||||||
// approximate curve length at every sample point
|
|
||||||
array_push(c->lengths, len3(sub3(c->samples[i], c->samples[i-1])) + c->lengths[i-1] );
|
|
||||||
}
|
|
||||||
// normalize lengths to be between 0 and 1
|
|
||||||
float maxv = c->lengths[k];
|
|
||||||
for( int i = 1; i <= k; ++i ) c->lengths[i] /= maxv;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
for( int i = 0; i <= k; ++i) {
|
|
||||||
printf("L%d)%f,", i, c->lengths[i]);
|
|
||||||
} puts("");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
array_push(c->indices, 0 );
|
|
||||||
for( int i = 0/*1*/; i </*=*/ k; ++i ) {
|
|
||||||
float s = (float)i / (k-1); //k;
|
|
||||||
int j; // j = so that lengths[j] <= s < lengths[j+1];
|
|
||||||
#if 0
|
|
||||||
for( j = 0; j <= k; ++j ) {
|
|
||||||
if( c->lengths[j] <= s && s < c->lengths[j+1] ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// j = Index of the highest length that is less or equal to s
|
|
||||||
// Can be optimized with a binary search instead
|
|
||||||
for( j = *array_back(c->indices) + 1; j </*=*/ k; ++j ) {
|
|
||||||
if( c->lengths[j] </*=*/ s ) continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (c->lengths[j] > 0.01)
|
|
||||||
array_push(c->indices, j );
|
|
||||||
}
|
|
||||||
|
|
||||||
for( int i = 0; i < array_count(c->indices); ++i ) {
|
|
||||||
printf("I%d,", c->indices[i]);
|
|
||||||
} puts("");
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 curve_eval(curve *c, float dt, unsigned *color) {
|
|
||||||
unsigned nil; if(!color) color = &nil;
|
|
||||||
if(dt <= 0) dt = 0; // return *color = c->colors[0], c->samples[0];
|
|
||||||
if(dt >= 1) dt = 1; // return *color = *array_back(c->colors), *array_back(c->samples);
|
|
||||||
int l = (int)(array_count(c->indices) - 1);
|
|
||||||
int p = (int)(dt * l);
|
|
||||||
#if 1
|
|
||||||
int t = c->indices[p];
|
|
||||||
#else
|
|
||||||
int t = p;
|
|
||||||
#endif
|
|
||||||
t %= (array_count(c->indices)-1);
|
|
||||||
vec3 pos = mix3(c->samples[t], c->samples[t+1], dt * l - p);
|
|
||||||
*color = c->colors[t];
|
|
||||||
|
|
||||||
puts/*window_title*/(stringf("dt=%5.2f P%dI%d %5f %5f %5f", dt, p,t, pos.x,pos.y,pos.z));
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
window_create(0.85, WINDOW_MSAA4);
|
window_create(0.85, WINDOW_MSAA4);
|
||||||
|
|
||||||
camera_t cam = camera();
|
camera_t cam = camera();
|
||||||
|
|
||||||
curve cv = {0};
|
curve_t cv = curve();
|
||||||
for(float t = 0; t <= 360; t += 36) {
|
for(float t = 0; t <= 360; t += 36) {
|
||||||
curve_add(&cv, vec3(cos(t*TO_RAD)*5,0,sin(t*TO_RAD)*5));
|
curve_add(&cv, vec3(cos(t*TO_RAD)*5,0,sin(t*TO_RAD)*5));
|
||||||
// curve_add(&cv, vec3(cos(t*TO_RAD)*5,0,sin(t*TO_RAD)*5));
|
|
||||||
// curve_add(&cv, vec3(cos(t*TO_RAD)*5,0,sin(t*TO_RAD)*5));
|
|
||||||
// curve_add(&cv, vec3(cos(t*TO_RAD)*5,0,sin(t*TO_RAD)*5));
|
|
||||||
}
|
}
|
||||||
int num_points = 11; // beware with these: 8,11,17,20,61,100,200
|
int num_points = 11; // beware with these: 8,11,17,20,61,100,200
|
||||||
curve_finish(&cv, num_points);
|
curve_finish(&cv, num_points);
|
||||||
|
|
|
@ -18145,6 +18145,23 @@ API void tween_delkey(tween_t *tw, float t);
|
||||||
API float tween_update(tween_t *tw, float dt);
|
API float tween_update(tween_t *tw, float dt);
|
||||||
API void tween_reset(tween_t *tw);
|
API void tween_reset(tween_t *tw);
|
||||||
API void tween_destroy(tween_t *tw);
|
API void tween_destroy(tween_t *tw);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// curve
|
||||||
|
|
||||||
|
typedef struct curve_t {
|
||||||
|
array(float) lengths;
|
||||||
|
array(unsigned) colors;
|
||||||
|
array(vec3) samples;
|
||||||
|
array(vec3) points;
|
||||||
|
array(int) indices;
|
||||||
|
} curve_t;
|
||||||
|
|
||||||
|
API curve_t curve();
|
||||||
|
API void curve_add(curve_t *c, vec3 p);
|
||||||
|
API void curve_finish(curve_t *c, int num_points);
|
||||||
|
API vec3 curve_eval(curve_t *c, float dt, unsigned *color);
|
||||||
|
API void curve_destroy(curve_t *c);
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "engine/split/v4k_system.h"
|
#line 1 "engine/split/v4k_system.h"
|
||||||
|
@ -355181,6 +355198,157 @@ void tween_delkey(tween_t *tw, float t) { // @todo: untested
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// curve
|
||||||
|
|
||||||
|
curve_t curve() {
|
||||||
|
curve_t c = {0};
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE
|
||||||
|
vec3 catmull( vec3 p0, vec3 p1, vec3 p2, vec3 p3, float t ) {
|
||||||
|
float t2 = t*t;
|
||||||
|
float t3 = t*t*t;
|
||||||
|
|
||||||
|
vec3 c;
|
||||||
|
c.x = 0.5 * ((2 * p1.x) + (-p0.x + p2.x) * t + (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 + (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
|
||||||
|
c.y = 0.5 * ((2 * p1.y) + (-p0.y + p2.y) * t + (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 + (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
|
||||||
|
c.z = 0.5 * ((2 * p1.z) + (-p0.z + p2.z) * t + (2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z) * t2 + (-p0.z + 3 * p1.z - 3 * p2.z + p3.z) * t3);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_add(curve_t *c, vec3 p) {
|
||||||
|
array_push(c->points, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_finish( curve_t *c, int k ) {
|
||||||
|
assert( k > 0 );
|
||||||
|
|
||||||
|
array_free(c->lengths);
|
||||||
|
array_free(c->samples);
|
||||||
|
array_free(c->indices);
|
||||||
|
array_free(c->colors);
|
||||||
|
|
||||||
|
// refit points[N] to samples[K]
|
||||||
|
int N = array_count(c->points);
|
||||||
|
if( k < N ) {
|
||||||
|
// truncate: expected k-points lesser or equal than existing N points
|
||||||
|
for( int i = 0; i <= k; ++i ) {
|
||||||
|
float s = (float)i / k;
|
||||||
|
int t = s * (N-1);
|
||||||
|
array_push(c->samples, c->points[t]);
|
||||||
|
|
||||||
|
float p = fmod(i, N-1) / (N-1); // [0..1)
|
||||||
|
int is_control_point = p <= 0 || p >= 1;
|
||||||
|
array_push(c->colors, is_control_point ? ORANGE: BLUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// interpolate: expected k-points greater than existing N-points
|
||||||
|
--N;
|
||||||
|
int upper = N - (k%N);
|
||||||
|
int lower = (k%N);
|
||||||
|
if(upper < lower)
|
||||||
|
k += upper;
|
||||||
|
else
|
||||||
|
k -= lower;
|
||||||
|
|
||||||
|
int points_per_segment = (k / N);
|
||||||
|
++N;
|
||||||
|
|
||||||
|
int looped = len3sq(sub3(c->points[0], *array_back(c->points))) < 0.1;
|
||||||
|
|
||||||
|
for( int i = 0; i <= k; ++i ) {
|
||||||
|
int point = i % points_per_segment;
|
||||||
|
float p = point / (float)points_per_segment; // [0..1)
|
||||||
|
int t = i / points_per_segment;
|
||||||
|
|
||||||
|
// linear
|
||||||
|
vec3 l = mix3(c->points[t], c->points[t+(i!=k)], p);
|
||||||
|
|
||||||
|
// printf("%d) %d>%d %f\n", i, t, t+(i!=k), p);
|
||||||
|
ASSERT(p <= 1);
|
||||||
|
|
||||||
|
// catmull
|
||||||
|
int p0 = t - 1;
|
||||||
|
int p1 = t + 0;
|
||||||
|
int p2 = t + 1;
|
||||||
|
int p3 = t + 2;
|
||||||
|
if( looped )
|
||||||
|
{
|
||||||
|
int M = N-1;
|
||||||
|
if(p0<0) p0+=M; else if(p0>=M) p0-=M;
|
||||||
|
if(p1<0) p1+=M; else if(p1>=M) p1-=M;
|
||||||
|
if(p2<0) p2+=M; else if(p2>=M) p2-=M;
|
||||||
|
if(p3<0) p3+=M; else if(p3>=M) p3-=M;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int M = N-1;
|
||||||
|
if(p0<0) p0=0; else if(p0>=M) p0=M;
|
||||||
|
if(p1<0) p1=0; else if(p1>=M) p1=M;
|
||||||
|
if(p2<0) p2=0; else if(p2>=M) p2=M;
|
||||||
|
if(p3<0) p3=0; else if(p3>=M) p3=M;
|
||||||
|
}
|
||||||
|
vec3 m = catmull(c->points[p0],c->points[p1],c->points[p2],c->points[p3],p);
|
||||||
|
l = m;
|
||||||
|
|
||||||
|
array_push(c->samples, l);
|
||||||
|
|
||||||
|
int is_control_point = p <= 0 || p >= 1;
|
||||||
|
array_push(c->colors, is_control_point ? ORANGE: BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push(c->lengths, 0 );
|
||||||
|
for( int i = 1; i <= k; ++i ) {
|
||||||
|
// approximate curve length at every sample point
|
||||||
|
array_push(c->lengths, len3(sub3(c->samples[i], c->samples[i-1])) + c->lengths[i-1] );
|
||||||
|
}
|
||||||
|
// normalize lengths to be between 0 and 1
|
||||||
|
float maxv = c->lengths[k];
|
||||||
|
for( int i = 1; i <= k; ++i ) c->lengths[i] /= maxv;
|
||||||
|
|
||||||
|
array_push(c->indices, 0 );
|
||||||
|
for( int i = 0/*1*/; i </*=*/ k; ++i ) {
|
||||||
|
float s = (float)i / (k-1); //k;
|
||||||
|
int j; // j = so that lengths[j] <= s < lengths[j+1];
|
||||||
|
|
||||||
|
// j = Index of the highest length that is less or equal to s
|
||||||
|
// Can be optimized with a binary search instead
|
||||||
|
for( j = *array_back(c->indices) + 1; j </*=*/ k; ++j ) {
|
||||||
|
if( c->lengths[j] </*=*/ s ) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->lengths[j] > 0.01)
|
||||||
|
array_push(c->indices, j );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 curve_eval(curve_t *c, float dt, unsigned *color) {
|
||||||
|
unsigned nil; if(!color) color = &nil;
|
||||||
|
dt = clampf(dt, 0.0f, 1.0f);
|
||||||
|
int l = (int)(array_count(c->indices) - 1);
|
||||||
|
int p = (int)(dt * l);
|
||||||
|
int t = c->indices[p];
|
||||||
|
|
||||||
|
t %= (array_count(c->indices)-1);
|
||||||
|
vec3 pos = mix3(c->samples[t], c->samples[t+1], dt * l - p);
|
||||||
|
*color = c->colors[t];
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_destroy(curve_t *c) {
|
||||||
|
array_free(c->lengths);
|
||||||
|
array_free(c->colors);
|
||||||
|
array_free(c->samples);
|
||||||
|
array_free(c->points);
|
||||||
|
array_free(c->indices);
|
||||||
|
}
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "engine/split/v4k_system.c"
|
#line 1 "engine/split/v4k_system.c"
|
||||||
|
|
|
@ -484,3 +484,154 @@ void tween_delkey(tween_t *tw, float t) { // @todo: untested
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// curve
|
||||||
|
|
||||||
|
curve_t curve() {
|
||||||
|
curve_t c = {0};
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE
|
||||||
|
vec3 catmull( vec3 p0, vec3 p1, vec3 p2, vec3 p3, float t ) {
|
||||||
|
float t2 = t*t;
|
||||||
|
float t3 = t*t*t;
|
||||||
|
|
||||||
|
vec3 c;
|
||||||
|
c.x = 0.5 * ((2 * p1.x) + (-p0.x + p2.x) * t + (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 + (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
|
||||||
|
c.y = 0.5 * ((2 * p1.y) + (-p0.y + p2.y) * t + (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 + (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
|
||||||
|
c.z = 0.5 * ((2 * p1.z) + (-p0.z + p2.z) * t + (2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z) * t2 + (-p0.z + 3 * p1.z - 3 * p2.z + p3.z) * t3);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_add(curve_t *c, vec3 p) {
|
||||||
|
array_push(c->points, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_finish( curve_t *c, int k ) {
|
||||||
|
assert( k > 0 );
|
||||||
|
|
||||||
|
array_free(c->lengths);
|
||||||
|
array_free(c->samples);
|
||||||
|
array_free(c->indices);
|
||||||
|
array_free(c->colors);
|
||||||
|
|
||||||
|
// refit points[N] to samples[K]
|
||||||
|
int N = array_count(c->points);
|
||||||
|
if( k < N ) {
|
||||||
|
// truncate: expected k-points lesser or equal than existing N points
|
||||||
|
for( int i = 0; i <= k; ++i ) {
|
||||||
|
float s = (float)i / k;
|
||||||
|
int t = s * (N-1);
|
||||||
|
array_push(c->samples, c->points[t]);
|
||||||
|
|
||||||
|
float p = fmod(i, N-1) / (N-1); // [0..1)
|
||||||
|
int is_control_point = p <= 0 || p >= 1;
|
||||||
|
array_push(c->colors, is_control_point ? ORANGE: BLUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// interpolate: expected k-points greater than existing N-points
|
||||||
|
--N;
|
||||||
|
int upper = N - (k%N);
|
||||||
|
int lower = (k%N);
|
||||||
|
if(upper < lower)
|
||||||
|
k += upper;
|
||||||
|
else
|
||||||
|
k -= lower;
|
||||||
|
|
||||||
|
int points_per_segment = (k / N);
|
||||||
|
++N;
|
||||||
|
|
||||||
|
int looped = len3sq(sub3(c->points[0], *array_back(c->points))) < 0.1;
|
||||||
|
|
||||||
|
for( int i = 0; i <= k; ++i ) {
|
||||||
|
int point = i % points_per_segment;
|
||||||
|
float p = point / (float)points_per_segment; // [0..1)
|
||||||
|
int t = i / points_per_segment;
|
||||||
|
|
||||||
|
// linear
|
||||||
|
vec3 l = mix3(c->points[t], c->points[t+(i!=k)], p);
|
||||||
|
|
||||||
|
// printf("%d) %d>%d %f\n", i, t, t+(i!=k), p);
|
||||||
|
ASSERT(p <= 1);
|
||||||
|
|
||||||
|
// catmull
|
||||||
|
int p0 = t - 1;
|
||||||
|
int p1 = t + 0;
|
||||||
|
int p2 = t + 1;
|
||||||
|
int p3 = t + 2;
|
||||||
|
if( looped )
|
||||||
|
{
|
||||||
|
int M = N-1;
|
||||||
|
if(p0<0) p0+=M; else if(p0>=M) p0-=M;
|
||||||
|
if(p1<0) p1+=M; else if(p1>=M) p1-=M;
|
||||||
|
if(p2<0) p2+=M; else if(p2>=M) p2-=M;
|
||||||
|
if(p3<0) p3+=M; else if(p3>=M) p3-=M;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int M = N-1;
|
||||||
|
if(p0<0) p0=0; else if(p0>=M) p0=M;
|
||||||
|
if(p1<0) p1=0; else if(p1>=M) p1=M;
|
||||||
|
if(p2<0) p2=0; else if(p2>=M) p2=M;
|
||||||
|
if(p3<0) p3=0; else if(p3>=M) p3=M;
|
||||||
|
}
|
||||||
|
vec3 m = catmull(c->points[p0],c->points[p1],c->points[p2],c->points[p3],p);
|
||||||
|
l = m;
|
||||||
|
|
||||||
|
array_push(c->samples, l);
|
||||||
|
|
||||||
|
int is_control_point = p <= 0 || p >= 1;
|
||||||
|
array_push(c->colors, is_control_point ? ORANGE: BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push(c->lengths, 0 );
|
||||||
|
for( int i = 1; i <= k; ++i ) {
|
||||||
|
// approximate curve length at every sample point
|
||||||
|
array_push(c->lengths, len3(sub3(c->samples[i], c->samples[i-1])) + c->lengths[i-1] );
|
||||||
|
}
|
||||||
|
// normalize lengths to be between 0 and 1
|
||||||
|
float maxv = c->lengths[k];
|
||||||
|
for( int i = 1; i <= k; ++i ) c->lengths[i] /= maxv;
|
||||||
|
|
||||||
|
array_push(c->indices, 0 );
|
||||||
|
for( int i = 0/*1*/; i </*=*/ k; ++i ) {
|
||||||
|
float s = (float)i / (k-1); //k;
|
||||||
|
int j; // j = so that lengths[j] <= s < lengths[j+1];
|
||||||
|
|
||||||
|
// j = Index of the highest length that is less or equal to s
|
||||||
|
// Can be optimized with a binary search instead
|
||||||
|
for( j = *array_back(c->indices) + 1; j </*=*/ k; ++j ) {
|
||||||
|
if( c->lengths[j] </*=*/ s ) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->lengths[j] > 0.01)
|
||||||
|
array_push(c->indices, j );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 curve_eval(curve_t *c, float dt, unsigned *color) {
|
||||||
|
unsigned nil; if(!color) color = &nil;
|
||||||
|
dt = clampf(dt, 0.0f, 1.0f);
|
||||||
|
int l = (int)(array_count(c->indices) - 1);
|
||||||
|
int p = (int)(dt * l);
|
||||||
|
int t = c->indices[p];
|
||||||
|
|
||||||
|
t %= (array_count(c->indices)-1);
|
||||||
|
vec3 pos = mix3(c->samples[t], c->samples[t+1], dt * l - p);
|
||||||
|
*color = c->colors[t];
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_destroy(curve_t *c) {
|
||||||
|
array_free(c->lengths);
|
||||||
|
array_free(c->colors);
|
||||||
|
array_free(c->samples);
|
||||||
|
array_free(c->points);
|
||||||
|
array_free(c->indices);
|
||||||
|
}
|
||||||
|
|
|
@ -137,3 +137,20 @@ API void tween_delkey(tween_t *tw, float t);
|
||||||
API float tween_update(tween_t *tw, float dt);
|
API float tween_update(tween_t *tw, float dt);
|
||||||
API void tween_reset(tween_t *tw);
|
API void tween_reset(tween_t *tw);
|
||||||
API void tween_destroy(tween_t *tw);
|
API void tween_destroy(tween_t *tw);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// curve
|
||||||
|
|
||||||
|
typedef struct curve_t {
|
||||||
|
array(float) lengths;
|
||||||
|
array(unsigned) colors;
|
||||||
|
array(vec3) samples;
|
||||||
|
array(vec3) points;
|
||||||
|
array(int) indices;
|
||||||
|
} curve_t;
|
||||||
|
|
||||||
|
API curve_t curve();
|
||||||
|
API void curve_add(curve_t *c, vec3 p);
|
||||||
|
API void curve_finish(curve_t *c, int num_points);
|
||||||
|
API vec3 curve_eval(curve_t *c, float dt, unsigned *color);
|
||||||
|
API void curve_destroy(curve_t *c);
|
||||||
|
|
151
engine/v4k.c
151
engine/v4k.c
|
@ -22793,6 +22793,157 @@ void tween_delkey(tween_t *tw, float t) { // @todo: untested
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// curve
|
||||||
|
|
||||||
|
curve_t curve() {
|
||||||
|
curve_t c = {0};
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static INLINE
|
||||||
|
vec3 catmull( vec3 p0, vec3 p1, vec3 p2, vec3 p3, float t ) {
|
||||||
|
float t2 = t*t;
|
||||||
|
float t3 = t*t*t;
|
||||||
|
|
||||||
|
vec3 c;
|
||||||
|
c.x = 0.5 * ((2 * p1.x) + (-p0.x + p2.x) * t + (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 + (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
|
||||||
|
c.y = 0.5 * ((2 * p1.y) + (-p0.y + p2.y) * t + (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 + (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
|
||||||
|
c.z = 0.5 * ((2 * p1.z) + (-p0.z + p2.z) * t + (2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z) * t2 + (-p0.z + 3 * p1.z - 3 * p2.z + p3.z) * t3);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_add(curve_t *c, vec3 p) {
|
||||||
|
array_push(c->points, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_finish( curve_t *c, int k ) {
|
||||||
|
assert( k > 0 );
|
||||||
|
|
||||||
|
array_free(c->lengths);
|
||||||
|
array_free(c->samples);
|
||||||
|
array_free(c->indices);
|
||||||
|
array_free(c->colors);
|
||||||
|
|
||||||
|
// refit points[N] to samples[K]
|
||||||
|
int N = array_count(c->points);
|
||||||
|
if( k < N ) {
|
||||||
|
// truncate: expected k-points lesser or equal than existing N points
|
||||||
|
for( int i = 0; i <= k; ++i ) {
|
||||||
|
float s = (float)i / k;
|
||||||
|
int t = s * (N-1);
|
||||||
|
array_push(c->samples, c->points[t]);
|
||||||
|
|
||||||
|
float p = fmod(i, N-1) / (N-1); // [0..1)
|
||||||
|
int is_control_point = p <= 0 || p >= 1;
|
||||||
|
array_push(c->colors, is_control_point ? ORANGE: BLUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// interpolate: expected k-points greater than existing N-points
|
||||||
|
--N;
|
||||||
|
int upper = N - (k%N);
|
||||||
|
int lower = (k%N);
|
||||||
|
if(upper < lower)
|
||||||
|
k += upper;
|
||||||
|
else
|
||||||
|
k -= lower;
|
||||||
|
|
||||||
|
int points_per_segment = (k / N);
|
||||||
|
++N;
|
||||||
|
|
||||||
|
int looped = len3sq(sub3(c->points[0], *array_back(c->points))) < 0.1;
|
||||||
|
|
||||||
|
for( int i = 0; i <= k; ++i ) {
|
||||||
|
int point = i % points_per_segment;
|
||||||
|
float p = point / (float)points_per_segment; // [0..1)
|
||||||
|
int t = i / points_per_segment;
|
||||||
|
|
||||||
|
// linear
|
||||||
|
vec3 l = mix3(c->points[t], c->points[t+(i!=k)], p);
|
||||||
|
|
||||||
|
// printf("%d) %d>%d %f\n", i, t, t+(i!=k), p);
|
||||||
|
ASSERT(p <= 1);
|
||||||
|
|
||||||
|
// catmull
|
||||||
|
int p0 = t - 1;
|
||||||
|
int p1 = t + 0;
|
||||||
|
int p2 = t + 1;
|
||||||
|
int p3 = t + 2;
|
||||||
|
if( looped )
|
||||||
|
{
|
||||||
|
int M = N-1;
|
||||||
|
if(p0<0) p0+=M; else if(p0>=M) p0-=M;
|
||||||
|
if(p1<0) p1+=M; else if(p1>=M) p1-=M;
|
||||||
|
if(p2<0) p2+=M; else if(p2>=M) p2-=M;
|
||||||
|
if(p3<0) p3+=M; else if(p3>=M) p3-=M;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int M = N-1;
|
||||||
|
if(p0<0) p0=0; else if(p0>=M) p0=M;
|
||||||
|
if(p1<0) p1=0; else if(p1>=M) p1=M;
|
||||||
|
if(p2<0) p2=0; else if(p2>=M) p2=M;
|
||||||
|
if(p3<0) p3=0; else if(p3>=M) p3=M;
|
||||||
|
}
|
||||||
|
vec3 m = catmull(c->points[p0],c->points[p1],c->points[p2],c->points[p3],p);
|
||||||
|
l = m;
|
||||||
|
|
||||||
|
array_push(c->samples, l);
|
||||||
|
|
||||||
|
int is_control_point = p <= 0 || p >= 1;
|
||||||
|
array_push(c->colors, is_control_point ? ORANGE: BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push(c->lengths, 0 );
|
||||||
|
for( int i = 1; i <= k; ++i ) {
|
||||||
|
// approximate curve length at every sample point
|
||||||
|
array_push(c->lengths, len3(sub3(c->samples[i], c->samples[i-1])) + c->lengths[i-1] );
|
||||||
|
}
|
||||||
|
// normalize lengths to be between 0 and 1
|
||||||
|
float maxv = c->lengths[k];
|
||||||
|
for( int i = 1; i <= k; ++i ) c->lengths[i] /= maxv;
|
||||||
|
|
||||||
|
array_push(c->indices, 0 );
|
||||||
|
for( int i = 0/*1*/; i </*=*/ k; ++i ) {
|
||||||
|
float s = (float)i / (k-1); //k;
|
||||||
|
int j; // j = so that lengths[j] <= s < lengths[j+1];
|
||||||
|
|
||||||
|
// j = Index of the highest length that is less or equal to s
|
||||||
|
// Can be optimized with a binary search instead
|
||||||
|
for( j = *array_back(c->indices) + 1; j </*=*/ k; ++j ) {
|
||||||
|
if( c->lengths[j] </*=*/ s ) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->lengths[j] > 0.01)
|
||||||
|
array_push(c->indices, j );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 curve_eval(curve_t *c, float dt, unsigned *color) {
|
||||||
|
unsigned nil; if(!color) color = &nil;
|
||||||
|
dt = clampf(dt, 0.0f, 1.0f);
|
||||||
|
int l = (int)(array_count(c->indices) - 1);
|
||||||
|
int p = (int)(dt * l);
|
||||||
|
int t = c->indices[p];
|
||||||
|
|
||||||
|
t %= (array_count(c->indices)-1);
|
||||||
|
vec3 pos = mix3(c->samples[t], c->samples[t+1], dt * l - p);
|
||||||
|
*color = c->colors[t];
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void curve_destroy(curve_t *c) {
|
||||||
|
array_free(c->lengths);
|
||||||
|
array_free(c->colors);
|
||||||
|
array_free(c->samples);
|
||||||
|
array_free(c->points);
|
||||||
|
array_free(c->indices);
|
||||||
|
}
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "engine/split/v4k_system.c"
|
#line 1 "engine/split/v4k_system.c"
|
||||||
|
|
17
engine/v4k.h
17
engine/v4k.h
|
@ -4212,6 +4212,23 @@ API void tween_delkey(tween_t *tw, float t);
|
||||||
API float tween_update(tween_t *tw, float dt);
|
API float tween_update(tween_t *tw, float dt);
|
||||||
API void tween_reset(tween_t *tw);
|
API void tween_reset(tween_t *tw);
|
||||||
API void tween_destroy(tween_t *tw);
|
API void tween_destroy(tween_t *tw);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// curve
|
||||||
|
|
||||||
|
typedef struct curve_t {
|
||||||
|
array(float) lengths;
|
||||||
|
array(unsigned) colors;
|
||||||
|
array(vec3) samples;
|
||||||
|
array(vec3) points;
|
||||||
|
array(int) indices;
|
||||||
|
} curve_t;
|
||||||
|
|
||||||
|
API curve_t curve();
|
||||||
|
API void curve_add(curve_t *c, vec3 p);
|
||||||
|
API void curve_finish(curve_t *c, int num_points);
|
||||||
|
API vec3 curve_eval(curve_t *c, float dt, unsigned *color);
|
||||||
|
API void curve_destroy(curve_t *c);
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "engine/split/v4k_system.h"
|
#line 1 "engine/split/v4k_system.h"
|
||||||
|
|
Loading…
Reference in New Issue