2nd sync pass

main
Dominik Madarász 2024-08-12 17:11:25 +02:00
parent a37e7970c1
commit 5d7de442e7
118 changed files with 742063 additions and 744322 deletions

1
.gitignore vendored
View File

@ -28,3 +28,4 @@ v4k.code-workspace
tests/out
tests/diff
mtb.ini
_fwk

View File

@ -449,33 +449,6 @@ enum AUDIO_FLAGS {
AUDIO_SINGLE_INSTANCE = 512,
};
int audio_queue( const void *samples, int num_samples, int flags );
typedef struct gjk_support {
int aid, bid;
vec3 a;
vec3 b;
} gjk_support;
typedef struct gjk_vertex {
vec3 a;
vec3 b;
vec3 p;
int aid, bid;
} gjk_vertex;
typedef struct gjk_simplex {
int max_iter, iter;
int hit, cnt;
gjk_vertex v[4];
float bc[4], D;
} gjk_simplex;
typedef struct gjk_result {
int hit;
vec3 p0;
vec3 p1;
float distance_squared;
int iterations;
} gjk_result;
int gjk(gjk_simplex *s, const gjk_support *sup, vec3 *dv);
gjk_result gjk_analyze(const gjk_simplex *s);
gjk_result gjk_quad(float a_radius, float b_radius);
typedef struct line { vec3 a, b; } line;
typedef struct sphere { vec3 c; float r; } sphere;
typedef struct aabb { vec3 min, max; } aabb;
@ -483,7 +456,6 @@ typedef struct plane { vec3 p, n; } plane;
typedef struct capsule { vec3 a, b; float r; } capsule;
typedef struct ray { vec3 p, d; } ray;
typedef struct triangle { vec3 p0,p1,p2; } triangle;
typedef struct poly { vec3* verts; int cnt; } poly;
typedef union frustum { struct { vec4 l, r, t, b, n, f; }; vec4 pl[6]; float v[24]; } frustum;
typedef struct hit {
union {
@ -510,7 +482,6 @@ typedef struct hit {
hit* sphere_hit_sphere(sphere a, sphere b);
int sphere_test_aabb(sphere s, aabb a);
int sphere_test_capsule(sphere s, capsule c);
int sphere_test_poly(sphere s, poly p);
int sphere_test_sphere(sphere a, sphere b);
vec3 aabb_closest_point(aabb a, vec3 p);
float aabb_distance2_point(aabb a, vec3 p);
@ -520,7 +491,6 @@ typedef struct hit {
hit* aabb_hit_sphere(aabb a, sphere s);
int aabb_test_aabb(aabb a, aabb b);
int aabb_test_capsule(aabb a, capsule c);
int aabb_test_poly(aabb a, poly p);
int aabb_test_sphere(aabb a, sphere s);
float capsule_distance2_point(capsule c, vec3 p);
vec3 capsule_closest_point(capsule c, vec3 p);
@ -529,32 +499,11 @@ typedef struct hit {
hit* capsule_hit_sphere(capsule c, sphere s);
int capsule_test_aabb(capsule c, aabb a);
int capsule_test_capsule(capsule a, capsule b);
int capsule_test_poly(capsule c, poly p);
int capsule_test_sphere(capsule c, sphere s);
int poly_test_sphere(poly p, sphere s);
int poly_test_aabb(poly p, aabb a);
int poly_test_capsule(poly p, capsule c);
int poly_test_poly(poly a, poly b);
int poly_test_sphere_transform(poly p, vec3 pos3, mat33 rot33, sphere s);
int poly_test_aabb_transform(poly p, vec3 apos3, mat33 arot33, aabb a);
int poly_test_capsule_transform(poly p, vec3 pos3, mat33 rot33, capsule c);
int poly_test_poly_transform(poly a, vec3 apos3, mat33 arot33, poly b, vec3 bpos3, mat33 brot33);
int poly_hit_sphere(struct gjk_result *res, poly p, sphere s);
int poly_hit_aabb(struct gjk_result *res, poly p, aabb a);
int poly_hit_capsule(struct gjk_result *res, poly p, capsule c);
int poly_hit_poly(struct gjk_result *res, poly a, poly b);
int poly_hit_sphere_transform(struct gjk_result *res, poly p, vec3 pos3, mat33 rot33, sphere s);
int poly_hit_aabb_transform(struct gjk_result *res, poly p, vec3 pos3, mat33 rot33, aabb a);
int poly_hit_capsule_transform(struct gjk_result *res, poly p, vec3 pos3, mat33 rot33, capsule c);
int poly_hit_poly_transform(struct gjk_result *res, poly a, vec3 at3, mat33 ar33, poly b, vec3 bt3, mat33 br33);
vec4 plane4(vec3 p, vec3 n);
frustum frustum_build(mat44 projview);
int frustum_test_sphere(frustum f, sphere s);
int frustum_test_aabb(frustum f, aabb a);
poly poly_alloc(int cnt);
void poly_free(poly *p);
poly pyramid(vec3 from, vec3 to, float size);
poly diamond(vec3 from, vec3 to, float size);
void collide_demo();
enum COOK_FLAGS {
COOK_SYNC = 0,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,140 +1,140 @@
// base64 de/encoder. Based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN).
// - rlyeh, public domain
#ifndef BASE64_H
#define BASE64_H
unsigned base64_bounds(unsigned size);
char* base64_encode(const void *inp, unsigned inlen); // free() after use
char* base64_decode(const char *inp, unsigned inlen); // array_free() after use
#endif
#ifdef BASE64_C
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#define BASE64_ENCODE_OUT_SIZE(s) ((unsigned int)((((s) + 2) / 3) * 4 + 1))
#define BASE64_DECODE_OUT_SIZE(s) ((unsigned int)(((s) / 4) * 3))
unsigned base64_bounds(unsigned size) {
return BASE64_ENCODE_OUT_SIZE(size);
}
char* base64_encode(const void *inp, unsigned inlen) { // free() after use
unsigned outlen = base64_bounds(inlen);
char *out_ = MALLOC(outlen);
out_[outlen] = 0;
uint_least32_t v;
unsigned ii, io, rem;
char *out = (char *)out_;
const unsigned char *in = (const unsigned char *)inp;
const uint8_t base64enc_tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(io = 0, ii = 0, v = 0, rem = 0; ii < inlen; ii ++) {
unsigned char ch;
ch = in[ii];
v = (v << 8) | ch;
rem += 8;
while (rem >= 6) {
rem -= 6;
if (io >= outlen)
return (FREE(out_), NULL); /* truncation is failure */
out[io ++] = base64enc_tab[(v >> rem) & 63];
}
}
if (rem) {
v <<= (6 - rem);
if (io >= outlen)
return (FREE(out_), NULL); /* truncation is failure */
out[io ++] = base64enc_tab[v & 63];
}
while(io&3) {
if(io>=outlen) return (FREE(out_), NULL); /* truncation is failure */
out[io++]='=';
}
if(io>=outlen) return (FREE(out_), NULL); /* no room for null terminator */
out[io]=0;
return out_;
}
#ifdef array_resize
array(char) base64_decode(const char *inp, unsigned inlen) { // array_free() after use
#if 0
unsigned long outlen = BASE64_DECODE_OUT_SIZE(inlen);
array(char) out_ = 0; array_resize(out_, outlen+1);
if( base64_decodex((const unsigned char *)inp, (unsigned long)inlen, (unsigned char *)out_, &outlen) != CRYPT_OK ) {
array_free(out_);
return 0;
}
array_resize(out_, outlen);
out_[outlen] = 0;
return out_;
#else
unsigned outlen = BASE64_DECODE_OUT_SIZE(inlen);
array(char) out_ = 0; array_resize(out_, outlen);
// based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN)
uint_least32_t v;
unsigned ii, io, rem;
char *out = (char *)out_;
const unsigned char *in = (const unsigned char *)inp;
const uint8_t base64dec_tab[256]= {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255 };
for (io = 0, ii = 0,v = 0, rem = 0; ii < inlen; ii ++) {
unsigned char ch;
if (isspace(in[ii]))
continue;
if ((in[ii]=='=') || (!in[ii]))
break; /* stop at = or null character*/
ch = base64dec_tab[(unsigned char)in[ii]];
if (ch == 255)
break; /* stop at a parse error */
v = (v<<6) | ch;
rem += 6;
if (rem >= 8) {
rem -= 8;
if (io >= outlen)
return (array_free(out_), NULL); /* truncation is failure */
out[io ++] = (v >> rem) & 255;
}
}
if (rem >= 8) {
rem -= 8;
if (io >= outlen)
return (array_free(out_), NULL); /* truncation is failure */
out[io ++] = (v >> rem) & 255;
}
return (array_resize(out_, io), out_);
#endif
}
#endif // array_resize
#endif // BASE64_C
// base64 de/encoder. Based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN).
// - rlyeh, public domain
#ifndef BASE64_H
#define BASE64_H
unsigned base64_bounds(unsigned size);
char* base64_encode(const void *inp, unsigned inlen); // free() after use
char* base64_decode(const char *inp, unsigned inlen); // array_free() after use
#endif
#ifdef BASE64_C
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#define BASE64_ENCODE_OUT_SIZE(s) ((unsigned int)((((s) + 2) / 3) * 4 + 1))
#define BASE64_DECODE_OUT_SIZE(s) ((unsigned int)(((s) / 4) * 3))
unsigned base64_bounds(unsigned size) {
return BASE64_ENCODE_OUT_SIZE(size);
}
char* base64_encode(const void *inp, unsigned inlen) { // free() after use
unsigned outlen = base64_bounds(inlen);
char *out_ = MALLOC(outlen);
out_[outlen] = 0;
uint_least32_t v;
unsigned ii, io, rem;
char *out = (char *)out_;
const unsigned char *in = (const unsigned char *)inp;
const uint8_t base64enc_tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(io = 0, ii = 0, v = 0, rem = 0; ii < inlen; ii ++) {
unsigned char ch;
ch = in[ii];
v = (v << 8) | ch;
rem += 8;
while (rem >= 6) {
rem -= 6;
if (io >= outlen)
return (FREE(out_), NULL); /* truncation is failure */
out[io ++] = base64enc_tab[(v >> rem) & 63];
}
}
if (rem) {
v <<= (6 - rem);
if (io >= outlen)
return (FREE(out_), NULL); /* truncation is failure */
out[io ++] = base64enc_tab[v & 63];
}
while(io&3) {
if(io>=outlen) return (FREE(out_), NULL); /* truncation is failure */
out[io++]='=';
}
if(io>=outlen) return (FREE(out_), NULL); /* no room for null terminator */
out[io]=0;
return out_;
}
#ifdef array_resize
array(char) base64_decode(const char *inp, unsigned inlen) { // array_free() after use
#if 0
unsigned long outlen = BASE64_DECODE_OUT_SIZE(inlen);
array(char) out_ = 0; array_resize(out_, outlen+1);
if( base64_decodex((const unsigned char *)inp, (unsigned long)inlen, (unsigned char *)out_, &outlen) != CRYPT_OK ) {
array_free(out_);
return 0;
}
array_resize(out_, outlen);
out_[outlen] = 0;
return out_;
#else
unsigned outlen = BASE64_DECODE_OUT_SIZE(inlen);
array(char) out_ = 0; array_resize(out_, outlen);
// based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN)
uint_least32_t v;
unsigned ii, io, rem;
char *out = (char *)out_;
const unsigned char *in = (const unsigned char *)inp;
const uint8_t base64dec_tab[256]= {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255 };
for (io = 0, ii = 0,v = 0, rem = 0; ii < inlen; ii ++) {
unsigned char ch;
if (isspace(in[ii]))
continue;
if ((in[ii]=='=') || (!in[ii]))
break; /* stop at = or null character*/
ch = base64dec_tab[(unsigned char)in[ii]];
if (ch == 255)
break; /* stop at a parse error */
v = (v<<6) | ch;
rem += 6;
if (rem >= 8) {
rem -= 8;
if (io >= outlen)
return (array_free(out_), NULL); /* truncation is failure */
out[io ++] = (v >> rem) & 255;
}
}
if (rem >= 8) {
rem -= 8;
if (io >= outlen)
return (array_free(out_), NULL); /* truncation is failure */
out[io ++] = (v >> rem) & 255;
}
return (array_resize(out_, io), out_);
#endif
}
#endif // array_resize
#endif // BASE64_C

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,407 +1,407 @@
/* public domain Simple, Minimalistic MPEG Layer 1 decoder - http://jonolick.com
*
* Revision History:
* 1.00 (2014-26-1) Initial release.
*
* Basic usage:
* int hz, channels, outputSize;
* short *output;
* jo_read_mp1(input, inputSize, output, outputSize, hz, channels);
* // Do something with the data here
* free(output);
*
* */
#ifndef JO_INCLUDE_MP1_H
#define JO_INCLUDE_MP1_H
#include <stdbool.h>
extern bool jo_read_mp1(const void *input, int inputSize, short **output, int *outputSize, int *hz, int *channels);
#endif // JO_INCLUDE_MP1_H
#ifndef JO_MP1_HEADER_FILE_ONLY
#if defined(_MSC_VER) && _MSC_VER >= 0x1400
#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen()
#endif
#include <stdlib.h>
#include <math.h>
#include <limits.h>
static const double s_jo_multTbl[64] = {
2.000000,1.587401,1.259921,1.000000,0.793701,0.629961,0.500000,0.396850,0.314980,0.250000,0.198425,0.157490,0.125000,0.099213,0.078745,0.062500,
0.049606,0.039373,0.031250,0.024803,0.019686,0.015625,0.012402,0.009843,0.007812,0.006201,0.004922,0.003906,0.003100,0.002461,0.001953,0.001550,
0.001230,0.000977,0.000775,0.000615,0.000488,0.000388,0.000308,0.000244,0.000194,0.000154,0.000122,0.000097,0.000077,0.000061,0.000048,0.000038,
0.000031,0.000024,0.000019,0.000015,0.000012,0.000010,0.000008,0.000006,0.000005,0.000004,0.000003,0.000002,0.000002,0.000002,0.000001,1e-20
};
// l = i - 256;
// s = (i & 0x40) ? 1 : -1;
// windowTbl[(i/16)|((i%16)<<5)] = s * 20 * exp(-(l/112)*-(l/112)) * sin(l * M_PI*2 / 112) / l;
static const double s_jo_windowTbl[512] = {
-0.000000,-0.000443,0.003250,-0.007004,0.031082,-0.078629,0.100311,-0.572037,1.144989,0.572037,0.100311,0.078629,0.031082,0.007004,0.003250,0.000443,
-0.000015,-0.000473,0.003326,-0.007919,0.030518,-0.084183,0.090927,-0.600220,1.144287,0.543823,0.108856,0.073059,0.031479,0.006119,0.003174,0.000397,
-0.000015,-0.000534,0.003387,-0.008865,0.029785,-0.089706,0.080688,-0.628296,1.142212,0.515610,0.116577,0.067520,0.031738,0.005295,0.003082,0.000366,
-0.000015,-0.000580,0.003433,-0.009842,0.028885,-0.095169,0.069595,-0.656219,1.138763,0.487473,0.123474,0.061996,0.031845,0.004486,0.002991,0.000320,
-0.000015,-0.000626,0.003464,-0.010849,0.027802,-0.100540,0.057617,-0.683914,1.133926,0.459473,0.129578,0.056534,0.031815,0.003723,0.002899,0.000290,
-0.000015,-0.000687,0.003479,-0.011887,0.026535,-0.105820,0.044785,-0.711319,1.127747,0.431656,0.134888,0.051132,0.031662,0.003006,0.002792,0.000259,
-0.000015,-0.000748,0.003479,-0.012939,0.025085,-0.110947,0.031082,-0.738373,1.120224,0.404083,0.139450,0.045837,0.031387,0.002335,0.002686,0.000244,
-0.000031,-0.000809,0.003464,-0.014023,0.023422,-0.115921,0.016510,-0.765030,1.111374,0.376801,0.143265,0.040634,0.031006,0.001694,0.002579,0.000214,
-0.000031,-0.000885,0.003418,-0.015121,0.021576,-0.120697,0.001068,-0.791214,1.101212,0.349869,0.146362,0.035553,0.030533,0.001099,0.002457,0.000198,
-0.000031,-0.000961,0.003372,-0.016235,0.019531,-0.125259,-0.015228,-0.816864,1.089783,0.323318,0.148773,0.030609,0.029938,0.000549,0.002350,0.000168,
-0.000031,-0.001038,0.003281,-0.017349,0.017258,-0.129562,-0.032379,-0.841949,1.077118,0.297211,0.150497,0.025818,0.029282,0.000031,0.002243,0.000153,
-0.000046,-0.001114,0.003174,-0.018463,0.014801,-0.133591,-0.050354,-0.866364,1.063217,0.271591,0.151596,0.021179,0.028534,-0.000443,0.002121,0.000137,
-0.000046,-0.001205,0.003052,-0.019577,0.012115,-0.137299,-0.069168,-0.890091,1.048157,0.246506,0.152069,0.016708,0.027725,-0.000870,0.002014,0.000122,
-0.000061,-0.001297,0.002884,-0.020691,0.009232,-0.140671,-0.088776,-0.913055,1.031937,0.221985,0.151962,0.012421,0.026840,-0.001266,0.001907,0.000107,
-0.000061,-0.001389,0.002701,-0.021790,0.006134,-0.143677,-0.109161,-0.935196,1.014618,0.198059,0.151306,0.008316,0.025909,-0.001617,0.001785,0.000107,
-0.000076,-0.001480,0.002487,-0.022858,0.002823,-0.146255,-0.130310,-0.956482,0.996246,0.174789,0.150116,0.004395,0.024933,-0.001938,0.001694,0.000092,
-0.000076,-0.001587,0.002228,-0.023911,-0.000687,-0.148422,-0.152206,-0.976852,0.976852,0.152206,0.148422,0.000687,0.023911,-0.002228,0.001587,0.000076,
-0.000092,-0.001694,0.001938,-0.024933,-0.004395,-0.150116,-0.174789,-0.996246,0.956482,0.130310,0.146255,-0.002823,0.022858,-0.002487,0.001480,0.000076,
-0.000107,-0.001785,0.001617,-0.025909,-0.008316,-0.151306,-0.198059,-1.014618,0.935196,0.109161,0.143677,-0.006134,0.021790,-0.002701,0.001389,0.000061,
-0.000107,-0.001907,0.001266,-0.026840,-0.012421,-0.151962,-0.221985,-1.031937,0.913055,0.088776,0.140671,-0.009232,0.020691,-0.002884,0.001297,0.000061,
-0.000122,-0.002014,0.000870,-0.027725,-0.016708,-0.152069,-0.246506,-1.048157,0.890091,0.069168,0.137299,-0.012115,0.019577,-0.003052,0.001205,0.000046,
-0.000137,-0.002121,0.000443,-0.028534,-0.021179,-0.151596,-0.271591,-1.063217,0.866364,0.050354,0.133591,-0.014801,0.018463,-0.003174,0.001114,0.000046,
-0.000153,-0.002243,-0.000031,-0.029282,-0.025818,-0.150497,-0.297211,-1.077118,0.841949,0.032379,0.129562,-0.017258,0.017349,-0.003281,0.001038,0.000031,
-0.000168,-0.002350,-0.000549,-0.029938,-0.030609,-0.148773,-0.323318,-1.089783,0.816864,0.015228,0.125259,-0.019531,0.016235,-0.003372,0.000961,0.000031,
-0.000198,-0.002457,-0.001099,-0.030533,-0.035553,-0.146362,-0.349869,-1.101212,0.791214,-0.001068,0.120697,-0.021576,0.015121,-0.003418,0.000885,0.000031,
-0.000214,-0.002579,-0.001694,-0.031006,-0.040634,-0.143265,-0.376801,-1.111374,0.765030,-0.016510,0.115921,-0.023422,0.014023,-0.003464,0.000809,0.000031,
-0.000244,-0.002686,-0.002335,-0.031387,-0.045837,-0.139450,-0.404083,-1.120224,0.738373,-0.031082,0.110947,-0.025085,0.012939,-0.003479,0.000748,0.000015,
-0.000259,-0.002792,-0.003006,-0.031662,-0.051132,-0.134888,-0.431656,-1.127747,0.711319,-0.044785,0.105820,-0.026535,0.011887,-0.003479,0.000687,0.000015,
-0.000290,-0.002899,-0.003723,-0.031815,-0.056534,-0.129578,-0.459473,-1.133926,0.683914,-0.057617,0.100540,-0.027802,0.010849,-0.003464,0.000626,0.000015,
-0.000320,-0.002991,-0.004486,-0.031845,-0.061996,-0.123474,-0.487473,-1.138763,0.656219,-0.069595,0.095169,-0.028885,0.009842,-0.003433,0.000580,0.000015,
-0.000366,-0.003082,-0.005295,-0.031738,-0.067520,-0.116577,-0.515610,-1.142212,0.628296,-0.080688,0.089706,-0.029785,0.008865,-0.003387,0.000534,0.000015,
-0.000397,-0.003174,-0.006119,-0.031479,-0.073059,-0.108856,-0.543823,-1.144287,0.600220,-0.090927,0.084183,-0.030518,0.007919,-0.003326,0.000473,0.000015,
};
// filterTbl[i][j] = cos(((M_PI/64*i+M_PI/4)*(2*j+1)));
static const double s_jo_filterTbl[64][32] = {
{0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,
0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107},
{0.671559,-0.803208,-0.514103,0.903989,0.336890,-0.970031,-0.146730,0.998795,-0.049068,-0.989177,0.242980,0.941544,-0.427555,-0.857729,0.595699,0.740951,
-0.740951,-0.595699,0.857729,0.427555,-0.941544,-0.242980,0.989177,0.049068,-0.998795,0.146730,0.970031,-0.336890,-0.903989,0.514103,0.803208,-0.671559},
{0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393,
-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393},
{0.595699,-0.941544,-0.049068,0.970031,-0.514103,-0.671559,0.903989,0.146730,-0.989177,0.427555,0.740951,-0.857729,-0.242980,0.998795,-0.336890,-0.803208,
0.803208,0.336890,-0.998795,0.242980,0.857729,-0.740951,-0.427555,0.989177,-0.146730,-0.903989,0.671559,0.514103,-0.970031,0.049068,0.941544,-0.595699},
{0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,
0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570},
{0.514103,-0.998795,0.427555,0.595699,-0.989177,0.336890,0.671559,-0.970031,0.242980,0.740951,-0.941544,0.146730,0.803208,-0.903989,0.049068,0.857729,
-0.857729,-0.049068,0.903989,-0.803208,-0.146730,0.941544,-0.740951,-0.242980,0.970031,-0.671559,-0.336890,0.989177,-0.595699,-0.427555,0.998795,-0.514103},
{0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397,
-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397},
{0.427555,-0.970031,0.803208,-0.049068,-0.740951,0.989177,-0.514103,-0.336890,0.941544,-0.857729,0.146730,0.671559,-0.998795,0.595699,0.242980,-0.903989,
0.903989,-0.242980,-0.595699,0.998795,-0.671559,-0.146730,0.857729,-0.941544,0.336890,0.514103,-0.989177,0.740951,0.049068,-0.803208,0.970031,-0.427555},
{0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,
0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683},
{0.336890,-0.857729,0.989177,-0.671559,0.049068,0.595699,-0.970031,0.903989,-0.427555,-0.242980,0.803208,-0.998795,0.740951,-0.146730,-0.514103,0.941544,
-0.941544,0.514103,0.146730,-0.740951,0.998795,-0.803208,0.242980,0.427555,-0.903989,0.970031,-0.595699,-0.049068,0.671559,-0.989177,0.857729,-0.336890},
{0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285,
-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285},
{0.242980,-0.671559,0.941544,-0.989177,0.803208,-0.427555,-0.049068,0.514103,-0.857729,0.998795,-0.903989,0.595699,-0.146730,-0.336890,0.740951,-0.970031,
0.970031,-0.740951,0.336890,0.146730,-0.595699,0.903989,-0.998795,0.857729,-0.514103,0.049068,0.427555,-0.803208,0.989177,-0.941544,0.671559,-0.242980},
{0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,
0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090},
{0.146730,-0.427555,0.671559,-0.857729,0.970031,-0.998795,0.941544,-0.803208,0.595699,-0.336890,0.049068,0.242980,-0.514103,0.740951,-0.903989,0.989177,
-0.989177,0.903989,-0.740951,0.514103,-0.242980,-0.049068,0.336890,-0.595699,0.803208,-0.941544,0.998795,-0.970031,0.857729,-0.671559,0.427555,-0.146730},
{0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017,
-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017},
{0.049068,-0.146730,0.242980,-0.336890,0.427555,-0.514103,0.595699,-0.671559,0.740951,-0.803208,0.857729,-0.903989,0.941544,-0.970031,0.989177,-0.998795,
0.998795,-0.989177,0.970031,-0.941544,0.903989,-0.857729,0.803208,-0.740951,0.671559,-0.595699,0.514103,-0.427555,0.336890,-0.242980,0.146730,-0.049068},
{0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,
0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000},
{-0.049068,0.146730,-0.242980,0.336890,-0.427555,0.514103,-0.595699,0.671559,-0.740951,0.803208,-0.857729,0.903989,-0.941544,0.970031,-0.989177,0.998795,
-0.998795,0.989177,-0.970031,0.941544,-0.903989,0.857729,-0.803208,0.740951,-0.671559,0.595699,-0.514103,0.427555,-0.336890,0.242980,-0.146730,0.049068},
{-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017,
0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017},
{-0.146730,0.427555,-0.671559,0.857729,-0.970031,0.998795,-0.941544,0.803208,-0.595699,0.336890,-0.049068,-0.242980,0.514103,-0.740951,0.903989,-0.989177,
0.989177,-0.903989,0.740951,-0.514103,0.242980,0.049068,-0.336890,0.595699,-0.803208,0.941544,-0.998795,0.970031,-0.857729,0.671559,-0.427555,0.146730},
{-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,
-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090},
{-0.242980,0.671559,-0.941544,0.989177,-0.803208,0.427555,0.049068,-0.514103,0.857729,-0.998795,0.903989,-0.595699,0.146730,0.336890,-0.740951,0.970031,
-0.970031,0.740951,-0.336890,-0.146730,0.595699,-0.903989,0.998795,-0.857729,0.514103,-0.049068,-0.427555,0.803208,-0.989177,0.941544,-0.671559,0.242980},
{-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285,
0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285},
{-0.336890,0.857729,-0.989177,0.671559,-0.049068,-0.595699,0.970031,-0.903989,0.427555,0.242980,-0.803208,0.998795,-0.740951,0.146730,0.514103,-0.941544,
0.941544,-0.514103,-0.146730,0.740951,-0.998795,0.803208,-0.242980,-0.427555,0.903989,-0.970031,0.595699,0.049068,-0.671559,0.989177,-0.857729,0.336890},
{-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,
-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683},
{-0.427555,0.970031,-0.803208,0.049068,0.740951,-0.989177,0.514103,0.336890,-0.941544,0.857729,-0.146730,-0.671559,0.998795,-0.595699,-0.242980,0.903989,
-0.903989,0.242980,0.595699,-0.998795,0.671559,0.146730,-0.857729,0.941544,-0.336890,-0.514103,0.989177,-0.740951,-0.049068,0.803208,-0.970031,0.427555},
{-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397,
0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397},
{-0.514103,0.998795,-0.427555,-0.595699,0.989177,-0.336890,-0.671559,0.970031,-0.242980,-0.740951,0.941544,-0.146730,-0.803208,0.903989,-0.049068,-0.857729,
0.857729,0.049068,-0.903989,0.803208,0.146730,-0.941544,0.740951,0.242980,-0.970031,0.671559,0.336890,-0.989177,0.595699,0.427555,-0.998795,0.514103},
{-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,
-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570},
{-0.595699,0.941544,0.049068,-0.970031,0.514103,0.671559,-0.903989,-0.146730,0.989177,-0.427555,-0.740951,0.857729,0.242980,-0.998795,0.336890,0.803208,
-0.803208,-0.336890,0.998795,-0.242980,-0.857729,0.740951,0.427555,-0.989177,0.146730,0.903989,-0.671559,-0.514103,0.970031,-0.049068,-0.941544,0.595699},
{-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393,
0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393},
{-0.671559,0.803208,0.514103,-0.903989,-0.336890,0.970031,0.146730,-0.998795,0.049068,0.989177,-0.242980,-0.941544,0.427555,0.857729,-0.595699,-0.740951,
0.740951,0.595699,-0.857729,-0.427555,0.941544,0.242980,-0.989177,-0.049068,0.998795,-0.146730,-0.970031,0.336890,0.903989,-0.514103,-0.803208,0.671559},
{-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,
-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107},
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951},
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
{-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,
-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000},
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951}
};
// up to 32-bits
static unsigned jo_readBits(const unsigned char *data, int *at, int num) {
unsigned r = 0;
// read partial starting bits
int sc = (8 - (*at & 7)) & 7;
sc = sc > num ? num : sc;
if(sc) {
r = (data[*at/8] >> (8 - (*at&7) - sc)) & ((1<<sc)-1);
num -= sc;
*at += sc;
}
// read full bytes
while(num>=8) {
r <<= 8;
r |= data[*at/8];
*at += 8;
num -= 8;
}
// read partial ending bits
if(num) {
r <<= num;
r |= (data[*at/8] >> (8 - num)) & ((1<<num)-1);
*at += num;
}
return r;
}
bool jo_read_mp1(const void *input, int inputSize, short **output_, int *outputSize_, int *hz_, int *channels_) {
int outputSize = 0, hz, channels;
short *output = 0;
int outputMax = 0;
const unsigned char *data = (const unsigned char *)input;
// Buffers for the lapped transform
double buf[2][2 * 512] = { 0 };
int bufOffset[2] = { 64,64 };
int at = 0; // Where in the stream are we?
while (at < inputSize * 8) {
// Sync markers are byte aligned
at = (at + 7)&-8;
while (at < inputSize * 8 - 32) {
if (data[at / 8] == 0xFF && (data[at / 8 + 1] & 0xF0) == 0xF0) {
break;
}
else {
at += 8;
}
}
if (at >= inputSize * 8 - 32) {
break;
}
unsigned header = jo_readBits(data, &at, 32);
//printf("header: %x.%x/%x: %08x\n", (at-32)/8, (at-32)&7, inputSize, header);
// sync = 0xFFF
// ID = 1
// layer = 3 (layer 1)
if ((header & 0xFFFE0000) != 0xFFFE0000) {
return false;
}
static const int bitrateTbl[16] = { 0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1 };
int kbps = bitrateTbl[(header >> 12) & 15];
if (kbps < 0) {
return false;
}
static const int hzTbl[4] = { 44100, 48000, 32000, 0 };
hz = hzTbl[(header >> 10) & 3];
if (!hz) {
return false;
}
// mode 0 = stereo
// mode 1 = joint stereo
// mode 2 = dual channel (no idea what this does TODO)
// mode 3 = mono
int mode = (header >> 6) & 3;
int modeExt = (header >> 4) & 3;
channels = mode == 3 ? 1 : 2;
const int bound = mode == 1 ? (modeExt + 1) * 4 : 32;
bool errorProtection = ((header >> 16) & 1) ^ 1; //< @r-lyeh extra parens
if (errorProtection) {
at += 16; // skip the CRC.
}
// Read bit allocations
int bitAlloc[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
bitAlloc[i][ch] = jo_readBits(data, &at, 4);
}
}
for (int i = bound; i < 32; ++i) {
bitAlloc[i][1] = bitAlloc[i][0] = jo_readBits(data, &at, 4);
}
// Read scale indexes
int scaleIdx[32][2];
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
scaleIdx[i][ch] = bitAlloc[i][ch] ? jo_readBits(data, &at, 6) : 63;
}
}
// Read & compute output samples
short pcm[12][2][32];
for (int s = 0; s < 12; ++s) {
// Read normalized, quantized band samples
int samples[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
if (bitAlloc[i][ch]) {
samples[i][ch] = jo_readBits(data, &at, bitAlloc[i][ch] + 1);
}
}
}
for (int i = bound; i < 32; ++i) {
if (bitAlloc[i][0]) {
samples[i][1] = samples[i][0] = jo_readBits(data, &at, bitAlloc[i][0] + 1);
}
}
// Compute bands: Dequantize & Denormalize
double bandTbl[2][32] = { 0 };
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
int b = bitAlloc[i][ch];
if (b++) {
int samp = samples[i][ch];
double f = ((samp >> (b - 1)) & 1) ? 0 : -1;
f += (samp & ((1 << (b - 1)) - 1)) / (double)(1 << (b - 1));
f = (f + 1.0 / (1 << (b - 1))) * (1 << b) / ((1 << b) - 1.0);
f *= s_jo_multTbl[scaleIdx[i][ch]];
bandTbl[ch][i] = f;
}
}
}
// Convert subbands to PCM
for (int ch = 0; ch < channels; ++ch) {
bufOffset[ch] = (bufOffset[ch] + 0x3C0) & 0x3ff;
double *bufOffsetPtr = buf[ch] + bufOffset[ch];
const double *f = s_jo_filterTbl[0];
for (int i = 0; i < 64; ++i) {
double sum = 0;
for (int j = 0; j < 32; ++j) {
sum += *f++ * bandTbl[ch][j];
}
bufOffsetPtr[i] = sum;
}
const double *w = s_jo_windowTbl;
for (int i = 0; i < 32; ++i) {
double sum = 0;
for (int j = 0; j < 16; ++j) {
int k = i | (j + (j + 1 & -2)) << 5;
sum += *w++ * buf[ch][(k + bufOffset[ch]) & 0x3ff];
}
int ss = (int)(sum * 0x8000);
ss = ss > SHRT_MAX ? SHRT_MAX : ss < SHRT_MIN ? SHRT_MIN : ss;
pcm[s][ch][i] = ss;
}
}
}
if (at > inputSize * 8) {
printf("file corruption?\n");
return false;
}
if (outputMax == 0) {
// estimate total number of samples (may be totally wrong, but its better than nothing)
at = (at + 7)&-8;
outputMax = inputSize / (at / 8) * 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
if (outputSize * sizeof(*output) + 384 * channels * sizeof(*output) > outputMax) {
outputMax += 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
for (int i = 0; i < 12; ++i) {
for (int j = 0; j < 32; ++j) {
for (int k = 0; k < channels; ++k) {
output[outputSize++] = pcm[i][k][j];
}
}
}
}
*outputSize_ = outputSize;
*hz_ = hz;
*channels_ = channels;
*output_ = output;
return outputSize && hz && channels && output;
}
#endif // JO_MP1_HEADER_FILE_ONLY
/* public domain Simple, Minimalistic MPEG Layer 1 decoder - http://jonolick.com
*
* Revision History:
* 1.00 (2014-26-1) Initial release.
*
* Basic usage:
* int hz, channels, outputSize;
* short *output;
* jo_read_mp1(input, inputSize, output, outputSize, hz, channels);
* // Do something with the data here
* free(output);
*
* */
#ifndef JO_INCLUDE_MP1_H
#define JO_INCLUDE_MP1_H
#include <stdbool.h>
extern bool jo_read_mp1(const void *input, int inputSize, short **output, int *outputSize, int *hz, int *channels);
#endif // JO_INCLUDE_MP1_H
#ifndef JO_MP1_HEADER_FILE_ONLY
#if defined(_MSC_VER) && _MSC_VER >= 0x1400
#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen()
#endif
#include <stdlib.h>
#include <math.h>
#include <limits.h>
static const double s_jo_multTbl[64] = {
2.000000,1.587401,1.259921,1.000000,0.793701,0.629961,0.500000,0.396850,0.314980,0.250000,0.198425,0.157490,0.125000,0.099213,0.078745,0.062500,
0.049606,0.039373,0.031250,0.024803,0.019686,0.015625,0.012402,0.009843,0.007812,0.006201,0.004922,0.003906,0.003100,0.002461,0.001953,0.001550,
0.001230,0.000977,0.000775,0.000615,0.000488,0.000388,0.000308,0.000244,0.000194,0.000154,0.000122,0.000097,0.000077,0.000061,0.000048,0.000038,
0.000031,0.000024,0.000019,0.000015,0.000012,0.000010,0.000008,0.000006,0.000005,0.000004,0.000003,0.000002,0.000002,0.000002,0.000001,1e-20
};
// l = i - 256;
// s = (i & 0x40) ? 1 : -1;
// windowTbl[(i/16)|((i%16)<<5)] = s * 20 * exp(-(l/112)*-(l/112)) * sin(l * M_PI*2 / 112) / l;
static const double s_jo_windowTbl[512] = {
-0.000000,-0.000443,0.003250,-0.007004,0.031082,-0.078629,0.100311,-0.572037,1.144989,0.572037,0.100311,0.078629,0.031082,0.007004,0.003250,0.000443,
-0.000015,-0.000473,0.003326,-0.007919,0.030518,-0.084183,0.090927,-0.600220,1.144287,0.543823,0.108856,0.073059,0.031479,0.006119,0.003174,0.000397,
-0.000015,-0.000534,0.003387,-0.008865,0.029785,-0.089706,0.080688,-0.628296,1.142212,0.515610,0.116577,0.067520,0.031738,0.005295,0.003082,0.000366,
-0.000015,-0.000580,0.003433,-0.009842,0.028885,-0.095169,0.069595,-0.656219,1.138763,0.487473,0.123474,0.061996,0.031845,0.004486,0.002991,0.000320,
-0.000015,-0.000626,0.003464,-0.010849,0.027802,-0.100540,0.057617,-0.683914,1.133926,0.459473,0.129578,0.056534,0.031815,0.003723,0.002899,0.000290,
-0.000015,-0.000687,0.003479,-0.011887,0.026535,-0.105820,0.044785,-0.711319,1.127747,0.431656,0.134888,0.051132,0.031662,0.003006,0.002792,0.000259,
-0.000015,-0.000748,0.003479,-0.012939,0.025085,-0.110947,0.031082,-0.738373,1.120224,0.404083,0.139450,0.045837,0.031387,0.002335,0.002686,0.000244,
-0.000031,-0.000809,0.003464,-0.014023,0.023422,-0.115921,0.016510,-0.765030,1.111374,0.376801,0.143265,0.040634,0.031006,0.001694,0.002579,0.000214,
-0.000031,-0.000885,0.003418,-0.015121,0.021576,-0.120697,0.001068,-0.791214,1.101212,0.349869,0.146362,0.035553,0.030533,0.001099,0.002457,0.000198,
-0.000031,-0.000961,0.003372,-0.016235,0.019531,-0.125259,-0.015228,-0.816864,1.089783,0.323318,0.148773,0.030609,0.029938,0.000549,0.002350,0.000168,
-0.000031,-0.001038,0.003281,-0.017349,0.017258,-0.129562,-0.032379,-0.841949,1.077118,0.297211,0.150497,0.025818,0.029282,0.000031,0.002243,0.000153,
-0.000046,-0.001114,0.003174,-0.018463,0.014801,-0.133591,-0.050354,-0.866364,1.063217,0.271591,0.151596,0.021179,0.028534,-0.000443,0.002121,0.000137,
-0.000046,-0.001205,0.003052,-0.019577,0.012115,-0.137299,-0.069168,-0.890091,1.048157,0.246506,0.152069,0.016708,0.027725,-0.000870,0.002014,0.000122,
-0.000061,-0.001297,0.002884,-0.020691,0.009232,-0.140671,-0.088776,-0.913055,1.031937,0.221985,0.151962,0.012421,0.026840,-0.001266,0.001907,0.000107,
-0.000061,-0.001389,0.002701,-0.021790,0.006134,-0.143677,-0.109161,-0.935196,1.014618,0.198059,0.151306,0.008316,0.025909,-0.001617,0.001785,0.000107,
-0.000076,-0.001480,0.002487,-0.022858,0.002823,-0.146255,-0.130310,-0.956482,0.996246,0.174789,0.150116,0.004395,0.024933,-0.001938,0.001694,0.000092,
-0.000076,-0.001587,0.002228,-0.023911,-0.000687,-0.148422,-0.152206,-0.976852,0.976852,0.152206,0.148422,0.000687,0.023911,-0.002228,0.001587,0.000076,
-0.000092,-0.001694,0.001938,-0.024933,-0.004395,-0.150116,-0.174789,-0.996246,0.956482,0.130310,0.146255,-0.002823,0.022858,-0.002487,0.001480,0.000076,
-0.000107,-0.001785,0.001617,-0.025909,-0.008316,-0.151306,-0.198059,-1.014618,0.935196,0.109161,0.143677,-0.006134,0.021790,-0.002701,0.001389,0.000061,
-0.000107,-0.001907,0.001266,-0.026840,-0.012421,-0.151962,-0.221985,-1.031937,0.913055,0.088776,0.140671,-0.009232,0.020691,-0.002884,0.001297,0.000061,
-0.000122,-0.002014,0.000870,-0.027725,-0.016708,-0.152069,-0.246506,-1.048157,0.890091,0.069168,0.137299,-0.012115,0.019577,-0.003052,0.001205,0.000046,
-0.000137,-0.002121,0.000443,-0.028534,-0.021179,-0.151596,-0.271591,-1.063217,0.866364,0.050354,0.133591,-0.014801,0.018463,-0.003174,0.001114,0.000046,
-0.000153,-0.002243,-0.000031,-0.029282,-0.025818,-0.150497,-0.297211,-1.077118,0.841949,0.032379,0.129562,-0.017258,0.017349,-0.003281,0.001038,0.000031,
-0.000168,-0.002350,-0.000549,-0.029938,-0.030609,-0.148773,-0.323318,-1.089783,0.816864,0.015228,0.125259,-0.019531,0.016235,-0.003372,0.000961,0.000031,
-0.000198,-0.002457,-0.001099,-0.030533,-0.035553,-0.146362,-0.349869,-1.101212,0.791214,-0.001068,0.120697,-0.021576,0.015121,-0.003418,0.000885,0.000031,
-0.000214,-0.002579,-0.001694,-0.031006,-0.040634,-0.143265,-0.376801,-1.111374,0.765030,-0.016510,0.115921,-0.023422,0.014023,-0.003464,0.000809,0.000031,
-0.000244,-0.002686,-0.002335,-0.031387,-0.045837,-0.139450,-0.404083,-1.120224,0.738373,-0.031082,0.110947,-0.025085,0.012939,-0.003479,0.000748,0.000015,
-0.000259,-0.002792,-0.003006,-0.031662,-0.051132,-0.134888,-0.431656,-1.127747,0.711319,-0.044785,0.105820,-0.026535,0.011887,-0.003479,0.000687,0.000015,
-0.000290,-0.002899,-0.003723,-0.031815,-0.056534,-0.129578,-0.459473,-1.133926,0.683914,-0.057617,0.100540,-0.027802,0.010849,-0.003464,0.000626,0.000015,
-0.000320,-0.002991,-0.004486,-0.031845,-0.061996,-0.123474,-0.487473,-1.138763,0.656219,-0.069595,0.095169,-0.028885,0.009842,-0.003433,0.000580,0.000015,
-0.000366,-0.003082,-0.005295,-0.031738,-0.067520,-0.116577,-0.515610,-1.142212,0.628296,-0.080688,0.089706,-0.029785,0.008865,-0.003387,0.000534,0.000015,
-0.000397,-0.003174,-0.006119,-0.031479,-0.073059,-0.108856,-0.543823,-1.144287,0.600220,-0.090927,0.084183,-0.030518,0.007919,-0.003326,0.000473,0.000015,
};
// filterTbl[i][j] = cos(((M_PI/64*i+M_PI/4)*(2*j+1)));
static const double s_jo_filterTbl[64][32] = {
{0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,
0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107},
{0.671559,-0.803208,-0.514103,0.903989,0.336890,-0.970031,-0.146730,0.998795,-0.049068,-0.989177,0.242980,0.941544,-0.427555,-0.857729,0.595699,0.740951,
-0.740951,-0.595699,0.857729,0.427555,-0.941544,-0.242980,0.989177,0.049068,-0.998795,0.146730,0.970031,-0.336890,-0.903989,0.514103,0.803208,-0.671559},
{0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393,
-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393},
{0.595699,-0.941544,-0.049068,0.970031,-0.514103,-0.671559,0.903989,0.146730,-0.989177,0.427555,0.740951,-0.857729,-0.242980,0.998795,-0.336890,-0.803208,
0.803208,0.336890,-0.998795,0.242980,0.857729,-0.740951,-0.427555,0.989177,-0.146730,-0.903989,0.671559,0.514103,-0.970031,0.049068,0.941544,-0.595699},
{0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,
0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570},
{0.514103,-0.998795,0.427555,0.595699,-0.989177,0.336890,0.671559,-0.970031,0.242980,0.740951,-0.941544,0.146730,0.803208,-0.903989,0.049068,0.857729,
-0.857729,-0.049068,0.903989,-0.803208,-0.146730,0.941544,-0.740951,-0.242980,0.970031,-0.671559,-0.336890,0.989177,-0.595699,-0.427555,0.998795,-0.514103},
{0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397,
-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397},
{0.427555,-0.970031,0.803208,-0.049068,-0.740951,0.989177,-0.514103,-0.336890,0.941544,-0.857729,0.146730,0.671559,-0.998795,0.595699,0.242980,-0.903989,
0.903989,-0.242980,-0.595699,0.998795,-0.671559,-0.146730,0.857729,-0.941544,0.336890,0.514103,-0.989177,0.740951,0.049068,-0.803208,0.970031,-0.427555},
{0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,
0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683},
{0.336890,-0.857729,0.989177,-0.671559,0.049068,0.595699,-0.970031,0.903989,-0.427555,-0.242980,0.803208,-0.998795,0.740951,-0.146730,-0.514103,0.941544,
-0.941544,0.514103,0.146730,-0.740951,0.998795,-0.803208,0.242980,0.427555,-0.903989,0.970031,-0.595699,-0.049068,0.671559,-0.989177,0.857729,-0.336890},
{0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285,
-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285},
{0.242980,-0.671559,0.941544,-0.989177,0.803208,-0.427555,-0.049068,0.514103,-0.857729,0.998795,-0.903989,0.595699,-0.146730,-0.336890,0.740951,-0.970031,
0.970031,-0.740951,0.336890,0.146730,-0.595699,0.903989,-0.998795,0.857729,-0.514103,0.049068,0.427555,-0.803208,0.989177,-0.941544,0.671559,-0.242980},
{0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,
0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090},
{0.146730,-0.427555,0.671559,-0.857729,0.970031,-0.998795,0.941544,-0.803208,0.595699,-0.336890,0.049068,0.242980,-0.514103,0.740951,-0.903989,0.989177,
-0.989177,0.903989,-0.740951,0.514103,-0.242980,-0.049068,0.336890,-0.595699,0.803208,-0.941544,0.998795,-0.970031,0.857729,-0.671559,0.427555,-0.146730},
{0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017,
-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017},
{0.049068,-0.146730,0.242980,-0.336890,0.427555,-0.514103,0.595699,-0.671559,0.740951,-0.803208,0.857729,-0.903989,0.941544,-0.970031,0.989177,-0.998795,
0.998795,-0.989177,0.970031,-0.941544,0.903989,-0.857729,0.803208,-0.740951,0.671559,-0.595699,0.514103,-0.427555,0.336890,-0.242980,0.146730,-0.049068},
{0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,
0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000},
{-0.049068,0.146730,-0.242980,0.336890,-0.427555,0.514103,-0.595699,0.671559,-0.740951,0.803208,-0.857729,0.903989,-0.941544,0.970031,-0.989177,0.998795,
-0.998795,0.989177,-0.970031,0.941544,-0.903989,0.857729,-0.803208,0.740951,-0.671559,0.595699,-0.514103,0.427555,-0.336890,0.242980,-0.146730,0.049068},
{-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017,
0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017},
{-0.146730,0.427555,-0.671559,0.857729,-0.970031,0.998795,-0.941544,0.803208,-0.595699,0.336890,-0.049068,-0.242980,0.514103,-0.740951,0.903989,-0.989177,
0.989177,-0.903989,0.740951,-0.514103,0.242980,0.049068,-0.336890,0.595699,-0.803208,0.941544,-0.998795,0.970031,-0.857729,0.671559,-0.427555,0.146730},
{-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,
-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090},
{-0.242980,0.671559,-0.941544,0.989177,-0.803208,0.427555,0.049068,-0.514103,0.857729,-0.998795,0.903989,-0.595699,0.146730,0.336890,-0.740951,0.970031,
-0.970031,0.740951,-0.336890,-0.146730,0.595699,-0.903989,0.998795,-0.857729,0.514103,-0.049068,-0.427555,0.803208,-0.989177,0.941544,-0.671559,0.242980},
{-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285,
0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285},
{-0.336890,0.857729,-0.989177,0.671559,-0.049068,-0.595699,0.970031,-0.903989,0.427555,0.242980,-0.803208,0.998795,-0.740951,0.146730,0.514103,-0.941544,
0.941544,-0.514103,-0.146730,0.740951,-0.998795,0.803208,-0.242980,-0.427555,0.903989,-0.970031,0.595699,0.049068,-0.671559,0.989177,-0.857729,0.336890},
{-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,
-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683},
{-0.427555,0.970031,-0.803208,0.049068,0.740951,-0.989177,0.514103,0.336890,-0.941544,0.857729,-0.146730,-0.671559,0.998795,-0.595699,-0.242980,0.903989,
-0.903989,0.242980,0.595699,-0.998795,0.671559,0.146730,-0.857729,0.941544,-0.336890,-0.514103,0.989177,-0.740951,-0.049068,0.803208,-0.970031,0.427555},
{-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397,
0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397},
{-0.514103,0.998795,-0.427555,-0.595699,0.989177,-0.336890,-0.671559,0.970031,-0.242980,-0.740951,0.941544,-0.146730,-0.803208,0.903989,-0.049068,-0.857729,
0.857729,0.049068,-0.903989,0.803208,0.146730,-0.941544,0.740951,0.242980,-0.970031,0.671559,0.336890,-0.989177,0.595699,0.427555,-0.998795,0.514103},
{-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,
-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570},
{-0.595699,0.941544,0.049068,-0.970031,0.514103,0.671559,-0.903989,-0.146730,0.989177,-0.427555,-0.740951,0.857729,0.242980,-0.998795,0.336890,0.803208,
-0.803208,-0.336890,0.998795,-0.242980,-0.857729,0.740951,0.427555,-0.989177,0.146730,0.903989,-0.671559,-0.514103,0.970031,-0.049068,-0.941544,0.595699},
{-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393,
0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393},
{-0.671559,0.803208,0.514103,-0.903989,-0.336890,0.970031,0.146730,-0.998795,0.049068,0.989177,-0.242980,-0.941544,0.427555,0.857729,-0.595699,-0.740951,
0.740951,0.595699,-0.857729,-0.427555,0.941544,0.242980,-0.989177,-0.049068,0.998795,-0.146730,-0.970031,0.336890,0.903989,-0.514103,-0.803208,0.671559},
{-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,
-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107},
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951},
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
{-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,
-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000},
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951}
};
// up to 32-bits
static unsigned jo_readBits(const unsigned char *data, int *at, int num) {
unsigned r = 0;
// read partial starting bits
int sc = (8 - (*at & 7)) & 7;
sc = sc > num ? num : sc;
if(sc) {
r = (data[*at/8] >> (8 - (*at&7) - sc)) & ((1<<sc)-1);
num -= sc;
*at += sc;
}
// read full bytes
while(num>=8) {
r <<= 8;
r |= data[*at/8];
*at += 8;
num -= 8;
}
// read partial ending bits
if(num) {
r <<= num;
r |= (data[*at/8] >> (8 - num)) & ((1<<num)-1);
*at += num;
}
return r;
}
bool jo_read_mp1(const void *input, int inputSize, short **output_, int *outputSize_, int *hz_, int *channels_) {
int outputSize = 0, hz, channels;
short *output = 0;
int outputMax = 0;
const unsigned char *data = (const unsigned char *)input;
// Buffers for the lapped transform
double buf[2][2 * 512] = { 0 };
int bufOffset[2] = { 64,64 };
int at = 0; // Where in the stream are we?
while (at < inputSize * 8) {
// Sync markers are byte aligned
at = (at + 7)&-8;
while (at < inputSize * 8 - 32) {
if (data[at / 8] == 0xFF && (data[at / 8 + 1] & 0xF0) == 0xF0) {
break;
}
else {
at += 8;
}
}
if (at >= inputSize * 8 - 32) {
break;
}
unsigned header = jo_readBits(data, &at, 32);
//printf("header: %x.%x/%x: %08x\n", (at-32)/8, (at-32)&7, inputSize, header);
// sync = 0xFFF
// ID = 1
// layer = 3 (layer 1)
if ((header & 0xFFFE0000) != 0xFFFE0000) {
return false;
}
static const int bitrateTbl[16] = { 0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1 };
int kbps = bitrateTbl[(header >> 12) & 15];
if (kbps < 0) {
return false;
}
static const int hzTbl[4] = { 44100, 48000, 32000, 0 };
hz = hzTbl[(header >> 10) & 3];
if (!hz) {
return false;
}
// mode 0 = stereo
// mode 1 = joint stereo
// mode 2 = dual channel (no idea what this does TODO)
// mode 3 = mono
int mode = (header >> 6) & 3;
int modeExt = (header >> 4) & 3;
channels = mode == 3 ? 1 : 2;
const int bound = mode == 1 ? (modeExt + 1) * 4 : 32;
bool errorProtection = ((header >> 16) & 1) ^ 1; //< @r-lyeh extra parens
if (errorProtection) {
at += 16; // skip the CRC.
}
// Read bit allocations
int bitAlloc[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
bitAlloc[i][ch] = jo_readBits(data, &at, 4);
}
}
for (int i = bound; i < 32; ++i) {
bitAlloc[i][1] = bitAlloc[i][0] = jo_readBits(data, &at, 4);
}
// Read scale indexes
int scaleIdx[32][2];
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
scaleIdx[i][ch] = bitAlloc[i][ch] ? jo_readBits(data, &at, 6) : 63;
}
}
// Read & compute output samples
short pcm[12][2][32];
for (int s = 0; s < 12; ++s) {
// Read normalized, quantized band samples
int samples[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
if (bitAlloc[i][ch]) {
samples[i][ch] = jo_readBits(data, &at, bitAlloc[i][ch] + 1);
}
}
}
for (int i = bound; i < 32; ++i) {
if (bitAlloc[i][0]) {
samples[i][1] = samples[i][0] = jo_readBits(data, &at, bitAlloc[i][0] + 1);
}
}
// Compute bands: Dequantize & Denormalize
double bandTbl[2][32] = { 0 };
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
int b = bitAlloc[i][ch];
if (b++) {
int samp = samples[i][ch];
double f = ((samp >> (b - 1)) & 1) ? 0 : -1;
f += (samp & ((1 << (b - 1)) - 1)) / (double)(1 << (b - 1));
f = (f + 1.0 / (1 << (b - 1))) * (1 << b) / ((1 << b) - 1.0);
f *= s_jo_multTbl[scaleIdx[i][ch]];
bandTbl[ch][i] = f;
}
}
}
// Convert subbands to PCM
for (int ch = 0; ch < channels; ++ch) {
bufOffset[ch] = (bufOffset[ch] + 0x3C0) & 0x3ff;
double *bufOffsetPtr = buf[ch] + bufOffset[ch];
const double *f = s_jo_filterTbl[0];
for (int i = 0; i < 64; ++i) {
double sum = 0;
for (int j = 0; j < 32; ++j) {
sum += *f++ * bandTbl[ch][j];
}
bufOffsetPtr[i] = sum;
}
const double *w = s_jo_windowTbl;
for (int i = 0; i < 32; ++i) {
double sum = 0;
for (int j = 0; j < 16; ++j) {
int k = i | (j + (j + 1 & -2)) << 5;
sum += *w++ * buf[ch][(k + bufOffset[ch]) & 0x3ff];
}
int ss = (int)(sum * 0x8000);
ss = ss > SHRT_MAX ? SHRT_MAX : ss < SHRT_MIN ? SHRT_MIN : ss;
pcm[s][ch][i] = ss;
}
}
}
if (at > inputSize * 8) {
printf("file corruption?\n");
return false;
}
if (outputMax == 0) {
// estimate total number of samples (may be totally wrong, but its better than nothing)
at = (at + 7)&-8;
outputMax = inputSize / (at / 8) * 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
if (outputSize * sizeof(*output) + 384 * channels * sizeof(*output) > outputMax) {
outputMax += 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
for (int i = 0; i < 12; ++i) {
for (int j = 0; j < 32; ++j) {
for (int k = 0; k < channels; ++k) {
output[outputSize++] = pcm[i][k][j];
}
}
}
}
*outputSize_ = outputSize;
*hz_ = hz;
*channels_ = channels;
*output_ = output;
return outputSize && hz && channels && output;
}
#endif // JO_MP1_HEADER_FILE_ONLY

View File

@ -1,264 +1,264 @@
/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com
*
* Latest revisions:
* 1.02c rgbx -> bgrx channel swap && vertical image flip && userdef components (@r-lyeh)
* 1.02 (22-03-2017) Fixed AC encoding bug.
* Fixed color space bug (thx r- lyeh!)
* 1.01 (18-10-2016) warning fixes
* 1.00 (25-09-2016) initial release
*
* Basic usage:
* char *frame = new char[width*height*4]; // 4 component. bgrx format, where X is unused
* FILE *fp = fopen("foo.mpg", "wb");
* jo_write_mpeg(fp, frame, width, height, 60); // frame 0
* jo_write_mpeg(fp, frame, width, height, 60); // frame 1
* jo_write_mpeg(fp, frame, width, height, 60); // frame 2
* ...
* fclose(fp);
*
* Notes:
* Only supports 24, 25, 30, 50, or 60 fps
*
* I don't know if decoders support changing of fps, or dimensions for each frame.
* Movie players *should* support it as the spec allows it, but ...
*
* MPEG-1/2 currently has no active patents as far as I am aware.
*
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
* http://www.cs.cornell.edu/dali/api/mpegvideo-c.html
* */
#ifndef JO_INCLUDE_MPEG_H
#define JO_INCLUDE_MPEG_H
#include <stdio.h>
// To get a header file for this, either cut and paste the header,
// or create jo_mpeg.h, #define JO_MPEG_HEADER_FILE_ONLY, and
// then include jo_mpeg.c from it.
// Returns false on failure
extern void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps);
#endif // JO_INCLUDE_MPEG_H
#ifndef JO_MPEG_HEADER_FILE_ONLY
#ifndef JO_MPEG_COMPONENTS
#define JO_MPEG_COMPONENTS 4
#endif
#include <stdio.h>
#include <math.h>
#include <memory.h>
// Huffman tables
static const unsigned char s_jo_HTDC_Y[9][2] = {{4,3}, {0,2}, {1,2}, {5,3}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}};
static const unsigned char s_jo_HTDC_C[9][2] = {{0,2}, {1,2}, {2,2}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}, {254,8}};
static const unsigned char s_jo_HTAC[32][40][2] = {
{{6,3},{8,5},{10,6},{12,8},{76,9},{66,9},{20,11},{58,13},{48,13},{38,13},{32,13},{52,14},{50,14},{48,14},{46,14},{62,15},{62,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,15},{32,15},{48,16},{46,16},{44,16},{42,16},{40,16},{38,16},{36,16},{34,16},{32,16},},
{{6,4},{12,7},{74,9},{24,11},{54,13},{44,14},{42,14},{62,16},{60,16},{58,16},{56,16},{54,16},{52,16},{50,16},{38,17},{36,17},{34,17},{32,17}},
{{10,5},{8,8},{22,11},{40,13},{40,14}},
{{14,6},{72,9},{56,13},{38,14}},
{{12,6},{30,11},{36,13}}, {{14,7},{18,11},{36,14}}, {{10,7},{60,13},{40,17}},
{{8,7},{42,13}}, {{14,8},{34,13}}, {{10,8},{34,14}}, {{78,9},{32,14}}, {{70,9},{52,17}}, {{68,9},{50,17}}, {{64,9},{48,17}}, {{28,11},{46,17}}, {{26,11},{44,17}}, {{16,11},{42,17}},
{{62,13}}, {{52,13}}, {{50,13}}, {{46,13}}, {{44,13}}, {{62,14}}, {{60,14}}, {{58,14}}, {{56,14}}, {{54,14}}, {{62,17}}, {{60,17}}, {{58,17}}, {{56,17}}, {{54,17}},
};
static const float s_jo_quantTbl[64] = {
0.015625f,0.005632f,0.005035f,0.004832f,0.004808f,0.005892f,0.007964f,0.013325f,
0.005632f,0.004061f,0.003135f,0.003193f,0.003338f,0.003955f,0.004898f,0.008828f,
0.005035f,0.003135f,0.002816f,0.003013f,0.003299f,0.003581f,0.005199f,0.009125f,
0.004832f,0.003484f,0.003129f,0.003348f,0.003666f,0.003979f,0.005309f,0.009632f,
0.005682f,0.003466f,0.003543f,0.003666f,0.003906f,0.004546f,0.005774f,0.009439f,
0.006119f,0.004248f,0.004199f,0.004228f,0.004546f,0.005062f,0.006124f,0.009942f,
0.008883f,0.006167f,0.006096f,0.005777f,0.006078f,0.006391f,0.007621f,0.012133f,
0.016780f,0.011263f,0.009907f,0.010139f,0.009849f,0.010297f,0.012133f,0.019785f,
};
static const unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
typedef struct {
FILE *fp;
int buf, cnt;
} jo_bits_t;
static void jo_writeBits(jo_bits_t *b, int value, int count) {
b->cnt += count;
b->buf |= value << (24 - b->cnt);
while(b->cnt >= 8) {
unsigned char c = (b->buf >> 16) & 255;
putc(c, b->fp);
b->buf <<= 8;
b->cnt -= 8;
}
}
static void jo_DCT(float *d0, float *d1, float *d2, float *d3, float *d4, float *d5, float *d6, float *d7) {
float tmp0 = *d0 + *d7;
float tmp7 = *d0 - *d7;
float tmp1 = *d1 + *d6;
float tmp6 = *d1 - *d6;
float tmp2 = *d2 + *d5;
float tmp5 = *d2 - *d5;
float tmp3 = *d3 + *d4;
float tmp4 = *d3 - *d4;
// Even part
float tmp10 = tmp0 + tmp3; // phase 2
float tmp13 = tmp0 - tmp3;
float tmp11 = tmp1 + tmp2;
float tmp12 = tmp1 - tmp2;
*d0 = tmp10 + tmp11; // phase 3
*d4 = tmp10 - tmp11;
float z1 = (tmp12 + tmp13) * 0.707106781f; // c4
*d2 = tmp13 + z1; // phase 5
*d6 = tmp13 - z1;
// Odd part
tmp10 = tmp4 + tmp5; // phase 2
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
// The rotator is modified from fig 4-8 to avoid extra negations.
float z5 = (tmp10 - tmp12) * 0.382683433f; // c6
float z2 = tmp10 * 0.541196100f + z5; // c2-c6
float z4 = tmp12 * 1.306562965f + z5; // c2+c6
float z3 = tmp11 * 0.707106781f; // c4
float z11 = tmp7 + z3; // phase 5
float z13 = tmp7 - z3;
*d5 = z13 + z2; // phase 6
*d3 = z13 - z2;
*d1 = z11 + z4;
*d7 = z11 - z4;
}
static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9][2], int DC) {
for(int dataOff=0; dataOff<64; dataOff+=8) {
jo_DCT(&A[dataOff], &A[dataOff+1], &A[dataOff+2], &A[dataOff+3], &A[dataOff+4], &A[dataOff+5], &A[dataOff+6], &A[dataOff+7]);
}
for(int dataOff=0; dataOff<8; ++dataOff) {
jo_DCT(&A[dataOff], &A[dataOff+8], &A[dataOff+16], &A[dataOff+24], &A[dataOff+32], &A[dataOff+40], &A[dataOff+48], &A[dataOff+56]);
}
int Q[64];
for(int i=0; i<64; ++i) {
float v = A[i]*s_jo_quantTbl[i];
Q[s_jo_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
}
DC = Q[0] - DC;
int aDC = DC < 0 ? -DC : DC;
int size = 0;
int tempval = aDC;
while(tempval) {
size++;
tempval >>= 1;
}
jo_writeBits(bits, htdc[size][0], htdc[size][1]);
if(DC < 0) aDC ^= (1 << size) - 1;
jo_writeBits(bits, aDC, size);
int endpos = 63;
for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ }
for(int i = 1; i <= endpos;) {
int run = 0;
while (Q[i]==0 && i<endpos) {
++run;
++i;
}
int AC = Q[i++];
int aAC = AC < 0 ? -AC : AC;
int code = 0, size = 0;
if (run<32 && aAC<=40) {
code = s_jo_HTAC[run][aAC-1][0];
size = s_jo_HTAC[run][aAC-1][1];
if (AC < 0) code += 1;
}
if(!size) {
jo_writeBits(bits, 1, 6);
jo_writeBits(bits, run, 6);
if (AC < -127) {
jo_writeBits(bits, 128, 8);
} else if(AC > 127) {
jo_writeBits(bits, 0, 8);
}
code = AC&255;
size = 8;
}
jo_writeBits(bits, code, size);
}
jo_writeBits(bits, 2, 2);
return Q[0];
}
void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps) {
int lastDCY = 128, lastDCCR = 128, lastDCCB = 128;
jo_bits_t bits = {fp};
// Sequence Header
fwrite("\x00\x00\x01\xB3", 4, 1, fp);
// 12 bits for width, height
putc((width>>4)&0xFF, fp);
putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp);
putc(height & 0xFF, fp);
// aspect ratio, framerate
if(fps <= 24) putc(0x12, fp);
else if(fps <= 25) putc(0x13, fp);
else if(fps <= 30) putc(0x15, fp);
else if(fps <= 50) putc(0x16, fp);
else putc(0x18, fp); // 60fps
fwrite("\xFF\xFF\xE0\xA0", 4, 1, fp);
fwrite("\x00\x00\x01\xB8\x80\x08\x00\x40", 8, 1, fp); // GOP header
fwrite("\x00\x00\x01\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header
fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header
jo_writeBits(&bits, 0x10, 6);
for (int vblock=0; vblock<(height+15)/16; vblock++) {
for (int hblock=0; hblock<(width+15)/16; hblock++) {
jo_writeBits(&bits, 3, 2);
float Y[256], CBx[256], CRx[256];
for (int i=0; i<256; ++i) {
int y = vblock*16+(i/16);
int x = hblock*16+(i&15);
x = x >= width ? width-1 : x;
y = y >= height ? height-1 : y;
int _4 = JO_MPEG_COMPONENTS;
// const unsigned char *c = bgrx + y*width*_4+x*_4; // original
const unsigned char *c = bgrx + ((height-1)-y)*width*_4+x*_4; // flipped
float b = c[0], g = c[1], r = c[2]; // channel swap
Y[i] = ( 0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16;
CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128;
CRx[i] = ( 0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
}
// Downsample Cb,Cr (420 format)
float CB[64], CR[64];
for (int i=0; i<64; ++i) {
int j =(i&7)*2 + (i&56)*4;
CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f;
CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f;
}
for (int k1=0; k1<2; ++k1) {
for (int k2=0; k2<2; ++k2) {
float block[64];
for (int i=0; i<64; i+=8) {
int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8;
memcpy(block+i, Y+j, 8*sizeof(Y[0]));
}
lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY);
}
}
lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB);
lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR);
}
}
jo_writeBits(&bits, 0, 7);
fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence
}
#endif
/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com
*
* Latest revisions:
* 1.02c rgbx -> bgrx channel swap && vertical image flip && userdef components (@r-lyeh)
* 1.02 (22-03-2017) Fixed AC encoding bug.
* Fixed color space bug (thx r- lyeh!)
* 1.01 (18-10-2016) warning fixes
* 1.00 (25-09-2016) initial release
*
* Basic usage:
* char *frame = new char[width*height*4]; // 4 component. bgrx format, where X is unused
* FILE *fp = fopen("foo.mpg", "wb");
* jo_write_mpeg(fp, frame, width, height, 60); // frame 0
* jo_write_mpeg(fp, frame, width, height, 60); // frame 1
* jo_write_mpeg(fp, frame, width, height, 60); // frame 2
* ...
* fclose(fp);
*
* Notes:
* Only supports 24, 25, 30, 50, or 60 fps
*
* I don't know if decoders support changing of fps, or dimensions for each frame.
* Movie players *should* support it as the spec allows it, but ...
*
* MPEG-1/2 currently has no active patents as far as I am aware.
*
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
* http://www.cs.cornell.edu/dali/api/mpegvideo-c.html
* */
#ifndef JO_INCLUDE_MPEG_H
#define JO_INCLUDE_MPEG_H
#include <stdio.h>
// To get a header file for this, either cut and paste the header,
// or create jo_mpeg.h, #define JO_MPEG_HEADER_FILE_ONLY, and
// then include jo_mpeg.c from it.
// Returns false on failure
extern void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps);
#endif // JO_INCLUDE_MPEG_H
#ifndef JO_MPEG_HEADER_FILE_ONLY
#ifndef JO_MPEG_COMPONENTS
#define JO_MPEG_COMPONENTS 4
#endif
#include <stdio.h>
#include <math.h>
#include <memory.h>
// Huffman tables
static const unsigned char s_jo_HTDC_Y[9][2] = {{4,3}, {0,2}, {1,2}, {5,3}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}};
static const unsigned char s_jo_HTDC_C[9][2] = {{0,2}, {1,2}, {2,2}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}, {254,8}};
static const unsigned char s_jo_HTAC[32][40][2] = {
{{6,3},{8,5},{10,6},{12,8},{76,9},{66,9},{20,11},{58,13},{48,13},{38,13},{32,13},{52,14},{50,14},{48,14},{46,14},{62,15},{62,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,15},{32,15},{48,16},{46,16},{44,16},{42,16},{40,16},{38,16},{36,16},{34,16},{32,16},},
{{6,4},{12,7},{74,9},{24,11},{54,13},{44,14},{42,14},{62,16},{60,16},{58,16},{56,16},{54,16},{52,16},{50,16},{38,17},{36,17},{34,17},{32,17}},
{{10,5},{8,8},{22,11},{40,13},{40,14}},
{{14,6},{72,9},{56,13},{38,14}},
{{12,6},{30,11},{36,13}}, {{14,7},{18,11},{36,14}}, {{10,7},{60,13},{40,17}},
{{8,7},{42,13}}, {{14,8},{34,13}}, {{10,8},{34,14}}, {{78,9},{32,14}}, {{70,9},{52,17}}, {{68,9},{50,17}}, {{64,9},{48,17}}, {{28,11},{46,17}}, {{26,11},{44,17}}, {{16,11},{42,17}},
{{62,13}}, {{52,13}}, {{50,13}}, {{46,13}}, {{44,13}}, {{62,14}}, {{60,14}}, {{58,14}}, {{56,14}}, {{54,14}}, {{62,17}}, {{60,17}}, {{58,17}}, {{56,17}}, {{54,17}},
};
static const float s_jo_quantTbl[64] = {
0.015625f,0.005632f,0.005035f,0.004832f,0.004808f,0.005892f,0.007964f,0.013325f,
0.005632f,0.004061f,0.003135f,0.003193f,0.003338f,0.003955f,0.004898f,0.008828f,
0.005035f,0.003135f,0.002816f,0.003013f,0.003299f,0.003581f,0.005199f,0.009125f,
0.004832f,0.003484f,0.003129f,0.003348f,0.003666f,0.003979f,0.005309f,0.009632f,
0.005682f,0.003466f,0.003543f,0.003666f,0.003906f,0.004546f,0.005774f,0.009439f,
0.006119f,0.004248f,0.004199f,0.004228f,0.004546f,0.005062f,0.006124f,0.009942f,
0.008883f,0.006167f,0.006096f,0.005777f,0.006078f,0.006391f,0.007621f,0.012133f,
0.016780f,0.011263f,0.009907f,0.010139f,0.009849f,0.010297f,0.012133f,0.019785f,
};
static const unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
typedef struct {
FILE *fp;
int buf, cnt;
} jo_bits_t;
static void jo_writeBits(jo_bits_t *b, int value, int count) {
b->cnt += count;
b->buf |= value << (24 - b->cnt);
while(b->cnt >= 8) {
unsigned char c = (b->buf >> 16) & 255;
putc(c, b->fp);
b->buf <<= 8;
b->cnt -= 8;
}
}
static void jo_DCT(float *d0, float *d1, float *d2, float *d3, float *d4, float *d5, float *d6, float *d7) {
float tmp0 = *d0 + *d7;
float tmp7 = *d0 - *d7;
float tmp1 = *d1 + *d6;
float tmp6 = *d1 - *d6;
float tmp2 = *d2 + *d5;
float tmp5 = *d2 - *d5;
float tmp3 = *d3 + *d4;
float tmp4 = *d3 - *d4;
// Even part
float tmp10 = tmp0 + tmp3; // phase 2
float tmp13 = tmp0 - tmp3;
float tmp11 = tmp1 + tmp2;
float tmp12 = tmp1 - tmp2;
*d0 = tmp10 + tmp11; // phase 3
*d4 = tmp10 - tmp11;
float z1 = (tmp12 + tmp13) * 0.707106781f; // c4
*d2 = tmp13 + z1; // phase 5
*d6 = tmp13 - z1;
// Odd part
tmp10 = tmp4 + tmp5; // phase 2
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
// The rotator is modified from fig 4-8 to avoid extra negations.
float z5 = (tmp10 - tmp12) * 0.382683433f; // c6
float z2 = tmp10 * 0.541196100f + z5; // c2-c6
float z4 = tmp12 * 1.306562965f + z5; // c2+c6
float z3 = tmp11 * 0.707106781f; // c4
float z11 = tmp7 + z3; // phase 5
float z13 = tmp7 - z3;
*d5 = z13 + z2; // phase 6
*d3 = z13 - z2;
*d1 = z11 + z4;
*d7 = z11 - z4;
}
static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9][2], int DC) {
for(int dataOff=0; dataOff<64; dataOff+=8) {
jo_DCT(&A[dataOff], &A[dataOff+1], &A[dataOff+2], &A[dataOff+3], &A[dataOff+4], &A[dataOff+5], &A[dataOff+6], &A[dataOff+7]);
}
for(int dataOff=0; dataOff<8; ++dataOff) {
jo_DCT(&A[dataOff], &A[dataOff+8], &A[dataOff+16], &A[dataOff+24], &A[dataOff+32], &A[dataOff+40], &A[dataOff+48], &A[dataOff+56]);
}
int Q[64];
for(int i=0; i<64; ++i) {
float v = A[i]*s_jo_quantTbl[i];
Q[s_jo_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
}
DC = Q[0] - DC;
int aDC = DC < 0 ? -DC : DC;
int size = 0;
int tempval = aDC;
while(tempval) {
size++;
tempval >>= 1;
}
jo_writeBits(bits, htdc[size][0], htdc[size][1]);
if(DC < 0) aDC ^= (1 << size) - 1;
jo_writeBits(bits, aDC, size);
int endpos = 63;
for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ }
for(int i = 1; i <= endpos;) {
int run = 0;
while (Q[i]==0 && i<endpos) {
++run;
++i;
}
int AC = Q[i++];
int aAC = AC < 0 ? -AC : AC;
int code = 0, size = 0;
if (run<32 && aAC<=40) {
code = s_jo_HTAC[run][aAC-1][0];
size = s_jo_HTAC[run][aAC-1][1];
if (AC < 0) code += 1;
}
if(!size) {
jo_writeBits(bits, 1, 6);
jo_writeBits(bits, run, 6);
if (AC < -127) {
jo_writeBits(bits, 128, 8);
} else if(AC > 127) {
jo_writeBits(bits, 0, 8);
}
code = AC&255;
size = 8;
}
jo_writeBits(bits, code, size);
}
jo_writeBits(bits, 2, 2);
return Q[0];
}
void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps) {
int lastDCY = 128, lastDCCR = 128, lastDCCB = 128;
jo_bits_t bits = {fp};
// Sequence Header
fwrite("\x00\x00\x01\xB3", 4, 1, fp);
// 12 bits for width, height
putc((width>>4)&0xFF, fp);
putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp);
putc(height & 0xFF, fp);
// aspect ratio, framerate
if(fps <= 24) putc(0x12, fp);
else if(fps <= 25) putc(0x13, fp);
else if(fps <= 30) putc(0x15, fp);
else if(fps <= 50) putc(0x16, fp);
else putc(0x18, fp); // 60fps
fwrite("\xFF\xFF\xE0\xA0", 4, 1, fp);
fwrite("\x00\x00\x01\xB8\x80\x08\x00\x40", 8, 1, fp); // GOP header
fwrite("\x00\x00\x01\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header
fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header
jo_writeBits(&bits, 0x10, 6);
for (int vblock=0; vblock<(height+15)/16; vblock++) {
for (int hblock=0; hblock<(width+15)/16; hblock++) {
jo_writeBits(&bits, 3, 2);
float Y[256], CBx[256], CRx[256];
for (int i=0; i<256; ++i) {
int y = vblock*16+(i/16);
int x = hblock*16+(i&15);
x = x >= width ? width-1 : x;
y = y >= height ? height-1 : y;
int _4 = JO_MPEG_COMPONENTS;
// const unsigned char *c = bgrx + y*width*_4+x*_4; // original
const unsigned char *c = bgrx + ((height-1)-y)*width*_4+x*_4; // flipped
float b = c[0], g = c[1], r = c[2]; // channel swap
Y[i] = ( 0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16;
CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128;
CRx[i] = ( 0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
}
// Downsample Cb,Cr (420 format)
float CB[64], CR[64];
for (int i=0; i<64; ++i) {
int j =(i&7)*2 + (i&56)*4;
CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f;
CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f;
}
for (int k1=0; k1<2; ++k1) {
for (int k2=0; k2<2; ++k2) {
float block[64];
for (int i=0; i<64; i+=8) {
int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8;
memcpy(block+i, Y+j, 8*sizeof(Y[0]));
}
lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY);
}
}
lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB);
lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR);
}
}
jo_writeBits(&bits, 0, 7);
fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence
}
#endif

View File

@ -1,440 +1,440 @@
// JSON5 + SJSON parser module
//
// License:
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
// No warranty is implied, use at your own risk.
//
// Credits:
// r-lyeh (fork)
// Dominik Madarasz (@zaklaus) (original code)
#ifndef JSON5_H
#define JSON5_H
#ifndef JSON5_ASSERT
#define JSON5_ASSERT do { printf("JSON5: Error L%d while parsing '%c' in '%.16s'\n", __LINE__, p[0], p); assert(0); } while(0)
#endif
#include <stdint.h>
#include <stdio.h>
typedef enum json5_type {
JSON5_UNDEFINED, // 0
JSON5_NULL, // 1
JSON5_BOOL, // 2
JSON5_OBJECT, // 3
JSON5_STRING, // 4
JSON5_ARRAY, // 5
JSON5_INTEGER, // 6
JSON5_REAL, // 7
} json5_type;
typedef struct json5 {
char* name;
#ifdef NDEBUG
unsigned type : 3;
#else
json5_type type;
#endif
unsigned count : 29;
union {
struct json5* array;
struct json5* nodes;
int64_t integer;
double real;
char* string;
int boolean;
};
} json5;
char* json5_parse(json5 *root, char *source, int flags);
void json5_write(FILE *fp, const json5 *root);
void json5_free(json5 *root);
#endif // JSON5_H
// json5 ----------------------------------------------------------------------
#ifdef JSON5_C
//#pragma once
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
char *json5__trim(char *p) {
while (*p) {
/**/ if( isspace(*p) ) ++p;
else if( p[0] == '/' && p[1] == '*' ) { // skip C comment
for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {}
if( *p ) p += 2;
}
else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment
for( p += 2; *p && p[0] != '\n'; ++p) {}
if( *p ) ++p;
}
else break;
}
return p;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code);
char *json5__parse_string(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( *p == '"' || *p == '\'' || *p == '`' ) {
obj->type = JSON5_STRING;
obj->string = p + 1;
char eos_char = *p, *b = obj->string, *e = b;
while (*e) {
/**/ if( *e == '\\' && (e[1] == eos_char) ) ++e;
else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' ';
else if( *e == eos_char ) break;
++e;
}
*e = '\0';
return p = e + 1;
}
//JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_object(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( 1 /* *p == '{' */ ) { /* <-- for SJSON */
int skip = *p == '{'; /* <-- for SJSON */
obj->type = JSON5_OBJECT;
obj->nodes = 0;
obj->count = 0;
while (*p) {
json5 node = { 0 };
do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' );
if( *p == '}' ) {
++p;
break;
}
// @todo: is_unicode() (s[0] == '\\' && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]))) {
else if( isalnum(*p) || *p == '_' || *p == '$' || *p == '.' ) { // also || is_unicode(p)
node.name = p;
do {
++p;
} while (*p && (isalnum(*p) || *p == '_' || *p == '$' || *p == '.') ); // also || is_unicode(p)
char *e = p;
p = json5__trim(p);
*e = '\0';
}
else { //if( *p == '"' || *p == '\'' || *p == '`' ) {
char *ps = json5__parse_string(&node, p, err_code);
if( !ps ) {
return NULL;
}
p = ps;
node.name = node.string;
p = json5__trim(p);
}
// @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
p = json5__trim(p + 1);
p = json5__parse_value(&node, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( node.type != JSON5_UNDEFINED ) {
array_push(obj->nodes, node);
++obj->count;
}
if( *p == '}') { ++p; break; }
}
return p;
}
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code) {
assert(obj && p);
p = json5__trim(p);
char *is_string = json5__parse_string(obj, p, err_code);
if( is_string ) {
p = is_string;
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '{' ) {
p = json5__parse_object( obj, p, err_code );
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '[' ) {
obj->type = JSON5_ARRAY;
obj->array = 0;
obj->count = 0;
while (*p) {
json5 elem = { 0 };
do { p = json5__trim(p + 1); } while( *p == ',' );
if( *p == ']') { ++p; break; }
p = json5__parse_value(&elem, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( elem.type != JSON5_UNDEFINED ) {
array_push(obj->array, elem);
++obj->count;
}
if (*p == ']') { ++p; break; }
}
}
else if( isalpha(*p) || (*p == '-' && !isdigit(p[1])) ) {
const char *labels[] = { "null", "on","true", "off","false", "nan","NaN", "-nan","-NaN", "inf","Infinity", "-inf","-Infinity", 0 };
const int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 };
for( int i = 0; labels[i]; ++i ) {
if( !strncmp(p, labels[i], lenghts[i] ) ) {
p += lenghts[i];
#ifdef _MSC_VER // somehow, NaN is apparently signed in MSC
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? NAN :-NAN;
#else
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN;
#endif
else if( i >= 1 ) obj->type = JSON5_BOOL, obj->boolean = i <= 2;
else obj->type = JSON5_NULL;
break;
}
}
if( obj->type == JSON5_UNDEFINED ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
}
else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) {
char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0;
while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) {
is_hex |= (*p | 32) == 'x';
is_dbl |= *p == '.';
*buf++ = *p++;
}
obj->type = is_dbl ? JSON5_REAL : JSON5_INTEGER;
long long unsigned int llu;
long long int lli;
/**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real );
else if( is_hex ) sscanf( buffer, "%llx", &llu ), obj->integer = llu; // SCNx64 -> inttypes.h
else sscanf( buffer, "%lld", &lli ), obj->integer = lli; // SCNd64 -> inttypes.h
}
else {
return NULL;
}
return p;
}
char *json5_parse(json5 *root, char *p, int flags) {
char *err_code = "";
*root = (json5) {0};
if( p && p[0] ) {
p = json5__trim(p);
if( *p == '[' ) { /* <-- for SJSON */
json5__parse_value(root, p, &err_code);
} else {
json5__parse_object(root, p, &err_code); /* <-- for SJSON */
}
} else {
root->type = JSON5_OBJECT;
}
return err_code[0] ? err_code : 0;
}
void json5_free(json5 *root) {
if( root->type == JSON5_ARRAY && root->array ) {
for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) {
json5_free(root->array + i);
}
array_free(root->array);
}
if( root->type == JSON5_OBJECT && root->nodes ) {
for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) {
json5_free(root->nodes + i);
}
array_free(root->nodes);
}
*root = (json5) {0}; // needed?
}
void json5_write(FILE *fp, const json5 *o) {
static __thread int indent = 0;
int tabs = 1; // 0,1,2,4,8
if( o->name ) {
fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":");
}
/**/ if( o->type == JSON5_NULL ) fprintf(fp, "%s", "null");
else if( o->type == JSON5_BOOL ) fprintf(fp, "%s", o->boolean ? "true" : "false");
else if( o->type == JSON5_INTEGER ) fprintf(fp, "%lld", (long long int)o->integer);
else if( o->type == JSON5_REAL ) {
/**/ if( isnan(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-nan" : "nan" );
else if( isinf(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-inf" : "inf" );
else fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness
}
#if 0
else if( o->type == JSON5_STRING ) { // write (escaped) string
char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256];
for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i];
const char *b = o->string, *e = strpbrk(b, chars), *sep = "\"";
while( e ) {
fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] );
e = strpbrk( b = e + 1, chars);
sep = "";
}
fprintf(fp, "%s%s\"", sep, b);
}
#else
else if( o->type == JSON5_STRING ) { // write string
fprintf(fp, "\"%s\"", o->string);
}
#endif
else if( o->type == JSON5_ARRAY ) {
const char *sep = "";
fprintf(fp, "%s", tabs ? "[ " : "[");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ", " : ",";
json5_write(fp, o->array + i);
}
fprintf(fp, "%s", tabs ? " ]" : "]");
}
else if( o->type == JSON5_OBJECT ) {
const char *sep = "";
fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":"");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ",";
json5_write(fp, o->nodes + i);
}
fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, "");
} else {
char p[16] = {0};
JSON5_ASSERT; /* "json5_error_invalid_value"; */
}
}
#ifdef JSON5_BENCH
#include <time.h>
int main() {
// https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/
char *content = 0;
for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) {
fseek(fp, 0L, SEEK_END);
size_t pos = ftell(fp);
fseek(fp, 0L, SEEK_SET);
content = (char*)malloc( pos + 1 );
fread(content, 1, pos, fp);
content[pos] = 0;
}
if( content ) {
clock_t start = clock();
json5 root = {0};
char *error = json5_parse(&root, content, 0);
clock_t end = clock();
double delta = ( end - start ) / (double)CLOCKS_PER_SEC;
if( !error ) {
printf("Parsing time: %.3fms\n", delta*1000);
printf("Total nodes: %d\n", array_count(root.array));
printf("Category: %s, air date: %s\nQuestion: %s\n", root.array[0].nodes[0].string,
root.array[0].nodes[1].string,
root.array[0].nodes[2].string);
} else {
printf("Error: %s\n", error);
}
json5_free(&root);
free(content);
}
}
#define main main__
#endif
#ifdef JSON5_DEMO
int main() {
char source5[] =
" // comments\n" /* json5 sample */
" unquoted: 'and you can quote me on that',\n"
" singleQuotes: 'I can use \"double quotes\" here',\n"
" lineBreaks : \"Look, Mom! \\\n"
"No \\n's!\",\n"
" hexadecimal: 0x100,\n"
" leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n"
" positiveSign: +1,\n"
" trailingComma: 'in objects', andIn: ['arrays', ],\n"
" \"backwardsCompatible\": \"with JSON\",\n"
""
" ip = \"127.0.0.1\"\n" /* sjson sample */
" port = 8888\n"
""
" /* comment //nested comment*/\n" /* tests */
" // comment /*nested comment*/\n"
" nil: null,"
" \"+lšctžýáíé=:\": true,,,,"
" huge: 2.2239333e5, "
" array: [+1,2,-3,4,5], "
" hello: 'world /*comment in string*/ //again', "
" abc: 42.67, def: false, "
" children : { a: 1, b: 2, },"
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],"
""
" multiline: `this is\n"
"a multiline string\n"
"yeah`"
"}\n";
json5 root = { 0 };
char *error = json5_parse(&root, source5, 0);
if( error ) {
printf("Error: %s\n", error);
} else {
json5_write(stdout, &root);
}
json5_free(&root);
}
#define main main__
#endif
#endif // JSON5_C
// JSON5 + SJSON parser module
//
// License:
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
// No warranty is implied, use at your own risk.
//
// Credits:
// r-lyeh (fork)
// Dominik Madarasz (@zaklaus) (original code)
#ifndef JSON5_H
#define JSON5_H
#ifndef JSON5_ASSERT
#define JSON5_ASSERT do { printf("JSON5: Error L%d while parsing '%c' in '%.16s'\n", __LINE__, p[0], p); assert(0); } while(0)
#endif
#include <stdint.h>
#include <stdio.h>
typedef enum json5_type {
JSON5_UNDEFINED, // 0
JSON5_NULL, // 1
JSON5_BOOL, // 2
JSON5_OBJECT, // 3
JSON5_STRING, // 4
JSON5_ARRAY, // 5
JSON5_INTEGER, // 6
JSON5_REAL, // 7
} json5_type;
typedef struct json5 {
char* name;
#ifdef NDEBUG
unsigned type : 3;
#else
json5_type type;
#endif
unsigned count : 29;
union {
struct json5* array;
struct json5* nodes;
int64_t integer;
double real;
char* string;
int boolean;
};
} json5;
char* json5_parse(json5 *root, char *source, int flags);
void json5_write(FILE *fp, const json5 *root);
void json5_free(json5 *root);
#endif // JSON5_H
// json5 ----------------------------------------------------------------------
#ifdef JSON5_C
//#pragma once
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
char *json5__trim(char *p) {
while (*p) {
/**/ if( isspace(*p) ) ++p;
else if( p[0] == '/' && p[1] == '*' ) { // skip C comment
for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {}
if( *p ) p += 2;
}
else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment
for( p += 2; *p && p[0] != '\n'; ++p) {}
if( *p ) ++p;
}
else break;
}
return p;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code);
char *json5__parse_string(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( *p == '"' || *p == '\'' || *p == '`' ) {
obj->type = JSON5_STRING;
obj->string = p + 1;
char eos_char = *p, *b = obj->string, *e = b;
while (*e) {
/**/ if( *e == '\\' && (e[1] == eos_char) ) ++e;
else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' ';
else if( *e == eos_char ) break;
++e;
}
*e = '\0';
return p = e + 1;
}
//JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_object(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( 1 /* *p == '{' */ ) { /* <-- for SJSON */
int skip = *p == '{'; /* <-- for SJSON */
obj->type = JSON5_OBJECT;
obj->nodes = 0;
obj->count = 0;
while (*p) {
json5 node = { 0 };
do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' );
if( *p == '}' ) {
++p;
break;
}
// @todo: is_unicode() (s[0] == '\\' && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]))) {
else if( isalnum(*p) || *p == '_' || *p == '$' || *p == '.' ) { // also || is_unicode(p)
node.name = p;
do {
++p;
} while (*p && (isalnum(*p) || *p == '_' || *p == '$' || *p == '.') ); // also || is_unicode(p)
char *e = p;
p = json5__trim(p);
*e = '\0';
}
else { //if( *p == '"' || *p == '\'' || *p == '`' ) {
char *ps = json5__parse_string(&node, p, err_code);
if( !ps ) {
return NULL;
}
p = ps;
node.name = node.string;
p = json5__trim(p);
}
// @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
p = json5__trim(p + 1);
p = json5__parse_value(&node, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( node.type != JSON5_UNDEFINED ) {
array_push(obj->nodes, node);
++obj->count;
}
if( *p == '}') { ++p; break; }
}
return p;
}
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code) {
assert(obj && p);
p = json5__trim(p);
char *is_string = json5__parse_string(obj, p, err_code);
if( is_string ) {
p = is_string;
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '{' ) {
p = json5__parse_object( obj, p, err_code );
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '[' ) {
obj->type = JSON5_ARRAY;
obj->array = 0;
obj->count = 0;
while (*p) {
json5 elem = { 0 };
do { p = json5__trim(p + 1); } while( *p == ',' );
if( *p == ']') { ++p; break; }
p = json5__parse_value(&elem, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( elem.type != JSON5_UNDEFINED ) {
array_push(obj->array, elem);
++obj->count;
}
if (*p == ']') { ++p; break; }
}
}
else if( isalpha(*p) || (*p == '-' && !isdigit(p[1])) ) {
const char *labels[] = { "null", "on","true", "off","false", "nan","NaN", "-nan","-NaN", "inf","Infinity", "-inf","-Infinity", 0 };
const int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 };
for( int i = 0; labels[i]; ++i ) {
if( !strncmp(p, labels[i], lenghts[i] ) ) {
p += lenghts[i];
#ifdef _MSC_VER // somehow, NaN is apparently signed in MSC
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? NAN :-NAN;
#else
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN;
#endif
else if( i >= 1 ) obj->type = JSON5_BOOL, obj->boolean = i <= 2;
else obj->type = JSON5_NULL;
break;
}
}
if( obj->type == JSON5_UNDEFINED ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
}
else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) {
char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0;
while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) {
is_hex |= (*p | 32) == 'x';
is_dbl |= *p == '.';
*buf++ = *p++;
}
obj->type = is_dbl ? JSON5_REAL : JSON5_INTEGER;
long long unsigned int llu;
long long int lli;
/**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real );
else if( is_hex ) sscanf( buffer, "%llx", &llu ), obj->integer = llu; // SCNx64 -> inttypes.h
else sscanf( buffer, "%lld", &lli ), obj->integer = lli; // SCNd64 -> inttypes.h
}
else {
return NULL;
}
return p;
}
char *json5_parse(json5 *root, char *p, int flags) {
char *err_code = "";
*root = (json5) {0};
if( p && p[0] ) {
p = json5__trim(p);
if( *p == '[' ) { /* <-- for SJSON */
json5__parse_value(root, p, &err_code);
} else {
json5__parse_object(root, p, &err_code); /* <-- for SJSON */
}
} else {
root->type = JSON5_OBJECT;
}
return err_code[0] ? err_code : 0;
}
void json5_free(json5 *root) {
if( root->type == JSON5_ARRAY && root->array ) {
for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) {
json5_free(root->array + i);
}
array_free(root->array);
}
if( root->type == JSON5_OBJECT && root->nodes ) {
for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) {
json5_free(root->nodes + i);
}
array_free(root->nodes);
}
*root = (json5) {0}; // needed?
}
void json5_write(FILE *fp, const json5 *o) {
static __thread int indent = 0;
int tabs = 1; // 0,1,2,4,8
if( o->name ) {
fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":");
}
/**/ if( o->type == JSON5_NULL ) fprintf(fp, "%s", "null");
else if( o->type == JSON5_BOOL ) fprintf(fp, "%s", o->boolean ? "true" : "false");
else if( o->type == JSON5_INTEGER ) fprintf(fp, "%lld", (long long int)o->integer);
else if( o->type == JSON5_REAL ) {
/**/ if( isnan(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-nan" : "nan" );
else if( isinf(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-inf" : "inf" );
else fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness
}
#if 0
else if( o->type == JSON5_STRING ) { // write (escaped) string
char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256];
for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i];
const char *b = o->string, *e = strpbrk(b, chars), *sep = "\"";
while( e ) {
fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] );
e = strpbrk( b = e + 1, chars);
sep = "";
}
fprintf(fp, "%s%s\"", sep, b);
}
#else
else if( o->type == JSON5_STRING ) { // write string
fprintf(fp, "\"%s\"", o->string);
}
#endif
else if( o->type == JSON5_ARRAY ) {
const char *sep = "";
fprintf(fp, "%s", tabs ? "[ " : "[");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ", " : ",";
json5_write(fp, o->array + i);
}
fprintf(fp, "%s", tabs ? " ]" : "]");
}
else if( o->type == JSON5_OBJECT ) {
const char *sep = "";
fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":"");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ",";
json5_write(fp, o->nodes + i);
}
fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, "");
} else {
char p[16] = {0};
JSON5_ASSERT; /* "json5_error_invalid_value"; */
}
}
#ifdef JSON5_BENCH
#include <time.h>
int main() {
// https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/
char *content = 0;
for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) {
fseek(fp, 0L, SEEK_END);
size_t pos = ftell(fp);
fseek(fp, 0L, SEEK_SET);
content = (char*)malloc( pos + 1 );
fread(content, 1, pos, fp);
content[pos] = 0;
}
if( content ) {
clock_t start = clock();
json5 root = {0};
char *error = json5_parse(&root, content, 0);
clock_t end = clock();
double delta = ( end - start ) / (double)CLOCKS_PER_SEC;
if( !error ) {
printf("Parsing time: %.3fms\n", delta*1000);
printf("Total nodes: %d\n", array_count(root.array));
printf("Category: %s, air date: %s\nQuestion: %s\n", root.array[0].nodes[0].string,
root.array[0].nodes[1].string,
root.array[0].nodes[2].string);
} else {
printf("Error: %s\n", error);
}
json5_free(&root);
free(content);
}
}
#define main main__
#endif
#ifdef JSON5_DEMO
int main() {
char source5[] =
" // comments\n" /* json5 sample */
" unquoted: 'and you can quote me on that',\n"
" singleQuotes: 'I can use \"double quotes\" here',\n"
" lineBreaks : \"Look, Mom! \\\n"
"No \\n's!\",\n"
" hexadecimal: 0x100,\n"
" leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n"
" positiveSign: +1,\n"
" trailingComma: 'in objects', andIn: ['arrays', ],\n"
" \"backwardsCompatible\": \"with JSON\",\n"
""
" ip = \"127.0.0.1\"\n" /* sjson sample */
" port = 8888\n"
""
" /* comment //nested comment*/\n" /* tests */
" // comment /*nested comment*/\n"
" nil: null,"
" \"+lšctžýáíé=:\": true,,,,"
" huge: 2.2239333e5, "
" array: [+1,2,-3,4,5], "
" hello: 'world /*comment in string*/ //again', "
" abc: 42.67, def: false, "
" children : { a: 1, b: 2, },"
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],"
""
" multiline: `this is\n"
"a multiline string\n"
"yeah`"
"}\n";
json5 root = { 0 };
char *error = json5_parse(&root, source5, 0);
if( error ) {
printf("Error: %s\n", error);
} else {
json5_write(stdout, &root);
}
json5_free(&root);
}
#define main main__
#endif
#endif // JSON5_C

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,275 +1,275 @@
// lite editor, platform details
// - rlyeh, public domain
#define LT_DATAPATH "/lite"
#define lt_assert(x) ASSERT(x)
#define lt_realpath(p, q) file_pathabs(p)
#define lt_realpath_free(p)
#define lt_malloc(n) MALLOC(n)
#define lt_calloc(n,m) CALLOC(n,m)
#define lt_free(p) FREE(p)
#define lt_memcpy(d,s,c) memcpy(d,s,c)
#define lt_memset(p,ch,c) memset(p,ch,c)
#define lt_time_ms() time_ms()
#define lt_sleep_ms(ms) sleep_ms(ms)
#define lt_getclipboard(w) window_clipboard()
#define lt_setclipboard(w,s) window_setclipboard(s)
#define lt_window() window_handle()
#define lt_setwindowmode(m) window_fullscreen(m == 2), (m < 2 && (window_maximize(m),1)) // 0:normal,1:maximized,2:fullscreen
#define lt_setwindowtitle(t) //window_title(t)
#define lt_haswindowfocus() window_has_focus()
#define lt_setcursor(shape) window_cursor_shape(lt_events & (1<<31) ? CURSOR_SW_AUTO : shape+1) // 0:arrow,1:ibeam,2:sizeh,3:sizev,4:hand
#define lt_prompt(msg,title) ifndef(win32, 0, (MessageBoxA(0, msg, title, MB_YESNO | MB_ICONWARNING) == IDYES))
unsigned lt_events = ~0u;
int lt_mx = 0, lt_my = 0, lt_wx = 0, lt_wy = 0, lt_ww = 0, lt_wh = 0;
typedef struct lt_surface {
unsigned w, h;
void *pixels;
texture_t t;
} lt_surface;
typedef struct lt_rect {
int x, y, width, height;
} lt_rect;
lt_surface *lt_getsurface(void *window) {
static lt_surface s = {0};
return &s;
}
void lt_updatesurfacerects(lt_surface *s, lt_rect* rects, unsigned count) {
if(0)
for( int i = 0; i < count; ++i ) {
memset( (unsigned*)s->pixels + (rects[i].x + rects[i].y * s->w), 0xFF, rects[i].width*4 );
memset( (unsigned*)s->pixels + (rects[i].x + (rects[i].y + (rects[i].height-1)) * s->w), 0xFF, rects[i].width*4 );
for( int y = 1; y < (rects[i].height-1); ++y ) {
((unsigned*)s->pixels)[ rects[i].x + y * s->w ] =
((unsigned*)s->pixels)[ rects[i].x + (rects[i].width-1) + y * s->w ] = 0xFFFFFFFF;
}
}
// update contents
texture_update(&s->t, s->w, s->h, 4, s->pixels, TEXTURE_LINEAR|TEXTURE_BGRA);
}
void ren_set_clip_rect(struct lt_rect rect);
void rencache_invalidate(void);
int lt_resizesurface(lt_surface *s, int ww, int wh) {
s->w = ww, s->h = wh;
if( s->t.id == 0 || s->w != s->t.w || s->h != s->t.h ) {
// invalidate tiles
ren_set_clip_rect( (lt_rect) { 0, 0, s->w, s->h } );
rencache_invalidate();
// texture clear
if( !s->t.id ) s->t = texture_create(1, 1, 4, " ", TEXTURE_LINEAR|TEXTURE_RGBA|TEXTURE_BYTE );
s->pixels = REALLOC(s->pixels, s->w * s->h * 4);
memset(s->pixels, 0, s->w * s->h * 4);
// texture update
lt_updatesurfacerects(s,0,0);
return 1; // resized
}
return 0; // unchanged
}
void *lt_load_file(const char *filename, int *size) {
int datalen; char *data = file_load(filename, &datalen);
if( !data || !datalen ) {
filename = (const char *)file_normalize(filename);
if( strbegi(filename, app_path()) ) filename += strlen(app_path());
data = vfs_load(filename, &datalen);
}
if (size) *size = 0;
if (!data) { return NULL; }
if (size) *size = datalen;
// return permanent buffers here, as file_load() and vfs_load() do return temporaries
data = memcpy(MALLOC(datalen+1), data, datalen);
data[datalen] = 0;
return data;
}
const char* lt_button_name(int button) {
if(button == GLFW_MOUSE_BUTTON_LEFT) return "left";
if(button == GLFW_MOUSE_BUTTON_RIGHT) return "right";
if(button == GLFW_MOUSE_BUTTON_MIDDLE) return "middle";
return "?";
}
char* lt_key_name(char *dst, int key, int vk, int mods) {
// @todo: "altgr" -> left ctrl + right alt
if( key == GLFW_KEY_UP ) return "up";
if( key == GLFW_KEY_DOWN ) return "down";
if( key == GLFW_KEY_LEFT ) return "left";
if( key == GLFW_KEY_RIGHT ) return "right";
if( key == GLFW_KEY_LEFT_ALT ) return "left alt";
if( key == GLFW_KEY_RIGHT_ALT ) return "right alt";
if( key == GLFW_KEY_LEFT_SHIFT ) return "left shift";
if( key == GLFW_KEY_RIGHT_SHIFT ) return "right shift";
if( key == GLFW_KEY_LEFT_CONTROL ) return "left ctrl";
if( key == GLFW_KEY_RIGHT_CONTROL ) return "right ctrl";
if( key == GLFW_KEY_LEFT_SUPER ) return "left windows";
if( key == GLFW_KEY_RIGHT_SUPER ) return "left windows";
if( key == GLFW_KEY_MENU ) return "menu";
if( key == GLFW_KEY_ESCAPE ) return "escape";
if( key == GLFW_KEY_BACKSPACE ) return "backspace";
if( key == GLFW_KEY_ENTER ) return "return";
if( key == GLFW_KEY_KP_ENTER ) return "keypad enter";
if( key == GLFW_KEY_TAB ) return "tab";
if( key == GLFW_KEY_CAPS_LOCK ) return "capslock";
if( key == GLFW_KEY_HOME ) return "home";
if( key == GLFW_KEY_END ) return "end";
if( key == GLFW_KEY_INSERT ) return "insert";
if( key == GLFW_KEY_DELETE ) return "delete";
if( key == GLFW_KEY_PAGE_UP ) return "pageup";
if( key == GLFW_KEY_PAGE_DOWN ) return "pagedown";
if( key == GLFW_KEY_F1 ) return "f1";
if( key == GLFW_KEY_F2 ) return "f2";
if( key == GLFW_KEY_F3 ) return "f3";
if( key == GLFW_KEY_F4 ) return "f4";
if( key == GLFW_KEY_F5 ) return "f5";
if( key == GLFW_KEY_F6 ) return "f6";
if( key == GLFW_KEY_F7 ) return "f7";
if( key == GLFW_KEY_F8 ) return "f8";
if( key == GLFW_KEY_F9 ) return "f9";
if( key == GLFW_KEY_F10 ) return "f10";
if( key == GLFW_KEY_F11 ) return "f11";
if( key == GLFW_KEY_F12 ) return "f12";
const char *name = glfwGetKeyName(key, vk);
strcpy(dst, name ? name : "");
char *p = dst;
while (*p) {
*p = tolower(*p);
p++;
}
return dst;
}
void lt_globpath(struct lua_State*L, const char *path) {
unsigned j = 0;
if(!strend(path, "/")) path = (const char *)va("%s/", path);
for( dir *d = dir_open(path, ""); d; dir_close(d), d = 0 ) {
for( unsigned i = 0, end = dir_count(d); i < end; ++i ) {
char *name = dir_name(d,i);
char *last = name + strlen(name) - 1;
if( *last == '/' ) *last = '\0';
name = file_name(name);
lua_pushstring(L, name);
lua_rawseti(L, -2, ++j);
}
}
for( const char *section = strstri(path, LT_DATAPATH); section && section[sizeof(LT_DATAPATH)-1] == '/'; section = 0) {
array(char*) list = vfs_list("**");
for( unsigned i = 0, end = array_count(list); i < end; ++i ) {
char *name = list[i];
if( !strstri(name, section+1) ) continue;
lua_pushstring(L, file_name(name));
lua_rawseti(L, -2, ++j);
}
if( array_count(list) ) return;
}
}
int lt_emit_event(lua_State *L, const char *event_name, const char *event_fmt, ...) {
int count = 0;
lua_pushstring(L, event_name);
if( event_fmt ) {
va_list va;
va_start(va, event_fmt);
for( ; event_fmt[count]; ++count ) {
/**/ if( event_fmt[count] == 'd' ) { int d = va_arg(va, int); lua_pushnumber(L, d); }
else if( event_fmt[count] == 'f' ) { double f = va_arg(va, double); lua_pushnumber(L, f); }
else if( event_fmt[count] == 's' ) { const char *s = va_arg(va, const char *); lua_pushstring(L, s); }
}
va_end(va);
}
return 1+count;
}
int printi(int i) {
// printf("clicks: %d\n", i);
return i;
}
static const char* codepoint_to_utf8_(unsigned c);
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
int rc = 0;
char buf[16];
static int prevx = 0, prevy = 0;
static unsigned clicks_time = 0, clicks = 0;
if( (lt_time_ms() - clicks_time) > 400 ) clicks = 0;
//
for( GLEQevent e; gleqNextEvent(&e); gleqFreeEvent(&e) )
if( lt_events & e.type )
switch (e.type) {
default:
break; case GLEQ_WINDOW_CLOSED: // it used to be ok. depends on window_swap() flow
rc += lt_emit_event(L, "quit", NULL);
return rc;
break; case GLEQ_WINDOW_MOVED:
lt_wx = e.pos.x;
lt_wy = e.pos.y;
break; case GLEQ_WINDOW_RESIZED:
rc += lt_emit_event(L, "resized", "dd", lt_ww = e.size.width, lt_wh = e.size.height);
lt_resizesurface(lt_getsurface(lt_window()), lt_ww, lt_wh);
break; case GLEQ_WINDOW_REFRESH:
rc += lt_emit_event(L, "exposed", NULL);
rencache_invalidate();
break; case GLEQ_FILE_DROPPED:
rc += lt_emit_event(L, "filedropped", "sdd", e.file.paths[0], lt_mx, lt_my);
break; case GLEQ_KEY_PRESSED:
case GLEQ_KEY_REPEATED:
rc += lt_emit_event(L, "keypressed", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
goto bottom;
break; case GLEQ_KEY_RELEASED:
rc += lt_emit_event(L, "keyreleased", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
goto bottom;
break; case GLEQ_CODEPOINT_INPUT:
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
break; case GLEQ_BUTTON_PRESSED:
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
break; case GLEQ_BUTTON_RELEASED:
clicks += e.mouse.button == GLFW_MOUSE_BUTTON_1;
clicks_time = lt_time_ms();
rc += lt_emit_event(L, "mousereleased", "sdd", lt_button_name(e.mouse.button), lt_mx, lt_my);
break; case GLEQ_CURSOR_MOVED:
lt_mx = e.pos.x - lt_wx, lt_my = e.pos.y - lt_wy;
rc += lt_emit_event(L, "mousemoved", "dddd", lt_mx, lt_my, lt_mx - prevx, lt_my - prevy);
prevx = lt_mx, prevy = lt_my;
break; case GLEQ_SCROLLED:
rc += lt_emit_event(L, "mousewheel", "f", e.scroll.y);
}
bottom:;
return rc;
}
// lite editor, platform details
// - rlyeh, public domain
#define LT_DATAPATH "/lite"
#define lt_assert(x) ASSERT(x)
#define lt_realpath(p, q) file_pathabs(p)
#define lt_realpath_free(p)
#define lt_malloc(n) MALLOC(n)
#define lt_calloc(n,m) CALLOC(n,m)
#define lt_free(p) FREE(p)
#define lt_memcpy(d,s,c) memcpy(d,s,c)
#define lt_memset(p,ch,c) memset(p,ch,c)
#define lt_time_ms() time_ms()
#define lt_sleep_ms(ms) sleep_ms(ms)
#define lt_getclipboard(w) window_clipboard()
#define lt_setclipboard(w,s) window_setclipboard(s)
#define lt_window() window_handle()
#define lt_setwindowmode(m) window_fullscreen(m == 2), (m < 2 && (window_maximize(m),1)) // 0:normal,1:maximized,2:fullscreen
#define lt_setwindowtitle(t) //window_title(t)
#define lt_haswindowfocus() window_has_focus()
#define lt_setcursor(shape) window_cursor_shape(lt_events & (1<<31) ? CURSOR_SW_AUTO : shape+1) // 0:arrow,1:ibeam,2:sizeh,3:sizev,4:hand
#define lt_prompt(msg,title) ifndef(win32, 0, (MessageBoxA(0, msg, title, MB_YESNO | MB_ICONWARNING) == IDYES))
unsigned lt_events = ~0u;
int lt_mx = 0, lt_my = 0, lt_wx = 0, lt_wy = 0, lt_ww = 0, lt_wh = 0;
typedef struct lt_surface {
unsigned w, h;
void *pixels;
texture_t t;
} lt_surface;
typedef struct lt_rect {
int x, y, width, height;
} lt_rect;
lt_surface *lt_getsurface(void *window) {
static lt_surface s = {0};
return &s;
}
void lt_updatesurfacerects(lt_surface *s, lt_rect* rects, unsigned count) {
if(0)
for( int i = 0; i < count; ++i ) {
memset( (unsigned*)s->pixels + (rects[i].x + rects[i].y * s->w), 0xFF, rects[i].width*4 );
memset( (unsigned*)s->pixels + (rects[i].x + (rects[i].y + (rects[i].height-1)) * s->w), 0xFF, rects[i].width*4 );
for( int y = 1; y < (rects[i].height-1); ++y ) {
((unsigned*)s->pixels)[ rects[i].x + y * s->w ] =
((unsigned*)s->pixels)[ rects[i].x + (rects[i].width-1) + y * s->w ] = 0xFFFFFFFF;
}
}
// update contents
texture_update(&s->t, s->w, s->h, 4, s->pixels, TEXTURE_LINEAR|TEXTURE_BGRA);
}
void ren_set_clip_rect(struct lt_rect rect);
void rencache_invalidate(void);
int lt_resizesurface(lt_surface *s, int ww, int wh) {
s->w = ww, s->h = wh;
if( s->t.id == 0 || s->w != s->t.w || s->h != s->t.h ) {
// invalidate tiles
ren_set_clip_rect( (lt_rect) { 0, 0, s->w, s->h } );
rencache_invalidate();
// texture clear
if( !s->t.id ) s->t = texture_create(1, 1, 4, " ", TEXTURE_LINEAR|TEXTURE_RGBA|TEXTURE_BYTE );
s->pixels = REALLOC(s->pixels, s->w * s->h * 4);
memset(s->pixels, 0, s->w * s->h * 4);
// texture update
lt_updatesurfacerects(s,0,0);
return 1; // resized
}
return 0; // unchanged
}
void *lt_load_file(const char *filename, int *size) {
int datalen; char *data = file_load(filename, &datalen);
if( !data || !datalen ) {
filename = (const char *)file_normalize(filename);
if( strbegi(filename, app_path()) ) filename += strlen(app_path());
data = vfs_load(filename, &datalen);
}
if (size) *size = 0;
if (!data) { return NULL; }
if (size) *size = datalen;
// return permanent buffers here, as file_load() and vfs_load() do return temporaries
data = memcpy(MALLOC(datalen+1), data, datalen);
data[datalen] = 0;
return data;
}
const char* lt_button_name(int button) {
if(button == GLFW_MOUSE_BUTTON_LEFT) return "left";
if(button == GLFW_MOUSE_BUTTON_RIGHT) return "right";
if(button == GLFW_MOUSE_BUTTON_MIDDLE) return "middle";
return "?";
}
char* lt_key_name(char *dst, int key, int vk, int mods) {
// @todo: "altgr" -> left ctrl + right alt
if( key == GLFW_KEY_UP ) return "up";
if( key == GLFW_KEY_DOWN ) return "down";
if( key == GLFW_KEY_LEFT ) return "left";
if( key == GLFW_KEY_RIGHT ) return "right";
if( key == GLFW_KEY_LEFT_ALT ) return "left alt";
if( key == GLFW_KEY_RIGHT_ALT ) return "right alt";
if( key == GLFW_KEY_LEFT_SHIFT ) return "left shift";
if( key == GLFW_KEY_RIGHT_SHIFT ) return "right shift";
if( key == GLFW_KEY_LEFT_CONTROL ) return "left ctrl";
if( key == GLFW_KEY_RIGHT_CONTROL ) return "right ctrl";
if( key == GLFW_KEY_LEFT_SUPER ) return "left windows";
if( key == GLFW_KEY_RIGHT_SUPER ) return "left windows";
if( key == GLFW_KEY_MENU ) return "menu";
if( key == GLFW_KEY_ESCAPE ) return "escape";
if( key == GLFW_KEY_BACKSPACE ) return "backspace";
if( key == GLFW_KEY_ENTER ) return "return";
if( key == GLFW_KEY_KP_ENTER ) return "keypad enter";
if( key == GLFW_KEY_TAB ) return "tab";
if( key == GLFW_KEY_CAPS_LOCK ) return "capslock";
if( key == GLFW_KEY_HOME ) return "home";
if( key == GLFW_KEY_END ) return "end";
if( key == GLFW_KEY_INSERT ) return "insert";
if( key == GLFW_KEY_DELETE ) return "delete";
if( key == GLFW_KEY_PAGE_UP ) return "pageup";
if( key == GLFW_KEY_PAGE_DOWN ) return "pagedown";
if( key == GLFW_KEY_F1 ) return "f1";
if( key == GLFW_KEY_F2 ) return "f2";
if( key == GLFW_KEY_F3 ) return "f3";
if( key == GLFW_KEY_F4 ) return "f4";
if( key == GLFW_KEY_F5 ) return "f5";
if( key == GLFW_KEY_F6 ) return "f6";
if( key == GLFW_KEY_F7 ) return "f7";
if( key == GLFW_KEY_F8 ) return "f8";
if( key == GLFW_KEY_F9 ) return "f9";
if( key == GLFW_KEY_F10 ) return "f10";
if( key == GLFW_KEY_F11 ) return "f11";
if( key == GLFW_KEY_F12 ) return "f12";
const char *name = glfwGetKeyName(key, vk);
strcpy(dst, name ? name : "");
char *p = dst;
while (*p) {
*p = tolower(*p);
p++;
}
return dst;
}
void lt_globpath(struct lua_State*L, const char *path) {
unsigned j = 0;
if(!strend(path, "/")) path = (const char *)va("%s/", path);
for( dir *d = dir_open(path, ""); d; dir_close(d), d = 0 ) {
for( unsigned i = 0, end = dir_count(d); i < end; ++i ) {
char *name = dir_name(d,i);
char *last = name + strlen(name) - 1;
if( *last == '/' ) *last = '\0';
name = file_name(name);
lua_pushstring(L, name);
lua_rawseti(L, -2, ++j);
}
}
for( const char *section = strstri(path, LT_DATAPATH); section && section[sizeof(LT_DATAPATH)-1] == '/'; section = 0) {
array(char*) list = vfs_list("**");
for( unsigned i = 0, end = array_count(list); i < end; ++i ) {
char *name = list[i];
if( !strstri(name, section+1) ) continue;
lua_pushstring(L, file_name(name));
lua_rawseti(L, -2, ++j);
}
if( array_count(list) ) return;
}
}
int lt_emit_event(lua_State *L, const char *event_name, const char *event_fmt, ...) {
int count = 0;
lua_pushstring(L, event_name);
if( event_fmt ) {
va_list va;
va_start(va, event_fmt);
for( ; event_fmt[count]; ++count ) {
/**/ if( event_fmt[count] == 'd' ) { int d = va_arg(va, int); lua_pushnumber(L, d); }
else if( event_fmt[count] == 'f' ) { double f = va_arg(va, double); lua_pushnumber(L, f); }
else if( event_fmt[count] == 's' ) { const char *s = va_arg(va, const char *); lua_pushstring(L, s); }
}
va_end(va);
}
return 1+count;
}
int printi(int i) {
// printf("clicks: %d\n", i);
return i;
}
static const char* codepoint_to_utf8_(unsigned c);
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
int rc = 0;
char buf[16];
static int prevx = 0, prevy = 0;
static unsigned clicks_time = 0, clicks = 0;
if( (lt_time_ms() - clicks_time) > 400 ) clicks = 0;
//
for( GLEQevent e; gleqNextEvent(&e); gleqFreeEvent(&e) )
if( lt_events & e.type )
switch (e.type) {
default:
break; case GLEQ_WINDOW_CLOSED: // it used to be ok. depends on window_swap() flow
rc += lt_emit_event(L, "quit", NULL);
return rc;
break; case GLEQ_WINDOW_MOVED:
lt_wx = e.pos.x;
lt_wy = e.pos.y;
break; case GLEQ_WINDOW_RESIZED:
rc += lt_emit_event(L, "resized", "dd", lt_ww = e.size.width, lt_wh = e.size.height);
lt_resizesurface(lt_getsurface(lt_window()), lt_ww, lt_wh);
break; case GLEQ_WINDOW_REFRESH:
rc += lt_emit_event(L, "exposed", NULL);
rencache_invalidate();
break; case GLEQ_FILE_DROPPED:
rc += lt_emit_event(L, "filedropped", "sdd", e.file.paths[0], lt_mx, lt_my);
break; case GLEQ_KEY_PRESSED:
case GLEQ_KEY_REPEATED:
rc += lt_emit_event(L, "keypressed", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
goto bottom;
break; case GLEQ_KEY_RELEASED:
rc += lt_emit_event(L, "keyreleased", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
goto bottom;
break; case GLEQ_CODEPOINT_INPUT:
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
break; case GLEQ_BUTTON_PRESSED:
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
break; case GLEQ_BUTTON_RELEASED:
clicks += e.mouse.button == GLFW_MOUSE_BUTTON_1;
clicks_time = lt_time_ms();
rc += lt_emit_event(L, "mousereleased", "sdd", lt_button_name(e.mouse.button), lt_mx, lt_my);
break; case GLEQ_CURSOR_MOVED:
lt_mx = e.pos.x - lt_wx, lt_my = e.pos.y - lt_wy;
rc += lt_emit_event(L, "mousemoved", "dddd", lt_mx, lt_my, lt_mx - prevx, lt_my - prevy);
prevx = lt_mx, prevy = lt_my;
break; case GLEQ_SCROLLED:
rc += lt_emit_event(L, "mousewheel", "f", e.scroll.y);
}
bottom:;
return rc;
}

View File

@ -1,424 +1,424 @@
/*
* GLEQ - A basic event queue for GLFW 3
* Copyright © Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#ifndef GLEQ_HEADER_FILE
#define GLEQ_HEADER_FILE
// #include <GLFW/glfw3.h>
#ifdef GLEQ_STATIC
#define GLEQDEF static
#else
#define GLEQDEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
GLEQ_NONE = 0,
GLEQ_WINDOW_MOVED = 1<<1,
GLEQ_WINDOW_RESIZED = 1<<2,
GLEQ_WINDOW_CLOSED = 1<<3,
GLEQ_WINDOW_REFRESH = 1<<4,
GLEQ_WINDOW_FOCUSED = 1<<5,
GLEQ_WINDOW_DEFOCUSED = 1<<6,
GLEQ_WINDOW_ICONIFIED = 1<<7,
GLEQ_WINDOW_UNICONIFIED = 1<<8,
GLEQ_FRAMEBUFFER_RESIZED = 1<<9,
GLEQ_BUTTON_PRESSED = 1<<10,
GLEQ_BUTTON_RELEASED = 1<<11,
GLEQ_CURSOR_MOVED = 1<<12,
GLEQ_CURSOR_ENTERED = 1<<13,
GLEQ_CURSOR_LEFT = 1<<14,
GLEQ_SCROLLED = 1<<15,
GLEQ_KEY_PRESSED = 1<<16,
GLEQ_KEY_REPEATED = 1<<17,
GLEQ_KEY_RELEASED = 1<<18,
GLEQ_CODEPOINT_INPUT = 1<<19,
GLEQ_MONITOR_CONNECTED = 1<<20,
GLEQ_MONITOR_DISCONNECTED = 1<<21,
#if GLFW_VERSION_MINOR >= 1
GLEQ_FILE_DROPPED = 1<<22,
#endif
#if GLFW_VERSION_MINOR >= 2
GLEQ_JOYSTICK_CONNECTED = 1<<23,
GLEQ_JOYSTICK_DISCONNECTED = 1<<24,
#endif
#if GLFW_VERSION_MINOR >= 3
GLEQ_WINDOW_MAXIMIZED = 1<<25,
GLEQ_WINDOW_UNMAXIMIZED = 1<<26,
GLEQ_WINDOW_SCALE_CHANGED = 1<<27,
#endif
} GLEQtype;
typedef struct GLEQevent
{
unsigned/*GLEQtype*/ type;
union {
GLFWwindow* window;
GLFWmonitor* monitor;
int joystick;
};
union {
struct {
int x;
int y;
} pos;
struct {
int width;
int height;
} size;
struct {
double x;
double y;
} scroll;
struct {
int key;
int scancode;
int mods;
} keyboard;
struct {
int button;
int mods;
} mouse;
unsigned int codepoint;
#if GLFW_VERSION_MINOR >= 1
struct {
char** paths;
int count;
} file;
#endif
#if GLFW_VERSION_MINOR >= 3
struct {
float x;
float y;
} scale;
#endif
};
} GLEQevent;
GLEQDEF void gleqInit(void);
GLEQDEF void gleqTrackWindow(GLFWwindow* window);
GLEQDEF int gleqNextEvent(GLEQevent* event);
GLEQDEF void gleqFreeEvent(GLEQevent* event);
#ifdef __cplusplus
}
#endif
#ifdef GLEQ_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef GLEQ_CAPACITY
#define GLEQ_CAPACITY 1024
#endif
static struct
{
GLEQevent events[GLEQ_CAPACITY];
size_t head;
size_t tail;
} gleq_queue = { {0}, 0, 0 };
static char* gleq_strdup(const char* string)
{
const size_t size = strlen(string) + 1;
char* result = (char*) malloc(size);
memcpy(result, string, size);
return result;
}
static GLEQevent* gleq_new_event(void)
{
GLEQevent* event = gleq_queue.events + gleq_queue.head;
gleq_queue.head = (gleq_queue.head + 1) % GLEQ_CAPACITY;
assert(gleq_queue.head != gleq_queue.tail);
memset(event, 0, sizeof(GLEQevent));
return event;
}
static void gleq_window_pos_callback(GLFWwindow* window, int x, int y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_MOVED;
event->window = window;
event->pos.x = x;
event->pos.y = y;
}
static void gleq_window_size_callback(GLFWwindow* window, int width, int height)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_RESIZED;
event->window = window;
event->size.width = width;
event->size.height = height;
}
static void gleq_window_close_callback(GLFWwindow* window)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_CLOSED;
event->window = window;
}
static void gleq_window_refresh_callback(GLFWwindow* window)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_REFRESH;
event->window = window;
}
static void gleq_window_focus_callback(GLFWwindow* window, int focused)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (focused)
event->type = GLEQ_WINDOW_FOCUSED;
else
event->type = GLEQ_WINDOW_DEFOCUSED;
}
static void gleq_window_iconify_callback(GLFWwindow* window, int iconified)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (iconified)
event->type = GLEQ_WINDOW_ICONIFIED;
else
event->type = GLEQ_WINDOW_UNICONIFIED;
}
static void gleq_framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_FRAMEBUFFER_RESIZED;
event->window = window;
event->size.width = width;
event->size.height = height;
}
static void gleq_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->mouse.button = button;
event->mouse.mods = mods;
if (action == GLFW_PRESS)
event->type = GLEQ_BUTTON_PRESSED;
else if (action == GLFW_RELEASE)
event->type = GLEQ_BUTTON_RELEASED;
}
static void gleq_cursor_pos_callback(GLFWwindow* window, double x, double y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_CURSOR_MOVED;
event->window = window;
event->pos.x = (int) x;
event->pos.y = (int) y;
}
static void gleq_cursor_enter_callback(GLFWwindow* window, int entered)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (entered)
event->type = GLEQ_CURSOR_ENTERED;
else
event->type = GLEQ_CURSOR_LEFT;
}
static void gleq_scroll_callback(GLFWwindow* window, double x, double y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_SCROLLED;
event->window = window;
event->scroll.x = x;
event->scroll.y = y;
}
static void gleq_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->keyboard.key = key;
event->keyboard.scancode = scancode;
event->keyboard.mods = mods;
if (action == GLFW_PRESS)
event->type = GLEQ_KEY_PRESSED;
else if (action == GLFW_RELEASE)
event->type = GLEQ_KEY_RELEASED;
else if (action == GLFW_REPEAT)
event->type = GLEQ_KEY_REPEATED;
}
static void (*gleq_char_callback_prev)(GLFWwindow* window, unsigned int codepoint) = 0;
static void gleq_char_callback(GLFWwindow* window, unsigned int codepoint)
{
if( gleq_char_callback_prev )
gleq_char_callback_prev(window, codepoint);
GLEQevent* event = gleq_new_event();
event->type = GLEQ_CODEPOINT_INPUT;
event->window = window;
event->codepoint = codepoint;
}
static void gleq_monitor_callback(GLFWmonitor* monitor, int action)
{
GLEQevent* event = gleq_new_event();
event->monitor = monitor;
if (action == GLFW_CONNECTED)
event->type = GLEQ_MONITOR_CONNECTED;
else if (action == GLFW_DISCONNECTED)
event->type = GLEQ_MONITOR_DISCONNECTED;
}
#if GLFW_VERSION_MINOR >= 1
static void gleq_file_drop_callback(GLFWwindow* window, int count, const char** paths)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_FILE_DROPPED;
event->window = window;
event->file.paths = (char**) malloc(count * sizeof(char*));
event->file.count = count;
while (count--)
event->file.paths[count] = gleq_strdup(paths[count]);
}
#endif
#if GLFW_VERSION_MINOR >= 2
static void gleq_joystick_callback(int jid, int action)
{
GLEQevent* event = gleq_new_event();
event->joystick = jid;
if (action == GLFW_CONNECTED)
event->type = GLEQ_JOYSTICK_CONNECTED;
else if (action == GLFW_DISCONNECTED)
event->type = GLEQ_JOYSTICK_DISCONNECTED;
}
#endif
#if GLFW_VERSION_MINOR >= 3
static void gleq_window_maximize_callback(GLFWwindow* window, int maximized)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (maximized)
event->type = GLEQ_WINDOW_MAXIMIZED;
else
event->type = GLEQ_WINDOW_UNMAXIMIZED;
}
static void gleq_window_content_scale_callback(GLFWwindow* window, float xscale, float yscale)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->type = GLEQ_WINDOW_SCALE_CHANGED;
event->scale.x = xscale;
event->scale.y = yscale;
}
#endif
GLEQDEF void gleqInit(void)
{
glfwSetMonitorCallback(gleq_monitor_callback);
#if GLFW_VERSION_MINOR >= 2
glfwSetJoystickCallback(gleq_joystick_callback);
#endif
}
GLEQDEF void gleqTrackWindow(GLFWwindow* window)
{
glfwSetWindowPosCallback(window, gleq_window_pos_callback);
glfwSetWindowSizeCallback(window, gleq_window_size_callback);
glfwSetWindowCloseCallback(window, gleq_window_close_callback);
glfwSetWindowRefreshCallback(window, gleq_window_refresh_callback);
glfwSetWindowFocusCallback(window, gleq_window_focus_callback);
glfwSetWindowIconifyCallback(window, gleq_window_iconify_callback);
glfwSetFramebufferSizeCallback(window, gleq_framebuffer_size_callback);
glfwSetMouseButtonCallback(window, gleq_mouse_button_callback);
glfwSetCursorPosCallback(window, gleq_cursor_pos_callback);
glfwSetCursorEnterCallback(window, gleq_cursor_enter_callback);
glfwSetScrollCallback(window, gleq_scroll_callback);
glfwSetKeyCallback(window, gleq_key_callback);
gleq_char_callback_prev = //< @r-lyeh
glfwSetCharCallback(window, gleq_char_callback);
#if GLFW_VERSION_MINOR >= 1
glfwSetDropCallback(window, gleq_file_drop_callback);
#endif
#if GLFW_VERSION_MINOR >= 3
glfwSetWindowMaximizeCallback(window, gleq_window_maximize_callback);
glfwSetWindowContentScaleCallback(window, gleq_window_content_scale_callback);
#endif
}
GLEQDEF int gleqNextEvent(GLEQevent* event)
{
memset(event, 0, sizeof(GLEQevent));
if (gleq_queue.head != gleq_queue.tail)
{
*event = gleq_queue.events[gleq_queue.tail];
gleq_queue.tail = (gleq_queue.tail + 1) % GLEQ_CAPACITY;
}
return event->type != GLEQ_NONE;
}
GLEQDEF void gleqFreeEvent(GLEQevent* event)
{
#if GLFW_VERSION_MINOR >= 1
if (event->type == GLEQ_FILE_DROPPED)
{
while (event->file.count--)
free(event->file.paths[event->file.count]);
free(event->file.paths);
}
#endif
memset(event, 0, sizeof(GLEQevent));
}
#endif /* GLEQ_IMPLEMENTATION */
#endif /* GLEQ_HEADER_FILE */
/*
* GLEQ - A basic event queue for GLFW 3
* Copyright © Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#ifndef GLEQ_HEADER_FILE
#define GLEQ_HEADER_FILE
// #include <GLFW/glfw3.h>
#ifdef GLEQ_STATIC
#define GLEQDEF static
#else
#define GLEQDEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
GLEQ_NONE = 0,
GLEQ_WINDOW_MOVED = 1<<1,
GLEQ_WINDOW_RESIZED = 1<<2,
GLEQ_WINDOW_CLOSED = 1<<3,
GLEQ_WINDOW_REFRESH = 1<<4,
GLEQ_WINDOW_FOCUSED = 1<<5,
GLEQ_WINDOW_DEFOCUSED = 1<<6,
GLEQ_WINDOW_ICONIFIED = 1<<7,
GLEQ_WINDOW_UNICONIFIED = 1<<8,
GLEQ_FRAMEBUFFER_RESIZED = 1<<9,
GLEQ_BUTTON_PRESSED = 1<<10,
GLEQ_BUTTON_RELEASED = 1<<11,
GLEQ_CURSOR_MOVED = 1<<12,
GLEQ_CURSOR_ENTERED = 1<<13,
GLEQ_CURSOR_LEFT = 1<<14,
GLEQ_SCROLLED = 1<<15,
GLEQ_KEY_PRESSED = 1<<16,
GLEQ_KEY_REPEATED = 1<<17,
GLEQ_KEY_RELEASED = 1<<18,
GLEQ_CODEPOINT_INPUT = 1<<19,
GLEQ_MONITOR_CONNECTED = 1<<20,
GLEQ_MONITOR_DISCONNECTED = 1<<21,
#if GLFW_VERSION_MINOR >= 1
GLEQ_FILE_DROPPED = 1<<22,
#endif
#if GLFW_VERSION_MINOR >= 2
GLEQ_JOYSTICK_CONNECTED = 1<<23,
GLEQ_JOYSTICK_DISCONNECTED = 1<<24,
#endif
#if GLFW_VERSION_MINOR >= 3
GLEQ_WINDOW_MAXIMIZED = 1<<25,
GLEQ_WINDOW_UNMAXIMIZED = 1<<26,
GLEQ_WINDOW_SCALE_CHANGED = 1<<27,
#endif
} GLEQtype;
typedef struct GLEQevent
{
unsigned/*GLEQtype*/ type;
union {
GLFWwindow* window;
GLFWmonitor* monitor;
int joystick;
};
union {
struct {
int x;
int y;
} pos;
struct {
int width;
int height;
} size;
struct {
double x;
double y;
} scroll;
struct {
int key;
int scancode;
int mods;
} keyboard;
struct {
int button;
int mods;
} mouse;
unsigned int codepoint;
#if GLFW_VERSION_MINOR >= 1
struct {
char** paths;
int count;
} file;
#endif
#if GLFW_VERSION_MINOR >= 3
struct {
float x;
float y;
} scale;
#endif
};
} GLEQevent;
GLEQDEF void gleqInit(void);
GLEQDEF void gleqTrackWindow(GLFWwindow* window);
GLEQDEF int gleqNextEvent(GLEQevent* event);
GLEQDEF void gleqFreeEvent(GLEQevent* event);
#ifdef __cplusplus
}
#endif
#ifdef GLEQ_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef GLEQ_CAPACITY
#define GLEQ_CAPACITY 1024
#endif
static struct
{
GLEQevent events[GLEQ_CAPACITY];
size_t head;
size_t tail;
} gleq_queue = { {0}, 0, 0 };
static char* gleq_strdup(const char* string)
{
const size_t size = strlen(string) + 1;
char* result = (char*) malloc(size);
memcpy(result, string, size);
return result;
}
static GLEQevent* gleq_new_event(void)
{
GLEQevent* event = gleq_queue.events + gleq_queue.head;
gleq_queue.head = (gleq_queue.head + 1) % GLEQ_CAPACITY;
assert(gleq_queue.head != gleq_queue.tail);
memset(event, 0, sizeof(GLEQevent));
return event;
}
static void gleq_window_pos_callback(GLFWwindow* window, int x, int y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_MOVED;
event->window = window;
event->pos.x = x;
event->pos.y = y;
}
static void gleq_window_size_callback(GLFWwindow* window, int width, int height)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_RESIZED;
event->window = window;
event->size.width = width;
event->size.height = height;
}
static void gleq_window_close_callback(GLFWwindow* window)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_CLOSED;
event->window = window;
}
static void gleq_window_refresh_callback(GLFWwindow* window)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_REFRESH;
event->window = window;
}
static void gleq_window_focus_callback(GLFWwindow* window, int focused)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (focused)
event->type = GLEQ_WINDOW_FOCUSED;
else
event->type = GLEQ_WINDOW_DEFOCUSED;
}
static void gleq_window_iconify_callback(GLFWwindow* window, int iconified)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (iconified)
event->type = GLEQ_WINDOW_ICONIFIED;
else
event->type = GLEQ_WINDOW_UNICONIFIED;
}
static void gleq_framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_FRAMEBUFFER_RESIZED;
event->window = window;
event->size.width = width;
event->size.height = height;
}
static void gleq_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->mouse.button = button;
event->mouse.mods = mods;
if (action == GLFW_PRESS)
event->type = GLEQ_BUTTON_PRESSED;
else if (action == GLFW_RELEASE)
event->type = GLEQ_BUTTON_RELEASED;
}
static void gleq_cursor_pos_callback(GLFWwindow* window, double x, double y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_CURSOR_MOVED;
event->window = window;
event->pos.x = (int) x;
event->pos.y = (int) y;
}
static void gleq_cursor_enter_callback(GLFWwindow* window, int entered)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (entered)
event->type = GLEQ_CURSOR_ENTERED;
else
event->type = GLEQ_CURSOR_LEFT;
}
static void gleq_scroll_callback(GLFWwindow* window, double x, double y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_SCROLLED;
event->window = window;
event->scroll.x = x;
event->scroll.y = y;
}
static void gleq_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->keyboard.key = key;
event->keyboard.scancode = scancode;
event->keyboard.mods = mods;
if (action == GLFW_PRESS)
event->type = GLEQ_KEY_PRESSED;
else if (action == GLFW_RELEASE)
event->type = GLEQ_KEY_RELEASED;
else if (action == GLFW_REPEAT)
event->type = GLEQ_KEY_REPEATED;
}
static void (*gleq_char_callback_prev)(GLFWwindow* window, unsigned int codepoint) = 0;
static void gleq_char_callback(GLFWwindow* window, unsigned int codepoint)
{
if( gleq_char_callback_prev )
gleq_char_callback_prev(window, codepoint);
GLEQevent* event = gleq_new_event();
event->type = GLEQ_CODEPOINT_INPUT;
event->window = window;
event->codepoint = codepoint;
}
static void gleq_monitor_callback(GLFWmonitor* monitor, int action)
{
GLEQevent* event = gleq_new_event();
event->monitor = monitor;
if (action == GLFW_CONNECTED)
event->type = GLEQ_MONITOR_CONNECTED;
else if (action == GLFW_DISCONNECTED)
event->type = GLEQ_MONITOR_DISCONNECTED;
}
#if GLFW_VERSION_MINOR >= 1
static void gleq_file_drop_callback(GLFWwindow* window, int count, const char** paths)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_FILE_DROPPED;
event->window = window;
event->file.paths = (char**) malloc(count * sizeof(char*));
event->file.count = count;
while (count--)
event->file.paths[count] = gleq_strdup(paths[count]);
}
#endif
#if GLFW_VERSION_MINOR >= 2
static void gleq_joystick_callback(int jid, int action)
{
GLEQevent* event = gleq_new_event();
event->joystick = jid;
if (action == GLFW_CONNECTED)
event->type = GLEQ_JOYSTICK_CONNECTED;
else if (action == GLFW_DISCONNECTED)
event->type = GLEQ_JOYSTICK_DISCONNECTED;
}
#endif
#if GLFW_VERSION_MINOR >= 3
static void gleq_window_maximize_callback(GLFWwindow* window, int maximized)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (maximized)
event->type = GLEQ_WINDOW_MAXIMIZED;
else
event->type = GLEQ_WINDOW_UNMAXIMIZED;
}
static void gleq_window_content_scale_callback(GLFWwindow* window, float xscale, float yscale)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->type = GLEQ_WINDOW_SCALE_CHANGED;
event->scale.x = xscale;
event->scale.y = yscale;
}
#endif
GLEQDEF void gleqInit(void)
{
glfwSetMonitorCallback(gleq_monitor_callback);
#if GLFW_VERSION_MINOR >= 2
glfwSetJoystickCallback(gleq_joystick_callback);
#endif
}
GLEQDEF void gleqTrackWindow(GLFWwindow* window)
{
glfwSetWindowPosCallback(window, gleq_window_pos_callback);
glfwSetWindowSizeCallback(window, gleq_window_size_callback);
glfwSetWindowCloseCallback(window, gleq_window_close_callback);
glfwSetWindowRefreshCallback(window, gleq_window_refresh_callback);
glfwSetWindowFocusCallback(window, gleq_window_focus_callback);
glfwSetWindowIconifyCallback(window, gleq_window_iconify_callback);
glfwSetFramebufferSizeCallback(window, gleq_framebuffer_size_callback);
glfwSetMouseButtonCallback(window, gleq_mouse_button_callback);
glfwSetCursorPosCallback(window, gleq_cursor_pos_callback);
glfwSetCursorEnterCallback(window, gleq_cursor_enter_callback);
glfwSetScrollCallback(window, gleq_scroll_callback);
glfwSetKeyCallback(window, gleq_key_callback);
gleq_char_callback_prev = //< @r-lyeh
glfwSetCharCallback(window, gleq_char_callback);
#if GLFW_VERSION_MINOR >= 1
glfwSetDropCallback(window, gleq_file_drop_callback);
#endif
#if GLFW_VERSION_MINOR >= 3
glfwSetWindowMaximizeCallback(window, gleq_window_maximize_callback);
glfwSetWindowContentScaleCallback(window, gleq_window_content_scale_callback);
#endif
}
GLEQDEF int gleqNextEvent(GLEQevent* event)
{
memset(event, 0, sizeof(GLEQevent));
if (gleq_queue.head != gleq_queue.tail)
{
*event = gleq_queue.events[gleq_queue.tail];
gleq_queue.tail = (gleq_queue.tail + 1) % GLEQ_CAPACITY;
}
return event->type != GLEQ_NONE;
}
GLEQDEF void gleqFreeEvent(GLEQevent* event)
{
#if GLFW_VERSION_MINOR >= 1
if (event->type == GLEQ_FILE_DROPPED)
{
while (event->file.count--)
free(event->file.paths[event->file.count]);
free(event->file.paths);
}
#endif
memset(event, 0, sizeof(GLEQevent));
}
#endif /* GLEQ_IMPLEMENTATION */
#endif /* GLEQ_HEADER_FILE */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,483 +1,483 @@
// file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain)
// - rlyeh, public domain
//
// changelog:
// - ported to V4K api
// - namespaced symbols
// - diverse win32 fixes
// - adaptive cols/rows
// - removed nk_begin()/nk_end() pairs
// - dangling nk_group_begin/end() pairs
// - simplified file<->media_group concept
// - minor cosmetics
#ifdef _WIN32
#include <direct.h> // _getcwd()
#else
#include <unistd.h> // getcwd()
#include <pwd.h> // getpwuid()
#define _popen popen
#define _pclose pclose
#endif
const char** old_file_list(const char *cwd, const char *masks) {
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
static __thread array(char*) list = 0;
const char *arg0 = cwd; // app_path();
int larg0 = strlen(arg0);
for( int i = 0; i < array_count(list); ++i ) {
FREE(list[i]);
}
array_resize(list, 0);//array_free(list);
for each_substring(masks,";",it) {
int recurse = !!strstr(it, "**");
#if is(win32)
char *glob = va("dir %s/b/o:n \"%s\\%s\" 2> NUL", recurse ? "/s":"", cwd, it);
#else // linux, osx
char *glob = va("find %s %s -name \"%s\" | sort", cwd, !recurse ? "-maxdepth 1":"-type f", it);
#endif
for( FILE *in = _popen(glob, "r"); in; _pclose(in), in = 0) {
char buf[1024], *line = buf;
while( fgets(buf, sizeof(buf), in) ) {
// clean up
if( strstr(line, arg0) ) line = buf + larg0;
if( !memcmp(line, "./", 2) ) line += 2;
int len = strlen(line); while( len > 0 && line[len-1] < 32 ) line[--len] = 0;
if( line[0] == '\0' ) continue;
// do not insert system folders/files
for(int i = 0; i < len; ++i ) if(line[i] == '\\') line[i] = '/';
if( line[0] == '.' ) if( !strcmp(line,".git") || !strcmp(line,".vs") || !strcmp(line,".") || !strcmp(line,"..") ) continue;
if( strstr(line, "/.") ) continue;
// insert copy
#if is(win32)
char *copy = STRDUP(line); // full path already provided
#else
// while(line[0] == '/') ++line;
char *copy = STRDUP(va("%s%s", cwd, line)); // need to prepend path
#endif
array_push(list, copy);
}
}
}
array_push(list, 0); // terminator
return (const char**)list;
}
#if 1
#define BROWSER_PRINTF(...) do {} while(0)
#else
#define BROWSER_PRINTF printf
#endif
enum browser_groups {
BROWSER_FOLDER,
BROWSER_HOME,
BROWSER_DESKTOP,
BROWSER_COMPUTER,
BROWSER_PROJECT,
BROWSER_MAXFOLDERS,
BROWSER_MAXTYPES = 64,
};
struct browser_media_group {
unsigned icon;
const char *extensions;
};
struct browser_media {
int font;
int icon_sheet;
struct nk_image custom_folders[BROWSER_MAXFOLDERS];
struct nk_image custom_files[BROWSER_MAXTYPES];
struct browser_media_group group[BROWSER_MAXTYPES];
} media = {0};
void browser_config_dir(struct nk_image icon, unsigned counter) {
if( counter < BROWSER_MAXFOLDERS ) {
media.custom_folders[ counter ] = icon;
}
}
void browser_config_type(struct nk_image icon, const char *extensions) {
static int counter = 0;
if( counter < BROWSER_MAXTYPES ) {
media.custom_files[ counter ] = icon;
media.group[ counter ].icon = counter;
media.group[ counter ].extensions = extensions;
++counter;
}
}
#define BROWSER_MAX_PATH 512
struct browser {
/* path */
char file[BROWSER_MAX_PATH]; // selection
char directory[BROWSER_MAX_PATH]; // current cwd while browsing
char home[BROWSER_MAX_PATH];
char desktop[BROWSER_MAX_PATH];
char computer[BROWSER_MAX_PATH];
char project[BROWSER_MAX_PATH]; // cwd when first invoked
/* directory content */
array(char*) files;
array(char*) directories;
size_t file_count;
size_t dir_count;
/* filtered directory content */
array(char*) ffiles;
array(char*) fdirectories;
/* view mode */
bool listing;
float zooming;
};
static struct nk_image* media_icon_for_file(const char *file) {
/* extract extension .xxx from file */
char *ext = strrchr(file, '.');
if( ext && strlen(ext) < 16 ) {
char ext_dot[16+1];
snprintf(ext_dot, 16, "%s.", ext);
/* check for all file definition of all groups for fitting extension. skip first group (default) */
for (int i = 1; i < BROWSER_MAXTYPES && media.group[i].extensions; ++i) {
if( strstri(media.group[i].extensions, ext_dot) ) {
return &media.custom_files[ media.group[i].icon ];
}
}
}
// return first (default) group
return &media.custom_files[0];
}
static void browser_reload_directory_content(struct browser *browser, const char *path) {
if(path[0] == '\0') path = va("./");
if(!strend(path, "/")) path = va("%s/", path);
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
array_resize(browser->files, 0);
array_resize(browser->directories, 0);
BROWSER_PRINTF("searching at %s\n", path);
const char** list = old_file_list(path, "*");
for( int i = 0; list[i]; ++i ) {
char *absolute = file_pathabs(ifndef(win32, list[i], va("%s/%s", path, list[i]))); // ../dir/./file.ext -> c:/prj/dir/file.ext
BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) );
if( file_directory(absolute) ) {
// remove last '/' if present. ok to overwrite absolute var, file_*() API returns writeable strings.
char *dir = absolute; if( dir[ strlen(dir) - 1 ] == '/' ) dir[ strlen(dir) - 1 ] = '\0';
dir = file_name(dir); // /home/rlyeh/prj/v4k/art -> art
BROWSER_PRINTF("%s\n", dir);
if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->directories, STRDUP(dir));
} else {
const char *fname = file_name(absolute);
BROWSER_PRINTF("%s\n", fname);
if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->files, STRDUP(fname));
}
}
browser->file_count = array_count(browser->files);
browser->dir_count = array_count(browser->directories);
}
static void browser_chdir_and_reload_directory_content(struct browser *browser, const char *path) {
if( path != browser->directory ) strncpy(browser->directory, path, BROWSER_MAX_PATH);
browser_reload_directory_content(browser, path);
}
static void browser_init(struct browser *browser) {
memset(browser, 0, sizeof(*browser));
{
/* load files and sub-directory list */
const char *home = getenv("HOME");
#ifdef _WIN32
if (!home) home = getenv("USERPROFILE");
#else
if (!home) home = getpwuid(getuid())->pw_dir;
#endif
snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home);
snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home);
snprintf(browser->computer, BROWSER_MAX_PATH, "%s", ifdef(win32, va("%.*s", 3, getenv("windir")), "/"));
{
ifdef(win32, _getcwd, getcwd)(browser->project, sizeof(browser->project) - 1); // -1 == room for '/'
strcat(browser->project, "/");
}
BROWSER_PRINTF("%s\n", browser->home);
BROWSER_PRINTF("%s\n", browser->desktop);
BROWSER_PRINTF("%s\n", browser->computer);
BROWSER_PRINTF("%s\n", browser->project);
browser_chdir_and_reload_directory_content(browser, browser->project);
}
}
static void browser_free(struct browser *browser) {
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
array_free(browser->files);
array_free(browser->directories);
array_free(browser->ffiles);
array_free(browser->fdirectories);
memset(browser, 0, sizeof(*browser));
}
static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) {
int clicked = 0;
static float ratio[] = {0.25f, NK_UNDEFINED};
float spacing_x = ctx->style.window.spacing.x;
/* output path directory selector in the menubar */
ctx->style.window.spacing.x = 0;
if( windowed ) nk_menubar_begin(ctx);
{
char *d = browser->directory;
#ifdef _WIN32
char *begin = d;
#else
char *begin = d + 1;
#endif
nk_layout_row_template_begin(ctx, 25);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_end(ctx);
if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) {
browser->listing ^= 1;
}
while (*d++) {
if (*d == '/') {
*d = '\0';
if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) {
*d++ = '/'; *d = '\0';
browser_chdir_and_reload_directory_content(browser, browser->directory);
break;
}
*d = '/';
begin = d + 1;
}
}
}
if( windowed ) nk_menubar_end(ctx);
ctx->style.window.spacing.x = spacing_x;
if(nk_window_has_focus(ctx)) {
browser->zooming = clampf( browser->zooming + (input(KEY_LCTRL) || input(KEY_RCTRL)) * input_diff(MOUSE_W) * 0.1, 1, 3);
}
bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons
size_t cols = total_space.w / (100 * browser->zooming);
int icon_height = (67 * browser->zooming) * (tiny ? 0.33 : 1.); // icon height (96) + button padding (??). originally: 135
/**/ if( tiny ) cols = (int)cols+1.5, cols /= 2, compact = total_space.w < 500; // cols <= 2;
else cols = (int)cols+1, compact = total_space.w < 500; // cols <= 5;
if( cols < 1 ) cols=1;
/* window layout */
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio);
if( !compact )
if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) {
nk_layout_row_dynamic(ctx, 40, 1);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->home);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->desktop);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->computer);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->project);
nk_group_end(ctx);
}
/* output directory content window */
if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) {
array(char*) *directories = &browser->directories;
array(char*) *files = &browser->files;
if( ui_filter && ui_filter[0] ) {
array_resize(browser->fdirectories, 0);
array_resize(browser->ffiles, 0);
for each_array(browser->directories,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->fdirectories, k);
for each_array(browser->files,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->ffiles, k);
directories = &browser->fdirectories;
files = &browser->ffiles;
}
int dir_count = array_count(*directories);
int file_count = array_count(*files);
int index = -1;
size_t i = 0, j = 0, k = 0;
size_t rows = 0;
size_t count = dir_count + file_count;
rows = count / cols;
for (i = 0; i <= rows; i += 1) {
if(!tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER]))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
if (nk_button_image(ctx, *icon)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
}}
if(!tiny)
{size_t n = k + cols;
nk_layout_row_dynamic(ctx, 20, (int)cols);
for (; k < count && k < n; k++) {
size_t t = k-dir_count;
/* draw one row of labels */
if (k < dir_count) {
nk_label(ctx, (*directories)[k], NK_TEXT_CENTERED);
} else {
nk_label(ctx, (*files)[t], NK_TEXT_CENTERED);
}
}}
if(tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], (*directories)[j], NK_TEXT_RIGHT))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
if (nk_button_image_label(ctx, *icon, (*files)[t],NK_TEXT_RIGHT)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
#if 0
bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop
bool has_popups = ui_popups(); // @fixme: move out of loop
if( !has_popups && has_focus ) {
struct nk_rect bounds = nk_widget_bounds(ctx);
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) {
char *name = j < dir_count ? (*directories)[j] : (*files)[j-dir_count];
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name);
struct stat t = {0};
if( stat( fullpath, &t ) != -1 ) {
char tooltip[256];
snprintf(tooltip, 256,
"Path: %s\n"
"Type: %lld\n" // file type and mode
"Size: %lld\n" // file size
"Owner: %lld\n" // user ID of file owner
"Modified: %s (%lld)", // last modification date
name, (int64_t)t.st_mode, (int64_t)t.st_size, (int64_t)t.st_uid, ctime(&t.st_mtime), (int64_t)t.st_mtime
);
nk_tooltip(ctx, tooltip);
}
}
}
#endif
}}
}
if (index != -1) {
BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]);
size_t n = strlen(browser->directory);
snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]);
BROWSER_PRINTF("%s\n", browser->directory);
browser_chdir_and_reload_directory_content(browser, browser->directory);
}
nk_group_end(ctx);
}
return clicked;
}
static struct nk_image icon_load(const char *filename) {
texture_t t = texture(filename, 0);
return nk_image_id((int)t.id);
}
static struct nk_image icon_load_rect(unsigned id, unsigned w, unsigned h, unsigned wcell, unsigned hcell, unsigned col, unsigned row) {
return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell });
}
/* demo:
struct browser browser = {0};
browser_init(&browser);
browser_config_dir(nk_image, BROWSER_HOME);
browser_config_dir(nk_image, BROWSER_PROJECT);
// [...]
browser_config_type(nk_image, ".ext1.ext2.ext3.");
browser_config_type(nk_image, ".ext1.ext2.ext3.");
browser_config_type(nk_image, ".ext1.ext2.ext3.");
// [...]
[...]
if( nk_begin(ctx, "window", ...) ) {
struct nk_rect total_space = nk_window_get_content_region(ctx);
if( browser_run(ctx, &browser, 0, total_space) ) {
puts( browser->directory );
puts( browser->file );
}
}
nk_end();
*/
// file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain)
// - rlyeh, public domain
//
// changelog:
// - ported to V4K api
// - namespaced symbols
// - diverse win32 fixes
// - adaptive cols/rows
// - removed nk_begin()/nk_end() pairs
// - dangling nk_group_begin/end() pairs
// - simplified file<->media_group concept
// - minor cosmetics
#ifdef _WIN32
#include <direct.h> // _getcwd()
#else
#include <unistd.h> // getcwd()
#include <pwd.h> // getpwuid()
#define _popen popen
#define _pclose pclose
#endif
const char** old_file_list(const char *cwd, const char *masks) {
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
static __thread array(char*) list = 0;
const char *arg0 = cwd; // app_path();
int larg0 = strlen(arg0);
for( int i = 0; i < array_count(list); ++i ) {
FREE(list[i]);
}
array_resize(list, 0);//array_free(list);
for each_substring(masks,";",it) {
int recurse = !!strstr(it, "**");
#if is(win32)
char *glob = va("dir %s/b/o:n \"%s\\%s\" 2> NUL", recurse ? "/s":"", cwd, it);
#else // linux, osx
char *glob = va("find %s %s -name \"%s\" | sort", cwd, !recurse ? "-maxdepth 1":"-type f", it);
#endif
for( FILE *in = _popen(glob, "r"); in; _pclose(in), in = 0) {
char buf[1024], *line = buf;
while( fgets(buf, sizeof(buf), in) ) {
// clean up
if( strstr(line, arg0) ) line = buf + larg0;
if( !memcmp(line, "./", 2) ) line += 2;
int len = strlen(line); while( len > 0 && line[len-1] < 32 ) line[--len] = 0;
if( line[0] == '\0' ) continue;
// do not insert system folders/files
for(int i = 0; i < len; ++i ) if(line[i] == '\\') line[i] = '/';
if( line[0] == '.' ) if( !strcmp(line,".git") || !strcmp(line,".vs") || !strcmp(line,".") || !strcmp(line,"..") ) continue;
if( strstr(line, "/.") ) continue;
// insert copy
#if is(win32)
char *copy = STRDUP(line); // full path already provided
#else
// while(line[0] == '/') ++line;
char *copy = STRDUP(va("%s%s", cwd, line)); // need to prepend path
#endif
array_push(list, copy);
}
}
}
array_push(list, 0); // terminator
return (const char**)list;
}
#if 1
#define BROWSER_PRINTF(...) do {} while(0)
#else
#define BROWSER_PRINTF printf
#endif
enum browser_groups {
BROWSER_FOLDER,
BROWSER_HOME,
BROWSER_DESKTOP,
BROWSER_COMPUTER,
BROWSER_PROJECT,
BROWSER_MAXFOLDERS,
BROWSER_MAXTYPES = 64,
};
struct browser_media_group {
unsigned icon;
const char *extensions;
};
struct browser_media {
int font;
int icon_sheet;
struct nk_image custom_folders[BROWSER_MAXFOLDERS];
struct nk_image custom_files[BROWSER_MAXTYPES];
struct browser_media_group group[BROWSER_MAXTYPES];
} media = {0};
void browser_config_dir(struct nk_image icon, unsigned counter) {
if( counter < BROWSER_MAXFOLDERS ) {
media.custom_folders[ counter ] = icon;
}
}
void browser_config_type(struct nk_image icon, const char *extensions) {
static int counter = 0;
if( counter < BROWSER_MAXTYPES ) {
media.custom_files[ counter ] = icon;
media.group[ counter ].icon = counter;
media.group[ counter ].extensions = extensions;
++counter;
}
}
#define BROWSER_MAX_PATH 512
struct browser {
/* path */
char file[BROWSER_MAX_PATH]; // selection
char directory[BROWSER_MAX_PATH]; // current cwd while browsing
char home[BROWSER_MAX_PATH];
char desktop[BROWSER_MAX_PATH];
char computer[BROWSER_MAX_PATH];
char project[BROWSER_MAX_PATH]; // cwd when first invoked
/* directory content */
array(char*) files;
array(char*) directories;
size_t file_count;
size_t dir_count;
/* filtered directory content */
array(char*) ffiles;
array(char*) fdirectories;
/* view mode */
bool listing;
float zooming;
};
static struct nk_image* media_icon_for_file(const char *file) {
/* extract extension .xxx from file */
char *ext = strrchr(file, '.');
if( ext && strlen(ext) < 16 ) {
char ext_dot[16+1];
snprintf(ext_dot, 16, "%s.", ext);
/* check for all file definition of all groups for fitting extension. skip first group (default) */
for (int i = 1; i < BROWSER_MAXTYPES && media.group[i].extensions; ++i) {
if( strstri(media.group[i].extensions, ext_dot) ) {
return &media.custom_files[ media.group[i].icon ];
}
}
}
// return first (default) group
return &media.custom_files[0];
}
static void browser_reload_directory_content(struct browser *browser, const char *path) {
if(path[0] == '\0') path = va("./");
if(!strend(path, "/")) path = va("%s/", path);
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
array_resize(browser->files, 0);
array_resize(browser->directories, 0);
BROWSER_PRINTF("searching at %s\n", path);
const char** list = old_file_list(path, "*");
for( int i = 0; list[i]; ++i ) {
char *absolute = file_pathabs(ifndef(win32, list[i], va("%s/%s", path, list[i]))); // ../dir/./file.ext -> c:/prj/dir/file.ext
BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) );
if( file_directory(absolute) ) {
// remove last '/' if present. ok to overwrite absolute var, file_*() API returns writeable strings.
char *dir = absolute; if( dir[ strlen(dir) - 1 ] == '/' ) dir[ strlen(dir) - 1 ] = '\0';
dir = file_name(dir); // /home/rlyeh/prj/v4k/art -> art
BROWSER_PRINTF("%s\n", dir);
if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->directories, STRDUP(dir));
} else {
const char *fname = file_name(absolute);
BROWSER_PRINTF("%s\n", fname);
if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->files, STRDUP(fname));
}
}
browser->file_count = array_count(browser->files);
browser->dir_count = array_count(browser->directories);
}
static void browser_chdir_and_reload_directory_content(struct browser *browser, const char *path) {
if( path != browser->directory ) strncpy(browser->directory, path, BROWSER_MAX_PATH);
browser_reload_directory_content(browser, path);
}
static void browser_init(struct browser *browser) {
memset(browser, 0, sizeof(*browser));
{
/* load files and sub-directory list */
const char *home = getenv("HOME");
#ifdef _WIN32
if (!home) home = getenv("USERPROFILE");
#else
if (!home) home = getpwuid(getuid())->pw_dir;
#endif
snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home);
snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home);
snprintf(browser->computer, BROWSER_MAX_PATH, "%s", ifdef(win32, va("%.*s", 3, getenv("windir")), "/"));
{
ifdef(win32, _getcwd, getcwd)(browser->project, sizeof(browser->project) - 1); // -1 == room for '/'
strcat(browser->project, "/");
}
BROWSER_PRINTF("%s\n", browser->home);
BROWSER_PRINTF("%s\n", browser->desktop);
BROWSER_PRINTF("%s\n", browser->computer);
BROWSER_PRINTF("%s\n", browser->project);
browser_chdir_and_reload_directory_content(browser, browser->project);
}
}
static void browser_free(struct browser *browser) {
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
array_free(browser->files);
array_free(browser->directories);
array_free(browser->ffiles);
array_free(browser->fdirectories);
memset(browser, 0, sizeof(*browser));
}
static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) {
int clicked = 0;
static float ratio[] = {0.25f, NK_UNDEFINED};
float spacing_x = ctx->style.window.spacing.x;
/* output path directory selector in the menubar */
ctx->style.window.spacing.x = 0;
if( windowed ) nk_menubar_begin(ctx);
{
char *d = browser->directory;
#ifdef _WIN32
char *begin = d;
#else
char *begin = d + 1;
#endif
nk_layout_row_template_begin(ctx, 25);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_end(ctx);
if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) {
browser->listing ^= 1;
}
while (*d++) {
if (*d == '/') {
*d = '\0';
if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) {
*d++ = '/'; *d = '\0';
browser_chdir_and_reload_directory_content(browser, browser->directory);
break;
}
*d = '/';
begin = d + 1;
}
}
}
if( windowed ) nk_menubar_end(ctx);
ctx->style.window.spacing.x = spacing_x;
if(nk_window_has_focus(ctx)) {
browser->zooming = clampf( browser->zooming + (input(KEY_LCTRL) || input(KEY_RCTRL)) * input_diff(MOUSE_W) * 0.1, 1, 3);
}
bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons
size_t cols = total_space.w / (100 * browser->zooming);
int icon_height = (67 * browser->zooming) * (tiny ? 0.33 : 1.); // icon height (96) + button padding (??). originally: 135
/**/ if( tiny ) cols = (int)cols+1.5, cols /= 2, compact = total_space.w < 500; // cols <= 2;
else cols = (int)cols+1, compact = total_space.w < 500; // cols <= 5;
if( cols < 1 ) cols=1;
/* window layout */
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio);
if( !compact )
if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) {
nk_layout_row_dynamic(ctx, 40, 1);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->home);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->desktop);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->computer);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->project);
nk_group_end(ctx);
}
/* output directory content window */
if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) {
array(char*) *directories = &browser->directories;
array(char*) *files = &browser->files;
if( ui_filter && ui_filter[0] ) {
array_resize(browser->fdirectories, 0);
array_resize(browser->ffiles, 0);
for each_array(browser->directories,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->fdirectories, k);
for each_array(browser->files,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->ffiles, k);
directories = &browser->fdirectories;
files = &browser->ffiles;
}
int dir_count = array_count(*directories);
int file_count = array_count(*files);
int index = -1;
size_t i = 0, j = 0, k = 0;
size_t rows = 0;
size_t count = dir_count + file_count;
rows = count / cols;
for (i = 0; i <= rows; i += 1) {
if(!tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER]))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
if (nk_button_image(ctx, *icon)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
}}
if(!tiny)
{size_t n = k + cols;
nk_layout_row_dynamic(ctx, 20, (int)cols);
for (; k < count && k < n; k++) {
size_t t = k-dir_count;
/* draw one row of labels */
if (k < dir_count) {
nk_label(ctx, (*directories)[k], NK_TEXT_CENTERED);
} else {
nk_label(ctx, (*files)[t], NK_TEXT_CENTERED);
}
}}
if(tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], (*directories)[j], NK_TEXT_RIGHT))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
if (nk_button_image_label(ctx, *icon, (*files)[t],NK_TEXT_RIGHT)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
#if 0
bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop
bool has_popups = ui_popups(); // @fixme: move out of loop
if( !has_popups && has_focus ) {
struct nk_rect bounds = nk_widget_bounds(ctx);
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) {
char *name = j < dir_count ? (*directories)[j] : (*files)[j-dir_count];
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name);
struct stat t = {0};
if( stat( fullpath, &t ) != -1 ) {
char tooltip[256];
snprintf(tooltip, 256,
"Path: %s\n"
"Type: %lld\n" // file type and mode
"Size: %lld\n" // file size
"Owner: %lld\n" // user ID of file owner
"Modified: %s (%lld)", // last modification date
name, (int64_t)t.st_mode, (int64_t)t.st_size, (int64_t)t.st_uid, ctime(&t.st_mtime), (int64_t)t.st_mtime
);
nk_tooltip(ctx, tooltip);
}
}
}
#endif
}}
}
if (index != -1) {
BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]);
size_t n = strlen(browser->directory);
snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]);
BROWSER_PRINTF("%s\n", browser->directory);
browser_chdir_and_reload_directory_content(browser, browser->directory);
}
nk_group_end(ctx);
}
return clicked;
}
static struct nk_image icon_load(const char *filename) {
texture_t t = texture(filename, 0);
return nk_image_id((int)t.id);
}
static struct nk_image icon_load_rect(unsigned id, unsigned w, unsigned h, unsigned wcell, unsigned hcell, unsigned col, unsigned row) {
return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell });
}
/* demo:
struct browser browser = {0};
browser_init(&browser);
browser_config_dir(nk_image, BROWSER_HOME);
browser_config_dir(nk_image, BROWSER_PROJECT);
// [...]
browser_config_type(nk_image, ".ext1.ext2.ext3.");
browser_config_type(nk_image, ".ext1.ext2.ext3.");
browser_config_type(nk_image, ".ext1.ext2.ext3.");
// [...]
[...]
if( nk_begin(ctx, "window", ...) ) {
struct nk_rect total_space = nk_window_get_content_region(ctx);
if( browser_run(ctx, &browser, 0, total_space) ) {
puts( browser->directory );
puts( browser->file );
}
}
nk_end();
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,409 +1,409 @@
/* Progressive Mesh type Polygon Reduction Algorithm
*
* 1998: Original version by Stan Melax (c) 1998
* Permission to use any of this code wherever you want is granted..
* Although, please do acknowledge authorship if appropriate.
*
* 2014: Code style upgraded to be more consistent with graphics/gamedev conventions. Relicensed as MIT/PD.
* Stan Melax: "Yes, this code can be licensed with the same license as the original. That should be fine."
*
* 2020: C version by Cloud Wu (c) 2020. Licensed as MIT/PD.
*/
static inline void array_find_and_remove(array(int) arr, int v) {
for( int i = 0, end = array_count(arr); i < end; i++ )
if( arr[i] == v ) { array_erase_fast(arr, i); --end; break; }
}
#include <assert.h>
#include <math.h>
#include <stdlib.h>
struct triangle_n {
int vertex[3]; // the 3 points (id) that make this tri
vec3 normal; // unit vector othogonal to this face
};
struct vertex {
vec3 position; // location of point in euclidean space
array(int) neighbor; // adjacent vertices
array(int) face; // adjacent triangles
int id; // place of vertex in original Array
int collapse; // candidate vertex (id) for collapse
float objdist; // cached cost of collapsing edge
};
struct mesh {
struct vertex *v;
struct triangle_n *t;
int n_face;
int n_vertex;
};
// array
static inline struct vertex *Vertex(struct mesh *M, int id) { return M->v + id; }
static inline struct triangle_n *Triangle(struct mesh *M, int id) { return M->t + id; }
static inline struct triangle_n *Face(struct mesh *M, struct vertex *v, int idx) { return M->t + v->face[idx]; }
static void AddVertex(struct mesh *M, const float *v) {
int id = M->n_vertex++;
struct vertex * tmp = Vertex(M, id);
tmp->position = ptr3(v);
tmp->neighbor = NULL;
tmp->face = NULL;
tmp->id = id;
tmp->collapse = -1;
tmp->objdist = 0;
}
static void RemoveVertex(struct mesh *M, int id) {
struct vertex * v = Vertex(M, id);
ASSERT(v->id == id);
ASSERT(array_count(v->face) == 0);
for (int i=0;i<array_count(v->face);i++) {
struct vertex * nv = Vertex(M, v->face[i]);
array_find_and_remove(nv->neighbor, id);
}
v->id = -1; // invalid vertex id
array_free(v->neighbor);
array_free(v->face);
}
static void ComputeNormal(struct mesh *M, struct triangle_n *t) {
struct vertex * v0 = Vertex(M, t->vertex[0]);
struct vertex * v1 = Vertex(M, t->vertex[1]);
struct vertex * v2 = Vertex(M, t->vertex[2]);
vec3 a = sub3(v1->position, v0->position);
vec3 b = sub3(v2->position, v1->position);
t->normal = norm3(cross3(a,b));
}
static void AddNeighbor(struct mesh *M, int vid, int id) {
struct vertex *v = Vertex(M, vid);
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id)
return;
}
array_push(v->neighbor, id);
}
static void AddTriangle(struct mesh *M, const int v[3]) {
if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2])
return;
ASSERT(v[0] < M->n_vertex);
ASSERT(v[1] < M->n_vertex);
ASSERT(v[2] < M->n_vertex);
int id = M->n_face++;
struct triangle_n * tmp = Triangle(M, id);
tmp->vertex[0] = v[0];
tmp->vertex[1] = v[1];
tmp->vertex[2] = v[2];
ComputeNormal(M, tmp);
for(int i=0;i<3;i++) {
struct vertex *obj = Vertex(M, v[i]);
array_push(obj->face, id);
}
AddNeighbor(M, v[0], v[1]);
AddNeighbor(M, v[0], v[2]);
AddNeighbor(M, v[1], v[0]);
AddNeighbor(M, v[1], v[2]);
AddNeighbor(M, v[2], v[0]);
AddNeighbor(M, v[2], v[1]);
}
static int HasVertex(struct triangle_n * t, int vid) {
return (t->vertex[0] == vid || t->vertex[1] == vid || t->vertex[2] == vid);
}
static void RemoveIfNonNeighbor_(struct mesh *M, struct vertex *v, int id) {
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id) {
for (int j=0;j<array_count(v->face);j++) {
if (HasVertex(Face(M, v, j), id))
return;
}
// remove from neighbors
array_erase_fast(v->neighbor, i);
return;
}
}
}
static void RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) {
if (v0 == NULL || v1 == NULL)
return;
RemoveIfNonNeighbor_(M, v0, v1->id);
RemoveIfNonNeighbor_(M, v1, v0->id);
}
static void RemoveTriangle(struct mesh *M, int id) {
struct triangle_n * face = Triangle(M, id);
struct vertex * v[3];
for (int i=0;i<3;i++) {
v[i] = Vertex(M, face->vertex[i]);
if (v[i]->id < 0)
v[i] = NULL;
else {
array_find_and_remove(v[i]->face, id);
}
}
RemoveIfNonNeighbor(M, v[0], v[1]);
RemoveIfNonNeighbor(M, v[1], v[2]);
RemoveIfNonNeighbor(M, v[2], v[0]);
}
static void ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) {
struct triangle_n * face = Triangle(M, faceid);
ASSERT(oldid >=0 && newid >= 0);
ASSERT(HasVertex(face, oldid));
ASSERT(!HasVertex(face, newid));
if(oldid==face->vertex[0]){
face->vertex[0]=newid;
} else if(oldid==face->vertex[1]){
face->vertex[1]=newid;
} else {
face->vertex[2]=newid;
}
struct vertex *vold = Vertex(M, oldid);
struct vertex *vnew = Vertex(M, newid);
array_find_and_remove(vold->face, faceid);
array_push(vnew->face, faceid);
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[0]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[1]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[2]));
AddNeighbor(M, face->vertex[0], face->vertex[1]);
AddNeighbor(M, face->vertex[0], face->vertex[2]);
AddNeighbor(M, face->vertex[1], face->vertex[0]);
AddNeighbor(M, face->vertex[1], face->vertex[2]);
AddNeighbor(M, face->vertex[2], face->vertex[0]);
AddNeighbor(M, face->vertex[2], face->vertex[1]);
ComputeNormal(M, face);
}
static void MeshInit(struct mesh *M, int vert_n, int tri_n) {
M->n_face = 0;
M->n_vertex = 0;
M->v = (struct vertex *)MALLOC(vert_n * sizeof(struct vertex));
M->t = (struct triangle_n *)MALLOC(tri_n * sizeof(struct triangle));
}
static void MeshFree(struct mesh *M) {
FREE(M->v);
FREE(M->t);
}
static float ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) {
// if we collapse edge uv by moving u to v then how
// much different will the model change, i.e. how much "error".
// Texture, vertex normal, and border vertex code was removed
// to keep this demo as simple as possible.
// The method of determining cost was designed in order
// to exploit small and coplanar regions for
// effective polygon reduction.
// Is is possible to add some checks here to see if "folds"
// would be generated. i.e. normal of a remaining face gets
// flipped. I never seemed to run into this problem and
// therefore never added code to detect this case.
struct vertex *v = Vertex(M, vid);
vec3 tmp = sub3(v->position, u->position);
float edgelength = len3(tmp);
float curvature=0;
// find the "sides" triangles that are on the edge uv
array(int) sides = 0;
for (int i = 0; i<array_count(u->face); i++) {
if (HasVertex(Face(M, u, i), vid)) {
array_push(sides, u->face[i]);
}
}
// use the triangle facing most away from the sides
// to determine our curvature term
for (int i = 0; i<array_count(u->face); i++) {
float mincurv=1; // curve for face i and closer side to it
for (int j = 0; j<array_count(sides); j++) {
float dotprod = dot3(Triangle(M, u->face[i])->normal,
Triangle(M, sides[j])->normal); // use dot product of face normals.
float t = (1-dotprod)/2.0f;
if (t < mincurv) {
mincurv = t;
}
}
if (mincurv > curvature)
curvature = mincurv;
}
array_free(sides);
// the more coplanar the lower the curvature term
return edgelength * curvature;
}
static void ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) {
// compute the edge collapse cost for all edges that start
// from vertex v. Since we are only interested in reducing
// the object by selecting the min cost edge at each step, we
// only cache the cost of the least cost edge at this vertex
// (in member variable collapse) as well as the value of the
// cost (in member variable objdist).
if (array_count(v->neighbor) == 0) {
// v doesn't have neighbors so it costs nothing to collapse
v->collapse=-1;
v->objdist=-0.01f;
return;
}
v->objdist = 1000000;
v->collapse=-1;
// search all neighboring edges for "least cost" edge
for (int i = 0; i<array_count(v->neighbor); i++) {
float dist = ComputeEdgeCollapseCost(M, v, v->neighbor[i]);
if(dist<v->objdist) {
v->collapse=v->neighbor[i]; // candidate for edge collapse
v->objdist=dist; // cost of the collapse
}
}
}
static void ComputeAllEdgeCollapseCosts(struct mesh *M) {
// For all the edges, compute the difference it would make
// to the model if it was collapsed. The least of these
// per vertex is cached in each vertex object.
for (int i = 0; i<M->n_vertex; i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, i));
}
}
static void Collapse(struct mesh *M, int uid, int vid) {
// Collapse the edge uv by moving vertex u onto v
// Actually remove tris on uv, then update tris that
// have u to have v, and then remove u.
struct vertex *u = Vertex(M, uid);
if(vid < 0) {
// u is a vertex all by itself so just delete it
RemoveVertex(M, uid);
return;
}
array(int) tmp = 0;
// make tmp a Array of all the neighbors of u
for (int i = 0; i<array_count(u->neighbor); i++) {
array_push(tmp, u->neighbor[i]);
}
// delete triangles on edge uv:
for( int i = array_count(u->face); i--; ) {
if (HasVertex(Face(M, u, i), vid)) {
RemoveTriangle(M, u->face[i]);
}
}
// update remaining triangles to have v instead of u
for( int i = array_count(u->face); i--; ) {
ReplaceVertex(M, u->face[i], uid, vid);
}
RemoveVertex(M, uid);
// recompute the edge collapse costs for neighboring vertices
for (int i = 0; i<array_count(tmp); i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, tmp[i]));
}
array_free(tmp);
}
static struct vertex *MinimumCostEdge(struct mesh *M) {
// Find the edge that when collapsed will affect model the least.
// This function actually returns a Vertex, the second vertex
// of the edge (collapse candidate) is stored in the vertex data.
// Serious optimization opportunity here: this function currently
// does a sequential search through an unsorted Array :-(
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
struct vertex *mn = NULL;
for (int i = 0; i<M->n_vertex; i++) {
struct vertex *v = Vertex(M, i);
if (v->id >=0) {
if (mn == NULL || v->objdist < mn->objdist) {
mn = v;
}
}
}
return mn;
}
/*
* The function ProgressiveMesh() takes a model in an "indexed face
* set" sort of way. i.e. Array of vertices and Array of triangles.
* The function then does the polygon reduction algorithm
* internally and reduces the model all the way down to 0
* vertices and then returns the order in which the
* vertices are collapsed and to which neighbor each vertex
* is collapsed to. More specifically the returned "permutation"
* indicates how to reorder your vertices so you can render
* an object by using the first n vertices (for the n
* vertex version). After permuting your vertices, the
* map Array indicates to which vertex each vertex is collapsed to.
*/
API void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation) {
struct mesh M;
MeshInit(&M, vert_n, tri_n);
// put input data into our data structures M
const char * tmp = (const char *)v;
for (int i=0;i<vert_n;i++, tmp += vert_stride ) {
AddVertex(&M, (const float *)tmp);
}
for (int i=0;i<tri_n;i++) {
AddTriangle(&M, &tri[i*3]);
}
ComputeAllEdgeCollapseCosts(&M); // cache all edge collapse costs
for (int i = vert_n-1; i>=0; i--) {
// get the next vertex to collapse
struct vertex *mn = MinimumCostEdge(&M);
// keep track of this vertex, i.e. the collapse ordering
permutation[mn->id] = i;
// keep track of vertex to which we collapse to
map[i] = mn->collapse;
// Collapse this edge
Collapse(&M, mn->id, mn->collapse);
}
// reorder the map Array based on the collapse ordering
for (int i = 0; i<vert_n; i++) {
map[i] = (map[i]==-1)?0:permutation[map[i]];
}
// The caller of this function should reorder their vertices
// according to the returned "permutation".
MeshFree(&M);
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Stan Melax
* Copyright (c) 2020 Cloud Wu
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* Progressive Mesh type Polygon Reduction Algorithm
*
* 1998: Original version by Stan Melax (c) 1998
* Permission to use any of this code wherever you want is granted..
* Although, please do acknowledge authorship if appropriate.
*
* 2014: Code style upgraded to be more consistent with graphics/gamedev conventions. Relicensed as MIT/PD.
* Stan Melax: "Yes, this code can be licensed with the same license as the original. That should be fine."
*
* 2020: C version by Cloud Wu (c) 2020. Licensed as MIT/PD.
*/
static inline void array_find_and_remove(array(int) arr, int v) {
for( int i = 0, end = array_count(arr); i < end; i++ )
if( arr[i] == v ) { array_erase_fast(arr, i); --end; break; }
}
#include <assert.h>
#include <math.h>
#include <stdlib.h>
struct triangle_n {
int vertex[3]; // the 3 points (id) that make this tri
vec3 normal; // unit vector othogonal to this face
};
struct vertex {
vec3 position; // location of point in euclidean space
array(int) neighbor; // adjacent vertices
array(int) face; // adjacent triangles
int id; // place of vertex in original Array
int collapse; // candidate vertex (id) for collapse
float objdist; // cached cost of collapsing edge
};
struct mesh {
struct vertex *v;
struct triangle_n *t;
int n_face;
int n_vertex;
};
// array
static inline struct vertex *Vertex(struct mesh *M, int id) { return M->v + id; }
static inline struct triangle_n *Triangle(struct mesh *M, int id) { return M->t + id; }
static inline struct triangle_n *Face(struct mesh *M, struct vertex *v, int idx) { return M->t + v->face[idx]; }
static void AddVertex(struct mesh *M, const float *v) {
int id = M->n_vertex++;
struct vertex * tmp = Vertex(M, id);
tmp->position = ptr3(v);
tmp->neighbor = NULL;
tmp->face = NULL;
tmp->id = id;
tmp->collapse = -1;
tmp->objdist = 0;
}
static void RemoveVertex(struct mesh *M, int id) {
struct vertex * v = Vertex(M, id);
ASSERT(v->id == id);
ASSERT(array_count(v->face) == 0);
for (int i=0;i<array_count(v->face);i++) {
struct vertex * nv = Vertex(M, v->face[i]);
array_find_and_remove(nv->neighbor, id);
}
v->id = -1; // invalid vertex id
array_free(v->neighbor);
array_free(v->face);
}
static void ComputeNormal(struct mesh *M, struct triangle_n *t) {
struct vertex * v0 = Vertex(M, t->vertex[0]);
struct vertex * v1 = Vertex(M, t->vertex[1]);
struct vertex * v2 = Vertex(M, t->vertex[2]);
vec3 a = sub3(v1->position, v0->position);
vec3 b = sub3(v2->position, v1->position);
t->normal = norm3(cross3(a,b));
}
static void AddNeighbor(struct mesh *M, int vid, int id) {
struct vertex *v = Vertex(M, vid);
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id)
return;
}
array_push(v->neighbor, id);
}
static void AddTriangle(struct mesh *M, const int v[3]) {
if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2])
return;
ASSERT(v[0] < M->n_vertex);
ASSERT(v[1] < M->n_vertex);
ASSERT(v[2] < M->n_vertex);
int id = M->n_face++;
struct triangle_n * tmp = Triangle(M, id);
tmp->vertex[0] = v[0];
tmp->vertex[1] = v[1];
tmp->vertex[2] = v[2];
ComputeNormal(M, tmp);
for(int i=0;i<3;i++) {
struct vertex *obj = Vertex(M, v[i]);
array_push(obj->face, id);
}
AddNeighbor(M, v[0], v[1]);
AddNeighbor(M, v[0], v[2]);
AddNeighbor(M, v[1], v[0]);
AddNeighbor(M, v[1], v[2]);
AddNeighbor(M, v[2], v[0]);
AddNeighbor(M, v[2], v[1]);
}
static int HasVertex(struct triangle_n * t, int vid) {
return (t->vertex[0] == vid || t->vertex[1] == vid || t->vertex[2] == vid);
}
static void RemoveIfNonNeighbor_(struct mesh *M, struct vertex *v, int id) {
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id) {
for (int j=0;j<array_count(v->face);j++) {
if (HasVertex(Face(M, v, j), id))
return;
}
// remove from neighbors
array_erase_fast(v->neighbor, i);
return;
}
}
}
static void RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) {
if (v0 == NULL || v1 == NULL)
return;
RemoveIfNonNeighbor_(M, v0, v1->id);
RemoveIfNonNeighbor_(M, v1, v0->id);
}
static void RemoveTriangle(struct mesh *M, int id) {
struct triangle_n * face = Triangle(M, id);
struct vertex * v[3];
for (int i=0;i<3;i++) {
v[i] = Vertex(M, face->vertex[i]);
if (v[i]->id < 0)
v[i] = NULL;
else {
array_find_and_remove(v[i]->face, id);
}
}
RemoveIfNonNeighbor(M, v[0], v[1]);
RemoveIfNonNeighbor(M, v[1], v[2]);
RemoveIfNonNeighbor(M, v[2], v[0]);
}
static void ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) {
struct triangle_n * face = Triangle(M, faceid);
ASSERT(oldid >=0 && newid >= 0);
ASSERT(HasVertex(face, oldid));
ASSERT(!HasVertex(face, newid));
if(oldid==face->vertex[0]){
face->vertex[0]=newid;
} else if(oldid==face->vertex[1]){
face->vertex[1]=newid;
} else {
face->vertex[2]=newid;
}
struct vertex *vold = Vertex(M, oldid);
struct vertex *vnew = Vertex(M, newid);
array_find_and_remove(vold->face, faceid);
array_push(vnew->face, faceid);
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[0]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[1]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[2]));
AddNeighbor(M, face->vertex[0], face->vertex[1]);
AddNeighbor(M, face->vertex[0], face->vertex[2]);
AddNeighbor(M, face->vertex[1], face->vertex[0]);
AddNeighbor(M, face->vertex[1], face->vertex[2]);
AddNeighbor(M, face->vertex[2], face->vertex[0]);
AddNeighbor(M, face->vertex[2], face->vertex[1]);
ComputeNormal(M, face);
}
static void MeshInit(struct mesh *M, int vert_n, int tri_n) {
M->n_face = 0;
M->n_vertex = 0;
M->v = (struct vertex *)MALLOC(vert_n * sizeof(struct vertex));
M->t = (struct triangle_n *)MALLOC(tri_n * sizeof(struct triangle));
}
static void MeshFree(struct mesh *M) {
FREE(M->v);
FREE(M->t);
}
static float ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) {
// if we collapse edge uv by moving u to v then how
// much different will the model change, i.e. how much "error".
// Texture, vertex normal, and border vertex code was removed
// to keep this demo as simple as possible.
// The method of determining cost was designed in order
// to exploit small and coplanar regions for
// effective polygon reduction.
// Is is possible to add some checks here to see if "folds"
// would be generated. i.e. normal of a remaining face gets
// flipped. I never seemed to run into this problem and
// therefore never added code to detect this case.
struct vertex *v = Vertex(M, vid);
vec3 tmp = sub3(v->position, u->position);
float edgelength = len3(tmp);
float curvature=0;
// find the "sides" triangles that are on the edge uv
array(int) sides = 0;
for (int i = 0; i<array_count(u->face); i++) {
if (HasVertex(Face(M, u, i), vid)) {
array_push(sides, u->face[i]);
}
}
// use the triangle facing most away from the sides
// to determine our curvature term
for (int i = 0; i<array_count(u->face); i++) {
float mincurv=1; // curve for face i and closer side to it
for (int j = 0; j<array_count(sides); j++) {
float dotprod = dot3(Triangle(M, u->face[i])->normal,
Triangle(M, sides[j])->normal); // use dot product of face normals.
float t = (1-dotprod)/2.0f;
if (t < mincurv) {
mincurv = t;
}
}
if (mincurv > curvature)
curvature = mincurv;
}
array_free(sides);
// the more coplanar the lower the curvature term
return edgelength * curvature;
}
static void ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) {
// compute the edge collapse cost for all edges that start
// from vertex v. Since we are only interested in reducing
// the object by selecting the min cost edge at each step, we
// only cache the cost of the least cost edge at this vertex
// (in member variable collapse) as well as the value of the
// cost (in member variable objdist).
if (array_count(v->neighbor) == 0) {
// v doesn't have neighbors so it costs nothing to collapse
v->collapse=-1;
v->objdist=-0.01f;
return;
}
v->objdist = 1000000;
v->collapse=-1;
// search all neighboring edges for "least cost" edge
for (int i = 0; i<array_count(v->neighbor); i++) {
float dist = ComputeEdgeCollapseCost(M, v, v->neighbor[i]);
if(dist<v->objdist) {
v->collapse=v->neighbor[i]; // candidate for edge collapse
v->objdist=dist; // cost of the collapse
}
}
}
static void ComputeAllEdgeCollapseCosts(struct mesh *M) {
// For all the edges, compute the difference it would make
// to the model if it was collapsed. The least of these
// per vertex is cached in each vertex object.
for (int i = 0; i<M->n_vertex; i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, i));
}
}
static void Collapse(struct mesh *M, int uid, int vid) {
// Collapse the edge uv by moving vertex u onto v
// Actually remove tris on uv, then update tris that
// have u to have v, and then remove u.
struct vertex *u = Vertex(M, uid);
if(vid < 0) {
// u is a vertex all by itself so just delete it
RemoveVertex(M, uid);
return;
}
array(int) tmp = 0;
// make tmp a Array of all the neighbors of u
for (int i = 0; i<array_count(u->neighbor); i++) {
array_push(tmp, u->neighbor[i]);
}
// delete triangles on edge uv:
for( int i = array_count(u->face); i--; ) {
if (HasVertex(Face(M, u, i), vid)) {
RemoveTriangle(M, u->face[i]);
}
}
// update remaining triangles to have v instead of u
for( int i = array_count(u->face); i--; ) {
ReplaceVertex(M, u->face[i], uid, vid);
}
RemoveVertex(M, uid);
// recompute the edge collapse costs for neighboring vertices
for (int i = 0; i<array_count(tmp); i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, tmp[i]));
}
array_free(tmp);
}
static struct vertex *MinimumCostEdge(struct mesh *M) {
// Find the edge that when collapsed will affect model the least.
// This function actually returns a Vertex, the second vertex
// of the edge (collapse candidate) is stored in the vertex data.
// Serious optimization opportunity here: this function currently
// does a sequential search through an unsorted Array :-(
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
struct vertex *mn = NULL;
for (int i = 0; i<M->n_vertex; i++) {
struct vertex *v = Vertex(M, i);
if (v->id >=0) {
if (mn == NULL || v->objdist < mn->objdist) {
mn = v;
}
}
}
return mn;
}
/*
* The function ProgressiveMesh() takes a model in an "indexed face
* set" sort of way. i.e. Array of vertices and Array of triangles.
* The function then does the polygon reduction algorithm
* internally and reduces the model all the way down to 0
* vertices and then returns the order in which the
* vertices are collapsed and to which neighbor each vertex
* is collapsed to. More specifically the returned "permutation"
* indicates how to reorder your vertices so you can render
* an object by using the first n vertices (for the n
* vertex version). After permuting your vertices, the
* map Array indicates to which vertex each vertex is collapsed to.
*/
API void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation) {
struct mesh M;
MeshInit(&M, vert_n, tri_n);
// put input data into our data structures M
const char * tmp = (const char *)v;
for (int i=0;i<vert_n;i++, tmp += vert_stride ) {
AddVertex(&M, (const float *)tmp);
}
for (int i=0;i<tri_n;i++) {
AddTriangle(&M, &tri[i*3]);
}
ComputeAllEdgeCollapseCosts(&M); // cache all edge collapse costs
for (int i = vert_n-1; i>=0; i--) {
// get the next vertex to collapse
struct vertex *mn = MinimumCostEdge(&M);
// keep track of this vertex, i.e. the collapse ordering
permutation[mn->id] = i;
// keep track of vertex to which we collapse to
map[i] = mn->collapse;
// Collapse this edge
Collapse(&M, mn->id, mn->collapse);
}
// reorder the map Array based on the collapse ordering
for (int i = 0; i<vert_n; i++) {
map[i] = (map[i]==-1)?0:permutation[map[i]];
}
// The caller of this function should reorder their vertices
// according to the returned "permutation".
MeshFree(&M);
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Stan Melax
* Copyright (c) 2020 Cloud Wu
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

File diff suppressed because it is too large Load Diff

View File

@ -1,373 +1,373 @@
/* rpmalloc.h - Memory allocator - Public Domain - 2016 Mattias Jansson
*
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
* The latest source code is always available at
*
* https://github.com/mjansson/rpmalloc
*
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
*
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__clang__) || defined(__GNUC__)
# define RPMALLOC_EXPORT __attribute__((visibility("default")))
# define RPMALLOC_ALLOCATOR
# if (defined(__clang_major__) && (__clang_major__ < 4)) || (defined(__GNUC__) && defined(ENABLE_PRELOAD) && ENABLE_PRELOAD)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
# else
# define RPMALLOC_ATTRIB_MALLOC __attribute__((__malloc__))
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size) __attribute__((alloc_size(size)))
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size) __attribute__((alloc_size(count, size)))
# endif
# define RPMALLOC_CDECL
#elif defined(_MSC_VER)
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR __declspec(allocator) __declspec(restrict)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL __cdecl
#else
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL
#endif
//! Define RPMALLOC_CONFIGURABLE to enable configuring sizes. Will introduce
// a very small overhead due to some size calculations not being compile time constants
#ifndef RPMALLOC_CONFIGURABLE
#define RPMALLOC_CONFIGURABLE 0
#endif
//! Define RPMALLOC_FIRST_CLASS_HEAPS to enable heap based API (rpmalloc_heap_* functions).
// Will introduce a very small overhead to track fully allocated spans in heaps
#ifndef RPMALLOC_FIRST_CLASS_HEAPS
#define RPMALLOC_FIRST_CLASS_HEAPS 0
#endif
//! Flag to rpaligned_realloc to not preserve content in reallocation
#define RPMALLOC_NO_PRESERVE 1
//! Flag to rpaligned_realloc to fail and return null pointer if grow cannot be done in-place,
// in which case the original pointer is still valid (just like a call to realloc which failes to allocate
// a new block).
#define RPMALLOC_GROW_OR_FAIL 2
typedef struct rpmalloc_global_statistics_t {
//! Current amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped;
//! Peak amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped_peak;
//! Current amount of memory in global caches for small and medium sizes (<32KiB)
size_t cached;
//! Current amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
size_t huge_alloc;
//! Peak amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
size_t huge_alloc_peak;
//! Total amount of memory mapped since initialization (only if ENABLE_STATISTICS=1)
size_t mapped_total;
//! Total amount of memory unmapped since initialization (only if ENABLE_STATISTICS=1)
size_t unmapped_total;
} rpmalloc_global_statistics_t;
typedef struct rpmalloc_thread_statistics_t {
//! Current number of bytes available in thread size class caches for small and medium sizes (<32KiB)
size_t sizecache;
//! Current number of bytes available in thread span caches for small and medium sizes (<32KiB)
size_t spancache;
//! Total number of bytes transitioned from thread cache to global cache (only if ENABLE_STATISTICS=1)
size_t thread_to_global;
//! Total number of bytes transitioned from global cache to thread cache (only if ENABLE_STATISTICS=1)
size_t global_to_thread;
//! Per span count statistics (only if ENABLE_STATISTICS=1)
struct {
//! Currently used number of spans
size_t current;
//! High water mark of spans used
size_t peak;
//! Number of spans transitioned to global cache
size_t to_global;
//! Number of spans transitioned from global cache
size_t from_global;
//! Number of spans transitioned to thread cache
size_t to_cache;
//! Number of spans transitioned from thread cache
size_t from_cache;
//! Number of spans transitioned to reserved state
size_t to_reserved;
//! Number of spans transitioned from reserved state
size_t from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} span_use[64];
//! Per size class statistics (only if ENABLE_STATISTICS=1)
struct {
//! Current number of allocations
size_t alloc_current;
//! Peak number of allocations
size_t alloc_peak;
//! Total number of allocations
size_t alloc_total;
//! Total number of frees
size_t free_total;
//! Number of spans transitioned to cache
size_t spans_to_cache;
//! Number of spans transitioned from cache
size_t spans_from_cache;
//! Number of spans transitioned from reserved state
size_t spans_from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} size_use[128];
} rpmalloc_thread_statistics_t;
typedef struct rpmalloc_config_t {
//! Map memory pages for the given number of bytes. The returned address MUST be
// aligned to the rpmalloc span size, which will always be a power of two.
// Optionally the function can store an alignment offset in the offset variable
// in case it performs alignment and the returned pointer is offset from the
// actual start of the memory region due to this alignment. The alignment offset
// will be passed to the memory unmap function. The alignment offset MUST NOT be
// larger than 65535 (storable in an uint16_t), if it is you must use natural
// alignment to shift it into 16 bits. If you set a memory_map function, you
// must also set a memory_unmap function or else the default implementation will
// be used for both. This function must be thread safe, it can be called by
// multiple threads simultaneously.
void* (*memory_map)(size_t size, size_t* offset);
//! Unmap the memory pages starting at address and spanning the given number of bytes.
// If release is set to non-zero, the unmap is for an entire span range as returned by
// a previous call to memory_map and that the entire range should be released. The
// release argument holds the size of the entire span range. If release is set to 0,
// the unmap is a partial decommit of a subset of the mapped memory range.
// If you set a memory_unmap function, you must also set a memory_map function or
// else the default implementation will be used for both. This function must be thread
// safe, it can be called by multiple threads simultaneously.
void (*memory_unmap)(void* address, size_t size, size_t offset, size_t release);
//! Called when an assert fails, if asserts are enabled. Will use the standard assert()
// if this is not set.
void (*error_callback)(const char* message);
//! Called when a call to map memory pages fails (out of memory). If this callback is
// not set or returns zero the library will return a null pointer in the allocation
// call. If this callback returns non-zero the map call will be retried. The argument
// passed is the number of bytes that was requested in the map call. Only used if
// the default system memory map function is used (memory_map callback is not set).
int (*map_fail_callback)(size_t size);
//! Size of memory pages. The page size MUST be a power of two. All memory mapping
// requests to memory_map will be made with size set to a multiple of the page size.
// Used if RPMALLOC_CONFIGURABLE is defined to 1, otherwise system page size is used.
size_t page_size;
//! Size of a span of memory blocks. MUST be a power of two, and in [4096,262144]
// range (unless 0 - set to 0 to use the default span size). Used if RPMALLOC_CONFIGURABLE
// is defined to 1.
size_t span_size;
//! Number of spans to map at each request to map new virtual memory blocks. This can
// be used to minimize the system call overhead at the cost of virtual memory address
// space. The extra mapped pages will not be written until actually used, so physical
// committed memory should not be affected in the default implementation. Will be
// aligned to a multiple of spans that match memory page size in case of huge pages.
size_t span_map_count;
//! Enable use of large/huge pages. If this flag is set to non-zero and page size is
// zero, the allocator will try to enable huge pages and auto detect the configuration.
// If this is set to non-zero and page_size is also non-zero, the allocator will
// assume huge pages have been configured and enabled prior to initializing the
// allocator.
// For Windows, see https://docs.microsoft.com/en-us/windows/desktop/memory/large-page-support
// For Linux, see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
int enable_huge_pages;
//! Respectively allocated pages and huge allocated pages names for systems
// supporting it to be able to distinguish among anonymous regions.
const char *page_name;
const char *huge_page_name;
} rpmalloc_config_t;
//! Initialize allocator with default configuration
RPMALLOC_EXPORT int
rpmalloc_initialize(void);
//! Initialize allocator with given configuration
RPMALLOC_EXPORT int
rpmalloc_initialize_config(const rpmalloc_config_t* config);
//! Get allocator configuration
RPMALLOC_EXPORT const rpmalloc_config_t*
rpmalloc_config(void);
//! Finalize allocator
RPMALLOC_EXPORT void
rpmalloc_finalize(void);
//! Initialize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_initialize(void);
//! Finalize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_finalize(int release_caches);
//! Perform deferred deallocations pending for the calling thread heap
RPMALLOC_EXPORT void
rpmalloc_thread_collect(void);
//! Query if allocator is initialized for calling thread
RPMALLOC_EXPORT int
rpmalloc_is_thread_initialized(void);
//! Get per-thread statistics
RPMALLOC_EXPORT void
rpmalloc_thread_statistics(rpmalloc_thread_statistics_t* stats);
//! Get global statistics
RPMALLOC_EXPORT void
rpmalloc_global_statistics(rpmalloc_global_statistics_t* stats);
//! Dump all statistics in human readable format to file (should be a FILE*)
RPMALLOC_EXPORT void
rpmalloc_dump_statistics(void* file);
//! Allocate a memory block of at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1);
//! Free the given memory block
RPMALLOC_EXPORT void
rpfree(void* ptr);
//! Allocate a memory block of at least the given size and zero initialize it
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpcalloc(size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(1, 2);
//! Reallocate the given block to at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rprealloc(void* ptr, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Reallocate the given block to at least the given size and alignment,
// with optional control flags (see RPMALLOC_NO_PRESERVE).
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_realloc(void* ptr, size_t alignment, size_t size, size_t oldsize, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_alloc(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size and alignment, and zero initialize it.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_calloc(size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmemalign(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT int
rpposix_memalign(void** memptr, size_t alignment, size_t size);
//! Query the usable size of the given memory block (from given pointer to the end of block)
RPMALLOC_EXPORT size_t
rpmalloc_usable_size(void* ptr);
//! Dummy empty function for forcing linker symbol inclusion
RPMALLOC_EXPORT void
rpmalloc_linker_reference(void);
#if RPMALLOC_FIRST_CLASS_HEAPS
//! Heap type
typedef struct heap_t rpmalloc_heap_t;
//! Acquire a new heap. Will reuse existing released heaps or allocate memory for a new heap
// if none available. Heap API is implemented with the strict assumption that only one single
// thread will call heap functions for a given heap at any given time, no functions are thread safe.
RPMALLOC_EXPORT rpmalloc_heap_t*
rpmalloc_heap_acquire(void);
//! Release a heap (does NOT free the memory allocated by the heap, use rpmalloc_heap_free_all before destroying the heap).
// Releasing a heap will enable it to be reused by other threads. Safe to pass a null pointer.
RPMALLOC_EXPORT void
rpmalloc_heap_release(rpmalloc_heap_t* heap);
//! Allocate a memory block of at least the given size using the given heap.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_alloc(rpmalloc_heap_t* heap, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size using the given heap. The returned
// block will have the requested alignment. Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_alloc(rpmalloc_heap_t* heap, size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_calloc(rpmalloc_heap_t* heap, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it. The returned
// block will have the requested alignment. Alignment must either be zero, or a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_calloc(rpmalloc_heap_t* heap, size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_realloc(rpmalloc_heap_t* heap, void* ptr, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function. The returned block will have the requested alignment.
// Alignment must be either zero, or a power of two and a multiple of sizeof(void*), and should ideally be
// less than memory page size. A caveat of rpmalloc internals is that this must also be strictly less than
// the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_realloc(rpmalloc_heap_t* heap, void* ptr, size_t alignment, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(4);
//! Free the given memory block from the given heap. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT void
rpmalloc_heap_free(rpmalloc_heap_t* heap, void* ptr);
//! Free all memory allocated by the heap
RPMALLOC_EXPORT void
rpmalloc_heap_free_all(rpmalloc_heap_t* heap);
//! Set the given heap as the current heap for the calling thread. A heap MUST only be current heap
// for a single thread, a heap can never be shared between multiple threads. The previous
// current heap for the calling thread is released to be reused by other threads.
RPMALLOC_EXPORT void
rpmalloc_heap_thread_set_current(rpmalloc_heap_t* heap);
//! Returns which heap the given pointer is allocated on
RPMALLOC_EXPORT rpmalloc_heap_t*
rpmalloc_get_heap_for_ptr(void* ptr);
#endif
#ifdef __cplusplus
}
#endif
/* rpmalloc.h - Memory allocator - Public Domain - 2016 Mattias Jansson
*
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
* The latest source code is always available at
*
* https://github.com/mjansson/rpmalloc
*
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
*
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__clang__) || defined(__GNUC__)
# define RPMALLOC_EXPORT __attribute__((visibility("default")))
# define RPMALLOC_ALLOCATOR
# if (defined(__clang_major__) && (__clang_major__ < 4)) || (defined(__GNUC__) && defined(ENABLE_PRELOAD) && ENABLE_PRELOAD)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
# else
# define RPMALLOC_ATTRIB_MALLOC __attribute__((__malloc__))
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size) __attribute__((alloc_size(size)))
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size) __attribute__((alloc_size(count, size)))
# endif
# define RPMALLOC_CDECL
#elif defined(_MSC_VER)
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR __declspec(allocator) __declspec(restrict)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL __cdecl
#else
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL
#endif
//! Define RPMALLOC_CONFIGURABLE to enable configuring sizes. Will introduce
// a very small overhead due to some size calculations not being compile time constants
#ifndef RPMALLOC_CONFIGURABLE
#define RPMALLOC_CONFIGURABLE 0
#endif
//! Define RPMALLOC_FIRST_CLASS_HEAPS to enable heap based API (rpmalloc_heap_* functions).
// Will introduce a very small overhead to track fully allocated spans in heaps
#ifndef RPMALLOC_FIRST_CLASS_HEAPS
#define RPMALLOC_FIRST_CLASS_HEAPS 0
#endif
//! Flag to rpaligned_realloc to not preserve content in reallocation
#define RPMALLOC_NO_PRESERVE 1
//! Flag to rpaligned_realloc to fail and return null pointer if grow cannot be done in-place,
// in which case the original pointer is still valid (just like a call to realloc which failes to allocate
// a new block).
#define RPMALLOC_GROW_OR_FAIL 2
typedef struct rpmalloc_global_statistics_t {
//! Current amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped;
//! Peak amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped_peak;
//! Current amount of memory in global caches for small and medium sizes (<32KiB)
size_t cached;
//! Current amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
size_t huge_alloc;
//! Peak amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
size_t huge_alloc_peak;
//! Total amount of memory mapped since initialization (only if ENABLE_STATISTICS=1)
size_t mapped_total;
//! Total amount of memory unmapped since initialization (only if ENABLE_STATISTICS=1)
size_t unmapped_total;
} rpmalloc_global_statistics_t;
typedef struct rpmalloc_thread_statistics_t {
//! Current number of bytes available in thread size class caches for small and medium sizes (<32KiB)
size_t sizecache;
//! Current number of bytes available in thread span caches for small and medium sizes (<32KiB)
size_t spancache;
//! Total number of bytes transitioned from thread cache to global cache (only if ENABLE_STATISTICS=1)
size_t thread_to_global;
//! Total number of bytes transitioned from global cache to thread cache (only if ENABLE_STATISTICS=1)
size_t global_to_thread;
//! Per span count statistics (only if ENABLE_STATISTICS=1)
struct {
//! Currently used number of spans
size_t current;
//! High water mark of spans used
size_t peak;
//! Number of spans transitioned to global cache
size_t to_global;
//! Number of spans transitioned from global cache
size_t from_global;
//! Number of spans transitioned to thread cache
size_t to_cache;
//! Number of spans transitioned from thread cache
size_t from_cache;
//! Number of spans transitioned to reserved state
size_t to_reserved;
//! Number of spans transitioned from reserved state
size_t from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} span_use[64];
//! Per size class statistics (only if ENABLE_STATISTICS=1)
struct {
//! Current number of allocations
size_t alloc_current;
//! Peak number of allocations
size_t alloc_peak;
//! Total number of allocations
size_t alloc_total;
//! Total number of frees
size_t free_total;
//! Number of spans transitioned to cache
size_t spans_to_cache;
//! Number of spans transitioned from cache
size_t spans_from_cache;
//! Number of spans transitioned from reserved state
size_t spans_from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} size_use[128];
} rpmalloc_thread_statistics_t;
typedef struct rpmalloc_config_t {
//! Map memory pages for the given number of bytes. The returned address MUST be
// aligned to the rpmalloc span size, which will always be a power of two.
// Optionally the function can store an alignment offset in the offset variable
// in case it performs alignment and the returned pointer is offset from the
// actual start of the memory region due to this alignment. The alignment offset
// will be passed to the memory unmap function. The alignment offset MUST NOT be
// larger than 65535 (storable in an uint16_t), if it is you must use natural
// alignment to shift it into 16 bits. If you set a memory_map function, you
// must also set a memory_unmap function or else the default implementation will
// be used for both. This function must be thread safe, it can be called by
// multiple threads simultaneously.
void* (*memory_map)(size_t size, size_t* offset);
//! Unmap the memory pages starting at address and spanning the given number of bytes.
// If release is set to non-zero, the unmap is for an entire span range as returned by
// a previous call to memory_map and that the entire range should be released. The
// release argument holds the size of the entire span range. If release is set to 0,
// the unmap is a partial decommit of a subset of the mapped memory range.
// If you set a memory_unmap function, you must also set a memory_map function or
// else the default implementation will be used for both. This function must be thread
// safe, it can be called by multiple threads simultaneously.
void (*memory_unmap)(void* address, size_t size, size_t offset, size_t release);
//! Called when an assert fails, if asserts are enabled. Will use the standard assert()
// if this is not set.
void (*error_callback)(const char* message);
//! Called when a call to map memory pages fails (out of memory). If this callback is
// not set or returns zero the library will return a null pointer in the allocation
// call. If this callback returns non-zero the map call will be retried. The argument
// passed is the number of bytes that was requested in the map call. Only used if
// the default system memory map function is used (memory_map callback is not set).
int (*map_fail_callback)(size_t size);
//! Size of memory pages. The page size MUST be a power of two. All memory mapping
// requests to memory_map will be made with size set to a multiple of the page size.
// Used if RPMALLOC_CONFIGURABLE is defined to 1, otherwise system page size is used.
size_t page_size;
//! Size of a span of memory blocks. MUST be a power of two, and in [4096,262144]
// range (unless 0 - set to 0 to use the default span size). Used if RPMALLOC_CONFIGURABLE
// is defined to 1.
size_t span_size;
//! Number of spans to map at each request to map new virtual memory blocks. This can
// be used to minimize the system call overhead at the cost of virtual memory address
// space. The extra mapped pages will not be written until actually used, so physical
// committed memory should not be affected in the default implementation. Will be
// aligned to a multiple of spans that match memory page size in case of huge pages.
size_t span_map_count;
//! Enable use of large/huge pages. If this flag is set to non-zero and page size is
// zero, the allocator will try to enable huge pages and auto detect the configuration.
// If this is set to non-zero and page_size is also non-zero, the allocator will
// assume huge pages have been configured and enabled prior to initializing the
// allocator.
// For Windows, see https://docs.microsoft.com/en-us/windows/desktop/memory/large-page-support
// For Linux, see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
int enable_huge_pages;
//! Respectively allocated pages and huge allocated pages names for systems
// supporting it to be able to distinguish among anonymous regions.
const char *page_name;
const char *huge_page_name;
} rpmalloc_config_t;
//! Initialize allocator with default configuration
RPMALLOC_EXPORT int
rpmalloc_initialize(void);
//! Initialize allocator with given configuration
RPMALLOC_EXPORT int
rpmalloc_initialize_config(const rpmalloc_config_t* config);
//! Get allocator configuration
RPMALLOC_EXPORT const rpmalloc_config_t*
rpmalloc_config(void);
//! Finalize allocator
RPMALLOC_EXPORT void
rpmalloc_finalize(void);
//! Initialize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_initialize(void);
//! Finalize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_finalize(int release_caches);
//! Perform deferred deallocations pending for the calling thread heap
RPMALLOC_EXPORT void
rpmalloc_thread_collect(void);
//! Query if allocator is initialized for calling thread
RPMALLOC_EXPORT int
rpmalloc_is_thread_initialized(void);
//! Get per-thread statistics
RPMALLOC_EXPORT void
rpmalloc_thread_statistics(rpmalloc_thread_statistics_t* stats);
//! Get global statistics
RPMALLOC_EXPORT void
rpmalloc_global_statistics(rpmalloc_global_statistics_t* stats);
//! Dump all statistics in human readable format to file (should be a FILE*)
RPMALLOC_EXPORT void
rpmalloc_dump_statistics(void* file);
//! Allocate a memory block of at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1);
//! Free the given memory block
RPMALLOC_EXPORT void
rpfree(void* ptr);
//! Allocate a memory block of at least the given size and zero initialize it
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpcalloc(size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(1, 2);
//! Reallocate the given block to at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rprealloc(void* ptr, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Reallocate the given block to at least the given size and alignment,
// with optional control flags (see RPMALLOC_NO_PRESERVE).
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_realloc(void* ptr, size_t alignment, size_t size, size_t oldsize, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_alloc(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size and alignment, and zero initialize it.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpaligned_calloc(size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmemalign(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT int
rpposix_memalign(void** memptr, size_t alignment, size_t size);
//! Query the usable size of the given memory block (from given pointer to the end of block)
RPMALLOC_EXPORT size_t
rpmalloc_usable_size(void* ptr);
//! Dummy empty function for forcing linker symbol inclusion
RPMALLOC_EXPORT void
rpmalloc_linker_reference(void);
#if RPMALLOC_FIRST_CLASS_HEAPS
//! Heap type
typedef struct heap_t rpmalloc_heap_t;
//! Acquire a new heap. Will reuse existing released heaps or allocate memory for a new heap
// if none available. Heap API is implemented with the strict assumption that only one single
// thread will call heap functions for a given heap at any given time, no functions are thread safe.
RPMALLOC_EXPORT rpmalloc_heap_t*
rpmalloc_heap_acquire(void);
//! Release a heap (does NOT free the memory allocated by the heap, use rpmalloc_heap_free_all before destroying the heap).
// Releasing a heap will enable it to be reused by other threads. Safe to pass a null pointer.
RPMALLOC_EXPORT void
rpmalloc_heap_release(rpmalloc_heap_t* heap);
//! Allocate a memory block of at least the given size using the given heap.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_alloc(rpmalloc_heap_t* heap, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size using the given heap. The returned
// block will have the requested alignment. Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_alloc(rpmalloc_heap_t* heap, size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_calloc(rpmalloc_heap_t* heap, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it. The returned
// block will have the requested alignment. Alignment must either be zero, or a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_calloc(rpmalloc_heap_t* heap, size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_realloc(rpmalloc_heap_t* heap, void* ptr, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function. The returned block will have the requested alignment.
// Alignment must be either zero, or a power of two and a multiple of sizeof(void*), and should ideally be
// less than memory page size. A caveat of rpmalloc internals is that this must also be strictly less than
// the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc_heap_aligned_realloc(rpmalloc_heap_t* heap, void* ptr, size_t alignment, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(4);
//! Free the given memory block from the given heap. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT void
rpmalloc_heap_free(rpmalloc_heap_t* heap, void* ptr);
//! Free all memory allocated by the heap
RPMALLOC_EXPORT void
rpmalloc_heap_free_all(rpmalloc_heap_t* heap);
//! Set the given heap as the current heap for the calling thread. A heap MUST only be current heap
// for a single thread, a heap can never be shared between multiple threads. The previous
// current heap for the calling thread is released to be reused by other threads.
RPMALLOC_EXPORT void
rpmalloc_heap_thread_set_current(rpmalloc_heap_t* heap);
//! Returns which heap the given pointer is allocated on
RPMALLOC_EXPORT rpmalloc_heap_t*
rpmalloc_get_heap_for_ptr(void* ptr);
#endif
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,481 +1,481 @@
/** 1D, 2D, 3D and 4D float Perlin Simplex noise */
/** Original code, stefan gustavson (PD). */
#ifdef SIMPLEX_C
/* SimplexNoise1234, Simplex noise with true analytic
* derivative in 1D to 4D.
*
* Author: Stefan Gustavson, 2003-2005
* Contact: stefan.gustavson@liu.se
*
* This code was GPL licensed until February 2011.
* As the original author of this code, I hereby
* release it into the public domain.
* Please feel free to use it for whatever you want.
* Credit is appreciated where appropriate, and I also
* appreciate being told where this code finds any use,
* but you may do as you like.
*/
/*
* This implementation is "Simplex Noise" as presented by
* Ken Perlin at a relatively obscure and not often cited course
* session "Real-Time Shading" at Siggraph 2001 (before real
* time shading actually took off), under the title "hardware noise".
* The 3D function is numerically equivalent to his Java reference
* code available in the PDF course notes, although I re-implemented
* it from scratch to get more readable code. The 1D, 2D and 4D cases
* were implemented from scratch by me from Ken Perlin's text.
*
* This file has no dependencies on any other file, not even its own
* header file. The header file is made for use by external code only.
*/
// We don't really need to include this, but play nice and do it anyway.
//#include "noise.c"
#define FASTFLOOR(x) ( ((int)(x)<=(x)) ? ((int)x) : (((int)x)-1) )
//---------------------------------------------------------------------
// Static data
/*
* Permutation table. This is just a random jumble of all numbers 0-255,
* repeated twice to avoid wrapping the index at 255 for each lookup.
* This needs to be exactly the same for all instances on all platforms,
* so it's easiest to just keep it as static explicit data.
* This also removes the need for any initialisation of this class.
*
* Note that making this an int[] instead of a char[] might make the
* code run faster on platforms with a high penalty for unaligned single
* byte addressing. Intel x86 is generally single-byte-friendly, but
* some other CPUs are faster with 4-aligned reads.
* However, a char[] is smaller, which avoids cache trashing, and that
* is probably the most important aspect on most architectures.
* This array is accessed a *lot* by the noise functions.
* A vector-valued noise over 3D accesses it 96 times, and a
* float-valued 4D noise 64 times. We want this to fit in the cache!
*/
unsigned char perm[512] = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
//---------------------------------------------------------------------
/*
* Helper functions to compute gradients-dot-residualvectors (1D to 4D)
* Note that these generate gradients of more than unit length. To make
* a close match with the value range of classic Perlin noise, the final
* noise values need to be rescaled to fit nicely within [-1,1].
* (The simplex noise functions as such also have different scaling.)
* Note also that these noise functions are the most practical and useful
* signed version of Perlin noise. To return values according to the
* RenderMan specification from the SL noise() and pnoise() functions,
* the noise values need to be scaled and offset to [0,1], like this:
* float SLnoise = (noise(x,y,z) + 1.0) * 0.5;
*/
float grad1( int hash, float x ) {
int h = hash & 15;
float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
if (h&8) grad = -grad; // Set a random sign for the gradient
return ( grad * x ); // Multiply the gradient with the distance
}
float grad2( int hash, float x, float y ) {
int h = hash & 7; // Convert low 3 bits of hash code
float u = h<4 ? x : y; // into 8 simple gradient directions,
float v = h<4 ? y : x; // and compute the dot product with (x,y).
return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v);
}
float grad3( int hash, float x, float y , float z ) {
int h = hash & 15; // Convert low 4 bits of hash code into 12 simple
float u = h<8 ? x : y; // gradient directions, and compute dot product.
float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15
return ((h&1)? -u : u) + ((h&2)? -v : v);
}
float grad4( int hash, float x, float y, float z, float t ) {
int h = hash & 31; // Convert low 5 bits of hash code into 32 simple
float u = h<24 ? x : y; // gradient directions, and compute dot product.
float v = h<16 ? y : z;
float w = h<8 ? z : t;
return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w);
}
// A lookup table to traverse the simplex around a given point in 4D.
// Details can be found where this table is used, in the 4D noise method.
/* TODO: This should not be required, backport it from Bill's GLSL code! */
static unsigned char simplex[64][4] = {
{0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0},
{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0},
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},
{1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
{2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0},
{2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}};
// 1D simplex noise
float snoise1(float x) {
int i0 = FASTFLOOR(x);
int i1 = i0 + 1;
float x0 = x - i0;
float x1 = x0 - 1.0f;
float n0, n1;
float t0 = 1.0f - x0*x0;
// if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case
t0 *= t0;
n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0);
float t1 = 1.0f - x1*x1;
// if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case
t1 *= t1;
n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1);
// The maximum value of this noise is 8*(3/4)^4 = 2.53125
// A factor of 0.395 would scale to fit exactly within [-1,1], but
// we want to match PRMan's 1D noise, so we scale it down some more.
return 0.25f * (n0 + n1);
}
// 2D simplex noise
float snoise2(float x, float y) {
#define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0)
#define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0
float n0, n1, n2; // Noise contributions from the three corners
// Skew the input space to determine which simplex cell we're in
float s = (x+y)*F2; // Hairy factor for 2D
float xs = x + s;
float ys = y + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
float t = (float)(i+j)*G2;
float X0 = i-t; // Unskew the cell origin back to (x,y) space
float Y0 = j-t;
float x0 = x-X0; // The x,y distances from the cell origin
float y0 = y-Y0;
// For the 2D case, the simplex shape is an equilateral triangle.
// Determine which simplex we are in.
int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
// c = (3-sqrt(3))/6
float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
float y1 = y0 - j1 + G2;
float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords
float y2 = y0 - 1.0f + 2.0f * G2;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
// Calculate the contribution from the three corners
float t0 = 0.5f - x0*x0-y0*y0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0);
}
float t1 = 0.5f - x1*x1-y1*y1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1);
}
float t2 = 0.5f - x2*x2-y2*y2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary!
}
// 3D simplex noise
float snoise3(float x, float y, float z) {
// Simple skewing factors for the 3D case
#define F3 0.333333333
#define G3 0.166666667
float n0, n1, n2, n3; // Noise contributions from the four corners
// Skew the input space to determine which simplex cell we're in
float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D
float xs = x+s;
float ys = y+s;
float zs = z+s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
float t = (float)(i+j+k)*G3;
float X0 = i-t; // Unskew the cell origin back to (x,y,z) space
float Y0 = j-t;
float Z0 = k-t;
float x0 = x-X0; // The x,y,z distances from the cell origin
float y0 = y-Y0;
float z0 = z-Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
/* This code would benefit from a backport from the GLSL version! */
if(x0>=y0) {
if(y0>=z0)
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
// c = 1/6.
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
float y1 = y0 - j1 + G3;
float z1 = z0 - k1 + G3;
float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords
float y2 = y0 - j2 + 2.0f*G3;
float z2 = z0 - k2 + 2.0f*G3;
float x3 = x0 - 1.0f + 3.0f*G3; // Offsets for last corner in (x,y,z) coords
float y3 = y0 - 1.0f + 3.0f*G3;
float z3 = z0 - 1.0f + 3.0f*G3;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
// Calculate the contribution from the four corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad3(perm[ii+perm[jj+perm[kk]]], x0, y0, z0);
}
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad3(perm[ii+i1+perm[jj+j1+perm[kk+k1]]], x1, y1, z1);
}
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad3(perm[ii+i2+perm[jj+j2+perm[kk+k2]]], x2, y2, z2);
}
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3;
if(t3<0.0f) n3 = 0.0f;
else {
t3 *= t3;
n3 = t3 * t3 * grad3(perm[ii+1+perm[jj+1+perm[kk+1]]], x3, y3, z3);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return 72.0f * (n0 + n1 + n2 + n3);
}
// 4D simplex noise
float snoise4(float x, float y, float z, float w) {
// The skewing and unskewing factors are hairy again for the 4D case
#define F4 0.309016994 // F4 = (Math.sqrt(5.0)-1.0)/4.0
#define G4 0.138196601 // G4 = (5.0-Math.sqrt(5.0))/20.0
float n0, n1, n2, n3, n4; // Noise contributions from the five corners
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
float s = (x + y + z + w) * F4; // Factor for 4D skewing
float xs = x + s;
float ys = y + s;
float zs = z + s;
float ws = w + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
int l = FASTFLOOR(ws);
float t = (i + j + k + l) * G4; // Factor for 4D unskewing
float X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
float Y0 = j - t;
float Z0 = k - t;
float W0 = l - t;
float x0 = x - X0; // The x,y,z,w distances from the cell origin
float y0 = y - Y0;
float z0 = z - Z0;
float w0 = w - W0;
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
// To find out which of the 24 possible simplices we're in, we need to
// determine the magnitude ordering of x0, y0, z0 and w0.
// The method below is a good way of finding the ordering of x,y,z,w and
// then find the correct traversal order for the simplex we?re in.
// First, six pair-wise comparisons are performed between each possible pair
// of the four coordinates, and the results are used to add up binary bits
// for an integer index.
int c1 = (x0 > y0) ? 32 : 0;
int c2 = (x0 > z0) ? 16 : 0;
int c3 = (y0 > z0) ? 8 : 0;
int c4 = (x0 > w0) ? 4 : 0;
int c5 = (y0 > w0) ? 2 : 0;
int c6 = (z0 > w0) ? 1 : 0;
int c = c1 + c2 + c3 + c4 + c5 + c6;
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
// impossible. Only the 24 indices which have non-zero entries make any sense.
// We use a thresholding to set the coordinates in turn from the largest magnitude.
// The number 3 in the "simplex" array is at the position of the largest coordinate.
i1 = simplex[c][0]>=3 ? 1 : 0;
j1 = simplex[c][1]>=3 ? 1 : 0;
k1 = simplex[c][2]>=3 ? 1 : 0;
l1 = simplex[c][3]>=3 ? 1 : 0;
// The number 2 in the "simplex" array is at the second largest coordinate.
i2 = simplex[c][0]>=2 ? 1 : 0;
j2 = simplex[c][1]>=2 ? 1 : 0;
k2 = simplex[c][2]>=2 ? 1 : 0;
l2 = simplex[c][3]>=2 ? 1 : 0;
// The number 1 in the "simplex" array is at the second smallest coordinate.
i3 = simplex[c][0]>=1 ? 1 : 0;
j3 = simplex[c][1]>=1 ? 1 : 0;
k3 = simplex[c][2]>=1 ? 1 : 0;
l3 = simplex[c][3]>=1 ? 1 : 0;
// The fifth corner has all coordinate offsets = 1, so no need to look that up.
float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
float y1 = y0 - j1 + G4;
float z1 = z0 - k1 + G4;
float w1 = w0 - l1 + G4;
float x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords
float y2 = y0 - j2 + 2.0f*G4;
float z2 = z0 - k2 + 2.0f*G4;
float w2 = w0 - l2 + 2.0f*G4;
float x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords
float y3 = y0 - j3 + 3.0f*G4;
float z3 = z0 - k3 + 3.0f*G4;
float w3 = w0 - l3 + 3.0f*G4;
float x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords
float y4 = y0 - 1.0f + 4.0f*G4;
float z4 = z0 - 1.0f + 4.0f*G4;
float w4 = w0 - 1.0f + 4.0f*G4;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
int ll = l & 0xff;
// Calculate the contribution from the five corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0 - w0*w0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad4(perm[ii+perm[jj+perm[kk+perm[ll]]]], x0, y0, z0, w0);
}
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1 - w1*w1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad4(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], x1, y1, z1, w1);
}
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2 - w2*w2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad4(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], x2, y2, z2, w2);
}
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3 - w3*w3;
if(t3 < 0.0f) n3 = 0.0f;
else {
t3 *= t3;
n3 = t3 * t3 * grad4(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], x3, y3, z3, w3);
}
float t4 = 0.5f - x4*x4 - y4*y4 - z4*z4 - w4*w4;
if(t4 < 0.0f) n4 = 0.0f;
else {
t4 *= t4;
n4 = t4 * t4 * grad4(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], x4, y4, z4, w4);
}
// Sum up and scale the result to cover the range [-1,1]
return 62.0f * (n0 + n1 + n2 + n3 + n4);
}
#undef F2
#undef G2
#undef F3
#undef G3
#undef F4
#undef G4
#endif
/** 1D, 2D, 3D and 4D float Perlin Simplex noise */
/** Original code, stefan gustavson (PD). */
#ifdef SIMPLEX_C
/* SimplexNoise1234, Simplex noise with true analytic
* derivative in 1D to 4D.
*
* Author: Stefan Gustavson, 2003-2005
* Contact: stefan.gustavson@liu.se
*
* This code was GPL licensed until February 2011.
* As the original author of this code, I hereby
* release it into the public domain.
* Please feel free to use it for whatever you want.
* Credit is appreciated where appropriate, and I also
* appreciate being told where this code finds any use,
* but you may do as you like.
*/
/*
* This implementation is "Simplex Noise" as presented by
* Ken Perlin at a relatively obscure and not often cited course
* session "Real-Time Shading" at Siggraph 2001 (before real
* time shading actually took off), under the title "hardware noise".
* The 3D function is numerically equivalent to his Java reference
* code available in the PDF course notes, although I re-implemented
* it from scratch to get more readable code. The 1D, 2D and 4D cases
* were implemented from scratch by me from Ken Perlin's text.
*
* This file has no dependencies on any other file, not even its own
* header file. The header file is made for use by external code only.
*/
// We don't really need to include this, but play nice and do it anyway.
//#include "noise.c"
#define FASTFLOOR(x) ( ((int)(x)<=(x)) ? ((int)x) : (((int)x)-1) )
//---------------------------------------------------------------------
// Static data
/*
* Permutation table. This is just a random jumble of all numbers 0-255,
* repeated twice to avoid wrapping the index at 255 for each lookup.
* This needs to be exactly the same for all instances on all platforms,
* so it's easiest to just keep it as static explicit data.
* This also removes the need for any initialisation of this class.
*
* Note that making this an int[] instead of a char[] might make the
* code run faster on platforms with a high penalty for unaligned single
* byte addressing. Intel x86 is generally single-byte-friendly, but
* some other CPUs are faster with 4-aligned reads.
* However, a char[] is smaller, which avoids cache trashing, and that
* is probably the most important aspect on most architectures.
* This array is accessed a *lot* by the noise functions.
* A vector-valued noise over 3D accesses it 96 times, and a
* float-valued 4D noise 64 times. We want this to fit in the cache!
*/
unsigned char perm[512] = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
//---------------------------------------------------------------------
/*
* Helper functions to compute gradients-dot-residualvectors (1D to 4D)
* Note that these generate gradients of more than unit length. To make
* a close match with the value range of classic Perlin noise, the final
* noise values need to be rescaled to fit nicely within [-1,1].
* (The simplex noise functions as such also have different scaling.)
* Note also that these noise functions are the most practical and useful
* signed version of Perlin noise. To return values according to the
* RenderMan specification from the SL noise() and pnoise() functions,
* the noise values need to be scaled and offset to [0,1], like this:
* float SLnoise = (noise(x,y,z) + 1.0) * 0.5;
*/
float grad1( int hash, float x ) {
int h = hash & 15;
float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
if (h&8) grad = -grad; // Set a random sign for the gradient
return ( grad * x ); // Multiply the gradient with the distance
}
float grad2( int hash, float x, float y ) {
int h = hash & 7; // Convert low 3 bits of hash code
float u = h<4 ? x : y; // into 8 simple gradient directions,
float v = h<4 ? y : x; // and compute the dot product with (x,y).
return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v);
}
float grad3( int hash, float x, float y , float z ) {
int h = hash & 15; // Convert low 4 bits of hash code into 12 simple
float u = h<8 ? x : y; // gradient directions, and compute dot product.
float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15
return ((h&1)? -u : u) + ((h&2)? -v : v);
}
float grad4( int hash, float x, float y, float z, float t ) {
int h = hash & 31; // Convert low 5 bits of hash code into 32 simple
float u = h<24 ? x : y; // gradient directions, and compute dot product.
float v = h<16 ? y : z;
float w = h<8 ? z : t;
return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w);
}
// A lookup table to traverse the simplex around a given point in 4D.
// Details can be found where this table is used, in the 4D noise method.
/* TODO: This should not be required, backport it from Bill's GLSL code! */
static unsigned char simplex[64][4] = {
{0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0},
{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0},
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},
{1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
{2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0},
{2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}};
// 1D simplex noise
float snoise1(float x) {
int i0 = FASTFLOOR(x);
int i1 = i0 + 1;
float x0 = x - i0;
float x1 = x0 - 1.0f;
float n0, n1;
float t0 = 1.0f - x0*x0;
// if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case
t0 *= t0;
n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0);
float t1 = 1.0f - x1*x1;
// if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case
t1 *= t1;
n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1);
// The maximum value of this noise is 8*(3/4)^4 = 2.53125
// A factor of 0.395 would scale to fit exactly within [-1,1], but
// we want to match PRMan's 1D noise, so we scale it down some more.
return 0.25f * (n0 + n1);
}
// 2D simplex noise
float snoise2(float x, float y) {
#define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0)
#define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0
float n0, n1, n2; // Noise contributions from the three corners
// Skew the input space to determine which simplex cell we're in
float s = (x+y)*F2; // Hairy factor for 2D
float xs = x + s;
float ys = y + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
float t = (float)(i+j)*G2;
float X0 = i-t; // Unskew the cell origin back to (x,y) space
float Y0 = j-t;
float x0 = x-X0; // The x,y distances from the cell origin
float y0 = y-Y0;
// For the 2D case, the simplex shape is an equilateral triangle.
// Determine which simplex we are in.
int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
// c = (3-sqrt(3))/6
float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
float y1 = y0 - j1 + G2;
float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords
float y2 = y0 - 1.0f + 2.0f * G2;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
// Calculate the contribution from the three corners
float t0 = 0.5f - x0*x0-y0*y0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0);
}
float t1 = 0.5f - x1*x1-y1*y1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1);
}
float t2 = 0.5f - x2*x2-y2*y2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary!
}
// 3D simplex noise
float snoise3(float x, float y, float z) {
// Simple skewing factors for the 3D case
#define F3 0.333333333
#define G3 0.166666667
float n0, n1, n2, n3; // Noise contributions from the four corners
// Skew the input space to determine which simplex cell we're in
float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D
float xs = x+s;
float ys = y+s;
float zs = z+s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
float t = (float)(i+j+k)*G3;
float X0 = i-t; // Unskew the cell origin back to (x,y,z) space
float Y0 = j-t;
float Z0 = k-t;
float x0 = x-X0; // The x,y,z distances from the cell origin
float y0 = y-Y0;
float z0 = z-Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
/* This code would benefit from a backport from the GLSL version! */
if(x0>=y0) {
if(y0>=z0)
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
// c = 1/6.
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
float y1 = y0 - j1 + G3;
float z1 = z0 - k1 + G3;
float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords
float y2 = y0 - j2 + 2.0f*G3;
float z2 = z0 - k2 + 2.0f*G3;
float x3 = x0 - 1.0f + 3.0f*G3; // Offsets for last corner in (x,y,z) coords
float y3 = y0 - 1.0f + 3.0f*G3;
float z3 = z0 - 1.0f + 3.0f*G3;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
// Calculate the contribution from the four corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad3(perm[ii+perm[jj+perm[kk]]], x0, y0, z0);
}
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad3(perm[ii+i1+perm[jj+j1+perm[kk+k1]]], x1, y1, z1);
}
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad3(perm[ii+i2+perm[jj+j2+perm[kk+k2]]], x2, y2, z2);
}
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3;
if(t3<0.0f) n3 = 0.0f;
else {
t3 *= t3;
n3 = t3 * t3 * grad3(perm[ii+1+perm[jj+1+perm[kk+1]]], x3, y3, z3);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return 72.0f * (n0 + n1 + n2 + n3);
}
// 4D simplex noise
float snoise4(float x, float y, float z, float w) {
// The skewing and unskewing factors are hairy again for the 4D case
#define F4 0.309016994 // F4 = (Math.sqrt(5.0)-1.0)/4.0
#define G4 0.138196601 // G4 = (5.0-Math.sqrt(5.0))/20.0
float n0, n1, n2, n3, n4; // Noise contributions from the five corners
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
float s = (x + y + z + w) * F4; // Factor for 4D skewing
float xs = x + s;
float ys = y + s;
float zs = z + s;
float ws = w + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
int l = FASTFLOOR(ws);
float t = (i + j + k + l) * G4; // Factor for 4D unskewing
float X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
float Y0 = j - t;
float Z0 = k - t;
float W0 = l - t;
float x0 = x - X0; // The x,y,z,w distances from the cell origin
float y0 = y - Y0;
float z0 = z - Z0;
float w0 = w - W0;
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
// To find out which of the 24 possible simplices we're in, we need to
// determine the magnitude ordering of x0, y0, z0 and w0.
// The method below is a good way of finding the ordering of x,y,z,w and
// then find the correct traversal order for the simplex we?re in.
// First, six pair-wise comparisons are performed between each possible pair
// of the four coordinates, and the results are used to add up binary bits
// for an integer index.
int c1 = (x0 > y0) ? 32 : 0;
int c2 = (x0 > z0) ? 16 : 0;
int c3 = (y0 > z0) ? 8 : 0;
int c4 = (x0 > w0) ? 4 : 0;
int c5 = (y0 > w0) ? 2 : 0;
int c6 = (z0 > w0) ? 1 : 0;
int c = c1 + c2 + c3 + c4 + c5 + c6;
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
// impossible. Only the 24 indices which have non-zero entries make any sense.
// We use a thresholding to set the coordinates in turn from the largest magnitude.
// The number 3 in the "simplex" array is at the position of the largest coordinate.
i1 = simplex[c][0]>=3 ? 1 : 0;
j1 = simplex[c][1]>=3 ? 1 : 0;
k1 = simplex[c][2]>=3 ? 1 : 0;
l1 = simplex[c][3]>=3 ? 1 : 0;
// The number 2 in the "simplex" array is at the second largest coordinate.
i2 = simplex[c][0]>=2 ? 1 : 0;
j2 = simplex[c][1]>=2 ? 1 : 0;
k2 = simplex[c][2]>=2 ? 1 : 0;
l2 = simplex[c][3]>=2 ? 1 : 0;
// The number 1 in the "simplex" array is at the second smallest coordinate.
i3 = simplex[c][0]>=1 ? 1 : 0;
j3 = simplex[c][1]>=1 ? 1 : 0;
k3 = simplex[c][2]>=1 ? 1 : 0;
l3 = simplex[c][3]>=1 ? 1 : 0;
// The fifth corner has all coordinate offsets = 1, so no need to look that up.
float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
float y1 = y0 - j1 + G4;
float z1 = z0 - k1 + G4;
float w1 = w0 - l1 + G4;
float x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords
float y2 = y0 - j2 + 2.0f*G4;
float z2 = z0 - k2 + 2.0f*G4;
float w2 = w0 - l2 + 2.0f*G4;
float x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords
float y3 = y0 - j3 + 3.0f*G4;
float z3 = z0 - k3 + 3.0f*G4;
float w3 = w0 - l3 + 3.0f*G4;
float x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords
float y4 = y0 - 1.0f + 4.0f*G4;
float z4 = z0 - 1.0f + 4.0f*G4;
float w4 = w0 - 1.0f + 4.0f*G4;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
int ll = l & 0xff;
// Calculate the contribution from the five corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0 - w0*w0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad4(perm[ii+perm[jj+perm[kk+perm[ll]]]], x0, y0, z0, w0);
}
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1 - w1*w1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad4(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], x1, y1, z1, w1);
}
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2 - w2*w2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad4(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], x2, y2, z2, w2);
}
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3 - w3*w3;
if(t3 < 0.0f) n3 = 0.0f;
else {
t3 *= t3;
n3 = t3 * t3 * grad4(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], x3, y3, z3, w3);
}
float t4 = 0.5f - x4*x4 - y4*y4 - z4*z4 - w4*w4;
if(t4 < 0.0f) n4 = 0.0f;
else {
t4 *= t4;
n4 = t4 * t4 * grad4(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], x4, y4, z4, w4);
}
// Sum up and scale the result to cover the range [-1,1]
return 62.0f * (n0 + n1 + n2 + n3 + n4);
}
#undef F2
#undef G2
#undef F3
#undef G3
#undef F4
#undef G4
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,290 +1,290 @@
// https://github.com/BareRose/swrap/blob/master/swrap.h
/*
swrap - Portable, protocol-agnostic TCP and UDP socket wrapper, primarily designed for client-server models in applications such as games
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring
rights to this software to the public domain worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with this software.
If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
/*
swrap supports the following three configurations:
#define SWRAP_EXTERN
Default, should be used when using swrap in multiple compilation units within the same project.
#define SWRAP_IMPLEMENTATION
Must be defined in exactly one source file within a project for swrap to be found by the linker.
#define SWRAP_STATIC
Defines all swrap functions as static, useful if swrap is only used in a single compilation unit.
*/
//include only once
#ifndef SWRAP_H
#define SWRAP_H
//process configuration
#ifdef SWRAP_STATIC
#define SWRAP_IMPLEMENTATION
#define SWDEF static
#else //SWRAP_EXTERN
#define SWDEF extern
#endif
//constants
#define SWRAP_TCP 0
#define SWRAP_UDP 1
#define SWRAP_BIND 0
#define SWRAP_CONNECT 1
#define SWRAP_DEFAULT 0x00
#define SWRAP_NOBLOCK 0x01
#define SWRAP_NODELAY 0x02
//structs
struct swrap_addr {
char data[128]; //enough space to hold any kind of address
};
//function declarations
SWDEF int swrapInit();
SWDEF int swrapSocket(int, int, char, const char*, const char*);
SWDEF void swrapClose(int);
SWDEF void swrapTerminate();
SWDEF int swrapListen(int, int);
SWDEF int swrapAccept(int, struct swrap_addr*);
SWDEF int swrapAddress(int, struct swrap_addr*);
SWDEF int swrapAddressInfo(struct swrap_addr*, char*, size_t, char*, size_t);
SWDEF int swrapSend(int, const char*, size_t);
SWDEF int swrapReceive(int, char*, size_t);
SWDEF int swrapSendTo(int, struct swrap_addr*, const char*, size_t);
SWDEF int swrapReceiveFrom(int, struct swrap_addr*, char*, size_t);
SWDEF int swrapSelect(int, double);
SWDEF int swrapMultiSelect(int*, size_t, double);
//implementation section
#ifdef SWRAP_IMPLEMENTATION
//includes
#ifdef _WIN32 //windows
#include <ws2tcpip.h>
#else //unix
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/tcp.h>
#endif
#include <stddef.h> //NULL
#include <limits.h> //INT_MAX on emscripten //< @r-lyeh: added
//general functions
SWDEF int swrapInit () {
//initializes socket functionality, returns 0 on success
#ifdef _WIN32
WSADATA WsaData;
return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR);
#else
return 0;
#endif
}
SWDEF int swrapSocket (int prot, int mode, char flags, const char* host, const char* serv) {
//protocol-agnostically creates a new socket configured according to the given parameters
//sockets have to be created and bound/connected all at once to allow for protocol-agnosticity
//int: Protocol of the socket, either SWRAP_TCP or SWRAP_UDP for TCP or UDP respectively
// SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept
// SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent
//int: Mode of the socket
// SWRAP_BIND: Bind to given address (or all interfaces if NULL) and port, e.g. for a server
// SWRAP_CONNECT: Connect to given address (localhost if NULL) and port, e.g. for a client
//char: Configuration flags, either SWRAP_DEFAULT or a bitwise combination of flags
// SWRAP_NOBLOCK: Sets the socket to be non-blocking, default is blocking
// SWRAP_NODELAY: Disables Nagle's for TCP sockets, default is enabled
//char*: Host/address as a string, can be IPv4, IPv6, etc...
//char*: Service/port as a string, e.g. "1728" or "http"
//returns socket handle, or -1 on failure
struct addrinfo* result, hint = {
(mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags
AF_UNSPEC, //ai_family
(prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype
0, 0, NULL, NULL, NULL};
//get address info
if (getaddrinfo(host, serv, &hint, &result)) return -1;
//create socket
#ifdef _WIN32
SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//convert to int
int sock = wsck;
#else
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == -1) return -1;
#endif
//make sure IPV6_ONLY is disabled
if (result->ai_family == AF_INET6) {
int no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
}
//set TCP_NODELAY if applicable
if (prot == SWRAP_TCP) {
int nodelay = (flags&SWRAP_NODELAY);
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay));
}
//bind if applicable
if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) {
swrapClose(sock);
return -1;
}
//set non-blocking if needed
if (flags&SWRAP_NOBLOCK) {
#ifdef _WIN32
DWORD no_block = 1;
if (ioctlsocket(sock, FIONBIO, &no_block)) {
swrapClose(sock);
return -1;
}
#else
if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) {
swrapClose(sock);
return -1;
}
#endif
}
//connect if applicable (return only relevant if blocking)
if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) {
swrapClose(sock);
return -1;
}
//free address info
freeaddrinfo(result);
//return socket handle
return sock;
}
SWDEF void swrapClose (int sock) {
//closes the given socket
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
}
SWDEF void swrapTerminate () {
//terminates socket functionality
#ifdef _WIN32
WSACleanup();
#endif
}
//connection functions
SWDEF int swrapListen (int sock, int blog) {
//configures the given socket (must be SWRAP_TCP + SWRAP_BIND) to listen for new connections with given maximum backlog
//returns 0 on success, non-zero on failure
return listen(sock, blog);
}
SWDEF int swrapAccept (int sock, struct swrap_addr* addr) {
//uses the given socket (must be swrapListen) to accept a new incoming connection, optionally returning its address
//returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//return new socket
return wsck;
#else
socklen_t addr_size = sizeof(struct swrap_addr);
return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
#endif
}
//address functions
SWDEF int swrapAddress (int sock, struct swrap_addr* addr) {
//writes the address the given socket is bound to into given address pointer, useful when automatically assigning port
//returns 0 on success, non-zero on failure
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return getsockname(sock, (struct sockaddr*)addr, &addr_size);
}
SWDEF int swrapAddressInfo (struct swrap_addr* addr, char* host, size_t host_size, char* serv, size_t serv_size) {
//writes the host/address and service/port of given address into given buffers (pointer + size), either buffer may be NULL
//returns 0 on success, non-zero on failure
return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0);
}
//send/receive functions
SWDEF int swrapSend (int sock, const char* data, size_t data_size) {
//uses the given socket (either SWRAP_CONNECT or returned by swrapAccept) to send given data (pointer + size)
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
//returns how much data was actually sent (may be less than data size), or -1 on failure
return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
SWDEF int swrapReceive (int sock, char* data, size_t data_size) {
//receives data using given socket (either SWRAP_CONNECT or returned by swrapAccept) into given buffer (pointer + size)
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
return recv(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
SWDEF int swrapSendTo (int sock, struct swrap_addr* addr, const char* data, size_t data_size) {
//uses the given socket to send given data (pointer + size) to the given swrap_addr (e.g. from swrapReceiveFrom)
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
//returns how much data was actually sent (may be less than data size), or -1 on failure
return sendto(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, sizeof(struct swrap_addr));
}
SWDEF int swrapReceiveFrom (int sock, struct swrap_addr* addr, char* data, size_t data_size) {
//receives data using given socket into given buffer (pointer + size), optionally returning sender's address
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size);
}
//select functions
SWDEF int swrapSelect (int sock, double timeout) {
//waits either until given socket has new data to receive or given time (in seconds) has passed
//if given socket is -1 an empty select will be performed, which is just a sub-second sleep
//returns 1 if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time;
//fd set
FD_ZERO(&set);
if (sock > -1) FD_SET(sock, &set);
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock+1, &set, NULL, NULL, &time);
}
SWDEF int swrapMultiSelect (int* socks, size_t socks_size, double timeout) {
//waits either until a socket in given list has new data to receive or given time (in seconds) has passed
//if the given list length is 0 an empty select will be performed, which is just a sub-second sleep
//returns 1 or more if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time; int sock_max = -1;
//fd set
FD_ZERO(&set);
for (size_t i = 0; i < socks_size; i++) {
if (socks[i] > sock_max) sock_max = socks[i];
if (socks[i] > -1) FD_SET(socks[i], &set);
}
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock_max+1, &set, NULL, NULL, &time);
}
#endif //SWRAP_IMPLEMENTATION
#endif //SWRAP_H
// https://github.com/BareRose/swrap/blob/master/swrap.h
/*
swrap - Portable, protocol-agnostic TCP and UDP socket wrapper, primarily designed for client-server models in applications such as games
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring
rights to this software to the public domain worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with this software.
If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
/*
swrap supports the following three configurations:
#define SWRAP_EXTERN
Default, should be used when using swrap in multiple compilation units within the same project.
#define SWRAP_IMPLEMENTATION
Must be defined in exactly one source file within a project for swrap to be found by the linker.
#define SWRAP_STATIC
Defines all swrap functions as static, useful if swrap is only used in a single compilation unit.
*/
//include only once
#ifndef SWRAP_H
#define SWRAP_H
//process configuration
#ifdef SWRAP_STATIC
#define SWRAP_IMPLEMENTATION
#define SWDEF static
#else //SWRAP_EXTERN
#define SWDEF extern
#endif
//constants
#define SWRAP_TCP 0
#define SWRAP_UDP 1
#define SWRAP_BIND 0
#define SWRAP_CONNECT 1
#define SWRAP_DEFAULT 0x00
#define SWRAP_NOBLOCK 0x01
#define SWRAP_NODELAY 0x02
//structs
struct swrap_addr {
char data[128]; //enough space to hold any kind of address
};
//function declarations
SWDEF int swrapInit();
SWDEF int swrapSocket(int, int, char, const char*, const char*);
SWDEF void swrapClose(int);
SWDEF void swrapTerminate();
SWDEF int swrapListen(int, int);
SWDEF int swrapAccept(int, struct swrap_addr*);
SWDEF int swrapAddress(int, struct swrap_addr*);
SWDEF int swrapAddressInfo(struct swrap_addr*, char*, size_t, char*, size_t);
SWDEF int swrapSend(int, const char*, size_t);
SWDEF int swrapReceive(int, char*, size_t);
SWDEF int swrapSendTo(int, struct swrap_addr*, const char*, size_t);
SWDEF int swrapReceiveFrom(int, struct swrap_addr*, char*, size_t);
SWDEF int swrapSelect(int, double);
SWDEF int swrapMultiSelect(int*, size_t, double);
//implementation section
#ifdef SWRAP_IMPLEMENTATION
//includes
#ifdef _WIN32 //windows
#include <ws2tcpip.h>
#else //unix
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/tcp.h>
#endif
#include <stddef.h> //NULL
#include <limits.h> //INT_MAX on emscripten //< @r-lyeh: added
//general functions
SWDEF int swrapInit () {
//initializes socket functionality, returns 0 on success
#ifdef _WIN32
WSADATA WsaData;
return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR);
#else
return 0;
#endif
}
SWDEF int swrapSocket (int prot, int mode, char flags, const char* host, const char* serv) {
//protocol-agnostically creates a new socket configured according to the given parameters
//sockets have to be created and bound/connected all at once to allow for protocol-agnosticity
//int: Protocol of the socket, either SWRAP_TCP or SWRAP_UDP for TCP or UDP respectively
// SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept
// SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent
//int: Mode of the socket
// SWRAP_BIND: Bind to given address (or all interfaces if NULL) and port, e.g. for a server
// SWRAP_CONNECT: Connect to given address (localhost if NULL) and port, e.g. for a client
//char: Configuration flags, either SWRAP_DEFAULT or a bitwise combination of flags
// SWRAP_NOBLOCK: Sets the socket to be non-blocking, default is blocking
// SWRAP_NODELAY: Disables Nagle's for TCP sockets, default is enabled
//char*: Host/address as a string, can be IPv4, IPv6, etc...
//char*: Service/port as a string, e.g. "1728" or "http"
//returns socket handle, or -1 on failure
struct addrinfo* result, hint = {
(mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags
AF_UNSPEC, //ai_family
(prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype
0, 0, NULL, NULL, NULL};
//get address info
if (getaddrinfo(host, serv, &hint, &result)) return -1;
//create socket
#ifdef _WIN32
SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//convert to int
int sock = wsck;
#else
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == -1) return -1;
#endif
//make sure IPV6_ONLY is disabled
if (result->ai_family == AF_INET6) {
int no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
}
//set TCP_NODELAY if applicable
if (prot == SWRAP_TCP) {
int nodelay = (flags&SWRAP_NODELAY);
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay));
}
//bind if applicable
if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) {
swrapClose(sock);
return -1;
}
//set non-blocking if needed
if (flags&SWRAP_NOBLOCK) {
#ifdef _WIN32
DWORD no_block = 1;
if (ioctlsocket(sock, FIONBIO, &no_block)) {
swrapClose(sock);
return -1;
}
#else
if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) {
swrapClose(sock);
return -1;
}
#endif
}
//connect if applicable (return only relevant if blocking)
if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) {
swrapClose(sock);
return -1;
}
//free address info
freeaddrinfo(result);
//return socket handle
return sock;
}
SWDEF void swrapClose (int sock) {
//closes the given socket
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
}
SWDEF void swrapTerminate () {
//terminates socket functionality
#ifdef _WIN32
WSACleanup();
#endif
}
//connection functions
SWDEF int swrapListen (int sock, int blog) {
//configures the given socket (must be SWRAP_TCP + SWRAP_BIND) to listen for new connections with given maximum backlog
//returns 0 on success, non-zero on failure
return listen(sock, blog);
}
SWDEF int swrapAccept (int sock, struct swrap_addr* addr) {
//uses the given socket (must be swrapListen) to accept a new incoming connection, optionally returning its address
//returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//return new socket
return wsck;
#else
socklen_t addr_size = sizeof(struct swrap_addr);
return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
#endif
}
//address functions
SWDEF int swrapAddress (int sock, struct swrap_addr* addr) {
//writes the address the given socket is bound to into given address pointer, useful when automatically assigning port
//returns 0 on success, non-zero on failure
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return getsockname(sock, (struct sockaddr*)addr, &addr_size);
}
SWDEF int swrapAddressInfo (struct swrap_addr* addr, char* host, size_t host_size, char* serv, size_t serv_size) {
//writes the host/address and service/port of given address into given buffers (pointer + size), either buffer may be NULL
//returns 0 on success, non-zero on failure
return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0);
}
//send/receive functions
SWDEF int swrapSend (int sock, const char* data, size_t data_size) {
//uses the given socket (either SWRAP_CONNECT or returned by swrapAccept) to send given data (pointer + size)
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
//returns how much data was actually sent (may be less than data size), or -1 on failure
return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
SWDEF int swrapReceive (int sock, char* data, size_t data_size) {
//receives data using given socket (either SWRAP_CONNECT or returned by swrapAccept) into given buffer (pointer + size)
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
return recv(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
SWDEF int swrapSendTo (int sock, struct swrap_addr* addr, const char* data, size_t data_size) {
//uses the given socket to send given data (pointer + size) to the given swrap_addr (e.g. from swrapReceiveFrom)
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
//returns how much data was actually sent (may be less than data size), or -1 on failure
return sendto(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, sizeof(struct swrap_addr));
}
SWDEF int swrapReceiveFrom (int sock, struct swrap_addr* addr, char* data, size_t data_size) {
//receives data using given socket into given buffer (pointer + size), optionally returning sender's address
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size);
}
//select functions
SWDEF int swrapSelect (int sock, double timeout) {
//waits either until given socket has new data to receive or given time (in seconds) has passed
//if given socket is -1 an empty select will be performed, which is just a sub-second sleep
//returns 1 if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time;
//fd set
FD_ZERO(&set);
if (sock > -1) FD_SET(sock, &set);
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock+1, &set, NULL, NULL, &time);
}
SWDEF int swrapMultiSelect (int* socks, size_t socks_size, double timeout) {
//waits either until a socket in given list has new data to receive or given time (in seconds) has passed
//if the given list length is 0 an empty select will be performed, which is just a sub-second sleep
//returns 1 or more if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time; int sock_max = -1;
//fd set
FD_ZERO(&set);
for (size_t i = 0; i < socks_size; i++) {
if (socks[i] > sock_max) sock_max = socks[i];
if (socks[i] > -1) FD_SET(socks[i], &set);
}
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock_max+1, &set, NULL, NULL, &time);
}
#endif //SWRAP_IMPLEMENTATION
#endif //SWRAP_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,82 +1,82 @@
// AI framework
// - rlyeh, public domain.
//
// [src] original A-star code by @mmozeiko (PD) - https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275
// [src] original swarm/boids code by @Cultrarius (UNLICENSE) - https://github.com/Cultrarius/Swarmz
// pathfinding -----------------------------------------------------------------
API int pathfind_astar(int width, int height, const unsigned* map, vec2i src, vec2i dst, vec2i* path, size_t maxpath);
// ----------------------------------------------------------------------------
// Behavior trees: decision planning and decision making.
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
typedef int (*bt_func)();
typedef struct bt_t {
uint64_t type;
int (*action)();
union {
int argi;
float argf;
};
array(struct bt_t) children;
} bt_t;
API bt_t bt(const char *ini_file, unsigned flags);
API int bt_run(bt_t *b);
API void bt_addfun(const char *name, int(*func)());
API bt_func bt_findfun(const char *name);
API char *bt_funcname(bt_func fn);
API int ui_bt(bt_t *b);
// boids/swarm -----------------------------------------------------------------
typedef enum SWARM_DISTANCE {
SWARM_DISTANCE_LINEAR,
SWARM_DISTANCE_INVERSE_LINEAR,
SWARM_DISTANCE_QUADRATIC,
SWARM_DISTANCE_INVERSE_QUADRATIC
} SWARM_DISTANCE;
#define boid(...) C_CAST(boid_t, __VA_ARGS__)
typedef struct boid_t {
vec3 position;
vec3 velocity;
vec3 acceleration;
vec3 prev_position;
} boid_t;
typedef struct swarm_t {
array(boid_t) boids;
float perception_radius; // determines the vision radius of each boid. Only boids within this distance influence each other.
float separation_weight; // how much boids repel each other
SWARM_DISTANCE separation_type;
float alignment_weight; // how much boids want go in the same direction
float cohesion_weight; // how much boids want to be in the center of the swarm
float steering_weight;
array(vec3) steering_targets;
SWARM_DISTANCE steering_target_type;
float blindspot_angledeg;
float max_acceleration; // how fast each boid can change its direction
float max_velocity; // how fast each boid can move
// private:
map(vec3*, array(boid_t*)) voxel_cache_;
float blindspot_angledeg_compare_value_;
} swarm_t;
API swarm_t swarm();
API void swarm_update(swarm_t *self, float delta); // acc,vel,pos
API void swarm_update_acceleration_only(swarm_t *self); // acc
API void swarm_update_acceleration_and_velocity_only(swarm_t *self, float delta); // acc,vel
API int ui_swarm(swarm_t *self);
// AI framework
// - rlyeh, public domain.
//
// [src] original A-star code by @mmozeiko (PD) - https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275
// [src] original swarm/boids code by @Cultrarius (UNLICENSE) - https://github.com/Cultrarius/Swarmz
// pathfinding -----------------------------------------------------------------
API int pathfind_astar(int width, int height, const unsigned* map, vec2i src, vec2i dst, vec2i* path, size_t maxpath);
// ----------------------------------------------------------------------------
// Behavior trees: decision planning and decision making.
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
typedef int (*bt_func)();
typedef struct bt_t {
uint64_t type;
int (*action)();
union {
int argi;
float argf;
};
array(struct bt_t) children;
} bt_t;
API bt_t bt(const char *ini_file, unsigned flags);
API int bt_run(bt_t *b);
API void bt_addfun(const char *name, int(*func)());
API bt_func bt_findfun(const char *name);
API char *bt_funcname(bt_func fn);
API int ui_bt(bt_t *b);
// boids/swarm -----------------------------------------------------------------
typedef enum SWARM_DISTANCE {
SWARM_DISTANCE_LINEAR,
SWARM_DISTANCE_INVERSE_LINEAR,
SWARM_DISTANCE_QUADRATIC,
SWARM_DISTANCE_INVERSE_QUADRATIC
} SWARM_DISTANCE;
#define boid(...) C_CAST(boid_t, __VA_ARGS__)
typedef struct boid_t {
vec3 position;
vec3 velocity;
vec3 acceleration;
vec3 prev_position;
} boid_t;
typedef struct swarm_t {
array(boid_t) boids;
float perception_radius; // determines the vision radius of each boid. Only boids within this distance influence each other.
float separation_weight; // how much boids repel each other
SWARM_DISTANCE separation_type;
float alignment_weight; // how much boids want go in the same direction
float cohesion_weight; // how much boids want to be in the center of the swarm
float steering_weight;
array(vec3) steering_targets;
SWARM_DISTANCE steering_target_type;
float blindspot_angledeg;
float max_acceleration; // how fast each boid can change its direction
float max_velocity; // how fast each boid can move
// private:
map(vec3*, array(boid_t*)) voxel_cache_;
float blindspot_angledeg_compare_value_;
} swarm_t;
API swarm_t swarm();
API void swarm_update(swarm_t *self, float delta); // acc,vel,pos
API void swarm_update_acceleration_only(swarm_t *self); // acc
API void swarm_update_acceleration_and_velocity_only(swarm_t *self, float delta); // acc,vel
API int ui_swarm(swarm_t *self);

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +1,60 @@
// -----------------------------------------------------------------------------
// audio framework
// - rlyeh, public domain
//
// fixme: leaks, audio_delete
// @todo: audio_volume_fx, audio_volume_bgm, audio_volume_master instead?
// @todo: destroystream() if( ss->type == WAV ) drwav_uninit(&ss->wav);
// @todo: destroystream() if( ss->type == MOD ) jar_mod_unload(&ss->mod);
// @todo: destroystream() if( ss->type == XM && ss->xm ) jar_xm_free_context(ss->xm);
// midi interface
API void midi_send(unsigned midi_msg);
// audio interface
typedef struct audio_handle* audio_t;
API audio_t audio_clip( const char *pathfile );
API audio_t audio_stream( const char *pathfile );
API int audio_play( audio_t s, int flags );
API int audio_play_gain( audio_t a, int flags, float gain/*0*/ );
API int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch/*1*/ );
API int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan/*0*/ );
API int audio_stop( audio_t a );
API void audio_loop( audio_t a, bool loop );
API bool audio_playing( audio_t a );
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. returns current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. returns current bgm volume in any case
API float audio_volume_master(float gain); // set master volume if gain is in [0..1] range. returns current master volume in any case
API int audio_mute(int mute);
API int audio_muted();
API int ui_audio();
enum AUDIO_FLAGS {
AUDIO_1CH = 0, // default
AUDIO_2CH = 1,
AUDIO_8 = 2,
AUDIO_16 = 0, // default
AUDIO_32 = 4,
AUDIO_FLOAT = 8,
AUDIO_8KHZ = 16,
AUDIO_11KHZ = 32,
AUDIO_22KHZ = 0, // default
AUDIO_32KHZ = 64,
AUDIO_44KHZ = 128,
AUDIO_MIXER_GAIN = 0, // default
AUDIO_IGNORE_MIXER_GAIN = 256,
AUDIO_MULTIPLE_INSTANCES = 0, // default
AUDIO_SINGLE_INSTANCE = 512,
};
API int audio_queue( const void *samples, int num_samples, int flags );
// -----------------------------------------------------------------------------
// audio framework
// - rlyeh, public domain
//
// fixme: leaks, audio_delete
// @todo: audio_volume_fx, audio_volume_bgm, audio_volume_master instead?
// @todo: destroystream() if( ss->type == WAV ) drwav_uninit(&ss->wav);
// @todo: destroystream() if( ss->type == MOD ) jar_mod_unload(&ss->mod);
// @todo: destroystream() if( ss->type == XM && ss->xm ) jar_xm_free_context(ss->xm);
// midi interface
API void midi_send(unsigned midi_msg);
// audio interface
typedef struct audio_handle* audio_t;
API audio_t audio_clip( const char *pathfile );
API audio_t audio_stream( const char *pathfile );
API int audio_play( audio_t s, int flags );
API int audio_play_gain( audio_t a, int flags, float gain/*0*/ );
API int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch/*1*/ );
API int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan/*0*/ );
API int audio_stop( audio_t a );
API void audio_loop( audio_t a, bool loop );
API bool audio_playing( audio_t a );
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. returns current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. returns current bgm volume in any case
API float audio_volume_master(float gain); // set master volume if gain is in [0..1] range. returns current master volume in any case
API int audio_mute(int mute);
API int audio_muted();
API int ui_audio();
enum AUDIO_FLAGS {
AUDIO_1CH = 0, // default
AUDIO_2CH = 1,
AUDIO_8 = 2,
AUDIO_16 = 0, // default
AUDIO_32 = 4,
AUDIO_FLOAT = 8,
AUDIO_8KHZ = 16,
AUDIO_11KHZ = 32,
AUDIO_22KHZ = 0, // default
AUDIO_32KHZ = 64,
AUDIO_44KHZ = 128,
AUDIO_MIXER_GAIN = 0, // default
AUDIO_IGNORE_MIXER_GAIN = 256,
AUDIO_MULTIPLE_INSTANCES = 0, // default
AUDIO_SINGLE_INSTANCE = 512,
};
API int audio_queue( const void *samples, int num_samples, int flags );

View File

@ -1,6 +1,6 @@
#define do_threadlock(mutexptr) \
for( int init_ = !!(mutexptr) || (thread_mutex_init( (mutexptr) = CALLOC(1, sizeof(thread_mutex_t)) ), 1); init_; init_ = 0) \
for( int lock_ = (thread_mutex_lock( mutexptr ), 1); lock_; lock_ = (thread_mutex_unlock( mutexptr ), 0) )
#define AS_NKCOLOR(color) \
((struct nk_color){ ((color>>0))&255,((color>>8))&255,((color>>16))&255,((color>>24))&255 })
#define do_threadlock(mutexptr) \
for( int init_ = !!(mutexptr) || (thread_mutex_init( (mutexptr) = CALLOC(1, sizeof(thread_mutex_t)) ), 1); init_; init_ = 0) \
for( int lock_ = (thread_mutex_lock( mutexptr ), 1); lock_; lock_ = (thread_mutex_unlock( mutexptr ), 0) )
#define AS_NKCOLOR(color) \
((struct nk_color){ ((color>>0))&255,((color>>8))&255,((color>>16))&255,((color>>24))&255 })

File diff suppressed because it is too large Load Diff

View File

@ -1,158 +1,89 @@
// -----------------------------------------------------------------------------
// original code by @vurtun (PD) and @barerose (CC0).
// [src] https://gist.github.com/vurtun/95f088e4889da2474ad1ce82d7911fee
// - rlyeh, public domain.
#ifndef GJK_H
#define GJK_H
#define GJK_MAX_ITERATIONS 20
typedef struct gjk_support {
int aid, bid;
vec3 a;
vec3 b;
} gjk_support;
typedef struct gjk_vertex {
vec3 a;
vec3 b;
vec3 p;
int aid, bid;
} gjk_vertex;
typedef struct gjk_simplex {
int max_iter, iter;
int hit, cnt;
gjk_vertex v[4];
float bc[4], D;
} gjk_simplex;
typedef struct gjk_result {
int hit;
vec3 p0;
vec3 p1;
float distance_squared;
int iterations;
} gjk_result;
int gjk(gjk_simplex *s, const gjk_support *sup, vec3 *dv);
gjk_result gjk_analyze(const gjk_simplex *s);
gjk_result gjk_quad(float a_radius, float b_radius);
#endif
//typedef struct gjk_result gjk_result;
typedef struct line { vec3 a, b; } line;
typedef struct sphere { vec3 c; float r; } sphere;
typedef struct aabb { vec3 min, max; } aabb;
typedef struct plane { vec3 p, n; } plane;
typedef struct capsule { vec3 a, b; float r; } capsule;
typedef struct ray { vec3 p, d; } ray;
typedef struct triangle { vec3 p0,p1,p2; } triangle;
typedef struct poly { vec3* verts; int cnt; } poly;
typedef union frustum { struct { vec4 l, r, t, b, n, f; }; vec4 pl[6]; float v[24]; } frustum;
#define line(...) C_CAST(line, __VA_ARGS__)
#define sphere(...) C_CAST(sphere, __VA_ARGS__)
#define aabb(...) C_CAST(aabb, __VA_ARGS__)
#define plane(...) C_CAST(plane, __VA_ARGS__)
#define capsule(...) C_CAST(capsule, __VA_ARGS__)
#define ray(p,normdir) C_CAST(ray, p, normdir)
#define triangle(...) C_CAST(triangle, __VA_ARGS__)
#define poly(...) C_CAST(poly, __VA_ARGS__)
#define frustum(...) C_CAST(frustum, __VA_ARGS__)
// ----------------------------------------------------------------------------
typedef struct hit {
union {
// general case
float depth;
// rays only: penetration (t0) and extraction (t1) points along ray line
struct { float t0, t1; };
// gjk only
struct { int hits; vec3 p0, p1; float distance2; int iterations; };
};
union { vec3 p; vec3 contact_point; };
union { vec3 n; vec3 normal; };
} hit;
#define hit(...) C_CAST(hit, __VA_ARGS__)
// ----------------------------------------------------------------------------
/* line/segment */
API float line_distance2_point(line l, vec3 p);
API vec3 line_closest_point(line l, vec3 p);
/* ray */
API float ray_test_plane(ray r, vec4 p4);
API float ray_test_triangle(ray r, triangle t);
API int ray_test_sphere(float *t0, float *t1, ray r, sphere s);
API int ray_test_aabb(float *t0, float *t1, ray r, aabb a);
API hit* ray_hit_plane(ray r, plane p);
API hit* ray_hit_triangle(ray r, triangle t);
API hit* ray_hit_sphere(ray r, sphere s);
API hit* ray_hit_aabb(ray r, aabb a);
/* sphere */
API vec3 sphere_closest_point(sphere s, vec3 p);
API hit* sphere_hit_aabb(sphere s, aabb a);
API hit* sphere_hit_capsule(sphere s, capsule c);
API hit* sphere_hit_sphere(sphere a, sphere b);
API int sphere_test_aabb(sphere s, aabb a);
API int sphere_test_capsule(sphere s, capsule c);
API int sphere_test_poly(sphere s, poly p);
API int sphere_test_sphere(sphere a, sphere b);
/* aabb */
API vec3 aabb_closest_point(aabb a, vec3 p);
API float aabb_distance2_point(aabb a, vec3 p);
API int aabb_contains_point(aabb a, vec3 p);
API hit* aabb_hit_aabb(aabb a, aabb b);
API hit* aabb_hit_capsule(aabb a, capsule c);
API hit* aabb_hit_sphere(aabb a, sphere s);
API int aabb_test_aabb(aabb a, aabb b);
API int aabb_test_capsule(aabb a, capsule c);
API int aabb_test_poly(aabb a, poly p);
API int aabb_test_sphere(aabb a, sphere s);
/* capsule */
API float capsule_distance2_point(capsule c, vec3 p);
API vec3 capsule_closest_point(capsule c, vec3 p);
API hit* capsule_hit_aabb(capsule c, aabb a);
API hit* capsule_hit_capsule(capsule a, capsule b);
API hit* capsule_hit_sphere(capsule c, sphere s);
API int capsule_test_aabb(capsule c, aabb a);
API int capsule_test_capsule(capsule a, capsule b);
API int capsule_test_poly(capsule c, poly p);
API int capsule_test_sphere(capsule c, sphere s);
/* poly: query */
API int poly_test_sphere(poly p, sphere s);
API int poly_test_aabb(poly p, aabb a);
API int poly_test_capsule(poly p, capsule c);
API int poly_test_poly(poly a, poly b);
/* poly: query transformed */
API int poly_test_sphere_transform(poly p, vec3 pos3, mat33 rot33, sphere s);
API int poly_test_aabb_transform(poly p, vec3 apos3, mat33 arot33, aabb a);
API int poly_test_capsule_transform(poly p, vec3 pos3, mat33 rot33, capsule c);
API int poly_test_poly_transform(poly a, vec3 apos3, mat33 arot33, poly b, vec3 bpos3, mat33 brot33);
/* poly: gjk result */
API int poly_hit_sphere(struct gjk_result *res, poly p, sphere s);
API int poly_hit_aabb(struct gjk_result *res, poly p, aabb a);
API int poly_hit_capsule(struct gjk_result *res, poly p, capsule c);
API int poly_hit_poly(struct gjk_result *res, poly a, poly b);
/* poly: gjk result transformed */
API int poly_hit_sphere_transform(struct gjk_result *res, poly p, vec3 pos3, mat33 rot33, sphere s);
API int poly_hit_aabb_transform(struct gjk_result *res, poly p, vec3 pos3, mat33 rot33, aabb a);
API int poly_hit_capsule_transform(struct gjk_result *res, poly p, vec3 pos3, mat33 rot33, capsule c);
API int poly_hit_poly_transform(struct gjk_result *res, poly a, vec3 at3, mat33 ar33, poly b, vec3 bt3, mat33 br33);
API vec4 plane4(vec3 p, vec3 n);
API frustum frustum_build(mat44 projview);
API int frustum_test_sphere(frustum f, sphere s);
API int frustum_test_aabb(frustum f, aabb a);
API poly poly_alloc(int cnt);
API void poly_free(poly *p);
API poly pyramid(vec3 from, vec3 to, float size); // poly_free() required
API poly diamond(vec3 from, vec3 to, float size); // poly_free() required
API void collide_demo(); // debug draw collisions
// -----------------------------------------------------------------------------
// original code by @vurtun (PD) and @barerose (CC0).
// [src] https://gist.github.com/vurtun/95f088e4889da2474ad1ce82d7911fee
// - rlyeh, public domain.
typedef struct line { vec3 a, b; } line;
typedef struct sphere { vec3 c; float r; } sphere;
typedef struct aabb { vec3 min, max; } aabb;
typedef struct plane { vec3 p, n; } plane;
typedef struct capsule { vec3 a, b; float r; } capsule;
typedef struct ray { vec3 p, d; } ray;
typedef struct triangle { vec3 p0,p1,p2; } triangle;
typedef union frustum { struct { vec4 l, r, t, b, n, f; }; vec4 pl[6]; float v[24]; } frustum;
#define line(...) C_CAST(line, __VA_ARGS__)
#define sphere(...) C_CAST(sphere, __VA_ARGS__)
#define aabb(...) C_CAST(aabb, __VA_ARGS__)
#define plane(...) C_CAST(plane, __VA_ARGS__)
#define capsule(...) C_CAST(capsule, __VA_ARGS__)
#define ray(p,normdir) C_CAST(ray, p, normdir)
#define triangle(...) C_CAST(triangle, __VA_ARGS__)
#define frustum(...) C_CAST(frustum, __VA_ARGS__)
// ----------------------------------------------------------------------------
typedef struct hit {
union {
// general case
float depth;
// rays only: penetration (t0) and extraction (t1) points along ray line
struct { float t0, t1; };
// gjk only
struct { int hits; vec3 p0, p1; float distance2; int iterations; };
};
union { vec3 p; vec3 contact_point; };
union { vec3 n; vec3 normal; };
} hit;
#define hit(...) C_CAST(hit, __VA_ARGS__)
// ----------------------------------------------------------------------------
/* line/segment */
API float line_distance2_point(line l, vec3 p);
API vec3 line_closest_point(line l, vec3 p);
/* ray */
API float ray_test_plane(ray r, vec4 p4);
API float ray_test_triangle(ray r, triangle t);
API int ray_test_sphere(float *t0, float *t1, ray r, sphere s);
API int ray_test_aabb(float *t0, float *t1, ray r, aabb a);
API hit* ray_hit_plane(ray r, plane p);
API hit* ray_hit_triangle(ray r, triangle t);
API hit* ray_hit_sphere(ray r, sphere s);
API hit* ray_hit_aabb(ray r, aabb a);
/* sphere */
API vec3 sphere_closest_point(sphere s, vec3 p);
API hit* sphere_hit_aabb(sphere s, aabb a);
API hit* sphere_hit_capsule(sphere s, capsule c);
API hit* sphere_hit_sphere(sphere a, sphere b);
API int sphere_test_aabb(sphere s, aabb a);
API int sphere_test_capsule(sphere s, capsule c);
API int sphere_test_sphere(sphere a, sphere b);
/* aabb */
API vec3 aabb_closest_point(aabb a, vec3 p);
API float aabb_distance2_point(aabb a, vec3 p);
API int aabb_contains_point(aabb a, vec3 p);
API hit* aabb_hit_aabb(aabb a, aabb b);
API hit* aabb_hit_capsule(aabb a, capsule c);
API hit* aabb_hit_sphere(aabb a, sphere s);
API int aabb_test_aabb(aabb a, aabb b);
API int aabb_test_capsule(aabb a, capsule c);
API int aabb_test_sphere(aabb a, sphere s);
/* capsule */
API float capsule_distance2_point(capsule c, vec3 p);
API vec3 capsule_closest_point(capsule c, vec3 p);
API hit* capsule_hit_aabb(capsule c, aabb a);
API hit* capsule_hit_capsule(capsule a, capsule b);
API hit* capsule_hit_sphere(capsule c, sphere s);
API int capsule_test_aabb(capsule c, aabb a);
API int capsule_test_capsule(capsule a, capsule b);
API int capsule_test_sphere(capsule c, sphere s);
API vec4 plane4(vec3 p, vec3 n);
API frustum frustum_build(mat44 projview);
API int frustum_test_sphere(frustum f, sphere s);
API int frustum_test_aabb(frustum f, aabb a);
API void collide_demo(); // debug draw collisions

View File

@ -1,116 +1,116 @@
//-----------------------------------------------------------------------------
// compat (unix & stdio.h)
#if is(tcc) && is(win32) // add missing symbols
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; /* :: */
const struct in6_addr in6addr_loopback; /* ::1 */
#endif
#if is(win32)
#include <io.h>
#if is(mingw)
#include <unistd.h>
#endif
#else
#include <unistd.h>
#include <sched.h> // sched_setaffinity(), CPU_ZERO(), CPU_COUNT()
#include <sys/ioctl.h>
#endif
#if is(ems)
//#define unlink(x) ((void)(x), 0)
#endif
#if is(win32)
//#define alloca _alloca
#define atoi64 _atoi64
#define popen _popen
#define pclose _pclose
//#define strncasecmp _strnicmp
#define mkdir(p,m) mkdir(p)
#define chdir ifdef(cl, _chdir, chdir)
#if is(cl) || is(tcc)
#define ftruncate _chsize_s
#endif
#define flockfile ifdef(cl,_lock_file,ifdef(mingw,_lock_file,(void)))
#define funlockfile ifdef(cl,_unlock_file,ifdef(mingw,_unlock_file,(void)))
#else // gcc
//#include <alloca.h> // mingw64 does not have it
#include <strings.h> // strncasecmp
#define atoi64 atoll
//#define strstri strcasestr
//#define strcmpi strcasecmp
#endif
#if defined MAX_PATH
#define DIR_MAX MAX_PATH
#elif defined PATH_MAX
#define DIR_MAX PATH_MAX
#else
#define DIR_MAX 260
#endif
#if is(win32) // _MSC_VER and __MINGW64__
#include <stdio.h>
#include <windows.h>
#include <share.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
FILE *fmemopen(void *buf, size_t len, const char *type) {
int fd = -1;
char temppath[DIR_MAX - 14], filename[DIR_MAX + 1];
if( GetTempPathA(sizeof(temppath), temppath) )
if( GetTempFileNameA(temppath, "v4k_temp", 0, filename) )
if( !_sopen_s(&fd, filename, _O_CREAT | _O_SHORT_LIVED | _O_TEMPORARY | _O_RDWR | _O_BINARY | _O_NOINHERIT, _SH_DENYRW, _S_IREAD | _S_IWRITE) )
for( FILE *fp = fd != -1 ? _fdopen(fd, "w+b") : NULL; fp; )
return fwrite(buf, len, 1, fp), rewind(fp), unlink(filename), fp; // no need to _close. fclose(on the returned FILE*) also _closes the file descriptor.
return fd != -1 ? _close(fd), NULL : NULL;
}
#endif
#if 0
#if !is(cl)
#define tmpfile file_temp
#endif
#define tmpnam(x) file_tempname()
#endif
#if 0
static
const char *pathfile_from_handle(FILE *fp) {
#if is(win32)
int fd = fileno(fp);
HANDLE handle = (HANDLE)_get_osfhandle( fd ); // <io.h>
DWORD size = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS);
wchar_t name[DIR_MAX] = L"";
size = GetFinalPathNameByHandleW(handle, name, size, VOLUME_NAME_DOS);
name[size] = L'\0';
return wchar16to8(name + 4); // skip \\?\ header
#else
// In Linux, you can use readlink on /proc/self/fd/NNN where NNN is the file descriptor
// In OSX:
// #include <sys/syslimits.h>
// #include <fcntl.h>
// char filePath[DIR_MAX];
// if (fcntl(fd, F_GETPATH, filePath) != -1) {
// // do something with the file path
// }
return 0;
#endif
}
#endif
// -----------------------------------------------------------------------------
// new C macros
#define cast(T) ifdef(c, void *, decltype(T))
#define literal(T) ifdef(c, T, (T))
// -----------------------------------------------------------------------------
void v4k_init();
static void v4k_pre_init();
static void v4k_post_init(float);
//-----------------------------------------------------------------------------
// compat (unix & stdio.h)
#if is(tcc) && is(win32) // add missing symbols
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; /* :: */
const struct in6_addr in6addr_loopback; /* ::1 */
#endif
#if is(win32)
#include <io.h>
#if is(mingw)
#include <unistd.h>
#endif
#else
#include <unistd.h>
#include <sched.h> // sched_setaffinity(), CPU_ZERO(), CPU_COUNT()
#include <sys/ioctl.h>
#endif
#if is(ems)
//#define unlink(x) ((void)(x), 0)
#endif
#if is(win32)
//#define alloca _alloca
#define atoi64 _atoi64
#define popen _popen
#define pclose _pclose
//#define strncasecmp _strnicmp
#define mkdir(p,m) mkdir(p)
#define chdir ifdef(cl, _chdir, chdir)
#if is(cl) || is(tcc)
#define ftruncate _chsize_s
#endif
#define flockfile ifdef(cl,_lock_file,ifdef(mingw,_lock_file,(void)))
#define funlockfile ifdef(cl,_unlock_file,ifdef(mingw,_unlock_file,(void)))
#else // gcc
//#include <alloca.h> // mingw64 does not have it
#include <strings.h> // strncasecmp
#define atoi64 atoll
//#define strstri strcasestr
//#define strcmpi strcasecmp
#endif
#if defined MAX_PATH
#define DIR_MAX MAX_PATH
#elif defined PATH_MAX
#define DIR_MAX PATH_MAX
#else
#define DIR_MAX 260
#endif
#if is(win32) // _MSC_VER and __MINGW64__
#include <stdio.h>
#include <windows.h>
#include <share.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
FILE *fmemopen(void *buf, size_t len, const char *type) {
int fd = -1;
char temppath[DIR_MAX - 14], filename[DIR_MAX + 1];
if( GetTempPathA(sizeof(temppath), temppath) )
if( GetTempFileNameA(temppath, "v4k_temp", 0, filename) )
if( !_sopen_s(&fd, filename, _O_CREAT | _O_SHORT_LIVED | _O_TEMPORARY | _O_RDWR | _O_BINARY | _O_NOINHERIT, _SH_DENYRW, _S_IREAD | _S_IWRITE) )
for( FILE *fp = fd != -1 ? _fdopen(fd, "w+b") : NULL; fp; )
return fwrite(buf, len, 1, fp), rewind(fp), unlink(filename), fp; // no need to _close. fclose(on the returned FILE*) also _closes the file descriptor.
return fd != -1 ? _close(fd), NULL : NULL;
}
#endif
#if 0
#if !is(cl)
#define tmpfile file_temp
#endif
#define tmpnam(x) file_tempname()
#endif
#if 0
static
const char *pathfile_from_handle(FILE *fp) {
#if is(win32)
int fd = fileno(fp);
HANDLE handle = (HANDLE)_get_osfhandle( fd ); // <io.h>
DWORD size = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS);
wchar_t name[DIR_MAX] = L"";
size = GetFinalPathNameByHandleW(handle, name, size, VOLUME_NAME_DOS);
name[size] = L'\0';
return wchar16to8(name + 4); // skip \\?\ header
#else
// In Linux, you can use readlink on /proc/self/fd/NNN where NNN is the file descriptor
// In OSX:
// #include <sys/syslimits.h>
// #include <fcntl.h>
// char filePath[DIR_MAX];
// if (fcntl(fd, F_GETPATH, filePath) != -1) {
// // do something with the file path
// }
return 0;
#endif
}
#endif
// -----------------------------------------------------------------------------
// new C macros
#define cast(T) ifdef(c, void *, decltype(T))
#define literal(T) ifdef(c, T, (T))
// -----------------------------------------------------------------------------
void v4k_init();
static void v4k_pre_init();
static void v4k_post_init(float);

View File

@ -1,415 +1,415 @@
// -----------------------------------------------------------------------------
// config directives
#ifndef ENABLE_FASTCALL_LUA
#define ENABLE_FASTCALL_LUA 1 ///+
#endif
#ifndef ENABLE_PROFILER
#define ENABLE_PROFILER ifdef(retail, 0, 1) ///+
#endif
#ifndef ENABLE_SELFIES
#define ENABLE_SELFIES 0 ///+
#endif
#ifndef ENABLE_MEMORY_POISON
#define ENABLE_MEMORY_POISON ifdef(debug, 1, 0) ///+
#endif
#ifndef ENABLE_MEMORY_LEAKS
#define ENABLE_MEMORY_LEAKS 0 ///+
#endif
#ifndef ENABLE_LINUX_CALLSTACKS
#define ENABLE_LINUX_CALLSTACKS 0 ///+
#endif
#ifndef ENABLE_AUTOTESTS
#define ENABLE_AUTOTESTS ifdef(debug, ifndef(ems, 1, 0), 0) ///+
#endif
#ifndef ENABLE_RETAIL
#define ENABLE_RETAIL 0 // ifdef(retail, 1, 0) ///+
#endif
#ifndef ENABLE_COOK
#define ENABLE_COOK ifdef(retail, 0, 1) ///+
#endif
#ifndef ENABLE_RPMALLOC
#define ENABLE_RPMALLOC 0 // ifdef(tcc, 0, 1) // forbidden on tcc because of lacking TLS support
#endif
// -----------------------------------------------------------------------------
// if/n/def hell
#define ifdef(macro, yes, /*no*/...) ifdef_##macro(yes, __VA_ARGS__)
#define ifndef(macro, yes, /*no*/...) ifdef_##macro(__VA_ARGS__, yes)
#define is(macro) ifdef_##macro(1,0)
#define isnt(macro) ifdef_##macro(0,1)
#define ifdef_true(yes, /*no*/...) yes
#define ifdef_false(yes, /*no*/...) __VA_ARGS__
#ifdef _MSC_VER
#define ifdef_gcc ifdef_false
#define ifdef_mingw ifdef_false
#define ifdef_tcc ifdef_false
#define ifdef_cl ifdef_true
#elif defined __TINYC__
#define ifdef_gcc ifdef_false
#define ifdef_mingw ifdef_false
#define ifdef_tcc ifdef_true
#define ifdef_cl ifdef_false
#elif defined __MINGW64__ || defined __MINGW32__
#define ifdef_gcc ifdef_true
#define ifdef_mingw ifdef_true
#define ifdef_tcc ifdef_false
#define ifdef_cl ifdef_false
#else // also __clang__
#define ifdef_gcc ifdef_true
#define ifdef_mingw ifdef_false
#define ifdef_tcc ifdef_false
#define ifdef_cl ifdef_false
#endif
#ifdef __cplusplus
#define ifdef_cpp ifdef_true
#define ifdef_c ifdef_false
#else
#define ifdef_c ifdef_true
#define ifdef_cpp ifdef_false
#endif
#if defined _WIN32
#define ifdef_win32 ifdef_true
#define ifdef_linux ifdef_false
#define ifdef_osx ifdef_false
#define ifdef_bsd ifdef_false
#define ifdef_ems ifdef_false
#elif defined __linux__
#define ifdef_win32 ifdef_false
#define ifdef_linux ifdef_true
#define ifdef_osx ifdef_false
#define ifdef_bsd ifdef_false
#define ifdef_ems ifdef_false
#elif defined __APPLE__
#define ifdef_win32 ifdef_false
#define ifdef_linux ifdef_false
#define ifdef_osx ifdef_true
#define ifdef_bsd ifdef_false
#define ifdef_ems ifdef_false
#elif defined __EMSCRIPTEN__
#define ifdef_win32 ifdef_false
#define ifdef_linux ifdef_false
#define ifdef_osx ifdef_false
#define ifdef_bsd ifdef_false
#define ifdef_ems ifdef_true
#else // __FreeBSD__ || @todo: __ANDROID_API__
#define ifdef_win32 ifdef_false
#define ifdef_linux ifdef_false
#define ifdef_osx ifdef_false
#define ifdef_bsd ifdef_true
#define ifdef_ems ifdef_false
#endif
#ifdef NDEBUG // rely on NDEBUG as the official/portable way to disable asserts
#define ifdef_debug ifdef_false
#define ifdef_release ifdef_true
#else
#define ifdef_debug ifdef_true
#define ifdef_release ifdef_false
#endif
#if ENABLE_RETAIL // NDEBUG>=2 ?
#define ifdef_retail ifdef_true
#else
#define ifdef_retail ifdef_false
#endif
#if ENABLE_COOK
#define ifdef_cook ifdef_true
#define ifdef_nocook ifdef_false
#else
#define ifdef_cook ifdef_false
#define ifdef_nocook ifdef_true
#endif
#if defined NDEBUG && NDEBUG >= 3 // we use NDEBUG=[0,1,2,3] to signal the compiler optimization flags O0,O1,O2,O3
#define ifdef_O3 ifdef_true
#define ifdef_O2 ifdef_false
#define ifdef_O1 ifdef_false
#define ifdef_O0 ifdef_false
#elif defined NDEBUG && NDEBUG >= 2
#define ifdef_O3 ifdef_false
#define ifdef_O2 ifdef_true
#define ifdef_O1 ifdef_false
#define ifdef_O0 ifdef_false
#elif defined NDEBUG && NDEBUG >= 1
#define ifdef_O3 ifdef_false
#define ifdef_O2 ifdef_false
#define ifdef_O1 ifdef_true
#define ifdef_O0 ifdef_false
#else
#define ifdef_O3 ifdef_false
#define ifdef_O2 ifdef_false
#define ifdef_O1 ifdef_false
#define ifdef_O0 ifdef_true
#endif
#include <stdint.h>
#if (defined INTPTR_MAX && INTPTR_MAX == INT64_MAX) || defined(_M_X64) || defined(__amd64__) || defined(__x86_64__) || defined(__ppc64__) || __WORDSIZE == 64
#define ifdef_64 ifdef_true
#define ifdef_32 ifdef_false
#else
#define ifdef_64 ifdef_false
#define ifdef_32 ifdef_true
#endif
// -----------------------------------------------------------------------------
// new C keywords
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
#define concat(a,b) conc4t(a,b)
#define conc4t(a,b) a##b ///-
#define macro(name) concat(name, __LINE__)
#define unique(name) concat(concat(concat(name,concat(_L,__LINE__)),_),__COUNTER__)
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
#define scope(end) defer((void)0, end)
#define benchmark for(double macro(i) = 1, macro(t) = (time_ss(),-time_ss()); macro(i); macro(t)+=time_ss(), macro(i)=0, printf("%.4fs %2.f%% (" FILELINE ")\n", macro(t), macro(t)*100/0.0166667 ))
#define benchmark_ms for(double macro(i) = 1, macro(t) = (time_ss(),-time_ss()); macro(i); macro(t)+=time_ss(), macro(i)=0, printf("%.2fms %2.f%% (" FILELINE ")\n", macro(t)*1000, macro(t)*100/0.016666667 ))
#define do_once static int macro(once) = 0; for(;!macro(once);macro(once)=1)
#if is(cl)
#define __thread __declspec(thread)
#elif is(tcc) && is(win32)
#define __thread __declspec(thread) // compiles fine apparently, but does not work
#elif is(tcc)
#define __thread
#endif
// usage: bool static(audio_is_init) = audio_init();
//#define static(var) static var; do_once var
//-----------------------------------------------------------------------------
// new C macros
#if ENABLE_RETAIL
#define ASSERT(expr, ...) (void)0
#define ASSERT_ONCE(expr, ...) (void)0
#else
#define ASSERT(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; alert(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)), breakpoint(); } } while(0)
#define ASSERT_ONCE(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; static int seen = 0; if(!seen) seen = 1, alert(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)), breakpoint(); } } while(0)
#endif
#ifndef STATIC_ASSERT
#define STATIC_ASSERT(EXPR) typedef struct { unsigned macro(static_assert_on_L) : !!(EXPR); } unique(static_assert_on_L)
#endif
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
#define STRINGIZE(x) STRINGIZ3(x)
#define STRINGIZ3(x) #x ///-
#define EXPAND(name, ...) EXPAND_QUOTE(EXPAND_JOIN(name, EXPAND_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
#define EXPAND_QUOTE(x, y) x y ///-
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count) ///-
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count) ///-
#define EXPAND_J01N(name, count) name##count ///-
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) ///-
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args ///-
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count ///-
// expands to the first argument
#define VA_FIRST(...) VA_F1RST(__VA_ARGS__, throwaway)
#define VA_F1RST(first, ...) first ///-
// if there's only one argument, expands to nothing. if there is more
// than one argument, expands to a comma followed by everything but
// the first argument. only supports up to 9 arguments but can be expanded.
#define VA_REST(...) VA_R3ST(VA_NUM(__VA_ARGS__), __VA_ARGS__)
#define VA_R3ST(qty, ...) VA_R3S7(qty, __VA_ARGS__) ///-
#define VA_R3S7(qty, ...) VA_R3S7_##qty(__VA_ARGS__) ///-
#define VA_R3S7_ONE(first) ///-
#define VA_R3S7_TWOORMORE(first, ...) , __VA_ARGS__ ///-
#define VA_NUM(...) VA_SELECT_10TH(__VA_ARGS__, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway) ///-
#define VA_SELECT_10TH(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, ...) A10
// VA_SPLIT() expands to A) 1 item OR B) 1 item + ',' + va_args[1..N]
#define VA_SPLIT(...) VA_FIRST(__VA_ARGS__) VA_REST(__VA_ARGS__)
// VA_COUNT() counts number of va args
#define VA_COUNT(...) (int)(sizeof((int[]){0, ##__VA_ARGS__})/sizeof(int)-1)
#if is(cl) && !is(cpp)
#define INLINE __inline
#else
#define INLINE inline
#endif
#if is(cl)
#define FORCE_INLINE __forceinline
#elif is(gcc)
#define FORCE_INLINE __attribute__((always_inline)) inline
#else
#define FORCE_INLINE INLINE
#endif
#if is(cl) && (_MSC_VER <= 1700)
#define FINITE _finite
#else
#define FINITE isfinite
#endif
// usage: #define vec2(...) C_CAST(vec2, __VA_ARGS__)
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
#define C_CAST(type, ...) ( ifdef(c,(type),type) { __VA_ARGS__ } )
// create a WARNING(...) macro
// usage: WARNING("this is displayed at compile time")
#if is(gcc)
# define WARNING(msg) WARN1NG( message( msg ) )
# define WARN1NG(msg) _Pragma(#msg)
#elif is(cl)
# define WARNING(msg) __pragma( message( msg ) )
#else
# define WARNING(msg)
#endif
// document todos and fixmes via compiler warnings
#define TODO(str) ifdef(debug,WARNING("TO DO: " str " (" FILELINE ")"))
#define FIXME(str) ifdef(debug,WARNING("FIXME: " str " (" FILELINE ")"))
// -----------------------------------------------------------------------------
// autorun initializers for C
// - rlyeh, public domain
//
// note: based on code by Joe Lowe (public domain).
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
#define AUTORUN AUTORUN_( unique(fn) )
#ifdef __cplusplus
#define AUTORUN_(fn) \
static void fn(void); \
static const int concat(fn,__1) = (fn(), 1); \
static void fn(void)
#elif defined _MSC_VER && !defined(__clang__) // cl, but not clang-cl
#define AUTORUN_(fn) \
static void fn(void); \
static int concat(fn,__1) (){ fn(); return 0; } \
__pragma(section(".CRT$XIU", long, read)) \
__declspec(allocate(".CRT$XIU")) \
static int(* concat(fn,__2) )() = concat(fn,__1); \
static void fn(void)
#elif defined __TINYC__ // tcc...
#define AUTORUN_(fn) \
__attribute__((constructor)) \
static void fn(void)
#else // gcc,clang,clang-cl...
#define AUTORUN_(fn) \
__attribute__((constructor(__COUNTER__+101))) \
static void fn(void)
#endif
// -----------------------------------------------------------------------------
// build info
#ifndef BUILD_VERSION
#define BUILD_VERSION ""
#endif
// -----------------------------------------------------------------------------
// visibility
// win32 users would need to -DAPI=EXPORT/IMPORT as needed when building/using V4K as DLL.
#define IMPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport)))
#define EXPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport)))
#define STATIC
#ifndef API
#define API STATIC
#endif
// -----------------------------------------------------------------------------
// system headers
#ifndef _GNU_SOURCE
#define _GNU_SOURCE ///- for linux
#endif
#if is(cl) && is(win32) // for VC IDE
#define _CRT_SECURE_NO_WARNINGS ///-
#define _CRT_NONSTDC_NO_DEPRECATE ///-
#define _WINSOCK_DEPRECATED_NO_WARNINGS ///-
#define _WIN32_WINNT 0x0600 ///- 0x0502 // GetInfoAddrW/FreeAddrInfoW for X86
#endif
#if is(cl)
#include <omp.h> // compile with /openmp to speed up some computations
#endif
#include <assert.h>
//#include <float.h>
//#include <limits.h>
#include <math.h> // NAN
#include <stdarg.h> // va_*(), ...
#include <stdbool.h> // bool,true,false
#include <stdint.h> // u/int8/16/32/64_t
#include <stdio.h> // FILE,NULL
#include <stdlib.h> // malloc,free,exit,
#include <string.h> // strlen,memset,memcpy,
#if is(tcc) && is(win32) && defined(__x86_64)
#include <tgmath.h>
// @fixme workarounds on `tcc0.9.27 -m64` (win) for fmod()/trunc() functions. test: 00-easing broken otherwise
//#define trunc(x) ((double)(int64_t)(x))
//#define fmod(x,y) ((x) - trunc((x) / (y)) * (y))
// @fixme workarounds on `tcc0.9.27 -m64` (win) for all functions with ending bool argument. test: 00-anims crashes otherwise
#undef bool
typedef char bool; ///-
// missing libm symbols on tinycc HEAD repo (tcc-x64 pre-0.9.28)
//#define fabsf fabs
#define sqrtf sqrt
#define sinf sin
#define asinf asin
#define cosf cos
#define acosf acos
#define tanf tan
#define atan2f atan2
#define powf pow
#define floorf floor
#define logf log
#define ceilf ceil
#define copysignf copysign
//#define ldexpf ldexp
#define expf exp
//#define frexpf frexp
#define fmodf fmod
#define log10f log10
//#define logf log
#define hypotf hypot
#endif
// -----------------------------------------------------------------------------
// pragma libs
#if is(win32) && (is(cl) || is(tcc))
#pragma comment(lib, "advapi32")
#pragma comment(lib, "comdlg32")
#pragma comment(lib, "dbghelp")
#pragma comment(lib, "gdi32")
#pragma comment(lib, "ole32")
#pragma comment(lib, "shell32")
#pragma comment(lib, "user32")
#pragma comment(lib, "winmm")
#pragma comment(lib, "wininet")
#pragma comment(lib, "ws2_32")
#endif
#if is(linux) && is(tcc)
#pragma comment(lib, "dl")
#pragma comment(lib, "m")
#pragma comment(lib, "pthread")
#endif
// -----------------------------------------------------------------------------
// config directives
#ifndef ENABLE_FASTCALL_LUA
#define ENABLE_FASTCALL_LUA 1 ///+
#endif
#ifndef ENABLE_PROFILER
#define ENABLE_PROFILER ifdef(retail, 0, 1) ///+
#endif
#ifndef ENABLE_SELFIES
#define ENABLE_SELFIES 0 ///+
#endif
#ifndef ENABLE_MEMORY_POISON
#define ENABLE_MEMORY_POISON ifdef(debug, 1, 0) ///+
#endif
#ifndef ENABLE_MEMORY_LEAKS
#define ENABLE_MEMORY_LEAKS 0 ///+
#endif
#ifndef ENABLE_LINUX_CALLSTACKS
#define ENABLE_LINUX_CALLSTACKS 0 ///+
#endif
#ifndef ENABLE_AUTOTESTS
#define ENABLE_AUTOTESTS ifdef(debug, ifndef(ems, 1, 0), 0) ///+
#endif
#ifndef ENABLE_RETAIL
#define ENABLE_RETAIL 0 // ifdef(retail, 1, 0) ///+
#endif
#ifndef ENABLE_COOK
#define ENABLE_COOK ifdef(retail, 0, 1) ///+
#endif
#ifndef ENABLE_RPMALLOC
#define ENABLE_RPMALLOC 0 // ifdef(tcc, 0, 1) // forbidden on tcc because of lacking TLS support
#endif
// -----------------------------------------------------------------------------
// if/n/def hell
#define ifdef(macro, yes, /*no*/...) ifdef_##macro(yes, __VA_ARGS__)
#define ifndef(macro, yes, /*no*/...) ifdef_##macro(__VA_ARGS__, yes)
#define is(macro) ifdef_##macro(1,0)
#define isnt(macro) ifdef_##macro(0,1)
#define ifdef_true(yes, /*no*/...) yes
#define ifdef_false(yes, /*no*/...) __VA_ARGS__
#ifdef _MSC_VER
#define ifdef_gcc ifdef_false
#define ifdef_mingw ifdef_false
#define ifdef_tcc ifdef_false
#define ifdef_cl ifdef_true
#elif defined __TINYC__
#define ifdef_gcc ifdef_false
#define ifdef_mingw ifdef_false
#define ifdef_tcc ifdef_true
#define ifdef_cl ifdef_false
#elif defined __MINGW64__ || defined __MINGW32__
#define ifdef_gcc ifdef_true
#define ifdef_mingw ifdef_true
#define ifdef_tcc ifdef_false
#define ifdef_cl ifdef_false
#else // also __clang__
#define ifdef_gcc ifdef_true
#define ifdef_mingw ifdef_false
#define ifdef_tcc ifdef_false
#define ifdef_cl ifdef_false
#endif
#ifdef __cplusplus
#define ifdef_cpp ifdef_true
#define ifdef_c ifdef_false
#else
#define ifdef_c ifdef_true
#define ifdef_cpp ifdef_false
#endif
#if defined _WIN32
#define ifdef_win32 ifdef_true
#define ifdef_linux ifdef_false
#define ifdef_osx ifdef_false
#define ifdef_bsd ifdef_false
#define ifdef_ems ifdef_false
#elif defined __linux__
#define ifdef_win32 ifdef_false
#define ifdef_linux ifdef_true
#define ifdef_osx ifdef_false
#define ifdef_bsd ifdef_false
#define ifdef_ems ifdef_false
#elif defined __APPLE__
#define ifdef_win32 ifdef_false
#define ifdef_linux ifdef_false
#define ifdef_osx ifdef_true
#define ifdef_bsd ifdef_false
#define ifdef_ems ifdef_false
#elif defined __EMSCRIPTEN__
#define ifdef_win32 ifdef_false
#define ifdef_linux ifdef_false
#define ifdef_osx ifdef_false
#define ifdef_bsd ifdef_false
#define ifdef_ems ifdef_true
#else // __FreeBSD__ || @todo: __ANDROID_API__
#define ifdef_win32 ifdef_false
#define ifdef_linux ifdef_false
#define ifdef_osx ifdef_false
#define ifdef_bsd ifdef_true
#define ifdef_ems ifdef_false
#endif
#ifdef NDEBUG // rely on NDEBUG as the official/portable way to disable asserts
#define ifdef_debug ifdef_false
#define ifdef_release ifdef_true
#else
#define ifdef_debug ifdef_true
#define ifdef_release ifdef_false
#endif
#if ENABLE_RETAIL // NDEBUG>=2 ?
#define ifdef_retail ifdef_true
#else
#define ifdef_retail ifdef_false
#endif
#if ENABLE_COOK
#define ifdef_cook ifdef_true
#define ifdef_nocook ifdef_false
#else
#define ifdef_cook ifdef_false
#define ifdef_nocook ifdef_true
#endif
#if defined NDEBUG && NDEBUG >= 3 // we use NDEBUG=[0,1,2,3] to signal the compiler optimization flags O0,O1,O2,O3
#define ifdef_O3 ifdef_true
#define ifdef_O2 ifdef_false
#define ifdef_O1 ifdef_false
#define ifdef_O0 ifdef_false
#elif defined NDEBUG && NDEBUG >= 2
#define ifdef_O3 ifdef_false
#define ifdef_O2 ifdef_true
#define ifdef_O1 ifdef_false
#define ifdef_O0 ifdef_false
#elif defined NDEBUG && NDEBUG >= 1
#define ifdef_O3 ifdef_false
#define ifdef_O2 ifdef_false
#define ifdef_O1 ifdef_true
#define ifdef_O0 ifdef_false
#else
#define ifdef_O3 ifdef_false
#define ifdef_O2 ifdef_false
#define ifdef_O1 ifdef_false
#define ifdef_O0 ifdef_true
#endif
#include <stdint.h>
#if (defined INTPTR_MAX && INTPTR_MAX == INT64_MAX) || defined(_M_X64) || defined(__amd64__) || defined(__x86_64__) || defined(__ppc64__) || __WORDSIZE == 64
#define ifdef_64 ifdef_true
#define ifdef_32 ifdef_false
#else
#define ifdef_64 ifdef_false
#define ifdef_32 ifdef_true
#endif
// -----------------------------------------------------------------------------
// new C keywords
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
#define concat(a,b) conc4t(a,b)
#define conc4t(a,b) a##b ///-
#define macro(name) concat(name, __LINE__)
#define unique(name) concat(concat(concat(name,concat(_L,__LINE__)),_),__COUNTER__)
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
#define scope(end) defer((void)0, end)
#define benchmark for(double macro(i) = 1, macro(t) = (time_ss(),-time_ss()); macro(i); macro(t)+=time_ss(), macro(i)=0, printf("%.4fs %2.f%% (" FILELINE ")\n", macro(t), macro(t)*100/0.0166667 ))
#define benchmark_ms for(double macro(i) = 1, macro(t) = (time_ss(),-time_ss()); macro(i); macro(t)+=time_ss(), macro(i)=0, printf("%.2fms %2.f%% (" FILELINE ")\n", macro(t)*1000, macro(t)*100/0.016666667 ))
#define do_once static int macro(once) = 0; for(;!macro(once);macro(once)=1)
#if is(cl)
#define __thread __declspec(thread)
#elif is(tcc) && is(win32)
#define __thread __declspec(thread) // compiles fine apparently, but does not work
#elif is(tcc)
#define __thread
#endif
// usage: bool static(audio_is_init) = audio_init();
//#define static(var) static var; do_once var
//-----------------------------------------------------------------------------
// new C macros
#if ENABLE_RETAIL
#define ASSERT(expr, ...) (void)0
#define ASSERT_ONCE(expr, ...) (void)0
#else
#define ASSERT(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; alert(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)), breakpoint(); } } while(0)
#define ASSERT_ONCE(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; static int seen = 0; if(!seen) seen = 1, alert(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)), breakpoint(); } } while(0)
#endif
#ifndef STATIC_ASSERT
#define STATIC_ASSERT(EXPR) typedef struct { unsigned macro(static_assert_on_L) : !!(EXPR); } unique(static_assert_on_L)
#endif
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
#define STRINGIZE(x) STRINGIZ3(x)
#define STRINGIZ3(x) #x ///-
#define EXPAND(name, ...) EXPAND_QUOTE(EXPAND_JOIN(name, EXPAND_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
#define EXPAND_QUOTE(x, y) x y ///-
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count) ///-
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count) ///-
#define EXPAND_J01N(name, count) name##count ///-
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) ///-
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args ///-
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count ///-
// expands to the first argument
#define VA_FIRST(...) VA_F1RST(__VA_ARGS__, throwaway)
#define VA_F1RST(first, ...) first ///-
// if there's only one argument, expands to nothing. if there is more
// than one argument, expands to a comma followed by everything but
// the first argument. only supports up to 9 arguments but can be expanded.
#define VA_REST(...) VA_R3ST(VA_NUM(__VA_ARGS__), __VA_ARGS__)
#define VA_R3ST(qty, ...) VA_R3S7(qty, __VA_ARGS__) ///-
#define VA_R3S7(qty, ...) VA_R3S7_##qty(__VA_ARGS__) ///-
#define VA_R3S7_ONE(first) ///-
#define VA_R3S7_TWOORMORE(first, ...) , __VA_ARGS__ ///-
#define VA_NUM(...) VA_SELECT_10TH(__VA_ARGS__, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway) ///-
#define VA_SELECT_10TH(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, ...) A10
// VA_SPLIT() expands to A) 1 item OR B) 1 item + ',' + va_args[1..N]
#define VA_SPLIT(...) VA_FIRST(__VA_ARGS__) VA_REST(__VA_ARGS__)
// VA_COUNT() counts number of va args
#define VA_COUNT(...) (int)(sizeof((int[]){0, ##__VA_ARGS__})/sizeof(int)-1)
#if is(cl) && !is(cpp)
#define INLINE __inline
#else
#define INLINE inline
#endif
#if is(cl)
#define FORCE_INLINE __forceinline
#elif is(gcc)
#define FORCE_INLINE __attribute__((always_inline)) inline
#else
#define FORCE_INLINE INLINE
#endif
#if is(cl) && (_MSC_VER <= 1700)
#define FINITE _finite
#else
#define FINITE isfinite
#endif
// usage: #define vec2(...) C_CAST(vec2, __VA_ARGS__)
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
#define C_CAST(type, ...) ( ifdef(c,(type),type) { __VA_ARGS__ } )
// create a WARNING(...) macro
// usage: WARNING("this is displayed at compile time")
#if is(gcc)
# define WARNING(msg) WARN1NG( message( msg ) )
# define WARN1NG(msg) _Pragma(#msg)
#elif is(cl)
# define WARNING(msg) __pragma( message( msg ) )
#else
# define WARNING(msg)
#endif
// document todos and fixmes via compiler warnings
#define TODO(str) ifdef(debug,WARNING("TO DO: " str " (" FILELINE ")"))
#define FIXME(str) ifdef(debug,WARNING("FIXME: " str " (" FILELINE ")"))
// -----------------------------------------------------------------------------
// autorun initializers for C
// - rlyeh, public domain
//
// note: based on code by Joe Lowe (public domain).
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
#define AUTORUN AUTORUN_( unique(fn) )
#ifdef __cplusplus
#define AUTORUN_(fn) \
static void fn(void); \
static const int concat(fn,__1) = (fn(), 1); \
static void fn(void)
#elif defined _MSC_VER && !defined(__clang__) // cl, but not clang-cl
#define AUTORUN_(fn) \
static void fn(void); \
static int concat(fn,__1) (){ fn(); return 0; } \
__pragma(section(".CRT$XIU", long, read)) \
__declspec(allocate(".CRT$XIU")) \
static int(* concat(fn,__2) )() = concat(fn,__1); \
static void fn(void)
#elif defined __TINYC__ // tcc...
#define AUTORUN_(fn) \
__attribute((constructor)) \
static void fn(void)
#else // gcc,clang,clang-cl...
#define AUTORUN_(fn) \
__attribute__((constructor(__COUNTER__+101))) \
static void fn(void)
#endif
// -----------------------------------------------------------------------------
// build info
#ifndef BUILD_VERSION
#define BUILD_VERSION ""
#endif
// -----------------------------------------------------------------------------
// visibility
// win32 users would need to -DAPI=EXPORT/IMPORT as needed when building/using V4K as DLL.
#define IMPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport)))
#define EXPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport)))
#define STATIC
#ifndef API
#define API STATIC
#endif
// -----------------------------------------------------------------------------
// system headers
#ifndef _GNU_SOURCE
#define _GNU_SOURCE ///- for linux
#endif
#if is(cl) && is(win32) // for VC IDE
#define _CRT_SECURE_NO_WARNINGS ///-
#define _CRT_NONSTDC_NO_DEPRECATE ///-
#define _WINSOCK_DEPRECATED_NO_WARNINGS ///-
#define _WIN32_WINNT 0x0600 ///- 0x0502 // GetInfoAddrW/FreeAddrInfoW for X86
#endif
#if is(cl)
#include <omp.h> // compile with /openmp to speed up some computations
#endif
#include <assert.h>
//#include <float.h>
//#include <limits.h>
#include <math.h> // NAN
#include <stdarg.h> // va_*(), ...
#include <stdbool.h> // bool,true,false
#include <stdint.h> // u/int8/16/32/64_t
#include <stdio.h> // FILE,NULL
#include <stdlib.h> // malloc,free,exit,
#include <string.h> // strlen,memset,memcpy,
#if is(tcc) && is(win32) && defined(__x86_64)
#include <tgmath.h>
// @fixme workarounds on `tcc0.9.27 -m64` (win) for fmod()/trunc() functions. test: 00-easing broken otherwise
//#define trunc(x) ((double)(int64_t)(x))
//#define fmod(x,y) ((x) - trunc((x) / (y)) * (y))
// @fixme workarounds on `tcc0.9.27 -m64` (win) for all functions with ending bool argument. test: 00-anims crashes otherwise
#undef bool
typedef char bool; ///-
// missing libm symbols on tinycc HEAD repo (tcc-x64 pre-0.9.28)
//#define fabsf fabs
#define sqrtf sqrt
#define sinf sin
#define asinf asin
#define cosf cos
#define acosf acos
#define tanf tan
#define atan2f atan2
#define powf pow
#define floorf floor
#define logf log
#define ceilf ceil
#define copysignf copysign
//#define ldexpf ldexp
#define expf exp
//#define frexpf frexp
#define fmodf fmod
#define log10f log10
//#define logf log
#define hypotf hypot
#endif
// -----------------------------------------------------------------------------
// pragma libs
#if is(win32) && (is(cl) || is(tcc))
#pragma comment(lib, "advapi32")
#pragma comment(lib, "comdlg32")
#pragma comment(lib, "dbghelp")
#pragma comment(lib, "gdi32")
#pragma comment(lib, "ole32")
#pragma comment(lib, "shell32")
#pragma comment(lib, "user32")
#pragma comment(lib, "winmm")
#pragma comment(lib, "wininet")
#pragma comment(lib, "ws2_32")
#endif
#if is(linux) && is(tcc)
#pragma comment(lib, "dl")
#pragma comment(lib, "m")
#pragma comment(lib, "pthread")
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +1,33 @@
// -----------------------------------------------------------------------------
// asset pipeline framework
// - rlyeh, public domain.
//
// all cooked assets are stored inside zip file at root folder, which acts as an asset database.
// during game boot, the database gets rebuilt as follows: (note: step 0 is an optional optimization)
// 0. for N given cores, split list of infiles into N zipfiles. then, parallelize cooks.
// 1. compare local disk files against file in zip database. for each mismatch do:
// 2. - invalidate its entry in database, if local file was removed from disk.
// 3. - write its *cooked* contents into database, if local file was created or modified from disk.
// 4. mount any existing zipfile(s) after cooking.
//
// notes: meta-datas from every raw asset are stored into comment field, inside .cook.zip archive.
// @todo: fix leaks
// @todo: symlink exact files
// @todo: idle threads should steal jobs from busy threads (maybe use jobs/coroutines for this?) ...
enum COOK_FLAGS {
COOK_SYNC = 0,
COOK_ASYNC = 1,
COOK_CANCELABLE = 2,
COOK_DEBUGLOG = 4, // log all cooking commands to a batch file
};
API void cook_config( const char *path_to_cook_ini ); // "tools/cook.ini"
API bool cook_start( const char *path_to_cook_ini, const char *masks, int flags ); // COOK_INI, "**"
API void cook_stop();
API void cook_cancel();
API int cook_jobs(); // [0..N]
API int cook_progress(); // [0..100]
// utils
API bool have_tools();
// -----------------------------------------------------------------------------
// asset pipeline framework
// - rlyeh, public domain.
//
// all cooked assets are stored inside zip file at root folder, which acts as an asset database.
// during game boot, the database gets rebuilt as follows: (note: step 0 is an optional optimization)
// 0. for N given cores, split list of infiles into N zipfiles. then, parallelize cooks.
// 1. compare local disk files against file in zip database. for each mismatch do:
// 2. - invalidate its entry in database, if local file was removed from disk.
// 3. - write its *cooked* contents into database, if local file was created or modified from disk.
// 4. mount any existing zipfile(s) after cooking.
//
// notes: meta-datas from every raw asset are stored into comment field, inside .cook.zip archive.
// @todo: fix leaks
// @todo: symlink exact files
// @todo: idle threads should steal jobs from busy threads (maybe use jobs/coroutines for this?) ...
enum COOK_FLAGS {
COOK_SYNC = 0,
COOK_ASYNC = 1,
COOK_CANCELABLE = 2,
COOK_DEBUGLOG = 4, // log all cooking commands to a batch file
};
API void cook_config( const char *path_to_cook_ini ); // "tools/cook.ini"
API bool cook_start( const char *path_to_cook_ini, const char *masks, int flags ); // COOK_INI, "**"
API void cook_stop();
API void cook_cancel();
API int cook_jobs(); // [0..N]
API int cook_progress(); // [0..100]
// utils
API bool have_tools();

View File

@ -1,226 +1,226 @@
static array(json5) roots;
static array(char*) sources;
bool json_push(const char *source) {
char *source_rw = STRDUP(source);
json5 root = {0};
char *error = json5_parse(&root, source_rw, 0);
if( error ) {
FREE(source_rw);
return false;
} else {
array_push(sources, source_rw);
array_push(roots, root);
return true;
}
}
bool json_pop() {
if( array_count(roots) > 0 ) {
FREE(*array_back(sources));
array_pop(sources);
json5_free(array_back(roots));
array_pop(roots);
return true;
}
return false;
}
json5* json_node(const char *keypath) {
json5 *j = array_back(roots), *r = j;
for each_substring( keypath, "/[.]", key ) {
r = 0;
/**/ if( j->type == JSON5_ARRAY ) r = j = &j->array[atoi(key)];
else if( j->type == JSON5_OBJECT && isdigit(key[0]) )
for( int i = 0, seq = atoi(key); !r && i < j->count; ++i ) {
if( i == seq ) {
r = j = &j->nodes[i];
break;
}
}
else if( j->type == JSON5_OBJECT )
for( int i = 0; !r && i < j->count; ++i ) {
if( j->nodes[i].name && !strcmp(j->nodes[i].name, key) ) {
r = j = &j->nodes[i];
break;
}
}
if( !j ) break;
}
return r;
}
int (json_count)(const char *keypath) {
json5* j = json_node(keypath);
return j ? j->count : 0;
}
json_t *json_find(const char *type_keypath) {
char type = type_keypath[0];
const char *key = type_keypath+1;
json5 *j = json_node(key);
if( !j ) return NULL;
static __thread int slot = 0;
static __thread json_t buf[128] = {0};
slot = (slot+1) % 128;
json_t *v = &buf[slot];
v->i = j ? j->integer : 0;
if(type == 's' && (!v->p || j->type == JSON5_NULL)) v->s = ""; // if_null_string
if(type == 'f' && j && j->type == JSON5_INTEGER) v->f = j->integer;
return v;
}
json_t json_get(const char *type_keypath) {
char type = type_keypath[0];
const char *key = type_keypath+1;
json5 *j = json_node(key);
json_t v = {0};
v.i = j ? j->integer : 0;
if(type == 's' && (!v.p || j->type == JSON5_NULL)) v.s = ""; // if_null_string
if(type == 'f' && j && j->type == JSON5_INTEGER) v.f = j->integer;
return v;
}
const char *(json_key)(const char *keypath) {
json5 *j = json_node(keypath);
if( !j ) return "";
return j->name;
}
// xml impl
static __thread array(char *) xml_sources;
static __thread array(struct xml *) xml_docs;
int xml_push(const char *xml_source) {
if( xml_source ) {
char *src = STRDUP(xml_source), *error = 0;
for( struct xml *doc = xml_parse(src, 0, &error); doc && !error; ) {
array_push(xml_docs, doc);
array_push(xml_sources, src);
return 1;
}
if( error ) PRINTF("%s\n", error);
FREE(src);
}
return 0;
}
void xml_pop() {
if( array_count(xml_docs) ) {
xml_free( *array_back(xml_docs) );
array_pop(xml_docs);
FREE( *array_back(xml_sources) );
array_pop(xml_sources);
}
}
static void *xml_path(struct xml *node, char *path, int down) {
if( !path || !path[0] ) return node;
if( node ) {
char type = path[0];
if( type == '/' ) {
int sep = strcspn(++path, "/[@$");
if( !sep ) type = path[0];
else
if( 1 ) { // path[ sep ] ) {
char tag[32]; snprintf(tag, 32, "%.*s", sep, path);
// Find the first sibling with the given tag name (may be the same node)
struct xml *next = down ? xml_find_down(node, tag) : xml_find(node, tag);
return xml_path(next, &path[ sep ], 1);
}
}
if( type == '$' ) {
return (void*)( node->down ? xml_text( node->down ) : xml_tag( node ) );
}
if( type == '@' ) {
return (void*)xml_att(node, ++path);
}
if( type == '[' ) {
for( int i = 0, end = atoi(++path); i < end; ++i ) { node = xml_find_next(node, xml_tag(node)); if(!node) return NULL; }
while( isdigit(path[0]) ) ++path;
return xml_path(node, ++path, 1);
}
}
return NULL;
}
const char *(xml_string)(char *key) {
struct xml *node = xml_path(*array_back(xml_docs), key, 0);
if( node && strchr(key, '@') ) return (const char *)node;
if( node && strchr(key, '$') ) return (const char *)node;
return "";
}
unsigned (xml_count)(char *key) {
struct xml *node = xml_path(*array_back(xml_docs), key, 0);
if( !node ) return 0;
const char *tag = xml_tag(node);
unsigned count = 1;
while( (node = xml_find_next(node, tag)) != 0) ++count;
return count;
}
array(char) (xml_blob)(char *key) { // base64 blob
struct xml *node = xml_path(*array_back(xml_docs), key, 0);
if( !node ) return 0;
if( !strchr(key, '$') ) return 0;
const char *data = (const char*)node;
array(char) out = base64_decode(data, strlen(data)); // either array of chars (ok) or null (error)
return out;
}
bool data_tests() {
// data tests (json5)
const char json5[] =
" /* json5 */ // comment\n"
" abc: 42.67, def: true, integer:0x100 \n"
" huge: 2.2239333e5, \n"
" hello: 'world /*comment in string*/ //again', \n"
" children : { a: 1, b: 2, c: 3 },\n"
" array: [+1,2,-3,4,5], \n"
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],";
if( json_push(json5) ) {
assert( json_float("/abc") == 42.67 );
assert( json_int("/def") == 1 );
assert( json_int("/integer") == 0x100 );
assert( json_float("/huge") > 2.22e5 );
assert( strlen(json_string("/hello")) == 35 );
assert( json_int("/children/a") == 1 );
assert( json_int("/children.b") == 2 );
assert( json_int("/children[c]") == 3 );
assert( json_int("/array[%d]", 2) == -3 );
assert( json_count("/invalids") == 8 );
assert( isnan(json_float("/invalids[0]")) );
assert( !json_find("/non_existing") );
assert( PRINTF("json5 tests OK\n") );
json_pop();
}
// data tests (xml)
const char *xml = // vfs_read("test1.xml");
"<!-- XML representation of a person record -->"
"<person created=\"2006-11-11T19:23\" modified=\"2006-12-31T23:59\">"
" <firstName>Robert</firstName>"
" <lastName>Smith</lastName>"
" <address type=\"home\">"
" <street>12345 Sixth Ave</street>"
" <city>Anytown</city>"
" <state>CA</state>"
" <postalCode>98765-4321</postalCode>"
" </address>"
"</person>";
if( xml_push(xml) ) {
assert( !strcmp("Robert", xml_string("/person/firstName/$")) );
assert( !strcmp("Smith", xml_string("/person/lastName/$")) );
assert( !strcmp("home", xml_string("/person/address/@type")) );
assert( PRINTF("xml tests OK\n") );
xml_pop();
}
return true;
}
static array(json5) roots;
static array(char*) sources;
bool json_push(const char *source) {
char *source_rw = STRDUP(source);
json5 root = {0};
char *error = json5_parse(&root, source_rw, 0);
if( error ) {
FREE(source_rw);
return false;
} else {
array_push(sources, source_rw);
array_push(roots, root);
return true;
}
}
bool json_pop() {
if( array_count(roots) > 0 ) {
FREE(*array_back(sources));
array_pop(sources);
json5_free(array_back(roots));
array_pop(roots);
return true;
}
return false;
}
json5* json_node(const char *keypath) {
json5 *j = array_back(roots), *r = j;
for each_substring( keypath, "/[.]", key ) {
r = 0;
/**/ if( j->type == JSON5_ARRAY ) r = j = &j->array[atoi(key)];
else if( j->type == JSON5_OBJECT && isdigit(key[0]) )
for( int i = 0, seq = atoi(key); !r && i < j->count; ++i ) {
if( i == seq ) {
r = j = &j->nodes[i];
break;
}
}
else if( j->type == JSON5_OBJECT )
for( int i = 0; !r && i < j->count; ++i ) {
if( j->nodes[i].name && !strcmp(j->nodes[i].name, key) ) {
r = j = &j->nodes[i];
break;
}
}
if( !j ) break;
}
return r;
}
int (json_count)(const char *keypath) {
json5* j = json_node(keypath);
return j ? j->count : 0;
}
json_t *json_find(const char *type_keypath) {
char type = type_keypath[0];
const char *key = type_keypath+1;
json5 *j = json_node(key);
if( !j ) return NULL;
static __thread int slot = 0;
static __thread json_t buf[128] = {0};
slot = (slot+1) % 128;
json_t *v = &buf[slot];
v->i = j ? j->integer : 0;
if(type == 's' && (!v->p || j->type == JSON5_NULL)) v->s = ""; // if_null_string
if(type == 'f' && j && j->type == JSON5_INTEGER) v->f = j->integer;
return v;
}
json_t json_get(const char *type_keypath) {
char type = type_keypath[0];
const char *key = type_keypath+1;
json5 *j = json_node(key);
json_t v = {0};
v.i = j ? j->integer : 0;
if(type == 's' && (!v.p || j->type == JSON5_NULL)) v.s = ""; // if_null_string
if(type == 'f' && j && j->type == JSON5_INTEGER) v.f = j->integer;
return v;
}
const char *(json_key)(const char *keypath) {
json5 *j = json_node(keypath);
if( !j ) return "";
return j->name;
}
// xml impl
static __thread array(char *) xml_sources;
static __thread array(struct xml *) xml_docs;
int xml_push(const char *xml_source) {
if( xml_source ) {
char *src = STRDUP(xml_source), *error = 0;
for( struct xml *doc = xml_parse(src, 0, &error); doc && !error; ) {
array_push(xml_docs, doc);
array_push(xml_sources, src);
return 1;
}
if( error ) PRINTF("%s\n", error);
FREE(src);
}
return 0;
}
void xml_pop() {
if( array_count(xml_docs) ) {
xml_free( *array_back(xml_docs) );
array_pop(xml_docs);
FREE( *array_back(xml_sources) );
array_pop(xml_sources);
}
}
static void *xml_path(struct xml *node, char *path, int down) {
if( !path || !path[0] ) return node;
if( node ) {
char type = path[0];
if( type == '/' ) {
int sep = strcspn(++path, "/[@$");
if( !sep ) type = path[0];
else
if( 1 ) { // path[ sep ] ) {
char tag[32]; snprintf(tag, 32, "%.*s", sep, path);
// Find the first sibling with the given tag name (may be the same node)
struct xml *next = down ? xml_find_down(node, tag) : xml_find(node, tag);
return xml_path(next, &path[ sep ], 1);
}
}
if( type == '$' ) {
return (void*)( node->down ? xml_text( node->down ) : xml_tag( node ) );
}
if( type == '@' ) {
return (void*)xml_att(node, ++path);
}
if( type == '[' ) {
for( int i = 0, end = atoi(++path); i < end; ++i ) { node = xml_find_next(node, xml_tag(node)); if(!node) return NULL; }
while( isdigit(path[0]) ) ++path;
return xml_path(node, ++path, 1);
}
}
return NULL;
}
const char *(xml_string)(char *key) {
struct xml *node = xml_path(*array_back(xml_docs), key, 0);
if( node && strchr(key, '@') ) return (const char *)node;
if( node && strchr(key, '$') ) return (const char *)node;
return "";
}
unsigned (xml_count)(char *key) {
struct xml *node = xml_path(*array_back(xml_docs), key, 0);
if( !node ) return 0;
const char *tag = xml_tag(node);
unsigned count = 1;
while( (node = xml_find_next(node, tag)) != 0) ++count;
return count;
}
array(char) (xml_blob)(char *key) { // base64 blob
struct xml *node = xml_path(*array_back(xml_docs), key, 0);
if( !node ) return 0;
if( !strchr(key, '$') ) return 0;
const char *data = (const char*)node;
array(char) out = base64_decode(data, strlen(data)); // either array of chars (ok) or null (error)
return out;
}
bool data_tests() {
// data tests (json5)
const char json5[] =
" /* json5 */ // comment\n"
" abc: 42.67, def: true, integer:0x100 \n"
" huge: 2.2239333e5, \n"
" hello: 'world /*comment in string*/ //again', \n"
" children : { a: 1, b: 2, c: 3 },\n"
" array: [+1,2,-3,4,5], \n"
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],";
if( json_push(json5) ) {
assert( json_float("/abc") == 42.67 );
assert( json_int("/def") == 1 );
assert( json_int("/integer") == 0x100 );
assert( json_float("/huge") > 2.22e5 );
assert( strlen(json_string("/hello")) == 35 );
assert( json_int("/children/a") == 1 );
assert( json_int("/children.b") == 2 );
assert( json_int("/children[c]") == 3 );
assert( json_int("/array[%d]", 2) == -3 );
assert( json_count("/invalids") == 8 );
assert( isnan(json_float("/invalids[0]")) );
assert( !json_find("/non_existing") );
assert( PRINTF("json5 tests OK\n") );
json_pop();
}
// data tests (xml)
const char *xml = // vfs_read("test1.xml");
"<!-- XML representation of a person record -->"
"<person created=\"2006-11-11T19:23\" modified=\"2006-12-31T23:59\">"
" <firstName>Robert</firstName>"
" <lastName>Smith</lastName>"
" <address type=\"home\">"
" <street>12345 Sixth Ave</street>"
" <city>Anytown</city>"
" <state>CA</state>"
" <postalCode>98765-4321</postalCode>"
" </address>"
"</person>";
if( xml_push(xml) ) {
assert( !strcmp("Robert", xml_string("/person/firstName/$")) );
assert( !strcmp("Smith", xml_string("/person/lastName/$")) );
assert( !strcmp("home", xml_string("/person/address/@type")) );
assert( PRINTF("xml tests OK\n") );
xml_pop();
}
return true;
}

View File

@ -1,36 +1,36 @@
// -----------------------------------------------------------------------------
// data framework (json5, xml, compression) @todo:kvdb
// - rlyeh, public domain
//
// @todo: vec2,vec3,vec4
typedef union json_t { char* s; double f; int64_t i; uintptr_t p; array(union json_t) arr; } json_t;
// json api
API bool json_push(const char *json_content);
API const char* json_key(const char *keypath);
API json_t* json_find(const char *type_keypath);
API json_t json_get(const char *type_keypath);
API int json_count(const char *keypath);
#define json_int(...) (json_get(va("i" __VA_ARGS__)).i)
#define json_float(...) (json_get(va("f" __VA_ARGS__)).f)
#define json_string(...) (json_get(va("s" __VA_ARGS__)).s)
#define json_key(...) json_key(va(__VA_ARGS__))
#define json_count(...) json_count(va(__VA_ARGS__))
API bool json_pop();
// xml api
API int xml_push(const char *xml_content);
API const char * xml_string(char *key);
API unsigned xml_count(char *key);
API array(char) xml_blob(char *key);
#define xml_string(...) xml_string(va(__VA_ARGS__)) // syntax sugar: string
#define xml_int(...) atoi(xml_string(__VA_ARGS__)) // syntax sugar: int
#define xml_float(...) atof(xml_string(__VA_ARGS__)) // syntax sugar: float
#define xml_blob(...) xml_blob(va(__VA_ARGS__)) // syntax sugar: base64 blob
#define xml_count(...) xml_count(va(__VA_ARGS__)) // syntax sugar: count nodes
API void xml_pop();
API bool data_tests();
// -----------------------------------------------------------------------------
// data framework (json5, xml, compression) @todo:kvdb
// - rlyeh, public domain
//
// @todo: vec2,vec3,vec4
typedef union json_t { char* s; double f; int64_t i; uintptr_t p; array(union json_t) arr; } json_t;
// json api
API bool json_push(const char *json_content);
API const char* json_key(const char *keypath);
API json_t* json_find(const char *type_keypath);
API json_t json_get(const char *type_keypath);
API int json_count(const char *keypath);
#define json_int(...) (json_get(va("i" __VA_ARGS__)).i)
#define json_float(...) (json_get(va("f" __VA_ARGS__)).f)
#define json_string(...) (json_get(va("s" __VA_ARGS__)).s)
#define json_key(...) json_key(va(__VA_ARGS__))
#define json_count(...) json_count(va(__VA_ARGS__))
API bool json_pop();
// xml api
API int xml_push(const char *xml_content);
API const char * xml_string(char *key);
API unsigned xml_count(char *key);
API array(char) xml_blob(char *key);
#define xml_string(...) xml_string(va(__VA_ARGS__)) // syntax sugar: string
#define xml_int(...) atoi(xml_string(__VA_ARGS__)) // syntax sugar: int
#define xml_float(...) atof(xml_string(__VA_ARGS__)) // syntax sugar: float
#define xml_blob(...) xml_blob(va(__VA_ARGS__)) // syntax sugar: base64 blob
#define xml_count(...) xml_count(va(__VA_ARGS__)) // syntax sugar: count nodes
API void xml_pop();
API bool data_tests();

View File

@ -1,387 +1,387 @@
// -----------------------------------------------------------------------------
// sort/less
int less_64_ptr(const void *a, const void *b) {
return 0[(uint64_t*)a] - 0[(uint64_t*)b];
}
int less_int_ptr(const void *a, const void *b) {
return 0[(int*)a] - 0[(int*)b];
}
int less_int(int a, int b) {
return a - b;
}
int less_64(uint64_t a, uint64_t b) {
return a > b ? +1 : -!!(a - b);
}
int less_ptr(void *a, void *b) {
return (uintptr_t)a > (uintptr_t)b ? +1 : -!!((uintptr_t)a - (uintptr_t)b);
}
int less_str(char *a, char *b) {
return strcmp((const char *)a, (const char *)b);
}
// -----------------------------------------------------------------------------
// un/hash
uint32_t unhash_32(uint32_t x) {
// Thomas Mueller at https://stackoverflow.com/questions/664014/ - says no collisions for 32bits!
x = ((x >> 16) ^ x) * 0x119de1f3;
x = ((x >> 16) ^ x) * 0x119de1f3;
x = (x >> 16) ^ x;
return x;
}
uint32_t hash_32(uint32_t x) {
// Thomas Mueller at https://stackoverflow.com/questions/664014/ - says no collisions for 32bits!
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
uint64_t hash_64(uint64_t x) {
#if 1
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
#else
// should we just use x2 hash_32?
uint32_t hi = (x >> 32ull), lo = (x & ~0u);
return (hash_32(hi) << 32ull) | hash_32(lo);
#endif
}
uint64_t hash_flt(double x) {
union { double d; uint64_t i; } c;
return c.d = x, hash_64(c.i);
}
uint64_t hash_str(const char* str) {
uint64_t hash = 14695981039346656037ULL; // hash(0),mul(131) faster than fnv1a, a few more collisions though
while( *str ) hash = ( (unsigned char)*str++ ^ hash ) * 0x100000001b3ULL;
return hash;
}
uint64_t hash_bin(const void* ptr, unsigned len) {
uint64_t hash = 14695981039346656037ULL; // hash(0),mul(131) faster than fnv1a, a few more collisions though
for( unsigned char *str = (unsigned char *)ptr; len--; )
hash = ( (unsigned char)*str++ ^ hash ) * 0x100000001b3ULL;
return hash;
}
uint64_t hash_int(int key) {
return hash_32((uint32_t)key);
}
uint64_t hash_ptr(const void *ptr) {
uint64_t key = (uint64_t)(uintptr_t)ptr;
return hash_64(key); // >> 3? needed?
}
// -----------------------------------------------------------------------------
// utils
uint64_t popcnt64(uint64_t x) {
// [src] https://en.wikipedia.org/wiki/Hamming_weight
x -= (x >> 1) & 0x5555555555555555ULL; //put count of each 2 bits into those 2 bits
x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; //put count of each 8 bits into those 8 bits
return (x * 0x0101010101010101ULL) >> 56; //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
}
// -----------------------------------------------------------------------------
// vector based allocator (x1.75 enlarge factor)
void* vrealloc( void* p, size_t sz ) {
if( !sz ) {
if( p ) {
size_t *ret = (size_t*)p - 2;
ret[0] = 0;
ret[1] = 0;
REALLOC( ret, 0 );
}
return 0;
} else {
size_t *ret;
if( !p ) {
ret = (size_t*)REALLOC( 0, sizeof(size_t) * 2 + sz );
ret[0] = sz;
ret[1] = 0;
} else {
ret = (size_t*)p - 2;
size_t osz = ret[0];
size_t ocp = ret[1];
if( sz <= (osz + ocp) ) {
ret[0] = sz;
ret[1] = ocp - (sz - osz);
} else {
ret = (size_t*)REALLOC( ret, sizeof(size_t) * 2 + sz * 1.75 );
ret[0] = sz;
ret[1] = (size_t)(sz * 1.75) - sz;
}
}
return &ret[2];
}
}
size_t vlen( void* p ) {
return p ? 0[ (size_t*)p - 2 ] : 0;
}
// -----------------------------------------------------------------------------
enum { MAP_GC_SLOT = MAP_HASHSIZE };
typedef int map_is_pow2_assert[ !(MAP_HASHSIZE & (MAP_HASHSIZE - 1)) ? 1:-1];
static int map_get_index(uint64_t hkey1) {
return hkey1 & (MAP_HASHSIZE-1);
}
void (map_init)(map* m) {
map c = {0};
*m = c;
array_resize(m->array, (MAP_HASHSIZE+1));
memset(m->array, 0, (MAP_HASHSIZE+1) * sizeof(m->array[0]) ); // array_resize() just did memset()
}
void (map_insert)(map* m, pair *p, void *key, void *value, uint64_t keyhash, void *super) {
p->keyhash = keyhash;
p->key = key;
p->value = value;
p->super = super;
/* Insert onto the beginning of the list */
int index = map_get_index(p->keyhash);
p->next = m->array[index];
m->array[index] = p;
m->is_sorted = 0;
++m->count;
}
void* (map_find)(map* m, void *key, uint64_t keyhash) {
int index = map_get_index(keyhash);
for( pair *cur = m->array[index]; cur; cur = cur->next ) {
if( cur->keyhash == keyhash ) {
char **c = (char **)cur->key;
char **k = (char **)key;
if( !m->cmp(c[0], k[0]) ) {
return cur->super;
}
}
}
return 0;
}
void (map_erase)(map* m, void *key, uint64_t keyhash) {
int index = map_get_index(keyhash);
for( pair *prev = 0, *cur = m->array[index]; cur; (prev = cur), (cur = cur->next) ) {
if( cur->keyhash == keyhash ) {
char **c = (char **)cur->key;
char **k = (char **)key;
if( !m->cmp(c[0], k[0]) ) {
if( prev ) prev->next = cur->next; else m->array[index] = cur->next ? cur->next : 0;
#if MAP_DONT_ERASE
/* Insert onto the beginning of the GC list */
cur->next = m->array[MAP_GC_SLOT];
m->array[MAP_GC_SLOT] = cur;
#else
REALLOC(cur,0);
#endif
--m->count;
m->is_sorted = 0;
return;
}
}
}
}
int (map_count)(map* m) { // clean deferred GC_SLOT only
return m->count;
int counter = 0;
for( int i = 0; i < MAP_HASHSIZE; ++i) {
for( pair *cur = m->array[i]; cur; cur = cur->next ) {
++counter;
}
}
return counter;
}
int (map_isempty)(map* m) { // clean deferred GC_SLOT only
return !m->count;
}
void (map_gc)(map* m) { // clean deferred GC_SLOT only
#if MAP_DONT_ERASE
for( pair *next, *cur = m->array[MAP_GC_SLOT]; cur; cur = next ) {
next = cur->next;
REALLOC(cur,0);
}
m->array[MAP_GC_SLOT] = 0;
#endif
}
void (map_clear)(map* m) {
for( int i = 0; i <= MAP_HASHSIZE; ++i) {
for( pair *next, *cur = m->array[i]; cur; cur = next ) {
next = cur->next;
REALLOC(cur,0);
}
m->array[i] = 0;
}
m->count = 0;
m->is_sorted = 0;
}
bool (map_sort)(map* m) {
if( m->is_sorted ) return false;
array_clear(m->sorted);
// array_reserve(m->sorted, m->count);
for( int i = 0; i < array_count(m->array); ++i) {
for( pair *cur = m->array[i]; cur; cur = cur->next ) {
array_push(m->sorted, cur);
}
}
#if 0
array_sort(m->sorted, m->cmp);
#else
// @fixme: do better than bubble sort below
for( int i = 0; i < array_count(m->sorted) - 1; ++i)
for( int j = i+1; j < array_count(m->sorted); ++j) {
pair *curi = m->sorted[i];
pair *curj = m->sorted[j];
char **c = (char **)curi->key;
char **k = (char **)curj->key;
if( m->cmp(c[0], k[0]) > 0 ) {
pair *swap = m->sorted[i];
m->sorted[i] = m->sorted[j];
m->sorted[j] = swap;
}
}
#endif
return m->is_sorted = true;
}
void (map_free)(map* m) {
(map_clear)(m);
array_free(m->array);
m->array = 0;
map c = {0};
*m = c;
}
// -----------------------------------------------------------------------------
enum { set_GC_SLOT = SET_HASHSIZE };
typedef int set_is_pow2_assert[ !(SET_HASHSIZE & (SET_HASHSIZE - 1)) ? 1:-1];
static int set_get_index(uint64_t hkey1) {
return hkey1 & (SET_HASHSIZE-1);
}
void (set_init)(set* m) {
set zero = {0};
*m = zero;
array_resize(m->array, (SET_HASHSIZE+1));
memset(m->array, 0, (SET_HASHSIZE+1) * sizeof(m->array[0]) ); // array_resize() just did memset()
}
void (set_insert)(set* m, set_item *p, void *key, uint64_t keyhash, void *super) {
p->keyhash = keyhash;
p->key = key;
p->super = super;
/* Insert onto the beginning of the list */
int index = set_get_index(p->keyhash);
p->next = m->array[index];
m->array[index] = p;
++m->count;
}
void* (set_find)(const set* m, void *key, uint64_t keyhash) {
int index = set_get_index(keyhash);
for( const set_item *cur = m->array[index]; cur; cur = cur->next ) {
if( cur->keyhash == keyhash ) {
char **c = (char **)cur->key;
char **k = (char **)key;
if( !m->cmp(c[0], k[0]) ) {
return cur->super;
}
}
}
return 0;
}
void (set_erase)(set* m, void *key, uint64_t keyhash) {
int index = set_get_index(keyhash);
for( set_item *prev = 0, *cur = m->array[index]; cur; (prev = cur), (cur = cur->next) ) {
if( cur->keyhash == keyhash ) {
char **c = (char **)cur->key;
char **k = (char **)key;
if( !m->cmp(c[0], k[0]) ) {
if (prev) prev->next = cur->next; else m->array[index] = cur->next ? cur->next : 0;
#if SET_DONT_ERASE
/* Insert onto the beginning of the GC list */
cur->next = m->array[set_GC_SLOT];
m->array[set_GC_SLOT] = cur;
#else
REALLOC(cur,0);
#endif
--m->count;
return;
}
}
}
}
int (set_count)(const set* m) { // does not include GC_SLOT
return m->count;
int counter = 0;
for( int i = 0; i < SET_HASHSIZE; ++i) {
for( const set_item *cur = m->array[i]; cur; cur = cur->next ) {
++counter;
}
}
return counter;
}
int (set_isempty)(const set *m) { // clean deferred GC_SLOT only
return !m->count;
}
void (set_gc)(set* m) { // clean deferred GC_SLOT only
#if SET_DONT_ERASE
for( set_item *next, *cur = m->array[set_GC_SLOT]; cur; cur = next ) {
next = cur->next;
REALLOC(cur,0);
}
m->array[set_GC_SLOT] = 0;
#endif
}
void (set_clear)(set* m) { // include GC_SLOT
for( int i = 0; i <= SET_HASHSIZE; ++i) {
for( set_item *next, *cur = m->array[i]; cur; cur = next ) {
next = cur->next;
REALLOC(cur,0);
}
m->array[i] = 0;
}
m->count = 0;
}
void (set_free)(set* m) {
(set_clear)(m);
array_free(m->array);
m->array = 0;
set zero = {0};
*m = zero;
}
// -----------------------------------------------------------------------------
// sort/less
int less_64_ptr(const void *a, const void *b) {
return 0[(uint64_t*)a] - 0[(uint64_t*)b];
}
int less_int_ptr(const void *a, const void *b) {
return 0[(int*)a] - 0[(int*)b];
}
int less_int(int a, int b) {
return a - b;
}
int less_64(uint64_t a, uint64_t b) {
return a > b ? +1 : -!!(a - b);
}
int less_ptr(void *a, void *b) {
return (uintptr_t)a > (uintptr_t)b ? +1 : -!!((uintptr_t)a - (uintptr_t)b);
}
int less_str(char *a, char *b) {
return strcmp((const char *)a, (const char *)b);
}
// -----------------------------------------------------------------------------
// un/hash
uint32_t unhash_32(uint32_t x) {
// Thomas Mueller at https://stackoverflow.com/questions/664014/ - says no collisions for 32bits!
x = ((x >> 16) ^ x) * 0x119de1f3;
x = ((x >> 16) ^ x) * 0x119de1f3;
x = (x >> 16) ^ x;
return x;
}
uint32_t hash_32(uint32_t x) {
// Thomas Mueller at https://stackoverflow.com/questions/664014/ - says no collisions for 32bits!
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
uint64_t hash_64(uint64_t x) {
#if 1
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
#else
// should we just use x2 hash_32?
uint32_t hi = (x >> 32ull), lo = (x & ~0u);
return (hash_32(hi) << 32ull) | hash_32(lo);
#endif
}
uint64_t hash_flt(double x) {
union { double d; uint64_t i; } c;
return c.d = x, hash_64(c.i);
}
uint64_t hash_str(const char* str) {
uint64_t hash = 14695981039346656037ULL; // hash(0),mul(131) faster than fnv1a, a few more collisions though
while( *str ) hash = ( (unsigned char)*str++ ^ hash ) * 0x100000001b3ULL;
return hash;
}
uint64_t hash_bin(const void* ptr, unsigned len) {
uint64_t hash = 14695981039346656037ULL; // hash(0),mul(131) faster than fnv1a, a few more collisions though
for( unsigned char *str = (unsigned char *)ptr; len--; )
hash = ( (unsigned char)*str++ ^ hash ) * 0x100000001b3ULL;
return hash;
}
uint64_t hash_int(int key) {
return hash_32((uint32_t)key);
}
uint64_t hash_ptr(const void *ptr) {
uint64_t key = (uint64_t)(uintptr_t)ptr;
return hash_64(key); // >> 3? needed?
}
// -----------------------------------------------------------------------------
// utils
uint64_t popcnt64(uint64_t x) {
// [src] https://en.wikipedia.org/wiki/Hamming_weight
x -= (x >> 1) & 0x5555555555555555ULL; //put count of each 2 bits into those 2 bits
x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; //put count of each 8 bits into those 8 bits
return (x * 0x0101010101010101ULL) >> 56; //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
}
// -----------------------------------------------------------------------------
// vector based allocator (x1.75 enlarge factor)
void* vrealloc( void* p, size_t sz ) {
if( !sz ) {
if( p ) {
size_t *ret = (size_t*)p - 2;
ret[0] = 0;
ret[1] = 0;
REALLOC( ret, 0 );
}
return 0;
} else {
size_t *ret;
if( !p ) {
ret = (size_t*)REALLOC( 0, sizeof(size_t) * 2 + sz );
ret[0] = sz;
ret[1] = 0;
} else {
ret = (size_t*)p - 2;
size_t osz = ret[0];
size_t ocp = ret[1];
if( sz <= (osz + ocp) ) {
ret[0] = sz;
ret[1] = ocp - (sz - osz);
} else {
ret = (size_t*)REALLOC( ret, sizeof(size_t) * 2 + sz * 1.75 );
ret[0] = sz;
ret[1] = (size_t)(sz * 1.75) - sz;
}
}
return &ret[2];
}
}
size_t vlen( void* p ) {
return p ? 0[ (size_t*)p - 2 ] : 0;
}
// -----------------------------------------------------------------------------
enum { MAP_GC_SLOT = MAP_HASHSIZE };
typedef int map_is_pow2_assert[ !(MAP_HASHSIZE & (MAP_HASHSIZE - 1)) ? 1:-1];
static int map_get_index(uint64_t hkey1) {
return hkey1 & (MAP_HASHSIZE-1);
}
void (map_init)(map* m) {
map c = {0};
*m = c;
array_resize(m->array, (MAP_HASHSIZE+1));
memset(m->array, 0, (MAP_HASHSIZE+1) * sizeof(m->array[0]) ); // array_resize() just did memset()
}
void (map_insert)(map* m, pair *p, void *key, void *value, uint64_t keyhash, void *super) {
p->keyhash = keyhash;
p->key = key;
p->value = value;
p->super = super;
/* Insert onto the beginning of the list */
int index = map_get_index(p->keyhash);
p->next = m->array[index];
m->array[index] = p;
m->is_sorted = 0;
++m->count;
}
void* (map_find)(map* m, void *key, uint64_t keyhash) {
int index = map_get_index(keyhash);
for( pair *cur = m->array[index]; cur; cur = cur->next ) {
if( cur->keyhash == keyhash ) {
char **c = (char **)cur->key;
char **k = (char **)key;
if( !m->cmp(c[0], k[0]) ) {
return cur->super;
}
}
}
return 0;
}
void (map_erase)(map* m, void *key, uint64_t keyhash) {
int index = map_get_index(keyhash);
for( pair *prev = 0, *cur = m->array[index]; cur; (prev = cur), (cur = cur->next) ) {
if( cur->keyhash == keyhash ) {
char **c = (char **)cur->key;
char **k = (char **)key;
if( !m->cmp(c[0], k[0]) ) {
if( prev ) prev->next = cur->next; else m->array[index] = cur->next ? cur->next : 0;
#if MAP_DONT_ERASE
/* Insert onto the beginning of the GC list */
cur->next = m->array[MAP_GC_SLOT];
m->array[MAP_GC_SLOT] = cur;
#else
REALLOC(cur,0);
#endif
--m->count;
m->is_sorted = 0;
return;
}
}
}
}
int (map_count)(map* m) { // clean deferred GC_SLOT only
return m->count;
int counter = 0;
for( int i = 0; i < MAP_HASHSIZE; ++i) {
for( pair *cur = m->array[i]; cur; cur = cur->next ) {
++counter;
}
}
return counter;
}
int (map_isempty)(map* m) { // clean deferred GC_SLOT only
return !m->count;
}
void (map_gc)(map* m) { // clean deferred GC_SLOT only
#if MAP_DONT_ERASE
for( pair *next, *cur = m->array[MAP_GC_SLOT]; cur; cur = next ) {
next = cur->next;
REALLOC(cur,0);
}
m->array[MAP_GC_SLOT] = 0;
#endif
}
void (map_clear)(map* m) {
for( int i = 0; i <= MAP_HASHSIZE; ++i) {
for( pair *next, *cur = m->array[i]; cur; cur = next ) {
next = cur->next;
REALLOC(cur,0);
}
m->array[i] = 0;
}
m->count = 0;
m->is_sorted = 0;
}
bool (map_sort)(map* m) {
if( m->is_sorted ) return false;
array_clear(m->sorted);
// array_reserve(m->sorted, m->count);
for( int i = 0; i < array_count(m->array); ++i) {
for( pair *cur = m->array[i]; cur; cur = cur->next ) {
array_push(m->sorted, cur);
}
}
#if 0
array_sort(m->sorted, m->cmp);
#else
// @fixme: do better than bubble sort below
for( int i = 0; i < array_count(m->sorted) - 1; ++i)
for( int j = i+1; j < array_count(m->sorted); ++j) {
pair *curi = m->sorted[i];
pair *curj = m->sorted[j];
char **c = (char **)curi->key;
char **k = (char **)curj->key;
if( m->cmp(c[0], k[0]) > 0 ) {
pair *swap = m->sorted[i];
m->sorted[i] = m->sorted[j];
m->sorted[j] = swap;
}
}
#endif
return m->is_sorted = true;
}
void (map_free)(map* m) {
(map_clear)(m);
array_free(m->array);
m->array = 0;
map c = {0};
*m = c;
}
// -----------------------------------------------------------------------------
enum { set_GC_SLOT = SET_HASHSIZE };
typedef int set_is_pow2_assert[ !(SET_HASHSIZE & (SET_HASHSIZE - 1)) ? 1:-1];
static int set_get_index(uint64_t hkey1) {
return hkey1 & (SET_HASHSIZE-1);
}
void (set_init)(set* m) {
set zero = {0};
*m = zero;
array_resize(m->array, (SET_HASHSIZE+1));
memset(m->array, 0, (SET_HASHSIZE+1) * sizeof(m->array[0]) ); // array_resize() just did memset()
}
void (set_insert)(set* m, set_item *p, void *key, uint64_t keyhash, void *super) {
p->keyhash = keyhash;
p->key = key;
p->super = super;
/* Insert onto the beginning of the list */
int index = set_get_index(p->keyhash);
p->next = m->array[index];
m->array[index] = p;
++m->count;
}
void* (set_find)(const set* m, void *key, uint64_t keyhash) {
int index = set_get_index(keyhash);
for( const set_item *cur = m->array[index]; cur; cur = cur->next ) {
if( cur->keyhash == keyhash ) {
char **c = (char **)cur->key;
char **k = (char **)key;
if( !m->cmp(c[0], k[0]) ) {
return cur->super;
}
}
}
return 0;
}
void (set_erase)(set* m, void *key, uint64_t keyhash) {
int index = set_get_index(keyhash);
for( set_item *prev = 0, *cur = m->array[index]; cur; (prev = cur), (cur = cur->next) ) {
if( cur->keyhash == keyhash ) {
char **c = (char **)cur->key;
char **k = (char **)key;
if( !m->cmp(c[0], k[0]) ) {
if (prev) prev->next = cur->next; else m->array[index] = cur->next ? cur->next : 0;
#if SET_DONT_ERASE
/* Insert onto the beginning of the GC list */
cur->next = m->array[set_GC_SLOT];
m->array[set_GC_SLOT] = cur;
#else
REALLOC(cur,0);
#endif
--m->count;
return;
}
}
}
}
int (set_count)(const set* m) { // does not include GC_SLOT
return m->count;
int counter = 0;
for( int i = 0; i < SET_HASHSIZE; ++i) {
for( const set_item *cur = m->array[i]; cur; cur = cur->next ) {
++counter;
}
}
return counter;
}
int (set_isempty)(const set *m) { // clean deferred GC_SLOT only
return !m->count;
}
void (set_gc)(set* m) { // clean deferred GC_SLOT only
#if SET_DONT_ERASE
for( set_item *next, *cur = m->array[set_GC_SLOT]; cur; cur = next ) {
next = cur->next;
REALLOC(cur,0);
}
m->array[set_GC_SLOT] = 0;
#endif
}
void (set_clear)(set* m) { // include GC_SLOT
for( int i = 0; i <= SET_HASHSIZE; ++i) {
for( set_item *next, *cur = m->array[i]; cur; cur = next ) {
next = cur->next;
REALLOC(cur,0);
}
m->array[i] = 0;
}
m->count = 0;
}
void (set_free)(set* m) {
(set_clear)(m);
array_free(m->array);
m->array = 0;
set zero = {0};
*m = zero;
}

View File

@ -1,436 +1,436 @@
// data structures and utils: array, set, map, hash, sort.
// - rlyeh, public domain
// -----------------------------------------------------------------------------
// less
API int less_64(uint64_t a, uint64_t b);
API int less_int(int a, int b);
API int less_ptr(void *a, void *b);
API int less_str(char *a, char *b);
// -----------------------------------------------------------------------------
// qsort
API int less_64_ptr(const void *a, const void *b);
API int less_int_ptr(const void *a, const void *b);
// -----------------------------------------------------------------------------
// un/hash
API uint32_t unhash_32(uint32_t x);
API uint32_t hash_32(uint32_t x);
API uint64_t hash_64(uint64_t x);
API uint64_t hash_flt(double x);
API uint64_t hash_int(int key);
API uint64_t hash_ptr(const void* ptr);
API uint64_t hash_bin(const void* ptr, unsigned len);
API uint64_t hash_str(const char* str);
// -----------------------------------------------------------------------------
// bits
API uint64_t popcnt64(uint64_t x);
// -----------------------------------------------------------------------------
// vector based allocator (x1.75 enlarge factor)
API void* vrealloc( void* p, size_t sz );
API size_t vlen( void* p );
// -----------------------------------------------------------------------------
// arrays
#if is(cpp)
#define array_cast(x) (decltype x)
#else
#define array_cast(x) (void *)
#endif
#define array(t) t*
#define array_init(t) ( (t) = 0 )
#define array_resize(t, n) ( array_c_ = array_count(t), array_n_ = (n), array_realloc_((t),array_n_), (array_n_>array_c_? memset(array_c_+(t),0,(array_n_-array_c_)*sizeof(0[t])) : (void*)0), (t) )
#define array_push(t, ...) ( array_realloc_((t),array_count(t)+1), (t)[ array_count(t) - 1 ] = (__VA_ARGS__) )
#define array_pop(t) ( array_realloc_((t), array_count(t)-1) )
#define array_back(t) ( &(t)[ array_count(t)-1 ] ) // ( (t) ? &(t)[ array_count(t)-1 ] : NULL )
#define array_data(t) (t)
#define array_at(t,i) (t[i])
#define array_count(t) (int)( (t) ? array_vlen_(t) / sizeof(0[t]) : 0u )
#define array_bytes(t) (int)( (t) ? array_vlen_(t) : 0u )
#define array_sort(t, cmpfunc) qsort( t, array_count(t), sizeof(0[t]), (uintptr_t)cmpfunc == (uintptr_t)strcmp ? (void*)strcmp_qsort : (void*)cmpfunc )
#define array_empty(t) ( !array_count(t) )
#define array_push_front(arr,x) \
(array_resize((arr), array_count(arr)+1), memmove((arr)+1, (arr), sizeof(0[arr])*array_count(arr)), 0[arr] = (x))
#define array_pop_front(arr) ( \
(array_count(arr) > 1 ? memmove((arr), (arr)+1, sizeof(0[arr])*(array_count(arr)-1)) : (void*)0), \
(array_count(arr) > 0 ? array_resize(arr, array_count(arr) - 1 ) : array_resize( arr, 0 ) ) )
static __thread unsigned array_c_;
static __thread unsigned array_n_;
#if 0 // original: no reserve support
#define array_reserve(t, n) ((void)0) // not implemented
#define array_clear(t) ( array_realloc_((t), 0), (t) = 0 )
#define array_vlen_(t) ( vlen(t) - 0 )
#define array_realloc_(t,n) ( (t) = array_cast(t) vrealloc((t), ((n)+0) * sizeof(0[t])) )
#define array_free(t) array_clear(t)
#else // new: with reserve support (@todo: check for bugs?)
#define array_reserve(t, n) ( array_realloc_((t),(n)), array_clear(t) )
#define array_clear(t) ( array_realloc_((t),0) ) // -1
#define array_vlen_(t) ( vlen(t) - sizeof(0[t]) ) // -1
#define array_realloc_(t,n) ( (t) = array_cast(t) vrealloc((t), ((n)+1) * sizeof(0[t])) ) // +1
#define array_free(t) ( array_realloc_((t), -1), (t) = 0 ) // -1
#endif
#define array_reverse(t) \
do if( array_count(t) ) { \
for(int l = array_count(t), e = l-1, i = (array_push(t, 0[t]), 0); i <= e/2; ++i ) \
{ l[t] = i[t]; i[t] = (e-i)[t]; (e-i)[t] = l[t]; } \
array_pop(t); \
} while(0)
#define array_foreach(t,val_t,v) for each_array(t,val_t,v)
#define each_array(a,val_t,v) \
( array(val_t) a_ = (a); a_; a_ = 0 ) \
for( int i_ = 0, e_ = array_count(a_); i_ < e_; ++i_ ) \
for( val_t v = i_[a_], *v_ = (void*)(uintptr_t)&v; v_; v_ = 0 )
#define array_foreach_ptr(t,val_t,v) for each_array_ptr(t,val_t,v)
#define each_array_ptr(a,val_t,v) \
( array(val_t) a_ = (a); a_; a_ = 0 ) \
for( int i_ = 0, e_ = array_count(a_); i_ < e_; ++i_ ) \
for( val_t *v = (val_t*)&i_[a_]; v; v = 0 )
#define array_search(t, key, cmpfn) /* requires sorted array beforehand */ \
bsearch(&key, t, array_count(t), sizeof(t[0]), cmpfn )
#define array_insert(t, i, n) do { \
int ac = array_count(t); \
if( i >= ac ) { \
array_push(t, n); \
} else { \
array_push(t, array_back(t)); \
memmove( &(t)[(i)+1], &(t)[i], (ac - (i)) * sizeof(t[0]) ); \
(t)[ i ] = (n); \
} \
} while(0)
#define array_copy(t, src) do { \
array_free(t); \
(t) = array_realloc_( (t), array_count(src)); \
memcpy( (t), src, array_count(src) * sizeof(0[t])); \
} while(0)
#define array_erase_fast(t, i) do { /*alters ordering*/ \
memcpy( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \
array_pop(t); \
} while(0)
#define array_erase_slow(t, i) do { /*preserves ordering*/ \
memmove( &(t)[i], &(t)[i + 1], sizeof(0[t])*(array_count(t) - i - 1)); \
array_pop(t); \
} while(0)
#define array_unique(t, cmpfunc) do { /*@todo: optimize me. requires array_sort() beforehand*/ \
int cnt = array_count(t), cnt_bak = cnt; \
if( cnt > 1 ) { \
for( int i = 1; i < cnt; ++i ) { \
while( i < cnt && !cmpfunc(&(t)[i-1], &(t)[i]) ) { \
memmove(&(t)[i-1], &(t)[i], (cnt - 1 - i) * sizeof((t)[0]) ) ; \
--cnt; \
} \
} \
if( cnt_bak != cnt ) array_resize((t), cnt); \
} \
} while(0)
#if 0 // snippet below does work
#define array_unique(t, cmpfunc) \
array_sort(t, cmpfunc); \
for( int i = 0, end = array_count(t) - 1; i < end; ) { \
if( !strcmp(t[i], t[i+1]) ) { \
/* array_erase(t, i+1); */ \
memmove(&(t)[i+1], &(t)[i+2], (end - 1 - i) * sizeof((t)[0]) ); \
array_pop(t); \
--end; \
} else { \
++i; \
} \
}
#endif
#define array_shuffle(t) do { /* https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle */ \
void* tmp = stack(sizeof(0[t])); \
for( int i = 0, n = array_count(t); i < n; ++i ) { \
int j = randi(i, n); /* j random integer such that [i,n) i<=j<n */ \
memcpy(tmp, &j[t], sizeof(0[t])); \
memcpy(&j[t], &i[t], sizeof(0[t])); \
memcpy(&i[t], tmp, sizeof(0[t])); \
} \
} while(0)
// -----------------------------------------------------------------------------
// set<K>
// ideas from: https://en.wikipedia.org/wiki/Hash_table
// ideas from: https://probablydance.com/2017/02/26/i-wrote-the-fastest-hashtable/
// ideas from: http://www.idryman.org/blog/2017/05/03/writing-a-damn-fast-hash-table-with-tiny-memory-footprints/
// config
#ifndef SET_HASHSIZE
#define SET_HASHSIZE (4096 << 4)
#endif
#ifndef SET_DONT_ERASE
#define SET_DONT_ERASE 1
#endif
// public api
#define set(K) \
struct { set base; struct { set_item p; K key; } tmp, *ptr; K *tmpval; \
int (*typed_cmp)(K, K); uint64_t (*typed_hash)(K); } *
#define set_init(m, cmpfn, hashfn) ( \
(m) = set_cast(m) REALLOC(0, sizeof(*m)), \
set_init(&(m)->base), \
(m)->base.cmp = (int(*)(void*,void*))( (m)->typed_cmp = set_cast(cmpfn) cmpfn ), \
(m)->base.hash = (uint64_t(*)(void*))( (m)->typed_hash = set_cast(hashfn) hashfn ) \
)
#define set_free(m) ( \
set_clear(m), \
set_free(&(m)->base), \
(m) = set_cast(m) REALLOC((m), 0), \
(m) = 0 \
)
#define set_insert(m, k) ( \
(m)->ptr = set_cast((m)->ptr) REALLOC(0, sizeof((m)->tmp)), \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
set_insert(&(m)->base, &(m)->ptr->p, &(m)->ptr->key, (m)->ptr->p.keyhash, (m)->ptr), \
&(m)->ptr->key \
)
#define set_find(m, k) ( \
(m)->ptr = &(m)->tmp, \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
(m)->ptr = set_cast((m)->ptr) set_find(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash), \
(m)->ptr ? &(m)->ptr->key : 0 \
)
#define set_find_or_add(m, k) ( \
(m)->tmp.key = (k), \
(m)->tmpval = set_find((m), ((m)->tmp.key)), \
(m)->tmpval = (m)->tmpval ? (m)->tmpval : set_insert((m), ((m)->tmp.key)) \
)
#define set_find_or_add_allocated_key(m, k) ( \
(m)->tmp.key = (k), \
(m)->tmpval = set_find((m), ((m)->tmp.key)), \
(m)->tmpval = (m)->tmpval ? FREE((m)->tmp.key), (m)->tmpval : set_insert((m), ((m)->tmp.key)) \
)
#define set_erase(m, k) ( \
(m)->ptr = &(m)->tmp, \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
set_erase(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash) \
)
#define set_foreach for each_set
#define each_set(m,key_t,k) \
( int i_ = (m)->base.count ? 0 : SET_HASHSIZE; i_ < SET_HASHSIZE; ++i_) \
for( set_item *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \
for( key_t k = *(key_t *)cur_->key; on_; on_ = 0 )
#define set_foreach_ptr for each_set_ptr
#define each_set_ptr(m,key_t,k) \
( int i_ = (m)->base.count ? 0 : SET_HASHSIZE; i_ < SET_HASHSIZE; ++i_) \
for( set_item *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \
for( key_t *k = (key_t *)cur_->key; on_; on_ = 0 )
#define set_clear(m) ( \
set_clear(&(m)->base) \
)
#define set_isempty(m) set_isempty(&(m)->base)
#define set_count(m) set_count(&(m)->base)
#define set_gc(m) set_gc(&(m)->base)
#ifndef set_init_int
#define set_init_int(m) set_init((m), less_int, hash_64) // hash_int)
#define set_init_str(m) set_init((m), less_str, hash_str)
#define set_init_ptr(m) set_init((m), less_ptr, hash_ptr)
#endif
// private:
#if is(cpp)
#define set_cast(t) (decltype(t))
#else
#define set_cast(t) (void *)
#endif
typedef struct set_item {
struct set_item *next;
uint64_t keyhash;
void *key;
void *super;
} set_item;
typedef struct set {
array(set_item*) array;
int (*cmp)(void *, void *);
uint64_t (*hash)(void *);
int count;
} set;
API void (set_init)(set *m);
API void (set_free)(set *m);
API void (set_insert)(set *m, set_item *p, void *key, uint64_t keyhash, void *super);
API void (set_erase)(set *m, void *key, uint64_t keyhash);
API void* (set_find)(const set *m, void *key, uint64_t keyhash);
API int (set_isempty)(const set *m);
API int (set_count)(const set *m);
API void (set_gc)(set *m); // only if using SET_DONT_ERASE
API void (set_clear)(set* m);
// -----------------------------------------------------------------------------
// map<K,V>
// ideas from: https://en.wikipedia.org/wiki/Hash_table
// ideas from: https://probablydance.com/2017/02/26/i-wrote-the-fastest-hashtable/
// ideas from: http://www.idryman.org/blog/2017/05/03/writing-a-damn-fast-hash-table-with-tiny-memory-footprints/
// config
#ifndef MAP_HASHSIZE
#define MAP_HASHSIZE (4096 << 4)
#endif
#ifndef MAP_DONT_ERASE
#define MAP_DONT_ERASE 1
#endif
// public api
#define map(K,V) \
struct { map base; struct { pair p; K key; V val; } tmp, *ptr; V* tmpval; \
int (*typed_cmp)(K, K); uint64_t (*typed_hash)(K); } *
#define map_init(m, cmpfn, hashfn) ( \
(m) = map_cast(m) REALLOC(0, sizeof(*(m))), \
map_init(&(m)->base), \
(m)->base.cmp = (int(*)(void*,void*))( (m)->typed_cmp = map_cast((m)->typed_cmp) cmpfn), \
(m)->base.hash = (uint64_t(*)(void*))( (m)->typed_hash = map_cast((m)->typed_hash) hashfn ) \
)
#define map_free(m) ( \
map_free(&(m)->base), \
map_cast(m) REALLOC((m), sizeof(*(m))), (m) = 0 \
)
#define map_insert(m, k, v) ( \
(m)->ptr = map_cast((m)->ptr) REALLOC(0, sizeof((m)->tmp)), \
(m)->ptr->val = (v), \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
map_insert(&(m)->base, &(m)->ptr->p, &(m)->ptr->key, &(m)->ptr->val, (m)->ptr->p.keyhash, (m)->ptr), \
&(m)->ptr->val \
)
#define map_find(m, k) ( \
(m)->ptr = &(m)->tmp, \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
(m)->ptr = map_cast((m)->ptr) map_find(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash), \
(m)->ptr ? &(m)->ptr->val : 0 \
)
#define map_find_or_add(m, k, v) ( \
(m)->tmp.key = (k), (m)->tmp.val = (v), \
(m)->tmpval = map_find((m), ((m)->tmp.key)), \
(m)->tmpval = (m)->tmpval ? (m)->tmpval : map_insert((m), ((m)->tmp.key), ((m)->tmp.val)) \
)
#define map_find_or_add_allocated_key(m, k, v) ( \
(m)->tmp.key = (k), (m)->tmp.val = (v), \
(m)->tmpval = map_find((m), ((m)->tmp.key)), \
(m)->tmpval = (m)->tmpval ? FREE((m)->tmp.key), (m)->tmpval : map_insert((m), ((m)->tmp.key), ((m)->tmp.val)) \
)
#define map_erase(m, k) ( \
(m)->ptr = &(m)->tmp, \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
map_erase(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash) \
)
#define map_foreach for each_map
#define each_map(m,key_t,k,val_t,v) \
( int i_ = (m)->base.count ? 0 : MAP_HASHSIZE; i_ < MAP_HASHSIZE; ++i_) \
for( pair *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \
for( key_t k = *(key_t *)cur_->key; on_; ) \
for( val_t v = *(val_t *)cur_->value; on_; on_ = 0 )
#define map_foreach_ptr for each_map_ptr
#define each_map_ptr(m,key_t,k,val_t,v) \
( int i_ = (m)->base.count ? 0 : MAP_HASHSIZE; i_ < MAP_HASHSIZE; ++i_) \
for( pair *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \
for( key_t *k = (key_t *)cur_->key; on_; ) \
for( val_t *v = (val_t *)cur_->value; on_; on_ = 0 )
#define map_foreach_ptr_sorted for each_map_ptr_sorted
#define each_map_ptr_sorted(m,key_t,k,val_t,v) \
( int i_ = (map_sort(&(m)->base), 0); i_ < array_count((m)->base.sorted); ++i_) \
for( pair *cur_ = (m)->base.sorted[i_]; cur_; ) \
for( key_t *k = (key_t *)cur_->key; cur_; ) \
for( val_t *v = (val_t *)cur_->value; cur_; cur_ = 0 )
#define map_clear(m) ( \
map_clear(&(m)->base) \
)
#define map_isempty(m) map_isempty(&(m)->base)
#define map_count(m) map_count(&(m)->base)
#define map_gc(m) map_gc(&(m)->base)
// aliases:
#ifndef map_init_int
#define map_init_int(m) map_init((m), less_int, hash_64) // hash_int
#define map_init_str(m) map_init((m), less_str, hash_str)
#define map_init_ptr(m) map_init((m), less_ptr, hash_ptr)
#endif
// private:
#if is(cpp)
#define map_cast(t) (decltype(t))
#else
#define map_cast(t) (void *)
#endif
typedef struct pair {
struct pair *next;
uint64_t keyhash;
void *key;
void *value;
void *super;
} pair;
typedef struct map {
array(pair*) array;
int (*cmp)(void *, void *);
uint64_t (*hash)(void *);
int count:31;
int is_sorted:1;
array(pair*) sorted;
} map;
API void (map_init)(map *m);
API void (map_free)(map *m);
API void (map_insert)(map *m, pair *p, void *key, void *value, uint64_t keyhash, void *super);
API void (map_erase)(map *m, void *key, uint64_t keyhash);
API void* (map_find)(map *m, void *key, uint64_t keyhash);
API int (map_isempty)(map *m);
API int (map_count)(map *m);
API void (map_gc)(map *m); // only if using MAP_DONT_ERASE
API bool (map_sort)(map* m);
API void (map_clear)(map* m);
// data structures and utils: array, set, map, hash, sort.
// - rlyeh, public domain
// -----------------------------------------------------------------------------
// less
API int less_64(uint64_t a, uint64_t b);
API int less_int(int a, int b);
API int less_ptr(void *a, void *b);
API int less_str(char *a, char *b);
// -----------------------------------------------------------------------------
// qsort
API int less_64_ptr(const void *a, const void *b);
API int less_int_ptr(const void *a, const void *b);
// -----------------------------------------------------------------------------
// un/hash
API uint32_t unhash_32(uint32_t x);
API uint32_t hash_32(uint32_t x);
API uint64_t hash_64(uint64_t x);
API uint64_t hash_flt(double x);
API uint64_t hash_int(int key);
API uint64_t hash_ptr(const void* ptr);
API uint64_t hash_bin(const void* ptr, unsigned len);
API uint64_t hash_str(const char* str);
// -----------------------------------------------------------------------------
// bits
API uint64_t popcnt64(uint64_t x);
// -----------------------------------------------------------------------------
// vector based allocator (x1.75 enlarge factor)
API void* vrealloc( void* p, size_t sz );
API size_t vlen( void* p );
// -----------------------------------------------------------------------------
// arrays
#if is(cpp)
#define array_cast(x) (decltype x)
#else
#define array_cast(x) (void *)
#endif
#define array(t) t*
#define array_init(t) ( (t) = 0 )
#define array_resize(t, n) ( array_c_ = array_count(t), array_n_ = (n), array_realloc_((t),array_n_), (array_n_>array_c_? memset(array_c_+(t),0,(array_n_-array_c_)*sizeof(0[t])) : (void*)0), (t) )
#define array_push(t, ...) ( array_realloc_((t),array_count(t)+1), (t)[ array_count(t) - 1 ] = (__VA_ARGS__) )
#define array_pop(t) ( array_realloc_((t), array_count(t)-1) )
#define array_back(t) ( &(t)[ array_count(t)-1 ] ) // ( (t) ? &(t)[ array_count(t)-1 ] : NULL )
#define array_data(t) (t)
#define array_at(t,i) (t[i])
#define array_count(t) (int)( (t) ? array_vlen_(t) / sizeof(0[t]) : 0u )
#define array_bytes(t) (int)( (t) ? array_vlen_(t) : 0u )
#define array_sort(t, cmpfunc) qsort( t, array_count(t), sizeof(0[t]), (uintptr_t)cmpfunc == (uintptr_t)strcmp ? (void*)strcmp_qsort : (void*)cmpfunc )
#define array_empty(t) ( !array_count(t) )
#define array_push_front(arr,x) \
(array_resize((arr), array_count(arr)+1), memmove((arr)+1, (arr), sizeof(0[arr])*array_count(arr)), 0[arr] = (x))
#define array_pop_front(arr) ( \
(array_count(arr) > 1 ? memmove((arr), (arr)+1, sizeof(0[arr])*(array_count(arr)-1)) : (void*)0), \
(array_count(arr) > 0 ? array_resize(arr, array_count(arr) - 1 ) : array_resize( arr, 0 ) ) )
static __thread unsigned array_c_;
static __thread unsigned array_n_;
#if 0 // original: no reserve support
#define array_reserve(t, n) ((void)0) // not implemented
#define array_clear(t) ( array_realloc_((t), 0), (t) = 0 )
#define array_vlen_(t) ( vlen(t) - 0 )
#define array_realloc_(t,n) ( (t) = array_cast(t) vrealloc((t), ((n)+0) * sizeof(0[t])) )
#define array_free(t) array_clear(t)
#else // new: with reserve support (@todo: check for bugs?)
#define array_reserve(t, n) ( array_realloc_((t),(n)), array_clear(t) )
#define array_clear(t) ( array_realloc_((t),0) ) // -1
#define array_vlen_(t) ( vlen(t) - sizeof(0[t]) ) // -1
#define array_realloc_(t,n) ( (t) = array_cast(t) vrealloc((t), ((n)+1) * sizeof(0[t])) ) // +1
#define array_free(t) ( array_realloc_((t), -1), (t) = 0 ) // -1
#endif
#define array_reverse(t) \
do if( array_count(t) ) { \
for(int l = array_count(t), e = l-1, i = (array_push(t, 0[t]), 0); i <= e/2; ++i ) \
{ l[t] = i[t]; i[t] = (e-i)[t]; (e-i)[t] = l[t]; } \
array_pop(t); \
} while(0)
#define array_foreach(t,val_t,v) for each_array(t,val_t,v)
#define each_array(a,val_t,v) \
( array(val_t) a_ = (a); a_; a_ = 0 ) \
for( int i_ = 0, e_ = array_count(a_); i_ < e_; ++i_ ) \
for( val_t v = i_[a_], *v_ = (void*)(uintptr_t)&v; v_; v_ = 0 )
#define array_foreach_ptr(t,val_t,v) for each_array_ptr(t,val_t,v)
#define each_array_ptr(a,val_t,v) \
( array(val_t) a_ = (a); a_; a_ = 0 ) \
for( int i_ = 0, e_ = array_count(a_); i_ < e_; ++i_ ) \
for( val_t *v = (val_t*)&i_[a_]; v; v = 0 )
#define array_search(t, key, cmpfn) /* requires sorted array beforehand */ \
bsearch(&key, t, array_count(t), sizeof(t[0]), cmpfn )
#define array_insert(t, i, n) do { \
int ac = array_count(t); \
if( i >= ac ) { \
array_push(t, n); \
} else { \
array_push(t, array_back(t)); \
memmove( &(t)[(i)+1], &(t)[i], (ac - (i)) * sizeof(t[0]) ); \
(t)[ i ] = (n); \
} \
} while(0)
#define array_copy(t, src) do { \
array_free(t); \
(t) = array_realloc_( (t), array_count(src)); \
memcpy( (t), src, array_count(src) * sizeof(0[t])); \
} while(0)
#define array_erase_fast(t, i) do { /*alters ordering*/ \
memcpy( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \
array_pop(t); \
} while(0)
#define array_erase_slow(t, i) do { /*preserves ordering*/ \
memmove( &(t)[i], &(t)[i + 1], sizeof(0[t])*(array_count(t) - i - 1)); \
array_pop(t); \
} while(0)
#define array_unique(t, cmpfunc) do { /*@todo: optimize me. requires array_sort() beforehand*/ \
int cnt = array_count(t), cnt_bak = cnt; \
if( cnt > 1 ) { \
for( int i = 1; i < cnt; ++i ) { \
while( i < cnt && !cmpfunc(&(t)[i-1], &(t)[i]) ) { \
memmove(&(t)[i-1], &(t)[i], (cnt - 1 - i) * sizeof((t)[0]) ) ; \
--cnt; \
} \
} \
if( cnt_bak != cnt ) array_resize((t), cnt); \
} \
} while(0)
#if 0 // snippet below does work
#define array_unique(t, cmpfunc) \
array_sort(t, cmpfunc); \
for( int i = 0, end = array_count(t) - 1; i < end; ) { \
if( !strcmp(t[i], t[i+1]) ) { \
/* array_erase(t, i+1); */ \
memmove(&(t)[i+1], &(t)[i+2], (end - 1 - i) * sizeof((t)[0]) ); \
array_pop(t); \
--end; \
} else { \
++i; \
} \
}
#endif
#define array_shuffle(t) do { /* https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle */ \
void* tmp = stack(sizeof(0[t])); \
for( int i = 0, n = array_count(t); i < n; ++i ) { \
int j = randi(i, n); /* j random integer such that [i,n) i<=j<n */ \
memcpy(tmp, &j[t], sizeof(0[t])); \
memcpy(&j[t], &i[t], sizeof(0[t])); \
memcpy(&i[t], tmp, sizeof(0[t])); \
} \
} while(0)
// -----------------------------------------------------------------------------
// set<K>
// ideas from: https://en.wikipedia.org/wiki/Hash_table
// ideas from: https://probablydance.com/2017/02/26/i-wrote-the-fastest-hashtable/
// ideas from: http://www.idryman.org/blog/2017/05/03/writing-a-damn-fast-hash-table-with-tiny-memory-footprints/
// config
#ifndef SET_HASHSIZE
#define SET_HASHSIZE (4096 << 4)
#endif
#ifndef SET_DONT_ERASE
#define SET_DONT_ERASE 1
#endif
// public api
#define set(K) \
struct { set base; struct { set_item p; K key; } tmp, *ptr; K *tmpval; \
int (*typed_cmp)(K, K); uint64_t (*typed_hash)(K); } *
#define set_init(m, cmpfn, hashfn) ( \
(m) = set_cast(m) REALLOC(0, sizeof(*m)), \
set_init(&(m)->base), \
(m)->base.cmp = (int(*)(void*,void*))( (m)->typed_cmp = set_cast(cmpfn) cmpfn ), \
(m)->base.hash = (uint64_t(*)(void*))( (m)->typed_hash = set_cast(hashfn) hashfn ) \
)
#define set_free(m) ( \
set_clear(m), \
set_free(&(m)->base), \
(m) = set_cast(m) REALLOC((m), 0), \
(m) = 0 \
)
#define set_insert(m, k) ( \
(m)->ptr = set_cast((m)->ptr) REALLOC(0, sizeof((m)->tmp)), \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
set_insert(&(m)->base, &(m)->ptr->p, &(m)->ptr->key, (m)->ptr->p.keyhash, (m)->ptr), \
&(m)->ptr->key \
)
#define set_find(m, k) ( \
(m)->ptr = &(m)->tmp, \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
(m)->ptr = set_cast((m)->ptr) set_find(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash), \
(m)->ptr ? &(m)->ptr->key : 0 \
)
#define set_find_or_add(m, k) ( \
(m)->tmp.key = (k), \
(m)->tmpval = set_find((m), ((m)->tmp.key)), \
(m)->tmpval = (m)->tmpval ? (m)->tmpval : set_insert((m), ((m)->tmp.key)) \
)
#define set_find_or_add_allocated_key(m, k) ( \
(m)->tmp.key = (k), \
(m)->tmpval = set_find((m), ((m)->tmp.key)), \
(m)->tmpval = (m)->tmpval ? FREE((m)->tmp.key), (m)->tmpval : set_insert((m), ((m)->tmp.key)) \
)
#define set_erase(m, k) ( \
(m)->ptr = &(m)->tmp, \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
set_erase(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash) \
)
#define set_foreach for each_set
#define each_set(m,key_t,k) \
( int i_ = (m)->base.count ? 0 : SET_HASHSIZE; i_ < SET_HASHSIZE; ++i_) \
for( set_item *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \
for( key_t k = *(key_t *)cur_->key; on_; on_ = 0 )
#define set_foreach_ptr for each_set_ptr
#define each_set_ptr(m,key_t,k) \
( int i_ = (m)->base.count ? 0 : SET_HASHSIZE; i_ < SET_HASHSIZE; ++i_) \
for( set_item *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \
for( key_t *k = (key_t *)cur_->key; on_; on_ = 0 )
#define set_clear(m) ( \
set_clear(&(m)->base) \
)
#define set_isempty(m) set_isempty(&(m)->base)
#define set_count(m) set_count(&(m)->base)
#define set_gc(m) set_gc(&(m)->base)
#ifndef set_init_int
#define set_init_int(m) set_init((m), less_int, hash_64) // hash_int)
#define set_init_str(m) set_init((m), less_str, hash_str)
#define set_init_ptr(m) set_init((m), less_ptr, hash_ptr)
#endif
// private:
#if is(cpp)
#define set_cast(t) (decltype(t))
#else
#define set_cast(t) (void *)
#endif
typedef struct set_item {
struct set_item *next;
uint64_t keyhash;
void *key;
void *super;
} set_item;
typedef struct set {
array(set_item*) array;
int (*cmp)(void *, void *);
uint64_t (*hash)(void *);
int count;
} set;
API void (set_init)(set *m);
API void (set_free)(set *m);
API void (set_insert)(set *m, set_item *p, void *key, uint64_t keyhash, void *super);
API void (set_erase)(set *m, void *key, uint64_t keyhash);
API void* (set_find)(const set *m, void *key, uint64_t keyhash);
API int (set_isempty)(const set *m);
API int (set_count)(const set *m);
API void (set_gc)(set *m); // only if using SET_DONT_ERASE
API void (set_clear)(set* m);
// -----------------------------------------------------------------------------
// map<K,V>
// ideas from: https://en.wikipedia.org/wiki/Hash_table
// ideas from: https://probablydance.com/2017/02/26/i-wrote-the-fastest-hashtable/
// ideas from: http://www.idryman.org/blog/2017/05/03/writing-a-damn-fast-hash-table-with-tiny-memory-footprints/
// config
#ifndef MAP_HASHSIZE
#define MAP_HASHSIZE (4096 << 4)
#endif
#ifndef MAP_DONT_ERASE
#define MAP_DONT_ERASE 1
#endif
// public api
#define map(K,V) \
struct { map base; struct { pair p; K key; V val; } tmp, *ptr; V* tmpval; \
int (*typed_cmp)(K, K); uint64_t (*typed_hash)(K); } *
#define map_init(m, cmpfn, hashfn) ( \
(m) = map_cast(m) REALLOC(0, sizeof(*(m))), \
map_init(&(m)->base), \
(m)->base.cmp = (int(*)(void*,void*))( (m)->typed_cmp = map_cast((m)->typed_cmp) cmpfn), \
(m)->base.hash = (uint64_t(*)(void*))( (m)->typed_hash = map_cast((m)->typed_hash) hashfn ) \
)
#define map_free(m) ( \
map_free(&(m)->base), \
map_cast(m) REALLOC((m), sizeof(*(m))), (m) = 0 \
)
#define map_insert(m, k, v) ( \
(m)->ptr = map_cast((m)->ptr) REALLOC(0, sizeof((m)->tmp)), \
(m)->ptr->val = (v), \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
map_insert(&(m)->base, &(m)->ptr->p, &(m)->ptr->key, &(m)->ptr->val, (m)->ptr->p.keyhash, (m)->ptr), \
&(m)->ptr->val \
)
#define map_find(m, k) ( \
(m)->ptr = &(m)->tmp, \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
(m)->ptr = map_cast((m)->ptr) map_find(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash), \
(m)->ptr ? &(m)->ptr->val : 0 \
)
#define map_find_or_add(m, k, v) ( \
(m)->tmp.key = (k), (m)->tmp.val = (v), \
(m)->tmpval = map_find((m), ((m)->tmp.key)), \
(m)->tmpval = (m)->tmpval ? (m)->tmpval : map_insert((m), ((m)->tmp.key), ((m)->tmp.val)) \
)
#define map_find_or_add_allocated_key(m, k, v) ( \
(m)->tmp.key = (k), (m)->tmp.val = (v), \
(m)->tmpval = map_find((m), ((m)->tmp.key)), \
(m)->tmpval = (m)->tmpval ? FREE((m)->tmp.key), (m)->tmpval : map_insert((m), ((m)->tmp.key), ((m)->tmp.val)) \
)
#define map_erase(m, k) ( \
(m)->ptr = &(m)->tmp, \
(m)->ptr->p.keyhash = (m)->typed_hash((m)->ptr->key = (k)), \
map_erase(&(m)->base, &(m)->ptr->key, (m)->ptr->p.keyhash) \
)
#define map_foreach for each_map
#define each_map(m,key_t,k,val_t,v) \
( int i_ = (m)->base.count ? 0 : MAP_HASHSIZE; i_ < MAP_HASHSIZE; ++i_) \
for( pair *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \
for( key_t k = *(key_t *)cur_->key; on_; ) \
for( val_t v = *(val_t *)cur_->value; on_; on_ = 0 )
#define map_foreach_ptr for each_map_ptr
#define each_map_ptr(m,key_t,k,val_t,v) \
( int i_ = (m)->base.count ? 0 : MAP_HASHSIZE; i_ < MAP_HASHSIZE; ++i_) \
for( pair *cur_ = (m)->base.array[i_], *on_ = cur_; cur_; on_ = cur_ = cur_->next ) \
for( key_t *k = (key_t *)cur_->key; on_; ) \
for( val_t *v = (val_t *)cur_->value; on_; on_ = 0 )
#define map_foreach_ptr_sorted for each_map_ptr_sorted
#define each_map_ptr_sorted(m,key_t,k,val_t,v) \
( int i_ = (map_sort(&(m)->base), 0); i_ < array_count((m)->base.sorted); ++i_) \
for( pair *cur_ = (m)->base.sorted[i_]; cur_; ) \
for( key_t *k = (key_t *)cur_->key; cur_; ) \
for( val_t *v = (val_t *)cur_->value; cur_; cur_ = 0 )
#define map_clear(m) ( \
map_clear(&(m)->base) \
)
#define map_isempty(m) map_isempty(&(m)->base)
#define map_count(m) map_count(&(m)->base)
#define map_gc(m) map_gc(&(m)->base)
// aliases:
#ifndef map_init_int
#define map_init_int(m) map_init((m), less_int, hash_int) // hash_64
#define map_init_str(m) map_init((m), less_str, hash_str)
#define map_init_ptr(m) map_init((m), less_ptr, hash_ptr)
#endif
// private:
#if is(cpp)
#define map_cast(t) (decltype(t))
#else
#define map_cast(t) (void *)
#endif
typedef struct pair {
struct pair *next;
uint64_t keyhash;
void *key;
void *value;
void *super;
} pair;
typedef struct map {
array(pair*) array;
int (*cmp)(void *, void *);
uint64_t (*hash)(void *);
int count:31;
int is_sorted:1;
array(pair*) sorted;
} map;
API void (map_init)(map *m);
API void (map_free)(map *m);
API void (map_insert)(map *m, pair *p, void *key, void *value, uint64_t keyhash, void *super);
API void (map_erase)(map *m, void *key, uint64_t keyhash);
API void* (map_find)(map *m, void *key, uint64_t keyhash);
API int (map_isempty)(map *m);
API int (map_count)(map *m);
API void (map_gc)(map *m); // only if using MAP_DONT_ERASE
API bool (map_sort)(map* m);
API void (map_clear)(map* m);

File diff suppressed because it is too large Load Diff

View File

@ -1,114 +1,114 @@
// -----------------------------------------------------------------------------
// in-game editor
// - rlyeh, public domain.
#define EDITOR_VERSION "2023.10"
// ----------------------------------------------------------------------------
// editor bindings
typedef struct editor_bind_t {
const char *command;
const char *bindings;
void (*fn)();
} editor_bind_t;
API void editor_addbind(editor_bind_t bind);
#define EDITOR_BIND(CMD,KEYS,...) \
void macro(editor_bind_##CMD##_fn_)() { __VA_ARGS__ }; AUTORUN { array_push(editor_binds, ((editor_bind_t){#CMD,KEYS,macro(editor_bind_##CMD##_fn_)}) ); }
// ----------------------------------------------------------------------------
// editor properties
#define EDITOR_PROPERTYDEF(T,property_name) \
typedef map(void*,T) editor_##property_name##_map_t; \
API editor_##property_name##_map_t *editor_##property_name##_map(); \
API T editor_##property_name(const void *obj); \
API void editor_set##property_name(const void *obj, T value); \
API void editor_alt##property_name(const void *obj); \
API void editor_no##property_name(void *obj);
EDITOR_PROPERTYDEF(int, open); ///- whether object is tree opened in tree editor
EDITOR_PROPERTYDEF(int, selected); ///- whether object is displaying a contextual popup or not
EDITOR_PROPERTYDEF(int, changed); ///- whether object is displaying a contextual popup or not
EDITOR_PROPERTYDEF(int, popup); ///- whether object is displaying a contextual popup or not
EDITOR_PROPERTYDEF(int, bookmarked); ///-
EDITOR_PROPERTYDEF(int, visible); ///-
EDITOR_PROPERTYDEF(int, script); ///-
EDITOR_PROPERTYDEF(int, event); ///-
EDITOR_PROPERTYDEF(char*,iconinstance); ///-
EDITOR_PROPERTYDEF(char*,iconclass); ///-
EDITOR_PROPERTYDEF(int, treeoffsety); ///-
API void editor_destroy_properties(void *o);
API void editor_load_on_boot(void);
API void editor_save_on_quit(void);
// ----------------------------------------------------------------------------
// editor ui
enum EDITOR_MODE {
EDITOR_PANEL,
EDITOR_WINDOW,
EDITOR_WINDOW_NK,
EDITOR_WINDOW_NK_SMALL,
};
API int editor_begin(const char *title, int mode);
API int editor_end(int mode);
// ----------------------------------------------------------------------------------------
// editor selection
API int editor_filter();
API void editor_select(const char *mask);
API void editor_unselect(); // same than editor_select("!**");
API void editor_select_aabb(aabb box);
API void editor_selectgroup(obj *first, obj *last);
API void* editor_first_selected();
API void* editor_last_selected();
// ----------------------------------------------------------------------------------------
// editor instancing
API void editor_addtoworld(obj *o);
API void editor_watch(const void *o);
API void* editor_spawn(const char *ini); // deprecate?
API void editor_spawn1();
API void editor_destroy_selected();
API void editor_inspect(obj *o);
// ----------------------------------------------------------------------------------------
// editor utils
//API void editor();
//API bool editor_active();
API vec3 editor_pick(float mouse_x, float mouse_y);
API char* editor_path(const char *path);
API void editor_setmouse(int x, int y);
API vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint);
API vec2 editor_glyphs(int x, int y, const char *style, const char *utf8);
API void editor_gizmos(int dim);
// ----------------------------------------------------------------------------------------
// editor loop
API int editor_send(const char *cmd); // returns job-id
API const char* editor_recv(int jobid, double timeout_ss);
API void editor_pump();
API void editor_frame( void (*game)(unsigned, float, double) );
// ----------------------------------------------------------------------------------------
// engine section: @todo: expand me
API float* engine_getf(const char *key);
API int* engine_geti(const char *key);
API char** engine_gets(const char *key);
API int engine_send(const char *cmd, const char *optional_value);
API int ui_engine();
// -----------------------------------------------------------------------------
// in-game editor
// - rlyeh, public domain.
#define EDITOR_VERSION "2023.10"
// ----------------------------------------------------------------------------
// editor bindings
typedef struct editor_bind_t {
const char *command;
const char *bindings;
void (*fn)();
} editor_bind_t;
API void editor_addbind(editor_bind_t bind);
#define EDITOR_BIND(CMD,KEYS,...) \
void macro(editor_bind_##CMD##_fn_)() { __VA_ARGS__ }; AUTORUN { array_push(editor_binds, ((editor_bind_t){#CMD,KEYS,macro(editor_bind_##CMD##_fn_)}) ); }
// ----------------------------------------------------------------------------
// editor properties
#define EDITOR_PROPERTYDEF(T,property_name) \
typedef map(void*,T) editor_##property_name##_map_t; \
API editor_##property_name##_map_t *editor_##property_name##_map(); \
API T editor_##property_name(const void *obj); \
API void editor_set##property_name(const void *obj, T value); \
API void editor_alt##property_name(const void *obj); \
API void editor_no##property_name(void *obj);
EDITOR_PROPERTYDEF(int, open); ///- whether object is tree opened in tree editor
EDITOR_PROPERTYDEF(int, selected); ///- whether object is displaying a contextual popup or not
EDITOR_PROPERTYDEF(int, changed); ///- whether object is displaying a contextual popup or not
EDITOR_PROPERTYDEF(int, popup); ///- whether object is displaying a contextual popup or not
EDITOR_PROPERTYDEF(int, bookmarked); ///-
EDITOR_PROPERTYDEF(int, visible); ///-
EDITOR_PROPERTYDEF(int, script); ///-
EDITOR_PROPERTYDEF(int, event); ///-
EDITOR_PROPERTYDEF(char*,iconinstance); ///-
EDITOR_PROPERTYDEF(char*,iconclass); ///-
EDITOR_PROPERTYDEF(int, treeoffsety); ///-
API void editor_destroy_properties(void *o);
API void editor_load_on_boot(void);
API void editor_save_on_quit(void);
// ----------------------------------------------------------------------------
// editor ui
enum EDITOR_MODE {
EDITOR_PANEL,
EDITOR_WINDOW,
EDITOR_WINDOW_NK,
EDITOR_WINDOW_NK_SMALL,
};
API int editor_begin(const char *title, int mode);
API int editor_end(int mode);
// ----------------------------------------------------------------------------------------
// editor selection
API int editor_filter();
API void editor_select(const char *mask);
API void editor_unselect(); // same than editor_select("!**");
API void editor_select_aabb(aabb box);
API void editor_selectgroup(obj *first, obj *last);
API void* editor_first_selected();
API void* editor_last_selected();
// ----------------------------------------------------------------------------------------
// editor instancing
API void editor_addtoworld(obj *o);
API void editor_watch(const void *o);
API void* editor_spawn(const char *ini); // deprecate?
API void editor_spawn1();
API void editor_destroy_selected();
API void editor_inspect(obj *o);
// ----------------------------------------------------------------------------------------
// editor utils
//API void editor();
//API bool editor_active();
API vec3 editor_pick(float mouse_x, float mouse_y);
API char* editor_path(const char *path);
API void editor_setmouse(int x, int y);
API vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint);
API vec2 editor_glyphs(int x, int y, const char *style, const char *utf8);
API void editor_gizmos(int dim);
// ----------------------------------------------------------------------------------------
// editor loop
API int editor_send(const char *cmd); // returns job-id
API const char* editor_recv(int jobid, double timeout_ss);
API void editor_pump();
API void editor_frame( void (*game)(unsigned, float, double) );
// ----------------------------------------------------------------------------------------
// engine section: @todo: expand me
API float* engine_getf(const char *key);
API int* engine_geti(const char *key);
API char** engine_gets(const char *key);
API int engine_send(const char *cmd, const char *optional_value);
API int ui_engine();

View File

@ -1,390 +1,390 @@
// editing:
// nope > functions: add/rem property
#define ICON_PLAY ICON_MD_PLAY_ARROW
#define ICON_PAUSE ICON_MD_PAUSE
#define ICON_STOP ICON_MD_STOP
#define ICON_CANCEL ICON_MD_CLOSE
#define ICON_WARNING ICON_MD_WARNING
#define ICON_BROWSER ICON_MD_FOLDER_SPECIAL
#define ICON_OUTLINER ICON_MD_VIEW_IN_AR
#define ICON_BUILD ICON_MD_BUILD
#define ICON_SCREENSHOT ICON_MD_PHOTO_CAMERA
#define ICON_CAMERA_ON ICON_MD_VIDEOCAM
#define ICON_CAMERA_OFF ICON_MD_VIDEOCAM_OFF
#define ICON_GAMEPAD_ON ICON_MD_VIDEOGAME_ASSET
#define ICON_GAMEPAD_OFF ICON_MD_VIDEOGAME_ASSET_OFF
#define ICON_AUDIO_ON ICON_MD_VOLUME_UP
#define ICON_AUDIO_OFF ICON_MD_VOLUME_OFF
#define ICON_WINDOWED ICON_MD_FULLSCREEN_EXIT
#define ICON_FULLSCREEN ICON_MD_FULLSCREEN
#define ICON_LIGHTS_ON ICON_MD_LIGHTBULB
#define ICON_LIGHTS_OFF ICON_MD_LIGHTBULB_OUTLINE
#define ICON_RENDER_BASIC ICON_MD_IMAGE_SEARCH
#define ICON_RENDER_FULL ICON_MD_INSERT_PHOTO
#define ICON_SIGNAL ICON_MD_SIGNAL_CELLULAR_ALT
#define ICON_DISK ICON_MD_STORAGE
#define ICON_RATE ICON_MD_SPEED
#define ICON_CLOCK ICON_MD_TODAY
#define ICON_CHRONO ICON_MD_TIMELAPSE
#define ICON_SETTINGS ICON_MD_SETTINGS
#define ICON_LANGUAGE ICON_MD_G_TRANSLATE
#define ICON_PERSONA ICON_MD_FACE
#define ICON_SOCIAL ICON_MD_MESSAGE
#define ICON_GAME ICON_MD_ROCKET_LAUNCH
#define ICON_KEYBOARD ICON_MD_KEYBOARD
#define ICON_MOUSE ICON_MD_MOUSE
#define ICON_GAMEPAD ICON_MD_GAMEPAD
#define ICON_MONITOR ICON_MD_MONITOR
#define ICON_WIFI ICON_MD_WIFI
#define ICON_BUDGET ICON_MD_SAVINGS
#define ICON_NEW_FOLDER ICON_MD_CREATE_NEW_FOLDER
#define ICON_PLUGIN ICON_MD_EXTENSION
#define ICON_RESTART ICON_MD_REPLAY
#define ICON_QUIT ICON_MD_CLOSE
#define ICON_POWER ICON_MD_BOLT // ICON_MD_POWER
#define ICON_BATTERY_CHARGING ICON_MD_BATTERY_CHARGING_FULL
#define ICON_BATTERY_LEVELS \
ICON_MD_BATTERY_ALERT, \
ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR, \
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR, \
ICON_MD_BATTERY_4_BAR,ICON_MD_BATTERY_5_BAR, \
ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL
char *editor_path(const char *path) {
return va("%s/%s", EDITOR, path);
}
vec3 editor_pick(float mouse_x, float mouse_y) {
#if 0
// unproject 2d coord as 3d coord
camera_t *camera = camera_get_active();
vec3 out, xyd = vec3(mouse_x,window_height()-mouse_y,1); // usually x:mouse_x,y:window_height()-mouse_y,d:0=znear/1=zfar
mat44 mvp, model; identity44(model); multiply44x3(mvp, camera->proj, camera->view, model);
bool ok = unproject44(&out, xyd, vec4(0,0,window_width(),window_height()), mvp);
return out;
#else
// unproject 2d coord as 3d coord
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
camera_t *camera = camera_get_active();
float x = (2.0f * mouse_x) / (dpi.x * window_width()) - 1.0f;
float y = 1.0f - (2.0f * mouse_y) / (dpi.y * window_height());
float z = 1.0f;
vec3 ray_nds = vec3(x, y, z);
vec4 ray_clip = vec4(ray_nds.x, ray_nds.y, -1.0, 1.0);
mat44 inv_proj; invert44(inv_proj, camera->proj);
mat44 inv_view; invert44(inv_view, camera->view);
vec4 p = transform444(inv_proj, ray_clip);
vec4 eye = vec4(p.x, p.y, -1.0, 0.0);
vec4 wld = norm4(transform444(inv_view, eye));
return vec3(wld.x, wld.y, wld.z);
#endif
}
typedef union engine_var {
int i;
float f;
char *s;
} engine_var;
static map(char*,engine_var) engine_vars;
float *engine_getf(const char *key) {
if(!engine_vars) map_init_str(engine_vars);
engine_var *found = map_find_or_add(engine_vars, (char*)key, ((engine_var){0}) );
return &found->f;
}
int *engine_geti(const char *key) {
if(!engine_vars) map_init_str(engine_vars);
engine_var *found = map_find_or_add(engine_vars, (char*)key, ((engine_var){0}) );
return &found->i;
}
char **engine_gets(const char *key) {
if(!engine_vars) map_init_str(engine_vars);
engine_var *found = map_find_or_add(engine_vars, (char*)key, ((engine_var){0}) );
if(!found->s) found->s = stringf("%s","");
return &found->s;
}
int engine_send(const char *cmd, const char *optional_value) {
unsigned *gamepads = engine_geti("gamepads"); // 0 off, mask gamepad1(1), gamepad2(2), gamepad3(4), gamepad4(8)...
unsigned *renders = engine_geti("renders"); // 0 off, mask: 1=lit, 2=ddraw, 3=whiteboxes
float *speed = engine_getf("speed"); // <0 num of frames to advance, 0 paused, [0..1] slomo, 1 play regular speed, >1 fast-forward (x2/x4/x8)
unsigned *powersave = engine_geti("powersave");
char *name;
/**/ if( !strcmp(cmd, "key_quit" )) record_stop(), exit(0);
else if( !strcmp(cmd, "key_stop" )) window_pause(1);
else if( !strcmp(cmd, "key_mute" )) audio_volume_master( 1 ^ !!audio_volume_master(-1) );
else if( !strcmp(cmd, "key_pause" )) window_pause( window_has_pause() ^ 1 );
else if( !strcmp(cmd, "key_reload" )) window_reload();
else if( !strcmp(cmd, "key_battery" )) *powersave = optional_value ? !!atoi(optional_value) : *powersave ^ 1;
else if( !strcmp(cmd, "key_browser" )) ui_show("File Browser", ui_visible("File Browser") ^ true);
else if( !strcmp(cmd, "key_outliner" )) ui_show("Outliner", ui_visible("Outliner") ^ true);
else if( !strcmp(cmd, "key_record" )) if(record_active()) record_stop(), ui_notify(va("Video recorded"), date_string()); else
app_beep(), name = file_counter(va("%s.mp4",app_name())), window_record(name);
else if( !strcmp(cmd, "key_screenshot" )) name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
else if( !strcmp(cmd, "key_profiler" )) ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
else if( !strcmp(cmd, "key_fullscreen" )) record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
else if( !strcmp(cmd, "key_gamepad" )) *gamepads = (*gamepads & ~1u) | ((*gamepads & 1) ^ 1);
else if( !strcmp(cmd, "key_lit" )) *renders = (*renders & ~1u) | ((*renders & 1) ^ 1);
else if( !strcmp(cmd, "key_ddraw" )) *renders = (*renders & ~2u) | ((*renders & 2) ^ 2);
else alert(va("editor could not handle `%s` command.", cmd));
return 0;
}
int engine_tick() {
enum { engine_hz_mid = 30 };
enum { engine_hz_low = 10 };
static double old_hz = 0.0;
if( *engine_geti("powersave") ) {
// adaptive framerate
int app_on_background = !window_has_focus();
int hz = app_on_background ? engine_hz_low : engine_hz_mid;
if (!old_hz) old_hz = window_fps_target();
window_fps_lock( hz );
} else if( old_hz && old_hz != window_fps_target() ) {
window_fps_lock( old_hz );
old_hz = 0.0;
}
return 0;
}
int ui_engine() {
static int time_factor = 0;
static int playing = 0;
static int paused = 0;
int advance_frame = 0;
#if 0
static int do_filter = 0;
static int do_profile = 0;
static int do_extra = 0;
char *EDITOR_TOOLBAR_ICONS = va("%s;%s;%s;%s;%s;%s;%s;%s",
do_filter ? ICON_MD_CLOSE : ICON_MD_SEARCH,
ICON_MD_PLAY_ARROW,
paused ? ICON_MD_SKIP_NEXT : ICON_MD_PAUSE,
ICON_MD_FAST_FORWARD,
ICON_MD_STOP,
ICON_MD_REPLAY,
ICON_MD_FACE,
ICON_MD_MENU
);
if( input_down(KEY_F) ) if( input(KEY_LCTRL) || input(KEY_RCTRL) ) do_filter ^= 1;
int choice = ui_toolbar(EDITOR_TOOLBAR_ICONS);
if( choice == 1 ) do_filter ^= 1, do_profile = 0, do_extra = 0;
if( choice == 2 ) playing = 1, paused = 0;
if( choice == 3 ) advance_frame = !!paused, paused = 1;
if( choice == 4 ) paused = 0, time_factor = (++time_factor) % 4;
if( choice == 5 ) playing = 0, paused = 0, advance_frame = 0, time_factor = 0;
if( choice == 6 ) window_reload();
if( choice == 7 ) do_filter = 0, do_profile ^= 1, do_extra = 0;
if( choice == 8 ) do_filter = 0, do_profile = 0, do_extra ^= 1;
if( do_filter ) {
char *bak = ui_filter; ui_filter = 0;
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &bak);
ui_filter = bak;
if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon)
do_filter = 0;
}
} else {
if( ui_filter ) ui_filter[0] = '\0';
}
char *filter_mask = ui_filter && ui_filter[0] ? va("*%s*", ui_filter) : "*";
static char *username = 0;
static char *userpass = 0;
if( do_profile ) {
ui_string(ICON_MD_FACE " Username", &username);
ui_string(ICON_MD_FACE " Password", &userpass);
}
if( do_extra ) {
int choice2 = ui_label2_toolbar(NULL,
ICON_MD_VIEW_IN_AR
ICON_MD_MESSAGE
ICON_MD_TIPS_AND_UPDATES ICON_MD_LIGHTBULB ICON_MD_LIGHTBULB_OUTLINE
ICON_MD_IMAGE_SEARCH ICON_MD_INSERT_PHOTO
ICON_MD_VIDEOGAME_ASSET ICON_MD_VIDEOGAME_ASSET_OFF
ICON_MD_VOLUME_UP ICON_MD_VOLUME_OFF // audio_volume_master(-1) > 0
ICON_MD_TROUBLESHOOT ICON_MD_SCHEMA ICON_MD_MENU
);
}
#endif
int open = 0, clicked_or_toggled = 0;
#define EDITOR_UI_COLLAPSE(f,...) \
for( int macro(p) = (open = ui_collapse(f,__VA_ARGS__)), macro(dummy) = (clicked_or_toggled = ui_collapse_clicked()); macro(p); ui_collapse_end(), macro(p) = 0)
EDITOR_UI_COLLAPSE(ICON_MD_VIEW_QUILT " Windows", "Debug.Windows") {
int choice = ui_toolbar(ICON_MD_RECYCLING "@Reset layout;" ICON_MD_SAVE_AS "@Save layout");
if( choice == 1 ) ui_layout_all_reset("*");
if( choice == 2 ) file_delete(WINDOWS_INI), ui_layout_all_save_disk("*");
for each_map_ptr_sorted(ui_windows, char*, k, unsigned, v) {
bool visible = ui_visible(*k);
if( ui_bool( *k, &visible ) ) {
ui_show( *k, ui_visible(*k) ^ true );
}
}
}
EDITOR_UI_COLLAPSE(ICON_MD_BUG_REPORT " Bugs 0", "Debug.Bugs") {
// @todo. parse /bugs.ini, includes saved screenshots & videos.
// @todo. screenshot include parseable level, position screen markers (same info as /bugs.ini)
}
// Art and bookmarks
EDITOR_UI_COLLAPSE(ICON_MD_FOLDER_SPECIAL " Art", "Debug.Art") {
bool inlined = true;
const char *file = 0;
if( ui_browse(&file, &inlined) ) {
const char *sep = ifdef(win32, "\"", "'");
app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep));
}
}
EDITOR_UI_COLLAPSE(ICON_MD_BOOKMARK " Bookmarks", "Debug.Bookmarks") { /* @todo */ }
// E,C,S,W
EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Scene", "Debug.Scene") {
EDITOR_UI_COLLAPSE(ICON_MD_BUBBLE_CHART/*ICON_MD_SCATTER_PLOT*/ " Entities", "Debug.Entities") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_TUNE " Components", "Debug.Components") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PRECISION_MANUFACTURING " Systems", "Debug.Systems") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PUBLIC " Levels", "Debug.Levels") {
//node_edit(editor.edit.down,&editor.edit);
}
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Init", "Debug.HierarchyInit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Draw", "Debug.HierarchyDraw") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Tick", "Debug.HierarchyTick") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Edit", "Debug.HierarchyEdit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Quit", "Debug.HierarchyQuit") { /* @todo */ }
// node_edit(&editor.init,&editor.init);
// node_edit(&editor.draw,&editor.draw);
// node_edit(&editor.tick,&editor.tick);
// node_edit(&editor.edit,&editor.edit);
// node_edit(&editor.quit,&editor.quit);
}
EDITOR_UI_COLLAPSE(ICON_MD_ROCKET_LAUNCH " AI", "Debug.AI") {
// @todo
}
EDITOR_UI_COLLAPSE(ICON_MD_VOLUME_UP " Audio", "Debug.Audio") {
ui_audio();
}
EDITOR_UI_COLLAPSE(ICON_MD_VIDEOCAM " Camera", "Debug.Camera") {
ui_camera( camera_get_active() );
}
EDITOR_UI_COLLAPSE(ICON_MD_MONITOR " Display", "Debug.Display") {
// @todo: fps lock, fps target, aspect ratio, fullscreen
char *text = va("%s;%s;%s",
window_has_fullscreen() ? ICON_MD_FULLSCREEN_EXIT : ICON_MD_FULLSCREEN,
ICON_MD_PHOTO_CAMERA,
record_active() ? ICON_MD_VIDEOCAM_OFF : ICON_MD_VIDEOCAM
);
int choice = ui_toolbar(text);
if( choice == 1 ) engine_send("key_fullscreen",0);
if( choice == 2 ) engine_send("key_screenshot",0);
if( choice == 3 ) engine_send("key_record",0);
}
EDITOR_UI_COLLAPSE(ICON_MD_KEYBOARD " Keyboard", "Debug.Keyboard") {
ui_keyboard();
}
EDITOR_UI_COLLAPSE(ICON_MD_MOUSE " Mouse", "Debug.Mouse") {
ui_mouse();
}
EDITOR_UI_COLLAPSE(ICON_MD_GAMEPAD " Gamepads", "Debug.Gamepads") {
for( int q = 0; q < 4; ++q ) {
for( int r = (open = ui_collapse(va("Gamepad #%d",q+1), va("Debug.Gamepads%d",q))), dummy = (clicked_or_toggled = ui_collapse_clicked()); r; ui_collapse_end(), r = 0) {
ui_gamepad(q);
}
}
}
EDITOR_UI_COLLAPSE(ICON_MD_TEXT_FIELDS " Fonts", "Debug.Fonts") {
ui_font();
}
EDITOR_UI_COLLAPSE(ICON_MD_CONTENT_PASTE " Scripts", "Debug.Scripts") {
// @todo
}
EDITOR_UI_COLLAPSE(ICON_MD_STAR_HALF " Shaders", "Debug.Shaders") {
ui_shaders();
}
EDITOR_UI_COLLAPSE(ICON_MD_MOVIE " FXs", "Debug.FXs") {
ui_fxs();
}
EDITOR_UI_COLLAPSE(ICON_MD_SAVINGS " Budgets", "Debug.Budgets") {
// @todo. // mem,fps,gfx,net,hdd,... also logging
}
EDITOR_UI_COLLAPSE(ICON_MD_WIFI/*ICON_MD_SIGNAL_CELLULAR_ALT*/ " Network 0/0 KiB", "Debug.Network") {
// @todo
// SIGNAL_CELLULAR_1_BAR SIGNAL_CELLULAR_2_BAR
}
EDITOR_UI_COLLAPSE(va(ICON_MD_SPEED " Profiler %5.2f/%dfps", window_fps(), (int)window_fps_target()), "Debug.Profiler") {
ui_profiler();
}
EDITOR_UI_COLLAPSE(va(ICON_MD_STORAGE " Storage %s", xstats()), "Debug.Storage") {
// @todo
}
// logic: either plug icon (power saving off) or one of the following ones (power saving on):
// if 0% batt (no batt): battery alert
// if discharging: battery levels [alert,0..6,full]
// if charging: battery charging
int battery_read = app_battery();
int battery_level = abs(battery_read);
int battery_discharging = battery_read < 0 && battery_level < 100;
const char *power_icon_label = ICON_MD_POWER " Power";
if( battery_level ) {
const char *battery_levels[9] = { // @todo: remap [7%..100%] -> [0..1] ?
ICON_MD_BATTERY_ALERT,ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR,
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR,ICON_MD_BATTERY_4_BAR,
ICON_MD_BATTERY_5_BAR,ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL,
};
power_icon_label = (const char*)va("%s Power %d%%",
battery_discharging ? battery_levels[(int)((9-1)*clampf(battery_level/100.f,0,1))] : ICON_MD_BATTERY_CHARGING_FULL,
battery_level);
}
EDITOR_UI_COLLAPSE(power_icon_label, "Debug.Power") {
int choice = ui_toolbar( ICON_MD_POWER ";" ICON_MD_BOLT );
if( choice == 1 ) engine_send("key_battery","0");
if( choice == 2 ) engine_send("key_battery","1");
}
EDITOR_UI_COLLAPSE(ICON_MD_WATER " Reflection", "Debug.Reflect") {
ui_reflect("*");
}
EDITOR_UI_COLLAPSE(ICON_MD_EXTENSION " Plugins", "Debug.Plugins") {
// @todo. include VCS
EDITOR_UI_COLLAPSE(ICON_MD_BUILD " Cook", "Debug.Cook") {
// @todo
}
}
return 0;
}
// editing:
// nope > functions: add/rem property
#define ICON_PLAY ICON_MD_PLAY_ARROW
#define ICON_PAUSE ICON_MD_PAUSE
#define ICON_STOP ICON_MD_STOP
#define ICON_CANCEL ICON_MD_CLOSE
#define ICON_WARNING ICON_MD_WARNING
#define ICON_BROWSER ICON_MD_FOLDER_SPECIAL
#define ICON_OUTLINER ICON_MD_VIEW_IN_AR
#define ICON_BUILD ICON_MD_BUILD
#define ICON_SCREENSHOT ICON_MD_PHOTO_CAMERA
#define ICON_CAMERA_ON ICON_MD_VIDEOCAM
#define ICON_CAMERA_OFF ICON_MD_VIDEOCAM_OFF
#define ICON_GAMEPAD_ON ICON_MD_VIDEOGAME_ASSET
#define ICON_GAMEPAD_OFF ICON_MD_VIDEOGAME_ASSET_OFF
#define ICON_AUDIO_ON ICON_MD_VOLUME_UP
#define ICON_AUDIO_OFF ICON_MD_VOLUME_OFF
#define ICON_WINDOWED ICON_MD_FULLSCREEN_EXIT
#define ICON_FULLSCREEN ICON_MD_FULLSCREEN
#define ICON_LIGHTS_ON ICON_MD_LIGHTBULB
#define ICON_LIGHTS_OFF ICON_MD_LIGHTBULB_OUTLINE
#define ICON_RENDER_BASIC ICON_MD_IMAGE_SEARCH
#define ICON_RENDER_FULL ICON_MD_INSERT_PHOTO
#define ICON_SIGNAL ICON_MD_SIGNAL_CELLULAR_ALT
#define ICON_DISK ICON_MD_STORAGE
#define ICON_RATE ICON_MD_SPEED
#define ICON_CLOCK ICON_MD_TODAY
#define ICON_CHRONO ICON_MD_TIMELAPSE
#define ICON_SETTINGS ICON_MD_SETTINGS
#define ICON_LANGUAGE ICON_MD_G_TRANSLATE
#define ICON_PERSONA ICON_MD_FACE
#define ICON_SOCIAL ICON_MD_MESSAGE
#define ICON_GAME ICON_MD_ROCKET_LAUNCH
#define ICON_KEYBOARD ICON_MD_KEYBOARD
#define ICON_MOUSE ICON_MD_MOUSE
#define ICON_GAMEPAD ICON_MD_GAMEPAD
#define ICON_MONITOR ICON_MD_MONITOR
#define ICON_WIFI ICON_MD_WIFI
#define ICON_BUDGET ICON_MD_SAVINGS
#define ICON_NEW_FOLDER ICON_MD_CREATE_NEW_FOLDER
#define ICON_PLUGIN ICON_MD_EXTENSION
#define ICON_RESTART ICON_MD_REPLAY
#define ICON_QUIT ICON_MD_CLOSE
#define ICON_POWER ICON_MD_BOLT // ICON_MD_POWER
#define ICON_BATTERY_CHARGING ICON_MD_BATTERY_CHARGING_FULL
#define ICON_BATTERY_LEVELS \
ICON_MD_BATTERY_ALERT, \
ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR, \
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR, \
ICON_MD_BATTERY_4_BAR,ICON_MD_BATTERY_5_BAR, \
ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL
char *editor_path(const char *path) {
return va("%s/%s", EDITOR, path);
}
vec3 editor_pick(float mouse_x, float mouse_y) {
#if 0
// unproject 2d coord as 3d coord
camera_t *camera = camera_get_active();
vec3 out, xyd = vec3(mouse_x,window_height()-mouse_y,1); // usually x:mouse_x,y:window_height()-mouse_y,d:0=znear/1=zfar
mat44 mvp, model; identity44(model); multiply44x3(mvp, camera->proj, camera->view, model);
bool ok = unproject44(&out, xyd, vec4(0,0,window_width(),window_height()), mvp);
return out;
#else
// unproject 2d coord as 3d coord
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
camera_t *camera = camera_get_active();
float x = (2.0f * mouse_x) / (dpi.x * window_width()) - 1.0f;
float y = 1.0f - (2.0f * mouse_y) / (dpi.y * window_height());
float z = 1.0f;
vec3 ray_nds = vec3(x, y, z);
vec4 ray_clip = vec4(ray_nds.x, ray_nds.y, -1.0, 1.0);
mat44 inv_proj; invert44(inv_proj, camera->proj);
mat44 inv_view; invert44(inv_view, camera->view);
vec4 p = transform444(inv_proj, ray_clip);
vec4 eye = vec4(p.x, p.y, -1.0, 0.0);
vec4 wld = norm4(transform444(inv_view, eye));
return vec3(wld.x, wld.y, wld.z);
#endif
}
typedef union engine_var {
int i;
float f;
char *s;
} engine_var;
static map(char*,engine_var) engine_vars;
float *engine_getf(const char *key) {
if(!engine_vars) map_init_str(engine_vars);
engine_var *found = map_find_or_add(engine_vars, (char*)key, ((engine_var){0}) );
return &found->f;
}
int *engine_geti(const char *key) {
if(!engine_vars) map_init_str(engine_vars);
engine_var *found = map_find_or_add(engine_vars, (char*)key, ((engine_var){0}) );
return &found->i;
}
char **engine_gets(const char *key) {
if(!engine_vars) map_init_str(engine_vars);
engine_var *found = map_find_or_add(engine_vars, (char*)key, ((engine_var){0}) );
if(!found->s) found->s = stringf("%s","");
return &found->s;
}
int engine_send(const char *cmd, const char *optional_value) {
unsigned *gamepads = engine_geti("gamepads"); // 0 off, mask gamepad1(1), gamepad2(2), gamepad3(4), gamepad4(8)...
unsigned *renders = engine_geti("renders"); // 0 off, mask: 1=lit, 2=ddraw, 3=whiteboxes
float *speed = engine_getf("speed"); // <0 num of frames to advance, 0 paused, [0..1] slomo, 1 play regular speed, >1 fast-forward (x2/x4/x8)
unsigned *powersave = engine_geti("powersave");
char *name;
/**/ if( !strcmp(cmd, "key_quit" )) record_stop(), exit(0);
else if( !strcmp(cmd, "key_stop" )) window_pause(1);
else if( !strcmp(cmd, "key_mute" )) audio_volume_master( 1 ^ !!audio_volume_master(-1) );
else if( !strcmp(cmd, "key_pause" )) window_pause( window_has_pause() ^ 1 );
else if( !strcmp(cmd, "key_reload" )) window_reload();
else if( !strcmp(cmd, "key_battery" )) *powersave = optional_value ? !!atoi(optional_value) : *powersave ^ 1;
else if( !strcmp(cmd, "key_browser" )) ui_show("File Browser", ui_visible("File Browser") ^ true);
else if( !strcmp(cmd, "key_outliner" )) ui_show("Outliner", ui_visible("Outliner") ^ true);
else if( !strcmp(cmd, "key_record" )) if(record_active()) record_stop(), ui_notify(va("Video recorded"), date_string()); else
app_beep(), name = file_counter(va("%s.mp4",app_name())), window_record(name);
else if( !strcmp(cmd, "key_screenshot" )) name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
else if( !strcmp(cmd, "key_profiler" )) ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
else if( !strcmp(cmd, "key_fullscreen" )) record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
else if( !strcmp(cmd, "key_gamepad" )) *gamepads = (*gamepads & ~1u) | ((*gamepads & 1) ^ 1);
else if( !strcmp(cmd, "key_lit" )) *renders = (*renders & ~1u) | ((*renders & 1) ^ 1);
else if( !strcmp(cmd, "key_ddraw" )) *renders = (*renders & ~2u) | ((*renders & 2) ^ 2);
else alert(va("editor could not handle `%s` command.", cmd));
return 0;
}
int engine_tick() {
enum { engine_hz_mid = 30 };
enum { engine_hz_low = 10 };
static double old_hz = 0.0;
if( *engine_geti("powersave") ) {
// adaptive framerate
int app_on_background = !window_has_focus();
int hz = app_on_background ? engine_hz_low : engine_hz_mid;
if (!old_hz) old_hz = window_fps_target();
window_fps_lock( hz );
} else if( old_hz && old_hz != window_fps_target() ) {
window_fps_lock( old_hz );
old_hz = 0.0;
}
return 0;
}
int ui_engine() {
static int time_factor = 0;
static int playing = 0;
static int paused = 0;
int advance_frame = 0;
#if 0
static int do_filter = 0;
static int do_profile = 0;
static int do_extra = 0;
char *EDITOR_TOOLBAR_ICONS = va("%s;%s;%s;%s;%s;%s;%s;%s",
do_filter ? ICON_MD_CLOSE : ICON_MD_SEARCH,
ICON_MD_PLAY_ARROW,
paused ? ICON_MD_SKIP_NEXT : ICON_MD_PAUSE,
ICON_MD_FAST_FORWARD,
ICON_MD_STOP,
ICON_MD_REPLAY,
ICON_MD_FACE,
ICON_MD_MENU
);
if( input_down(KEY_F) ) if( input(KEY_LCTRL) || input(KEY_RCTRL) ) do_filter ^= 1;
int choice = ui_toolbar(EDITOR_TOOLBAR_ICONS);
if( choice == 1 ) do_filter ^= 1, do_profile = 0, do_extra = 0;
if( choice == 2 ) playing = 1, paused = 0;
if( choice == 3 ) advance_frame = !!paused, paused = 1;
if( choice == 4 ) paused = 0, time_factor = (++time_factor) % 4;
if( choice == 5 ) playing = 0, paused = 0, advance_frame = 0, time_factor = 0;
if( choice == 6 ) window_reload();
if( choice == 7 ) do_filter = 0, do_profile ^= 1, do_extra = 0;
if( choice == 8 ) do_filter = 0, do_profile = 0, do_extra ^= 1;
if( do_filter ) {
char *bak = ui_filter; ui_filter = 0;
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &bak);
ui_filter = bak;
if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon)
do_filter = 0;
}
} else {
if( ui_filter ) ui_filter[0] = '\0';
}
char *filter_mask = ui_filter && ui_filter[0] ? va("*%s*", ui_filter) : "*";
static char *username = 0;
static char *userpass = 0;
if( do_profile ) {
ui_string(ICON_MD_FACE " Username", &username);
ui_string(ICON_MD_FACE " Password", &userpass);
}
if( do_extra ) {
int choice2 = ui_label2_toolbar(NULL,
ICON_MD_VIEW_IN_AR
ICON_MD_MESSAGE
ICON_MD_TIPS_AND_UPDATES ICON_MD_LIGHTBULB ICON_MD_LIGHTBULB_OUTLINE
ICON_MD_IMAGE_SEARCH ICON_MD_INSERT_PHOTO
ICON_MD_VIDEOGAME_ASSET ICON_MD_VIDEOGAME_ASSET_OFF
ICON_MD_VOLUME_UP ICON_MD_VOLUME_OFF // audio_volume_master(-1) > 0
ICON_MD_TROUBLESHOOT ICON_MD_SCHEMA ICON_MD_MENU
);
}
#endif
int open = 0, clicked_or_toggled = 0;
#define EDITOR_UI_COLLAPSE(f,...) \
for( int macro(p) = (open = ui_collapse(f,__VA_ARGS__)), macro(dummy) = (clicked_or_toggled = ui_collapse_clicked()); macro(p); ui_collapse_end(), macro(p) = 0)
EDITOR_UI_COLLAPSE(ICON_MD_VIEW_QUILT " Windows", "Debug.Windows") {
int choice = ui_toolbar(ICON_MD_RECYCLING "@Reset layout;" ICON_MD_SAVE_AS "@Save layout");
if( choice == 1 ) ui_layout_all_reset("*");
if( choice == 2 ) file_delete(WINDOWS_INI), ui_layout_all_save_disk("*");
for each_map_ptr_sorted(ui_windows, char*, k, unsigned, v) {
bool visible = ui_visible(*k);
if( ui_bool( *k, &visible ) ) {
ui_show( *k, ui_visible(*k) ^ true );
}
}
}
EDITOR_UI_COLLAPSE(ICON_MD_BUG_REPORT " Bugs 0", "Debug.Bugs") {
// @todo. parse /bugs.ini, includes saved screenshots & videos.
// @todo. screenshot include parseable level, position screen markers (same info as /bugs.ini)
}
// Art and bookmarks
EDITOR_UI_COLLAPSE(ICON_MD_FOLDER_SPECIAL " Art", "Debug.Art") {
bool inlined = true;
const char *file = 0;
if( ui_browse(&file, &inlined) ) {
const char *sep = ifdef(win32, "\"", "'");
app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep));
}
}
EDITOR_UI_COLLAPSE(ICON_MD_BOOKMARK " Bookmarks", "Debug.Bookmarks") { /* @todo */ }
// E,C,S,W
EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Scene", "Debug.Scene") {
EDITOR_UI_COLLAPSE(ICON_MD_BUBBLE_CHART/*ICON_MD_SCATTER_PLOT*/ " Entities", "Debug.Entities") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_TUNE " Components", "Debug.Components") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PRECISION_MANUFACTURING " Systems", "Debug.Systems") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PUBLIC " Levels", "Debug.Levels") {
//node_edit(editor.edit.down,&editor.edit);
}
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Init", "Debug.HierarchyInit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Draw", "Debug.HierarchyDraw") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Tick", "Debug.HierarchyTick") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Edit", "Debug.HierarchyEdit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Quit", "Debug.HierarchyQuit") { /* @todo */ }
// node_edit(&editor.init,&editor.init);
// node_edit(&editor.draw,&editor.draw);
// node_edit(&editor.tick,&editor.tick);
// node_edit(&editor.edit,&editor.edit);
// node_edit(&editor.quit,&editor.quit);
}
EDITOR_UI_COLLAPSE(ICON_MD_ROCKET_LAUNCH " AI", "Debug.AI") {
// @todo
}
EDITOR_UI_COLLAPSE(ICON_MD_VOLUME_UP " Audio", "Debug.Audio") {
ui_audio();
}
EDITOR_UI_COLLAPSE(ICON_MD_VIDEOCAM " Camera", "Debug.Camera") {
ui_camera( camera_get_active() );
}
EDITOR_UI_COLLAPSE(ICON_MD_MONITOR " Display", "Debug.Display") {
// @todo: fps lock, fps target, aspect ratio, fullscreen
char *text = va("%s;%s;%s",
window_has_fullscreen() ? ICON_MD_FULLSCREEN_EXIT : ICON_MD_FULLSCREEN,
ICON_MD_PHOTO_CAMERA,
record_active() ? ICON_MD_VIDEOCAM_OFF : ICON_MD_VIDEOCAM
);
int choice = ui_toolbar(text);
if( choice == 1 ) engine_send("key_fullscreen",0);
if( choice == 2 ) engine_send("key_screenshot",0);
if( choice == 3 ) engine_send("key_record",0);
}
EDITOR_UI_COLLAPSE(ICON_MD_KEYBOARD " Keyboard", "Debug.Keyboard") {
ui_keyboard();
}
EDITOR_UI_COLLAPSE(ICON_MD_MOUSE " Mouse", "Debug.Mouse") {
ui_mouse();
}
EDITOR_UI_COLLAPSE(ICON_MD_GAMEPAD " Gamepads", "Debug.Gamepads") {
for( int q = 0; q < 4; ++q ) {
for( int r = (open = ui_collapse(va("Gamepad #%d",q+1), va("Debug.Gamepads%d",q))), dummy = (clicked_or_toggled = ui_collapse_clicked()); r; ui_collapse_end(), r = 0) {
ui_gamepad(q);
}
}
}
EDITOR_UI_COLLAPSE(ICON_MD_TEXT_FIELDS " Fonts", "Debug.Fonts") {
ui_font();
}
EDITOR_UI_COLLAPSE(ICON_MD_CONTENT_PASTE " Scripts", "Debug.Scripts") {
// @todo
}
EDITOR_UI_COLLAPSE(ICON_MD_STAR_HALF " Shaders", "Debug.Shaders") {
ui_shaders();
}
EDITOR_UI_COLLAPSE(ICON_MD_MOVIE " FXs", "Debug.FXs") {
ui_fxs();
}
EDITOR_UI_COLLAPSE(ICON_MD_SAVINGS " Budgets", "Debug.Budgets") {
// @todo. // mem,fps,gfx,net,hdd,... also logging
}
EDITOR_UI_COLLAPSE(ICON_MD_WIFI/*ICON_MD_SIGNAL_CELLULAR_ALT*/ " Network 0/0 KiB", "Debug.Network") {
// @todo
// SIGNAL_CELLULAR_1_BAR SIGNAL_CELLULAR_2_BAR
}
EDITOR_UI_COLLAPSE(va(ICON_MD_SPEED " Profiler %5.2f/%dfps", window_fps(), (int)window_fps_target()), "Debug.Profiler") {
ui_profiler();
}
EDITOR_UI_COLLAPSE(va(ICON_MD_STORAGE " Storage %s", xstats()), "Debug.Storage") {
// @todo
}
// logic: either plug icon (power saving off) or one of the following ones (power saving on):
// if 0% batt (no batt): battery alert
// if discharging: battery levels [alert,0..6,full]
// if charging: battery charging
int battery_read = app_battery();
int battery_level = abs(battery_read);
int battery_discharging = battery_read < 0 && battery_level < 100;
const char *power_icon_label = ICON_MD_POWER " Power";
if( battery_level ) {
const char *battery_levels[9] = { // @todo: remap [7%..100%] -> [0..1] ?
ICON_MD_BATTERY_ALERT,ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR,
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR,ICON_MD_BATTERY_4_BAR,
ICON_MD_BATTERY_5_BAR,ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL,
};
power_icon_label = (const char*)va("%s Power %d%%",
battery_discharging ? battery_levels[(int)((9-1)*clampf(battery_level/100.f,0,1))] : ICON_MD_BATTERY_CHARGING_FULL,
battery_level);
}
EDITOR_UI_COLLAPSE(power_icon_label, "Debug.Power") {
int choice = ui_toolbar( ICON_MD_POWER ";" ICON_MD_BOLT );
if( choice == 1 ) engine_send("key_battery","0");
if( choice == 2 ) engine_send("key_battery","1");
}
EDITOR_UI_COLLAPSE(ICON_MD_WATER " Reflection", "Debug.Reflect") {
ui_reflect("*");
}
EDITOR_UI_COLLAPSE(ICON_MD_EXTENSION " Plugins", "Debug.Plugins") {
// @todo. include VCS
EDITOR_UI_COLLAPSE(ICON_MD_BUILD " Cook", "Debug.Cook") {
// @todo
}
}
return 0;
}

View File

@ -1,21 +1,21 @@
#define BROWSER_ICON ICON_MD_FOLDER_SPECIAL
#define BROWSER_TITLE "Browser " BROWSER_ICON
EDITOR_BIND(browser, "held(CTRL)&down(2)", { ui_show(BROWSER_TITLE, ui_visible(BROWSER_TITLE) ^ true); });
int editor_browser(int window_mode) {
window_mode = EDITOR_WINDOW; // force window
if( editor_begin(BROWSER_TITLE, window_mode) ) {
const char *file = 0;
if( ui_browse(&file, NULL) ) {
const char *sep = ifdef(win32, "\"", "'");
app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep));
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_browser);
}
#define BROWSER_ICON ICON_MD_FOLDER_SPECIAL
#define BROWSER_TITLE "Browser " BROWSER_ICON
EDITOR_BIND(browser, "held(CTRL)&down(2)", { ui_show(BROWSER_TITLE, ui_visible(BROWSER_TITLE) ^ true); });
int editor_browser(int window_mode) {
window_mode = EDITOR_WINDOW; // force window
if( editor_begin(BROWSER_TITLE, window_mode) ) {
const char *file = 0;
if( ui_browse(&file, NULL) ) {
const char *sep = ifdef(win32, "\"", "'");
app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep));
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_browser);
}

View File

@ -1,56 +1,56 @@
#define CONSOLE_ICON ICON_MDI_CONSOLE
#define CONSOLE_TITLE "Console " CONSOLE_ICON
EDITOR_BIND(console, "held(CTRL)&down(4)", { ui_show(CONSOLE_TITLE, ui_visible(CONSOLE_TITLE) ^ true); });
int editor_console(int window_mode) {
if( editor_begin(CONSOLE_TITLE, window_mode) ) {
// peek complete window space
struct nk_rect bounds = nk_window_get_content_region(ui_ctx);
enum { CONSOLE_LINE_HEIGHT = 20 };
static array(char*) lines = 0;
do_once {
array_push(lines, stringf("> Editor v%s. Type `%s` for help.", EDITOR_VERSION, ""));
}
int max_lines = (bounds.h - UI_ROW_HEIGHT) / (CONSOLE_LINE_HEIGHT * 2);
if( max_lines >= 1 ) {
nk_layout_row_static(ui_ctx, bounds.h - UI_ROW_HEIGHT, bounds.w, 1);
if(nk_group_begin(ui_ctx, "console.group", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER)) {
nk_layout_row_static(ui_ctx, CONSOLE_LINE_HEIGHT, bounds.w, 1);
for( int i = array_count(lines); i < max_lines; ++i )
array_push_front(lines, 0);
for( int i = array_count(lines) - max_lines; i < array_count(lines); ++i ) {
if( !lines[i] ) {
#if 0 // debug
nk_label_wrap(ui_ctx, va("%d.A/%d",i+1,max_lines));
nk_label_wrap(ui_ctx, va("%d.B/%d",i+1,max_lines));
#else
nk_label_wrap(ui_ctx, "");
nk_label_wrap(ui_ctx, "");
#endif
} else {
nk_label_wrap(ui_ctx, lines[i]);
const char *answer = isdigit(*lines[i]) ? editor_recv( atoi(lines[i]), 0 ) : NULL;
nk_label_wrap(ui_ctx, answer ? answer : "");
}
}
nk_group_end(ui_ctx);
}
}
static char *cmd = 0;
if( ui_string(NULL, &cmd) ) {
int jobid = editor_send(cmd);
array_push(lines, stringf("%d> %s", jobid, cmd));
cmd[0] = 0;
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_console);
}
#define CONSOLE_ICON ICON_MDI_CONSOLE
#define CONSOLE_TITLE "Console " CONSOLE_ICON
EDITOR_BIND(console, "held(CTRL)&down(4)", { ui_show(CONSOLE_TITLE, ui_visible(CONSOLE_TITLE) ^ true); });
int editor_console(int window_mode) {
if( editor_begin(CONSOLE_TITLE, window_mode) ) {
// peek complete window space
struct nk_rect bounds = nk_window_get_content_region(ui_ctx);
enum { CONSOLE_LINE_HEIGHT = 20 };
static array(char*) lines = 0;
do_once {
array_push(lines, stringf("> Editor v%s. Type `%s` for help.", EDITOR_VERSION, ""));
}
int max_lines = (bounds.h - UI_ROW_HEIGHT) / (CONSOLE_LINE_HEIGHT * 2);
if( max_lines >= 1 ) {
nk_layout_row_static(ui_ctx, bounds.h - UI_ROW_HEIGHT, bounds.w, 1);
if(nk_group_begin(ui_ctx, "console.group", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER)) {
nk_layout_row_static(ui_ctx, CONSOLE_LINE_HEIGHT, bounds.w, 1);
for( int i = array_count(lines); i < max_lines; ++i )
array_push_front(lines, 0);
for( int i = array_count(lines) - max_lines; i < array_count(lines); ++i ) {
if( !lines[i] ) {
#if 0 // debug
nk_label_wrap(ui_ctx, va("%d.A/%d",i+1,max_lines));
nk_label_wrap(ui_ctx, va("%d.B/%d",i+1,max_lines));
#else
nk_label_wrap(ui_ctx, "");
nk_label_wrap(ui_ctx, "");
#endif
} else {
nk_label_wrap(ui_ctx, lines[i]);
const char *answer = isdigit(*lines[i]) ? editor_recv( atoi(lines[i]), 0 ) : NULL;
nk_label_wrap(ui_ctx, answer ? answer : "");
}
}
nk_group_end(ui_ctx);
}
}
static char *cmd = 0;
if( ui_string(NULL, &cmd) ) {
int jobid = editor_send(cmd);
array_push(lines, stringf("%d> %s", jobid, cmd));
cmd[0] = 0;
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_console);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,180 +1,180 @@
#define SCENE_ICON ICON_MDI_FILE_TREE
#define SCENE_TITLE "Scene " SCENE_ICON
EDITOR_BIND(scene, "held(CTRL)&down(1)", { ui_show(SCENE_TITLE, ui_visible(SCENE_TITLE) ^ true); });
EDITOR_BIND(node_new, "down(INS)", { editor_spawn1(); } );
EDITOR_BIND(node_del, "down(DEL)", { editor_destroy_selected(); } );
EDITOR_BIND(node_save, "held(CTRL)&down(S)", { puts("@todo"); } );
EDITOR_BIND(scene_save, "held(CTRL)&down(S)&held(SHIFT)",{ puts("@todo"); } );
EDITOR_BIND(select_all, "held(CTRL) & down(A)", { editor_select("**"); } );
EDITOR_BIND(select_none, "held(CTRL) & down(D)", { editor_select("!**"); } );
EDITOR_BIND(select_invert, "held(CTRL) & down(I)", { editor_select("~**"); } );
EDITOR_BIND(bookmark, "held(CTRL) & down(B)", { editor_selected_map_t *map = editor_selected_map(); \
int on = 0; \
for each_map_ptr(*map,void*,o,int,selected) if(*selected) on |= !editor_bookmarked(*o); \
for each_map_ptr(*map,void*,o,int,selected) if(*selected) editor_setbookmarked(*o, on); \
} );
enum {
SCENE_RECURSE = 1,
SCENE_SELECTION = 2,
SCENE_CHECKBOX = 4,
SCENE_INDENT = 8,
SCENE_ALL = ~0u
};
static
void editor_scene_(obj *o, unsigned flags) {
static unsigned tabs = ~0u;
++tabs;
if( o ) {
unsigned do_tags = 1;
unsigned do_indent = !!(flags & SCENE_INDENT);
unsigned do_checkbox = !!(flags & SCENE_CHECKBOX);
unsigned do_recurse = !!(flags & SCENE_RECURSE);
unsigned do_selection = !!(flags & SCENE_SELECTION);
nk_layout_row_dynamic(ui_ctx, 25, 1);
const char *objicon = editor_iconinstance(o);
if(!objicon) objicon = editor_iconclass(obj_type(o));
if(!objicon) objicon = ICON_MDI_CUBE_OUTLINE;
const char *objname = va("%s (%s)", obj_type(o), obj_name(o));
const char *objchevron =
!do_recurse || array_count(*obj_children(o)) <= 1 ? ICON_MDI_CIRCLE_SMALL :
editor_open(o) ? ICON_MDI_CHEVRON_DOWN : ICON_MDI_CHEVRON_RIGHT;
char *label = va("%*s%s%s %s", do_indent*(4+2*tabs), "", objchevron, objicon, objname);
const char *iconsL =
//editor_selected(o) ? ICON_MD_CHECK_BOX : ICON_MD_CHECK_BOX_OUTLINE_BLANK;
editor_selected(o) ? ICON_MDI_CHECKBOX_MARKED : ICON_MDI_CHECKBOX_BLANK_OUTLINE;
const char *iconsR = va("%s%s%s",
editor_script(o) ? ICON_MDI_SCRIPT : ICON_MDI_CIRCLE_SMALL,
editor_event(o) ? ICON_MDI_CALENDAR : ICON_MDI_CIRCLE_SMALL,
editor_visible(o) ? ICON_MDI_EYE_OUTLINE : ICON_MDI_EYE_CLOSED );
UI_TOOLBAR_OVERLAY_DECLARE(int choiceL, choiceR);
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx);
int clicked = nk_hovered_text(ui_ctx, label, strlen(label), NK_TEXT_LEFT, editor_selected(o));
if( clicked && nk_input_is_mouse_hovering_rect(&ui_ctx->input, ((struct nk_rect) { bounds.x,bounds.y,bounds.w*0.66,bounds.h })) )
editor_altselected( o );
vec2i offset_in_tree = {0};
if( do_indent ) {
float thickness = 2.f;
struct nk_color color = {255,255,255,64};
int offsx = 30;
int spacx = 10;
int lenx = (tabs+1)*spacx;
int halfy = bounds.h / 2;
int offsy = halfy + 2;
offset_in_tree = vec2i(bounds.x+offsx+lenx-spacx,bounds.y+offsy);
editor_settreeoffsety(o, offset_in_tree.y);
for( obj *p = obj_parent(o); p ; p = 0 )
nk_stroke_line(canvas, offset_in_tree.x-6,offset_in_tree.y, offset_in_tree.x-spacx,offset_in_tree.y, thickness, color),
nk_stroke_line(canvas, offset_in_tree.x-spacx,offset_in_tree.y,offset_in_tree.x-spacx,editor_treeoffsety(p)+4, thickness, color);
}
if( ui_contextual() ) {
int choice = ui_label(ICON_MD_BOOKMARK_ADDED "Toggle bookmarks (CTRL+B)");
if( choice & 1 ) editor_send("bookmark");
ui_contextual_end(!!choice);
}
UI_TOOLBAR_OVERLAY(choiceL,iconsL,nk_rgba_f(1,1,1,do_checkbox*ui_alpha*0.65),NK_TEXT_LEFT);
if( do_tags )
UI_TOOLBAR_OVERLAY(choiceR,iconsR,nk_rgba_f(1,1,1,ui_alpha*0.65),NK_TEXT_RIGHT);
if( choiceR == 3 ) editor_altscript( o );
if( choiceR == 2 ) editor_altevent( o);
if( choiceR == 1 ) editor_altvisible( o );
if( do_recurse && editor_open(o) ) {
for each_objchild(o,obj*,oo) {
editor_scene_(oo,flags);
}
}
if( clicked && !choiceL && !choiceR ) {
int is_picking = input(KEY_CTRL);
if( !is_picking ) {
if( input(KEY_SHIFT) ) {
editor_selectgroup( editor_first_selected(), editor_last_selected() );
} else {
editor_unselect();
editor_setselected(o, 1);
}
}
for( obj *p = obj_parent(o); p; p = obj_parent(p) ) {
editor_setopen(p, 1);
}
if( nk_input_is_mouse_hovering_rect(&ui_ctx->input, ((struct nk_rect) { bounds.x,bounds.y,offset_in_tree.x-bounds.x+UI_ICON_FONTSIZE/2,bounds.h })) ) {
editor_altopen( o );
}
}
}
--tabs;
}
int editor_scene(int window_mode) {
window_mode = EDITOR_WINDOW; // force window
if( editor_begin(SCENE_TITLE, window_mode)) {
// #define HELP ICON_MDI_INFORMATION_OUTLINE "@-A\n-B\n-C\n" ";"
int choice = ui_toolbar(ICON_MDI_PLUS "@New node (CTRL+N);" ICON_MDI_DOWNLOAD "@Save node (CTRL+S);" ICON_MDI_DOWNLOAD "@Save scene (SHIFT+CTRL+S);" ICON_MD_BOOKMARK_ADDED "@Toggle Bookmark (CTRL+B);");
if( choice == 1 ) editor_send("node_new");
if( choice == 2 ) editor_send("node_save");
if( choice == 3 ) editor_send("scene_save");
if( choice == 4 ) editor_send("bookmark");
array(obj*) bookmarks = 0;
for each_map_ptr(*editor_bookmarked_map(), void*,o,int,bookmarked) {
if( *bookmarked ) {
array_push(bookmarks, *o);
}
}
if( ui_collapse("!" ICON_MD_BOOKMARK "Bookmarks", "DEBUG:BOOKMARK")) {
for each_array( bookmarks, obj*, o )
editor_scene_( o, SCENE_ALL & ~(SCENE_RECURSE|SCENE_INDENT|SCENE_CHECKBOX) );
ui_collapse_end();
}
array_free(bookmarks);
editor_scene_( editor.root, SCENE_ALL );
for each_array( editor.objs, obj*, o )
editor_scene_( o, SCENE_ALL );
ui_separator();
// edit selection
for each_map(*editor_selected_map(), void*,o, int, k) {
if( k ) editor_inspect(o);
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_scene);
}
#define SCENE_ICON ICON_MDI_FILE_TREE
#define SCENE_TITLE "Scene " SCENE_ICON
EDITOR_BIND(scene, "held(CTRL)&down(1)", { ui_show(SCENE_TITLE, ui_visible(SCENE_TITLE) ^ true); });
EDITOR_BIND(node_new, "down(INS)", { editor_spawn1(); } );
EDITOR_BIND(node_del, "down(DEL)", { editor_destroy_selected(); } );
EDITOR_BIND(node_save, "held(CTRL)&down(S)", { puts("@todo"); } );
EDITOR_BIND(scene_save, "held(CTRL)&down(S)&held(SHIFT)",{ puts("@todo"); } );
EDITOR_BIND(select_all, "held(CTRL) & down(A)", { editor_select("**"); } );
EDITOR_BIND(select_none, "held(CTRL) & down(D)", { editor_select("!**"); } );
EDITOR_BIND(select_invert, "held(CTRL) & down(I)", { editor_select("~**"); } );
EDITOR_BIND(bookmark, "held(CTRL) & down(B)", { editor_selected_map_t *map = editor_selected_map(); \
int on = 0; \
for each_map_ptr(*map,void*,o,int,selected) if(*selected) on |= !editor_bookmarked(*o); \
for each_map_ptr(*map,void*,o,int,selected) if(*selected) editor_setbookmarked(*o, on); \
} );
enum {
SCENE_RECURSE = 1,
SCENE_SELECTION = 2,
SCENE_CHECKBOX = 4,
SCENE_INDENT = 8,
SCENE_ALL = ~0u
};
static
void editor_scene_(obj *o, unsigned flags) {
static unsigned tabs = ~0u;
++tabs;
if( o ) {
unsigned do_tags = 1;
unsigned do_indent = !!(flags & SCENE_INDENT);
unsigned do_checkbox = !!(flags & SCENE_CHECKBOX);
unsigned do_recurse = !!(flags & SCENE_RECURSE);
unsigned do_selection = !!(flags & SCENE_SELECTION);
nk_layout_row_dynamic(ui_ctx, 25, 1);
const char *objicon = editor_iconinstance(o);
if(!objicon) objicon = editor_iconclass(obj_type(o));
if(!objicon) objicon = ICON_MDI_CUBE_OUTLINE;
const char *objname = va("%s (%s)", obj_type(o), obj_name(o));
const char *objchevron =
!do_recurse || array_count(*obj_children(o)) <= 1 ? ICON_MDI_CIRCLE_SMALL :
editor_open(o) ? ICON_MDI_CHEVRON_DOWN : ICON_MDI_CHEVRON_RIGHT;
char *label = va("%*s%s%s %s", do_indent*(4+2*tabs), "", objchevron, objicon, objname);
const char *iconsL =
//editor_selected(o) ? ICON_MD_CHECK_BOX : ICON_MD_CHECK_BOX_OUTLINE_BLANK;
editor_selected(o) ? ICON_MDI_CHECKBOX_MARKED : ICON_MDI_CHECKBOX_BLANK_OUTLINE;
const char *iconsR = va("%s%s%s",
editor_script(o) ? ICON_MDI_SCRIPT : ICON_MDI_CIRCLE_SMALL,
editor_event(o) ? ICON_MDI_CALENDAR : ICON_MDI_CIRCLE_SMALL,
editor_visible(o) ? ICON_MDI_EYE_OUTLINE : ICON_MDI_EYE_CLOSED );
UI_TOOLBAR_OVERLAY_DECLARE(int choiceL, choiceR);
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx);
int clicked = nk_hovered_text(ui_ctx, label, strlen(label), NK_TEXT_LEFT, editor_selected(o));
if( clicked && nk_input_is_mouse_hovering_rect(&ui_ctx->input, ((struct nk_rect) { bounds.x,bounds.y,bounds.w*0.66,bounds.h })) )
editor_altselected( o );
vec2i offset_in_tree = {0};
if( do_indent ) {
float thickness = 2.f;
struct nk_color color = {255,255,255,64};
int offsx = 30;
int spacx = 10;
int lenx = (tabs+1)*spacx;
int halfy = bounds.h / 2;
int offsy = halfy + 2;
offset_in_tree = vec2i(bounds.x+offsx+lenx-spacx,bounds.y+offsy);
editor_settreeoffsety(o, offset_in_tree.y);
for( obj *p = obj_parent(o); p ; p = 0 )
nk_stroke_line(canvas, offset_in_tree.x-6,offset_in_tree.y, offset_in_tree.x-spacx,offset_in_tree.y, thickness, color),
nk_stroke_line(canvas, offset_in_tree.x-spacx,offset_in_tree.y,offset_in_tree.x-spacx,editor_treeoffsety(p)+4, thickness, color);
}
if( ui_contextual() ) {
int choice = ui_label(ICON_MD_BOOKMARK_ADDED "Toggle bookmarks (CTRL+B)");
if( choice & 1 ) editor_send("bookmark");
ui_contextual_end(!!choice);
}
UI_TOOLBAR_OVERLAY(choiceL,iconsL,nk_rgba_f(1,1,1,do_checkbox*ui_alpha*0.65),NK_TEXT_LEFT);
if( do_tags )
UI_TOOLBAR_OVERLAY(choiceR,iconsR,nk_rgba_f(1,1,1,ui_alpha*0.65),NK_TEXT_RIGHT);
if( choiceR == 3 ) editor_altscript( o );
if( choiceR == 2 ) editor_altevent( o);
if( choiceR == 1 ) editor_altvisible( o );
if( do_recurse && editor_open(o) ) {
for each_objchild(o,obj*,oo) {
editor_scene_(oo,flags);
}
}
if( clicked && !choiceL && !choiceR ) {
int is_picking = input(KEY_CTRL);
if( !is_picking ) {
if( input(KEY_SHIFT) ) {
editor_selectgroup( editor_first_selected(), editor_last_selected() );
} else {
editor_unselect();
editor_setselected(o, 1);
}
}
for( obj *p = obj_parent(o); p; p = obj_parent(p) ) {
editor_setopen(p, 1);
}
if( nk_input_is_mouse_hovering_rect(&ui_ctx->input, ((struct nk_rect) { bounds.x,bounds.y,offset_in_tree.x-bounds.x+UI_ICON_FONTSIZE/2,bounds.h })) ) {
editor_altopen( o );
}
}
}
--tabs;
}
int editor_scene(int window_mode) {
window_mode = EDITOR_WINDOW; // force window
if( editor_begin(SCENE_TITLE, window_mode)) {
// #define HELP ICON_MDI_INFORMATION_OUTLINE "@-A\n-B\n-C\n" ";"
int choice = ui_toolbar(ICON_MDI_PLUS "@New node (CTRL+N);" ICON_MDI_DOWNLOAD "@Save node (CTRL+S);" ICON_MDI_DOWNLOAD "@Save scene (SHIFT+CTRL+S);" ICON_MD_BOOKMARK_ADDED "@Toggle Bookmark (CTRL+B);");
if( choice == 1 ) editor_send("node_new");
if( choice == 2 ) editor_send("node_save");
if( choice == 3 ) editor_send("scene_save");
if( choice == 4 ) editor_send("bookmark");
array(obj*) bookmarks = 0;
for each_map_ptr(*editor_bookmarked_map(), void*,o,int,bookmarked) {
if( *bookmarked ) {
array_push(bookmarks, *o);
}
}
if( ui_collapse("!" ICON_MD_BOOKMARK "Bookmarks", "DEBUG:BOOKMARK")) {
for each_array( bookmarks, obj*, o )
editor_scene_( o, SCENE_ALL & ~(SCENE_RECURSE|SCENE_INDENT|SCENE_CHECKBOX) );
ui_collapse_end();
}
array_free(bookmarks);
editor_scene_( editor.root, SCENE_ALL );
for each_array( editor.objs, obj*, o )
editor_scene_( o, SCENE_ALL );
ui_separator();
// edit selection
for each_map(*editor_selected_map(), void*,o, int, k) {
if( k ) editor_inspect(o);
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_scene);
}

View File

@ -1,79 +1,79 @@
int ui_texture_fit(texture_t t, struct nk_rect bounds) {
// allocate complete window space
struct nk_rect total_space = nk_window_get_content_region(ui_ctx);
nk_layout_space_begin(ui_ctx, NK_DYNAMIC, total_space.h - 4, 1); // -4 to hide scrollbar Y
nk_layout_space_push(ui_ctx, nk_rect(0,0,1,1));
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct nk_image image = nk_image_id((int)t.id);
nk_draw_image(canvas, bounds, &image, nk_white);
nk_layout_space_end(ui_ctx);
return 0;
}
#define LITE_ICON ICON_MDI_SCRIPT_TEXT
#define LITE_TITLE "Script " LITE_ICON
EDITOR_BIND(script, "held(CTRL)&down(6)", { ui_show(LITE_TITLE, ui_visible(LITE_TITLE) ^ true); });
int editor_scripted(int window_mode) {
window_mode = EDITOR_WINDOW; // force mode
static lua_State *L = 0;
do_once {
L = script_init_env(SCRIPT_LUA|SCRIPT_DEBUGGER);
const char *platform = "" // "Android" "FreeBSD" "OpenBSD" "NetBSD"
ifdef(ems, "Emscripten")
ifdef(linux, "Linux")
ifdef(osx, "macOS")
ifdef(win32, "Windows")
;
const char *pathexe = vac("%s%s%s", app_path(), app_name(), ifdef(win32, ".exe", ""));
gleqInit();
gleqTrackWindow(window_handle());
lt_init(L, window_handle(), LT_DATAPATH, __argc, __argv, window_scale(), platform, pathexe);
}
unsigned lt_none = 0u;
unsigned lt_all = ~0u & ~(GLEQ_WINDOW_MOVED/*|GLEQ_WINDOW_RESIZED|GLEQ_WINDOW_REFRESH*/);
lt_events = lt_none;
int mouse_in_rect = 0;
if( editor_begin(LITE_TITLE, window_mode) ) {
lt_events = lt_all;
if( !nk_window_has_focus(ui_ctx) ) lt_events = lt_none;
struct nk_rect bounds = nk_window_get_content_region(ui_ctx);
lt_mx = input(MOUSE_X) - bounds.x;
lt_my = input(MOUSE_Y) - bounds.y;
lt_wx = bounds.x;
lt_wy = bounds.y;
lt_ww = bounds.w;
lt_wh = bounds.h;
if( lt_resizesurface(lt_getsurface(0), lt_ww, lt_wh) ) {
gleq_window_refresh_callback(window_handle());
}
// fullscreen_quad_rgb( lt_getsurface(0)->t, 1.2f );
ui_texture_fit(lt_getsurface(0)->t, bounds);
if( !!nk_input_is_mouse_hovering_rect(&ui_ctx->input, ((struct nk_rect){lt_wx+5,lt_wy+5,lt_ww-10,lt_wh-10})) ) {
lt_events &= ~(1<<31); // dont cursor shape
}
editor_end(window_mode);
}
lt_tick(L);
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_scripted);
}
int ui_texture_fit(texture_t t, struct nk_rect bounds) {
// allocate complete window space
struct nk_rect total_space = nk_window_get_content_region(ui_ctx);
nk_layout_space_begin(ui_ctx, NK_DYNAMIC, total_space.h - 4, 1); // -4 to hide scrollbar Y
nk_layout_space_push(ui_ctx, nk_rect(0,0,1,1));
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct nk_image image = nk_image_id((int)t.id);
nk_draw_image(canvas, bounds, &image, nk_white);
nk_layout_space_end(ui_ctx);
return 0;
}
#define LITE_ICON ICON_MDI_SCRIPT_TEXT
#define LITE_TITLE "Script " LITE_ICON
EDITOR_BIND(script, "held(CTRL)&down(6)", { ui_show(LITE_TITLE, ui_visible(LITE_TITLE) ^ true); });
int editor_scripted(int window_mode) {
window_mode = EDITOR_WINDOW; // force mode
static lua_State *L = 0;
do_once {
L = script_init_env(SCRIPT_LUA|SCRIPT_DEBUGGER);
const char *platform = "" // "Android" "FreeBSD" "OpenBSD" "NetBSD"
ifdef(ems, "Emscripten")
ifdef(linux, "Linux")
ifdef(osx, "macOS")
ifdef(win32, "Windows")
;
const char *pathexe = vac("%s%s%s", app_path(), app_name(), ifdef(win32, ".exe", ""));
gleqInit();
gleqTrackWindow(window_handle());
lt_init(L, window_handle(), LT_DATAPATH, __argc, __argv, window_scale(), platform, pathexe);
}
unsigned lt_none = 0u;
unsigned lt_all = ~0u & ~(GLEQ_WINDOW_MOVED/*|GLEQ_WINDOW_RESIZED|GLEQ_WINDOW_REFRESH*/);
lt_events = lt_none;
int mouse_in_rect = 0;
if( editor_begin(LITE_TITLE, window_mode) ) {
lt_events = lt_all;
if( !nk_window_has_focus(ui_ctx) ) lt_events = lt_none;
struct nk_rect bounds = nk_window_get_content_region(ui_ctx);
lt_mx = input(MOUSE_X) - bounds.x;
lt_my = input(MOUSE_Y) - bounds.y;
lt_wx = bounds.x;
lt_wy = bounds.y;
lt_ww = bounds.w;
lt_wh = bounds.h;
if( lt_resizesurface(lt_getsurface(0), lt_ww, lt_wh) ) {
gleq_window_refresh_callback(window_handle());
}
// fullscreen_quad_rgb( lt_getsurface(0)->t, 1.2f );
ui_texture_fit(lt_getsurface(0)->t, bounds);
if( !!nk_input_is_mouse_hovering_rect(&ui_ctx->input, ((struct nk_rect){lt_wx+5,lt_wy+5,lt_ww-10,lt_wh-10})) ) {
lt_events &= ~(1<<31); // dont cursor shape
}
editor_end(window_mode);
}
lt_tick(L);
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_scripted);
}

View File

@ -1,172 +1,172 @@
#define TIMELINE_ICON ICON_MDI_CHART_TIMELINE
#define TIMELINE_TITLE "Timeline " TIMELINE_ICON
EDITOR_BIND(timeline, "held(CTRL)&down(3)", { ui_show(TIMELINE_TITLE, ui_visible(TIMELINE_TITLE) ^ true); });
int ui_tween(const char *label, tween_t *t) {
if( ui_filter && ui_filter[0] ) if( !strstr(label, ui_filter) ) return 0;
int expand_keys = label[0] == '!'; label += expand_keys;
const char *id = label;
if( strchr(id, '@') ) *strchr((char*)(id = (const char*)va("%s", label)), '@') = '\0';
enum { LABEL_SPACING = 250 };
enum { ROUNDING = 0 };
enum { THICKNESS = 1 };
enum { PIXELS_PER_SECOND = 60 };
enum { KEY_WIDTH = 5, KEY_HEIGHT = 5 };
enum { TIMELINE_HEIGHT = 25 };
enum { MARKER1_HEIGHT = 5, MARKER10_HEIGHT = 20, MARKER5_HEIGHT = (MARKER1_HEIGHT + MARKER10_HEIGHT) / 2 };
unsigned base_color = WHITE;
unsigned time_color = YELLOW;
unsigned duration_color = ORANGE;
unsigned key_color = GREEN;
int changed = 0;
#if 0
// two rows with height:30 composed of three widgets
nk_layout_row_template_begin(ui_ctx, 30);
nk_layout_row_template_push_variable(ui_ctx, t->duration * PIXELS_PER_SECOND); // min 80px. can grow
nk_layout_row_template_end(ui_ctx);
#endif
char *sid = va("%s.%d", id, 0);
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
ui_label(label);
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx);
bounds.y -= 30;
struct nk_rect baseline = bounds; baseline.y += 30/2;
baseline.x += LABEL_SPACING;
baseline.w -= LABEL_SPACING;
// tween duration
{
struct nk_rect pos = baseline;
pos.w = pos.x + t->duration * PIXELS_PER_SECOND;
pos.y -= TIMELINE_HEIGHT/2;
pos.h = TIMELINE_HEIGHT;
nk_stroke_rect(canvas, pos, ROUNDING, THICKNESS*2, AS_NKCOLOR(duration_color));
}
// tween ranges
for(int i = 0, end = array_count(t->keyframes) - 1; i < end; ++i) {
tween_keyframe_t *k = t->keyframes + i;
tween_keyframe_t *next = k + 1;
struct nk_rect pos = baseline;
pos.x += k->t * PIXELS_PER_SECOND;
pos.w = (next->t - k->t) * PIXELS_PER_SECOND;
pos.y -= TIMELINE_HEIGHT/2;
pos.h = TIMELINE_HEIGHT;
char *sid = va("%s.%d", id, i);
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
struct nk_color c = nk_hsva_f(ui_hue, 0.75f, 0.8f, ui_alpha);
nk_fill_rect(canvas, pos, ROUNDING, k->ease == EASE_ZERO ? AS_NKCOLOR(0) : c); // AS_NKCOLOR(track_color));
}
// horizontal line
nk_stroke_line(canvas, baseline.x, baseline.y, baseline.x+baseline.w,baseline.y, THICKNESS, AS_NKCOLOR(base_color));
// unit, 5-unit and 10-unit markers
for( float i = 0, j = 0; i < baseline.w; i += PIXELS_PER_SECOND/10, ++j ) {
int len = !((int)j%10) ? MARKER10_HEIGHT : !((int)j%5) ? MARKER5_HEIGHT : MARKER1_HEIGHT;
nk_stroke_line(canvas, baseline.x+i, baseline.y-len, baseline.x+i, baseline.y+len, THICKNESS, AS_NKCOLOR(base_color));
}
// time marker
float px = t->time * PIXELS_PER_SECOND;
nk_stroke_line(canvas, baseline.x+px, bounds.y, baseline.x+px, bounds.y+bounds.h, THICKNESS*2, AS_NKCOLOR(time_color));
nk_draw_symbol(canvas, NK_SYMBOL_TRIANGLE_DOWN, ((struct nk_rect){ baseline.x+px-4,bounds.y-4-8,8,8}), /*bg*/AS_NKCOLOR(0), /*fg*/AS_NKCOLOR(time_color), 0.f/*border_width*/, ui_ctx->style.font);
// key markers
for each_array_ptr(t->keyframes, tween_keyframe_t, k) {
struct nk_rect pos = baseline;
pos.x += k->t * PIXELS_PER_SECOND;
vec2 romboid[] = {
{pos.x-KEY_WIDTH,pos.y}, {pos.x,pos.y-KEY_HEIGHT},
{pos.x+KEY_WIDTH,pos.y}, {pos.x,pos.y+KEY_HEIGHT}
};
nk_fill_polygon(canvas, (float*)romboid, countof(romboid), AS_NKCOLOR(key_color));
}
// keys ui
if( expand_keys )
for(int i = 0, end = array_count(t->keyframes); i < end; ++i) {
tween_keyframe_t *k = t->keyframes + i;
if( ui_collapse(va("Key %d", i), va("%s.%d", id, i))) {
changed |= ui_float("Time", &k->t);
changed |= ui_float3("Value", &k->v.x);
changed |= ui_list("Ease", ease_enums(), EASE_NUM, &k->ease );
ui_collapse_end();
}
}
return changed;
}
tween_t* rand_tween() {
tween_t demo = tween();
int num_keys = randi(2,8);
double t = 0;
for( int i = 0; i < num_keys; ++i) {
tween_setkey(&demo, t, scale3(vec3(randf(),randf(),randf()),randi(-5,5)), randi(0,EASE_NUM) );
t += randi(1,5) / ((float)(1 << randi(0,2)));
}
tween_t *p = CALLOC(1, sizeof(tween_t));
memcpy(p, &demo, sizeof(tween_t));
return p;
}
int editor_timeline(int window_mode) {
static array(tween_t*) tweens = 0;
do_once {
array_push(tweens, rand_tween());
}
if( editor.t == 0 )
for each_array(tweens, tween_t*,t) {
tween_reset(t);
}
else
for each_array(tweens, tween_t*,t) {
tween_update(t, editor.dt);
}
static void *selected = NULL;
if( editor_begin(TIMELINE_TITLE, window_mode) ) {
int choice = ui_toolbar(ICON_MDI_PLUS ";" ICON_MDI_MINUS );
if( choice == 1 ) array_push(tweens, rand_tween());
if( choice == 2 && selected ) {
int target = -1;
for( int i = 0, end = array_count(tweens); i < end; ++i ) if( tweens[i] == selected ) { target = i; break; }
if( target >= 0 ) { array_erase_slow(tweens, target); selected = NULL; }
}
for each_array(tweens, tween_t*,t) {
ui_tween(va("%s%p@%05.2fs Value: %s", t == selected ? "!":"", t, t->time, ftoa3(t->result)), t);
if(ui_label_icon_clicked_L.x) selected = (t != selected) ? t : NULL;
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_timeline);
}
#define TIMELINE_ICON ICON_MDI_CHART_TIMELINE
#define TIMELINE_TITLE "Timeline " TIMELINE_ICON
EDITOR_BIND(timeline, "held(CTRL)&down(3)", { ui_show(TIMELINE_TITLE, ui_visible(TIMELINE_TITLE) ^ true); });
int ui_tween(const char *label, tween_t *t) {
if( ui_filter && ui_filter[0] ) if( !strstr(label, ui_filter) ) return 0;
int expand_keys = label[0] == '!'; label += expand_keys;
const char *id = label;
if( strchr(id, '@') ) *strchr((char*)(id = (const char*)va("%s", label)), '@') = '\0';
enum { LABEL_SPACING = 250 };
enum { ROUNDING = 0 };
enum { THICKNESS = 1 };
enum { PIXELS_PER_SECOND = 60 };
enum { KEY_WIDTH = 5, KEY_HEIGHT = 5 };
enum { TIMELINE_HEIGHT = 25 };
enum { MARKER1_HEIGHT = 5, MARKER10_HEIGHT = 20, MARKER5_HEIGHT = (MARKER1_HEIGHT + MARKER10_HEIGHT) / 2 };
unsigned base_color = WHITE;
unsigned time_color = YELLOW;
unsigned duration_color = ORANGE;
unsigned key_color = GREEN;
int changed = 0;
#if 0
// two rows with height:30 composed of three widgets
nk_layout_row_template_begin(ui_ctx, 30);
nk_layout_row_template_push_variable(ui_ctx, t->duration * PIXELS_PER_SECOND); // min 80px. can grow
nk_layout_row_template_end(ui_ctx);
#endif
char *sid = va("%s.%d", id, 0);
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
ui_label(label);
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx);
bounds.y -= 30;
struct nk_rect baseline = bounds; baseline.y += 30/2;
baseline.x += LABEL_SPACING;
baseline.w -= LABEL_SPACING;
// tween duration
{
struct nk_rect pos = baseline;
pos.w = pos.x + t->duration * PIXELS_PER_SECOND;
pos.y -= TIMELINE_HEIGHT/2;
pos.h = TIMELINE_HEIGHT;
nk_stroke_rect(canvas, pos, ROUNDING, THICKNESS*2, AS_NKCOLOR(duration_color));
}
// tween ranges
for(int i = 0, end = array_count(t->keyframes) - 1; i < end; ++i) {
tween_keyframe_t *k = t->keyframes + i;
tween_keyframe_t *next = k + 1;
struct nk_rect pos = baseline;
pos.x += k->t * PIXELS_PER_SECOND;
pos.w = (next->t - k->t) * PIXELS_PER_SECOND;
pos.y -= TIMELINE_HEIGHT/2;
pos.h = TIMELINE_HEIGHT;
char *sid = va("%s.%d", id, i);
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
struct nk_color c = nk_hsva_f(ui_hue, 0.75f, 0.8f, ui_alpha);
nk_fill_rect(canvas, pos, ROUNDING, k->ease == EASE_ZERO ? AS_NKCOLOR(0) : c); // AS_NKCOLOR(track_color));
}
// horizontal line
nk_stroke_line(canvas, baseline.x, baseline.y, baseline.x+baseline.w,baseline.y, THICKNESS, AS_NKCOLOR(base_color));
// unit, 5-unit and 10-unit markers
for( float i = 0, j = 0; i < baseline.w; i += PIXELS_PER_SECOND/10, ++j ) {
int len = !((int)j%10) ? MARKER10_HEIGHT : !((int)j%5) ? MARKER5_HEIGHT : MARKER1_HEIGHT;
nk_stroke_line(canvas, baseline.x+i, baseline.y-len, baseline.x+i, baseline.y+len, THICKNESS, AS_NKCOLOR(base_color));
}
// time marker
float px = t->time * PIXELS_PER_SECOND;
nk_stroke_line(canvas, baseline.x+px, bounds.y, baseline.x+px, bounds.y+bounds.h, THICKNESS*2, AS_NKCOLOR(time_color));
nk_draw_symbol(canvas, NK_SYMBOL_TRIANGLE_DOWN, ((struct nk_rect){ baseline.x+px-4,bounds.y-4-8,8,8}), /*bg*/AS_NKCOLOR(0), /*fg*/AS_NKCOLOR(time_color), 0.f/*border_width*/, ui_ctx->style.font);
// key markers
for each_array_ptr(t->keyframes, tween_keyframe_t, k) {
struct nk_rect pos = baseline;
pos.x += k->t * PIXELS_PER_SECOND;
vec2 romboid[] = {
{pos.x-KEY_WIDTH,pos.y}, {pos.x,pos.y-KEY_HEIGHT},
{pos.x+KEY_WIDTH,pos.y}, {pos.x,pos.y+KEY_HEIGHT}
};
nk_fill_polygon(canvas, (float*)romboid, countof(romboid), AS_NKCOLOR(key_color));
}
// keys ui
if( expand_keys )
for(int i = 0, end = array_count(t->keyframes); i < end; ++i) {
tween_keyframe_t *k = t->keyframes + i;
if( ui_collapse(va("Key %d", i), va("%s.%d", id, i))) {
changed |= ui_float("Time", &k->t);
changed |= ui_float3("Value", &k->v.x);
changed |= ui_list("Ease", ease_enums(), EASE_NUM, &k->ease );
ui_collapse_end();
}
}
return changed;
}
tween_t* rand_tween() {
tween_t demo = tween();
int num_keys = randi(2,8);
double t = 0;
for( int i = 0; i < num_keys; ++i) {
tween_setkey(&demo, t, scale3(vec3(randf(),randf(),randf()),randi(-5,5)), randi(0,EASE_NUM) );
t += randi(1,5) / ((float)(1 << randi(0,2)));
}
tween_t *p = CALLOC(1, sizeof(tween_t));
memcpy(p, &demo, sizeof(tween_t));
return p;
}
int editor_timeline(int window_mode) {
static array(tween_t*) tweens = 0;
do_once {
array_push(tweens, rand_tween());
}
if( editor.t == 0 )
for each_array(tweens, tween_t*,t) {
tween_reset(t);
}
else
for each_array(tweens, tween_t*,t) {
tween_update(t, editor.dt);
}
static void *selected = NULL;
if( editor_begin(TIMELINE_TITLE, window_mode) ) {
int choice = ui_toolbar(ICON_MDI_PLUS ";" ICON_MDI_MINUS );
if( choice == 1 ) array_push(tweens, rand_tween());
if( choice == 2 && selected ) {
int target = -1;
for( int i = 0, end = array_count(tweens); i < end; ++i ) if( tweens[i] == selected ) { target = i; break; }
if( target >= 0 ) { array_erase_slow(tweens, target); selected = NULL; }
}
for each_array(tweens, tween_t*,t) {
ui_tween(va("%s%p@%05.2fs Value: %s", t == selected ? "!":"", t, t->time, ftoa3(t->result)), t);
if(ui_label_icon_clicked_L.x) selected = (t != selected) ? t : NULL;
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_timeline);
}

View File

@ -1,36 +1,36 @@
// Enable more performant GPUs on laptops. Does this work into a dll?
// int NvOptimusEnablement = 1;
// int AmdPowerXpressRequestHighPerformance = 1;
#if is(linux) && is(tcc) // fixes `tcc: error: undefined symbol '__dso_handle'`
int __dso_handle; // compiled with: `tcc demo.c v4k.c -D__STDC_NO_VLA__ -lX11`
#endif
#if is(win32) && is(tcc) // fixes `tcc: error: undefined symbol '_InterlockedExchangeAdd'` when compiling with `-m64` flag
__CRT_INLINE LONG _InterlockedExchangeAdd(LONG volatile *add, LONG val) {
LONG old;
do old = *add; while( InterlockedCompareExchange(add, old + val, old) != old );
return old;
}
__CRT_INLINE LONGLONG _InterlockedExchangeAdd64(LONGLONG volatile *add, LONGLONG val) { // 64bit version, for completeness
LONGLONG old;
do old = *add; while( InterlockedCompareExchange64(add, old + val, old) != old );
return old;
}
#endif
#ifdef ZIG_CC
static int IN6_IS_ADDR_V4MAPPED(const struct in6_addr *a) { return ((a->s6_words[0]==0) && (a->s6_words[1]==0) && (a->s6_words[2]==0) && (a->s6_words[3]==0) && (a->s6_words[4]==0) && (a->s6_words[5]==0xffff)); }
const struct in6_addr in6addr_any; // = IN6ADDR_ANY_INIT;
//static const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
#endif
ifdef(retail, AUTORUN {
fclose(stderr);
fclose(stdout);
const char* null_stream = ifdef(win32, "nul:", "/dev/null");
if (!freopen(null_stream, "a", stdout)) PANIC("cannot recreate standard streams");
if (!freopen(null_stream, "a", stderr)) PANIC("cannot recreate standard streams");
} )
// Enable more performant GPUs on laptops. Does this work into a dll?
// int NvOptimusEnablement = 1;
// int AmdPowerXpressRequestHighPerformance = 1;
#if is(linux) && is(tcc) // fixes `tcc: error: undefined symbol '__dso_handle'`
int __dso_handle; // compiled with: `tcc demo.c v4k.c -D__STDC_NO_VLA__ -lX11`
#endif
#if is(win32) && is(tcc) // fixes `tcc: error: undefined symbol '_InterlockedExchangeAdd'` when compiling with `-m64` flag
__CRT_INLINE LONG _InterlockedExchangeAdd(LONG volatile *add, LONG val) {
LONG old;
do old = *add; while( InterlockedCompareExchange(add, old + val, old) != old );
return old;
}
__CRT_INLINE LONGLONG _InterlockedExchangeAdd64(LONGLONG volatile *add, LONGLONG val) { // 64bit version, for completeness
LONGLONG old;
do old = *add; while( InterlockedCompareExchange64(add, old + val, old) != old );
return old;
}
#endif
#ifdef ZIG_CC
static int IN6_IS_ADDR_V4MAPPED(const struct in6_addr *a) { return ((a->s6_words[0]==0) && (a->s6_words[1]==0) && (a->s6_words[2]==0) && (a->s6_words[3]==0) && (a->s6_words[4]==0) && (a->s6_words[5]==0xffff)); }
const struct in6_addr in6addr_any; // = IN6ADDR_ANY_INIT;
//static const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
#endif
ifdef(retail, AUTORUN {
fclose(stderr);
fclose(stdout);
const char* null_stream = ifdef(win32, "nul:", "/dev/null");
if (!freopen(null_stream, "a", stdout)) PANIC("cannot recreate standard streams");
if (!freopen(null_stream, "a", stderr)) PANIC("cannot recreate standard streams");
} )

View File

@ -1,304 +1,306 @@
// dll ------------------------------------------------------------------------
/* deprecated
#if is(win32)
# include <winsock2.h>
# define dlopen(name,flags) (void*)( (name) ? LoadLibraryA(name) : GetModuleHandleA(NULL))
# define dlsym(handle,symbol) GetProcAddress((HMODULE)(handle), symbol )
# define dlclose(handle) 0
#else
# include <dlfcn.h>
# define dlopen(name,flags) (void*)( (name) ? dlopen(name, flags) : NULL )
# define dlsym(handle,symbol) dlsym( (handle) ? (handle) : ifdef(osx,RTLD_SELF,NULL), symbol )
#endif
*/
void* dll(const char *fname, const char *symbol) {
if( fname && !file_exist(fname) ) {
char *buf, *path = file_path(fname), *base = file_base(fname);
if( file_exist(buf = va("%s%s.dll", path, base)) ||
file_exist(buf = va("%s%s.so", path, base)) ||
file_exist(buf = va("%slib%s.so", path, base)) ||
file_exist(buf = va("%s%s.dylib", path, base)) ) {
fname = (const char *)buf;
} else {
return NULL;
}
}
#if is(win32)
return (void*)GetProcAddress(fname ? LoadLibraryA(fname) : GetModuleHandleA(NULL), symbol);
#else
return dlsym(fname ? dlopen(fname, RTLD_NOW|RTLD_LOCAL) : ifdef(osx, RTLD_SELF, NULL), symbol);
#endif
}
#if 0 // demo: cl demo.c /LD && REM dll
EXPORT int add2(int a, int b) { return a + b; }
int main() { int (*adder)() = dll("demo.dll", "add2"); printf("%d\n", adder(2,3)); }
#endif
// script ---------------------------------------------------------------------
typedef lua_State lua;
// the Lua interpreter(s)
static array(lua*) Ls;
// the **current** Lua interpreter
static lua *L;
#if is(linux)
void luaopen_libv4k(lua_State *L) {}
#endif
static void* script__realloc(void *userdef, void *ptr, size_t osize, size_t nsize) {
(void)userdef;
return ptr = REALLOC( ptr, /* (osize+1) * */ nsize );
}
static int script__traceback(lua_State *L) {
if (!lua_isstring(L, 1)) { // try metamethod if non-string error object
if (lua_isnoneornil(L, 1) ||
!luaL_callmeta(L, 1, "__tostring") ||
!lua_isstring(L, -1))
return 1; // return non-string error object
lua_remove(L, 1); // replace object with result of __tostring metamethod
}
luaL_traceback(L, L, lua_tostring(L, 1), 1);
return 1;
}
static void script__error(lua_State *L, int status) {
if (status != 0) {
const char *errormsg = lua_tostring(L, -1);
PRINTF( "!-- %s\n", errormsg);
lua_pop(L, 1); // remove error message
}
}
static int script__call(lua_State *L, int narg, int clear) {
#if ENABLE_FASTCALL_LUA
lua_call(L, 0, 0);
return 0;
#else
int base = lua_gettop(L) - narg; // function index
lua_pushcfunction(L, script__traceback); // push traceback function
lua_insert(L, base); // put it under chunk and args
int status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
script__error(L, status);
lua_remove(L, base); // remove traceback function
if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); // force gc in case of errors
return status;
#endif
}
void script_bind_function(const char *c_name, void *c_function) {
lua_pushcfunction( L, c_function );
lua_setglobal( L, c_name );
}
void script_call(const char *lua_function) {
lua_getglobal( L, lua_function );
script__call( L, 0, 1 );
}
void script_bind_class(const char *classname, int num, const char **methods, void **functions) {
lua_newtable( L );
for( int i = 0; i < num; ++i) {
lua_pushcfunction( L, functions[i] );
lua_setfield( L, 1, methods[i] );
}
lua_setglobal( L, classname );
}
void script_run(const char *script) {
int ret = luaL_dostring(L, script);
if( ret != LUA_OK ) {
PRINTF("!Script failed to run: %s\n", lua_tostring(L, -1));
lua_pop(L, 1); // pop error message
}
}
void script_runfile(const char *pathfile) {
PRINTF( "Loading script '%s'\n", pathfile );
int loadResult = luaL_loadfile( L, pathfile );
/**/ if( loadResult == LUA_OK ) {
script__call( L, 0, 1 );
}
else if( loadResult == LUA_ERRSYNTAX ) {
PRINTF("!Script failed to load (LUA_ERRSYNTAX, '%s'): %s\n", lua_tostring( L, 1 ), pathfile );
// lua_pop(L, 1); // pop error message
}
else if( loadResult == LUA_ERRMEM ) {
PRINTF("!Script failed to load (LUA_ERRMEM): %s\n", pathfile);
}
else {
PRINTF("!Script failed to load: %s\n", pathfile );
}
}
// syntax sugars
/* usage:
int window_create_lua(lua *L) {
window_create(arg_float(1), arg_int(2));
return_void(0);
}
int window_swap_lua(lua *L) {
int r = window_swap();
return_int(r);
}
*/
#define arg_int(nth) lua_tointeger(L, nth)
#define arg_bool(nth) lua_toboolean(L, nth)
#define arg__Bool(nth) lua_toboolean(L, nth)
#define arg_float(nth) (float)lua_tonumber(L, nth)
#define arg_double(nth) lua_tonumber(L, nth)
#define arg_string(nth) lua_tolstring(L, nth, 0)
#define return_void(x) return ((x), 0)
#define return_bool(x) return (lua_pushboolean(L, x), 1)
#define return__Bool(x) return (lua_pushboolean(L, x), 1)
#define return_int(x) return (lua_pushinteger(L, x), 1)
#define return_float(x) return (lua_pushnumber(L, x), 1)
#define return_double(x) return (lua_pushnumber(L, x), 1)
#define return_string(x) return (lua_pushstring(L, x), 1)
#define WRAP_ALL(...) EXPAND(WRAP_ALL, __VA_ARGS__)
#define WRAP_ALL2(rc, func) int func##_lua(lua*L) { return_##rc(func()); }
#define WRAP_ALL3(rc, func, a1) int func##_lua(lua*L) { return_##rc(func(arg_##a1(1))); }
#define WRAP_ALL4(rc, func, a1,a2) int func##_lua(lua*L) { return_##rc(func(arg_##a1(1),arg_##a2(2))); }
#define BIND_ALL(...) EXPAND(BIND_ALL, __VA_ARGS__);
#define BIND_ALL2(rc,func) script_bind_function(#func, func##_lua)
#define BIND_ALL3(rc,func,a1) script_bind_function(#func, func##_lua)
#define BIND_ALL4(rc,func,a1,a2) script_bind_function(#func, func##_lua)
#define XMACRO(X) /* @fixme: add all remaining V4K functions */ \
X(bool, window_create, float, int ) \
X(bool, window_swap ) \
X(void, ddraw_grid, float ) \
X(bool, ui_panel, string, int ) \
X(bool, ui_notify, string, string ) \
X(void, ui_panel_end )
XMACRO(WRAP_ALL)
void script_quit(void) {
if( L ) {
lua_close(L);
L = 0;
}
}
void script_init() {
if( !L ) {
// v4k_init();
// initialize Lua
L = lua_newstate(script__realloc, 0); // L = luaL_newstate();
// load various Lua libraries
luaL_openlibs(L);
luaopen_base(L);
luaopen_table(L);
luaopen_io(L);
luaopen_string(L);
luaopen_math(L);
// enable ffi (via luaffi)
luaopen_ffi(L);
// @fixme: workaround that prevents script binding on lua 5.4.3 on top of luajit 2.1.0-beta3 on linux. lua_setglobal() crashing when accessing null L->l_G
if(L->l_G) {
XMACRO(BIND_ALL);
}
atexit(script_quit);
}
}
bool script_tests() {
// script test (lua)
script_run( "-- Bye.lua\nio.write(\"script test: Bye world!, from \", _VERSION, \"\\n\")" );
return true;
}
#undef XMACRO
// script v2 ------------------------------------------------------------------
#define luaL_dostringsafe(L, str) \
luaL_dostring(L, \
"xpcall(function()\n" \
str \
"end, function(err)\n" \
" print('Error: ' .. tostring(err))\n" \
" print(debug.traceback(nil, 2))\n" \
" if core and core.on_error then\n" \
" pcall(core.on_error, err)\n" \
" end\n" \
" os.exit(1)\n" \
"end)" \
);
static int f_vfs_read(lua_State *L) {
char *file = file_normalize(luaL_checkstring(L, 1));
if( strbegi(file, app_path()) ) file += strlen(app_path());
strswap(file+1, ".", "/");
strswap(file+1, "/lua", ".lua");
int len; char *data = vfs_load(file, &len);
if( len ) {
data = memcpy(MALLOC(len+1), data, len), data[len] = 0;
//tty_color(data ? GREEN : RED);
//printf("%s (%s)\n%s", file, data ? "ok" : "failed", data);
//tty_color(0);
}
return lua_pushstring(L, data), 1; // "\n\tcannot find `%s` within mounted zipfiles", file), 1;
}
// add our zip loader at the end of package.loaders
void lua_add_ziploader(lua_State* L) {
lua_pushcfunction( L, f_vfs_read );
lua_setglobal( L, "vfs_read" );
luaL_dostringsafe(L,
// "package.path = [[;<?>;<<?.lua>>;]]\n" // .. package.path\n"
"package.searchers[#package.searchers + 1] = function(libraryname)\n"
" for pattern in string.gmatch( package.path, '[^;]+' ) do\n"
" local proper_path = string.gsub(pattern, '?', libraryname)\n"
" local f = vfs_read(proper_path)\n"
" if f ~= nil then\n"
" return load(f, proper_path)\n"
" end\n"
" end\n"
" return nil\n"
"end\n"
);
}
void *script_init_env(unsigned flags) {
if( flags & SCRIPT_LUA ) {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if( flags & SCRIPT_DEBUGGER ) {
// Register debuggers/inspectors
// luaL_dostringsafe(L, "I = require('inspect').inspect\n");
dbg_setup_default(L);
}
lua_add_ziploader(L);
return L;
}
return 0;
}
bool script_push(void *env) {
array_push(Ls, L = env);
return true;
}
bool script_pop() {
L = array_count(Ls) && (array_pop(Ls), array_count(Ls)) ? *array_back(Ls) : NULL;
return !!array_count(Ls);
}
// dll ------------------------------------------------------------------------
/* deprecated
#if is(win32)
# include <winsock2.h>
# define dlopen(name,flags) (void*)( (name) ? LoadLibraryA(name) : GetModuleHandleA(NULL))
# define dlsym(handle,symbol) GetProcAddress((HMODULE)(handle), symbol )
# define dlclose(handle) 0
#else
# include <dlfcn.h>
# define dlopen(name,flags) (void*)( (name) ? dlopen(name, flags) : NULL )
# define dlsym(handle,symbol) dlsym( (handle) ? (handle) : ifdef(osx,RTLD_SELF,NULL), symbol )
#endif
*/
void* dll(const char *fname, const char *symbol) {
if( fname && !file_exist(fname) ) {
char *buf, *path = file_path(fname), *base = file_base(fname);
if( file_exist(buf = va("%s%s.dll", path, base)) ||
file_exist(buf = va("%s%s.so", path, base)) ||
file_exist(buf = va("%slib%s.so", path, base)) ||
file_exist(buf = va("%s%s.dylib", path, base)) ) {
fname = (const char *)buf;
} else {
return NULL;
}
}
#if is(win32)
return (void*)GetProcAddress(fname ? LoadLibraryA(fname) : GetModuleHandleA(NULL), symbol);
#else
return dlsym(fname ? dlopen(fname, RTLD_NOW|RTLD_LOCAL) : ifdef(osx, RTLD_SELF, NULL), symbol);
#endif
}
#if 0 // demo: cl demo.c /LD && REM dll
EXPORT int add2(int a, int b) { return a + b; }
int main() { int (*adder)() = dll("demo.dll", "add2"); printf("%d\n", adder(2,3)); }
#endif
// script ---------------------------------------------------------------------
typedef lua_State lua;
// the Lua interpreter(s)
static array(lua*) Ls;
// the **current** Lua interpreter
static lua *L;
#if is(linux)
void luaopen_libv4k(lua_State *L) {}
#endif
static void* script__realloc(void *userdef, void *ptr, size_t osize, size_t nsize) {
(void)userdef;
return ptr = REALLOC( ptr, /* (osize+1) * */ nsize );
}
static int script__traceback(lua_State *L) {
if (!lua_isstring(L, 1)) { // try metamethod if non-string error object
if (lua_isnoneornil(L, 1) ||
!luaL_callmeta(L, 1, "__tostring") ||
!lua_isstring(L, -1))
return 1; // return non-string error object
lua_remove(L, 1); // replace object with result of __tostring metamethod
}
luaL_traceback(L, L, lua_tostring(L, 1), 1);
return 1;
}
static void script__error(lua_State *L, int status) {
if (status != 0) {
const char *errormsg = lua_tostring(L, -1);
PRINTF( "!-- %s\n", errormsg);
lua_pop(L, 1); // remove error message
}
}
static int script__call(lua_State *L, int narg, int clear) {
#if ENABLE_FASTCALL_LUA
lua_call(L, 0, 0);
return 0;
#else
int base = lua_gettop(L) - narg; // function index
lua_pushcfunction(L, script__traceback); // push traceback function
lua_insert(L, base); // put it under chunk and args
int status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
script__error(L, status);
lua_remove(L, base); // remove traceback function
if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); // force gc in case of errors
return status;
#endif
}
void script_bind_function(const char *c_name, void *c_function) {
lua_pushcfunction( L, c_function );
lua_setglobal( L, c_name );
}
void script_call(const char *lua_function) {
lua_getglobal( L, lua_function );
script__call( L, 0, 1 );
}
void script_bind_class(const char *classname, int num, const char **methods, void **functions) {
lua_newtable( L );
for( int i = 0; i < num; ++i) {
lua_pushcfunction( L, functions[i] );
lua_setfield( L, 1, methods[i] );
}
lua_setglobal( L, classname );
}
void script_run(const char *script) {
int ret = luaL_dostring(L, script);
if( ret != LUA_OK ) {
PRINTF("!Script failed to run: %s\n", lua_tostring(L, -1));
lua_pop(L, 1); // pop error message
}
}
void script_runfile(const char *pathfile) {
PRINTF( "Loading script '%s'\n", pathfile );
int loadResult = luaL_loadfile( L, pathfile );
/**/ if( loadResult == LUA_OK ) {
script__call( L, 0, 1 );
}
else if( loadResult == LUA_ERRSYNTAX ) {
PRINTF("!Script failed to load (LUA_ERRSYNTAX, '%s'): %s\n", lua_tostring( L, 1 ), pathfile );
// lua_pop(L, 1); // pop error message
}
else if( loadResult == LUA_ERRMEM ) {
PRINTF("!Script failed to load (LUA_ERRMEM): %s\n", pathfile);
}
else {
PRINTF("!Script failed to load: %s\n", pathfile );
}
}
// syntax sugars
/* usage:
int window_create_lua(lua *L) {
window_create(arg_float(1), arg_int(2));
return_void(0);
}
int window_swap_lua(lua *L) {
int r = window_swap();
return_int(r);
}
*/
#define arg_int(nth) lua_tointeger(L, nth)
#define arg_bool(nth) lua_toboolean(L, nth)
#define arg__Bool(nth) lua_toboolean(L, nth)
#define arg_float(nth) (float)lua_tonumber(L, nth)
#define arg_double(nth) lua_tonumber(L, nth)
#define arg_string(nth) lua_tolstring(L, nth, 0)
#define return_void(x) return ((x), 0)
#define return_bool(x) return (lua_pushboolean(L, x), 1)
#define return__Bool(x) return (lua_pushboolean(L, x), 1)
#define return_int(x) return (lua_pushinteger(L, x), 1)
#define return_float(x) return (lua_pushnumber(L, x), 1)
#define return_double(x) return (lua_pushnumber(L, x), 1)
#define return_string(x) return (lua_pushstring(L, x), 1)
#define WRAP_ALL(...) EXPAND(WRAP_ALL, __VA_ARGS__)
#define WRAP_ALL2(rc, func) int func##_lua(lua*L) { return_##rc(func()); }
#define WRAP_ALL3(rc, func, a1) int func##_lua(lua*L) { return_##rc(func(arg_##a1(1))); }
#define WRAP_ALL4(rc, func, a1,a2) int func##_lua(lua*L) { return_##rc(func(arg_##a1(1),arg_##a2(2))); }
#define BIND_ALL(...) EXPAND(BIND_ALL, __VA_ARGS__);
#define BIND_ALL2(rc,func) script_bind_function(#func, func##_lua)
#define BIND_ALL3(rc,func,a1) script_bind_function(#func, func##_lua)
#define BIND_ALL4(rc,func,a1,a2) script_bind_function(#func, func##_lua)
#define XMACRO(X) /* @fixme: add all remaining V4K functions */ \
X(bool, window_create, float, int ) \
X(bool, window_swap ) \
X(void, ddraw_grid, float ) \
X(bool, ui_panel, string, int ) \
X(bool, ui_notify, string, string ) \
X(void, ui_panel_end )
XMACRO(WRAP_ALL)
void script_quit(void) {
if( L ) {
lua_close(L);
L = 0;
}
}
void script_init() {
if( !L ) {
// v4k_init();
// initialize Lua
L = lua_newstate(script__realloc, 0); // L = luaL_newstate();
// load various Lua libraries
luaL_openlibs(L);
luaopen_base(L);
luaopen_table(L);
luaopen_io(L);
luaopen_string(L);
luaopen_math(L);
#if !is(ems)
// enable ffi (via luaffi)
luaopen_ffi(L);
#endif
// @fixme: workaround that prevents script binding on lua 5.4.3 on top of luajit 2.1.0-beta3 on linux. lua_setglobal() crashing when accessing null L->l_G
if(L->l_G) {
XMACRO(BIND_ALL);
}
atexit(script_quit);
}
}
bool script_tests() {
// script test (lua)
script_run( "-- Bye.lua\nio.write(\"script test: Bye world!, from \", _VERSION, \"\\n\")" );
return true;
}
#undef XMACRO
// script v2 ------------------------------------------------------------------
#define luaL_dostringsafe(L, str) \
luaL_dostring(L, \
"xpcall(function()\n" \
str \
"end, function(err)\n" \
" print('Error: ' .. tostring(err))\n" \
" print(debug.traceback(nil, 2))\n" \
" if core and core.on_error then\n" \
" pcall(core.on_error, err)\n" \
" end\n" \
" os.exit(1)\n" \
"end)" \
);
static int f_vfs_read(lua_State *L) {
char *file = file_normalize(luaL_checkstring(L, 1));
if( strbegi(file, app_path()) ) file += strlen(app_path());
strswap(file+1, ".", "/");
strswap(file+1, "/lua", ".lua");
int len; char *data = vfs_load(file, &len);
if( len ) {
data = memcpy(MALLOC(len+1), data, len), data[len] = 0;
//tty_color(data ? GREEN : RED);
//printf("%s (%s)\n%s", file, data ? "ok" : "failed", data);
//tty_color(0);
}
return lua_pushstring(L, data), 1; // "\n\tcannot find `%s` within mounted zipfiles", file), 1;
}
// add our zip loader at the end of package.loaders
void lua_add_ziploader(lua_State* L) {
lua_pushcfunction( L, f_vfs_read );
lua_setglobal( L, "vfs_read" );
luaL_dostringsafe(L,
// "package.path = [[;<?>;<<?.lua>>;]]\n" // .. package.path\n"
"package.searchers[#package.searchers + 1] = function(libraryname)\n"
" for pattern in string.gmatch( package.path, '[^;]+' ) do\n"
" local proper_path = string.gsub(pattern, '?', libraryname)\n"
" local f = vfs_read(proper_path)\n"
" if f ~= nil then\n"
" return load(f, proper_path)\n"
" end\n"
" end\n"
" return nil\n"
"end\n"
);
}
void *script_init_env(unsigned flags) {
if( flags & SCRIPT_LUA ) {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if( flags & SCRIPT_DEBUGGER ) {
// Register debuggers/inspectors
// luaL_dostringsafe(L, "I = require('inspect').inspect\n");
dbg_setup_default(L);
}
lua_add_ziploader(L);
return L;
}
return 0;
}
bool script_push(void *env) {
array_push(Ls, L = env);
return true;
}
bool script_pop() {
L = array_count(Ls) && (array_pop(Ls), array_count(Ls)) ? *array_back(Ls) : NULL;
return !!array_count(Ls);
}

View File

@ -1,34 +1,34 @@
// dll ------------------------------------------------------------------------
/// !!! `filename` must contain extension
/// load dynamic library `file` and search for `symbol`
/// return: NULL if not found, found symbol otherwise.
/// filename: path to dynamic library file. must contain extension.
/// symbol: symbol name. must not be NULL.
/// see: dlopen^, dlclose^
/// > bool (*plugin_init)(void) = dll("plugin.dll", "init");
/// > assert(plugin_init());
API void* dll(const char *filename, const char *symbol);
// -----------------------------------------------------------------------------
// script framework
enum {
SCRIPT_LUA = 1,
SCRIPT_DEBUGGER = 2,
};
API void script_init(); // @deprecate
API void *script_init_env(unsigned flags);
API bool script_push(void *env);
API void script_run(const char *script);
API void script_runfile(const char *pathfile);
API void script_bind_class(const char *objname, int num_methods, const char **c_names, void **c_functions);
API void script_bind_function(const char *c_name, void *c_function);
API void script_call(const char *lua_function);
API bool script_tests();
API bool script_pop();
// dll ------------------------------------------------------------------------
/// !!! `filename` must contain extension
/// load dynamic library `file` and search for `symbol`
/// return: NULL if not found, found symbol otherwise.
/// filename: path to dynamic library file. must contain extension.
/// symbol: symbol name. must not be NULL.
/// see: dlopen^, dlclose^
/// > bool (*plugin_init)(void) = dll("plugin.dll", "init");
/// > assert(plugin_init());
API void* dll(const char *filename, const char *symbol);
// -----------------------------------------------------------------------------
// script framework
enum {
SCRIPT_LUA = 1,
SCRIPT_DEBUGGER = 2,
};
API void script_init(); // @deprecate
API void *script_init_env(unsigned flags);
API bool script_push(void *env);
API void script_run(const char *script);
API void script_runfile(const char *pathfile);
API void script_bind_class(const char *objname, int num_methods, const char **c_names, void **c_functions);
API void script_bind_function(const char *c_name, void *c_function);
API void script_call(const char *lua_function);
API bool script_tests();
API bool script_pop();

File diff suppressed because it is too large Load Diff

View File

@ -1,98 +1,98 @@
// -----------------------------------------------------------------------------
// files, cache and virtual filesystem (registered directories and/or compressed zip archives).
// - rlyeh, public domain.
//
// - note: vfs_mount() order matters (last mounts have higher priority).
// - note: directory/with/trailing/slash/ as mount_point, or zip/tar/pak archive otherwise.
//
// @todo: file_mmap
// @todo: file_find() from first file_scan()
// physical filesystem. files
API array(char*) file_list( const char *pathmasks ); // folder/*.ico;**.png;*.c
API bool file_write( const char *file, const void *ptr, int len );
API bool file_append( const char *file, const void *ptr, int len );
API char * file_read(const char *filename);
API char * file_load(const char *filename, int *len);
API uint64_t file_size(const char *pathfile);
API bool file_directory(const char *pathfile);
API char * file_pathabs(const char *pathfile); // ../dir/./file.ext -> c:/prj/dir/file.ext
API char * file_path(const char *pathfile); // c:/prj/dir/file.ext -> c:/prj/dir/
API char * file_name(const char *pathfile); // c:/prj/dir/file.ext -> file.ext
API char * file_base(const char *pathfile); // c:/prj/dir/file.ext -> file
API char * file_ext(const char *pathfile); // c:/prj/dir/file.ext -> .ext
API char * file_id(const char *pathfile); // c:/prj/dir/file.ext -> file/dir/prj (name then alphabetical)
API char * file_normalize(const char *pathfile); // c:/prj/dir/file.ext -> c/prj/dir/file_ext
//API char * file_normalize_with_folder(const char *pathfile); // c:/prj/dir/file.ext -> dir/file_ext
API char * file_counter(const char *pathfile); // in: v4k.ini -> out: v4k(001).ini -> out: v4k(002).ini [-> etc...]
API uint64_t file_stamp(const char *pathfile); // 1616153596 (seconds since unix epoch)
API uint64_t file_stamp10(const char *pathfile); // 20210319113316 (absolute datetime in base10)
API bool file_exist(const char *pathfile);
API bool file_delete(const char *pathfile);
API bool file_copy(const char *src, const char *dst);
API bool file_move(const char *src, const char *dst);
API FILE* file_temp();
API char* file_tempname();
API void* file_md5(const char *file); // 16 bytes
API void* file_sha1(const char *file); // 20 bytes
API void* file_crc32(const char *file); // 4 bytes
// compressed files
API array(char*) file_zip_list(const char *zipfile);
API array(char) file_zip_extract(const char *zipfile, const char *filename);
API bool file_zip_append(const char *zipfile, const char *filename, int clevel);
API bool file_zip_appendmem(const char *zipfile, const char *entryname, const void *ptr, unsigned len, int clevel);
// storage (emscripten only)
// Mounts local storage folder for writing. Useful for Emscripten only. @path_folder: "/save" for example
// Reads local storage to memory. Usually call it one time only, after mount. Useful for Emscripten only.
// Writes memory contents to local storage. Usually call it after all fclose
API void storage_mount(const char* folder);
API void storage_read();
API void storage_flush();
// virtual filesystem
API bool vfs_mount(const char *mount_point);
API array(char*) vfs_list(const char *masks); // **.png;*.c
API char * vfs_read(const char *pathfile);
API char * vfs_load(const char *pathfile, int *size);
API int vfs_size(const char *pathfile);
API void vfs_reload();
API const char * vfs_resolve(const char *fuzzyname); // guess best match. @todo: fuzzy path
//API const char*vfs_extract(const char *pathfile); // extracts vfs file into local filesystem (temporary file), so it can be read by foreign/3rd party libs
API FILE* vfs_handle(const char *pathfile); // same as above, but returns file handle instead. preferred way, will clean descriptors at exit
// cache
API void * cache_insert(const char *key, void *value, int size);
API void * cache_lookup(const char *key, int *size);
// ini
// @todo: evaluate alt api #1
// char *ini(filename, section.key, default);
// float inif(filename, section.key, default);
// @todo: evaluate alt api #2
// char *val = ini(filename, section_key);
// int count = ini_count(filename);
// char *key = ini_key_id(filename, id);
// char *val = ini_val_id(filename, id);
typedef map(char*,char*) ini_t;
API ini_t ini(const char *filename);
API ini_t ini_from_mem(const char *data);
API void ini_destroy(ini_t);
API bool ini_write(const char *filename, const char *section, const char *key, const char *value);
// -----------------------------------------------------------------------------
// files, cache and virtual filesystem (registered directories and/or compressed zip archives).
// - rlyeh, public domain.
//
// - note: vfs_mount() order matters (last mounts have higher priority).
// - note: directory/with/trailing/slash/ as mount_point, or zip/tar/pak archive otherwise.
//
// @todo: file_mmap
// @todo: file_find() from first file_scan()
// physical filesystem. files
API array(char*) file_list( const char *pathmasks ); // folder/*.ico;**.png;*.c
API bool file_write( const char *file, const void *ptr, int len );
API bool file_append( const char *file, const void *ptr, int len );
API char * file_read(const char *filename);
API char * file_load(const char *filename, int *len);
API uint64_t file_size(const char *pathfile);
API bool file_directory(const char *pathfile);
API char * file_pathabs(const char *pathfile); // ../dir/./file.ext -> c:/prj/dir/file.ext
API char * file_path(const char *pathfile); // c:/prj/dir/file.ext -> c:/prj/dir/
API char * file_name(const char *pathfile); // c:/prj/dir/file.ext -> file.ext
API char * file_base(const char *pathfile); // c:/prj/dir/file.ext -> file
API char * file_ext(const char *pathfile); // c:/prj/dir/file.ext -> .ext
API char * file_id(const char *pathfile); // c:/prj/dir/file.ext -> file/dir/prj (name then alphabetical)
API char * file_normalize(const char *pathfile); // c:/prj/dir/file.ext -> c/prj/dir/file_ext
//API char * file_normalize_with_folder(const char *pathfile); // c:/prj/dir/file.ext -> dir/file_ext
API char * file_counter(const char *pathfile); // in: v4k.ini -> out: v4k(001).ini -> out: v4k(002).ini [-> etc...]
API uint64_t file_stamp(const char *pathfile); // 1616153596 (seconds since unix epoch)
API uint64_t file_stamp10(const char *pathfile); // 20210319113316 (absolute datetime in base10)
API bool file_exist(const char *pathfile);
API bool file_delete(const char *pathfile);
API bool file_copy(const char *src, const char *dst);
API bool file_move(const char *src, const char *dst);
API FILE* file_temp();
API char* file_tempname();
API void* file_md5(const char *file); // 16 bytes
API void* file_sha1(const char *file); // 20 bytes
API void* file_crc32(const char *file); // 4 bytes
// compressed files
API array(char*) file_zip_list(const char *zipfile);
API array(char) file_zip_extract(const char *zipfile, const char *filename);
API bool file_zip_append(const char *zipfile, const char *filename, int clevel);
API bool file_zip_appendmem(const char *zipfile, const char *entryname, const void *ptr, unsigned len, int clevel);
// storage (emscripten only)
// Mounts local storage folder for writing. Useful for Emscripten only. @path_folder: "/save" for example
// Reads local storage to memory. Usually call it one time only, after mount. Useful for Emscripten only.
// Writes memory contents to local storage. Usually call it after all fclose
API void storage_mount(const char* folder);
API void storage_read();
API void storage_flush();
// virtual filesystem
API bool vfs_mount(const char *mount_point);
API array(char*) vfs_list(const char *masks); // **.png;*.c
API char * vfs_read(const char *pathfile);
API char * vfs_load(const char *pathfile, int *size);
API int vfs_size(const char *pathfile);
API void vfs_reload();
API const char * vfs_resolve(const char *fuzzyname); // guess best match. @todo: fuzzy path
//API const char*vfs_extract(const char *pathfile); // extracts vfs file into local filesystem (temporary file), so it can be read by foreign/3rd party libs
API FILE* vfs_handle(const char *pathfile); // same as above, but returns file handle instead. preferred way, will clean descriptors at exit
// cache
API void * cache_insert(const char *key, void *value, int size);
API void * cache_lookup(const char *key, int *size);
// ini
// @todo: evaluate alt api #1
// char *ini(filename, section.key, default);
// float inif(filename, section.key, default);
// @todo: evaluate alt api #2
// char *val = ini(filename, section_key);
// int count = ini_count(filename);
// char *key = ini_key_id(filename, id);
// char *val = ini_val_id(filename, id);
typedef map(char*,char*) ini_t;
API ini_t ini(const char *filename);
API ini_t ini_from_mem(const char *data);
API void ini_destroy(ini_t);
API bool ini_write(const char *filename, const char *section, const char *key, const char *value);

File diff suppressed because it is too large Load Diff

View File

@ -1,102 +1,102 @@
// -----------------------------------------------------------------------------
// font framework
// - rlyeh, public domain
// font size tags
#define FONT_H1 "\1" // largest
#define FONT_H2 "\2"
#define FONT_H3 "\3"
#define FONT_H4 "\4"
#define FONT_H5 "\5"
#define FONT_H6 "\6" // smallest
// font color tags
#define FONT_COLOR1 "\x1a"
#define FONT_COLOR2 "\x1b"
#define FONT_COLOR3 "\x1c"
#define FONT_COLOR4 "\x1d"
#define FONT_COLOR5 "\x1e"
#define FONT_COLOR6 "\x1f"
// font face tags
#define FONT_FACE1 "\x10"
#define FONT_FACE2 "\x11"
#define FONT_FACE3 "\x12"
#define FONT_FACE4 "\x13"
#define FONT_FACE5 "\x14"
#define FONT_FACE6 "\x15"
#define FONT_FACE7 "\x16"
#define FONT_FACE8 "\x17" // editor may override this one
#define FONT_FACE9 "\x18" // editor may override this one
#define FONT_FACE10 "\x19" // editor may override this one
// font align tags
#define FONT_LEFT "\\<"
#define FONT_CENTER "\\|"
#define FONT_JUSTIFY "\\$"
#define FONT_RIGHT "\\>"
#define FONT_TOP "\\^"
#define FONT_MIDDLE "\\-"
#define FONT_BASELINE "\\_"
#define FONT_BOTTOM "\\v"
// font flags
enum FONT_FLAGS {
// font atlas size
FONT_512 = 0x0,
FONT_1024 = 0x1,
FONT_2048 = 0x2,
FONT_4096 = 0x4,
// font oversampling
FONT_NO_OVERSAMPLE = 0x0,
FONT_OVERSAMPLE_X = 0x08,
FONT_OVERSAMPLE_Y = 0x10,
// unicode ranges
FONT_ASCII = 0x800, // Compatible charset
FONT_AR = 0x001000, // Arabic and Arabic-Indic digits
FONT_ZH = 0x002000, // Chinese Simplified (@todo: add ZH_FULL)
FONT_EL = 0x004000, // Greek, Coptic, modern Georgian, Svan, Mingrelian, Ancient Greek
FONT_EM = 0x008000, // Emoji
FONT_EU = 0x010000, // Eastern/western Europe, IPA, Latin ext A/B
FONT_HE = 0x020000, // Hebrew, Yiddish, Ladino, and other diaspora languages
FONT_JP = 0x040000, // Hiragana, Katakana, Punctuations, Half-width chars
FONT_KR = 0x080000, // Korean, Hangul
FONT_RU = 0x100000, // Cyrillic + ext A/B
FONT_TH = 0x200000, // Thai
FONT_VI = 0x400000, // Vietnamese
FONT_CJK = FONT_ZH|FONT_JP|FONT_KR,
// FONT_DEFAULTS = FONT_512 | FONT_NO_OVERSAMPLE | FONT_ASCII,
};
typedef struct font_metrics_t {
float ascent; // max distance above baseline for all glyphs
float descent; // max distance below baseline for all glyphs
float linegap; // distance between ascent of next line and descent of current line
float linedist; // distance between the baseline of two lines (ascent - descent + linegap)
} font_metrics_t;
// configures
API void font_face(const char *face_tag, const char *filename_ttf, float font_size, unsigned flags);
API void font_face_from_mem(const char *tag, const void *ttf_buffer, unsigned ttf_len, float font_size, unsigned flags);
API void font_scale(const char *face_tag, int scale_index, float value);
API void font_scales(const char *face_tag, float h1, float h2, float h3, float h4, float h5, float h6);
API void font_color(const char *color_tag, uint32_t color);
// commands
API vec2 font_xy();
API void font_goto(float x, float y);
API vec2 font_print(const char *text);
API vec2 font_clip(const char *text, vec4 rect);
API const char* font_wrap(const char *text, float max_width);
API vec2 font_rect(const char *text);
API font_metrics_t font_metrics(const char *text);
// syntax highlighting
API void* font_colorize(const char *text, const char *comma_types, const char *comma_keywords); // comma separated tokens. expensive, please cache result.
API vec2 font_highlight(const char *text, const void *colors);
// ui
API void ui_font();
// -----------------------------------------------------------------------------
// font framework
// - rlyeh, public domain
// font size tags
#define FONT_H1 "\1" // largest
#define FONT_H2 "\2"
#define FONT_H3 "\3"
#define FONT_H4 "\4"
#define FONT_H5 "\5"
#define FONT_H6 "\6" // smallest
// font color tags
#define FONT_COLOR1 "\x1a"
#define FONT_COLOR2 "\x1b"
#define FONT_COLOR3 "\x1c"
#define FONT_COLOR4 "\x1d"
#define FONT_COLOR5 "\x1e"
#define FONT_COLOR6 "\x1f"
// font face tags
#define FONT_FACE1 "\x10"
#define FONT_FACE2 "\x11"
#define FONT_FACE3 "\x12"
#define FONT_FACE4 "\x13"
#define FONT_FACE5 "\x14"
#define FONT_FACE6 "\x15"
#define FONT_FACE7 "\x16"
#define FONT_FACE8 "\x17" // editor may override this one
#define FONT_FACE9 "\x18" // editor may override this one
#define FONT_FACE10 "\x19" // editor may override this one
// font align tags
#define FONT_LEFT "\\<"
#define FONT_CENTER "\\|"
#define FONT_JUSTIFY "\\$"
#define FONT_RIGHT "\\>"
#define FONT_TOP "\\^"
#define FONT_MIDDLE "\\-"
#define FONT_BASELINE "\\_"
#define FONT_BOTTOM "\\v"
// font flags
enum FONT_FLAGS {
// font atlas size
FONT_512 = 0x0,
FONT_1024 = 0x1,
FONT_2048 = 0x2,
FONT_4096 = 0x4,
// font oversampling
FONT_NO_OVERSAMPLE = 0x0,
FONT_OVERSAMPLE_X = 0x08,
FONT_OVERSAMPLE_Y = 0x10,
// unicode ranges
FONT_ASCII = 0x800, // Compatible charset
FONT_AR = 0x001000, // Arabic and Arabic-Indic digits
FONT_ZH = 0x002000, // Chinese Simplified (@todo: add ZH_FULL)
FONT_EL = 0x004000, // Greek, Coptic, modern Georgian, Svan, Mingrelian, Ancient Greek
FONT_EM = 0x008000, // Emoji
FONT_EU = 0x010000, // Eastern/western Europe, IPA, Latin ext A/B
FONT_HE = 0x020000, // Hebrew, Yiddish, Ladino, and other diaspora languages
FONT_JP = 0x040000, // Hiragana, Katakana, Punctuations, Half-width chars
FONT_KR = 0x080000, // Korean, Hangul
FONT_RU = 0x100000, // Cyrillic + ext A/B
FONT_TH = 0x200000, // Thai
FONT_VI = 0x400000, // Vietnamese
FONT_CJK = FONT_ZH|FONT_JP|FONT_KR,
// FONT_DEFAULTS = FONT_512 | FONT_NO_OVERSAMPLE | FONT_ASCII,
};
typedef struct font_metrics_t {
float ascent; // max distance above baseline for all glyphs
float descent; // max distance below baseline for all glyphs
float linegap; // distance between ascent of next line and descent of current line
float linedist; // distance between the baseline of two lines (ascent - descent + linegap)
} font_metrics_t;
// configures
API void font_face(const char *face_tag, const char *filename_ttf, float font_size, unsigned flags);
API void font_face_from_mem(const char *tag, const void *ttf_buffer, unsigned ttf_len, float font_size, unsigned flags);
API void font_scale(const char *face_tag, int scale_index, float value);
API void font_scales(const char *face_tag, float h1, float h2, float h3, float h4, float h5, float h6);
API void font_color(const char *color_tag, uint32_t color);
// commands
API vec2 font_xy();
API void font_goto(float x, float y);
API vec2 font_print(const char *text);
API vec2 font_clip(const char *text, vec4 rect);
API const char* font_wrap(const char *text, float max_width);
API vec2 font_rect(const char *text);
API font_metrics_t font_metrics(const char *text);
// syntax highlighting
API void* font_colorize(const char *text, const char *comma_types, const char *comma_keywords); // comma separated tokens. expensive, please cache result.
API vec2 font_highlight(const char *text, const void *colors);
// ui
API void ui_font();

View File

@ -1,484 +1,484 @@
// ----------------------------------------------------------------------------
// game ui (utils)
API void gui_drawrect( texture_t spritesheet, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end );
#define v42v2(rect) vec2(rect.x,rect.y), vec2(rect.z,rect.w)
void gui_drawrect( texture_t texture, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end ) {
static renderstate_t rect_rs;
do_once {
rect_rs = renderstate();
rect_rs.depth_test_enabled = false;
rect_rs.blend_enabled = true;
rect_rs.blend_src = GL_SRC_ALPHA;
rect_rs.blend_dst = GL_ONE_MINUS_SRC_ALPHA;
rect_rs.front_face = GL_CW;
}
static int program = -1, vbo = -1, vao = -1, u_tint = -1, u_has_tex = -1, u_window_width = -1, u_window_height = -1;
float gamma = 1;
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
if( program < 0 ) {
const char* vs = vfs_read("shaders/rect_2d.vs");
const char* fs = vfs_read("shaders/rect_2d.fs");
program = shader(vs, fs, "", "fragcolor" , NULL);
ASSERT(program > 0);
u_tint = glGetUniformLocation(program, "u_tint");
u_has_tex = glGetUniformLocation(program, "u_has_tex");
u_window_width = glGetUniformLocation(program, "u_window_width");
u_window_height = glGetUniformLocation(program, "u_window_height");
glGenVertexArrays( 1, (GLuint*)&vao );
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
}
start = mul2(start, dpi);
end = mul2(end, dpi);
renderstate_apply(&rect_rs);
GLenum texture_type = texture.flags & TEXTURE_ARRAY ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
glUseProgram( program );
glBindVertexArray( vao );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( texture_type, texture.id );
glUniform1i(u_has_tex, (texture.id != 0));
glUniform1f(u_window_width, (float)window_width());
glUniform1f(u_window_height, (float)window_height());
vec4 rgbaf = {((rgba>>24)&255)/255.f, ((rgba>>16)&255)/255.f,((rgba>>8)&255)/255.f,((rgba>>0)&255)/255.f};
glUniform4fv(u_tint, GL_TRUE, &rgbaf.x);
// normalize texture regions
if (texture.id != 0) {
tex_start.x /= texture.w;
tex_start.y /= texture.h;
tex_end.x /= texture.w;
tex_end.y /= texture.h;
}
GLfloat vertices[] = {
// Positions // UVs
start.x, start.y, tex_start.x, tex_start.y,
end.x, start.y, tex_end.x, tex_start.y,
end.x, end.y, tex_end.x, tex_end.y,
start.x, start.y, tex_start.x, tex_start.y,
end.x, end.y, tex_end.x, tex_end.y,
start.x, end.y, tex_start.x, tex_end.y
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
glDrawArrays( GL_TRIANGLES, 0, 6 );
profile_incstat("Render.num_drawcalls", +1);
profile_incstat("Render.num_triangles", +2);
glBindTexture( texture_type, 0 );
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray( 0 );
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram( 0 );
}
// ----------------------------------------------------------------------------
// game ui
typedef union gui_state_t {
struct {
bool held;
bool hover;
};
} gui_state_t;
static __thread array(guiskin_t) skins=0;
static __thread guiskin_t *last_skin=0;
static __thread map(int, gui_state_t) ctl_states=0; //@leak
static __thread array(vec4) scissor_rects=0;
static __thread bool any_widget_used=0;
void gui_pushskin(guiskin_t skin) {
array_push(skins, skin);
last_skin = array_back(skins);
}
void gui_popskin() {
if (!last_skin) return;
if (last_skin->free) last_skin->free(last_skin->userdata);
array_pop(skins);
last_skin = array_count(skins) ? array_back(skins) : NULL;
}
void *gui_userdata() {
return last_skin->userdata;
}
vec2 gui_getskinsize(const char *skin, const char *fallback) {
vec2 size={0};
if (last_skin->getskinsize) last_skin->getskinsize(last_skin->userdata, skin, fallback, &size);
return size;
}
unsigned gui_getskincolor(const char *skin, const char *fallback) {
unsigned color = 0xFFFFFFFF;
if (last_skin->getskincolor) last_skin->getskincolor(last_skin->userdata, skin, fallback, &color);
return color;
}
bool gui_ismouseinrect(const char *skin, const char *fallback, vec4 rect) {
if (last_skin->ismouseinrect) return last_skin->ismouseinrect(last_skin->userdata, skin, fallback, rect);
return false;
}
vec4 gui_getscissorrect(const char *skin, const char *fallback, vec4 rect) {
vec4 scissor = rect;
if (last_skin->getscissorrect) last_skin->getscissorrect(last_skin->userdata, skin, fallback, rect, &scissor);
return scissor;
}
static
gui_state_t *gui_getstate(int id) {
if (!ctl_states) map_init(ctl_states, less_int, hash_int);
return map_find_or_add(ctl_states, id, (gui_state_t){0});
}
void gui_panel_id(int id, vec4 rect, const char *skin) {
(void)id;
vec4 scissor={0, 0, window_width(), window_height()};
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin, NULL, rect);
scissor = gui_getscissorrect(skin, NULL, rect);
if (!array_count(scissor_rects))
glEnable(GL_SCISSOR_TEST);
glScissor(scissor.x, window_height()-scissor.w-scissor.y, scissor.z, scissor.w);
array_push(scissor_rects, scissor);
}
void gui_panel_end() {
ASSERT(array_count(scissor_rects));
array_pop(scissor_rects);
if (array_count(scissor_rects)) {
vec4 scissor = *array_back(scissor_rects);
glScissor(scissor.x, scissor.y, scissor.z, scissor.w);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
bool gui_button_id(int id, vec4 r, const char *skin) {
gui_state_t *entry = gui_getstate(id);
bool was_clicked=0;
skin=skin?skin:"button";
char *btn = va("%s%s", skin, entry->held?"_press":entry->hover?"_hover":"");
if (gui_ismouseinrect(btn, skin, r)) {
if (input_up(MOUSE_L) && entry->held) {
was_clicked=1;
}
if (!any_widget_used) {
any_widget_used = entry->held = input_held(MOUSE_L);
entry->hover = true;
}
}
else {
entry->hover = false;
}
if (input_up(MOUSE_L) && entry->held) {
entry->held = false;
any_widget_used = false;
}
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, btn, skin, r);
return was_clicked;
}
bool gui_button_label_id(int id, const char *text, vec4 rect, const char *skin) {
gui_state_t *entry = gui_getstate(id);
bool state = gui_button_id(id, rect, skin);
vec2 buttonsize={0};
skin=skin?skin:"button";
char *btn = va("%s%s", skin, entry->held?"_press":entry->hover?"_hover":"");
buttonsize = gui_getskinsize(btn, skin);
vec2 textsize = font_rect(text);
vec4 pos;
pos.x = rect.x + max(buttonsize.x*.5f, rect.z*.5f) - textsize.x*.5f;
pos.y = rect.y + max(buttonsize.y*.5f, rect.w*.5f) - textsize.y*.5f;
gui_label(btn, text, pos);
return state;
}
static
float slider2posx(float min, float max, float value, float step, float w) {
float norm = value - min;
float range = max - min;
float rel = norm / range;
float res = w * rel;
return step==0.0f?res:(round(res/step)*step);
}
static
float posx2slider(vec4 rect, float min, float max, float xpos, float step) {
xpos = clampf(xpos, rect.x, rect.x+rect.z);
double rel = (xpos - rect.x) / rect.z;
float res = min + (rel * (max - min));
return step==0.0f?res:(round(res/step)*step);
}
bool gui_slider_id(int id, vec4 rect, const char *skin, float min, float max, float step, float *value) {
gui_state_t *entry = gui_getstate(id);
skin = skin?skin:"slider";
char *cursorskin = va("%s_cursor%s", skin, entry->held?"_press":entry->hover?"_hover":"");
char *fbcursor = va("%s_cursor", skin);
if (gui_ismouseinrect(skin, NULL, rect) && !any_widget_used) {
any_widget_used = entry->held = input_held(MOUSE_L);
entry->hover = true;
}
else if (input_up(MOUSE_L) && entry->held) {
entry->held = false;
any_widget_used = false;
}
else {
entry->hover = false;
}
float old_value = *value;
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin, NULL, rect);
vec2 slidersize={0}, cursorsize={0};
vec4 usablerect = gui_getscissorrect(skin, NULL, rect);
slidersize = gui_getskinsize(skin, NULL);
cursorsize = gui_getskinsize(cursorskin, fbcursor);
if (entry->held) {
*value = posx2slider(usablerect, min, max, input(MOUSE_X), step);
}
float sliderx = slider2posx(min, max, *value, step, usablerect.z);
vec2 cursorpos = vec2(sliderx+(usablerect.x-rect.x)-cursorsize.x*.5f, (slidersize.y*.5f - cursorsize.y*.5f));
vec4 cursorrect = rect;
cursorrect.x += cursorpos.x;
cursorrect.y += cursorpos.y;
cursorrect.z = cursorsize.x;
cursorrect.w = max(cursorsize.y, rect.w);
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, cursorskin, fbcursor, cursorrect);
return entry->held && (old_value!=*value);
}
bool gui_slider_label_id(int id, const char *text, vec4 rect, const char *skin, float min, float max, float step, float *value) {
bool state = gui_slider_id(id, rect, skin, min, max, step, value);
vec2 slidersize={0};
skin=skin?skin:"slider";
slidersize = gui_getskinsize(skin, NULL);
vec2 textsize = font_rect(text);
vec4 pos;
pos.x = rect.x + max(slidersize.x, rect.z) + 8 /*padding*/;
pos.y = rect.y + max(slidersize.y*.5f, rect.w*.5f) - textsize.y*.5f;
gui_label(skin, text, pos);
return state;
}
void gui_rect_id(int id, vec4 r, const char *skin) {
(void)id;
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin, NULL, r);
}
void gui_label_id(int id, const char *skin, const char *text, vec4 rect) {
(void)id;
font_color(FONT_COLOR6, gui_getskincolor(skin, NULL));
font_goto(rect.x, rect.y);
font_print(va(FONT_COLOR6 "%s", text));
}
/* skinned */
static
void skinned_free(void* userdata) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_destroy(&a->atlas);
FREE(a);
}
static
atlas_slice_frame_t *skinned_getsliceframe(atlas_t *a, const char *name) {
if (!name) return NULL;
for (int i = 0; i < array_count(a->slices); i++)
if (!strcmp(quark_string(&a->db, a->slices[i].name), name))
return &a->slice_frames[a->slices[i].frames[0]];
// PRINTF("slice name: '%s' is missing in atlas!\n", name);
return NULL;
}
static
void skinned_getskincolor(void *userdata, const char *skin, const char *fallback, unsigned *color) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (!f) return;
if (f->text && f->text[0] == '#') *color = atorgba(f->text);
}
static
void skinned_draw_missing_rect(vec4 r) {
vec4 size = vec4(0, 0, texture_checker().w, texture_checker().h);
gui_drawrect(texture_checker(), v42v2(size), 0x800080FF, v42v2(r));
}
static
bool skinned_ismouseinrect(void *userdata, const char *skin, const char *fallback, vec4 r) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (!f) return false;
vec4 outer = f->bounds;
r.x -= f->pivot.x*a->scale;
r.y -= f->pivot.y*a->scale;
r.z += r.x;
r.w += r.y;
if ((r.z-r.x) < (outer.z-outer.x) * a->scale) {
r.z = r.x + (outer.z-outer.x) * a->scale;
}
if ((r.w-r.y) < (outer.w-outer.y) * a->scale) {
r.w = r.y + (outer.w-outer.y) * a->scale;
}
return (input(MOUSE_X) > r.x && input(MOUSE_X) < r.z && input(MOUSE_Y) > r.y && input(MOUSE_Y) < r.w);
}
static
void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r) {
vec4 outer = f->bounds;
r.x -= f->pivot.x*scale;
r.y -= f->pivot.y*scale;
r.z += r.x;
r.w += r.y;
// Ensure dest rectangle is large enough to render the whole element
if ((r.z-r.x) < (outer.z-outer.x) * scale) {
r.z = r.x + (outer.z-outer.x) * scale;
}
if ((r.w-r.y) < (outer.w-outer.y) * scale) {
r.w = r.y + (outer.w-outer.y) * scale;
}
if (!f->has_9slice) {
gui_drawrect(a->tex, v42v2(f->bounds), 0xFFFFFFFF, v42v2(r));
return;
}
vec4 core = f->core;
core.x += outer.x;
core.y += outer.y;
core.z += outer.x;
core.w += outer.y;
// Define the 9 slices
vec4 top_left_slice = {outer.x, outer.y, core.x, core.y};
vec4 top_middle_slice = {core.x, outer.y, core.z, core.y};
vec4 top_right_slice = {core.z, outer.y, outer.z, core.y};
vec4 middle_left_slice = {outer.x, core.y, core.x, core.w};
vec4 center_slice = core;
vec4 middle_right_slice = {core.z, core.y, outer.z, core.w};
vec4 bottom_left_slice = {outer.x, core.w, core.x, outer.w};
vec4 bottom_middle_slice = {core.x, core.w, core.z, outer.w};
vec4 bottom_right_slice = {core.z, core.w, outer.z, outer.w};
vec4 top_left = {r.x, r.y, r.x + (core.x - outer.x) * scale, r.y + (core.y - outer.y) * scale};
vec4 top_right = {r.z - (outer.z - core.z) * scale, r.y, r.z, r.y + (core.y - outer.y) * scale};
vec4 bottom_left = {r.x, r.w - (outer.w - core.w) * scale, r.x + (core.x - outer.x) * scale, r.w};
vec4 bottom_right = {r.z - (outer.z - core.z) * scale, r.w - (outer.w - core.w) * scale, r.z, r.w};
vec4 top = {top_left.z, r.y, top_right.x, top_left.w};
vec4 bottom = {bottom_left.z, bottom_left.y, bottom_right.x, r.w};
vec4 left = {r.x, top_left.w, top_left.z, bottom_left.y};
vec4 right = {top_right.x, top_right.w, r.z, bottom_right.y};
vec4 center = {top_left.z, top_left.w, top_right.x, bottom_right.y};
gui_drawrect(a->tex, v42v2(center_slice), 0xFFFFFFFF, v42v2(center));
gui_drawrect(a->tex, v42v2(top_left_slice), 0xFFFFFFFF, v42v2(top_left));
gui_drawrect(a->tex, v42v2(top_right_slice), 0xFFFFFFFF, v42v2(top_right));
gui_drawrect(a->tex, v42v2(bottom_left_slice), 0xFFFFFFFF, v42v2(bottom_left));
gui_drawrect(a->tex, v42v2(bottom_right_slice), 0xFFFFFFFF, v42v2(bottom_right));
gui_drawrect(a->tex, v42v2(top_middle_slice), 0xFFFFFFFF, v42v2(top));
gui_drawrect(a->tex, v42v2(bottom_middle_slice), 0xFFFFFFFF, v42v2(bottom));
gui_drawrect(a->tex, v42v2(middle_left_slice), 0xFFFFFFFF, v42v2(left));
gui_drawrect(a->tex, v42v2(middle_right_slice), 0xFFFFFFFF, v42v2(right));
}
static
void skinned_draw_rect(void* userdata, const char *skin, const char *fallback, vec4 r) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (!f) skinned_draw_missing_rect(r);
else skinned_draw_sprite(a->scale, &a->atlas, f, r);
}
void skinned_getskinsize(void *userdata, const char *skin, const char *fallback, vec2 *size) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (f) {
size->x = (f->bounds.z-f->bounds.x)*a->scale;
size->y = (f->bounds.w-f->bounds.y)*a->scale;
}
}
static
void skinned_getscissorrect(void* userdata, const char *skin, const char *fallback, vec4 rect, vec4 *dims) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (!f) return;
*dims = rect;
if (!f->has_9slice) return;
vec2 skinsize, coresize;
skinsize.x = (f->bounds.z-f->bounds.x)*a->scale;
skinsize.y = (f->bounds.w-f->bounds.y)*a->scale;
coresize.x = (f->core.z-f->core.x)*a->scale;
coresize.y = (f->core.w-f->core.y)*a->scale;
dims->x += f->core.x*a->scale;
dims->y += f->core.y*a->scale;
dims->z -= (skinsize.x - coresize.x);
dims->w -= (skinsize.y - coresize.y);
}
guiskin_t gui_skinned(const char *asefile, float scale) {
skinned_t *a = REALLOC(0, sizeof(skinned_t));
a->atlas = atlas_create(asefile, 0);
a->scale = scale?scale:1.0f;
guiskin_t skin={0};
skin.userdata = a;
skin.drawrect = skinned_draw_rect;
skin.getskinsize = skinned_getskinsize;
skin.getskincolor = skinned_getskincolor;
skin.ismouseinrect = skinned_ismouseinrect;
skin.getscissorrect = skinned_getscissorrect;
skin.free = skinned_free;
return skin;
}
// ----------------------------------------------------------------------------
// game ui (utils)
API void gui_drawrect( texture_t spritesheet, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end );
#define v42v2(rect) vec2(rect.x,rect.y), vec2(rect.z,rect.w)
void gui_drawrect( texture_t texture, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end ) {
static renderstate_t rect_rs;
do_once {
rect_rs = renderstate();
rect_rs.depth_test_enabled = false;
rect_rs.blend_enabled = true;
rect_rs.blend_src = GL_SRC_ALPHA;
rect_rs.blend_dst = GL_ONE_MINUS_SRC_ALPHA;
rect_rs.front_face = GL_CW;
}
static int program = -1, vbo = -1, vao = -1, u_tint = -1, u_has_tex = -1, u_window_width = -1, u_window_height = -1;
float gamma = 1;
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
if( program < 0 ) {
const char* vs = vfs_read("shaders/rect_2d.vs");
const char* fs = vfs_read("shaders/rect_2d.fs");
program = shader(vs, fs, "", "fragcolor" , NULL);
ASSERT(program > 0);
u_tint = glGetUniformLocation(program, "u_tint");
u_has_tex = glGetUniformLocation(program, "u_has_tex");
u_window_width = glGetUniformLocation(program, "u_window_width");
u_window_height = glGetUniformLocation(program, "u_window_height");
glGenVertexArrays( 1, (GLuint*)&vao );
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
}
start = mul2(start, dpi);
end = mul2(end, dpi);
renderstate_apply(&rect_rs);
GLenum texture_type = texture.flags & TEXTURE_ARRAY ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
glUseProgram( program );
glBindVertexArray( vao );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( texture_type, texture.id );
glUniform1i(u_has_tex, (texture.id != 0));
glUniform1f(u_window_width, (float)window_width());
glUniform1f(u_window_height, (float)window_height());
vec4 rgbaf = {((rgba>>24)&255)/255.f, ((rgba>>16)&255)/255.f,((rgba>>8)&255)/255.f,((rgba>>0)&255)/255.f};
glUniform4fv(u_tint, GL_TRUE, &rgbaf.x);
// normalize texture regions
if (texture.id != 0) {
tex_start.x /= texture.w;
tex_start.y /= texture.h;
tex_end.x /= texture.w;
tex_end.y /= texture.h;
}
GLfloat vertices[] = {
// Positions // UVs
start.x, start.y, tex_start.x, tex_start.y,
end.x, start.y, tex_end.x, tex_start.y,
end.x, end.y, tex_end.x, tex_end.y,
start.x, start.y, tex_start.x, tex_start.y,
end.x, end.y, tex_end.x, tex_end.y,
start.x, end.y, tex_start.x, tex_end.y
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
glDrawArrays( GL_TRIANGLES, 0, 6 );
profile_incstat("Render.num_drawcalls", +1);
profile_incstat("Render.num_triangles", +2);
glBindTexture( texture_type, 0 );
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray( 0 );
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram( 0 );
}
// ----------------------------------------------------------------------------
// game ui
typedef union gui_state_t {
struct {
bool held;
bool hover;
};
} gui_state_t;
static __thread array(guiskin_t) skins=0;
static __thread guiskin_t *last_skin=0;
static __thread map(int, gui_state_t) ctl_states=0; //@leak
static __thread array(vec4) scissor_rects=0;
static __thread bool any_widget_used=0;
void gui_pushskin(guiskin_t skin) {
array_push(skins, skin);
last_skin = array_back(skins);
}
void gui_popskin() {
if (!last_skin) return;
if (last_skin->free) last_skin->free(last_skin->userdata);
array_pop(skins);
last_skin = array_count(skins) ? array_back(skins) : NULL;
}
void *gui_userdata() {
return last_skin->userdata;
}
vec2 gui_getskinsize(const char *skin, const char *fallback) {
vec2 size={0};
if (last_skin->getskinsize) last_skin->getskinsize(last_skin->userdata, skin, fallback, &size);
return size;
}
unsigned gui_getskincolor(const char *skin, const char *fallback) {
unsigned color = 0xFFFFFFFF;
if (last_skin->getskincolor) last_skin->getskincolor(last_skin->userdata, skin, fallback, &color);
return color;
}
bool gui_ismouseinrect(const char *skin, const char *fallback, vec4 rect) {
if (last_skin->ismouseinrect) return last_skin->ismouseinrect(last_skin->userdata, skin, fallback, rect);
return false;
}
vec4 gui_getscissorrect(const char *skin, const char *fallback, vec4 rect) {
vec4 scissor = rect;
if (last_skin->getscissorrect) last_skin->getscissorrect(last_skin->userdata, skin, fallback, rect, &scissor);
return scissor;
}
static
gui_state_t *gui_getstate(int id) {
if (!ctl_states) map_init(ctl_states, less_int, hash_int);
return map_find_or_add(ctl_states, id, (gui_state_t){0});
}
void gui_panel_id(int id, vec4 rect, const char *skin) {
(void)id;
vec4 scissor={0, 0, window_width(), window_height()};
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin, NULL, rect);
scissor = gui_getscissorrect(skin, NULL, rect);
if (!array_count(scissor_rects))
glEnable(GL_SCISSOR_TEST);
glScissor(scissor.x, window_height()-scissor.w-scissor.y, scissor.z, scissor.w);
array_push(scissor_rects, scissor);
}
void gui_panel_end() {
ASSERT(array_count(scissor_rects));
array_pop(scissor_rects);
if (array_count(scissor_rects)) {
vec4 scissor = *array_back(scissor_rects);
glScissor(scissor.x, scissor.y, scissor.z, scissor.w);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
bool gui_button_id(int id, vec4 r, const char *skin) {
gui_state_t *entry = gui_getstate(id);
bool was_clicked=0;
skin=skin?skin:"button";
char *btn = va("%s%s", skin, entry->held?"_press":entry->hover?"_hover":"");
if (gui_ismouseinrect(btn, skin, r)) {
if (input_up(MOUSE_L) && entry->held) {
was_clicked=1;
}
if (!any_widget_used) {
any_widget_used = entry->held = input_held(MOUSE_L);
entry->hover = true;
}
}
else {
entry->hover = false;
}
if (input_up(MOUSE_L) && entry->held) {
entry->held = false;
any_widget_used = false;
}
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, btn, skin, r);
return was_clicked;
}
bool gui_button_label_id(int id, const char *text, vec4 rect, const char *skin) {
gui_state_t *entry = gui_getstate(id);
bool state = gui_button_id(id, rect, skin);
vec2 buttonsize={0};
skin=skin?skin:"button";
char *btn = va("%s%s", skin, entry->held?"_press":entry->hover?"_hover":"");
buttonsize = gui_getskinsize(btn, skin);
vec2 textsize = font_rect(text);
vec4 pos;
pos.x = rect.x + max(buttonsize.x*.5f, rect.z*.5f) - textsize.x*.5f;
pos.y = rect.y + max(buttonsize.y*.5f, rect.w*.5f) - textsize.y*.5f;
gui_label(btn, text, pos);
return state;
}
static
float slider2posx(float min, float max, float value, float step, float w) {
float norm = value - min;
float range = max - min;
float rel = norm / range;
float res = w * rel;
return step==0.0f?res:(round(res/step)*step);
}
static
float posx2slider(vec4 rect, float min, float max, float xpos, float step) {
xpos = clampf(xpos, rect.x, rect.x+rect.z);
double rel = (xpos - rect.x) / rect.z;
float res = min + (rel * (max - min));
return step==0.0f?res:(round(res/step)*step);
}
bool gui_slider_id(int id, vec4 rect, const char *skin, float min, float max, float step, float *value) {
gui_state_t *entry = gui_getstate(id);
skin = skin?skin:"slider";
char *cursorskin = va("%s_cursor%s", skin, entry->held?"_press":entry->hover?"_hover":"");
char *fbcursor = va("%s_cursor", skin);
if (gui_ismouseinrect(skin, NULL, rect) && !any_widget_used) {
any_widget_used = entry->held = input_held(MOUSE_L);
entry->hover = true;
}
else if (input_up(MOUSE_L) && entry->held) {
entry->held = false;
any_widget_used = false;
}
else {
entry->hover = false;
}
float old_value = *value;
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin, NULL, rect);
vec2 slidersize={0}, cursorsize={0};
vec4 usablerect = gui_getscissorrect(skin, NULL, rect);
slidersize = gui_getskinsize(skin, NULL);
cursorsize = gui_getskinsize(cursorskin, fbcursor);
if (entry->held) {
*value = posx2slider(usablerect, min, max, input(MOUSE_X), step);
}
float sliderx = slider2posx(min, max, *value, step, usablerect.z);
vec2 cursorpos = vec2(sliderx+(usablerect.x-rect.x)-cursorsize.x*.5f, (slidersize.y*.5f - cursorsize.y*.5f));
vec4 cursorrect = rect;
cursorrect.x += cursorpos.x;
cursorrect.y += cursorpos.y;
cursorrect.z = cursorsize.x;
cursorrect.w = max(cursorsize.y, rect.w);
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, cursorskin, fbcursor, cursorrect);
return entry->held && (old_value!=*value);
}
bool gui_slider_label_id(int id, const char *text, vec4 rect, const char *skin, float min, float max, float step, float *value) {
bool state = gui_slider_id(id, rect, skin, min, max, step, value);
vec2 slidersize={0};
skin=skin?skin:"slider";
slidersize = gui_getskinsize(skin, NULL);
vec2 textsize = font_rect(text);
vec4 pos;
pos.x = rect.x + max(slidersize.x, rect.z) + 8 /*padding*/;
pos.y = rect.y + max(slidersize.y*.5f, rect.w*.5f) - textsize.y*.5f;
gui_label(skin, text, pos);
return state;
}
void gui_rect_id(int id, vec4 r, const char *skin) {
(void)id;
if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin, NULL, r);
}
void gui_label_id(int id, const char *skin, const char *text, vec4 rect) {
(void)id;
font_color(FONT_COLOR6, gui_getskincolor(skin, NULL));
font_goto(rect.x, rect.y);
font_print(va(FONT_COLOR6 "%s", text));
}
/* skinned */
static
void skinned_free(void* userdata) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_destroy(&a->atlas);
FREE(a);
}
static
atlas_slice_frame_t *skinned_getsliceframe(atlas_t *a, const char *name) {
if (!name) return NULL;
for (int i = 0; i < array_count(a->slices); i++)
if (!strcmp(quark_string(&a->db, a->slices[i].name), name))
return &a->slice_frames[a->slices[i].frames[0]];
// PRINTF("slice name: '%s' is missing in atlas!\n", name);
return NULL;
}
static
void skinned_getskincolor(void *userdata, const char *skin, const char *fallback, unsigned *color) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (!f) return;
if (f->text && f->text[0] == '#') *color = atorgba(f->text);
}
static
void skinned_draw_missing_rect(vec4 r) {
vec4 size = vec4(0, 0, texture_checker().w, texture_checker().h);
gui_drawrect(texture_checker(), v42v2(size), 0x800080FF, v42v2(r));
}
static
bool skinned_ismouseinrect(void *userdata, const char *skin, const char *fallback, vec4 r) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (!f) return false;
vec4 outer = f->bounds;
r.x -= f->pivot.x*a->scale;
r.y -= f->pivot.y*a->scale;
r.z += r.x;
r.w += r.y;
if ((r.z-r.x) < (outer.z-outer.x) * a->scale) {
r.z = r.x + (outer.z-outer.x) * a->scale;
}
if ((r.w-r.y) < (outer.w-outer.y) * a->scale) {
r.w = r.y + (outer.w-outer.y) * a->scale;
}
return (input(MOUSE_X) > r.x && input(MOUSE_X) < r.z && input(MOUSE_Y) > r.y && input(MOUSE_Y) < r.w);
}
static
void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r) {
vec4 outer = f->bounds;
r.x -= f->pivot.x*scale;
r.y -= f->pivot.y*scale;
r.z += r.x;
r.w += r.y;
// Ensure dest rectangle is large enough to render the whole element
if ((r.z-r.x) < (outer.z-outer.x) * scale) {
r.z = r.x + (outer.z-outer.x) * scale;
}
if ((r.w-r.y) < (outer.w-outer.y) * scale) {
r.w = r.y + (outer.w-outer.y) * scale;
}
if (!f->has_9slice) {
gui_drawrect(a->tex, v42v2(f->bounds), 0xFFFFFFFF, v42v2(r));
return;
}
vec4 core = f->core;
core.x += outer.x;
core.y += outer.y;
core.z += outer.x;
core.w += outer.y;
// Define the 9 slices
vec4 top_left_slice = {outer.x, outer.y, core.x, core.y};
vec4 top_middle_slice = {core.x, outer.y, core.z, core.y};
vec4 top_right_slice = {core.z, outer.y, outer.z, core.y};
vec4 middle_left_slice = {outer.x, core.y, core.x, core.w};
vec4 center_slice = core;
vec4 middle_right_slice = {core.z, core.y, outer.z, core.w};
vec4 bottom_left_slice = {outer.x, core.w, core.x, outer.w};
vec4 bottom_middle_slice = {core.x, core.w, core.z, outer.w};
vec4 bottom_right_slice = {core.z, core.w, outer.z, outer.w};
vec4 top_left = {r.x, r.y, r.x + (core.x - outer.x) * scale, r.y + (core.y - outer.y) * scale};
vec4 top_right = {r.z - (outer.z - core.z) * scale, r.y, r.z, r.y + (core.y - outer.y) * scale};
vec4 bottom_left = {r.x, r.w - (outer.w - core.w) * scale, r.x + (core.x - outer.x) * scale, r.w};
vec4 bottom_right = {r.z - (outer.z - core.z) * scale, r.w - (outer.w - core.w) * scale, r.z, r.w};
vec4 top = {top_left.z, r.y, top_right.x, top_left.w};
vec4 bottom = {bottom_left.z, bottom_left.y, bottom_right.x, r.w};
vec4 left = {r.x, top_left.w, top_left.z, bottom_left.y};
vec4 right = {top_right.x, top_right.w, r.z, bottom_right.y};
vec4 center = {top_left.z, top_left.w, top_right.x, bottom_right.y};
gui_drawrect(a->tex, v42v2(center_slice), 0xFFFFFFFF, v42v2(center));
gui_drawrect(a->tex, v42v2(top_left_slice), 0xFFFFFFFF, v42v2(top_left));
gui_drawrect(a->tex, v42v2(top_right_slice), 0xFFFFFFFF, v42v2(top_right));
gui_drawrect(a->tex, v42v2(bottom_left_slice), 0xFFFFFFFF, v42v2(bottom_left));
gui_drawrect(a->tex, v42v2(bottom_right_slice), 0xFFFFFFFF, v42v2(bottom_right));
gui_drawrect(a->tex, v42v2(top_middle_slice), 0xFFFFFFFF, v42v2(top));
gui_drawrect(a->tex, v42v2(bottom_middle_slice), 0xFFFFFFFF, v42v2(bottom));
gui_drawrect(a->tex, v42v2(middle_left_slice), 0xFFFFFFFF, v42v2(left));
gui_drawrect(a->tex, v42v2(middle_right_slice), 0xFFFFFFFF, v42v2(right));
}
static
void skinned_draw_rect(void* userdata, const char *skin, const char *fallback, vec4 r) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (!f) skinned_draw_missing_rect(r);
else skinned_draw_sprite(a->scale, &a->atlas, f, r);
}
void skinned_getskinsize(void *userdata, const char *skin, const char *fallback, vec2 *size) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (f) {
size->x = (f->bounds.z-f->bounds.x)*a->scale;
size->y = (f->bounds.w-f->bounds.y)*a->scale;
}
}
static
void skinned_getscissorrect(void* userdata, const char *skin, const char *fallback, vec4 rect, vec4 *dims) {
skinned_t *a = C_CAST(skinned_t*, userdata);
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin);
if (!f && fallback) f = skinned_getsliceframe(&a->atlas, fallback);
if (!f) return;
*dims = rect;
if (!f->has_9slice) return;
vec2 skinsize, coresize;
skinsize.x = (f->bounds.z-f->bounds.x)*a->scale;
skinsize.y = (f->bounds.w-f->bounds.y)*a->scale;
coresize.x = (f->core.z-f->core.x)*a->scale;
coresize.y = (f->core.w-f->core.y)*a->scale;
dims->x += f->core.x*a->scale;
dims->y += f->core.y*a->scale;
dims->z -= (skinsize.x - coresize.x);
dims->w -= (skinsize.y - coresize.y);
}
guiskin_t gui_skinned(const char *asefile, float scale) {
skinned_t *a = REALLOC(0, sizeof(skinned_t));
a->atlas = atlas_create(asefile, 0);
a->scale = scale?scale:1.0f;
guiskin_t skin={0};
skin.userdata = a;
skin.drawrect = skinned_draw_rect;
skin.getskinsize = skinned_getskinsize;
skin.getskincolor = skinned_getskincolor;
skin.ismouseinrect = skinned_ismouseinrect;
skin.getscissorrect = skinned_getscissorrect;
skin.free = skinned_free;
return skin;
}

View File

@ -1,65 +1,65 @@
// ----------------------------------------------------------------------------
// game ui
typedef struct guiskin_t {
void (*drawrect)(void* userdata, const char *skin, const char *fallback, vec4 rect);
void (*getskinsize)(void* userdata, const char *skin, const char *fallback, vec2 *size);
void (*getskincolor)(void* userdata, const char *skin, const char *fallback, unsigned *color);
void (*getscissorrect)(void* userdata, const char *skin, const char *fallback, vec4 rect, vec4 *dims);
bool (*ismouseinrect)(void* userdata, const char *skin, const char *fallback, vec4 rect);
void (*free)(void* userdata);
void *userdata;
} guiskin_t;
API void gui_pushskin(guiskin_t skin);
API void* gui_userdata();
API vec2 gui_getskinsize(const char *skin, const char *fallback);
API unsigned gui_getskincolor(const char *skin, const char *fallback);
API bool gui_ismouseinrect(const char *skin, const char *fallback, vec4 rect);
API vec4 gui_getscissorrect(const char *skin, const char *fallback, vec4 rect);
// --
API void gui_panel_id(int id, vec4 rect, const char *skin);
API void gui_rect_id(int id, vec4 rect, const char *skin);
API void gui_label_id(int id, const char *skin, const char *text, vec4 rect);
API bool gui_button_id(int id, vec4 rect, const char *skin);
API bool gui_button_label_id(int id, const char *text, vec4 rect, const char *skin);
API bool gui_slider_id(int id, vec4 rect, const char *skin, float min, float max, float step, float *value);
API bool gui_slider_label_id(int id, const char *text, vec4 rect, const char *skin, float min, float max, float step, float *value);
API void gui_panel_end();
API void gui_popskin();
// helpers
#define gui_panel(...) gui_panel_id(__LINE__, __VA_ARGS__)
#define gui_rect(...) gui_rect_id(__LINE__, __VA_ARGS__)
#define gui_label(...) gui_label_id(__LINE__, __VA_ARGS__)
#define gui_button(...) gui_button_id(__LINE__, __VA_ARGS__)
#define gui_button_label(...) gui_button_label_id(__LINE__, __VA_ARGS__)
#define gui_slider(...) gui_slider_id(__LINE__, __VA_ARGS__)
#define gui_slider_label(...) gui_slider_label_id(__LINE__, __VA_ARGS__)
// default renderers
typedef struct skinned_t {
atlas_t atlas;
float scale;
} skinned_t;
// The skinning engine depends on an Aseprite asset with slices set up.
// While you can specify your own skins for various GUI widgets, some
// skin variants are hardcoded and expected to be present in your asset:
//
// gui_panel():
// - "panel" (overridable)
// gui_button():
// - "button" (base overridable)
// - "_hover" (ex. "scarybtn_hover")
// - "_press"
// gui_rect():
// - no defaults, always pass your own skin/slice name
// gui_slider():
// - "slider" (overridable)
// - "slider_cursor" (partially overridable, ex. "bigslider_cursor")
// - "_hover" (ex. "slider_cursor_hover")
// - "_press"
//
API guiskin_t gui_skinned(const char *asefile, float scale);
// ----------------------------------------------------------------------------
// game ui
typedef struct guiskin_t {
void (*drawrect)(void* userdata, const char *skin, const char *fallback, vec4 rect);
void (*getskinsize)(void* userdata, const char *skin, const char *fallback, vec2 *size);
void (*getskincolor)(void* userdata, const char *skin, const char *fallback, unsigned *color);
void (*getscissorrect)(void* userdata, const char *skin, const char *fallback, vec4 rect, vec4 *dims);
bool (*ismouseinrect)(void* userdata, const char *skin, const char *fallback, vec4 rect);
void (*free)(void* userdata);
void *userdata;
} guiskin_t;
API void gui_pushskin(guiskin_t skin);
API void* gui_userdata();
API vec2 gui_getskinsize(const char *skin, const char *fallback);
API unsigned gui_getskincolor(const char *skin, const char *fallback);
API bool gui_ismouseinrect(const char *skin, const char *fallback, vec4 rect);
API vec4 gui_getscissorrect(const char *skin, const char *fallback, vec4 rect);
// --
API void gui_panel_id(int id, vec4 rect, const char *skin);
API void gui_rect_id(int id, vec4 rect, const char *skin);
API void gui_label_id(int id, const char *skin, const char *text, vec4 rect);
API bool gui_button_id(int id, vec4 rect, const char *skin);
API bool gui_button_label_id(int id, const char *text, vec4 rect, const char *skin);
API bool gui_slider_id(int id, vec4 rect, const char *skin, float min, float max, float step, float *value);
API bool gui_slider_label_id(int id, const char *text, vec4 rect, const char *skin, float min, float max, float step, float *value);
API void gui_panel_end();
API void gui_popskin();
// helpers
#define gui_panel(...) gui_panel_id(__LINE__, __VA_ARGS__)
#define gui_rect(...) gui_rect_id(__LINE__, __VA_ARGS__)
#define gui_label(...) gui_label_id(__LINE__, __VA_ARGS__)
#define gui_button(...) gui_button_id(__LINE__, __VA_ARGS__)
#define gui_button_label(...) gui_button_label_id(__LINE__, __VA_ARGS__)
#define gui_slider(...) gui_slider_id(__LINE__, __VA_ARGS__)
#define gui_slider_label(...) gui_slider_label_id(__LINE__, __VA_ARGS__)
// default renderers
typedef struct skinned_t {
atlas_t atlas;
float scale;
} skinned_t;
// The skinning engine depends on an Aseprite asset with slices set up.
// While you can specify your own skins for various GUI widgets, some
// skin variants are hardcoded and expected to be present in your asset:
//
// gui_panel():
// - "panel" (overridable)
// gui_button():
// - "button" (base overridable)
// - "_hover" (ex. "scarybtn_hover")
// - "_press"
// gui_rect():
// - no defaults, always pass your own skin/slice name
// gui_slider():
// - "slider" (overridable)
// - "slider_cursor" (partially overridable, ex. "bigslider_cursor")
// - "_hover" (ex. "slider_cursor_hover")
// - "_press"
//
API guiskin_t gui_skinned(const char *asefile, float scale);

File diff suppressed because it is too large Load Diff

View File

@ -1,124 +1,124 @@
// -----------------------------------------------------------------------------
// input framework
// - rlyeh, public domain
//
// @todo: window
// @todo: for extra savings (168->72 bytes), promote bits to real bits (/8 %8) & normalized floats [-1,+1] to shorts or chars
// @todo: GAMEPAD_A|2, MOUSE_L|1, KEY_C|3
// @todo: load/save
// @todo: send virtual presses & outputs (rumble, light, led, text, etc)
// @todo: fix if logger !60 hz
// @tofo: fix click2/repeat edge cases
API int input_use( int controller_id ); // [0..3]
// -- basic polling api (read input at current frame)
API float input( int vk );
API vec2 input2( int vk );
API float input_diff( int vk ); // @todo: rename diff->delta
API vec2 input_diff2( int vk ); // @todo: rename diff2->delta2
API const char* input_string( int vk );
// -- extended polling api (read input at Nth frame ago)
API float input_frame( int vk, int Nth_frame );
API vec2 input_frame2( int vk, int Nth_frame );
// -- events api
API int input_up( int vk ); // ON -> OFF (release)
API int input_down( int vk ); // OFF -> ON (trigger)
API int input_held( int vk ); // ON -> ON (pressed)
API int input_idle( int vk ); // OFF -> OFF
API int input_click( int vk, int ms ); // OFF -> ON -> OFF
API int input_click2( int vk, int ms ); // OFF -> ON -> OFF -> ON -> OFF
API int input_repeat( int vk, int ms ); // [...] ON -> ON -> ON
API int input_chord2( int vk1, int vk2 ); // all vk1 && vk2 are ON
API int input_chord3( int vk1, int vk2, int vk3 ); // all vk1 && vk2 && vk3 are ON
API int input_chord4( int vk1, int vk2, int vk3, int vk4 ); // all vk1 && vk2 && vk3 && vk4 are ON
// -- 1d/2d filters
API float input_filter_positive( float v ); // [-1..1] -> [0..1]
API vec2 input_filter_positive2( vec2 v ); // [-1..1] -> [0..1]
API vec2 input_filter_deadzone( vec2 v, float deadzone_treshold );
API vec2 input_filter_deadzone_4way( vec2 v, float deadzone_treshold );
// -- multi-touch
enum TOUCH_BUTTONS {
TOUCH_0, // defaults to left screen area. input_touch_area() to override
TOUCH_1, // defaults to right screen area. input_touch_area() to override
};
API void input_touch_area(unsigned button, vec2 begin_coord_ndc, vec2 end_coord_ndc);
API vec2 input_touch(unsigned button, float sensitivity); // absolute position in 2d coords
API vec2 input_touch_delta(unsigned button, float sensitivity); // delta from previous position
API vec2 input_touch_delta_from_origin(unsigned button, float sensitivity); // relative position from initial touch
API bool input_touch_active();
// -- utils
API void input_mappings(const char *filename); // update gamepad mappings (usually "gamecontrollerdb.txt" file)
API char input_keychar(unsigned code); // Converts keyboard code to its latin char (if any)
API int input_enum(const char *sym);
API int input_anykey();
API int input_eval(const char *expression); // "down(X)*input(CTRL)"
// inject state
API void input_send( int vk ); // @todo
// load/save input
API array(char) save_input(); // @todo
API bool load_input(array(char) replay); // @todo
// visualize input
API int ui_keyboard();
API int ui_mouse();
API int ui_gamepad(int id);
API int ui_gamepads();
// --
enum INPUT_ENUMS {
// -- bits: x104 keyboard, x3 mouse, x15 gamepad, x7 window
// keyboard gaming keys (53-bit): first-class keys for gaming
KEY_0,KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9, KEY_TICK,KEY_BS, KEY_ESC,
KEY_TAB, KEY_Q,KEY_W,KEY_E,KEY_R,KEY_T,KEY_Y,KEY_U,KEY_I,KEY_O,KEY_P,
KEY_CAPS, KEY_A,KEY_S,KEY_D,KEY_F,KEY_G,KEY_H,KEY_J,KEY_K,KEY_L, KEY_ENTER,
KEY_LSHIFT, KEY_Z,KEY_X,KEY_C,KEY_V,KEY_B,KEY_N,KEY_M, KEY_RSHIFT, KEY_UP,
KEY_LCTRL,KEY_LALT, KEY_SPACE, KEY_RALT,KEY_RCTRL, KEY_LEFT,KEY_DOWN,KEY_RIGHT,
// for completeness, secondary keys below (52-bit). beware!
KEY_INS,KEY_HOME,KEY_PGUP,KEY_DEL,KEY_END,KEY_PGDN, // beware: different behavior win/osx (also, osx: no home/end).
KEY_LMETA,KEY_RMETA,KEY_MENU,KEY_PRINT,KEY_PAUSE,KEY_SCROLL,KEY_NUMLOCK, // beware: may trigger unexpected OS behavior. (@todo: add RSHIFT here for win?)
KEY_MINUS,KEY_EQUAL,KEY_LSQUARE,KEY_RSQUARE,KEY_SEMICOLON,KEY_QUOTE,KEY_HASH,KEY_BAR,KEY_COMMA,KEY_DOT,KEY_SLASH, // beware: non-us keyboard layouts
KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,KEY_F11,KEY_F12, // beware: complicated on laptops/osx
KEY_PAD1,KEY_PAD2,KEY_PAD3,KEY_PAD4,KEY_PAD5,KEY_PAD6,KEY_PAD7,KEY_PAD8,KEY_PAD9,KEY_PAD0, // beware: complicated on laptops
KEY_PADADD,KEY_PADSUB,KEY_PADMUL,KEY_PADDIV,KEY_PADDOT,KEY_PADENTER, // beware: complicated on laptops
MOUSE_L, MOUSE_M, MOUSE_R, // @todo: MOUSE_CLICKS,
GAMEPAD_CONNECTED, GAMEPAD_A, GAMEPAD_B, GAMEPAD_X, GAMEPAD_Y,
GAMEPAD_UP, GAMEPAD_DOWN, GAMEPAD_LEFT, GAMEPAD_RIGHT, GAMEPAD_MENU, GAMEPAD_START,
GAMEPAD_LB, GAMEPAD_RB, GAMEPAD_LTHUMB, GAMEPAD_RTHUMB,
WINDOW_BLUR, WINDOW_FOCUS, WINDOW_CLOSE, WINDOW_MINIMIZE, WINDOW_MAXIMIZE, WINDOW_FULLSCREEN, WINDOW_WINDOWED, // MINI/MAXI/RESTORED, SHOWN/HIDDEN
// -- floats: x7 gamepad, x3 mouse, x4 touch, x4 window
GAMEPAD_LPAD, GAMEPAD_LPADX = GAMEPAD_LPAD, GAMEPAD_LPADY,
GAMEPAD_RPAD, GAMEPAD_RPADX = GAMEPAD_RPAD, GAMEPAD_RPADY,
GAMEPAD_LTRIGGER, GAMEPAD_LT = GAMEPAD_LTRIGGER, GAMEPAD_RTRIGGER, GAMEPAD_RT = GAMEPAD_RTRIGGER, GAMEPAD_BATTERY,
MOUSE, MOUSE_X = MOUSE, MOUSE_Y, MOUSE_W,
TOUCH_X1, TOUCH_Y1, TOUCH_X2, TOUCH_Y2,
WINDOW_RESIZE, WINDOW_RESIZEX = WINDOW_RESIZE, WINDOW_RESIZEY, WINDOW_ORIENTATION, WINDOW_BATTERY,
// -- strings: x2 gamepad
GAMEPAD_GUID, GAMEPAD_NAME,
};
// these aliases do check both left and right counterparts
enum INPUT_ALIASES {
KEY_SHIFT = KEY_LSHIFT,
KEY_ALT = KEY_LALT,
KEY_CTRL = KEY_LCTRL,
};
// -----------------------------------------------------------------------------
// input framework
// - rlyeh, public domain
//
// @todo: window
// @todo: for extra savings (168->72 bytes), promote bits to real bits (/8 %8) & normalized floats [-1,+1] to shorts or chars
// @todo: GAMEPAD_A|2, MOUSE_L|1, KEY_C|3
// @todo: load/save
// @todo: send virtual presses & outputs (rumble, light, led, text, etc)
// @todo: fix if logger !60 hz
// @tofo: fix click2/repeat edge cases
API int input_use( int controller_id ); // [0..3]
// -- basic polling api (read input at current frame)
API float input( int vk );
API vec2 input2( int vk );
API float input_diff( int vk ); // @todo: rename diff->delta
API vec2 input_diff2( int vk ); // @todo: rename diff2->delta2
API const char* input_string( int vk );
// -- extended polling api (read input at Nth frame ago)
API float input_frame( int vk, int Nth_frame );
API vec2 input_frame2( int vk, int Nth_frame );
// -- events api
API int input_up( int vk ); // ON -> OFF (release)
API int input_down( int vk ); // OFF -> ON (trigger)
API int input_held( int vk ); // ON -> ON (pressed)
API int input_idle( int vk ); // OFF -> OFF
API int input_click( int vk, int ms ); // OFF -> ON -> OFF
API int input_click2( int vk, int ms ); // OFF -> ON -> OFF -> ON -> OFF
API int input_repeat( int vk, int ms ); // [...] ON -> ON -> ON
API int input_chord2( int vk1, int vk2 ); // all vk1 && vk2 are ON
API int input_chord3( int vk1, int vk2, int vk3 ); // all vk1 && vk2 && vk3 are ON
API int input_chord4( int vk1, int vk2, int vk3, int vk4 ); // all vk1 && vk2 && vk3 && vk4 are ON
// -- 1d/2d filters
API float input_filter_positive( float v ); // [-1..1] -> [0..1]
API vec2 input_filter_positive2( vec2 v ); // [-1..1] -> [0..1]
API vec2 input_filter_deadzone( vec2 v, float deadzone_treshold );
API vec2 input_filter_deadzone_4way( vec2 v, float deadzone_treshold );
// -- multi-touch
enum TOUCH_BUTTONS {
TOUCH_0, // defaults to left screen area. input_touch_area() to override
TOUCH_1, // defaults to right screen area. input_touch_area() to override
};
API void input_touch_area(unsigned button, vec2 begin_coord_ndc, vec2 end_coord_ndc);
API vec2 input_touch(unsigned button, float sensitivity); // absolute position in 2d coords
API vec2 input_touch_delta(unsigned button, float sensitivity); // delta from previous position
API vec2 input_touch_delta_from_origin(unsigned button, float sensitivity); // relative position from initial touch
API bool input_touch_active();
// -- utils
API void input_mappings(const char *filename); // update gamepad mappings (usually "gamecontrollerdb.txt" file)
API char input_keychar(unsigned code); // Converts keyboard code to its latin char (if any)
API int input_enum(const char *sym);
API int input_anykey();
API int input_eval(const char *expression); // "down(X)*input(CTRL)"
// inject state
API void input_send( int vk ); // @todo
// load/save input
API array(char) save_input(); // @todo
API bool load_input(array(char) replay); // @todo
// visualize input
API int ui_keyboard();
API int ui_mouse();
API int ui_gamepad(int id);
API int ui_gamepads();
// --
enum INPUT_ENUMS {
// -- bits: x104 keyboard, x3 mouse, x15 gamepad, x7 window
// keyboard gaming keys (53-bit): first-class keys for gaming
KEY_0,KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9, KEY_TICK,KEY_BS, KEY_ESC,
KEY_TAB, KEY_Q,KEY_W,KEY_E,KEY_R,KEY_T,KEY_Y,KEY_U,KEY_I,KEY_O,KEY_P,
KEY_CAPS, KEY_A,KEY_S,KEY_D,KEY_F,KEY_G,KEY_H,KEY_J,KEY_K,KEY_L, KEY_ENTER,
KEY_LSHIFT, KEY_Z,KEY_X,KEY_C,KEY_V,KEY_B,KEY_N,KEY_M, KEY_RSHIFT, KEY_UP,
KEY_LCTRL,KEY_LALT, KEY_SPACE, KEY_RALT,KEY_RCTRL, KEY_LEFT,KEY_DOWN,KEY_RIGHT,
// for completeness, secondary keys below (52-bit). beware!
KEY_INS,KEY_HOME,KEY_PGUP,KEY_DEL,KEY_END,KEY_PGDN, // beware: different behavior win/osx (also, osx: no home/end).
KEY_LMETA,KEY_RMETA,KEY_MENU,KEY_PRINT,KEY_PAUSE,KEY_SCROLL,KEY_NUMLOCK, // beware: may trigger unexpected OS behavior. (@todo: add RSHIFT here for win?)
KEY_MINUS,KEY_EQUAL,KEY_LSQUARE,KEY_RSQUARE,KEY_SEMICOLON,KEY_QUOTE,KEY_HASH,KEY_BAR,KEY_COMMA,KEY_DOT,KEY_SLASH, // beware: non-us keyboard layouts
KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,KEY_F11,KEY_F12, // beware: complicated on laptops/osx
KEY_PAD1,KEY_PAD2,KEY_PAD3,KEY_PAD4,KEY_PAD5,KEY_PAD6,KEY_PAD7,KEY_PAD8,KEY_PAD9,KEY_PAD0, // beware: complicated on laptops
KEY_PADADD,KEY_PADSUB,KEY_PADMUL,KEY_PADDIV,KEY_PADDOT,KEY_PADENTER, // beware: complicated on laptops
MOUSE_L, MOUSE_M, MOUSE_R, // @todo: MOUSE_CLICKS,
GAMEPAD_CONNECTED, GAMEPAD_A, GAMEPAD_B, GAMEPAD_X, GAMEPAD_Y,
GAMEPAD_UP, GAMEPAD_DOWN, GAMEPAD_LEFT, GAMEPAD_RIGHT, GAMEPAD_MENU, GAMEPAD_START,
GAMEPAD_LB, GAMEPAD_RB, GAMEPAD_LTHUMB, GAMEPAD_RTHUMB,
WINDOW_BLUR, WINDOW_FOCUS, WINDOW_CLOSE, WINDOW_MINIMIZE, WINDOW_MAXIMIZE, WINDOW_FULLSCREEN, WINDOW_WINDOWED, // MINI/MAXI/RESTORED, SHOWN/HIDDEN
// -- floats: x7 gamepad, x3 mouse, x4 touch, x4 window
GAMEPAD_LPAD, GAMEPAD_LPADX = GAMEPAD_LPAD, GAMEPAD_LPADY,
GAMEPAD_RPAD, GAMEPAD_RPADX = GAMEPAD_RPAD, GAMEPAD_RPADY,
GAMEPAD_LTRIGGER, GAMEPAD_LT = GAMEPAD_LTRIGGER, GAMEPAD_RTRIGGER, GAMEPAD_RT = GAMEPAD_RTRIGGER, GAMEPAD_BATTERY,
MOUSE, MOUSE_X = MOUSE, MOUSE_Y, MOUSE_W,
TOUCH_X1, TOUCH_Y1, TOUCH_X2, TOUCH_Y2,
WINDOW_RESIZE, WINDOW_RESIZEX = WINDOW_RESIZE, WINDOW_RESIZEY, WINDOW_ORIENTATION, WINDOW_BATTERY,
// -- strings: x2 gamepad
GAMEPAD_GUID, GAMEPAD_NAME,
};
// these aliases do check both left and right counterparts
enum INPUT_ALIASES {
KEY_SHIFT = KEY_LSHIFT,
KEY_ALT = KEY_LALT,
KEY_CTRL = KEY_LCTRL,
};

View File

@ -1,98 +1,98 @@
// ----------------------------------------------------------------------------
static void v4k_pre_init() {
window_icon(va("%s%s.png", app_path(), app_name()));
glfwPollEvents();
int i;
#pragma omp parallel for
for( i = 0; i <= 3; ++i) {
/**/ if( i == 0 ) ddraw_init();// init this on thread#0 since it will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
else if( i == 1 ) sprite_init();
else if( i == 2 ) profiler_init();
else if( i == 3 ) storage_mount("save/"), storage_read(), touch_init(); // for ems
}
// window_swap();
}
static void v4k_post_init(float refresh_rate) {
// cook cleanup
cook_stop();
vfs_reload();
// init subsystems that depend on cooked assets now
int i;
#pragma omp parallel for
for( i = 0; i <= 3; ++i ) {
if(i == 0) scene_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
if(i == 0) ui_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
if(i == 0) window_icon(va("%s.png", app_name())); // init on thread #0, because of glfw
if(i == 0) input_init(); // init on thread #0, because of glfw
if(i == 1) audio_init(0);
if(i == 2) script_init(), kit_init(), midi_init();
if(i == 3) network_init();
}
// display window
glfwShowWindow(window);
glfwGetFramebufferSize(window, &w, &h); //glfwGetWindowSize(window, &w, &h);
randset(time_ns() * !tests_captureframes());
boot_time = -time_ss(); // measure boot time, this is continued in window_stats()
// clean any errno setup by cooking stage
errno = 0;
hz = refresh_rate;
// t = glfwGetTime();
// preload brdf LUT early
(void)brdf_lut();
uint64_t fps = optioni("--fps", 0);
if( fps ) {
window_fps_lock(fps);
}
}
// ----------------------------------------------------------------------------
static
void v4k_quit(void) {
storage_flush();
midi_quit();
}
void v4k_init() {
do_once {
// install signal handlers
ifdef(debug, trap_install());
// init panic handler
panic_oom_reserve = SYS_MEM_REALLOC(panic_oom_reserve, 1<<20); // 1MiB
// init glfw
glfw_init();
// enable ansi console
tty_init();
// chdir to root (if invoked as tcc -g -run)
// chdir(app_path());
// skip tcc argvs (if invoked as tcc file.c v4k.c -g -run) (win)
if( __argc > 1 ) if( strstr(__argv[0], "/tcc") || strstr(__argv[0], "\\tcc") ) {
__argc = 0;
}
// create or update cook.zip file
if( /* !COOK_ON_DEMAND && */ have_tools() && cook_jobs() ) {
cook_start(COOK_INI, "**", 0|COOK_ASYNC|COOK_CANCELABLE );
}
atexit(v4k_quit);
}
}
// ----------------------------------------------------------------------------
static void v4k_pre_init() {
window_icon(va("%s%s.png", app_path(), app_name()));
glfwPollEvents();
int i;
#pragma omp parallel for
for( i = 0; i <= 3; ++i) {
/**/ if( i == 0 ) ddraw_init();// init this on thread#0 since it will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
else if( i == 1 ) sprite_init();
else if( i == 2 ) profiler_init();
else if( i == 3 ) storage_mount("save/"), storage_read(), touch_init(); // for ems
}
// window_swap();
}
static void v4k_post_init(float refresh_rate) {
// cook cleanup
cook_stop();
vfs_reload();
// init subsystems that depend on cooked assets now
int i;
#pragma omp parallel for
for( i = 0; i <= 3; ++i ) {
if(i == 0) scene_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
if(i == 0) ui_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
if(i == 0) window_icon(va("%s.png", app_name())); // init on thread #0, because of glfw
if(i == 0) input_init(); // init on thread #0, because of glfw
if(i == 1) audio_init(0);
if(i == 2) script_init(), kit_init(), midi_init();
if(i == 3) network_init();
}
// display window
glfwShowWindow(window);
glfwGetFramebufferSize(window, &w, &h); //glfwGetWindowSize(window, &w, &h);
randset(time_ns() * !tests_captureframes());
boot_time = -time_ss(); // measure boot time, this is continued in window_stats()
// clean any errno setup by cooking stage
errno = 0;
hz = refresh_rate;
// t = glfwGetTime();
// preload brdf LUT early
(void)brdf_lut();
uint64_t fps = optioni("--fps", 0);
if( fps ) {
window_fps_lock(fps);
}
}
// ----------------------------------------------------------------------------
static
void v4k_quit(void) {
storage_flush();
midi_quit();
}
void v4k_init() {
do_once {
// install signal handlers
ifdef(debug, trap_install());
// init panic handler
panic_oom_reserve = SYS_MEM_REALLOC(panic_oom_reserve, 1<<20); // 1MiB
// init glfw
glfw_init();
// enable ansi console
tty_init();
// chdir to root (if invoked as tcc -g -run)
// chdir(app_path());
// skip tcc argvs (if invoked as tcc file.c v4k.c -g -run) (win)
if( __argc > 1 ) if( strstr(__argv[0], "/tcc") || strstr(__argv[0], "\\tcc") ) {
__argc = 0;
}
// create or update cook.zip file
if( /* !COOK_ON_DEMAND && */ have_tools() && cook_jobs() ) {
cook_start(COOK_INI, "**", 0|COOK_ASYNC|COOK_CANCELABLE );
}
atexit(v4k_quit);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,279 +1,279 @@
// -----------------------------------------------------------------------------
// math framework: rand, ease, vec2, vec3, vec4, quat, mat2, mat33, mat34, mat4
// - rlyeh, public domain
//
// Credits: @ands+@krig+@vurtun (PD), @datenwolf (WTFPL2), @evanw+@barerose (CC0), @sgorsten (Unlicense).
#define C_EPSILON (1e-6)
#define C_PI (3.14159265358979323846f) // (3.141592654f)
#define TO_RAD (C_PI/180)
#define TO_DEG (180/C_PI)
// ----------------------------------------------------------------------------
//#define ptr(type) 0[&(type).x]
#define vec2i(x, y ) C_CAST(vec2i,(int)(x), (int)(y) )
#define vec3i(x, y, z ) C_CAST(vec3i,(int)(x), (int)(y), (int)(z) )
#define vec2(x, y ) C_CAST(vec2, (float)(x), (float)(y) )
#define vec3(x, y, z ) C_CAST(vec3, (float)(x), (float)(y), (float)(z), )
#define vec4(x, y, z, w) C_CAST(vec4, (float)(x), (float)(y), (float)(z), (float)(w))
#define quat(x, y, z, w) C_CAST(quat, (float)(x), (float)(y), (float)(z), (float)(w))
#define axis(x, y, z) C_CAST(axis, (float)(x), (float)(y), (float)(z))
#define mat33(...) C_CAST(mat33, __VA_ARGS__ )
#define mat34(...) C_CAST(mat34, __VA_ARGS__ )
#define mat44(...) C_CAST(mat44, __VA_ARGS__ )
typedef union vec2i{ struct { int X,Y; }; struct { int x,y; }; struct { int r,g; }; struct { int w,h; }; struct { int min,max; }; struct { int from,to; }; struct { int src,dst; }; int v2[2]; int array[1]; } vec2i;
typedef union vec3i{ struct { int X,Y,Z; }; struct { int x,y,z; }; struct { int r,g,b; }; struct { int w,h,d; }; struct { int min,max; }; struct { int from,to,step; }; struct { int src,dst; }; int v3[3]; int array[1]; } vec3i;
typedef union vec2 { struct { float X,Y; }; struct { float x,y; }; struct { float r,g; }; struct { float w,h; }; struct { float min,max; }; struct { float from,to; }; struct { float src,dst; }; float v2[2]; float array[1]; } vec2;
typedef union vec3 { struct { float X,Y,Z; }; struct { float x,y,z; }; struct { float r,g,b; }; struct { float min,max; }; struct { float from,to; }; vec2 xy; vec2 rg; vec2 wh; float v3[3]; float array[1]; } vec3;
typedef union vec4 { struct { float X,Y,Z,W; }; struct { float x,y,z,w; }; struct { float r,g,b,a; }; struct { float min,max; }; struct { float from,to; }; vec2 xy; vec3 xyz; vec2 rg; vec3 rgb; vec2 wh; vec3 whd; float v4[4]; float array[1]; } vec4;
typedef union quat { struct { float X,Y,Z,W; }; struct { float x,y,z,w; }; vec3 xyz; vec4 xyzw; float v4[4]; float array[1]; } quat;
typedef float mat33[9];
typedef float mat34[12];
typedef float mat44[16];
// ----------------------------------------------------------------------------
API void randset(uint64_t state);
API uint64_t rand64(void);
API double randf(void); // [0, 1) interval
API int randi(int mini, int maxi); // [mini, maxi) interval
// ----------------------------------------------------------------------------
API float simplex1( float x );
API float simplex2( vec2 xy );
API float simplex3( vec3 xyz );
API float simplex4( vec4 xyzw );
// ----------------------------------------------------------------------------
API float deg (float radians);
API float rad (float degrees);
API int mini (int a, int b);
API int maxi (int a, int b);
API int absi (int a );
API int clampi (int v,int a,int b);
API float minf (float a, float b);
API float maxf (float a, float b);
API float absf (float a );
API float pmodf (float a, float b);
API float signf (float a) ;
API float clampf (float v,float a,float b);
API float mixf (float a,float b,float t);
API float slerpf (float a,float b,float t);
API float fractf (float a);
// ----------------------------------------------------------------------------
API vec2 ptr2 (const float *a );
//
API vec2 neg2 (vec2 a );
API vec2 add2 (vec2 a, vec2 b);
API vec2 sub2 (vec2 a, vec2 b);
API vec2 mul2 (vec2 a, vec2 b);
API vec2 div2 (vec2 a, vec2 b);
API vec2 inc2 (vec2 a, float b);
API vec2 dec2 (vec2 a, float b);
API vec2 scale2 (vec2 a, float b);
API vec2 pmod2 (vec2 a, float b);
API vec2 min2 (vec2 a, vec2 b);
API vec2 max2 (vec2 a, vec2 b);
API vec2 abs2 (vec2 a );
API vec2 floor2 (vec2 a );
API vec2 fract2 (vec2 a );
API vec2 ceil2 (vec2 a );
API float dot2 (vec2 a, vec2 b);
API vec2 refl2 (vec2 a, vec2 b);
API float cross2 (vec2 a, vec2 b);
API float len2sq (vec2 a );
API float len2 (vec2 a );
API vec2 norm2 (vec2 a );
API int finite2 (vec2 a );
API vec2 mix2 (vec2 a,vec2 b,float t);
API vec2 clamp2(vec2 v,vec2 a,vec2 b);
API vec2 clamp2f(vec2 v,float a,float b);
// ----------------------------------------------------------------------------
API vec3 rnd3 (void); // @todo: rnd2,rnd4,rndq
API vec3 ptr3 (const float *a );
API vec3 vec23 (vec2 a, float z );
//
API vec3 neg3 (vec3 a );
API vec3 add3 (vec3 a, vec3 b);
API vec3 sub3 (vec3 a, vec3 b);
API vec3 mul3 (vec3 a, vec3 b);
API vec3 div3 (vec3 a, vec3 b);
API vec3 inc3 (vec3 a, float b);
API vec3 dec3 (vec3 a, float b);
API vec3 scale3 (vec3 a, float b);
API vec3 pmod3 (vec3 a, float b);
API vec3 min3 (vec3 a, vec3 b);
API vec3 max3 (vec3 a, vec3 b);
API vec3 abs3 (vec3 a );
API vec3 floor3 (vec3 a );
API vec3 fract3 (vec3 a );
API vec3 ceil3 (vec3 a );
API vec3 cross3 (vec3 a, vec3 b);
API float dot3 (vec3 a, vec3 b);
API vec3 refl3 (vec3 a, vec3 b);
API float len3sq (vec3 a );
API float len3 (vec3 a );
API vec3 norm3 (vec3 a );
API vec3 norm3sq (vec3 a );
API int finite3 (vec3 a );
API vec3 mix3 (vec3 a,vec3 b,float t);
API vec3 clamp3(vec3 v,vec3 a,vec3 b);
API vec3 clamp3f(vec3 v,float a,float b);
//vec3 tricross3 (vec3 a, vec3 b, vec3 c);
API void ortho3 (vec3 *left, vec3 *up, vec3 v);
API vec3 rotatex3 (vec3 dir, float degrees);
API vec3 rotatey3 (vec3 dir, float degrees);
API vec3 rotatez3 (vec3 dir, float degrees);
// ----------------------------------------------------------------------------
API vec4 ptr4 (const float *a );
API vec4 vec34 (vec3 a, float w );
//
API vec4 neg4 (vec4 a );
API vec4 add4 (vec4 a, vec4 b);
API vec4 sub4 (vec4 a, vec4 b);
API vec4 mul4 (vec4 a, vec4 b);
API vec4 div4 (vec4 a, vec4 b);
API vec4 inc4 (vec4 a, float b);
API vec4 dec4 (vec4 a, float b);
API vec4 scale4 (vec4 a, float b);
API vec4 pmod4 (vec4 a, float b);
API vec4 min4 (vec4 a, vec4 b);
API vec4 max4 (vec4 a, vec4 b);
API vec4 abs4 (vec4 a );
API vec4 floor4 (vec4 a );
API vec4 fract4 (vec4 a );
API vec4 ceil4 (vec4 a );
API float dot4 (vec4 a, vec4 b);
API vec4 refl4 (vec4 a, vec4 b);
API float len4sq (vec4 a );
API float len4 (vec4 a );
API vec4 norm4 (vec4 a );
API vec4 norm4sq (vec4 a );
API int finite4 (vec4 a );
API vec4 mix4 (vec4 a,vec4 b,float t);
API vec4 clamp4(vec4 v,vec4 a,vec4 b);
API vec4 clamp4f(vec4 v,float a,float b);
// vec4 cross4(vec4 v0, vec4 v1);
// ----------------------------------------------------------------------------
API quat idq ( );
API quat ptrq (const float *a );
API quat vec3q (vec3 a, float w );
API quat vec4q (vec4 a );
//
API quat negq (quat a );
API quat conjq (quat a );
API quat addq (quat a, quat b);
API quat subq (quat a, quat b);
API quat mulq (quat p, quat q);
API quat scaleq (quat a, float s);
API quat normq (quat a );
API float dotq (quat a, quat b);
API quat mixq(quat a, quat b, float t);
/* quat lerpq(quat a, quat b, float s);
return norm(quat((1-s)*a.x + s*b.x, (1-s)*a.y + s*b.y, (1-s)*a.z + s*b.z, (1-s)*a.w + s*b.w));
}*/
API quat slerpq(quat a, quat b, float s);
API quat rotationq(float deg,float x,float y,float z);
API quat mat44q (mat44 M);
API vec3 rotate3q_2(vec3 v, quat q);
API vec3 rotate3q(vec3 v, quat r);
// euler <-> quat
API vec3 euler (quat q);
API quat eulerq (vec3 pyr_degrees);
// ----------------------------------------------------------------------------
API void scaling33(mat33 m, float x, float y, float z);
API void scale33(mat33 m, float x, float y, float z);
API void id33(mat33 m);
API void extract33(mat33 m, const mat44 m4);
API void copy33(mat33 m, const mat33 a);//
API vec3 mulv33(mat33 m, vec3 v);
API void multiply33x2(mat33 m, const mat33 a, const mat33 b);
API void rotation33(mat33 m, float degrees, float x,float y,float z);
API void rotationq33(mat33 m, quat q);
API void rotate33(mat33 r, float degrees, float x,float y,float z);
API void compose33(mat33 m, quat r, vec3 s);
// ----------------------------------------------------------------------------
API void id34(mat34 m);
API void copy34(mat34 m, const mat34 a);
API void scale34(mat34 m, float s);
API void add34(mat34 m, mat34 n);
API void muladd34(mat34 m, mat34 n, float s);
API void add34x2(mat34 m, mat34 n, mat34 o);
API void lerp34(mat34 m, mat34 n, mat34 o, float alpha); // mix34?
API void multiply34x2(mat34 m, const mat34 m0, const mat34 m1);
API void multiply34(mat34 m, const mat34 a);
API void multiply34x3(mat34 m, const mat34 a, const mat34 b, const mat34 c);
API void compose34(mat34 m, vec3 t, quat q, vec3 s);
API void invert34(mat34 m, const mat34 o);
// ----------------------------------------------------------------------------
API void id44(mat44 m);
API void identity44(mat44 m);
API void copy44(mat44 m, const mat44 a);
API void multiply44x2(mat44 m, const mat44 a, const mat44 b);
API void multiply44x3(mat44 m, const mat44 a, const mat44 b, const mat44 c);
API void multiply44(mat44 m, const mat44 a);
// ---
API void ortho44(mat44 m, float l, float r, float b, float t, float n, float f);
API void frustum44(mat44 m, float l, float r, float b, float t, float n, float f);
API void perspective44(mat44 m, float fovy_degrees, float aspect, float nearp, float farp);
API void lookat44(mat44 m, vec3 eye, vec3 center, vec3 up);
// ---
API void translation44(mat44 m, float x, float y, float z);
API void translate44(mat44 m, float x, float y, float z);
API void relocate44(mat44 m, float x, float y, float z);
API void rotationq44(mat44 m, quat q);
API void rotation44(mat44 m, float degrees, float x, float y, float z);
API void rotate44(mat44 m, float degrees, float x, float y, float z);
API void scaling44(mat44 m, float x, float y, float z);
API void scale44(mat44 m, float x, float y, float z);
// ---
API void transpose44(mat44 m, const mat44 a);
API float det44(const mat44 M);
API bool invert44(mat44 T, const mat44 M);
API void compose44(mat44 m, vec3 t, quat q, vec3 s);
// ----------------------------------------------------------------------------
API vec3 transformq(const quat q, const vec3 v);
API vec3 transform33(const mat33 m, vec3 p);
API vec3 transform344(const mat44 m, const vec3 p);
API vec4 transform444(const mat44 m, const vec4 p);
API bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
// ----------------------------------------------------------------------------
// debugging and utils
API void print2i( vec2i v );
API void print3i( vec3i v );
API void print2( vec2 v );
API void print3( vec3 v );
API void print4( vec4 v );
API void printq( quat q );
API void print33( float *m );
API void print34( float *m );
API void print44( float *m );
// -----------------------------------------------------------------------------
// math framework: rand, ease, vec2, vec3, vec4, quat, mat2, mat33, mat34, mat4
// - rlyeh, public domain
//
// Credits: @ands+@krig+@vurtun (PD), @datenwolf (WTFPL2), @evanw+@barerose (CC0), @sgorsten (Unlicense).
#define C_EPSILON (1e-6)
#define C_PI (3.14159265358979323846f) // (3.141592654f)
#define TO_RAD (C_PI/180)
#define TO_DEG (180/C_PI)
// ----------------------------------------------------------------------------
//#define ptr(type) 0[&(type).x]
#define vec2i(x, y ) C_CAST(vec2i,(int)(x), (int)(y) )
#define vec3i(x, y, z ) C_CAST(vec3i,(int)(x), (int)(y), (int)(z) )
#define vec2(x, y ) C_CAST(vec2, (float)(x), (float)(y) )
#define vec3(x, y, z ) C_CAST(vec3, (float)(x), (float)(y), (float)(z), )
#define vec4(x, y, z, w) C_CAST(vec4, (float)(x), (float)(y), (float)(z), (float)(w))
#define quat(x, y, z, w) C_CAST(quat, (float)(x), (float)(y), (float)(z), (float)(w))
#define axis(x, y, z) C_CAST(axis, (float)(x), (float)(y), (float)(z))
#define mat33(...) C_CAST(mat33, __VA_ARGS__ )
#define mat34(...) C_CAST(mat34, __VA_ARGS__ )
#define mat44(...) C_CAST(mat44, __VA_ARGS__ )
typedef union vec2i{ struct { int X,Y; }; struct { int x,y; }; struct { int r,g; }; struct { int w,h; }; struct { int min,max; }; struct { int from,to; }; struct { int src,dst; }; int v2[2]; int array[1]; } vec2i;
typedef union vec3i{ struct { int X,Y,Z; }; struct { int x,y,z; }; struct { int r,g,b; }; struct { int w,h,d; }; struct { int min,max; }; struct { int from,to,step; }; struct { int src,dst; }; int v3[3]; int array[1]; } vec3i;
typedef union vec2 { struct { float X,Y; }; struct { float x,y; }; struct { float r,g; }; struct { float w,h; }; struct { float min,max; }; struct { float from,to; }; struct { float src,dst; }; float v2[2]; float array[1]; } vec2;
typedef union vec3 { struct { float X,Y,Z; }; struct { float x,y,z; }; struct { float r,g,b; }; struct { float min,max; }; struct { float from,to; }; vec2 xy; vec2 rg; vec2 wh; float v3[3]; float array[1]; } vec3;
typedef union vec4 { struct { float X,Y,Z,W; }; struct { float x,y,z,w; }; struct { float r,g,b,a; }; struct { float min,max; }; struct { float from,to; }; vec2 xy; vec3 xyz; vec2 rg; vec3 rgb; vec2 wh; vec3 whd; float v4[4]; float array[1]; } vec4;
typedef union quat { struct { float X,Y,Z,W; }; struct { float x,y,z,w; }; vec3 xyz; vec4 xyzw; float v4[4]; float array[1]; } quat;
typedef float mat33[9];
typedef float mat34[12];
typedef float mat44[16];
// ----------------------------------------------------------------------------
API void randset(uint64_t state);
API uint64_t rand64(void);
API double randf(void); // [0, 1) interval
API int randi(int mini, int maxi); // [mini, maxi) interval
// ----------------------------------------------------------------------------
API float simplex1( float x );
API float simplex2( vec2 xy );
API float simplex3( vec3 xyz );
API float simplex4( vec4 xyzw );
// ----------------------------------------------------------------------------
API float deg (float radians);
API float rad (float degrees);
API int mini (int a, int b);
API int maxi (int a, int b);
API int absi (int a );
API int clampi (int v,int a,int b);
API float minf (float a, float b);
API float maxf (float a, float b);
API float absf (float a );
API float pmodf (float a, float b);
API float signf (float a) ;
API float clampf (float v,float a,float b);
API float mixf (float a,float b,float t);
API float slerpf (float a,float b,float t);
API float fractf (float a);
// ----------------------------------------------------------------------------
API vec2 ptr2 (const float *a );
//
API vec2 neg2 (vec2 a );
API vec2 add2 (vec2 a, vec2 b);
API vec2 sub2 (vec2 a, vec2 b);
API vec2 mul2 (vec2 a, vec2 b);
API vec2 div2 (vec2 a, vec2 b);
API vec2 inc2 (vec2 a, float b);
API vec2 dec2 (vec2 a, float b);
API vec2 scale2 (vec2 a, float b);
API vec2 pmod2 (vec2 a, float b);
API vec2 min2 (vec2 a, vec2 b);
API vec2 max2 (vec2 a, vec2 b);
API vec2 abs2 (vec2 a );
API vec2 floor2 (vec2 a );
API vec2 fract2 (vec2 a );
API vec2 ceil2 (vec2 a );
API float dot2 (vec2 a, vec2 b);
API vec2 refl2 (vec2 a, vec2 b);
API float cross2 (vec2 a, vec2 b);
API float len2sq (vec2 a );
API float len2 (vec2 a );
API vec2 norm2 (vec2 a );
API int finite2 (vec2 a );
API vec2 mix2 (vec2 a,vec2 b,float t);
API vec2 clamp2(vec2 v,vec2 a,vec2 b);
API vec2 clamp2f(vec2 v,float a,float b);
// ----------------------------------------------------------------------------
API vec3 rnd3 (void); // @todo: rnd2,rnd4,rndq
API vec3 ptr3 (const float *a );
API vec3 vec23 (vec2 a, float z );
//
API vec3 neg3 (vec3 a );
API vec3 add3 (vec3 a, vec3 b);
API vec3 sub3 (vec3 a, vec3 b);
API vec3 mul3 (vec3 a, vec3 b);
API vec3 div3 (vec3 a, vec3 b);
API vec3 inc3 (vec3 a, float b);
API vec3 dec3 (vec3 a, float b);
API vec3 scale3 (vec3 a, float b);
API vec3 pmod3 (vec3 a, float b);
API vec3 min3 (vec3 a, vec3 b);
API vec3 max3 (vec3 a, vec3 b);
API vec3 abs3 (vec3 a );
API vec3 floor3 (vec3 a );
API vec3 fract3 (vec3 a );
API vec3 ceil3 (vec3 a );
API vec3 cross3 (vec3 a, vec3 b);
API float dot3 (vec3 a, vec3 b);
API vec3 refl3 (vec3 a, vec3 b);
API float len3sq (vec3 a );
API float len3 (vec3 a );
API vec3 norm3 (vec3 a );
API vec3 norm3sq (vec3 a );
API int finite3 (vec3 a );
API vec3 mix3 (vec3 a,vec3 b,float t);
API vec3 clamp3(vec3 v,vec3 a,vec3 b);
API vec3 clamp3f(vec3 v,float a,float b);
//vec3 tricross3 (vec3 a, vec3 b, vec3 c);
API void ortho3 (vec3 *left, vec3 *up, vec3 v);
API vec3 rotatex3 (vec3 dir, float degrees);
API vec3 rotatey3 (vec3 dir, float degrees);
API vec3 rotatez3 (vec3 dir, float degrees);
// ----------------------------------------------------------------------------
API vec4 ptr4 (const float *a );
API vec4 vec34 (vec3 a, float w );
//
API vec4 neg4 (vec4 a );
API vec4 add4 (vec4 a, vec4 b);
API vec4 sub4 (vec4 a, vec4 b);
API vec4 mul4 (vec4 a, vec4 b);
API vec4 div4 (vec4 a, vec4 b);
API vec4 inc4 (vec4 a, float b);
API vec4 dec4 (vec4 a, float b);
API vec4 scale4 (vec4 a, float b);
API vec4 pmod4 (vec4 a, float b);
API vec4 min4 (vec4 a, vec4 b);
API vec4 max4 (vec4 a, vec4 b);
API vec4 abs4 (vec4 a );
API vec4 floor4 (vec4 a );
API vec4 fract4 (vec4 a );
API vec4 ceil4 (vec4 a );
API float dot4 (vec4 a, vec4 b);
API vec4 refl4 (vec4 a, vec4 b);
API float len4sq (vec4 a );
API float len4 (vec4 a );
API vec4 norm4 (vec4 a );
API vec4 norm4sq (vec4 a );
API int finite4 (vec4 a );
API vec4 mix4 (vec4 a,vec4 b,float t);
API vec4 clamp4(vec4 v,vec4 a,vec4 b);
API vec4 clamp4f(vec4 v,float a,float b);
// vec4 cross4(vec4 v0, vec4 v1);
// ----------------------------------------------------------------------------
API quat idq ( );
API quat ptrq (const float *a );
API quat vec3q (vec3 a, float w );
API quat vec4q (vec4 a );
//
API quat negq (quat a );
API quat conjq (quat a );
API quat addq (quat a, quat b);
API quat subq (quat a, quat b);
API quat mulq (quat p, quat q);
API quat scaleq (quat a, float s);
API quat normq (quat a );
API float dotq (quat a, quat b);
API quat mixq(quat a, quat b, float t);
/* quat lerpq(quat a, quat b, float s);
return norm(quat((1-s)*a.x + s*b.x, (1-s)*a.y + s*b.y, (1-s)*a.z + s*b.z, (1-s)*a.w + s*b.w));
}*/
API quat slerpq(quat a, quat b, float s);
API quat rotationq(float deg,float x,float y,float z);
API quat mat44q (mat44 M);
API vec3 rotate3q_2(vec3 v, quat q);
API vec3 rotate3q(vec3 v, quat r);
// euler <-> quat
API vec3 euler (quat q);
API quat eulerq (vec3 pyr_degrees);
// ----------------------------------------------------------------------------
API void scaling33(mat33 m, float x, float y, float z);
API void scale33(mat33 m, float x, float y, float z);
API void id33(mat33 m);
API void extract33(mat33 m, const mat44 m4);
API void copy33(mat33 m, const mat33 a);//
API vec3 mulv33(mat33 m, vec3 v);
API void multiply33x2(mat33 m, const mat33 a, const mat33 b);
API void rotation33(mat33 m, float degrees, float x,float y,float z);
API void rotationq33(mat33 m, quat q);
API void rotate33(mat33 r, float degrees, float x,float y,float z);
API void compose33(mat33 m, quat r, vec3 s);
// ----------------------------------------------------------------------------
API void id34(mat34 m);
API void copy34(mat34 m, const mat34 a);
API void scale34(mat34 m, float s);
API void add34(mat34 m, mat34 n);
API void muladd34(mat34 m, mat34 n, float s);
API void add34x2(mat34 m, mat34 n, mat34 o);
API void lerp34(mat34 m, mat34 n, mat34 o, float alpha); // mix34?
API void multiply34x2(mat34 m, const mat34 m0, const mat34 m1);
API void multiply34(mat34 m, const mat34 a);
API void multiply34x3(mat34 m, const mat34 a, const mat34 b, const mat34 c);
API void compose34(mat34 m, vec3 t, quat q, vec3 s);
API void invert34(mat34 m, const mat34 o);
// ----------------------------------------------------------------------------
API void id44(mat44 m);
API void identity44(mat44 m);
API void copy44(mat44 m, const mat44 a);
API void multiply44x2(mat44 m, const mat44 a, const mat44 b);
API void multiply44x3(mat44 m, const mat44 a, const mat44 b, const mat44 c);
API void multiply44(mat44 m, const mat44 a);
// ---
API void ortho44(mat44 m, float l, float r, float b, float t, float n, float f);
API void frustum44(mat44 m, float l, float r, float b, float t, float n, float f);
API void perspective44(mat44 m, float fovy_degrees, float aspect, float nearp, float farp);
API void lookat44(mat44 m, vec3 eye, vec3 center, vec3 up);
// ---
API void translation44(mat44 m, float x, float y, float z);
API void translate44(mat44 m, float x, float y, float z);
API void relocate44(mat44 m, float x, float y, float z);
API void rotationq44(mat44 m, quat q);
API void rotation44(mat44 m, float degrees, float x, float y, float z);
API void rotate44(mat44 m, float degrees, float x, float y, float z);
API void scaling44(mat44 m, float x, float y, float z);
API void scale44(mat44 m, float x, float y, float z);
// ---
API void transpose44(mat44 m, const mat44 a);
API float det44(const mat44 M);
API bool invert44(mat44 T, const mat44 M);
API void compose44(mat44 m, vec3 t, quat q, vec3 s);
// ----------------------------------------------------------------------------
API vec3 transformq(const quat q, const vec3 v);
API vec3 transform33(const mat33 m, vec3 p);
API vec3 transform344(const mat44 m, const vec3 p);
API vec4 transform444(const mat44 m, const vec4 p);
API bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
// ----------------------------------------------------------------------------
// debugging and utils
API void print2i( vec2i v );
API void print3i( vec3i v );
API void print2( vec2 v );
API void print3( vec3 v );
API void print4( vec4 v );
API void printq( quat q );
API void print33( float *m );
API void print34( float *m );
API void print44( float *m );

View File

@ -1,101 +1,101 @@
size_t dlmalloc_usable_size(void*); // __ANDROID_API__
#if is(bsd) || is(osx) // bsd or osx
# include <malloc/malloc.h>
#else
# include <malloc.h>
#endif
#ifndef SYS_MEM_INIT
#define SYS_MEM_INIT()
#define SYS_MEM_REALLOC realloc
#define SYS_MEM_SIZE /* bsd/osx, then win32, then ems/__GLIBC__, then __ANDROID_API__ */ \
ifdef(osx, malloc_size, ifdef(bsd, malloc_size, \
ifdef(win32, _msize, malloc_usable_size)))
#endif
// xrealloc --------------------------------------------------------------------
static __thread uint64_t xstats_current = 0, xstats_total = 0, xstats_allocs = 0;
void* xrealloc(void* oldptr, size_t size) {
static __thread int once = 0; for(;!once;once = 1) SYS_MEM_INIT();
// for stats
size_t oldsize = xsize(oldptr);
void *ptr = SYS_MEM_REALLOC(oldptr, size);
if( !ptr && size ) {
PANIC("Not memory enough (trying to allocate %u bytes)", (unsigned)size);
}
#if ENABLE_MEMORY_POISON
if( !oldptr && size ) {
memset(ptr, 0xCD, size);
}
#endif
// for stats
if( oldptr ) {
xstats_current += (int64_t)size - (int64_t)oldsize;
xstats_allocs -= !size;
} else {
xstats_current += size;
xstats_allocs += !!size;
}
if( xstats_current > xstats_total ) {
xstats_total = xstats_current;
}
return ptr;
}
size_t xsize(void* p) {
if( p ) return SYS_MEM_SIZE(p);
return 0;
}
char *xstats(void) {
uint64_t xtra = 0; // xstats_allocs * 65536; // assumes 64K pagesize for every alloc
return va("%03u/%03uMB", (unsigned)((xstats_current+xtra) / 1024 / 1024), (unsigned)((xstats_total+xtra) / 1024 / 1024));
}
// stack -----------------------------------------------------------------------
void* stack(int bytes) { // use negative bytes to rewind stack
static __thread uint8_t *stack_mem = 0;
static __thread uint64_t stack_ptr = 0;
static __thread uint64_t stack_max = 0; // watch this var, in case you want to fine tune 4 MiB value below
if( bytes < 0 ) {
if( stack_ptr > stack_max ) stack_max = stack_ptr;
return (stack_ptr = 0), NULL;
}
if( !stack_mem ) stack_mem = xrealloc(stack_mem, xsize(stack_mem) + 4 * 1024 * 1024);
return &stack_mem[ (stack_ptr += bytes) - bytes ];
}
// leaks ----------------------------------------------------------------------
void* watch( void *ptr, int sz ) {
static __thread int open = 1;
if( ptr && open ) {
open = 0;
char buf[256];
sprintf(buf, "%p.mem", ptr);
for( FILE *fp = fopen(buf, "a+"); fp; fclose(fp), fp = 0 ) {
fseek(fp, 0L, SEEK_END);
const char *cs = callstack( +16 ); // +48
fprintf(fp, "Built %s %s\n", __DATE__, __TIME__); // today() instead?
fprintf(fp, "Memleak address: [%p], size: %d\n%s\n", ptr, sz, cs ? cs : "No callstack.");
}
open = 1;
}
return ptr;
}
void* forget( void *ptr ) {
if( ptr ) {
char buf[256];
sprintf(buf, "%p.mem", ptr);
unlink(buf);
}
return ptr;
}
size_t dlmalloc_usable_size(void*); // __ANDROID_API__
#if is(bsd) || is(osx) // bsd or osx
# include <malloc/malloc.h>
#else
# include <malloc.h>
#endif
#ifndef SYS_MEM_INIT
#define SYS_MEM_INIT()
#define SYS_MEM_REALLOC realloc
#define SYS_MEM_SIZE /* bsd/osx, then win32, then ems/__GLIBC__, then __ANDROID_API__ */ \
ifdef(osx, malloc_size, ifdef(bsd, malloc_size, \
ifdef(win32, _msize, malloc_usable_size)))
#endif
// xrealloc --------------------------------------------------------------------
static __thread uint64_t xstats_current = 0, xstats_total = 0, xstats_allocs = 0;
void* xrealloc(void* oldptr, size_t size) {
static __thread int once = 0; for(;!once;once = 1) SYS_MEM_INIT();
// for stats
size_t oldsize = xsize(oldptr);
void *ptr = SYS_MEM_REALLOC(oldptr, size);
if( !ptr && size ) {
PANIC("Not memory enough (trying to allocate %u bytes)", (unsigned)size);
}
#if ENABLE_MEMORY_POISON
if( !oldptr && size ) {
memset(ptr, 0xCD, size);
}
#endif
// for stats
if( oldptr ) {
xstats_current += (int64_t)size - (int64_t)oldsize;
xstats_allocs -= !size;
} else {
xstats_current += size;
xstats_allocs += !!size;
}
if( xstats_current > xstats_total ) {
xstats_total = xstats_current;
}
return ptr;
}
size_t xsize(void* p) {
if( p ) return SYS_MEM_SIZE(p);
return 0;
}
char *xstats(void) {
uint64_t xtra = 0; // xstats_allocs * 65536; // assumes 64K pagesize for every alloc
return va("%03u/%03uMB", (unsigned)((xstats_current+xtra) / 1024 / 1024), (unsigned)((xstats_total+xtra) / 1024 / 1024));
}
// stack -----------------------------------------------------------------------
void* stack(int bytes) { // use negative bytes to rewind stack
static __thread uint8_t *stack_mem = 0;
static __thread uint64_t stack_ptr = 0;
static __thread uint64_t stack_max = 0; // watch this var, in case you want to fine tune 4 MiB value below
if( bytes < 0 ) {
if( stack_ptr > stack_max ) stack_max = stack_ptr;
return (stack_ptr = 0), NULL;
}
if( !stack_mem ) stack_mem = xrealloc(stack_mem, xsize(stack_mem) + 4 * 1024 * 1024);
return &stack_mem[ (stack_ptr += bytes) - bytes ];
}
// leaks ----------------------------------------------------------------------
void* watch( void *ptr, int sz ) {
static __thread int open = 1;
if( ptr && open ) {
open = 0;
char buf[256];
sprintf(buf, "%p.mem", ptr);
for( FILE *fp = fopen(buf, "a+"); fp; fclose(fp), fp = 0 ) {
fseek(fp, 0L, SEEK_END);
const char *cs = callstack( +16 ); // +48
fprintf(fp, "Built %s %s\n", __DATE__, __TIME__); // today() instead?
fprintf(fp, "Memleak address: [%p], size: %d\n%s\n", ptr, sz, cs ? cs : "No callstack.");
}
open = 1;
}
return ptr;
}
void* forget( void *ptr ) {
if( ptr ) {
char buf[256];
sprintf(buf, "%p.mem", ptr);
unlink(buf);
}
return ptr;
}

View File

@ -1,37 +1,37 @@
// -----------------------------------------------------------------------------
// memory framework
// - rlyeh, public domain
// memory leaks detector
#if ENABLE_MEMORY_LEAKS
#define WATCH(ptr,sz) watch((ptr), (sz))
#define FORGET(ptr) forget(ptr)
#else
#define WATCH(ptr,sz) (ptr)
#define FORGET(ptr) (ptr)
#endif
// default allocator (aborts on out-of-mem)
API void* xrealloc(void* p, size_t sz);
API size_t xsize(void* p);
API char* xstats(void);
// stack based allocator (negative bytes does rewind stack, like when entering new frame)
API void* stack(int bytes);
// memory leaks api (this is already integrated as long as you compile with -DENABLE_MEMORY_LEAKS)
API void* watch( void *ptr, int sz );
API void* forget( void *ptr );
// memory api
#define ALLOCSIZE(p) xsize(p)
#define MALLOC(n) REALLOC_(0,(n))
#define FREE(p) REALLOC_((p), 0)
#define REALLOC(p,n) REALLOC_((p),(n))
#define CALLOC(m,n) CALLOC_((m),(n))
#define STRDUP(s) STRDUP_(s)
#define ALLOCA(n) ifdef(gcc, __builtin_alloca(n), ifdef(win32, _alloca(n), __builtin_alloca(n)))
static FORCE_INLINE void *(REALLOC_)(void *p, size_t n) { return n ? WATCH(xrealloc(p,n),n) : xrealloc(FORGET(p),0); } ///-
static FORCE_INLINE void *(CALLOC_)(size_t m, size_t n) { return n *= m, memset(REALLOC(0,n),0,n); } ///-
static FORCE_INLINE char *(STRDUP_)(const char *s) { size_t n = strlen(s)+1; return ((char*)memcpy(REALLOC(0,n), s, n)); } ///-
// -----------------------------------------------------------------------------
// memory framework
// - rlyeh, public domain
// memory leaks detector
#if ENABLE_MEMORY_LEAKS
#define WATCH(ptr,sz) watch((ptr), (sz))
#define FORGET(ptr) forget(ptr)
#else
#define WATCH(ptr,sz) (ptr)
#define FORGET(ptr) (ptr)
#endif
// default allocator (aborts on out-of-mem)
API void* xrealloc(void* p, size_t sz);
API size_t xsize(void* p);
API char* xstats(void);
// stack based allocator (negative bytes does rewind stack, like when entering new frame)
API void* stack(int bytes);
// memory leaks api (this is already integrated as long as you compile with -DENABLE_MEMORY_LEAKS)
API void* watch( void *ptr, int sz );
API void* forget( void *ptr );
// memory api
#define ALLOCSIZE(p) xsize(p)
#define MALLOC(n) REALLOC_(0,(n))
#define FREE(p) REALLOC_((p), 0)
#define REALLOC(p,n) REALLOC_((p),(n))
#define CALLOC(m,n) CALLOC_((m),(n))
#define STRDUP(s) STRDUP_(s)
#define ALLOCA(n) ifdef(gcc, __builtin_alloca(n), ifdef(win32, _alloca(n), __builtin_alloca(n)))
static FORCE_INLINE void *(REALLOC_)(void *p, size_t n) { return n ? WATCH(xrealloc(p,n),n) : xrealloc(FORGET(p),0); } ///-
static FORCE_INLINE void *(CALLOC_)(size_t m, size_t n) { return n *= m, memset(REALLOC(0,n),0,n); } ///-
static FORCE_INLINE char *(STRDUP_)(const char *s) { size_t n = strlen(s)+1; return ((char*)memcpy(REALLOC(0,n), s, n)); } ///-

File diff suppressed because it is too large Load Diff

View File

@ -1,83 +1,83 @@
// high-level, socket-less networking api. inspired by Quake, MPI and RenderBuckets theories.
// - rlyeh, public domain
//
// Usage:
// 1. configure networked memory buffers with flags (world, player1, player2, etc). network_buffer();
// 2. then during game loop:
// - modify your buffers as much as needed.
// - sync buffers at least once per frame. network_sync();
// - render your world
// 3. optionally, monitor network status & variables. network_get();
//
// @todo: maybe network_send(msg) + msg *network_recv(); instead of event queue of network_sync() ?
//enum { NETWORK_HANDSHAKE, NETWORK_ENCRYPT, NETWORK_VERSIONED, NETWORK_CHECKSUM }; // negotiation
//enum { NETWORK_TCP, NETWORK_UDP, NETWORK_KCP, NETWORK_ENET, NETWORK_WEBSOCKET }; // transport, where
enum { NETWORK_BIND = 2, NETWORK_CONNECT = 4, NETWORK_NOFAIL = 8 };
API void network_create(unsigned max_clients, const char *ip, const char *port, unsigned flags); // both ip and port can be null
//enum { NETWORK_LOSSY, NETWORK_COMPRESS }; // post-processes
//enum { NETWORK_PREDICT, NETWORK_RECONCILE, NETWORK_INTERPOLATE, NETWORK_COMPENSATE }; // time authority, when
//enum { NETWORK_LAGS, NETWORK_DROPS, NETWORK_THROTTLES, NETWORK_DUPES }; // quality sim, how much
//enum { NETWORK_CONST = 1, NETWORK_64,NETWORK_32,NETWORK_16,NETWORK_8, NETWORK_FLT, NETWORK_STR, NETWORK_BLOB }; // type, what
enum { NETWORK_SEND = 2, NETWORK_RECV = 4 };
enum { NETWORK_UNRELIABLE = 8, NETWORK_UNORDERED = 16/*, NETWORK_PRIORITY = 32*/ };
API void* network_buffer(void *ptr, unsigned sz, uint64_t flags, int64_t rank); // configures a shared/networked buffer
API char** network_sync(unsigned timeout_ms); // syncs all buffers & returns null-terminated list of network events
enum {
NETWORK_EVENT_CONNECT,
NETWORK_EVENT_DISCONNECT,
NETWORK_EVENT_RECEIVE,
NETWORK_EVENT_DISCONNECT_TIMEOUT,
/* offset from internal networking events */
NETWORK_EVENT_RPC = 10,
NETWORK_EVENT_RPC_RESP,
};
/* errcode and errstr are optional arguments, pass NULL to ignore them,
errstr is filled by va() */
API int network_event(const char *msg, int *errcode, char **errstr);
enum { NETWORK_RANK = 0 }; // [0..N] where 0 is server
enum { NETWORK_PING = 1 }; // NETWORK_BANDWIDTH, NETWORK_QUALITY };
enum { NETWORK_PORT = 2, NETWORK_IP, NETWORK_LIVE };
enum { NETWORK_SEND_MS = 4 };
enum { NETWORK_BUF_CLEAR_ON_JOIN = 5 };
enum { NETWORK_USERID = 7, /*NETWORK_SALT,*/ NETWORK_COUNT/*N users*/ /*...*/, NETWORK_CAPACITY };
API int64_t network_get(uint64_t key);
API int64_t network_put(uint64_t key, int64_t value);
API void network_rpc(const char *signature, void *function);
API void network_rpc_send_to(int64_t rank, unsigned id, const char *cmdline);
API void network_rpc_send(unsigned id, const char *cmdline);
// -----------------------------------------------------------------------------
// low-level api (sockets based)
API bool server_bind(int max_clients, int port);
API char** server_poll(unsigned timeout_ms);
API char** client_poll(unsigned timeout_ms);
API void server_broadcast_bin_flags(const void *ptr, int len, uint64_t flags);
API void server_broadcast_bin(const void *ptr, int len);
API void server_broadcast_flags(const char *msg, uint64_t flags);
API void server_broadcast(const char *msg);
API void server_terminate();
API void server_send(int64_t handle, const char *msg);
API void server_send_bin(int64_t handle, const void *ptr, int len);
API void server_drop(int64_t handle);
API int64_t client_join(const char *ip, int port);
#define client_send_flags(msg,flags) server_broadcast(msg, flags)
#define client_send(msg) server_broadcast(msg)
#define client_send_bin_flags(ptr,len,flags) server_broadcast_bin(ptr, len, flags)
#define client_send_bin(ptr,len) server_broadcast_bin(ptr, len)
#define client_terminate() server_terminate()
#define ANYHOST_IPV4 "0.0.0.0"
#define ANYHOST_IPV6 "::0"
#define LOCALHOST_IPV4 "127.0.0.1"
#define LOCALHOST_IPV6 "::1"
// high-level, socket-less networking api. inspired by Quake, MPI and RenderBuckets theories.
// - rlyeh, public domain
//
// Usage:
// 1. configure networked memory buffers with flags (world, player1, player2, etc). network_buffer();
// 2. then during game loop:
// - modify your buffers as much as needed.
// - sync buffers at least once per frame. network_sync();
// - render your world
// 3. optionally, monitor network status & variables. network_get();
//
// @todo: maybe network_send(msg) + msg *network_recv(); instead of event queue of network_sync() ?
//enum { NETWORK_HANDSHAKE, NETWORK_ENCRYPT, NETWORK_VERSIONED, NETWORK_CHECKSUM }; // negotiation
//enum { NETWORK_TCP, NETWORK_UDP, NETWORK_KCP, NETWORK_ENET, NETWORK_WEBSOCKET }; // transport, where
enum { NETWORK_BIND = 2, NETWORK_CONNECT = 4, NETWORK_NOFAIL = 8 };
API void network_create(unsigned max_clients, const char *ip, const char *port, unsigned flags); // both ip and port can be null
//enum { NETWORK_LOSSY, NETWORK_COMPRESS }; // post-processes
//enum { NETWORK_PREDICT, NETWORK_RECONCILE, NETWORK_INTERPOLATE, NETWORK_COMPENSATE }; // time authority, when
//enum { NETWORK_LAGS, NETWORK_DROPS, NETWORK_THROTTLES, NETWORK_DUPES }; // quality sim, how much
//enum { NETWORK_CONST = 1, NETWORK_64,NETWORK_32,NETWORK_16,NETWORK_8, NETWORK_FLT, NETWORK_STR, NETWORK_BLOB }; // type, what
enum { NETWORK_SEND = 2, NETWORK_RECV = 4 };
enum { NETWORK_UNRELIABLE = 8, NETWORK_UNORDERED = 16/*, NETWORK_PRIORITY = 32*/ };
API void* network_buffer(void *ptr, unsigned sz, uint64_t flags, int64_t rank); // configures a shared/networked buffer
API char** network_sync(unsigned timeout_ms); // syncs all buffers & returns null-terminated list of network events
enum {
NETWORK_EVENT_CONNECT,
NETWORK_EVENT_DISCONNECT,
NETWORK_EVENT_RECEIVE,
NETWORK_EVENT_DISCONNECT_TIMEOUT,
/* offset from internal networking events */
NETWORK_EVENT_RPC = 10,
NETWORK_EVENT_RPC_RESP,
};
/* errcode and errstr are optional arguments, pass NULL to ignore them,
errstr is filled by va() */
API int network_event(const char *msg, int *errcode, char **errstr);
enum { NETWORK_RANK = 0 }; // [0..N] where 0 is server
enum { NETWORK_PING = 1 }; // NETWORK_BANDWIDTH, NETWORK_QUALITY };
enum { NETWORK_PORT = 2, NETWORK_IP, NETWORK_LIVE };
enum { NETWORK_SEND_MS = 4 };
enum { NETWORK_BUF_CLEAR_ON_JOIN = 5 };
enum { NETWORK_USERID = 7, /*NETWORK_SALT,*/ NETWORK_COUNT/*N users*/ /*...*/, NETWORK_CAPACITY };
API int64_t network_get(uint64_t key);
API int64_t network_put(uint64_t key, int64_t value);
API void network_rpc(const char *signature, void *function);
API void network_rpc_send_to(int64_t rank, unsigned id, const char *cmdline);
API void network_rpc_send(unsigned id, const char *cmdline);
// -----------------------------------------------------------------------------
// low-level api (sockets based)
API bool server_bind(int max_clients, int port);
API char** server_poll(unsigned timeout_ms);
API char** client_poll(unsigned timeout_ms);
API void server_broadcast_bin_flags(const void *ptr, int len, uint64_t flags);
API void server_broadcast_bin(const void *ptr, int len);
API void server_broadcast_flags(const char *msg, uint64_t flags);
API void server_broadcast(const char *msg);
API void server_terminate();
API void server_send(int64_t handle, const char *msg);
API void server_send_bin(int64_t handle, const void *ptr, int len);
API void server_drop(int64_t handle);
API int64_t client_join(const char *ip, int port);
#define client_send_flags(msg,flags) server_broadcast(msg, flags)
#define client_send(msg) server_broadcast(msg)
#define client_send_bin_flags(ptr,len,flags) server_broadcast_bin(ptr, len, flags)
#define client_send_bin(ptr,len) server_broadcast_bin(ptr, len)
#define client_terminate() server_terminate()
#define ANYHOST_IPV4 "0.0.0.0"
#define ANYHOST_IPV6 "::0"
#define LOCALHOST_IPV4 "127.0.0.1"
#define LOCALHOST_IPV6 "::1"

View File

@ -1,294 +1,294 @@
#if is(tcc) && is(win32) // @fixme: https lib is broken with tcc. replaced with InternetReadFile() api for now
# include <wininet.h>
# pragma comment(lib,"wininet")
int download_file( FILE *out, const char *url ) {
int ok = false;
char buffer[ 4096 ];
DWORD response_size = 0;
if( out )
for( HINTERNET session = InternetOpenA("v4k.download_file", PRE_CONFIG_INTERNET_ACCESS, NULL, INTERNET_INVALID_PORT_NUMBER, 0); session; InternetCloseHandle(session), session = 0 )
for( HINTERNET request = InternetOpenUrlA(session, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); request; InternetCloseHandle(request), request = 0 )
for(; InternetReadFile(request, buffer, sizeof(buffer), &response_size) != FALSE && response_size > 0; ) {
ok = (fwrite(buffer, response_size, 1, out) == 1);
if(!ok) break;
}
return ok;
}
array(char) download( const char *url ) {
char buffer[ 4096 ];
DWORD response_size = 0, pos = 0;
array(char) out = 0;
for( HINTERNET session = InternetOpenA("v4k.download", PRE_CONFIG_INTERNET_ACCESS, NULL, INTERNET_INVALID_PORT_NUMBER, 0); session; InternetCloseHandle(session), session = 0 )
for( HINTERNET request = InternetOpenUrlA(session, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); request; InternetCloseHandle(request), request = 0 )
for(; InternetReadFile(request, buffer, sizeof(buffer), &response_size) != FALSE && response_size > 0; ) {
array_resize(out, pos + response_size);
memcpy(out + (pos += response_size) - response_size, buffer, response_size);
}
return out;
}
#else
int download_file( FILE *out, const char *url ) {
int ok = false;
if( out ) for( https_t *h = https_get(url, NULL); h; https_release(h), h = NULL ) {
while (https_process(h) == HTTPS_STATUS_PENDING) sleep_ms(1);
//printf("fetch status%d, %d %s\n\n%.*s\n", https_process(h), h->status_code, h->content_type, (int)h->response_size, (char*)h->response_data);
if(https_process(h) == HTTPS_STATUS_COMPLETED)
ok = fwrite(h->response_data, h->response_size, 1, out) == 1;
}
return ok;
}
// @fixme: broken with tcc -m64 (our default tcc configuration)
array(char) download( const char *url ) {
array(char) out = 0;
for( https_t *h = https_get(url, NULL); h; https_release(h), h = NULL ) {
while (https_process(h) == HTTPS_STATUS_PENDING) sleep_ms(1);
//printf("fetch status:%d, %d %s\n\n%.*s\n", https_process(h), h->status_code, h->content_type, (int)h->response_size, (char*)h->response_data);
if( https_process(h) == HTTPS_STATUS_COMPLETED ) {
array_resize(out, h->response_size);
memcpy(out, h->response_data, h->response_size);
}
}
return out;
}
#endif
bool network_tests() {
// network test (https)
array(char) webfile = download("https://www.google.com/");
printf("Network test: %d bytes downloaded from google.com\n", array_count(webfile));
// array_push(webfile, '\0'); puts(webfile);
return true;
}
int portname( const char *service_name, unsigned retries ) {
// Determine port for a given service based on hash of its name.
// If port cant be reached, client should retry with next hash.
// Algorithm: fnv1a(name of service) -> splitmix64 num retries -> remap bucket as [min..max] ports.
// hash64
uint64_t hash = 14695981039346656037ULL;
while( *service_name ) {
hash = ( (unsigned char)*service_name++ ^ hash ) * 0x100000001b3ULL;
}
// splitmix64
for( unsigned i = 0; i < retries; ++i ) {
uint64_t h = (hash += UINT64_C(0x9E3779B97F4A7C15));
h = (h ^ (h >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);
h = (h ^ (h >> 27)) * UINT64_C(0x94D049BB133111EB);
h = (h ^ (h >> 31));
hash = h;
}
// See dynamic ports: https://en.wikipedia.org/wiki/Ephemeral_port
// So, excluded ranges: 32768..60999 (linux), 49152..65535 (freebsd+vista+win7), 1024..5000 (winsrv2003+bsd)
// Output range: [5001..32724], in 4096 steps
return ((hash & 0xFFF) * 677 / 100 + 5001);
}
// -----------------------------------------------------------------------------
#define UDP_DEBUG 0
static int udp_init() {
do_once {
int rc = swrapInit(); // atexit(swrapTerminate);
if( rc ) PANIC("udp_init: swrapInit error");
}
return 1;
}
int udp_open(const char *address, const char *port) {
do_once udp_init();
int fd = swrapSocket(SWRAP_UDP, SWRAP_CONNECT, 0, address, port);
// if( fd == -1 ) PANIC("udp_open: swrapSocket error");
return fd;
}
int udp_bind(const char *address, const char *port) {
do_once udp_init();
int fd = swrapSocket(SWRAP_UDP, SWRAP_BIND, 0, address, port);
// if( fd == -1 ) PANIC("udp_bind: swrapSocket error");
return fd;
}
int udp_send( int fd, const void *buf, int len ) { // returns bytes sent, or -1 if error
int rc = -1;
if( fd >= 0 ) for( ;; ) {
rc = swrapSend(fd, (const char *)buf, len);
#if is(win32)
if( rc == -1 && WSAGetLastError() == WSAEINTR ) continue;
else break;
#else
if( rc == -1 && errno == EINTR ) continue;
else break;
#endif
}
#if UDP_DEBUG
if( rc > 0 ) {
char host[128], serv[128];
int rc2 = swrapAddressInfo(&sa, host, 128, serv, 128 );
if( rc2 != 0 ) PANIC("swrapAddressInfo error");
printf("udp_send: %d bytes to %s:%s : %.*s\n", rc, host, serv, rc, buf );
hexdump(buf, rc);
}
#endif
return rc;
}
int udp_close( int fd ) { // @todo: expose me? needed?
#if is(win32)
// closesocket(fd);
#else
// close(fd);
#endif
fd = -1; // noop
return 0;
}
#if 0
// use socket to send data to another address
int udp_sendto( int fd, const char *ip, const char *port, const void *buf, int len ) { // return number of bytes sent
#if 0
int rc = swrapSendTo(fd, struct swrap_addr*, (const char*)buf, len);
if( rc == -1 ) return -1; //PANIC("udp_send: swrapSend error");
return rc;
#else
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
// use inet_addr. tcc(win32) wont work otherwise.
addr.sin_addr.s_addr = inet_addr(ip); // inet_pton(AF_INET, ip, &addr.sin_addr);
addr.sin_port = htons(atoi(port));
int n = sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr));
return n < 0 ? -1 : n;
#endif
}
#endif
int udp_peek( int fd ) { // <0 error, 0 timeout, >0 data
int rc = swrapSelect(fd, 0.00001);
if( rc < 0 ) return -1; // PANIC("udp_peek: swrapSelect error");
if( rc == 0 ) return 0; // timeout
return 1; //> 0: new data is available
}
int udp_recv( int fd, void *buf, int len ) { // <0 error, 0 orderly shutdown, >0 received bytes
struct swrap_addr sa = {0};
int rc = swrapReceiveFrom(fd, &sa, buf, len);
if( rc < 0 ) return -1; // PANIC("udp_recv: swrapReceiveFrom error");
if( rc == 0 ) return 0; // orderly shutdown
#if UDP_DEBUG
char host[128], serv[128];
int rc2 = swrapAddressInfo(&sa, host, 128, serv, 128 );
if( rc2 != 0 ) PANIC("swrapAddressInfo error");
printf("udp_recv: %d bytes from %s:%s : %.*s\n", rc, host, serv, rc, buf );
hexdump(buf, rc);
#endif
return rc;
}
// -----------------------------------------------------------------------------
#define TCP_DEBUG 1
#if TCP_DEBUG
static set(int) tcp_set;
#endif
void tcp_init(void) {
do_once {
udp_init();
#if TCP_DEBUG
set_init(tcp_set, less_int, hash_int);
#endif
}
}
int tcp_open(const char *address, const char *port) {
do_once tcp_init();
int fd = swrapSocket(SWRAP_TCP, SWRAP_CONNECT, 0/*|SWRAP_NODELAY*/, address, port);
return fd;
}
int tcp_bind(const char *interface_, const char *port, int backlog) {
do_once tcp_init();
int fd = swrapSocket(SWRAP_TCP, SWRAP_BIND, 0/*|SWRAP_NODELAY*//*|SWRAP_NOBLOCK*/, interface_, port);
if( fd >= 0 ) swrapListen(fd, backlog);
return fd;
}
int tcp_peek(int fd, int(*callback)(int)) {
struct swrap_addr sa;
int fd2 = swrapAccept(fd, &sa);
if( fd2 >= 0 ) return callback(fd2);
return -1;
}
int tcp_send(int fd, const void *buf, int len) {
int rc = swrapSend(fd, (const char *)buf, len);
#if TCP_DEBUG
if( set_find(tcp_set, fd) ) {
printf("send -> %11d (status: %d) %s:%s\n", len, rc, tcp_host(fd), tcp_port(fd));
if( rc > 0 ) hexdump(buf, rc);
}
#endif
return rc;
}
int tcp_recv(int fd, void *buf, int len) {
int rc = swrapReceive(fd, (char*)buf, len);
#if TCP_DEBUG
if( rc != 0 && set_find(tcp_set, fd) ) {
printf("recv <- %11d (status: %d) %s:%s\n", len, rc, tcp_host(fd), tcp_port(fd));
if( rc > 0 ) hexdump(buf, rc);
}
#endif
return rc;
}
char* tcp_host(int fd) {
char buf[1024];
struct swrap_addr sa;
swrapAddress(fd, &sa);
swrapAddressInfo(&sa, buf, 512, buf+512, 512);
return va("%s", buf);
}
char* tcp_port(int fd) {
char buf[1024];
struct swrap_addr sa;
swrapAddress(fd, &sa);
swrapAddressInfo(&sa, buf, 512, buf+512, 512);
return va("%s", buf+512);
}
int tcp_close(int fd) {
swrapClose(fd);
return 0;
}
int tcp_debug(int fd) {
#if TCP_DEBUG
if( set_find(tcp_set, fd) ) {
set_erase(tcp_set, fd);
return 0;
} else {
set_insert(tcp_set, fd);
return 1;
}
#else
return 0;
#endif
}
// -----------------------------------------------------------------------------
static void network_init() {
do_once {
udp_init();
tcp_init();
}
}
#if is(tcc) && is(win32) // @fixme: https lib is broken with tcc. replaced with InternetReadFile() api for now
# include <wininet.h>
# pragma comment(lib,"wininet")
int download_file( FILE *out, const char *url ) {
int ok = false;
char buffer[ 4096 ];
DWORD response_size = 0;
if( out )
for( HINTERNET session = InternetOpenA("v4k.download_file", PRE_CONFIG_INTERNET_ACCESS, NULL, INTERNET_INVALID_PORT_NUMBER, 0); session; InternetCloseHandle(session), session = 0 ) // @fixme: download_file
for( HINTERNET request = InternetOpenUrlA(session, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); request; InternetCloseHandle(request), request = 0 )
for(; InternetReadFile(request, buffer, sizeof(buffer), &response_size) != FALSE && response_size > 0; ) {
ok = (fwrite(buffer, response_size, 1, out) == 1);
if(!ok) break;
}
return ok;
}
array(char) download( const char *url ) {
char buffer[ 4096 ];
DWORD response_size = 0, pos = 0;
array(char) out = 0;
for( HINTERNET session = InternetOpenA("v4k.download", PRE_CONFIG_INTERNET_ACCESS, NULL, INTERNET_INVALID_PORT_NUMBER, 0); session; InternetCloseHandle(session), session = 0 )
for( HINTERNET request = InternetOpenUrlA(session, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); request; InternetCloseHandle(request), request = 0 )
for(; InternetReadFile(request, buffer, sizeof(buffer), &response_size) != FALSE && response_size > 0; ) {
array_resize(out, pos + response_size);
memcpy(out + (pos += response_size) - response_size, buffer, response_size);
}
return out;
}
#else
int download_file( FILE *out, const char *url ) {
int ok = false;
if( out ) for( https_t *h = https_get(url, NULL); h; https_release(h), h = NULL ) {
while (https_process(h) == HTTPS_STATUS_PENDING) sleep_ms(1);
//printf("fetch status%d, %d %s\n\n%.*s\n", https_process(h), h->status_code, h->content_type, (int)h->response_size, (char*)h->response_data);
if(https_process(h) == HTTPS_STATUS_COMPLETED)
ok = fwrite(h->response_data, h->response_size, 1, out) == 1;
}
return ok;
}
// @fixme: broken with tcc -m64 (our default tcc configuration)
array(char) download( const char *url ) {
array(char) out = 0;
for( https_t *h = https_get(url, NULL); h; https_release(h), h = NULL ) {
while (https_process(h) == HTTPS_STATUS_PENDING) sleep_ms(1);
//printf("fetch status:%d, %d %s\n\n%.*s\n", https_process(h), h->status_code, h->content_type, (int)h->response_size, (char*)h->response_data);
if( https_process(h) == HTTPS_STATUS_COMPLETED ) {
array_resize(out, h->response_size);
memcpy(out, h->response_data, h->response_size);
}
}
return out;
}
#endif
bool network_tests() {
// network test (https)
array(char) webfile = download("https://www.google.com/");
printf("Network test: %d bytes downloaded from google.com\n", array_count(webfile));
// array_push(webfile, '\0'); puts(webfile);
return true;
}
int portname( const char *service_name, unsigned retries ) {
// Determine port for a given service based on hash of its name.
// If port cant be reached, client should retry with next hash.
// Algorithm: fnv1a(name of service) -> splitmix64 num retries -> remap bucket as [min..max] ports.
// hash64
uint64_t hash = 14695981039346656037ULL;
while( *service_name ) {
hash = ( (unsigned char)*service_name++ ^ hash ) * 0x100000001b3ULL;
}
// splitmix64
for( unsigned i = 0; i < retries; ++i ) {
uint64_t h = (hash += UINT64_C(0x9E3779B97F4A7C15));
h = (h ^ (h >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);
h = (h ^ (h >> 27)) * UINT64_C(0x94D049BB133111EB);
h = (h ^ (h >> 31));
hash = h;
}
// See dynamic ports: https://en.wikipedia.org/wiki/Ephemeral_port
// So, excluded ranges: 32768..60999 (linux), 49152..65535 (freebsd+vista+win7), 1024..5000 (winsrv2003+bsd)
// Output range: [5001..32724], in 4096 steps
return ((hash & 0xFFF) * 677 / 100 + 5001);
}
// -----------------------------------------------------------------------------
#define UDP_DEBUG 0
static int udp_init() {
do_once {
int rc = swrapInit(); // atexit(swrapTerminate);
if( rc ) PANIC("udp_init: swrapInit error");
}
return 1;
}
int udp_open(const char *address, const char *port) {
do_once udp_init();
int fd = swrapSocket(SWRAP_UDP, SWRAP_CONNECT, 0, address, port);
// if( fd == -1 ) PANIC("udp_open: swrapSocket error");
return fd;
}
int udp_bind(const char *address, const char *port) {
do_once udp_init();
int fd = swrapSocket(SWRAP_UDP, SWRAP_BIND, 0, address, port);
// if( fd == -1 ) PANIC("udp_bind: swrapSocket error");
return fd;
}
int udp_send( int fd, const void *buf, int len ) { // returns bytes sent, or -1 if error
int rc = -1;
if( fd >= 0 ) for( ;; ) {
rc = swrapSend(fd, (const char *)buf, len);
#if is(win32)
if( rc == -1 && WSAGetLastError() == WSAEINTR ) continue;
else break;
#else
if( rc == -1 && errno == EINTR ) continue;
else break;
#endif
}
#if UDP_DEBUG
if( rc > 0 ) {
char host[128], serv[128];
int rc2 = swrapAddressInfo(&sa, host, 128, serv, 128 );
if( rc2 != 0 ) PANIC("swrapAddressInfo error");
printf("udp_send: %d bytes to %s:%s : %.*s\n", rc, host, serv, rc, buf );
hexdump(buf, rc);
}
#endif
return rc;
}
int udp_close( int fd ) { // @todo: expose me? needed?
#if is(win32)
// closesocket(fd);
#else
// close(fd);
#endif
fd = -1; // noop
return 0;
}
#if 0
// use socket to send data to another address
int udp_sendto( int fd, const char *ip, const char *port, const void *buf, int len ) { // return number of bytes sent
#if 0
int rc = swrapSendTo(fd, struct swrap_addr*, (const char*)buf, len);
if( rc == -1 ) return -1; //PANIC("udp_send: swrapSend error");
return rc;
#else
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
// use inet_addr. tcc(win32) wont work otherwise.
addr.sin_addr.s_addr = inet_addr(ip); // inet_pton(AF_INET, ip, &addr.sin_addr);
addr.sin_port = htons(atoi(port));
int n = sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr));
return n < 0 ? -1 : n;
#endif
}
#endif
int udp_peek( int fd ) { // <0 error, 0 timeout, >0 data
int rc = swrapSelect(fd, 0.00001);
if( rc < 0 ) return -1; // PANIC("udp_peek: swrapSelect error");
if( rc == 0 ) return 0; // timeout
return 1; //> 0: new data is available
}
int udp_recv( int fd, void *buf, int len ) { // <0 error, 0 orderly shutdown, >0 received bytes
struct swrap_addr sa = {0};
int rc = swrapReceiveFrom(fd, &sa, buf, len);
if( rc < 0 ) return -1; // PANIC("udp_recv: swrapReceiveFrom error");
if( rc == 0 ) return 0; // orderly shutdown
#if UDP_DEBUG
char host[128], serv[128];
int rc2 = swrapAddressInfo(&sa, host, 128, serv, 128 );
if( rc2 != 0 ) PANIC("swrapAddressInfo error");
printf("udp_recv: %d bytes from %s:%s : %.*s\n", rc, host, serv, rc, buf );
hexdump(buf, rc);
#endif
return rc;
}
// -----------------------------------------------------------------------------
#define TCP_DEBUG 1
#if TCP_DEBUG
static set(int) tcp_set;
#endif
void tcp_init(void) {
do_once {
udp_init();
#if TCP_DEBUG
set_init(tcp_set, less_int, hash_int);
#endif
}
}
int tcp_open(const char *address, const char *port) {
do_once tcp_init();
int fd = swrapSocket(SWRAP_TCP, SWRAP_CONNECT, 0/*|SWRAP_NODELAY*/, address, port);
return fd;
}
int tcp_bind(const char *interface_, const char *port, int backlog) {
do_once tcp_init();
int fd = swrapSocket(SWRAP_TCP, SWRAP_BIND, 0/*|SWRAP_NODELAY*//*|SWRAP_NOBLOCK*/, interface_, port);
if( fd >= 0 ) swrapListen(fd, backlog);
return fd;
}
int tcp_peek(int fd, int(*callback)(int)) {
struct swrap_addr sa;
int fd2 = swrapAccept(fd, &sa);
if( fd2 >= 0 ) return callback(fd2);
return -1;
}
int tcp_send(int fd, const void *buf, int len) {
int rc = swrapSend(fd, (const char *)buf, len);
#if TCP_DEBUG
if( set_find(tcp_set, fd) ) {
printf("send -> %11d (status: %d) %s:%s\n", len, rc, tcp_host(fd), tcp_port(fd));
if( rc > 0 ) hexdump(buf, rc);
}
#endif
return rc;
}
int tcp_recv(int fd, void *buf, int len) {
int rc = swrapReceive(fd, (char*)buf, len);
#if TCP_DEBUG
if( rc != 0 && set_find(tcp_set, fd) ) {
printf("recv <- %11d (status: %d) %s:%s\n", len, rc, tcp_host(fd), tcp_port(fd));
if( rc > 0 ) hexdump(buf, rc);
}
#endif
return rc;
}
char* tcp_host(int fd) {
char buf[1024];
struct swrap_addr sa;
swrapAddress(fd, &sa);
swrapAddressInfo(&sa, buf, 512, buf+512, 512);
return va("%s", buf);
}
char* tcp_port(int fd) {
char buf[1024];
struct swrap_addr sa;
swrapAddress(fd, &sa);
swrapAddressInfo(&sa, buf, 512, buf+512, 512);
return va("%s", buf+512);
}
int tcp_close(int fd) {
swrapClose(fd);
return 0;
}
int tcp_debug(int fd) {
#if TCP_DEBUG
if( set_find(tcp_set, fd) ) {
set_erase(tcp_set, fd);
return 0;
} else {
set_insert(tcp_set, fd);
return 1;
}
#else
return 0;
#endif
}
// -----------------------------------------------------------------------------
static void network_init() {
do_once {
udp_init();
tcp_init();
}
}

View File

@ -1,49 +1,49 @@
// -----------------------------------------------------------------------------
// network framework
// - rlyeh, public domain
API array(char) download( const char *url );
API int download_file( FILE *out, const char *url );
API int portname( const char *service_name, unsigned retries );
API bool network_tests();
// -----------------------------------------------------------------------------
// udp wrapper
// - rlyeh, public domain.
// server
API int udp_bind(const char *address, const char *port);
// client
API int udp_open(const char *address, const char *port);
// common
API int udp_send(int, const void *buf, int len ); // <0 error, >0 bytes sent ok
API int udp_sendto(int, const char *ip, const char *port, const void *buf, int len ); // <0 error, >0 bytes sent ok
API int udp_recv(int, void *buf, int len ); // <0 error, 0 orderly shutdown, >0 received bytes
API int udp_peek(int); // <0 error, 0 timeout, >0 data
// -----------------------------------------------------------------------------
// tcp wrapper
// - rlyeh, public domain
// client
API int tcp_open(const char *address, const char *port);
// server
API int tcp_bind(const char *interface_, const char *port, int queue);
API int tcp_peek(int, int(*callback)(int));
// common
API int tcp_send(int, const void* buf, int len);
API int tcp_recv(int, void* buf, int len);
API char* tcp_host(int); // info
API char* tcp_port(int); // info
API int tcp_close(int);
// extras
API int tcp_debug(int); // toggle traffic monitoring on/off for given socket
//API int tcp_printf(int, const char *fmt, ...); // printf message in remote end
//API int tcp_crypt(int,uint64_t); // set shared secret
// -----------------------------------------------------------------------------
// network framework
// - rlyeh, public domain
API array(char) download( const char *url );
API int download_file( FILE *out, const char *url );
API int portname( const char *service_name, unsigned retries );
API bool network_tests();
// -----------------------------------------------------------------------------
// udp wrapper
// - rlyeh, public domain.
// server
API int udp_bind(const char *address, const char *port);
// client
API int udp_open(const char *address, const char *port);
// common
API int udp_send(int, const void *buf, int len ); // <0 error, >0 bytes sent ok
API int udp_sendto(int, const char *ip, const char *port, const void *buf, int len ); // <0 error, >0 bytes sent ok
API int udp_recv(int, void *buf, int len ); // <0 error, 0 orderly shutdown, >0 received bytes
API int udp_peek(int); // <0 error, 0 timeout, >0 data
// -----------------------------------------------------------------------------
// tcp wrapper
// - rlyeh, public domain
// client
API int tcp_open(const char *address, const char *port);
// server
API int tcp_bind(const char *interface_, const char *port, int queue);
API int tcp_peek(int, int(*callback)(int));
// common
API int tcp_send(int, const void* buf, int len);
API int tcp_recv(int, void* buf, int len);
API char* tcp_host(int); // info
API char* tcp_port(int); // info
API int tcp_close(int);
// extras
API int tcp_debug(int); // toggle traffic monitoring on/off for given socket
//API int tcp_printf(int, const char *fmt, ...); // printf message in remote end
//API int tcp_crypt(int,uint64_t); // set shared secret

File diff suppressed because it is too large Load Diff

View File

@ -1,364 +1,364 @@
// -----------------------------------------------------------------------------
// factory of handle ids
// convert between hard refs (pointers) and weak refs (ids)
API uintptr_t id_make(void *ptr);
API void * id_handle(uintptr_t id);
API void id_dispose(uintptr_t id);
API bool id_valid(uintptr_t id);
// configuration:
// ideally, these two should be 32 each. they were changed to fit our OBJHEADER bits
#ifndef ID_INDEX_BITS
#define ID_INDEX_BITS 16
#endif
#ifndef ID_COUNT_BITS
#define ID_COUNT_BITS 3
#endif
// C objects framework
// - rlyeh, public domain.
//
// ## object limitations
// - 8-byte overhead per object
// - XX-byte overhead per object-entity
// - 32 components max per object-entity
// - 256 classes max per game
// - 256 references max per object
// - 1024K bytes max per object
// - 8 generations + 64K IDs per running instance (19-bit IDs)
// - support for pragma pack(1) structs not enabled by default.
/* /!\ if you plan to use pragma pack(1) on any struct, you need #define OBJ_MIN_PRAGMAPACK_BITS 0 at the expense of max class size /!\ */
#ifndef OBJ_MIN_PRAGMAPACK_BITS
//#define OBJ_MIN_PRAGMAPACK_BITS 3 // allows pragma packs >= 8. objsizew becomes 8<<3, so 2048 bytes max per class (default)
#define OBJ_MIN_PRAGMAPACK_BITS 2 // allows pragma packs >= 4. objsizew becomes 8<<2, so 1024 bytes max per class
//#define OBJ_MIN_PRAGMAPACK_BITS 1 // allows pragma packs >= 2. objsizew becomes 8<<1, so 512 bytes max per class
//#define OBJ_MIN_PRAGMAPACK_BITS 0 // allows pragma packs >= 1. objsizew becomes 8<<0, so 256 bytes max per class
#endif
#define OBJHEADER \
struct { \
ifdef(debug, const char *objname;) \
union { \
uintptr_t objheader; \
struct { \
uintptr_t objtype:8; \
uintptr_t objsizew:8; \
uintptr_t objrefs:8; \
uintptr_t objheap:1; \
uintptr_t objcomps:1; /* << can be removed? check payload ptr instead? */ \
uintptr_t objunused:64-8-8-8-1-1-ID_INDEX_BITS-ID_COUNT_BITS; /*19*/ \
uintptr_t objid:ID_INDEX_BITS+ID_COUNT_BITS; /*16+3*/ \
}; \
}; \
array(struct obj*) objchildren; \
};
#ifndef OBJ
#define OBJ \
OBJHEADER
#endif
// ----------------------------------------------------------------------------
// syntax sugars
#ifdef OBJTYPE
#undef OBJTYPE
#endif
#define OBJTYPE(T) \
OBJTYPE_##T
#define OBJTYPEDEF(NAME,N) \
enum { OBJTYPE(NAME) = N }; \
STATIC_ASSERT( N <= 255 ); \
STATIC_ASSERT( sizeof(NAME) == ((sizeof(NAME)>>OBJ_MIN_PRAGMAPACK_BITS)<<OBJ_MIN_PRAGMAPACK_BITS) ); // (sizeof(NAME) & ((1<<OBJ_MIN_PRAGMAPACK_BITS)-1)) == 0 );
// ----------------------------------------------------------------------------
// objects
#define TYPEDEF_STRUCT(NAME,N,...) \
typedef struct NAME { OBJ \
__VA_ARGS__ \
char payload[0]; \
} NAME; OBJTYPEDEF(NAME,N);
// TYPEDEF_STRUCT(obj,0);
typedef struct obj { OBJ } obj;
// ----------------------------------------------------------------------------
// entities
#define OBJCOMPONENTS_MAX 32
#define OBJCOMPONENTS_ALL_ENABLED 0xAAAAAAAAAAAAAAAAULL
#define OBJCOMPONENTS_ALL_FLAGGED 0x5555555555555555ULL
#define COMPONENTS_ONLY(x) ((x) & ~OBJCOMPONENTS_ALL_FLAGGED)
#ifndef ENTITY
#define ENTITY \
struct { OBJHEADER union { struct { uintptr_t objenabled:OBJCOMPONENTS_MAX, objflagged:OBJCOMPONENTS_MAX; }; uintptr_t cflags; }; void *c[OBJCOMPONENTS_MAX]; };
#endif
#define TYPEDEF_ENTITY(NAME,N,...) \
typedef struct NAME { ENTITY \
__VA_ARGS__ \
char payload[0]; \
} NAME; OBJTYPEDEF(NAME,N);
// OBJTYPEDEF(entity,1)
typedef struct entity { ENTITY } entity;
#define entity_new(TYPE, ...) OBJ_CTOR(TYPE, #TYPE, 1, 0, __VA_ARGS__)
#define entity_new_ext(TYPE, NAME, ...) OBJ_CTOR(TYPE, NAME, 1, 0, __VA_ARGS__)
// ----------------------------------------------------------------------------
// heap/stack ctor/dtor
static __thread obj *objtmp;
#define OBJ_CTOR_HDR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE) ( \
(PTR)->objheader = HEAP ? id_make(PTR) : 0, /*should assign to .objid instead. however, id_make() returns shifted bits already*/ \
(PTR)->objtype = (OBJ_TYPE), \
(PTR)->objheap = (HEAP), \
(PTR)->objsizew = (SIZEOF_OBJ>>OBJ_MIN_PRAGMAPACK_BITS))
#define OBJ_CTOR_PTR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE) ( \
OBJ_CTOR_HDR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE), \
obj_ctor(PTR))
#define OBJ_CTOR(TYPE, NAME, HEAP, PAYLOAD_SIZE, ...) (TYPE*)( \
objtmp = (HEAP ? MALLOC(sizeof(TYPE)+(PAYLOAD_SIZE)) : ALLOCA(sizeof(TYPE)+(PAYLOAD_SIZE))), \
*(TYPE*)objtmp = ((TYPE){ {0,}, __VA_ARGS__}), \
((PAYLOAD_SIZE) ? memset((char*)objtmp + sizeof(TYPE), 0, (PAYLOAD_SIZE)) : objtmp), \
( OBJTYPES[ OBJTYPE(TYPE) ] = #TYPE ), \
OBJ_CTOR_PTR(objtmp, HEAP,sizeof(TYPE),OBJTYPE(TYPE)), \
ifdef(debug, (obj_printf)(objtmp, va("%s", callstack(+16))), 0), \
obj_setname(objtmp, NAME))
#define obj(TYPE, ...) *OBJ_CTOR(TYPE, #TYPE, 0, 0, __VA_ARGS__)
#define obj_new(TYPE, ...) OBJ_CTOR(TYPE, #TYPE, 1, 0, __VA_ARGS__)
#define obj_new_ext(TYPE, NAME, ...) OBJ_CTOR(TYPE, NAME, 1, 0, __VA_ARGS__)
void* obj_malloc(unsigned sz);
void* obj_free(void *o);
// ----------------------------------------------------------------------------
// obj generics. can be extended.
#define obj_ctor(o,...) obj_method(ctor, o, ##__VA_ARGS__)
#define obj_dtor(o,...) obj_method(dtor, o, ##__VA_ARGS__)
#define obj_save(o,...) obj_method(save, o, ##__VA_ARGS__)
#define obj_load(o,...) obj_method(load, o, ##__VA_ARGS__)
#define obj_test(o,...) obj_method(test, o, ##__VA_ARGS__)
#define obj_init(o,...) obj_method(init, o, ##__VA_ARGS__)
#define obj_quit(o,...) obj_method(quit, o, ##__VA_ARGS__)
#define obj_tick(o,...) obj_method(tick, o, ##__VA_ARGS__)
#define obj_draw(o,...) obj_method(draw, o, ##__VA_ARGS__)
#define obj_lerp(o,...) obj_method(lerp, o, ##__VA_ARGS__)
#define obj_edit(o,...) obj_method(edit, o, ##__VA_ARGS__)
#define obj_menu(o,...) obj_method(menu, o, ##__VA_ARGS__)
#define obj_aabb(o,...) obj_method(aabb, o, ##__VA_ARGS__)
#define obj_icon(o,...) obj_method(icon, o, ##__VA_ARGS__)
// --- syntax sugars
#define obj_extend(T,method) (obj_##method[OBJTYPE(T)] = (void*)T##_##method)
#define obj_extend_t(T,method) (obj_##method[OBJTYPE(T##_t)] = (void*)T##_##method)
#define obj_method(method,o,...) (obj_##method[((struct obj*)(o))->objtype](o,##__VA_ARGS__)) // (obj_##method[((struct obj*)(o))->objtype]((o), ##__VA_ARGS__))
#define obj_hasmethod(o,method) (obj_typeid(o)[obj_##method])
#define obj_vtable(method,RC,...) RC macro(obj_##method)(){ __VA_ARGS__ }; RC (*obj_##method[256])() = { REPEAT256(macro(obj_##method)) };
#define obj_vtable_null(method,RC) RC (*obj_##method[256])() = { 0 }; // null virtual table. will crash unless obj_extend'ed
#define REPEAT16(f) f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f ///-
#define REPEAT64(f) REPEAT16(f),REPEAT16(f),REPEAT16(f),REPEAT16(f) ///-
#define REPEAT256(f) REPEAT64(f),REPEAT64(f),REPEAT64(f),REPEAT64(f) ///-
#undef EXTEND
#define EXTEND(...) EXPAND(EXTEND, __VA_ARGS__)
#define EXTEND2(o,F1) obj_extend(o,F1) ///-
#define EXTEND3(o,F1,F2) obj_extend(o,F1), obj_extend(o,F2) ///-
#define EXTEND4(o,F1,F2,F3) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3) ///-
#define EXTEND5(o,F1,F2,F3,F4) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4) ///-
#define EXTEND6(o,F1,F2,F3,F4,F5) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5) ///-
#define EXTEND7(o,F1,F2,F3,F4,F5,F6) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5), obj_extend(o,F6) ///-
#define EXTEND8(o,F1,F2,F3,F4,F5,F6,F7) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5), obj_extend(o,F6), obj_extend(o,F7) ///-
#define EXTEND9(o,F1,F2,F3,F4,F5,F6,F7,F8) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5), obj_extend(o,F6), obj_extend(o,F7), obj_extend(o,F8) ///-
#define EXTEND10(o,F1,F2,F3,F4,F5,F6,F7,F8,F9) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5), obj_extend(o,F6), obj_extend(o,F7), obj_extend(o,F8), obj_extend(o,F9) ///-
#define EXTEND_T(...) EXPAND(EXTEND_T, __VA_ARGS__)
#define EXTEND_T2(o,F1) obj_extend_t(o,F1) ///-
#define EXTEND_T3(o,F1,F2) obj_extend_t(o,F1), obj_extend_t(o,F2) ///-
#define EXTEND_T4(o,F1,F2,F3) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3) ///-
#define EXTEND_T5(o,F1,F2,F3,F4) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4) ///-
#define EXTEND_T6(o,F1,F2,F3,F4,F5) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5) ///-
#define EXTEND_T7(o,F1,F2,F3,F4,F5,F6) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5), obj_extend_t(o,F6) ///-
#define EXTEND_T8(o,F1,F2,F3,F4,F5,F6,F7) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5), obj_extend_t(o,F6), obj_extend_t(o,F7) ///-
#define EXTEND_T9(o,F1,F2,F3,F4,F5,F6,F7,F8) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5), obj_extend_t(o,F6), obj_extend_t(o,F7), obj_extend_t(o,F8) ///-
#define EXTEND_T10(o,F1,F2,F3,F4,F5,F6,F7,F8,F9) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5), obj_extend_t(o,F6), obj_extend_t(o,F7), obj_extend_t(o,F8), obj_extend_t(o,F9) ///-
// --- declare vtables
API extern void (*obj_ctor[256])(); ///-
API extern void (*obj_dtor[256])(); ///-
API extern char* (*obj_save[256])(); ///-
API extern bool (*obj_load[256])(); ///-
API extern int (*obj_test[256])(); ///-
API extern int (*obj_init[256])(); ///-
API extern int (*obj_quit[256])(); ///-
API extern int (*obj_tick[256])(); ///-
API extern int (*obj_draw[256])(); ///-
API extern int (*obj_lerp[256])(); ///-
API extern int (*obj_aabb[256])(); ///-
API extern int (*obj_edit[256])(); ///-
API extern int (*obj_menu[256])(); ///-
API extern char* (*obj_icon[256])(); ///-
API extern const char*OBJTYPES[256]; ///-
// ----------------------------------------------------------------------------
// core
API uintptr_t obj_header(const void *o);
API uintptr_t obj_id(const void *o);
API const char* obj_type(const void *o);
API unsigned obj_typeid(const void *o);
API int obj_sizeof(const void *o);
API int obj_size(const void *o); // size of all members together in struct. may include padding bytes.
API char* obj_data(void *o); // pointer to the first member in struct
API const char* obj_datac(const void *o); // const pointer to the first struct member
API void* obj_payload(const void *o); // pointer right after last member in struct
API void* obj_zero(void *o); // reset all object members
// ----------------------------------------------------------------------------
// refcounting
API void* obj_ref(void *oo);
API void* obj_unref(void *oo);
// ----------------------------------------------------------------------------
// scene tree
#define each_objchild(p,T,o) /*non-recursive*/ \
(array(struct obj*)* children = obj_children(p); children; children = 0) \
for(int _i = 1, _end = array_count(*children); _i < _end; ++_i) \
for(T o = (T)((*children)[_i]); o && (obj_parent(o) == p); o = 0)
API obj* obj_detach(void *c);
API obj* obj_attach(void *o, void *c);
API obj* obj_root(const void *o);
API obj* obj_parent(const void *o);
API array(obj*)*obj_children(const void *o); // child[0]: parent, child[1]: 1st child, child[2]: 2nd child...
API array(obj*)*obj_siblings(const void *o); // child[0]: grandpa, child[1]: sibling1, child[2]: sibling2...
API int obj_dumptree(const void *o);
// ----------------------------------------------------------------------------
// metadata
API void* obj_setmeta(void *o, const char *key, const char *value);
API const char* obj_meta(const void *o, const char *key);
API void* obj_setname(void *o, const char *name);
API const char* obj_name(const void *o);
// ----------------------------------------------------------------------------
// stl
API void* obj_swap(void *dst, void *src);
API void* obj_copy_fast(void *dst, const void *src);
API void* obj_copy(void *dst, const void *src);
API int obj_comp_fast(const void *a, const void *b);
API int obj_comp(const void *a, const void *b);
API int obj_lesser(const void *a, const void *b);
API int obj_greater(const void *a, const void *b);
API int obj_equal(const void *a, const void *b);
API uint64_t obj_hash(const void *o);
// ----------------------------------------------------------------------------
// debug
API bool obj_hexdump(const void *oo);
API int obj_print(const void *o);
API int obj_printf(const void *o, const char *text);
API int obj_console(const void *o); // obj_output() ?
#define obj_printf(o, ...) obj_printf(o, va(__VA_ARGS__))
// ----------------------------------------------------------------------------
// serialization
API char* obj_saveini(const void *o);
API obj* obj_mergeini(void *o, const char *ini);
API obj* obj_loadini(void *o, const char *ini);
API char* obj_savejson(const void *o);
API obj* obj_mergejson(void *o, const char *json);
API obj* obj_loadjson(void *o, const char *json);
API char* obj_savebin(const void *o);
API obj* obj_mergebin(void *o, const char *sav);
API obj* obj_loadbin(void *o, const char *sav);
API char* obj_savempack(const void *o); // @todo
API obj* obj_mergempack(void *o, const char *sav); // @todo
API obj* obj_loadmpack(void *o, const char *sav); // @todo
API int obj_push(const void *o);
API int obj_pop(void *o);
// ----------------------------------------------------------------------------
// components
API bool obj_addcomponent(entity *e, unsigned c, void *ptr);
API bool obj_hascomponent(entity *e, unsigned c);
API void* obj_getcomponent(entity *e, unsigned c);
API bool obj_delcomponent(entity *e, unsigned c);
API bool obj_usecomponent(entity *e, unsigned c);
API bool obj_offcomponent(entity *e, unsigned c);
API char* entity_save(entity *self);
// ----------------------------------------------------------------------------
// reflection
#define each_objmember(oo,TYPE,NAME,PTR) \
(array(reflect_t) *found_ = members_find(obj_type(oo)); found_; found_ = 0) \
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
for(reflect_t *R = &(*found_)[it_]; R; R = 0 ) \
for(const char *NAME = R->name, *TYPE = R->type; NAME || TYPE; ) \
for(void *PTR = ((char*)oo) + R->sz ; NAME || TYPE ; NAME = TYPE = 0 )
API void* obj_clone(const void *src);
API void* obj_merge(void *dst, const void *src); // @testme
API void* obj_mutate(void *dst, const void *src);
API void* obj_make(const char *str);
// built-ins
typedef enum OBJTYPE_BUILTINS {
OBJTYPE_obj = 0,
OBJTYPE_entity = 1,
OBJTYPE_vec2 = 2,
OBJTYPE_vec3 = 3,
OBJTYPE_vec4 = 4,
OBJTYPE_quat = 5,
OBJTYPE_mat33 = 6,
OBJTYPE_mat34 = 7,
OBJTYPE_mat44 = 8,
OBJTYPE_vec2i = 9,
OBJTYPE_vec3i = 10,
} OBJTYPE_BUILTINS;
// -----------------------------------------------------------------------------
// factory of handle ids
// convert between hard refs (pointers) and weak refs (ids)
API uintptr_t id_make(void *ptr);
API void * id_handle(uintptr_t id);
API void id_dispose(uintptr_t id);
API bool id_valid(uintptr_t id);
// configuration:
// ideally, these two should be 32 each. they were changed to fit our OBJHEADER bits
#ifndef ID_INDEX_BITS
#define ID_INDEX_BITS 16
#endif
#ifndef ID_COUNT_BITS
#define ID_COUNT_BITS 3
#endif
// C objects framework
// - rlyeh, public domain.
//
// ## object limitations
// - 8-byte overhead per object
// - XX-byte overhead per object-entity
// - 32 components max per object-entity
// - 256 classes max per game
// - 256 references max per object
// - 1024K bytes max per object
// - 8 generations + 64K IDs per running instance (19-bit IDs)
// - support for pragma pack(1) structs not enabled by default.
/* /!\ if you plan to use pragma pack(1) on any struct, you need #define OBJ_MIN_PRAGMAPACK_BITS 0 at the expense of max class size /!\ */
#ifndef OBJ_MIN_PRAGMAPACK_BITS
//#define OBJ_MIN_PRAGMAPACK_BITS 3 // allows pragma packs >= 8. objsizew becomes 8<<3, so 2048 bytes max per class (default)
#define OBJ_MIN_PRAGMAPACK_BITS 2 // allows pragma packs >= 4. objsizew becomes 8<<2, so 1024 bytes max per class
//#define OBJ_MIN_PRAGMAPACK_BITS 1 // allows pragma packs >= 2. objsizew becomes 8<<1, so 512 bytes max per class
//#define OBJ_MIN_PRAGMAPACK_BITS 0 // allows pragma packs >= 1. objsizew becomes 8<<0, so 256 bytes max per class
#endif
#define OBJHEADER \
struct { \
ifdef(debug, const char *objname;) \
union { \
uintptr_t objheader; \
struct { \
uintptr_t objtype:8; \
uintptr_t objsizew:8; \
uintptr_t objrefs:8; \
uintptr_t objheap:1; \
uintptr_t objcomps:1; /* << can be removed? check payload ptr instead? */ \
uintptr_t objunused:64-8-8-8-1-1-ID_INDEX_BITS-ID_COUNT_BITS; /*19*/ \
uintptr_t objid:ID_INDEX_BITS+ID_COUNT_BITS; /*16+3*/ \
}; \
}; \
array(struct obj*) objchildren; \
};
#ifndef OBJ
#define OBJ \
OBJHEADER
#endif
// ----------------------------------------------------------------------------
// syntax sugars
#ifdef OBJTYPE
#undef OBJTYPE
#endif
#define OBJTYPE(T) \
OBJTYPE_##T
#define OBJTYPEDEF(NAME,N) \
enum { OBJTYPE(NAME) = N }; \
STATIC_ASSERT( N <= 255 ); \
STATIC_ASSERT( sizeof(NAME) == ((sizeof(NAME)>>OBJ_MIN_PRAGMAPACK_BITS)<<OBJ_MIN_PRAGMAPACK_BITS) ); // (sizeof(NAME) & ((1<<OBJ_MIN_PRAGMAPACK_BITS)-1)) == 0 );
// ----------------------------------------------------------------------------
// objects
#define TYPEDEF_STRUCT(NAME,N,...) \
typedef struct NAME { OBJ \
__VA_ARGS__ \
char payload[0]; \
} NAME; OBJTYPEDEF(NAME,N);
// TYPEDEF_STRUCT(obj,0);
typedef struct obj { OBJ } obj;
// ----------------------------------------------------------------------------
// entities
#define OBJCOMPONENTS_MAX 32
#define OBJCOMPONENTS_ALL_ENABLED 0xAAAAAAAAAAAAAAAAULL
#define OBJCOMPONENTS_ALL_FLAGGED 0x5555555555555555ULL
#define COMPONENTS_ONLY(x) ((x) & ~OBJCOMPONENTS_ALL_FLAGGED)
#ifndef ENTITY
#define ENTITY \
struct { OBJHEADER union { struct { uintptr_t objenabled:OBJCOMPONENTS_MAX, objflagged:OBJCOMPONENTS_MAX; }; uintptr_t cflags; }; void *c[OBJCOMPONENTS_MAX]; };
#endif
#define TYPEDEF_ENTITY(NAME,N,...) \
typedef struct NAME { ENTITY \
__VA_ARGS__ \
char payload[0]; \
} NAME; OBJTYPEDEF(NAME,N);
// OBJTYPEDEF(entity,1)
typedef struct entity { ENTITY } entity;
#define entity_new(TYPE, ...) OBJ_CTOR(TYPE, #TYPE, 1, 0, __VA_ARGS__)
#define entity_new_ext(TYPE, NAME, ...) OBJ_CTOR(TYPE, NAME, 1, 0, __VA_ARGS__)
// ----------------------------------------------------------------------------
// heap/stack ctor/dtor
static __thread obj *objtmp;
#define OBJ_CTOR_HDR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE) ( \
(PTR)->objheader = HEAP ? id_make(PTR) : 0, /*should assign to .objid instead. however, id_make() returns shifted bits already*/ \
(PTR)->objtype = (OBJ_TYPE), \
(PTR)->objheap = (HEAP), \
(PTR)->objsizew = (SIZEOF_OBJ>>OBJ_MIN_PRAGMAPACK_BITS))
#define OBJ_CTOR_PTR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE) ( \
OBJ_CTOR_HDR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE), \
obj_ctor(PTR))
#define OBJ_CTOR(TYPE, NAME, HEAP, PAYLOAD_SIZE, ...) (TYPE*)( \
objtmp = (HEAP ? MALLOC(sizeof(TYPE)+(PAYLOAD_SIZE)) : ALLOCA(sizeof(TYPE)+(PAYLOAD_SIZE))), \
*(TYPE*)objtmp = ((TYPE){ {0,}, __VA_ARGS__}), \
((PAYLOAD_SIZE) ? memset((char*)objtmp + sizeof(TYPE), 0, (PAYLOAD_SIZE)) : objtmp), \
( OBJTYPES[ OBJTYPE(TYPE) ] = #TYPE ), \
OBJ_CTOR_PTR(objtmp, HEAP,sizeof(TYPE),OBJTYPE(TYPE)), \
ifdef(debug, (obj_printf)(objtmp, va("%s", callstack(+16))), 0), \
obj_setname(objtmp, NAME))
#define obj(TYPE, ...) *OBJ_CTOR(TYPE, #TYPE, 0, 0, __VA_ARGS__)
#define obj_new(TYPE, ...) OBJ_CTOR(TYPE, #TYPE, 1, 0, __VA_ARGS__)
#define obj_new_ext(TYPE, NAME, ...) OBJ_CTOR(TYPE, NAME, 1, 0, __VA_ARGS__)
void* obj_malloc(unsigned sz);
void* obj_free(void *o);
// ----------------------------------------------------------------------------
// obj generics. can be extended.
#define obj_ctor(o,...) obj_method(ctor, o, ##__VA_ARGS__)
#define obj_dtor(o,...) obj_method(dtor, o, ##__VA_ARGS__)
#define obj_save(o,...) obj_method(save, o, ##__VA_ARGS__)
#define obj_load(o,...) obj_method(load, o, ##__VA_ARGS__)
#define obj_test(o,...) obj_method(test, o, ##__VA_ARGS__)
#define obj_init(o,...) obj_method(init, o, ##__VA_ARGS__)
#define obj_quit(o,...) obj_method(quit, o, ##__VA_ARGS__)
#define obj_tick(o,...) obj_method(tick, o, ##__VA_ARGS__)
#define obj_draw(o,...) obj_method(draw, o, ##__VA_ARGS__)
#define obj_lerp(o,...) obj_method(lerp, o, ##__VA_ARGS__)
#define obj_edit(o,...) obj_method(edit, o, ##__VA_ARGS__)
#define obj_menu(o,...) obj_method(menu, o, ##__VA_ARGS__)
#define obj_aabb(o,...) obj_method(aabb, o, ##__VA_ARGS__)
#define obj_icon(o,...) obj_method(icon, o, ##__VA_ARGS__)
// --- syntax sugars
#define obj_extend(T,method) (obj_##method[OBJTYPE(T)] = (void*)T##_##method)
#define obj_extend_t(T,method) (obj_##method[OBJTYPE(T##_t)] = (void*)T##_##method)
#define obj_method(method,o,...) (obj_##method[((struct obj*)(o))->objtype](o,##__VA_ARGS__)) // (obj_##method[((struct obj*)(o))->objtype]((o), ##__VA_ARGS__))
#define obj_hasmethod(o,method) (obj_typeid(o)[obj_##method])
#define obj_vtable(method,RC,...) RC macro(obj_##method)(){ __VA_ARGS__ }; RC (*obj_##method[256])() = { REPEAT256(macro(obj_##method)) };
#define obj_vtable_null(method,RC) RC (*obj_##method[256])() = { 0 }; // null virtual table. will crash unless obj_extend'ed
#define REPEAT16(f) f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f ///-
#define REPEAT64(f) REPEAT16(f),REPEAT16(f),REPEAT16(f),REPEAT16(f) ///-
#define REPEAT256(f) REPEAT64(f),REPEAT64(f),REPEAT64(f),REPEAT64(f) ///-
#undef EXTEND
#define EXTEND(...) EXPAND(EXTEND, __VA_ARGS__)
#define EXTEND2(o,F1) obj_extend(o,F1) ///-
#define EXTEND3(o,F1,F2) obj_extend(o,F1), obj_extend(o,F2) ///-
#define EXTEND4(o,F1,F2,F3) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3) ///-
#define EXTEND5(o,F1,F2,F3,F4) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4) ///-
#define EXTEND6(o,F1,F2,F3,F4,F5) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5) ///-
#define EXTEND7(o,F1,F2,F3,F4,F5,F6) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5), obj_extend(o,F6) ///-
#define EXTEND8(o,F1,F2,F3,F4,F5,F6,F7) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5), obj_extend(o,F6), obj_extend(o,F7) ///-
#define EXTEND9(o,F1,F2,F3,F4,F5,F6,F7,F8) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5), obj_extend(o,F6), obj_extend(o,F7), obj_extend(o,F8) ///-
#define EXTEND10(o,F1,F2,F3,F4,F5,F6,F7,F8,F9) obj_extend(o,F1), obj_extend(o,F2), obj_extend(o,F3), obj_extend(o,F4), obj_extend(o,F5), obj_extend(o,F6), obj_extend(o,F7), obj_extend(o,F8), obj_extend(o,F9) ///-
#define EXTEND_T(...) EXPAND(EXTEND_T, __VA_ARGS__)
#define EXTEND_T2(o,F1) obj_extend_t(o,F1) ///-
#define EXTEND_T3(o,F1,F2) obj_extend_t(o,F1), obj_extend_t(o,F2) ///-
#define EXTEND_T4(o,F1,F2,F3) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3) ///-
#define EXTEND_T5(o,F1,F2,F3,F4) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4) ///-
#define EXTEND_T6(o,F1,F2,F3,F4,F5) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5) ///-
#define EXTEND_T7(o,F1,F2,F3,F4,F5,F6) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5), obj_extend_t(o,F6) ///-
#define EXTEND_T8(o,F1,F2,F3,F4,F5,F6,F7) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5), obj_extend_t(o,F6), obj_extend_t(o,F7) ///-
#define EXTEND_T9(o,F1,F2,F3,F4,F5,F6,F7,F8) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5), obj_extend_t(o,F6), obj_extend_t(o,F7), obj_extend_t(o,F8) ///-
#define EXTEND_T10(o,F1,F2,F3,F4,F5,F6,F7,F8,F9) obj_extend_t(o,F1), obj_extend_t(o,F2), obj_extend_t(o,F3), obj_extend_t(o,F4), obj_extend_t(o,F5), obj_extend_t(o,F6), obj_extend_t(o,F7), obj_extend_t(o,F8), obj_extend_t(o,F9) ///-
// --- declare vtables
API extern void (*obj_ctor[256])(); ///-
API extern void (*obj_dtor[256])(); ///-
API extern char* (*obj_save[256])(); ///-
API extern bool (*obj_load[256])(); ///-
API extern int (*obj_test[256])(); ///-
API extern int (*obj_init[256])(); ///-
API extern int (*obj_quit[256])(); ///-
API extern int (*obj_tick[256])(); ///-
API extern int (*obj_draw[256])(); ///-
API extern int (*obj_lerp[256])(); ///-
API extern int (*obj_aabb[256])(); ///-
API extern int (*obj_edit[256])(); ///-
API extern int (*obj_menu[256])(); ///-
API extern char* (*obj_icon[256])(); ///-
API extern const char*OBJTYPES[256]; ///-
// ----------------------------------------------------------------------------
// core
API uintptr_t obj_header(const void *o);
API uintptr_t obj_id(const void *o);
API const char* obj_type(const void *o);
API unsigned obj_typeid(const void *o);
API int obj_sizeof(const void *o);
API int obj_size(const void *o); // size of all members together in struct. may include padding bytes.
API char* obj_data(void *o); // pointer to the first member in struct
API const char* obj_datac(const void *o); // const pointer to the first struct member
API void* obj_payload(const void *o); // pointer right after last member in struct
API void* obj_zero(void *o); // reset all object members
// ----------------------------------------------------------------------------
// refcounting
API void* obj_ref(void *oo);
API void* obj_unref(void *oo);
// ----------------------------------------------------------------------------
// scene tree
#define each_objchild(p,T,o) /*non-recursive*/ \
(array(struct obj*)* children = obj_children(p); children; children = 0) \
for(int _i = 1, _end = array_count(*children); _i < _end; ++_i) \
for(T o = (T)((*children)[_i]); o && (obj_parent(o) == p); o = 0)
API obj* obj_detach(void *c);
API obj* obj_attach(void *o, void *c);
API obj* obj_root(const void *o);
API obj* obj_parent(const void *o);
API array(obj*)*obj_children(const void *o); // child[0]: parent, child[1]: 1st child, child[2]: 2nd child...
API array(obj*)*obj_siblings(const void *o); // child[0]: grandpa, child[1]: sibling1, child[2]: sibling2...
API int obj_dumptree(const void *o);
// ----------------------------------------------------------------------------
// metadata
API void* obj_setmeta(void *o, const char *key, const char *value);
API const char* obj_meta(const void *o, const char *key);
API void* obj_setname(void *o, const char *name);
API const char* obj_name(const void *o);
// ----------------------------------------------------------------------------
// stl
API void* obj_swap(void *dst, void *src);
API void* obj_copy_fast(void *dst, const void *src);
API void* obj_copy(void *dst, const void *src);
API int obj_comp_fast(const void *a, const void *b);
API int obj_comp(const void *a, const void *b);
API int obj_lesser(const void *a, const void *b);
API int obj_greater(const void *a, const void *b);
API int obj_equal(const void *a, const void *b);
API uint64_t obj_hash(const void *o);
// ----------------------------------------------------------------------------
// debug
API bool obj_hexdump(const void *oo);
API int obj_print(const void *o);
API int obj_printf(const void *o, const char *text);
API int obj_console(const void *o); // obj_output() ?
#define obj_printf(o, ...) obj_printf(o, va(__VA_ARGS__))
// ----------------------------------------------------------------------------
// serialization
API char* obj_saveini(const void *o);
API obj* obj_mergeini(void *o, const char *ini);
API obj* obj_loadini(void *o, const char *ini);
API char* obj_savejson(const void *o);
API obj* obj_mergejson(void *o, const char *json);
API obj* obj_loadjson(void *o, const char *json);
API char* obj_savebin(const void *o);
API obj* obj_mergebin(void *o, const char *sav);
API obj* obj_loadbin(void *o, const char *sav);
API char* obj_savempack(const void *o); // @todo
API obj* obj_mergempack(void *o, const char *sav); // @todo
API obj* obj_loadmpack(void *o, const char *sav); // @todo
API int obj_push(const void *o);
API int obj_pop(void *o);
// ----------------------------------------------------------------------------
// components
API bool obj_addcomponent(entity *e, unsigned c, void *ptr);
API bool obj_hascomponent(entity *e, unsigned c);
API void* obj_getcomponent(entity *e, unsigned c);
API bool obj_delcomponent(entity *e, unsigned c);
API bool obj_usecomponent(entity *e, unsigned c);
API bool obj_offcomponent(entity *e, unsigned c);
API char* entity_save(entity *self);
// ----------------------------------------------------------------------------
// reflection
#define each_objmember(oo,TYPE,NAME,PTR) \
(array(reflect_t) *found_ = members_find(obj_type(oo)); found_; found_ = 0) \
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
for(reflect_t *R = &(*found_)[it_]; R; R = 0 ) \
for(const char *NAME = R->name, *TYPE = R->type; NAME || TYPE; ) \
for(void *PTR = ((char*)oo) + R->sz ; NAME || TYPE ; NAME = TYPE = 0 )
API void* obj_clone(const void *src);
API void* obj_merge(void *dst, const void *src); // @testme
API void* obj_mutate(void *dst, const void *src);
API void* obj_make(const char *str);
// built-ins
typedef enum OBJTYPE_BUILTINS {
OBJTYPE_obj = 0,
OBJTYPE_entity = 1,
OBJTYPE_vec2 = 2,
OBJTYPE_vec3 = 3,
OBJTYPE_vec4 = 4,
OBJTYPE_quat = 5,
OBJTYPE_mat33 = 6,
OBJTYPE_mat34 = 7,
OBJTYPE_mat44 = 8,
OBJTYPE_vec2i = 9,
OBJTYPE_vec3i = 10,
} OBJTYPE_BUILTINS;

File diff suppressed because it is too large Load Diff

View File

@ -1,389 +1,389 @@
// ----------------------------------------------------------------------------
// compression api
enum COMPRESS_FLAGS {
COMPRESS_RAW = 0,
COMPRESS_PPP = (1<<4),
COMPRESS_ULZ = (2<<4),
COMPRESS_LZ4 = (3<<4),
COMPRESS_CRUSH = (4<<4),
COMPRESS_DEFLATE = (5<<4),
COMPRESS_LZP1 = (6<<4),
COMPRESS_LZMA = (7<<4),
COMPRESS_BALZ = (8<<4),
COMPRESS_LZW3 = (9<<4),
COMPRESS_LZSS = (10<<4),
COMPRESS_BCM = (11<<4),
COMPRESS_ZLIB = (12<<4), // same as deflate with header
};
API unsigned zbounds(unsigned inlen, unsigned flags);
API unsigned zencode(void *out, unsigned outlen, const void *in, unsigned inlen, unsigned flags);
API unsigned zexcess(unsigned flags);
API unsigned zdecode(void *out, unsigned outlen, const void *in, unsigned inlen, unsigned flags);
// ----------------------------------------------------------------------------
// array de/interleaving
//
// results:
// R0G0B0 R1G1B1 R2G2B2... -> R0R1R2... B0B1B2... G0G1G2...
// R0G0B0A0 R1G1B1A1 R2G2B2A2... -> R0R1R2... A0A1A2... B0B1B2... G0G1G2...
API void *interleave( void *out, const void *list, int list_count, int sizeof_item, unsigned columns );
// ----------------------------------------------------------------------------
// cobs en/decoder
API unsigned cobs_bounds(unsigned len);
API unsigned cobs_encode(const void *in, unsigned inlen, void *out, unsigned outlen);
API unsigned cobs_decode(const void *in, unsigned inlen, void *out, unsigned outlen);
// ----------------------------------------------------------------------------
// base92 en/decoder
API unsigned base92_encode(const void *in, unsigned inlen, void* out, unsigned outlen);
API unsigned base92_decode(const void *in, unsigned inlen, void* out, unsigned outlen);
API unsigned base92_bounds(unsigned inlen);
// ----------------------------------------------------------------------------
// netstring en/decoder
API unsigned netstring_bounds(unsigned inlen);
API unsigned netstring_encode(const char *in, unsigned inlen, char *out, unsigned outlen);
API unsigned netstring_decode(const char *in, unsigned inlen, char *out, unsigned outlen);
// ----------------------------------------------------------------------------
// delta en/decoder
API void delta8_encode(void *buffer, unsigned count);
API void delta8_decode(void *buffer, unsigned count);
API void delta16_encode(void *buffer, unsigned count);
API void delta16_decode(void *buffer, unsigned count);
API void delta32_encode(void *buffer, unsigned count);
API void delta32_decode(void *buffer, unsigned count);
API void delta64_encode(void *buffer, unsigned count);
API void delta64_decode(void *buffer, unsigned count);
// ----------------------------------------------------------------------------
// zigzag en/decoder
API uint64_t zig64( int64_t value ); // convert sign|magnitude to magnitude|sign
API int64_t zag64( uint64_t value ); // convert magnitude|sign to sign|magnitude
API uint32_t enczig32u( int32_t n);
API uint64_t enczig64u( int64_t n);
API int32_t deczig32i(uint32_t n);
API int64_t deczig64i(uint64_t n);
// ----------------------------------------------------------------------------
// arc4 en/decryptor
API void *arc4( void *buffer, unsigned buflen, const void *pass, unsigned passlen );
// ----------------------------------------------------------------------------
// crc64
API uint64_t crc64(uint64_t h, const void *ptr, uint64_t len);
// ----------------------------------------------------------------------------
// entropy encoder
API void entropy( void *buf, unsigned n );
// -----------------------------------------------------------------------------
// semantic versioning in a single byte (octal)
API int semver( int major, int minor, int patch );
API int semvercmp( int v1, int v2 );
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
#define SEMVERFMT "%03o"
// -----------------------------------------------------------------------------
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
typedef struct byte2 { uint8_t x,y; } byte2;
typedef struct byte3 { uint8_t x,y,z; } byte3;
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
typedef struct int2 { int x,y; } int2;
typedef struct int3 { int x,y,z; } int3;
typedef struct int4 { int x,y,z,w; } int4;
typedef struct uint2 { unsigned int x,y; } uint2;
typedef struct uint3 { unsigned int x,y,z; } uint3;
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
typedef struct float2 { float x,y; } float2;
typedef struct float3 { float x,y,z; } float3;
typedef struct float4 { float x,y,z,w; } float4;
typedef struct double2 { double x,y; } double2;
typedef struct double3 { double x,y,z; } double3;
typedef struct double4 { double x,y,z,w; } double4;
#define byte2(x,y) C_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
#define byte3(x,y,z) C_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
#define byte4(x,y,z,w) C_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
#define int2(x,y) C_CAST(int2, (int)(x), (int)(y) )
#define int3(x,y,z) C_CAST(int3, (int)(x), (int)(y), (int)(z) )
#define int4(x,y,z,w) C_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
#define uint2(x,y) C_CAST(uint2, (unsigned)(x), (unsigned)(y) )
#define uint3(x,y,z) C_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
#define uint4(x,y,z,w) C_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
#define float2(x,y) C_CAST(float2, (float)(x), (float)(y) )
#define float3(x,y,z) C_CAST(float3, (float)(x), (float)(y), (float)(z) )
#define float4(x,y,z,w) C_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
#define double2(x,y) C_CAST(double2, (double)(x), (double)(y) )
#define double3(x,y,z) C_CAST(double3, (double)(x), (double)(y), (double)(z) )
#define double4(x,y,z,w) C_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
// -----------------------------------------------------------------------------
// compile-time fourcc, eightcc
API char *cc4str(unsigned cc);
API char *cc8str(uint64_t cc);
enum {
# define _(a,b,c,d,e) cc__##a, cc__##b, cc__##c, cc__##d, cc__##e
cc__1 = '1', _(2,3,4,5,6),_(7,8,9,0,_), cc__ = ' ',
cc__A = '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),
cc__a = '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),
# undef _
};
#ifdef BIG
#define cc4(a,b,c,d) ((uint32_t)(cc__##a<<24) | (cc__##b<<16) | (cc__##c<<8) | (cc__##d<<0))
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(a,b,c,d) << 32ULL) | cc4(e,f,g,h))
#else
#define cc4(a,b,c,d) ((uint32_t)(cc__##d<<24) | (cc__##c<<16) | (cc__##b<<8) | (cc__##a<<0))
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(e,f,g,h) << 32ULL) | cc4(a,b,c,d))
#endif
#define cc3(a,b,c) cc4(,a,b,c)
#define cc5(a,b,c,d,e) cc6(,a,b,c,d,e)
#define cc6(a,b,c,d,e,f) cc7(,a,b,c,d,e,f)
#define cc7(a,b,c,d,e,f,g) cc8(,a,b,c,d,e,f,g)
// ----------------------------------------------------------------------------
// text conversions
API char* ftoa1(float v);
API char* ftoa2(vec2 v);
API char* ftoa3(vec3 v);
API char* ftoa4(vec4 v);
API float atof1(const char *s);
API vec2 atof2(const char *s);
API vec3 atof3(const char *s);
API vec4 atof4(const char *s);
API char* itoa1(int v);
API char* itoa2(vec2i v);
API char* itoa3(vec3i v);
API int atoi1(const char *s);
API vec2i atoi2(const char *s);
API vec3i atoi3(const char *s);
// ----------------------------------------------------------------------------
// endianness
API int is_big();
API int is_little();
API uint16_t swap16( uint16_t x );
API uint32_t swap32( uint32_t x );
API uint64_t swap64( uint64_t x );
API float swap32f(float n);
API double swap64f(double n);
API void swapf(float *a, float *b);
API void swapf2(vec2 *a, vec2 *b);
API void swapf3(vec3 *a, vec3 *b);
API void swapf4(vec4 *a, vec4 *b);
API uint16_t lil16(uint16_t n); // swap16 as lil
API uint32_t lil32(uint32_t n); // swap32 as lil
API uint64_t lil64(uint64_t n); // swap64 as lil
API float lil32f(float n); // swap32 as lil
API double lil64f(double n); // swap64 as lil
API uint16_t big16(uint16_t n); // swap16 as big
API uint32_t big32(uint32_t n); // swap32 as big
API uint64_t big64(uint64_t n); // swap64 as big
API float big32f(float n); // swap32 as big
API double big64f(double n); // swap64 as big
API uint16_t* lil16p(void *p, int sz);
API uint32_t* lil32p(void *p, int sz);
API uint64_t* lil64p(void *p, int sz);
API float * lil32pf(void *p, int sz);
API double * lil64pf(void *p, int sz);
API uint16_t* big16p(void *p, int sz);
API uint32_t* big32p(void *p, int sz);
API uint64_t* big64p(void *p, int sz);
API float * big32pf(void *p, int sz);
API double * big64pf(void *p, int sz);
#if is(cl)
#define swap16 _byteswap_ushort
#define swap32 _byteswap_ulong
#define swap64 _byteswap_uint64
#elif is(gcc)
#define swap16 __builtin_bswap16
#define swap32 __builtin_bswap32
#define swap64 __builtin_bswap64
#endif
#define hton16 big16
#define ntoh16 big16
#define hton32 big32
#define ntoh32 big32
#define hton64 big64
#define ntoh64 big64
#define IS_BIG ((*(uint16_t *)"\0\1") == 1)
#define IS_LITTLE ((*(uint16_t *)"\0\1") != 1)
// ----------------------------------------------------------------------------
// half packing
typedef uint16_t half;
API float half_to_float(half value);
API half float_to_half(float value);
// ----------------------------------------------------------------------------
// int packing
// pack16i() -- store a 16-bit int into a char buffer (like htons())
// pack32i() -- store a 32-bit int into a char buffer (like htonl())
// pack64i() -- store a 64-bit int into a char buffer (like htonl())
API void pack16i(uint8_t *buf, uint16_t i, int swap);
API void pack32i(uint8_t *buf, uint32_t i, int swap);
API void pack64i(uint8_t *buf, uint64_t i, int swap);
// unpack16i() -- unpack a 16-bit int from a char buffer (like ntohs())
// unpack32i() -- unpack a 32-bit int from a char buffer (like ntohl())
// unpack64i() -- unpack a 64-bit int from a char buffer (like ntohl())
// changes unsigned numbers to signed if needed.
API int16_t unpack16i(const uint8_t *buf, int swap);
API int32_t unpack32i(const uint8_t *buf, int swap);
API int64_t unpack64i(const uint8_t *buf, int swap);
// ----------------------------------------------------------------------------
// float un/packing: 8 (micro), 16 (half), 32 (float), 64 (double) types
#define pack754_8(f) ( pack754((f), 8, 4))
#define pack754_16(f) ( pack754((f), 16, 5))
#define pack754_32(f) ( pack754((f), 32, 8))
#define pack754_64(f) ( pack754((f), 64, 11))
#define unpack754_8(u) (unpack754((u), 8, 4))
#define unpack754_16(u) (unpack754((u), 16, 5))
#define unpack754_32(u) (unpack754((u), 32, 8))
#define unpack754_64(u) (unpack754((u), 64, 11))
API uint64_t pack754(long double f, unsigned bits, unsigned expbits);
API long double unpack754(uint64_t i, unsigned bits, unsigned expbits);
// ----------------------------------------------------------------------------
// variable-length integer packing
API uint64_t pack64uv( uint8_t *buffer, uint64_t value );
API uint64_t unpack64uv( const uint8_t *buffer, uint64_t *value );
API uint64_t pack64iv( uint8_t *buffer, int64_t value_ );
API uint64_t unpack64iv( const uint8_t *buffer, int64_t *value );
// ----------------------------------------------------------------------------
// msgpack v5, schema based struct/buffer bitpacking
// api v2
API int msgpack(const char *fmt, ... ); // va arg pack "n,b,u,d/i,s,p,f/g,e,[,{". returns number of written bytes
API int msgunpack(const char *fmt, ... ); // va arg pack "n,b,u,d/i,s,p,f/g,e,[,{". returns number of parsed args
// api v1
API int msgpack_new(uint8_t *w, size_t l);
API int msgpack_nil(); // write null
API int msgpack_chr(bool n); // write boolean
API int msgpack_uns(uint64_t n); // write unsigned integer
API int msgpack_int(int64_t n); // write integer
API int msgpack_str(const char *s); // write string
API int msgpack_bin(const char *s, size_t n); // write binary pointer
API int msgpack_flt(double g); // write real
API int msgpack_ext(uint8_t key, void *val, size_t n); // write extension type
API int msgpack_arr(uint32_t n); // write array mark for next N items
API int msgpack_map(uint32_t n); // write map mark for next N pairs (N keys + N values)
API int msgpack_eof(); // write full?
API int msgpack_err(); // write error?
API bool msgunpack_new( const void *opaque_or_FILE, size_t bytes );
API bool msgunpack_nil();
API bool msgunpack_chr(bool *chr);
API bool msgunpack_uns(uint64_t *uns);
API bool msgunpack_int(int64_t *sig);
API bool msgunpack_str(char **str);
API bool msgunpack_bin(void **bin, uint64_t *len);
API bool msgunpack_flt(float *flt);
API bool msgunpack_dbl(double *dbl);
API bool msgunpack_ext(uint8_t *key, void **val, uint64_t *len);
API bool msgunpack_arr(uint64_t *len);
API bool msgunpack_map(uint64_t *len);
API bool msgunpack_eof();
API bool msgunpack_err();
// ----------------------------------------------------------------------------
// Based on code by Brian "Beej Jorgensen" Hall (public domain) [1].
// Based on code by Ginger Bill's half<->float (public domain) [2].
// - rlyeh, public domain.
//
// pack.c -- perl/python-ish pack/unpack functions
// like printf and scanf, but for binary data.
//
// format flags:
// (<) little endian (>) big endian (! also) (=) native endian
// (c) 8-bit char (b) 8-bit byte
// (h) 16-bit half (w) 16-bit word
// (i) 32-bit integer (u) 32-bit unsigned (f) 32-bit float
// (l) 64-bit long (q) 64-bit quad (d) 64-bit double
// (v) varint
// (s) string (64-bit varint length prepended)
// (S) string (32-bit fixed length prepended)
// (m) memblock (64-bit varint length prepended)
// (M) memblock (32-bit fixed length prepended)
// (z) memblock (zeroed)
// (#) number of arguments processed (only when unpacking)
//
// @todo:
// - (x) document & test flag
// @totest:
// - (s) string (64-bit variable length automatically prepended)
// - (S) string (32-bit fixed length automatically prepended)
// - (m) memblock (64-bit variable length automatically prepended)
// - (M) memblock (32-bit fixed length automatically prepended)
// - (z) memblock (zeroed)
// - (#) number of arguments processed (only when unpacking)
// - save data dictated by the format string from the buffer. return: number of bytes written, or 0 if error.
// if first argument is zero, returns number of bytes required for packing.
API int savef(FILE *file, const char *format, ...);
API int saveb(unsigned char *buf, const char *format, ...);
// - load data dictated by the format string into the buffer. return: number of bytes read, or 0 if error.
// if first argument is zero, returns number of bytes required for unpacking.
API int loadf(FILE *file, const char *format, ...);
API int loadb(const unsigned char *buf, const char *format, ...);
// ----------------------------------------------------------------------------
// compression api
enum COMPRESS_FLAGS {
COMPRESS_RAW = 0,
COMPRESS_PPP = (1<<4),
COMPRESS_ULZ = (2<<4),
COMPRESS_LZ4 = (3<<4),
COMPRESS_CRUSH = (4<<4),
COMPRESS_DEFLATE = (5<<4),
COMPRESS_LZP1 = (6<<4),
COMPRESS_LZMA = (7<<4),
COMPRESS_BALZ = (8<<4),
COMPRESS_LZW3 = (9<<4),
COMPRESS_LZSS = (10<<4),
COMPRESS_BCM = (11<<4),
COMPRESS_ZLIB = (12<<4), // same as deflate with header
};
API unsigned zbounds(unsigned inlen, unsigned flags);
API unsigned zencode(void *out, unsigned outlen, const void *in, unsigned inlen, unsigned flags);
API unsigned zexcess(unsigned flags);
API unsigned zdecode(void *out, unsigned outlen, const void *in, unsigned inlen, unsigned flags);
// ----------------------------------------------------------------------------
// array de/interleaving
//
// results:
// R0G0B0 R1G1B1 R2G2B2... -> R0R1R2... B0B1B2... G0G1G2...
// R0G0B0A0 R1G1B1A1 R2G2B2A2... -> R0R1R2... A0A1A2... B0B1B2... G0G1G2...
API void *interleave( void *out, const void *list, int list_count, int sizeof_item, unsigned columns );
// ----------------------------------------------------------------------------
// cobs en/decoder
API unsigned cobs_bounds(unsigned len);
API unsigned cobs_encode(const void *in, unsigned inlen, void *out, unsigned outlen);
API unsigned cobs_decode(const void *in, unsigned inlen, void *out, unsigned outlen);
// ----------------------------------------------------------------------------
// base92 en/decoder
API unsigned base92_encode(const void *in, unsigned inlen, void* out, unsigned outlen);
API unsigned base92_decode(const void *in, unsigned inlen, void* out, unsigned outlen);
API unsigned base92_bounds(unsigned inlen);
// ----------------------------------------------------------------------------
// netstring en/decoder
API unsigned netstring_bounds(unsigned inlen);
API unsigned netstring_encode(const char *in, unsigned inlen, char *out, unsigned outlen);
API unsigned netstring_decode(const char *in, unsigned inlen, char *out, unsigned outlen);
// ----------------------------------------------------------------------------
// delta en/decoder
API void delta8_encode(void *buffer, unsigned count);
API void delta8_decode(void *buffer, unsigned count);
API void delta16_encode(void *buffer, unsigned count);
API void delta16_decode(void *buffer, unsigned count);
API void delta32_encode(void *buffer, unsigned count);
API void delta32_decode(void *buffer, unsigned count);
API void delta64_encode(void *buffer, unsigned count);
API void delta64_decode(void *buffer, unsigned count);
// ----------------------------------------------------------------------------
// zigzag en/decoder
API uint64_t zig64( int64_t value ); // convert sign|magnitude to magnitude|sign
API int64_t zag64( uint64_t value ); // convert magnitude|sign to sign|magnitude
API uint32_t enczig32u( int32_t n);
API uint64_t enczig64u( int64_t n);
API int32_t deczig32i(uint32_t n);
API int64_t deczig64i(uint64_t n);
// ----------------------------------------------------------------------------
// arc4 en/decryptor
API void *arc4( void *buffer, unsigned buflen, const void *pass, unsigned passlen );
// ----------------------------------------------------------------------------
// crc64
API uint64_t crc64(uint64_t h, const void *ptr, uint64_t len);
// ----------------------------------------------------------------------------
// entropy encoder
API void entropy( void *buf, unsigned n );
// -----------------------------------------------------------------------------
// semantic versioning in a single byte (octal)
API int semver( int major, int minor, int patch );
API int semvercmp( int v1, int v2 );
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
#define SEMVERFMT "%03o"
// -----------------------------------------------------------------------------
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
typedef struct byte2 { uint8_t x,y; } byte2;
typedef struct byte3 { uint8_t x,y,z; } byte3;
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
typedef struct int2 { int x,y; } int2;
typedef struct int3 { int x,y,z; } int3;
typedef struct int4 { int x,y,z,w; } int4;
typedef struct uint2 { unsigned int x,y; } uint2;
typedef struct uint3 { unsigned int x,y,z; } uint3;
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
typedef struct float2 { float x,y; } float2;
typedef struct float3 { float x,y,z; } float3;
typedef struct float4 { float x,y,z,w; } float4;
typedef struct double2 { double x,y; } double2;
typedef struct double3 { double x,y,z; } double3;
typedef struct double4 { double x,y,z,w; } double4;
#define byte2(x,y) C_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
#define byte3(x,y,z) C_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
#define byte4(x,y,z,w) C_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
#define int2(x,y) C_CAST(int2, (int)(x), (int)(y) )
#define int3(x,y,z) C_CAST(int3, (int)(x), (int)(y), (int)(z) )
#define int4(x,y,z,w) C_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
#define uint2(x,y) C_CAST(uint2, (unsigned)(x), (unsigned)(y) )
#define uint3(x,y,z) C_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
#define uint4(x,y,z,w) C_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
#define float2(x,y) C_CAST(float2, (float)(x), (float)(y) )
#define float3(x,y,z) C_CAST(float3, (float)(x), (float)(y), (float)(z) )
#define float4(x,y,z,w) C_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
#define double2(x,y) C_CAST(double2, (double)(x), (double)(y) )
#define double3(x,y,z) C_CAST(double3, (double)(x), (double)(y), (double)(z) )
#define double4(x,y,z,w) C_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
// -----------------------------------------------------------------------------
// compile-time fourcc, eightcc
API char *cc4str(unsigned cc);
API char *cc8str(uint64_t cc);
enum {
# define _(a,b,c,d,e) cc__##a, cc__##b, cc__##c, cc__##d, cc__##e
cc__1 = '1', _(2,3,4,5,6),_(7,8,9,0,_), cc__ = ' ',
cc__A = '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),
cc__a = '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),
# undef _
};
#ifdef BIG
#define cc4(a,b,c,d) ((uint32_t)(cc__##a<<24) | (cc__##b<<16) | (cc__##c<<8) | (cc__##d<<0))
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(a,b,c,d) << 32ULL) | cc4(e,f,g,h))
#else
#define cc4(a,b,c,d) ((uint32_t)(cc__##d<<24) | (cc__##c<<16) | (cc__##b<<8) | (cc__##a<<0))
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(e,f,g,h) << 32ULL) | cc4(a,b,c,d))
#endif
#define cc3(a,b,c) cc4(,a,b,c)
#define cc5(a,b,c,d,e) cc6(,a,b,c,d,e)
#define cc6(a,b,c,d,e,f) cc7(,a,b,c,d,e,f)
#define cc7(a,b,c,d,e,f,g) cc8(,a,b,c,d,e,f,g)
// ----------------------------------------------------------------------------
// text conversions
API char* ftoa1(float v);
API char* ftoa2(vec2 v);
API char* ftoa3(vec3 v);
API char* ftoa4(vec4 v);
API float atof1(const char *s);
API vec2 atof2(const char *s);
API vec3 atof3(const char *s);
API vec4 atof4(const char *s);
API char* itoa1(int v);
API char* itoa2(vec2i v);
API char* itoa3(vec3i v);
API int atoi1(const char *s);
API vec2i atoi2(const char *s);
API vec3i atoi3(const char *s);
// ----------------------------------------------------------------------------
// endianness
API int is_big();
API int is_little();
API uint16_t swap16( uint16_t x );
API uint32_t swap32( uint32_t x );
API uint64_t swap64( uint64_t x );
API float swap32f(float n);
API double swap64f(double n);
API void swapf(float *a, float *b);
API void swapf2(vec2 *a, vec2 *b);
API void swapf3(vec3 *a, vec3 *b);
API void swapf4(vec4 *a, vec4 *b);
API uint16_t lil16(uint16_t n); // swap16 as lil
API uint32_t lil32(uint32_t n); // swap32 as lil
API uint64_t lil64(uint64_t n); // swap64 as lil
API float lil32f(float n); // swap32 as lil
API double lil64f(double n); // swap64 as lil
API uint16_t big16(uint16_t n); // swap16 as big
API uint32_t big32(uint32_t n); // swap32 as big
API uint64_t big64(uint64_t n); // swap64 as big
API float big32f(float n); // swap32 as big
API double big64f(double n); // swap64 as big
API uint16_t* lil16p(void *p, int sz);
API uint32_t* lil32p(void *p, int sz);
API uint64_t* lil64p(void *p, int sz);
API float * lil32pf(void *p, int sz);
API double * lil64pf(void *p, int sz);
API uint16_t* big16p(void *p, int sz);
API uint32_t* big32p(void *p, int sz);
API uint64_t* big64p(void *p, int sz);
API float * big32pf(void *p, int sz);
API double * big64pf(void *p, int sz);
#if is(cl)
#define swap16 _byteswap_ushort
#define swap32 _byteswap_ulong
#define swap64 _byteswap_uint64
#elif is(gcc)
#define swap16 __builtin_bswap16
#define swap32 __builtin_bswap32
#define swap64 __builtin_bswap64
#endif
#define hton16 big16
#define ntoh16 big16
#define hton32 big32
#define ntoh32 big32
#define hton64 big64
#define ntoh64 big64
#define IS_BIG ((*(uint16_t *)"\0\1") == 1)
#define IS_LITTLE ((*(uint16_t *)"\0\1") != 1)
// ----------------------------------------------------------------------------
// half packing
typedef uint16_t half;
API float half_to_float(half value);
API half float_to_half(float value);
// ----------------------------------------------------------------------------
// int packing
// pack16i() -- store a 16-bit int into a char buffer (like htons())
// pack32i() -- store a 32-bit int into a char buffer (like htonl())
// pack64i() -- store a 64-bit int into a char buffer (like htonl())
API void pack16i(uint8_t *buf, uint16_t i, int swap);
API void pack32i(uint8_t *buf, uint32_t i, int swap);
API void pack64i(uint8_t *buf, uint64_t i, int swap);
// unpack16i() -- unpack a 16-bit int from a char buffer (like ntohs())
// unpack32i() -- unpack a 32-bit int from a char buffer (like ntohl())
// unpack64i() -- unpack a 64-bit int from a char buffer (like ntohl())
// changes unsigned numbers to signed if needed.
API int16_t unpack16i(const uint8_t *buf, int swap);
API int32_t unpack32i(const uint8_t *buf, int swap);
API int64_t unpack64i(const uint8_t *buf, int swap);
// ----------------------------------------------------------------------------
// float un/packing: 8 (micro), 16 (half), 32 (float), 64 (double) types
#define pack754_8(f) ( pack754((f), 8, 4))
#define pack754_16(f) ( pack754((f), 16, 5))
#define pack754_32(f) ( pack754((f), 32, 8))
#define pack754_64(f) ( pack754((f), 64, 11))
#define unpack754_8(u) (unpack754((u), 8, 4))
#define unpack754_16(u) (unpack754((u), 16, 5))
#define unpack754_32(u) (unpack754((u), 32, 8))
#define unpack754_64(u) (unpack754((u), 64, 11))
API uint64_t pack754(long double f, unsigned bits, unsigned expbits);
API long double unpack754(uint64_t i, unsigned bits, unsigned expbits);
// ----------------------------------------------------------------------------
// variable-length integer packing
API uint64_t pack64uv( uint8_t *buffer, uint64_t value );
API uint64_t unpack64uv( const uint8_t *buffer, uint64_t *value );
API uint64_t pack64iv( uint8_t *buffer, int64_t value_ );
API uint64_t unpack64iv( const uint8_t *buffer, int64_t *value );
// ----------------------------------------------------------------------------
// msgpack v5, schema based struct/buffer bitpacking
// api v2
API int msgpack(const char *fmt, ... ); // va arg pack "n,b,u,d/i,s,p,f/g,e,[,{". returns number of written bytes
API int msgunpack(const char *fmt, ... ); // va arg pack "n,b,u,d/i,s,p,f/g,e,[,{". returns number of parsed args
// api v1
API int msgpack_new(uint8_t *w, size_t l);
API int msgpack_nil(); // write null
API int msgpack_chr(bool n); // write boolean
API int msgpack_uns(uint64_t n); // write unsigned integer
API int msgpack_int(int64_t n); // write integer
API int msgpack_str(const char *s); // write string
API int msgpack_bin(const char *s, size_t n); // write binary pointer
API int msgpack_flt(double g); // write real
API int msgpack_ext(uint8_t key, void *val, size_t n); // write extension type
API int msgpack_arr(uint32_t n); // write array mark for next N items
API int msgpack_map(uint32_t n); // write map mark for next N pairs (N keys + N values)
API int msgpack_eof(); // write full?
API int msgpack_err(); // write error?
API bool msgunpack_new( const void *opaque_or_FILE, size_t bytes );
API bool msgunpack_nil();
API bool msgunpack_chr(bool *chr);
API bool msgunpack_uns(uint64_t *uns);
API bool msgunpack_int(int64_t *sig);
API bool msgunpack_str(char **str);
API bool msgunpack_bin(void **bin, uint64_t *len);
API bool msgunpack_flt(float *flt);
API bool msgunpack_dbl(double *dbl);
API bool msgunpack_ext(uint8_t *key, void **val, uint64_t *len);
API bool msgunpack_arr(uint64_t *len);
API bool msgunpack_map(uint64_t *len);
API bool msgunpack_eof();
API bool msgunpack_err();
// ----------------------------------------------------------------------------
// Based on code by Brian "Beej Jorgensen" Hall (public domain) [1].
// Based on code by Ginger Bill's half<->float (public domain) [2].
// - rlyeh, public domain.
//
// pack.c -- perl/python-ish pack/unpack functions
// like printf and scanf, but for binary data.
//
// format flags:
// (<) little endian (>) big endian (! also) (=) native endian
// (c) 8-bit char (b) 8-bit byte
// (h) 16-bit half (w) 16-bit word
// (i) 32-bit integer (u) 32-bit unsigned (f) 32-bit float
// (l) 64-bit long (q) 64-bit quad (d) 64-bit double
// (v) varint
// (s) string (64-bit varint length prepended)
// (S) string (32-bit fixed length prepended)
// (m) memblock (64-bit varint length prepended)
// (M) memblock (32-bit fixed length prepended)
// (z) memblock (zeroed)
// (#) number of arguments processed (only when unpacking)
//
// @todo:
// - (x) document & test flag
// @totest:
// - (s) string (64-bit variable length automatically prepended)
// - (S) string (32-bit fixed length automatically prepended)
// - (m) memblock (64-bit variable length automatically prepended)
// - (M) memblock (32-bit fixed length automatically prepended)
// - (z) memblock (zeroed)
// - (#) number of arguments processed (only when unpacking)
// - save data dictated by the format string from the buffer. return: number of bytes written, or 0 if error.
// if first argument is zero, returns number of bytes required for packing.
API int savef(FILE *file, const char *format, ...);
API int saveb(unsigned char *buf, const char *format, ...);
// - load data dictated by the format string into the buffer. return: number of bytes read, or 0 if error.
// if first argument is zero, returns number of bytes required for unpacking.
API int loadf(FILE *file, const char *format, ...);
API int loadb(const unsigned char *buf, const char *format, ...);

View File

@ -1,55 +1,55 @@
#if ENABLE_PROFILER
profiler_t profiler;
int profiler_enabled = 1;
void (profiler_init)() { map_init(profiler, less_str, hash_str); profiler_enabled &= !!profiler; }
int (profiler_enable)(bool on) { return profiler_enabled = on; }
void (ui_profiler)() {
// @todo: ui_plot()
double fps = window_fps();
profile_setstat("Render.num_fps", fps);
enum { COUNT = 300 };
static float values[COUNT] = {0}; static int offset = 0;
values[offset=(offset+1)%COUNT] = fps;
// draw fps-meter: 300 samples, [0..70] range each, 70px height plot ...
// ... unless filtering is enabled
if( !(ui_filter && ui_filter[0]) ) {
nk_layout_row_dynamic(ui_ctx, 70, 1);
int index = -1;
if( nk_chart_begin(ui_ctx, NK_CHART_LINES, COUNT, 0.f, 70.f) ) {
for( int i = 0; i < COUNT; ++i ) {
nk_flags res = nk_chart_push(ui_ctx, (float)values[i]);
if( res & NK_CHART_HOVERING ) index = i;
if( res & NK_CHART_CLICKED ) index = i;
}
nk_chart_end(ui_ctx);
}
// hightlight 60fps, 36fps and 12fps
struct nk_rect space; nk_layout_peek(&space, ui_ctx);
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
nk_stroke_line(canvas, space.x+0,space.y-60,space.x+space.w,space.y-60, 1.0, nk_rgba(0,255,0,128));
nk_stroke_line(canvas, space.x+0,space.y-36,space.x+space.w,space.y-36, 1.0, nk_rgba(255,255,0,128));
nk_stroke_line(canvas, space.x+0,space.y-12,space.x+space.w,space.y-12, 1.0, nk_rgba(255,0,0,128));
if( index >= 0 ) {
nk_tooltipf(ui_ctx, "%.2f fps", (float)values[index]);
}
}
for each_map_ptr_sorted(profiler, const char *, key, struct profile_t, val ) {
if( isnan(val->stat) ) {
float v = val->avg/1000.0;
ui_slider2(*key, &v, va("%.2f ms", val->avg/1000.0));
} else {
float v = val->stat;
ui_slider2(*key, &v, va("%.2f", val->stat));
val->stat = 0;
}
}
}
#endif
#if ENABLE_PROFILER
profiler_t profiler;
int profiler_enabled = 1;
void (profiler_init)() { map_init(profiler, less_str, hash_str); profiler_enabled &= !!profiler; }
int (profiler_enable)(bool on) { return profiler_enabled = on; }
void (ui_profiler)() {
// @todo: ui_plot()
double fps = window_fps();
profile_setstat("Render.num_fps", fps);
enum { COUNT = 300 };
static float values[COUNT] = {0}; static int offset = 0;
values[offset=(offset+1)%COUNT] = fps;
// draw fps-meter: 300 samples, [0..70] range each, 70px height plot ...
// ... unless filtering is enabled
if( !(ui_filter && ui_filter[0]) ) {
nk_layout_row_dynamic(ui_ctx, 70, 1);
int index = -1;
if( nk_chart_begin(ui_ctx, NK_CHART_LINES, COUNT, 0.f, 70.f) ) {
for( int i = 0; i < COUNT; ++i ) {
nk_flags res = nk_chart_push(ui_ctx, (float)values[i]);
if( res & NK_CHART_HOVERING ) index = i;
if( res & NK_CHART_CLICKED ) index = i;
}
nk_chart_end(ui_ctx);
}
// hightlight 60fps, 36fps and 12fps
struct nk_rect space; nk_layout_peek(&space, ui_ctx);
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
nk_stroke_line(canvas, space.x+0,space.y-60,space.x+space.w,space.y-60, 1.0, nk_rgba(0,255,0,128));
nk_stroke_line(canvas, space.x+0,space.y-36,space.x+space.w,space.y-36, 1.0, nk_rgba(255,255,0,128));
nk_stroke_line(canvas, space.x+0,space.y-12,space.x+space.w,space.y-12, 1.0, nk_rgba(255,0,0,128));
if( index >= 0 ) {
nk_tooltipf(ui_ctx, "%.2f fps", (float)values[index]);
}
}
for each_map_ptr_sorted(profiler, const char *, key, struct profile_t, val ) {
if( isnan(val->stat) ) {
float v = val->avg/1000.0;
ui_slider2(*key, &v, va("%.2f ms", val->avg/1000.0));
} else {
float v = val->stat;
ui_slider2(*key, &v, va("%.2f", val->stat));
val->stat = 0;
}
}
}
#endif

View File

@ -1,29 +1,29 @@
// -----------------------------------------------------------------------------
// profiler & stats (@fixme: threadsafe)
#if !ENABLE_PROFILER
# define profile(section) for(int macro(i) = 1; macro(i); macro(i) = 0)
# define profile_incstat(name, accum) do {} while(0)
# define profile_setstat(name, value) do {} while(0)
# define profiler_init() do {} while(0)
# define profiler_enable(x) 0
# define ui_profiler() do {} while(0)
#else
# define profile(section) for( \
struct profile_t *found = profiler_enabled ? \
map_find_or_add(profiler, section "@" FILELINE, (struct profile_t){NAN} ) : NULL, \
*doit = found + ( found ? found->cost = -time_us(), 1 : 1 ); doit; \
doit = found ? found->cost += time_us(), found->avg = found->cost * 0.25 + found->avg * 0.75, NULL : NULL) ///+
# define profile_incstat(name, accum) for( \
struct profile_t *found = profiler_enabled ? map_find_or_add(profiler, name, (struct profile_t){0}) : NULL; \
found; found->stat += accum, found = NULL) ///+
# define profile_setstat(name, value) for( \
struct profile_t *found = profiler_enabled ? map_find_or_add(profiler, name, (struct profile_t){0}) : NULL; \
found; found->stat = value, found = NULL) ///+
API int profiler_enable(bool on);
struct profile_t { double stat; int32_t cost, avg; }; ///-
typedef map(char *, struct profile_t) profiler_t; ///-
extern API profiler_t profiler; ///-
extern API int profiler_enabled; ///-
#endif
// -----------------------------------------------------------------------------
// profiler & stats (@fixme: threadsafe)
#if !ENABLE_PROFILER
# define profile(section) for(int macro(i) = 1; macro(i); macro(i) = 0)
# define profile_incstat(name, accum) do {} while(0)
# define profile_setstat(name, value) do {} while(0)
# define profiler_init() do {} while(0)
# define profiler_enable(x) 0
# define ui_profiler() do {} while(0)
#else
# define profile(section) for( \
struct profile_t *found = profiler_enabled ? \
map_find_or_add(profiler, section "@" FILELINE, (struct profile_t){NAN} ) : NULL, \
*doit = found + ( found ? found->cost = -time_us(), 1 : 1 ); doit; \
doit = found ? found->cost += time_us(), found->avg = found->cost * 0.25 + found->avg * 0.75, NULL : NULL) ///+
# define profile_incstat(name, accum) for( \
struct profile_t *found = profiler_enabled ? map_find_or_add(profiler, name, (struct profile_t){0}) : NULL; \
found; found->stat += accum, found = NULL) ///+
# define profile_setstat(name, value) for( \
struct profile_t *found = profiler_enabled ? map_find_or_add(profiler, name, (struct profile_t){0}) : NULL; \
found; found->stat = value, found = NULL) ///+
API int profiler_enable(bool on);
struct profile_t { double stat; int32_t cost, avg; }; ///-
typedef map(char *, struct profile_t) profiler_t; ///-
extern API profiler_t profiler; ///-
extern API int profiler_enabled; ///-
#endif

View File

@ -1,191 +1,191 @@
// C reflection: enums, functions, structs, members and anotations.
// - rlyeh, public domain
//
// @todo: nested structs? pointers in members?
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
static map(unsigned, reflect_t) reflects;
static map(unsigned, array(reflect_t)) members;
void reflect_init() {
if(!reflects) map_init_int(reflects);
if(!members) map_init_int(members);
}
AUTORUN {
reflect_init();
}
const char* symbol_naked(const char *s) {
if( strbeg(s, "const ") ) s += 6;
if( strbeg(s, "union ") ) s += 6;
if( strbeg(s, "struct ") ) s += 7;
if(!strstr(s, " *") ) return s;
char *copy = va("%s", s);
do strswap(copy," *","*"); while( strstr(copy, " *") ); // char * -> char*
return (const char*)copy;
}
void type_inscribe(const char *TY,unsigned TYsz,const char *infos) {
reflect_init();
unsigned TYid = intern(TY = symbol_naked(TY));
map_find_or_add(reflects, TYid, ((reflect_t){TYid, 0, TYsz, STRDUP(TY), infos})); // @leak
}
void enum_inscribe(const char *E,unsigned Eval,const char *infos) {
reflect_init();
unsigned Eid = intern(E = symbol_naked(E));
map_find_or_add(reflects, Eid, ((reflect_t){Eid,0, Eval, STRDUP(E),infos})); // @leak
}
unsigned enum_find(const char *E) {
reflect_init();
E = symbol_naked(E);
return map_find(reflects, intern(E))->sz;
}
void function_inscribe(const char *F,void *func,const char *infos) {
reflect_init();
unsigned Fid = intern(F = symbol_naked(F));
map_find_or_add(reflects, Fid, ((reflect_t){Fid,0, 0, STRDUP(F),infos, func})); // @leak
reflect_t *found = map_find(reflects,Fid);
}
void *function_find(const char *F) {
reflect_init();
F = symbol_naked(F);
return map_find(reflects, intern(F))->addr;
}
void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
reflect_init();
unsigned Tid = intern(T = symbol_naked(T));
map_find_or_add(reflects, Tid, ((reflect_t){Tid, OBJTYPEid, Tsz, STRDUP(T), infos})); // @leak
}
void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *TYPE, unsigned bytes) {
reflect_init();
unsigned Tid = intern(T = symbol_naked(T));
unsigned Mid = intern(M = symbol_naked(M));
unsigned Xid = intern(TYPE = symbol_naked(TYPE));
map_find_or_add(reflects, (Mid<<16)|Tid, ((reflect_t){Mid, 0, Msz, STRDUP(M), infos, NULL, Tid, STRDUP(TYPE) })); // @leak
// add member separately as well
if(!members) map_init_int(members);
array(reflect_t) *found = map_find_or_add(members, Tid, 0);
reflect_t data = {Mid, 0, Msz, STRDUP(M), infos, NULL, Tid, STRDUP(TYPE), bytes }; // @leak
// ensure member has not been added previously
#if 1
// works, without altering member order
reflect_t *index = 0;
for(int i = 0, end = array_count(*found); i < end; ++i) {
if( (*found)[i].id == Mid ) { index = (*found)+i; break; }
}
if( index ) *index = data; else array_push(*found, data);
#else
// works, although members get sorted
array_push(*found, data);
array_sort(*found, less_unsigned_ptr); //< first member type in reflect_t is `unsigned id`, so less_unsigned_ptr works
array_unique(*found, less_unsigned_ptr); //< first member type in reflect_t is `unsigned id`, so less_unsigned_ptr works
#endif
}
reflect_t member_find(const char *T, const char *M) {
reflect_init();
T = symbol_naked(T);
M = symbol_naked(M);
return *map_find(reflects, (intern(M)<<16)|intern(T));
}
void *member_findptr(void *obj, const char *T, const char *M) {
reflect_init();
T = symbol_naked(T);
M = symbol_naked(M);
return (char*)obj + member_find(T,M).sz;
}
array(reflect_t)* members_find(const char *T) {
reflect_init();
T = symbol_naked(T);
return map_find(members, intern(T));
}
static
void ui_reflect_(const reflect_t *R, const char *filter, int mask) {
// debug:
// ui_label(va("name:%s info:'%s' id:%u objtype:%u sz:%u addr:%p parent:%u type:%s\n",
// R->name ? R->name : "", R->info ? R->info : "", R->id, R->objtype, R->sz, R->addr, R->parent, R->type ? R->type : ""));
if( mask == *R->info ) {
static __thread char *buf = 0;
if( buf ) *buf = '\0';
struct nk_context *ui_ctx = (struct nk_context *)ui_handle();
for ui_push_hspace(16) {
array(reflect_t) *T = map_find(members, intern(R->name));
/**/ if( T ) {ui_label(strcatf(&buf,"S struct %s@%s", R->name, R->info+1));
for each_array_ptr(*T, reflect_t, it)
if(strmatchi(it->name,filter)) {
if( !R->type && !strcmp(it->name,R->name) ) // avoid recursion
ui_label(strcatf(&buf,"M %s %s@%s", it->type, it->name, it->info+1));
else
ui_reflect_(it,filter,'M');
}
}
else if( R->addr ) ui_label(strcatf(&buf,"F func %s()@%s", R->name, R->info+1));
else if( !R->parent ) ui_label(strcatf(&buf,"E enum %s = %d@%s", R->name, R->sz, R->info+1));
else ui_label(strcatf(&buf,"M %s %s@%s", R->type, R->name, R->info+1));
}
}
}
API void *ui_handle();
int ui_reflect(const char *filter) {
if( !filter ) filter = "*";
int enabled = ui_enabled();
ui_disable();
// ENUMS, then FUNCTIONS, then STRUCTS
unsigned masks[] = { 'E', 'F', 'S' };
for( int i = 0; i < countof(masks); ++i )
for each_map_ptr(reflects, unsigned, k, reflect_t, R) {
if( strmatchi(R->name, filter)) {
ui_reflect_(R, filter, masks[i]);
}
}
if( enabled ) ui_enable();
return 0;
}
// -- tests
// type0 is reserved (no type)
// type1 reserved for objs
// type2 reserved for entities
// @todo: type3 and 4 likely reserved for components and systems??
// enum { OBJTYPE_vec3 = 0x03 };
AUTOTEST {
// register structs, enums and functions. with and without comments+tags
STRUCT( vec3, float, x );
STRUCT( vec3, float, y );
STRUCT( vec3, float, z, "Up" );
ENUM( IMAGE_RGB );
ENUM( TEXTURE_RGB, "3-channel Red+Green+Blue texture flag" );
ENUM( TEXTURE_RGBA, "4-channel Red+Green+Blue+Alpha texture flag" );
FUNCTION( puts );
FUNCTION( printf, "function that prints formatted text to stdout" );
// verify some reflected infos
test( function_find("puts") == puts );
test( function_find("printf") == printf );
test( enum_find("TEXTURE_RGB") == TEXTURE_RGB );
test( enum_find("TEXTURE_RGBA") == TEXTURE_RGBA );
// iterate reflected struct
for each_member("vec3", R) {
//printf("+%s vec3.%s (+%x) // %s\n", R->type, R->name, R->member_offset, R->info);
}
//reflect_print("puts");
//reflect_print("TEXTURE_RGBA");
//reflect_print("vec3");
//reflect_dump("*");
}
// C reflection: enums, functions, structs, members and anotations.
// - rlyeh, public domain
//
// @todo: nested structs? pointers in members?
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
static map(unsigned, reflect_t) reflects;
static map(unsigned, array(reflect_t)) members;
void reflect_init() {
if(!reflects) map_init_int(reflects);
if(!members) map_init_int(members);
}
AUTORUN {
reflect_init();
}
const char* symbol_naked(const char *s) {
if( strbeg(s, "const ") ) s += 6;
if( strbeg(s, "union ") ) s += 6;
if( strbeg(s, "struct ") ) s += 7;
if(!strstr(s, " *") ) return s;
char *copy = va("%s", s);
do strswap(copy," *","*"); while( strstr(copy, " *") ); // char * -> char*
return (const char*)copy;
}
void type_inscribe(const char *TY,unsigned TYsz,const char *infos) {
reflect_init();
unsigned TYid = intern(TY = symbol_naked(TY));
map_find_or_add(reflects, TYid, ((reflect_t){TYid, 0, TYsz, STRDUP(TY), infos})); // @leak
}
void enum_inscribe(const char *E,unsigned Eval,const char *infos) {
reflect_init();
unsigned Eid = intern(E = symbol_naked(E));
map_find_or_add(reflects, Eid, ((reflect_t){Eid,0, Eval, STRDUP(E),infos})); // @leak
}
unsigned enum_find(const char *E) {
reflect_init();
E = symbol_naked(E);
return map_find(reflects, intern(E))->sz;
}
void function_inscribe(const char *F,void *func,const char *infos) {
reflect_init();
unsigned Fid = intern(F = symbol_naked(F));
map_find_or_add(reflects, Fid, ((reflect_t){Fid,0, 0, STRDUP(F),infos, func})); // @leak
reflect_t *found = map_find(reflects,Fid);
}
void *function_find(const char *F) {
reflect_init();
F = symbol_naked(F);
return map_find(reflects, intern(F))->addr;
}
void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
reflect_init();
unsigned Tid = intern(T = symbol_naked(T));
map_find_or_add(reflects, Tid, ((reflect_t){Tid, OBJTYPEid, Tsz, STRDUP(T), infos})); // @leak
}
void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *TYPE, unsigned bytes) {
reflect_init();
unsigned Tid = intern(T = symbol_naked(T));
unsigned Mid = intern(M = symbol_naked(M));
unsigned Xid = intern(TYPE = symbol_naked(TYPE));
map_find_or_add(reflects, (Mid<<16)|Tid, ((reflect_t){Mid, 0, Msz, STRDUP(M), infos, NULL, Tid, STRDUP(TYPE) })); // @leak
// add member separately as well
if(!members) map_init_int(members);
array(reflect_t) *found = map_find_or_add(members, Tid, 0);
reflect_t data = {Mid, 0, Msz, STRDUP(M), infos, NULL, Tid, STRDUP(TYPE), bytes }; // @leak
// ensure member has not been added previously
#if 1
// works, without altering member order
reflect_t *index = 0;
for(int i = 0, end = array_count(*found); i < end; ++i) {
if( (*found)[i].id == Mid ) { index = (*found)+i; break; }
}
if( index ) *index = data; else array_push(*found, data);
#else
// works, although members get sorted
array_push(*found, data);
array_sort(*found, less_unsigned_ptr); //< first member type in reflect_t is `unsigned id`, so less_unsigned_ptr works
array_unique(*found, less_unsigned_ptr); //< first member type in reflect_t is `unsigned id`, so less_unsigned_ptr works
#endif
}
reflect_t member_find(const char *T, const char *M) {
reflect_init();
T = symbol_naked(T);
M = symbol_naked(M);
return *map_find(reflects, (intern(M)<<16)|intern(T));
}
void *member_findptr(void *obj, const char *T, const char *M) {
reflect_init();
T = symbol_naked(T);
M = symbol_naked(M);
return (char*)obj + member_find(T,M).sz;
}
array(reflect_t)* members_find(const char *T) {
reflect_init();
T = symbol_naked(T);
return map_find(members, intern(T));
}
static
void ui_reflect_(const reflect_t *R, const char *filter, int mask) {
// debug:
// ui_label(va("name:%s info:'%s' id:%u objtype:%u sz:%u addr:%p parent:%u type:%s\n",
// R->name ? R->name : "", R->info ? R->info : "", R->id, R->objtype, R->sz, R->addr, R->parent, R->type ? R->type : ""));
if( mask == *R->info ) {
static __thread char *buf = 0;
if( buf ) *buf = '\0';
struct nk_context *ui_ctx = (struct nk_context *)ui_handle();
for ui_push_hspace(16) {
array(reflect_t) *T = map_find(members, intern(R->name));
/**/ if( T ) {ui_label(strcatf(&buf,"S struct %s@%s", R->name, R->info+1));
for each_array_ptr(*T, reflect_t, it)
if(strmatchi(it->name,filter)) {
if( !R->type && !strcmp(it->name,R->name) ) // avoid recursion
ui_label(strcatf(&buf,"M %s %s@%s", it->type, it->name, it->info+1));
else
ui_reflect_(it,filter,'M');
}
}
else if( R->addr ) ui_label(strcatf(&buf,"F func %s()@%s", R->name, R->info+1));
else if( !R->parent ) ui_label(strcatf(&buf,"E enum %s = %d@%s", R->name, R->sz, R->info+1));
else ui_label(strcatf(&buf,"M %s %s@%s", R->type, R->name, R->info+1));
}
}
}
API void *ui_handle();
int ui_reflect(const char *filter) {
if( !filter ) filter = "*";
int enabled = ui_enabled();
ui_disable();
// ENUMS, then FUNCTIONS, then STRUCTS
unsigned masks[] = { 'E', 'F', 'S' };
for( int i = 0; i < countof(masks); ++i )
for each_map_ptr(reflects, unsigned, k, reflect_t, R) {
if( strmatchi(R->name, filter)) {
ui_reflect_(R, filter, masks[i]);
}
}
if( enabled ) ui_enable();
return 0;
}
// -- tests
// type0 is reserved (no type)
// type1 reserved for objs
// type2 reserved for entities
// @todo: type3 and 4 likely reserved for components and systems??
// enum { OBJTYPE_vec3 = 0x03 };
AUTOTEST {
// register structs, enums and functions. with and without comments+tags
STRUCT( vec3, float, x );
STRUCT( vec3, float, y );
STRUCT( vec3, float, z, "Up" );
ENUM( IMAGE_RGB );
ENUM( TEXTURE_RGB, "3-channel Red+Green+Blue texture flag" );
ENUM( TEXTURE_RGBA, "4-channel Red+Green+Blue+Alpha texture flag" );
FUNCTION( puts );
FUNCTION( printf, "function that prints formatted text to stdout" );
// verify some reflected infos
test( function_find("puts") == puts );
test( function_find("printf") == printf );
test( enum_find("TEXTURE_RGB") == TEXTURE_RGB );
test( enum_find("TEXTURE_RGBA") == TEXTURE_RGBA );
// iterate reflected struct
for each_member("vec3", R) {
//printf("+%s vec3.%s (+%x) // %s\n", R->type, R->name, R->member_offset, R->info);
}
//reflect_print("puts");
//reflect_print("TEXTURE_RGBA");
//reflect_print("vec3");
//reflect_dump("*");
}

View File

@ -1,64 +1,64 @@
// C reflection: enums, functions, structs, members and anotations.
// - rlyeh, public domain
//
// @todo: nested structs? pointers in members?
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
#ifndef OBJTYPE
#define OBJTYPE(T) 0
#endif
typedef struct reflect_t {
unsigned id, objtype;
union {
unsigned sz;
unsigned member_offset;
unsigned enum_value;
};
const char *name;
const char *info;
void *addr;
unsigned parent;
const char *type;
unsigned bytes;
} reflect_t;
// inscribe api
#define ENUM(V, ...) \
enum_inscribe(#V,V, "E" __VA_ARGS__ " ("FILELINE")")
#define FUNCTION(F, ...) \
function_inscribe(#F,(void*)F, "F" __VA_ARGS__ " ("FILELINE")")
#define STRUCT(T, type, member, ...) \
struct_inscribe(#T,sizeof(T),OBJTYPE(T),"S" " ("FILELINE")"), \
type_inscribe(#type,sizeof(((T){0}).member),"T" __VA_ARGS__ " ("FILELINE")"), \
member_inscribe(#T, #member,(uintptr_t)&((T*)0)->member, "M" __VA_ARGS__ " ("FILELINE")", #type, sizeof(((T){0}).member) )
// find api
API unsigned enum_find(const char *E);
API void * function_find(const char *F);
API reflect_t member_find(const char *T, const char *M); /// find specific member
API void * member_findptr(void *obj, const char *T, const char *M); // @deprecate
API array(reflect_t)* members_find(const char *T);
// iterate members in a struct
#define each_member(T,R) \
(array(reflect_t) *found_ = members_find(T); found_; found_ = 0) \
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
for(reflect_t *R = &(*found_)[it_]; R; R = 0 )
// private api, still exposed
API void type_inscribe(const char *TY,unsigned TYsz,const char *infos);
API void enum_inscribe(const char *E,unsigned Eval,const char *infos);
API void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
API void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *type, unsigned bytes);
API void function_inscribe(const char *F,void *func,const char *infos);
API const char* symbol_naked(const char *s);
API int ui_reflect(const char *mask); // *, model* or NULL
// C reflection: enums, functions, structs, members and anotations.
// - rlyeh, public domain
//
// @todo: nested structs? pointers in members?
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
#ifndef OBJTYPE
#define OBJTYPE(T) 0
#endif
typedef struct reflect_t {
unsigned id, objtype;
union {
unsigned sz;
unsigned member_offset;
unsigned enum_value;
};
const char *name;
const char *info;
void *addr;
unsigned parent;
const char *type;
unsigned bytes;
} reflect_t;
// inscribe api
#define ENUM(V, ...) \
enum_inscribe(#V,V, "E" __VA_ARGS__ " ("FILELINE")")
#define FUNCTION(F, ...) \
function_inscribe(#F,(void*)F, "F" __VA_ARGS__ " ("FILELINE")")
#define STRUCT(T, type, member, ...) \
struct_inscribe(#T,sizeof(T),OBJTYPE(T),"S" " ("FILELINE")"), \
type_inscribe(#type,sizeof(((T){0}).member),"T" __VA_ARGS__ " ("FILELINE")"), \
member_inscribe(#T, #member,(uintptr_t)&((T*)0)->member, "M" __VA_ARGS__ " ("FILELINE")", #type, sizeof(((T){0}).member) )
// find api
API unsigned enum_find(const char *E);
API void * function_find(const char *F);
API reflect_t member_find(const char *T, const char *M); /// find specific member
API void * member_findptr(void *obj, const char *T, const char *M); // @deprecate
API array(reflect_t)* members_find(const char *T);
// iterate members in a struct
#define each_member(T,R) \
(array(reflect_t) *found_ = members_find(T); found_; found_ = 0) \
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
for(reflect_t *R = &(*found_)[it_]; R; R = 0 )
// private api, still exposed
API void type_inscribe(const char *TY,unsigned TYsz,const char *infos);
API void enum_inscribe(const char *E,unsigned Eval,const char *infos);
API void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
API void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *type, unsigned bytes);
API void function_inscribe(const char *F,void *func,const char *infos);
API const char* symbol_naked(const char *s);
API int ui_reflect(const char *mask); // *, model* or NULL

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,71 +1,71 @@
// -----------------------------------------------------------------------------
// debugdraw framework
// - rlyeh, public domain.
//
// Credits: Based on work by @glampert https://github.com/glampert/debug-draw (PD)
// [x] grid, axis, frustum, cube, sphere, triangle, square, pentagon, hexagon, circle, normal.
// [x] arrow, point, text, capsule, aabb, plane, flotilla-style locator, boid, bone, ring
// [x] line batching
// [*] line width and stipple
// [*] (proper) gizmo,
// [ ] camera, light bulb, light probe,
API void ddraw_line_width(float width);
API void ddraw_color(unsigned rgb);
API void ddraw_color_push(unsigned rgb);
API void ddraw_color_pop();
//
API void ddraw_ontop(int enabled);
API void ddraw_ontop_push(int enabled);
API void ddraw_ontop_pop();
//
API void ddraw_push_2d();
API void ddraw_pop_2d();
//
API void ddraw_aabb(vec3 minbb, vec3 maxbb);
API void ddraw_aabb_corners(vec3 minbb, vec3 maxbb);
API void ddraw_arrow(vec3 begin, vec3 end);
API void ddraw_axis(float units);
API void ddraw_boid(vec3 pos, vec3 dir);
API void ddraw_bone(vec3 center, vec3 end); // @todo: use me
API void ddraw_bounds(const vec3 points[8]);
API void ddraw_box(vec3 center, vec3 extents);
API void ddraw_capsule(vec3 from, vec3 to, float radius);
API void ddraw_circle(vec3 pos, vec3 n, float radius);
API void ddraw_ring(vec3 pos, vec3 n, float radius);
API void ddraw_cone(vec3 center, vec3 top, float radius);
API void ddraw_cube(vec3 center, float radius);
API void ddraw_cube33(vec3 center, vec3 radius, mat33 M);
API void ddraw_diamond(vec3 from, vec3 to, float size);
API void ddraw_frustum(float projview[16]);
API void ddraw_ground(float scale);
API void ddraw_grid(float scale);
API void ddraw_hexagon(vec3 pos, float radius);
API void ddraw_line(vec3 from, vec3 to);
API void ddraw_line_dashed(vec3 from, vec3 to);
API void ddraw_line_thin(vec3 from, vec3 to);
API void ddraw_normal(vec3 pos, vec3 n);
API void ddraw_pentagon(vec3 pos, float radius);
API void ddraw_plane(vec3 p, vec3 n, float scale);
API void ddraw_point(vec3 from);
API void ddraw_position(vec3 pos, float radius);
API void ddraw_position_dir(vec3 pos, vec3 dir, float radius);
API void ddraw_pyramid(vec3 center, float height, int segments);
API void ddraw_cylinder(vec3 center, float height, int segments);
API void ddraw_sphere(vec3 pos, float radius);
API void ddraw_square(vec3 pos, float radius);
API void ddraw_text(vec3 pos, float scale, const char *text);
API void ddraw_text2d(vec2 pos, const char *text);
API void ddraw_triangle(vec3 p1, vec3 p2, vec3 p3);
//
API void ddraw_prism(vec3 center, float radius, float height, vec3 normal, int segments);
//
API void ddraw_demo();
API void ddraw_flush();
API void ddraw_flush_projview(mat44 proj, mat44 view);
// transform gizmos
API int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
API bool gizmo_active();
API bool gizmo_hover();
// -----------------------------------------------------------------------------
// debugdraw framework
// - rlyeh, public domain.
//
// Credits: Based on work by @glampert https://github.com/glampert/debug-draw (PD)
// [x] grid, axis, frustum, cube, sphere, triangle, square, pentagon, hexagon, circle, normal.
// [x] arrow, point, text, capsule, aabb, plane, flotilla-style locator, boid, bone, ring
// [x] line batching
// [*] line width and stipple
// [*] (proper) gizmo,
// [ ] camera, light bulb, light probe,
API void ddraw_line_width(float width);
API void ddraw_color(unsigned rgb);
API void ddraw_color_push(unsigned rgb);
API void ddraw_color_pop();
//
API void ddraw_ontop(int enabled);
API void ddraw_ontop_push(int enabled);
API void ddraw_ontop_pop();
//
API void ddraw_push_2d();
API void ddraw_pop_2d();
//
API void ddraw_aabb(vec3 minbb, vec3 maxbb);
API void ddraw_aabb_corners(vec3 minbb, vec3 maxbb);
API void ddraw_arrow(vec3 begin, vec3 end);
API void ddraw_axis(float units);
API void ddraw_boid(vec3 pos, vec3 dir);
API void ddraw_bone(vec3 center, vec3 end); // @todo: use me
API void ddraw_bounds(const vec3 points[8]);
API void ddraw_box(vec3 center, vec3 extents);
API void ddraw_capsule(vec3 from, vec3 to, float radius);
API void ddraw_circle(vec3 pos, vec3 n, float radius);
API void ddraw_ring(vec3 pos, vec3 n, float radius);
API void ddraw_cone(vec3 center, vec3 top, float radius);
API void ddraw_cube(vec3 center, float radius);
API void ddraw_cube33(vec3 center, vec3 radius, mat33 M);
API void ddraw_diamond(vec3 from, vec3 to, float size);
API void ddraw_frustum(float projview[16]);
API void ddraw_ground(float scale);
API void ddraw_grid(float scale);
API void ddraw_hexagon(vec3 pos, float radius);
API void ddraw_line(vec3 from, vec3 to);
API void ddraw_line_dashed(vec3 from, vec3 to);
API void ddraw_line_thin(vec3 from, vec3 to);
API void ddraw_normal(vec3 pos, vec3 n);
API void ddraw_pentagon(vec3 pos, float radius);
API void ddraw_plane(vec3 p, vec3 n, float scale);
API void ddraw_point(vec3 from);
API void ddraw_position(vec3 pos, float radius);
API void ddraw_position_dir(vec3 pos, vec3 dir, float radius);
API void ddraw_pyramid(vec3 center, float height, int segments);
API void ddraw_cylinder(vec3 center, float height, int segments);
API void ddraw_sphere(vec3 pos, float radius);
API void ddraw_square(vec3 pos, float radius);
API void ddraw_text(vec3 pos, float scale, const char *text);
API void ddraw_text2d(vec2 pos, const char *text);
API void ddraw_triangle(vec3 p1, vec3 p2, vec3 p3);
//
API void ddraw_prism(vec3 center, float radius, float height, vec3 normal, int segments);
//
API void ddraw_demo();
API void ddraw_flush();
API void ddraw_flush_projview(mat44 proj, mat44 view);
// transform gizmos
API int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
API bool gizmo_active();
API bool gizmo_hover();

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More