add curve module

main
Dominik Madarász 2023-11-02 18:45:44 +01:00
parent 1e8f7c57e9
commit 47c3add134
7 changed files with 525 additions and 191 deletions

View File

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

View File

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

View File

@ -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"

View File

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

View File

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

View File

@ -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"

View File

@ -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"