main
Dominik Madarász 2023-11-01 12:24:16 +01:00
parent 6aa3fc1d4a
commit fb515681b1
40 changed files with 3757 additions and 1859 deletions

View File

@ -959,9 +959,9 @@ ffi.cdef([[
//lcpp INF [0000] quat: macro name but used as C declaration in: void printq( quat q );
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 v;
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 result;
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void tween_keyframe_set(tween_t *tw, float t, int easing_mode, vec3 v);
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void tween_keyframe_set(tween_t *tw, float t, int easing_mode, vec3 v);
//lcpp INF [0000] vec3: macro name but used as C declaration in: void tween_keyframe_set(tween_t *tw, float t, int easing_mode, vec3 v);
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
//lcpp INF [0000] vec3: macro name but used as C declaration in: void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 position;
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 velocity;
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 acceleration;
@ -1529,11 +1529,12 @@ ffi.cdef([[
typedef struct FILE FILE;
typedef long int ptrdiff_t;
typedef long unsigned int size_t;
int sort_64(const void *a, const void *b);
int less_64(uint64_t a, uint64_t b);
int less_int(int a, int b);
int less_ptr(void *a, void *b);
int less_str(char *a, char *b);
int less_64_ptr(const void *a, const void *b);
int less_int_ptr(const void *a, const void *b);
uint32_t unhash_32(uint32_t x);
uint32_t hash_32(uint32_t x);
uint64_t hash_64(uint64_t x);
@ -1789,6 +1790,7 @@ typedef float mat44[16];
void print33( float *m );
void print34( float *m );
void print44( float *m );
float ease_nop(float t);
float ease_linear(float t);
float ease_out_sine(float t);
float ease_out_quad(float t);
@ -1822,7 +1824,6 @@ typedef float mat44[16];
float ease_inout_bounce(float t);
float ease_inout_perlin(float t);
enum EASE_FLAGS {
EASE_LINEAR,
EASE_SINE,
EASE_QUAD,
EASE_CUBIC,
@ -1834,8 +1835,12 @@ EASE_BACK,
EASE_ELASTIC,
EASE_BOUNCE,
EASE_IN,
EASE_INOUT = EASE_IN * 2,
EASE_OUT = 0,
EASE_INOUT = EASE_IN * 2,
EASE_NOP = EASE_INOUT | (EASE_BOUNCE + 1),
EASE_LINEAR,
EASE_INOUT_PERLIN,
EASE_NUM
};
float ease(float t01, unsigned fn);
float ease_pong(float t01, unsigned fn);
@ -1844,9 +1849,9 @@ EASE_OUT = 0,
const char *ease_enum(unsigned fn);
const char**ease_enums();
typedef struct tween_keyframe_t {
int easing_mode;
float t;
vec3 v;
unsigned ease;
} tween_keyframe_t;
typedef struct tween_t {
tween_keyframe_t* keyframes;
@ -1855,11 +1860,11 @@ float time;
float duration;
} tween_t;
tween_t tween();
void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
void tween_delkey(tween_t *tw, float t);
float tween_update(tween_t *tw, float dt);
void tween_reset(tween_t *tw);
void tween_destroy(tween_t *tw);
void tween_keyframe_set(tween_t *tw, float t, int easing_mode, vec3 v);
void tween_keyframe_unset(tween_t *tw, float t);
typedef enum SWARM_DISTANCE {
SWARM_DISTANCE_LINEAR,
SWARM_DISTANCE_INVERSE_LINEAR,
@ -2389,8 +2394,7 @@ TOUCH_1,
int ui_gamepad(int id);
int ui_gamepads();
enum INPUT_ENUMS {
KEY_ESC,
KEY_TICK, KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9,KEY_0, KEY_BS,
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,
@ -2511,6 +2515,7 @@ void* obj_free(void *o);
extern int (*obj_draw[256])();
extern int (*obj_lerp[256])();
extern int (*obj_edit[256])();
extern int (*obj_aabb[256])();
uintptr_t obj_header(const void *o);
uintptr_t obj_id(const void *o);
const char* obj_type(const void *o);

File diff suppressed because it is too large Load Diff

View File

@ -19810,7 +19810,7 @@ nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type pan
float t = font->width(font->userdata, font->height, title, text_len);
text.padding = nk_vec2(0,0);
label.x = header.x - style->window.header.padding.x; //< @r-lyeh: + -> -
label.x = header.x + (win->flags & NK_WINDOW_MINIMIZABLE ? -1 : 2) * style->window.header.padding.x; //< @r-lyeh: + -> - on minimizable
// label.x += style->window.header.label_padding.x; //< @r-lyeh: disabled
label.y = header.y + style->window.header.label_padding.y;
label.h = font->height + 2 * style->window.header.label_padding.y;

View File

@ -1,4 +1,4 @@
/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb
/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk
Do this:
@ -48,6 +48,8 @@ LICENSE
RECENT REVISION HISTORY:
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
2.26 (2020-07-13) many minor fixes
2.25 (2020-02-02) fix warnings
2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
@ -89,7 +91,7 @@ RECENT REVISION HISTORY:
Jeremy Sawicki (handle all ImageNet JPGs)
Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
Arseny Kapoulkine
Arseny Kapoulkine Simon Breuss (16-bit PNM)
John-Mark Allen
Carmelo J Fdez-Aguera
@ -102,19 +104,21 @@ RECENT REVISION HISTORY:
Thomas Ruf Ronny Chevalier github:rlyeh
Janez Zemva John Bartholomew Michal Cichon github:romigrou
Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
Laurent Gomila Cort Stratton github:snagar
Eugene Golushkov Laurent Gomila Cort Stratton github:snagar
Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex
Cass Everitt Ryamond Barbiero github:grim210
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
Josh Tobin Matthew Gregan github:poppolopoppo
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
Brad Weinberger Matvey Cherevko [reserved]
Brad Weinberger Matvey Cherevko github:mosra
Luca Sas Alexander Veselov Zack Middleton [reserved]
Ryan C. Gordon [reserved] [reserved]
DO NOT ADD YOUR NAME HERE
Jacko Dirks
To add your name to the credits, pick a random blank space in the middle and fill it.
80% of merge conflicts on stb PRs are due to people adding their name at the end
of the credits.
@ -137,7 +141,7 @@ RECENT REVISION HISTORY:
// // ... x = width, y = height, n = # 8-bit components per pixel ...
// // ... replace '0' with '1'..'4' to force that many components per pixel
// // ... but 'n' will always be the number that it would have been if you said 0
// stbi_image_free(data)
// stbi_image_free(data);
//
// Standard parameters:
// int *x -- outputs image width in pixels
@ -176,6 +180,32 @@ RECENT REVISION HISTORY:
//
// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
//
// To query the width, height and component count of an image without having to
// decode the full file, you can use the stbi_info family of functions:
//
// int x,y,n,ok;
// ok = stbi_info(filename, &x, &y, &n);
// // returns ok=1 and sets x, y, n if image is a supported format,
// // 0 otherwise.
//
// Note that stb_image pervasively uses ints in its public API for sizes,
// including sizes of memory buffers. This is now part of the API and thus
// hard to change without causing breakage. As a result, the various image
// loaders all have certain limits on image size; these differ somewhat
// by format but generally boil down to either just under 2GB or just under
// 1GB. When the decoded image would be larger than this, stb_image decoding
// will fail.
//
// Additionally, stb_image will reject image files that have any of their
// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,
// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,
// the only way to have an image with such dimensions load correctly
// is for it to have a rather extreme aspect ratio. Either way, the
// assumption here is that such larger images are likely to be malformed
// or malicious. If you do need to load an image with individual dimensions
// larger than that, and it still fits in the overall size limit, you can
// #define STBI_MAX_DIMENSIONS on your own to be something larger.
//
// ===========================================================================
//
// UNICODE:
@ -281,11 +311,10 @@ RECENT REVISION HISTORY:
//
// iPhone PNG support:
//
// By default we convert iphone-formatted PNGs back to RGB, even though
// they are internally encoded differently. You can disable this conversion
// by calling stbi_convert_iphone_png_to_rgb(0), in which case
// you will always just get the native iphone "format" through (which
// is BGR stored in RGB).
// We optionally support converting iPhone-formatted PNGs (which store
// premultiplied BGRA) back to RGB, even though they're internally encoded
// differently. To enable this conversion, call
// stbi_convert_iphone_png_to_rgb(1).
//
// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
// pixel to remove any premultiplied alpha *only* if the image file explicitly
@ -489,6 +518,8 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
// as above, but only applies to images loaded on the thread that calls the function
// this function is only available if your compiler supports thread-local variables;
// calling it will fail to link if your compiler doesn't
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
// ZLIB client - used by PNG, available for other purposes
@ -605,7 +636,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch
#endif
#endif
#ifdef _MSC_VER
#if defined(_MSC_VER) || defined(__SYMBIAN32__)
typedef unsigned short stbi__uint16;
typedef signed short stbi__int16;
typedef unsigned int stbi__uint32;
@ -634,7 +665,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#ifdef STBI_HAS_LROTL
#define stbi_lrot(x,y) _lrotl(x,y)
#else
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31)))
#endif
#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
@ -748,9 +779,12 @@ static int stbi__sse2_available(void)
#ifdef STBI_NEON
#include <arm_neon.h>
// assume GCC or Clang on ARM targets
#ifdef _MSC_VER
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
#else
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif
#endif
#ifndef STBI_SIMD_ALIGN
#define STBI_SIMD_ALIGN(type, name) type name
@ -924,6 +958,7 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__pnm_test(stbi__context *s);
static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__pnm_is16(stbi__context *s);
#endif
static
@ -998,7 +1033,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add)
}
// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
{
return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
@ -1021,7 +1056,7 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add)
return stbi__malloc(a*b*c + add);
}
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
{
if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
@ -1029,6 +1064,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
}
#endif
// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
static int stbi__addints_valid(int a, int b)
{
if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow
if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
return a <= INT_MAX - b;
}
// returns 1 if the product of two signed shorts is valid, 0 on overflow.
static int stbi__mul2shorts_valid(short a, short b)
{
if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
return a >= SHRT_MIN / b;
}
// stbi__err - error
// stbi__errpf - error returning pointer to float
// stbi__errpuc - error returning pointer to unsigned char
@ -1087,9 +1139,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
ri->num_channels = 0;
#ifndef STBI_NO_JPEG
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
#endif
// test the formats with a very explicit header first (at least a FOURCC
// or distinctive magic number first)
#ifndef STBI_NO_PNG
if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
#endif
@ -1107,6 +1158,13 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
#ifndef STBI_NO_PIC
if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
#endif
// then the formats that can end up attempting to load with just 1 or 2
// bytes matching expectations; these are prone to false positives, so
// try them later
#ifndef STBI_NO_JPEG
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_PNM
if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri);
#endif
@ -1262,12 +1320,12 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in
#ifndef STBI_NO_STDIO
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
#endif
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
@ -1277,16 +1335,16 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch
static FILE *stbi__fopen(char const *filename, char const *mode)
{
FILE *f;
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
wchar_t wMode[64];
wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
return 0;
#if _MSC_VER >= 1400
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0;
#else
@ -1662,7 +1720,8 @@ static int stbi__get16le(stbi__context *s)
static stbi__uint32 stbi__get32le(stbi__context *s)
{
stbi__uint32 z = stbi__get16le(s);
return z + (stbi__get16le(s) << 16);
z += (stbi__uint32)stbi__get16le(s) << 16;
return z;
}
#endif
@ -1944,9 +2003,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count)
int i,j,k=0;
unsigned int code;
// build size list for each symbol (from JPEG spec)
for (i=0; i < 16; ++i)
for (j=0; j < count[i]; ++j)
for (i=0; i < 16; ++i) {
for (j=0; j < count[i]; ++j) {
h->size[k++] = (stbi_uc) (i+1);
if(k >= 257) return stbi__err("bad size list","Corrupt JPEG");
}
}
h->size[k] = 0;
// compute actual symbols (from jpeg spec)
@ -2071,6 +2133,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
// convert the huffman code to the symbol id
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
if(c < 0 || c >= 256) // symbol id out of bounds!
return -1;
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
// convert the id to a symbol
@ -2089,14 +2153,14 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
unsigned int k;
int sgn;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
k = stbi_lrot(j->code_buffer, n);
if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0;
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
j->code_bits -= n;
return k + (stbi__jbias[n] & ~sgn);
return k + (stbi__jbias[n] & (sgn - 1));
}
// get some unsigned bits
@ -2104,6 +2168,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
{
unsigned int k;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
k = stbi_lrot(j->code_buffer, n);
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
@ -2115,6 +2180,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
{
unsigned int k;
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing
k = j->code_buffer;
j->code_buffer <<= 1;
--j->code_bits;
@ -2146,14 +2212,16 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
t = stbi__jpeg_huff_decode(j, hdc);
if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG");
if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG");
// 0 all the ac values now so we can do it 32-bits at a time
memset(data,0,64*sizeof(data[0]));
diff = t ? stbi__extend_receive(j, t) : 0;
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * dequant[0]);
// decode AC components, see JPEG spec
@ -2167,6 +2235,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
j->code_buffer <<= s;
j->code_bits -= s;
// decode into unzigzag'd location
@ -2203,12 +2272,14 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
// first scan for DC coefficient, must be first
memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
t = stbi__jpeg_huff_decode(j, hdc);
if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
diff = t ? stbi__extend_receive(j, t) : 0;
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
data[0] = (short) (dc << j->succ_low);
if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * (1 << j->succ_low));
} else {
// refinement scan for DC coefficient
if (stbi__jpeg_get_bit(j))
@ -2242,10 +2313,11 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
j->code_buffer <<= s;
j->code_bits -= s;
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) ((r >> 8) << shift);
data[zig] = (short) ((r >> 8) * (1 << shift));
} else {
int rs = stbi__jpeg_huff_decode(j, hac);
if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
@ -2263,7 +2335,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
} else {
k += r;
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) (stbi__extend_receive(j,s) << shift);
data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift));
}
}
} while (k <= j->spec_end);
@ -3062,6 +3134,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
sizes[i] = stbi__get8(z->s);
n += sizes[i];
}
if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values!
L -= 17;
if (tc == 0) {
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
@ -3227,6 +3300,13 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
}
// check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios
// and I've never seen a non-corrupted JPEG file actually use them
for (i=0; i < s->img_n; ++i) {
if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG");
if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG");
}
// compute interleaved mcu info
z->img_h_max = h_max;
z->img_v_max = v_max;
@ -3304,6 +3384,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
return 1;
}
static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
{
// some JPEGs have junk at end, skip over it but if we find what looks
// like a valid marker, resume there
while (!stbi__at_eof(j->s)) {
int x = stbi__get8(j->s);
while (x == 255) { // might be a marker
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
x = stbi__get8(j->s);
if (x != 0x00 && x != 0xff) {
// not a stuffed zero or lead-in to another marker, looks
// like an actual marker, return it
return x;
}
// stuffed zero has x=0 now which ends the loop, meaning we go
// back to regular scan loop.
// repeated 0xff keeps trying to read the next byte of the marker.
}
}
return STBI__MARKER_none;
}
// decode image to YCbCr format
static int stbi__decode_jpeg_image(stbi__jpeg *j)
{
@ -3320,26 +3422,23 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j)
if (!stbi__process_scan_header(j)) return 0;
if (!stbi__parse_entropy_coded_data(j)) return 0;
if (j->marker == STBI__MARKER_none ) {
// handle 0s at the end of image data from IP Kamera 9060
while (!stbi__at_eof(j->s)) {
int x = stbi__get8(j->s);
if (x == 255) {
j->marker = stbi__get8(j->s);
break;
}
}
j->marker = stbi__skip_jpeg_junk_at_end(j);
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
}
m = stbi__get_marker(j);
if (STBI__RESTART(m))
m = stbi__get_marker(j);
} else if (stbi__DNL(m)) {
int Ld = stbi__get16be(j->s);
stbi__uint32 NL = stbi__get16be(j->s);
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
m = stbi__get_marker(j);
} else {
if (!stbi__process_marker(j, m)) return 0;
}
if (!stbi__process_marker(j, m)) return 1;
m = stbi__get_marker(j);
}
}
if (j->progressive)
stbi__jpeg_finish(j);
return 1;
@ -3782,6 +3881,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
else
decode_n = z->s->img_n;
// nothing to do if no components requested; check this now to avoid
// accessing uninitialized coutput[0] later
if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; }
// resample and color-convert
{
int k;
@ -3924,6 +4027,8 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
{
unsigned char* result;
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__errpuc("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
STBI_NOTUSED(ri);
j->s = s;
stbi__setup_jpeg(j);
@ -3936,6 +4041,8 @@ static int stbi__jpeg_test(stbi__context *s)
{
int r;
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__err("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
j->s = s;
stbi__setup_jpeg(j);
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
@ -3960,6 +4067,8 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
{
int result;
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
if (!j) return stbi__err("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
j->s = s;
result = stbi__jpeg_info_raw(j, x, y, comp);
STBI_FREE(j);
@ -3979,6 +4088,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
// fast-way is faster to check than jpeg huffman, but slow way is slower
#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet
// zlib-style huffman encoding
// (jpegs packs from left, zlib from right, so can't share code)
@ -3988,8 +4098,8 @@ typedef struct
stbi__uint16 firstcode[16];
int maxcode[17];
stbi__uint16 firstsymbol[16];
stbi_uc size[288];
stbi__uint16 value[288];
stbi_uc size[STBI__ZNSYMS];
stbi__uint16 value[STBI__ZNSYMS];
} stbi__zhuffman;
stbi_inline static int stbi__bitreverse16(int n)
@ -4120,7 +4230,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
if (s >= 16) return -1; // invalid code!
// code size is s, so:
b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere!
if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere!
if (z->size[b] != s) return -1; // was originally an assert, but report failure instead.
a->code_buffer >>= s;
a->num_bits -= s;
@ -4201,11 +4311,12 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
a->zout = zout;
return 1;
}
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
z -= 257;
len = stbi__zlength_base[z];
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
z = stbi__zhuffman_decode(a, &a->z_distance);
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
dist = stbi__zdist_base[z];
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
@ -4317,7 +4428,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a)
return 1;
}
static const stbi_uc stbi__zdefault_length[288] =
static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] =
{
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
@ -4363,7 +4474,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
} else {
if (type == 1) {
// use fixed code lengths
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0;
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0;
if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
} else {
if (!stbi__compute_huffman_codes(a)) return 0;
@ -4759,6 +4870,7 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3
// de-interlacing
final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
if (!final) return stbi__err("outofmem", "Out of memory");
for (p=0; p < 7; ++p) {
int xorig[] = { 0,4,0,2,0,1,0 };
int yorig[] = { 0,0,4,0,2,0,1 };
@ -4879,19 +4991,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int
return 1;
}
static int stbi__unpremultiply_on_load = 0;
static int stbi__de_iphone_flag = 0;
static int stbi__unpremultiply_on_load_global = 0;
static int stbi__de_iphone_flag_global = 0;
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;
}
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
{
stbi__de_iphone_flag = flag_true_if_should_convert;
stbi__de_iphone_flag_global = flag_true_if_should_convert;
}
#ifndef STBI_THREAD_LOCAL
#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global
#define stbi__de_iphone_flag stbi__de_iphone_flag_global
#else
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_set = 1;
}
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)
{
stbi__de_iphone_flag_local = flag_true_if_should_convert;
stbi__de_iphone_flag_set = 1;
}
#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \
? stbi__unpremultiply_on_load_local \
: stbi__unpremultiply_on_load_global)
#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \
? stbi__de_iphone_flag_local \
: stbi__de_iphone_flag_global)
#endif // STBI_THREAD_LOCAL
static void stbi__de_iphone(stbi__png *z)
{
stbi__context *s = z->s;
@ -4981,14 +5120,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!pal_img_n) {
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
if (scan == STBI__SCAN_header) return 1;
} else {
// if paletted, then pal_n is our final components, and
// img_n is # components to decompress/filter.
s->img_n = 1;
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
// if SCAN_header, have to scan to see if we have a tRNS
}
// even with SCAN_header, have to scan to see if we have a tRNS
break;
}
@ -5020,6 +5158,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
has_trans = 1;
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
if (z->depth == 16) {
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
} else {
@ -5032,7 +5172,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
case STBI__PNG_TYPE('I','D','A','T'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
if (scan == STBI__SCAN_header) {
// header scan definitely stops at first IDAT
if (pal_img_n)
s->img_n = pal_img_n;
return 1;
}
if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
if ((int)(ioff + c.length) < (int)ioff) return 0;
if (ioff + c.length > idata_limit) {
stbi__uint32 idata_limit_old = idata_limit;
@ -5272,6 +5418,32 @@ typedef struct
int extra_read;
} stbi__bmp_data;
static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress)
{
// BI_BITFIELDS specifies masks explicitly, don't override
if (compress == 3)
return 1;
if (compress == 0) {
if (info->bpp == 16) {
info->mr = 31u << 10;
info->mg = 31u << 5;
info->mb = 31u << 0;
} else if (info->bpp == 32) {
info->mr = 0xffu << 16;
info->mg = 0xffu << 8;
info->mb = 0xffu << 0;
info->ma = 0xffu << 24;
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
} else {
// otherwise, use defaults, which is all-0
info->mr = info->mg = info->mb = info->ma = 0;
}
return 1;
}
return 0; // error
}
static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
{
int hsz;
@ -5299,6 +5471,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
if (hsz != 12) {
int compress = stbi__get32le(s);
if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes
if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel
stbi__get32le(s); // discard sizeof
stbi__get32le(s); // discard hres
stbi__get32le(s); // discard vres
@ -5313,17 +5487,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
}
if (info->bpp == 16 || info->bpp == 32) {
if (compress == 0) {
if (info->bpp == 32) {
info->mr = 0xffu << 16;
info->mg = 0xffu << 8;
info->mb = 0xffu << 0;
info->ma = 0xffu << 24;
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
} else {
info->mr = 31u << 10;
info->mg = 31u << 5;
info->mb = 31u << 0;
}
stbi__bmp_set_mask_defaults(info, compress);
} else if (compress == 3) {
info->mr = stbi__get32le(s);
info->mg = stbi__get32le(s);
@ -5338,6 +5502,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
return stbi__errpuc("bad BMP", "bad BMP");
}
} else {
// V4/V5 header
int i;
if (hsz != 108 && hsz != 124)
return stbi__errpuc("bad BMP", "bad BMP");
@ -5345,6 +5510,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
info->mg = stbi__get32le(s);
info->mb = stbi__get32le(s);
info->ma = stbi__get32le(s);
if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs
stbi__bmp_set_mask_defaults(info, compress);
stbi__get32le(s); // discard color space
for (i=0; i < 12; ++i)
stbi__get32le(s); // discard color space parameters
@ -5394,9 +5561,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
psize = (info.offset - info.extra_read - info.hsz) >> 2;
}
if (psize == 0) {
STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original));
if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) {
// accept some number of extra bytes after the header, but if the offset points either to before
// the header ends or implies a large amount of extra data, reject the file as malformed
int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
int header_limit = 1024; // max we actually read is below 256 bytes currently.
int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
return stbi__errpuc("bad header", "Corrupt BMP");
}
// we established that bytes_read_so_far is positive and sensible.
// the first half of this test rejects offsets that are either too small positives, or
// negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
// ensures the number computed in the second half of the test can't overflow.
if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
return stbi__errpuc("bad offset", "Corrupt BMP");
} else {
stbi__skip(s, info.offset - bytes_read_so_far);
}
}
@ -6342,6 +6522,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c
// intermediate buffer is RGBA
result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);
if (!result) return stbi__errpuc("outofmem", "Out of memory");
memset(result, 0xff, x*y*4);
if (!stbi__pic_load_core(s,x,y,comp, result)) {
@ -6457,6 +6638,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in
static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
{
stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
if (!g) return stbi__err("outofmem", "Out of memory");
if (!stbi__gif_header(s, g, comp, 1)) {
STBI_FREE(g);
stbi__rewind( s );
@ -6766,6 +6948,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
}
}
static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays)
{
STBI_FREE(g->out);
STBI_FREE(g->history);
STBI_FREE(g->background);
if (out) STBI_FREE(out);
if (delays && *delays) STBI_FREE(*delays);
return stbi__errpuc("outofmem", "Out of memory");
}
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
{
if (stbi__gif_test(s)) {
@ -6777,6 +6970,10 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
int stride;
int out_size = 0;
int delays_size = 0;
STBI_NOTUSED(out_size);
STBI_NOTUSED(delays_size);
memset(&g, 0, sizeof(g));
if (delays) {
*delays = 0;
@ -6794,26 +6991,29 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
if (out) {
void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );
if (NULL == tmp) {
STBI_FREE(g.out);
STBI_FREE(g.history);
STBI_FREE(g.background);
return stbi__errpuc("outofmem", "Out of memory");
}
if (!tmp)
return stbi__load_gif_main_outofmem(&g, out, delays);
else {
out = (stbi_uc*) tmp;
out_size = layers * stride;
}
if (delays) {
*delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
if (!new_delays)
return stbi__load_gif_main_outofmem(&g, out, delays);
*delays = new_delays;
delays_size = layers * sizeof(int);
}
} else {
out = (stbi_uc*)stbi__malloc( layers * stride );
if (!out)
return stbi__load_gif_main_outofmem(&g, out, delays);
out_size = layers * stride;
if (delays) {
*delays = (int*) stbi__malloc( layers * sizeof(int) );
if (!*delays)
return stbi__load_gif_main_outofmem(&g, out, delays);
delays_size = layers * sizeof(int);
}
}
@ -7064,12 +7264,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
// Run
value = stbi__get8(s);
count -= 128;
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = value;
} else {
// Dump
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = stbi__get8(s);
}
@ -7138,9 +7338,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
info.all_a = 255;
p = stbi__bmp_parse_header(s, &info);
if (p == NULL) {
stbi__rewind( s );
if (p == NULL)
return 0;
}
if (x) *x = s->img_x;
if (y) *y = s->img_y;
if (comp) {
@ -7206,8 +7407,8 @@ static int stbi__psd_is16(stbi__context *s)
stbi__rewind( s );
return 0;
}
(void) stbi__get32be(s);
(void) stbi__get32be(s);
STBI_NOTUSED(stbi__get32be(s));
STBI_NOTUSED(stbi__get32be(s));
depth = stbi__get16be(s);
if (depth != 16) {
stbi__rewind( s );
@ -7286,7 +7487,6 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
// Known limitations:
// Does not support comments in the header section
// Does not support ASCII image data (formats P2 and P3)
// Does not support 16-bit-per-channel
#ifndef STBI_NO_PNM
@ -7307,7 +7507,8 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
stbi_uc *out;
STBI_NOTUSED(ri);
if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))
ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n);
if (ri->bits_per_channel == 0)
return 0;
if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
@ -7317,15 +7518,22 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
*y = s->img_y;
if (comp) *comp = s->img_n;
if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0))
if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0))
return stbi__errpuc("too large", "PNM too large");
out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0);
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
stbi__getn(s, out, s->img_n * s->img_x * s->img_y);
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
STBI_FREE(out);
return stbi__errpuc("bad PNM", "PNM file truncated");
}
if (req_comp && req_comp != s->img_n) {
if (ri->bits_per_channel == 16) {
out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);
} else {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
}
if (out == NULL) return out; // stbi__convert_format frees input on failure
}
return out;
@ -7362,6 +7570,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c)
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
value = value*10 + (*c - '0');
*c = (char) stbi__get8(s);
if((value > 214748364) || (value == 214748364 && *c > '7'))
return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
}
return value;
@ -7392,17 +7602,29 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
stbi__pnm_skip_whitespace(s, &c);
*x = stbi__pnm_getinteger(s, &c); // read width
if(*x == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
stbi__pnm_skip_whitespace(s, &c);
*y = stbi__pnm_getinteger(s, &c); // read height
if (*y == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
stbi__pnm_skip_whitespace(s, &c);
maxv = stbi__pnm_getinteger(s, &c); // read max value
if (maxv > 255)
return stbi__err("max value > 255", "PPM image not 8-bit");
if (maxv > 65535)
return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images");
else if (maxv > 255)
return 16;
else
return 8;
}
static int stbi__pnm_is16(stbi__context *s)
{
if (stbi__pnm_info(s, NULL, NULL, NULL) == 16)
return 1;
return 0;
}
#endif
@ -7458,6 +7680,9 @@ static int stbi__is_16_main(stbi__context *s)
if (stbi__psd_is16(s)) return 1;
#endif
#ifndef STBI_NO_PNM
if (stbi__pnm_is16(s)) return 1;
#endif
return 0;
}

View File

@ -1,4 +1,4 @@
/* stb_image_write - v1.15 - public domain - http://nothings.org/stb
/* stb_image_write - v1.16 - public domain - http://nothings.org/stb
writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
no warranty implied; use at your own risk
@ -140,6 +140,7 @@ CREDITS:
Ivan Tikhonov
github:ignotion
Adam Schackart
Andrew Kensler
LICENSE
@ -166,9 +167,9 @@ LICENSE
#endif
#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations
extern int stbi_write_tga_with_rle;
extern int stbi_write_png_compression_level;
extern int stbi_write_force_png_filter;
STBIWDEF int stbi_write_tga_with_rle;
STBIWDEF int stbi_write_png_compression_level;
STBIWDEF int stbi_write_force_png_filter;
#endif
#ifndef STBI_WRITE_NO_STDIO
@ -178,7 +179,7 @@ STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const
STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality);
#ifdef STBI_WINDOWS_UTF8
#ifdef STBIW_WINDOWS_UTF8
STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
#endif
#endif
@ -285,7 +286,7 @@ static void stbi__stdio_write(void *context, void *data, int size)
fwrite(data,1,size,(FILE*) context);
}
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8)
#ifdef __cplusplus
#define STBIW_EXTERN extern "C"
#else
@ -303,16 +304,16 @@ STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const w
static FILE *stbiw__fopen(char const *filename, char const *mode)
{
FILE *f;
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8)
wchar_t wMode[64];
wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
return 0;
#if _MSC_VER >= 1400
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0;
#else
@ -397,7 +398,7 @@ static void stbiw__putc(stbi__write_context *s, unsigned char c)
static void stbiw__write1(stbi__write_context *s, unsigned char a)
{
if (s->buf_used + 1 > sizeof(s->buffer))
if ((size_t)s->buf_used + 1 > sizeof(s->buffer))
stbiw__write_flush(s);
s->buffer[s->buf_used++] = a;
}
@ -405,7 +406,7 @@ static void stbiw__write1(stbi__write_context *s, unsigned char a)
static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
{
int n;
if (s->buf_used + 3 > sizeof(s->buffer))
if ((size_t)s->buf_used + 3 > sizeof(s->buffer))
stbiw__write_flush(s);
n = s->buf_used;
s->buf_used = n+3;
@ -490,11 +491,22 @@ static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x,
static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)
{
if (comp != 4) {
// write RGB bitmap
int pad = (-x*3) & 3;
return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
"11 4 22 4" "4 44 22 444444",
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
} else {
// RGBA bitmaps need a v4 header
// use BI_BITFIELDS mode with 32bpp and alpha mask
// (straight BI_RGB with alpha mask doesn't work in most readers)
return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0,
"11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444",
'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header
108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header
}
}
STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
@ -622,6 +634,8 @@ STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const
#define stbiw__max(a, b) ((a) > (b) ? (a) : (b))
#ifndef STBI_WRITE_NO_STDIO
static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
{
int exponent;
@ -756,7 +770,7 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f
char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
s->func(s->context, header, sizeof(header)-1);
#ifdef __STDC_WANT_SECURE_LIB__
#ifdef __STDC_LIB_EXT1__
len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
#else
len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
@ -777,7 +791,6 @@ STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x,
return stbi_write_hdr_core(&s, x, y, comp, (float *) data);
}
#ifndef STBI_WRITE_NO_STDIO
STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
{
stbi__write_context s = { 0 };
@ -968,6 +981,23 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
(void) stbiw__sbfree(hash_table[i]);
STBIW_FREE(hash_table);
// store uncompressed instead if compression was worse
if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) {
stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1
for (j = 0; j < data_len;) {
int blocklen = data_len - j;
if (blocklen > 32767) blocklen = 32767;
stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression
stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN
stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8));
stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN
stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8));
memcpy(out+stbiw__sbn(out), data+j, blocklen);
stbiw__sbn(out) += blocklen;
j += blocklen;
}
}
{
// compute adler32 on input
unsigned int s1=1, s2=0;
@ -1598,6 +1628,10 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
/* Revision history
1.16 (2021-07-11)
make Deflate code emit uncompressed blocks when it would otherwise expand
support writing BMPs with alpha channel
1.15 (2020-07-13) unknown
1.14 (2020-02-02) updated JPEG writer to downsample chroma channels
1.13
1.12

View File

@ -1,4 +1,4 @@
// Ogg Vorbis audio decoder - v1.19 - public domain
// Ogg Vorbis audio decoder - v1.22 - public domain
// http://nothings.org/stb_vorbis/
//
// Original version written by Sean Barrett in 2007.
@ -29,11 +29,16 @@
// Bernhard Wodo Evan Balster github:alxprd
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
// Phillip Bennefall Rohit Thiago Goulart
// github:manxorist saga musix github:infatum
// github:manxorist Saga Musix github:infatum
// Timur Gagiev Maxwell Koo Peter Waller
// github:audinowho Dougall Johnson
// github:audinowho Dougall Johnson David Reid
// github:Clownacy Pedro J. Estebanez Remi Verschelde
// AnthoFoxo github:morlat Gabriel Ravier
//
// Partial history:
// 1.22 - 2021-07-11 - various small fixes
// 1.21 - 2021-07-02 - fix bug for files with no comments
// 1.20 - 2020-07-11 - several small fixes
// 1.19 - 2020-02-05 - warnings
// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc.
// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure)
@ -218,6 +223,12 @@ extern int stb_vorbis_decode_frame_pushdata(
// channel. In other words, (*output)[0][0] contains the first sample from
// the first channel, and (*output)[1][0] contains the first sample from
// the second channel.
//
// *output points into stb_vorbis's internal output buffer storage; these
// buffers are owned by stb_vorbis and application code should not free
// them or modify their contents. They are transient and will be overwritten
// once you ask for more data to get decoded, so be sure to grab any data
// you need before then.
extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
// inform stb_vorbis that your next datablock will not be contiguous with
@ -577,7 +588,7 @@ enum STBVorbisError
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <malloc.h>
#endif
#if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__)
#if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__)
#include <alloca.h>
#endif
#else // STB_VORBIS_NO_CRT
@ -644,6 +655,12 @@ typedef signed int int32;
typedef float codetype;
#ifdef _MSC_VER
#define STBV_NOTUSED(v) (void)(v)
#else
#define STBV_NOTUSED(v) (void)sizeof(v)
#endif
// @NOTE
//
// Some arrays below are tagged "//varies", which means it's actually
@ -963,7 +980,7 @@ static void *setup_temp_malloc(vorb *f, int sz)
static void setup_temp_free(vorb *f, void *p, int sz)
{
if (f->alloc.alloc_buffer) {
f->temp_offset += (sz+3)&~3;
f->temp_offset += (sz+7)&~7;
return;
}
free(p);
@ -1044,7 +1061,7 @@ static float float32_unpack(uint32 x)
uint32 sign = x & 0x80000000;
uint32 exp = (x & 0x7fe00000) >> 21;
double res = sign ? -(double)mantissa : (double)mantissa;
return (float) ldexp((float)res, exp-788);
return (float) ldexp((float)res, (int)exp-788);
}
@ -1075,6 +1092,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
// find the first entry
for (k=0; k < n; ++k) if (len[k] < NO_CODE) break;
if (k == n) { assert(c->sorted_entries == 0); return TRUE; }
assert(len[k] < 32); // no error return required, code reading lens checks this
// add to the list
add_entry(c, 0, k, m++, len[k], values);
// add all available leaves
@ -1088,6 +1106,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
uint32 res;
int z = len[i], y;
if (z == NO_CODE) continue;
assert(z < 32); // no error return required, code reading lens checks this
// find lowest available leaf (should always be earliest,
// which is what the specification calls for)
// note that this property, and the fact we can never have
@ -1097,12 +1116,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
while (z > 0 && !available[z]) --z;
if (z == 0) { return FALSE; }
res = available[z];
assert(z >= 0 && z < 32);
available[z] = 0;
add_entry(c, bit_reverse(res), i, m++, len[i], values);
// propagate availability up the tree
if (z != len[i]) {
assert(len[i] >= 0 && len[i] < 32);
for (y=len[i]; y > z; --y) {
assert(available[y] == 0);
available[y] = res + (1 << (32-y));
@ -1602,7 +1619,8 @@ static uint32 get_bits(vorb *f, int n)
f->valid_bits += 8;
}
}
if (f->valid_bits < 0) return 0;
assert(f->valid_bits >= n);
z = f->acc & ((1 << n)-1);
f->acc >>= n;
f->valid_bits -= n;
@ -2574,34 +2592,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A,
while (z > base) {
float k00,k11;
float l00,l11;
k00 = z[-0] - z[-8];
k11 = z[-1] - z[-9];
l00 = z[-2] - z[-10];
l11 = z[-3] - z[-11];
z[-0] = z[-0] + z[-8];
z[-1] = z[-1] + z[-9];
z[-8] = k00;
z[-9] = k11 ;
k00 = z[ -2] - z[-10];
k11 = z[ -3] - z[-11];
z[ -2] = z[ -2] + z[-10];
z[ -3] = z[ -3] + z[-11];
z[-10] = (k00+k11) * A2;
z[-11] = (k11-k00) * A2;
z[ -8] = k00;
z[ -9] = k11;
z[-10] = (l00+l11) * A2;
z[-11] = (l11-l00) * A2;
k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation
k00 = z[ -4] - z[-12];
k11 = z[ -5] - z[-13];
l00 = z[ -6] - z[-14];
l11 = z[ -7] - z[-15];
z[ -4] = z[ -4] + z[-12];
z[ -5] = z[ -5] + z[-13];
z[-12] = k11;
z[-13] = k00;
k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation
k11 = z[ -7] - z[-15];
z[ -6] = z[ -6] + z[-14];
z[ -7] = z[ -7] + z[-15];
z[-14] = (k00+k11) * A2;
z[-15] = (k00-k11) * A2;
z[-12] = k11;
z[-13] = -k00;
z[-14] = (l11-l00) * A2;
z[-15] = (l00+l11) * -A2;
iter_54(z);
iter_54(z-8);
@ -3066,6 +3083,7 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f
for (q=1; q < g->values; ++q) {
j = g->sorted_order[q];
#ifndef STB_VORBIS_NO_DEFER_FLOOR
STBV_NOTUSED(step2_flag);
if (finalY[j] >= 0)
#else
if (step2_flag[j])
@ -3168,6 +3186,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
// WINDOWING
STBV_NOTUSED(left_end);
n = f->blocksize[m->blockflag];
map = &f->mapping[m->mapping];
@ -3365,7 +3384,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
// this isn't to spec, but spec would require us to read ahead
// and decode the size of all current frames--could be done,
// but presumably it's not a commonly used feature
f->current_loc = -n2; // start of first frame is positioned for discard
f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around)
// we might have to discard samples "from" the next frame too,
// if we're lapping a large block then a small at the start?
f->discard_samples_deferred = n - right_end;
@ -3632,17 +3651,24 @@ static int start_decoder(vorb *f)
//file vendor
len = get32_packet(f);
f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1));
if (f->vendor == NULL) return error(f, VORBIS_outofmem);
for(i=0; i < len; ++i) {
f->vendor[i] = get8_packet(f);
}
f->vendor[len] = (char)'\0';
//user comments
f->comment_list_length = get32_packet(f);
f->comment_list = NULL;
if (f->comment_list_length > 0)
{
f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length));
if (f->comment_list == NULL) return error(f, VORBIS_outofmem);
}
for(i=0; i < f->comment_list_length; ++i) {
len = get32_packet(f);
f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1));
if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem);
for(j=0; j < len; ++j) {
f->comment_list[i][j] = get8_packet(f);
@ -3859,8 +3885,7 @@ static int start_decoder(vorb *f)
unsigned int div=1;
for (k=0; k < c->dimensions; ++k) {
int off = (z / div) % c->lookup_values;
float val = mults[off];
val = mults[off]*c->delta_value + c->minimum_value + last;
float val = mults[off]*c->delta_value + c->minimum_value + last;
c->multiplicands[j*c->dimensions + k] = val;
if (c->sequence_p)
last = val;
@ -3943,7 +3968,7 @@ static int start_decoder(vorb *f)
if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
}
for (k=0; k < 1 << g->class_subclasses[j]; ++k) {
g->subclass_books[j][k] = get_bits(f,8)-1;
g->subclass_books[j][k] = (int16)get_bits(f,8)-1;
if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
}
}
@ -4255,7 +4280,7 @@ static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z)
memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start
if (z) {
p->alloc = *z;
p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3;
p->alloc.alloc_buffer_length_in_bytes &= ~7;
p->temp_offset = p->alloc.alloc_buffer_length_in_bytes;
}
p->eof = 0;
@ -4501,6 +4526,7 @@ stb_vorbis *stb_vorbis_open_pushdata(
*error = VORBIS_need_more_data;
else
*error = p.error;
vorbis_deinit(&p);
return NULL;
}
f = vorbis_alloc(&p);
@ -4558,7 +4584,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
header[i] = get8(f);
if (f->eof) return 0;
if (header[4] != 0) goto invalid;
goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24);
goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24);
for (i=22; i < 26; ++i)
header[i] = 0;
crc = 0;
@ -4962,7 +4988,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
// set. whoops!
break;
}
previous_safe = last_page_loc+1;
//previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging
last_page_loc = stb_vorbis_get_file_offset(f);
}
@ -5073,7 +5099,10 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st
stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc)
{
stb_vorbis *f, p;
if (data == NULL) return NULL;
if (!data) {
if (error) *error = VORBIS_unexpected_eof;
return NULL;
}
vorbis_init(&p, alloc);
p.stream = (uint8 *) data;
p.stream_end = (uint8 *) data + len;
@ -5148,11 +5177,11 @@ static void copy_samples(short *dest, float *src, int len)
static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len)
{
#define BUFFER_SIZE 32
float buffer[BUFFER_SIZE];
int i,j,o,n = BUFFER_SIZE;
#define STB_BUFFER_SIZE 32
float buffer[STB_BUFFER_SIZE];
int i,j,o,n = STB_BUFFER_SIZE;
check_endianness();
for (o = 0; o < len; o += BUFFER_SIZE) {
for (o = 0; o < len; o += STB_BUFFER_SIZE) {
memset(buffer, 0, sizeof(buffer));
if (o + n > len) n = len - o;
for (j=0; j < num_c; ++j) {
@ -5169,16 +5198,17 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in
output[o+i] = v;
}
}
#undef STB_BUFFER_SIZE
}
static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len)
{
#define BUFFER_SIZE 32
float buffer[BUFFER_SIZE];
int i,j,o,n = BUFFER_SIZE >> 1;
#define STB_BUFFER_SIZE 32
float buffer[STB_BUFFER_SIZE];
int i,j,o,n = STB_BUFFER_SIZE >> 1;
// o is the offset in the source data
check_endianness();
for (o = 0; o < len; o += BUFFER_SIZE >> 1) {
for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) {
// o2 is the offset in the output data
int o2 = o << 1;
memset(buffer, 0, sizeof(buffer));
@ -5208,6 +5238,7 @@ static void compute_stereo_samples(short *output, int num_c, float **data, int d
output[o2+i] = v;
}
}
#undef STB_BUFFER_SIZE
}
static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples)
@ -5280,8 +5311,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short
float **outputs;
int len = num_shorts / channels;
int n=0;
int z = f->channels;
if (z > channels) z = channels;
while (n < len) {
int k = f->channel_buffer_end - f->channel_buffer_start;
if (n+k >= len) k = len - n;
@ -5300,8 +5329,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, in
{
float **outputs;
int n=0;
int z = f->channels;
if (z > channels) z = channels;
while (n < len) {
int k = f->channel_buffer_end - f->channel_buffer_start;
if (n+k >= len) k = len - n;

View File

@ -1,3 +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 })

View File

@ -6,7 +6,7 @@
#endif
#ifndef ENABLE_PROFILER
#define ENABLE_PROFILER ifdef(debug, 1, 0) ///+
#define ENABLE_PROFILER ifdef(retail, 0, 1) ///+
#endif
#ifndef ENABLE_SELFIES

View File

@ -2,9 +2,12 @@
// -----------------------------------------------------------------------------
// sort/less
int sort_64(const void *a, const void *b) {
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;

View File

@ -1,11 +1,6 @@
// data structures and utils: array, set, map, hash, sort.
// - rlyeh, public domain
// -----------------------------------------------------------------------------
// sort
API int sort_64(const void *a, const void *b);
// -----------------------------------------------------------------------------
// less
@ -14,6 +9,12 @@ 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

View File

@ -263,6 +263,18 @@ int ui_debug() {
#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.
@ -350,18 +362,6 @@ int ui_debug() {
EDITOR_UI_COLLAPSE(ICON_MD_MOVIE " FXs", "Debug.FXs") {
ui_fxs();
}
EDITOR_UI_COLLAPSE(ICON_MD_VIEW_QUILT " UI", "Debug.UI") {
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_SAVINGS " Budgets", "Debug.Budgets") {

View File

@ -1292,15 +1292,12 @@ static const char bm_mini_ttf[] = {
/*004ec0*/ 0x00,0x08,0x00,0x40,0x00,0x0a,0x00,0x00,0x00,0x84,0x00,0xde,0x00,0x01,0x00,0x01
};
static const unsigned bm_mini_ttf_length = (unsigned)sizeof(bm_mini_ttf);
// -----------------------------------------------------------------------------
// The following data tables are coming from Dear Imgui.
// Re-licensed under permission as MIT-0.
//
// @todo: 0x3100, 0x312F, FONT_TW, // Bopomofo
// 0xE000, 0xEB4C, FONT_EM, // Private use (emojis)
static const unsigned table_common[] = {
0x0020, 0x00FF, // Basic Latin + Latin Supplement
@ -1388,6 +1385,12 @@ static const unsigned table_middle_east[] = {
0
};
static const unsigned table_emoji[] = {
// 0xE000, 0xEB4C, // Private use (emojis)
0xE000, 0xF8FF, // Private use (emojis+webfonts)
0
};
// Store 2500 regularly used characters for Simplified Chinese. Table below covers 97.97% of all characters used during the month in July, 1987.
// [ref] https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8
static const unsigned short packed_table_chinese[] = { // starts with 0x4E00
@ -1612,18 +1615,18 @@ static font_t fonts[8] = {0};
static
void font_init() {
do_once {
font_face_from_mem(FONT_FACE1, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE2, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE3, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE4, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE5, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE6, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE1, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE2, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE3, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE4, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE5, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE6, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
}
}
// Remap color within all existing color textures
void font_color(const char *tag, uint32_t color) {
do_once font_init();
font_init();
unsigned index = *tag - FONT_COLOR1[0];
if( index < FONT_MAX_COLORS ) {
@ -1641,7 +1644,7 @@ void font_color(const char *tag, uint32_t color) {
}
void font_scales(const char *tag, float h1, float h2, float h3, float h4, float h5, float h6) {
do_once font_init();
font_init();
unsigned index = *tag - FONT_FACE1[0];
if( index >= 8 ) return;
@ -1662,16 +1665,14 @@ void font_scales(const char *tag, float h1, float h2, float h3, float h4, float
// 1. Call stb_truetype.h routines to read and parse a .ttf file.
// 1. Create a bitmap that is uploaded to the gpu using opengl.
// 1. Calculate and save a bunch of useful variables and put them in the global font variable.
void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_len, float font_size, unsigned flags) {
const unsigned char *ttf_buffer = ttf_bufferv;
flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is always in
void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, float font_size, unsigned flags) {
unsigned index = *tag - FONT_FACE1[0];
if( index >= 8 ) return;
if( !ttf_buffer /*|| !ttf_len*/ ) return;
if( font_size <= 0 || font_size > 72 ) return;
if( !ttf_data || !ttf_len ) return;
flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is always in
font_t *f = &fonts[index];
f->initialized = 1;
@ -1692,8 +1693,8 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
f->scale[6] = 0.2500f; // H6
const char *vs_filename = 0, *fs_filename = 0;
const char *vs = vs_filename ? file_read(vs_filename) : mv_vs_source;
const char *fs = fs_filename ? file_read(fs_filename) : mv_fs_source;
const char *vs = vs_filename ? vfs_read(vs_filename) : mv_vs_source;
const char *fs = fs_filename ? vfs_read(fs_filename) : mv_fs_source;
f->program = shader(vs, fs, "vertexPosition,instanceGlyph", "outColor", NULL);
// figure out what ranges we're about to bake
@ -1712,6 +1713,7 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
array(uint64_t) sorted = 0;
if(flags & FONT_ASCII) { MERGE_TABLE(table_common); }
if(flags & FONT_EM) { MERGE_TABLE(table_emoji); }
if(flags & FONT_EU) { MERGE_TABLE(table_eastern_europe); }
if(flags & FONT_RU) { MERGE_TABLE(table_western_europe); }
if(flags & FONT_EL) { MERGE_TABLE(table_western_europe); }
@ -1724,8 +1726,8 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
if(flags & FONT_ZH) { MERGE_TABLE(table_chinese_japanese_common); MERGE_PACKED_TABLE(0x4E00, packed_table_chinese); } // zh-simplified
if(flags & FONT_ZH) { MERGE_TABLE(table_chinese_punctuation); } // both zh-simplified and zh-full
// if(flags & FONT_ZH) { MERGE_TABLE(table_chinese_full); } // zh-full
array_sort(sorted, sort_64);
array_unique(sorted, sort_64); // sort + unique pass
array_sort(sorted, less_64_ptr);
array_unique(sorted, less_64_ptr); // sort + unique pass
// pack and create bitmap
unsigned char *bitmap = (unsigned char*)MALLOC(f->height*f->width);
@ -1749,7 +1751,7 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
while( i < (num-1) && (sorted[i+1]-sorted[i]) == 1 ) end = sorted[++i];
//printf("(%d,%d)", (unsigned)begin, (unsigned)end);
if( stbtt_PackFontRange(&pc, ttf_buffer, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin) ) {
if( stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin) ) {
for( int j = begin; j <= end; ++j ) {
// unicode->index runtime lookup
f->cp2iter[ j ] = count;
@ -1762,11 +1764,13 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
stbtt_PackEnd(&pc);
f->num_glyphs = count;
assert( f->num_glyphs < charCount );
array_free(sorted);
// calculate vertical font metrics
stbtt_fontinfo info;
stbtt_InitFont(&info, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
stbtt_fontinfo info = {0};
stbtt_InitFont(&info, ttf_data, stbtt_GetFontOffsetForIndex(ttf_data,0));
int a, d, l;
float s = stbtt_ScaleForPixelHeight(&info, f->font_size);
@ -1832,7 +1836,7 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
// last chance to inspect the font atlases
if( flag("--font-debug") )
stbi_write_png(va("debug_font_atlas%d.png", index), f->width, f->height, 1, bitmap, 0);
stbi_write_png(va("font_debug%d.png", index), f->width, f->height, 1, bitmap, 0);
FREE(bitmap);
@ -1899,12 +1903,13 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
}
void font_face(const char *tag, const char *filename_ttf, float font_size, unsigned flags) {
do_once font_init();
font_init();
const char *buffer = //file_read(filename_ttf);
//if(!buffer) buffer =
vfs_read(filename_ttf);
font_face_from_mem(tag, buffer,0, font_size, flags);
int len;
const char *buffer = vfs_load(filename_ttf, &len);
if( !buffer ) buffer = file_load(filename_ttf, &len);
font_face_from_mem(tag, buffer,len, font_size, flags);
}
static
@ -1989,7 +1994,7 @@ void font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, float fact
// 1. draw the string
static
vec2 font_draw_ex(const char *text, vec2 offset, const char *col, void (*draw_cmd)(font_t *,const float *,int,float,vec2)) {
do_once font_init();
font_init();
// sanity checks
int len = strlen(text);

View File

@ -470,7 +470,7 @@ int input_enum(const char *vk) {
static map(char*,int) m = 0;
do_once {
map_init_str(m);
#define k(VK) map_insert(m, STRINGIZE(KEY_##VK), KEY_##VK); map_insert(m, STRINGIZE(VK), KEY_##VK);
#define k(VK) map_find_or_add(m, STRINGIZE(VK), KEY_##VK); map_find_or_add(m, STRINGIZE(KEY_##VK), KEY_##VK);
k(ESC)
k(TICK) k(1) k(2) k(3) k(4) k(5) k(6) k(7) k(8) k(9) k(0) k(BS)
k(TAB) k(Q) k(W) k(E) k(R) k(T) k(Y) k(U) k(I) k(O) k(P)
@ -672,6 +672,7 @@ int ui_mouse() {
int ui_keyboard() {
char *keys[] = {
"F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12",
"ESC",
"TICK","1","2","3","4","5","6","7","8","9","0","BS",
"TAB","Q","W","E","R","T","Y","U","I","O","P",
@ -680,25 +681,23 @@ int ui_keyboard() {
"LCTRL","LALT","SPACE","RALT","RCTRL","<","V",">",
};
vec2i rows[] = {
vec2i(0,1),
vec2i(1,13),
vec2i(13,24),
vec2i(24,35),
vec2i(35,45),
vec2i(45,countof(keys)),
float rows[] = {
12,
1,
12,
11,
11,
10,
8
};
for( int i = 0; i < countof(rows); ++i ) {
int any = 0;
char *row = 0;
for( int j = rows[i].x; j < rows[i].y; ++j ) {
strcatf(&row, input(KEY_ESC + j) ? (any|=1, "[%s]") : " %s ", keys[j]);
for( int row = 0, k = 0; row < countof(rows); ++row ) {
static char *buf = 0; if(buf) *buf = 0;
for( int col = 0; col < rows[row]; ++col, ++k ) {
assert( input_enum(keys[k]) == input_enum(va("KEY_%s", keys[k])) );
strcatf(&buf, input(input_enum(keys[k])) ? "[%s]" : " %s ", keys[k]);
}
if(!any) ui_disable();
ui_label(row);
ui_enable();
FREE(row);
ui_label(buf);
}
return 0;

View File

@ -85,8 +85,7 @@ 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_ESC,
KEY_TICK, KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9,KEY_0, KEY_BS,
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,

View File

@ -17,6 +17,9 @@ obj_vtable_null(draw, int );
obj_vtable_null(lerp, int );
obj_vtable_null(edit, int ); // OSC cmds: argc,argv "undo","redo","cut","copy","paste","edit","view","menu"
obj_vtable_null(menu, int );
obj_vtable_null(aabb, int );
obj_vtable_null(icon, char* );
// ----------------------------------------------------------------------------

View File

@ -136,9 +136,13 @@ void* obj_free(void *o);
#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 EXTEND obj_extend
#define obj_extend(T,func) (obj_##func[OBJTYPE(T)] = (void*)T##_##func)
#define obj_method(method,o,...) (obj_##method[((obj*)(o))->objtype](o,##__VA_ARGS__)) // (obj_##method[((obj*)(o))->objtype]((o), ##__VA_ARGS__))
@ -165,6 +169,7 @@ API extern int (*obj_draw[256])(); ///-
API extern int (*obj_lerp[256])(); ///-
API extern int (*obj_edit[256])(); ///-
API extern int (*obj_aabb[256])(); ///-
// ----------------------------------------------------------------------------
// core

View File

@ -10,14 +10,16 @@ void (ui_profiler)() {
double fps = window_fps();
profile_setstat("Render.num_fps", fps);
// draw fps-meter: 300 samples, [0..70] range each, 70px height plot.
nk_layout_row_dynamic(ui_ctx, 70, 1);
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 ) {
@ -38,6 +40,7 @@ void (ui_profiler)() {
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) ) {

View File

@ -29,12 +29,12 @@ const char* symbol(const char *s) {
void type_inscribe(const char *TY,unsigned TYsz,const char *infos) {
reflect_init();
unsigned TYid = intern(TY = symbol(TY));
map_find_or_add(reflects, TYid, ((reflect_t){TYid, 0, TYsz, TY, infos}));
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(E));
map_find_or_add(reflects, Eid, ((reflect_t){Eid,0, Eval, E,infos}));
map_find_or_add(reflects, Eid, ((reflect_t){Eid,0, Eval, STRDUP(E),infos})); // @leak
}
unsigned enum_find(const char *E) {
reflect_init();
@ -44,7 +44,7 @@ unsigned enum_find(const char *E) {
void function_inscribe(const char *F,void *func,const char *infos) {
reflect_init();
unsigned Fid = intern(F = symbol(F));
map_find_or_add(reflects, Fid, ((reflect_t){Fid,0, 0, F,infos, func}));
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) {
@ -55,18 +55,31 @@ void *function_find(const char *F) {
void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
reflect_init();
unsigned Tid = intern(T = symbol(T));
map_find_or_add(reflects, Tid, ((reflect_t){Tid, OBJTYPEid, Tsz, T, infos}));
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) {
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(T));
unsigned Mid = intern(M = symbol(M));
type = symbol(type);
map_find_or_add(reflects, (Mid<<16)|Tid, ((reflect_t){Mid, 0, Msz, M, infos, NULL, Tid, 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);
array_push(*found, ((reflect_t){Mid, 0, Msz, M, infos, NULL, Tid, type, bytes }));
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();
@ -97,9 +110,17 @@ void ui_reflect_(const reflect_t *R, const char *filter, int mask) {
if( buf ) *buf = '\0';
struct nk_context *ui_ctx = (struct nk_context *)ui_handle();
/*for ui_push_hspace(16)*/ {
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)) ui_reflect_(it,filter,'M'); }
/**/ 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));

View File

@ -18,23 +18,13 @@ API unsigned bgraf( float b, float g, float r, float a );
API unsigned alpha( unsigned rgba );
#define RGBX(rgb,x) ( ((rgb)&0xFFFFFF) | (((unsigned)(x))<<24) )
#define RGB3(r,g,b) ( (255<<24) | ((r)<<16) | ((g)<<8) | (b) )
#define RGB4(r,g,b,a) RGBX(RGB3(r,g,b),a)
#define RGB3(r,g,b) ( (255<<24) | ((b)<<16) | ((g)<<8) | (r) )
#define RGB4(r,g,b,a) ( ((a)<<24) | ((b)<<16) | ((g)<<8) | (r) )
#define BLACK RGBX(0x000000,255)
#define WHITE RGBX(0xFFF1E8,255)
#define WHITE RGBX(0xE8F1FF,255)
#if 0
#define RED RGBX(0xFF004D,255)
#define GREEN RGBX(0x00B543,255)
#define BLUE RGBX(0x065AB5,255)
#define ORANGE RGBX(0xFF6C24,255)
#define CYAN RGBX(0x29ADFF,255)
#define PURPLE RGBX(0x7E2553,255)
#define YELLOW RGBX(0xFFEC27,255)
#define GRAY RGBX(0x725158,255)
#else
#define RED RGB3( 255,48,48 )
#define RED RGB3( 255, 0,48 )
#define GREEN RGB3( 144,255,48 )
#define CYAN RGB3( 0,192,255 )
#define ORANGE RGB3( 255,144,48 )
@ -45,8 +35,7 @@ API unsigned alpha( unsigned rgba );
#define PINK RGB3( 255,48,144 )
#define AQUA RGB3( 48,255,144 )
#define BLUE RGBX(0x065AB5,255)
#endif
#define BLUE RGBX(0xB55A06,255)
// -----------------------------------------------------------------------------
// images

View File

@ -92,7 +92,7 @@ void ddraw_flush_projview(mat44 proj, mat44 view) {
int count = array_count(list);
if(!count) continue;
// color
vec3 rgbf = {((rgb>>16)&255)/255.f,((rgb>>8)&255)/255.f,((rgb>>0)&255)/255.f};
vec3 rgbf = {((rgb>>0)&255)/255.f,((rgb>>8)&255)/255.f,((rgb>>16)&255)/255.f};
glUniform3fv(dd_u_color, GL_TRUE, &rgbf.x);
// config vertex data
glBufferData(GL_ARRAY_BUFFER, count * 3 * 4, list, GL_STATIC_DRAW);
@ -124,7 +124,7 @@ void ddraw_flush_projview(mat44 proj, mat44 view) {
int count = array_count(list);
if(!count) continue;
// color
vec3 rgbf = {((rgb>>16)&255)/255.f,((rgb>>8)&255)/255.f,((rgb>>0)&255)/255.f};
vec3 rgbf = {((rgb>>0)&255)/255.f,((rgb>>8)&255)/255.f,((rgb>>16)&255)/255.f};
glUniform3fv(dd_u_color, GL_TRUE, &rgbf.x);
// config vertex data
glBufferData(GL_ARRAY_BUFFER, count * 3 * 4, list, GL_STATIC_DRAW);

View File

@ -325,7 +325,7 @@ const char *extract_utf32(const char *s, uint32_t *out) {
/**/ if( (s[0] & 0x80) == 0x00 ) return *out = (s[0]), s + 1;
else if( (s[0] & 0xe0) == 0xc0 ) return *out = (s[0] & 31) << 6 | (s[1] & 63), s + 2;
else if( (s[0] & 0xf0) == 0xe0 ) return *out = (s[0] & 15) << 12 | (s[1] & 63) << 6 | (s[2] & 63), s + 3;
else if( (s[0] & 0xf8) != 0xf0 ) return *out = (s[0] & 7) << 18 | (s[1] & 63) << 12 | (s[2] & 63) << 8 | (s[3] & 63), s + 4;
else if( (s[0] & 0xf8) == 0xf0 ) return *out = (s[0] & 7) << 18 | (s[1] & 63) << 12 | (s[2] & 63) << 8 | (s[3] & 63), s + 4;
return *out = 0, s + 0;
}
array(uint32_t) string32( const char *utf8 ) {

View File

@ -561,9 +561,9 @@ void tty_color(unsigned color) {
#endif
if( color ) {
// if( color == RED ) alert("break on error message (RED)"), breakpoint(); // debug
unsigned r = (color >> 16) & 255;
unsigned r = (color >> 0) & 255;
unsigned g = (color >> 8) & 255;
unsigned b = (color >> 0) & 255;
unsigned b = (color >> 16) & 255;
// 24-bit console ESC[ … 38;2;<r>;<g>;<b> … m Select RGB foreground color
// 256-color console ESC[38;5;<fgcode>m
// 0x00-0x07: standard colors (as in ESC [ 30..37 m)

View File

@ -1,6 +1,7 @@
// ----------------------------------------------------------------------------
// ease
float ease_nop(float t) { return 0; }
float ease_linear(float t) { return t; }
float ease_out_sine(float t) { return sinf(t*(C_PI*0.5f)); }
@ -41,7 +42,6 @@ float ease_inout_perlin(float t) { float t3=t*t*t,t4=t3*t,t5=t4*t; return 6*t5-1
float ease(float t01, unsigned mode) {
typedef float (*easing)(float);
easing modes[] = {
ease_linear,
ease_out_sine,
ease_out_quad,
ease_out_cubic,
@ -53,7 +53,6 @@ float ease(float t01, unsigned mode) {
ease_out_elastic,
ease_out_bounce,
ease_linear,
ease_in_sine,
ease_in_quad,
ease_in_cubic,
@ -65,7 +64,6 @@ float ease(float t01, unsigned mode) {
ease_in_elastic,
ease_in_bounce,
ease_linear,
ease_inout_sine,
ease_inout_quad,
ease_inout_cubic,
@ -77,6 +75,8 @@ float ease(float t01, unsigned mode) {
ease_inout_elastic,
ease_inout_bounce,
ease_nop,
ease_linear,
ease_inout_perlin,
};
return modes[clampi(mode, 0, countof(modes))](clampf(t01,0,1));
@ -89,7 +89,6 @@ float ease_pong_ping(float t, unsigned fn1, unsigned fn2) { return 1 - ease_ping
const char **ease_enums() {
static const char *list[] = {
"ease_linear",
"ease_out_sine",
"ease_out_quad",
"ease_out_cubic",
@ -101,7 +100,6 @@ const char **ease_enums() {
"ease_out_elastic",
"ease_out_bounce",
"ease_linear",
"ease_in_sine",
"ease_in_quad",
"ease_in_cubic",
@ -113,7 +111,6 @@ const char **ease_enums() {
"ease_in_elastic",
"ease_in_bounce",
"ease_linear",
"ease_inout_sine",
"ease_inout_quad",
"ease_inout_cubic",
@ -125,6 +122,8 @@ const char **ease_enums() {
"ease_inout_elastic",
"ease_inout_bounce",
"ease_nop",
"ease_linear",
"ease_inout_perlin",
0
};
@ -169,6 +168,10 @@ const char *ease_enum(unsigned mode) {
ENUM(EASE_BACK|EASE_INOUT);
ENUM(EASE_ELASTIC|EASE_INOUT);
ENUM(EASE_BOUNCE|EASE_INOUT);
ENUM(EASE_NOP);
ENUM(EASE_LINEAR);
ENUM(EASE_INOUT_PERLIN);
};*/
// ----------------------------------------------------------------------------
@ -182,12 +185,12 @@ tween_t tween() {
float tween_update(tween_t *tw, float dt) {
if (!array_count(tw->keyframes)) return 0.0f;
for (size_t i = 0; i < array_count(tw->keyframes) - 1; ++i) {
for( int i = 0, end = array_count(tw->keyframes) - 1; i < end; ++i ) {
tween_keyframe_t *kf1 = &tw->keyframes[i];
tween_keyframe_t *kf2 = &tw->keyframes[i + 1];
if (tw->time >= kf1->t && tw->time <= kf2->t) {
float localT = (tw->time - kf1->t) / (kf2->t - kf1->t);
float easedT = ease(localT, kf1->easing_mode);
float easedT = ease(localT, kf1->ease);
tw->result = mix3(kf1->v, kf2->v, easedT);
break;
}
@ -215,23 +218,19 @@ int tween_comp_keyframes(const void *a, const void *b) {
return (t1 > t2) - (t1 < t2);
}
void tween_keyframe_set(tween_t *tw, float t, int mode, vec3 v) {
tween_keyframe_t keyframe = { mode, t, v };
void tween_setkey(tween_t *tw, float t, vec3 v, unsigned mode) {
tween_keyframe_t keyframe = { t, v, mode };
array_push(tw->keyframes, keyframe);
array_sort(tw->keyframes, tween_comp_keyframes);
tw->duration = array_back(tw->keyframes)->t;
}
void tween_keyframe_unset(tween_t *tw, float t) { // @todo: untested
int id = -1;
for (int i = 0; i < array_count(tw->keyframes); i++) {
void tween_delkey(tween_t *tw, float t) { // @todo: untested
for( int i = 0, end = array_count(tw->keyframes); i < end; i++ ) {
if (tw->keyframes[i].t == t) {
id = i;
break;
array_erase_slow(tw->keyframes, i);
tw->duration = array_back(tw->keyframes)->t;
return;
}
}
if (id == -1) return;
array_erase_slow(tw->keyframes, id);
tw->duration = array_back(tw->keyframes)->t;
}

View File

@ -1,6 +1,7 @@
// ----------------------------------------------------------------------------
// ease
API float ease_nop(float t);
API float ease_linear(float t);
API float ease_out_sine(float t);
@ -39,7 +40,6 @@ API float ease_inout_bounce(float t);
API float ease_inout_perlin(float t);
enum EASE_FLAGS {
EASE_LINEAR,
EASE_SINE,
EASE_QUAD,
EASE_CUBIC,
@ -52,8 +52,14 @@ enum EASE_FLAGS {
EASE_BOUNCE,
EASE_IN,
EASE_INOUT = EASE_IN * 2,
EASE_OUT = 0,
EASE_INOUT = EASE_IN * 2,
EASE_NOP = EASE_INOUT | (EASE_BOUNCE + 1),
EASE_LINEAR,
EASE_INOUT_PERLIN,
EASE_NUM
};
API float ease(float t01, unsigned fn); // / 0-to-1
@ -68,9 +74,9 @@ API const char**ease_enums();
// tween
typedef struct tween_keyframe_t {
int easing_mode;
float t;
vec3 v;
unsigned ease;
} tween_keyframe_t;
typedef struct tween_t {
@ -82,9 +88,9 @@ typedef struct tween_t {
} tween_t;
API tween_t tween();
API void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
API void tween_delkey(tween_t *tw, float t);
API float tween_update(tween_t *tw, float dt);
API void tween_reset(tween_t *tw);
API void tween_destroy(tween_t *tw);
API void tween_keyframe_set(tween_t *tw, float t, int easing_mode, vec3 v);
API void tween_keyframe_unset(tween_t *tw, float t);

View File

@ -6,7 +6,7 @@ nk_text_width(struct nk_context *ctx, const char *str, unsigned len) {
const struct nk_style *style = &ctx->style;
const struct nk_user_font *f = style->font;
float pixels_width = f->width(f->userdata, f->height, str, len ? (int)len : (int)strlen(str));
return pixels_width;
return pixels_width + 10; // 10 -> internal widget padding
}
static nk_bool
@ -76,6 +76,7 @@ nk_hovered_text(struct nk_context *ctx, const char *str, int len,
int glyphs = strlen(TEXT) / 4 /*3:MD,4:MDI*/; CHOICE *= !!clicked_x * (CHOICE <= glyphs); } while(0)
// menu macros that work not only standalone but also contained within a panel or window
static int ui_using_v2_menubar = 0;
#define UI_MENU(N, ...) do { \
enum { MENUROW_HEIGHT = 25 }; \
int embedded = !!ui_ctx->current; \
@ -83,6 +84,7 @@ nk_hovered_text(struct nk_context *ctx, const char *str, int len,
if( embedded ) total_space = nk_window_get_bounds(ui_ctx), total_space.w -= 10; \
int created = !embedded && nk_begin(ui_ctx, "MENU_" STRINGIZE(__COUNTER__), nk_rect(0, 0, window_width(), UI_MENUROW_HEIGHT), NK_WINDOW_NO_SCROLLBAR); \
if ( embedded || created ) { \
ui_using_v2_menubar = 1; \
int align = NK_TEXT_LEFT, Nth = (N), ITEM_WIDTH = 30, span = 0; \
nk_menubar_begin(ui_ctx); \
nk_layout_row_begin(ui_ctx, NK_STATIC, MENUROW_HEIGHT, Nth); \
@ -391,7 +393,7 @@ int ui_menu_editbox(char *buf, int bufcap) {
}
int ui_has_menubar() {
return !!ui_items; // array_count(ui_items) > 0;
return ui_using_v2_menubar || !!ui_items; // array_count(ui_items) > 0;
}
static
@ -683,6 +685,7 @@ int ui_set_enable_(int enabled) {
off.text.color.a *= alpha;
#if 0
off.button.normal.data.color.a *= alpha;
off.button.hover.data.color.a *= alpha;
off.button.active.data.color.a *= alpha;
@ -700,7 +703,7 @@ int ui_set_enable_(int enabled) {
off.contextual_button.text_normal.a *= alpha;
off.contextual_button.text_hover.a *= alpha;
off.contextual_button.text_active.a *= alpha;
#endif
off.menu_button.normal.data.color.a *= alpha;
off.menu_button.hover.data.color.a *= alpha;
off.menu_button.active.data.color.a *= alpha;
@ -709,7 +712,7 @@ int ui_set_enable_(int enabled) {
off.menu_button.text_normal.a *= alpha;
off.menu_button.text_hover.a *= alpha;
off.menu_button.text_active.a *= alpha;
#if 0
off.option.normal.data.color.a *= alpha;
off.option.hover.data.color.a *= alpha;
off.option.active.data.color.a *= alpha;
@ -782,7 +785,7 @@ int ui_set_enable_(int enabled) {
off.progress.cursor_hover.data.color.a *= alpha;
off.progress.cursor_active.data.color.a *= alpha;
off.progress.cursor_border_color.a *= alpha;
#endif
off.property.normal.data.color.a *= alpha;
off.property.hover.data.color.a *= alpha;
off.property.active.data.color.a *= alpha;
@ -837,7 +840,7 @@ int ui_set_enable_(int enabled) {
off.edit.selected_hover.a *= alpha;
off.edit.selected_text_normal.a *= alpha;
off.edit.selected_text_hover.a *= alpha;
#if 0
off.chart.background.data.color.a *= alpha;
off.chart.border_color.a *= alpha;
off.chart.selected_color.a *= alpha;
@ -864,7 +867,7 @@ int ui_set_enable_(int enabled) {
off.tab.background.data.color.a *= alpha;
off.tab.border_color.a *= alpha;
off.tab.text.a *= alpha;
#endif
off.combo.normal.data.color.a *= alpha;
off.combo.hover.data.color.a *= alpha;
off.combo.active.data.color.a *= alpha;
@ -883,7 +886,7 @@ int ui_set_enable_(int enabled) {
off.combo.button.text_normal.a *= alpha;
off.combo.button.text_hover.a *= alpha;
off.combo.button.text_active.a *= alpha;
#if 0
off.window.fixed_background.data.color.a *= alpha;
off.window.background.a *= alpha;
off.window.border_color.a *= alpha;
@ -897,6 +900,7 @@ int ui_set_enable_(int enabled) {
off.window.header.normal.data.color.a *= alpha;
off.window.header.hover.data.color.a *= alpha;
off.window.header.active.data.color.a *= alpha;
#endif
}
static struct nk_input input;
if (!enabled) {

View File

@ -521,7 +521,7 @@ int window_frame_begin() {
ui_create();
#if !ENABLE_RETAIL
bool has_menu = 0; // ui_has_menubar();
bool has_menu = ui_has_menubar();
bool may_render_debug_panel = 1;
if( have_tools() ) {
@ -764,9 +764,9 @@ void window_title(const char *title_) {
if( !title[0] ) glfwSetWindowTitle(window, title);
}
void window_color(unsigned color) {
unsigned b = (color >> 0) & 255;
unsigned r = (color >> 0) & 255;
unsigned g = (color >> 8) & 255;
unsigned r = (color >> 16) & 255;
unsigned b = (color >> 16) & 255;
unsigned a = (color >> 24) & 255;
winbgcolor = vec4(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
}

File diff suppressed because it is too large Load Diff

View File

@ -117,6 +117,9 @@ API void *ui_handle();
#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 })
#line 0
#line 1 "engine/split/v4k_ds.c"
@ -124,9 +127,12 @@ API void *ui_handle();
// -----------------------------------------------------------------------------
// sort/less
int sort_64(const void *a, const void *b) {
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;
@ -834,7 +840,7 @@ const char *extract_utf32(const char *s, uint32_t *out) {
/**/ if( (s[0] & 0x80) == 0x00 ) return *out = (s[0]), s + 1;
else if( (s[0] & 0xe0) == 0xc0 ) return *out = (s[0] & 31) << 6 | (s[1] & 63), s + 2;
else if( (s[0] & 0xf0) == 0xe0 ) return *out = (s[0] & 15) << 12 | (s[1] & 63) << 6 | (s[2] & 63), s + 3;
else if( (s[0] & 0xf8) != 0xf0 ) return *out = (s[0] & 7) << 18 | (s[1] & 63) << 12 | (s[2] & 63) << 8 | (s[3] & 63), s + 4;
else if( (s[0] & 0xf8) == 0xf0 ) return *out = (s[0] & 7) << 18 | (s[1] & 63) << 12 | (s[2] & 63) << 8 | (s[3] & 63), s + 4;
return *out = 0, s + 0;
}
array(uint32_t) string32( const char *utf8 ) {
@ -7431,15 +7437,12 @@ static const char bm_mini_ttf[] = {
/*004ec0*/ 0x00,0x08,0x00,0x40,0x00,0x0a,0x00,0x00,0x00,0x84,0x00,0xde,0x00,0x01,0x00,0x01
};
static const unsigned bm_mini_ttf_length = (unsigned)sizeof(bm_mini_ttf);
// -----------------------------------------------------------------------------
// The following data tables are coming from Dear Imgui.
// Re-licensed under permission as MIT-0.
//
// @todo: 0x3100, 0x312F, FONT_TW, // Bopomofo
// 0xE000, 0xEB4C, FONT_EM, // Private use (emojis)
static const unsigned table_common[] = {
0x0020, 0x00FF, // Basic Latin + Latin Supplement
@ -7527,6 +7530,12 @@ static const unsigned table_middle_east[] = {
0
};
static const unsigned table_emoji[] = {
// 0xE000, 0xEB4C, // Private use (emojis)
0xE000, 0xF8FF, // Private use (emojis+webfonts)
0
};
// Store 2500 regularly used characters for Simplified Chinese. Table below covers 97.97% of all characters used during the month in July, 1987.
// [ref] https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8
static const unsigned short packed_table_chinese[] = { // starts with 0x4E00
@ -7751,18 +7760,18 @@ static font_t fonts[8] = {0};
static
void font_init() {
do_once {
font_face_from_mem(FONT_FACE1, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE2, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE3, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE4, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE5, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE6, bm_mini_ttf,0, 42.5f, 0);
font_face_from_mem(FONT_FACE1, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE2, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE3, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE4, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE5, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
font_face_from_mem(FONT_FACE6, bm_mini_ttf,countof(bm_mini_ttf), 42.5f, 0);
}
}
// Remap color within all existing color textures
void font_color(const char *tag, uint32_t color) {
do_once font_init();
font_init();
unsigned index = *tag - FONT_COLOR1[0];
if( index < FONT_MAX_COLORS ) {
@ -7780,7 +7789,7 @@ void font_color(const char *tag, uint32_t color) {
}
void font_scales(const char *tag, float h1, float h2, float h3, float h4, float h5, float h6) {
do_once font_init();
font_init();
unsigned index = *tag - FONT_FACE1[0];
if( index >= 8 ) return;
@ -7801,16 +7810,14 @@ void font_scales(const char *tag, float h1, float h2, float h3, float h4, float
// 1. Call stb_truetype.h routines to read and parse a .ttf file.
// 1. Create a bitmap that is uploaded to the gpu using opengl.
// 1. Calculate and save a bunch of useful variables and put them in the global font variable.
void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_len, float font_size, unsigned flags) {
const unsigned char *ttf_buffer = ttf_bufferv;
flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is always in
void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, float font_size, unsigned flags) {
unsigned index = *tag - FONT_FACE1[0];
if( index >= 8 ) return;
if( !ttf_buffer /*|| !ttf_len*/ ) return;
if( font_size <= 0 || font_size > 72 ) return;
if( !ttf_data || !ttf_len ) return;
flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is always in
font_t *f = &fonts[index];
f->initialized = 1;
@ -7831,8 +7838,8 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
f->scale[6] = 0.2500f; // H6
const char *vs_filename = 0, *fs_filename = 0;
const char *vs = vs_filename ? file_read(vs_filename) : mv_vs_source;
const char *fs = fs_filename ? file_read(fs_filename) : mv_fs_source;
const char *vs = vs_filename ? vfs_read(vs_filename) : mv_vs_source;
const char *fs = fs_filename ? vfs_read(fs_filename) : mv_fs_source;
f->program = shader(vs, fs, "vertexPosition,instanceGlyph", "outColor", NULL);
// figure out what ranges we're about to bake
@ -7851,6 +7858,7 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
array(uint64_t) sorted = 0;
if(flags & FONT_ASCII) { MERGE_TABLE(table_common); }
if(flags & FONT_EM) { MERGE_TABLE(table_emoji); }
if(flags & FONT_EU) { MERGE_TABLE(table_eastern_europe); }
if(flags & FONT_RU) { MERGE_TABLE(table_western_europe); }
if(flags & FONT_EL) { MERGE_TABLE(table_western_europe); }
@ -7863,8 +7871,8 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
if(flags & FONT_ZH) { MERGE_TABLE(table_chinese_japanese_common); MERGE_PACKED_TABLE(0x4E00, packed_table_chinese); } // zh-simplified
if(flags & FONT_ZH) { MERGE_TABLE(table_chinese_punctuation); } // both zh-simplified and zh-full
// if(flags & FONT_ZH) { MERGE_TABLE(table_chinese_full); } // zh-full
array_sort(sorted, sort_64);
array_unique(sorted, sort_64); // sort + unique pass
array_sort(sorted, less_64_ptr);
array_unique(sorted, less_64_ptr); // sort + unique pass
// pack and create bitmap
unsigned char *bitmap = (unsigned char*)MALLOC(f->height*f->width);
@ -7888,7 +7896,7 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
while( i < (num-1) && (sorted[i+1]-sorted[i]) == 1 ) end = sorted[++i];
//printf("(%d,%d)", (unsigned)begin, (unsigned)end);
if( stbtt_PackFontRange(&pc, ttf_buffer, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin) ) {
if( stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin) ) {
for( int j = begin; j <= end; ++j ) {
// unicode->index runtime lookup
f->cp2iter[ j ] = count;
@ -7901,11 +7909,13 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
stbtt_PackEnd(&pc);
f->num_glyphs = count;
assert( f->num_glyphs < charCount );
array_free(sorted);
// calculate vertical font metrics
stbtt_fontinfo info;
stbtt_InitFont(&info, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
stbtt_fontinfo info = {0};
stbtt_InitFont(&info, ttf_data, stbtt_GetFontOffsetForIndex(ttf_data,0));
int a, d, l;
float s = stbtt_ScaleForPixelHeight(&info, f->font_size);
@ -7971,7 +7981,7 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
// last chance to inspect the font atlases
if( flag("--font-debug") )
stbi_write_png(va("debug_font_atlas%d.png", index), f->width, f->height, 1, bitmap, 0);
stbi_write_png(va("font_debug%d.png", index), f->width, f->height, 1, bitmap, 0);
FREE(bitmap);
@ -8038,12 +8048,13 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
}
void font_face(const char *tag, const char *filename_ttf, float font_size, unsigned flags) {
do_once font_init();
font_init();
const char *buffer = //file_read(filename_ttf);
//if(!buffer) buffer =
vfs_read(filename_ttf);
font_face_from_mem(tag, buffer,0, font_size, flags);
int len;
const char *buffer = vfs_load(filename_ttf, &len);
if( !buffer ) buffer = file_load(filename_ttf, &len);
font_face_from_mem(tag, buffer,len, font_size, flags);
}
static
@ -8128,7 +8139,7 @@ void font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, float fact
// 1. draw the string
static
vec2 font_draw_ex(const char *text, vec2 offset, const char *col, void (*draw_cmd)(font_t *,const float *,int,float,vec2)) {
do_once font_init();
font_init();
// sanity checks
int len = strlen(text);
@ -8959,7 +8970,7 @@ int input_enum(const char *vk) {
static map(char*,int) m = 0;
do_once {
map_init_str(m);
#define k(VK) map_insert(m, STRINGIZE(KEY_##VK), KEY_##VK); map_insert(m, STRINGIZE(VK), KEY_##VK);
#define k(VK) map_find_or_add(m, STRINGIZE(VK), KEY_##VK); map_find_or_add(m, STRINGIZE(KEY_##VK), KEY_##VK);
k(ESC)
k(TICK) k(1) k(2) k(3) k(4) k(5) k(6) k(7) k(8) k(9) k(0) k(BS)
k(TAB) k(Q) k(W) k(E) k(R) k(T) k(Y) k(U) k(I) k(O) k(P)
@ -9161,6 +9172,7 @@ int ui_mouse() {
int ui_keyboard() {
char *keys[] = {
"F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12",
"ESC",
"TICK","1","2","3","4","5","6","7","8","9","0","BS",
"TAB","Q","W","E","R","T","Y","U","I","O","P",
@ -9169,25 +9181,23 @@ int ui_keyboard() {
"LCTRL","LALT","SPACE","RALT","RCTRL","<","V",">",
};
vec2i rows[] = {
vec2i(0,1),
vec2i(1,13),
vec2i(13,24),
vec2i(24,35),
vec2i(35,45),
vec2i(45,countof(keys)),
float rows[] = {
12,
1,
12,
11,
11,
10,
8
};
for( int i = 0; i < countof(rows); ++i ) {
int any = 0;
char *row = 0;
for( int j = rows[i].x; j < rows[i].y; ++j ) {
strcatf(&row, input(KEY_ESC + j) ? (any|=1, "[%s]") : " %s ", keys[j]);
for( int row = 0, k = 0; row < countof(rows); ++row ) {
static char *buf = 0; if(buf) *buf = 0;
for( int col = 0; col < rows[row]; ++col, ++k ) {
assert( input_enum(keys[k]) == input_enum(va("KEY_%s", keys[k])) );
strcatf(&buf, input(input_enum(keys[k])) ? "[%s]" : " %s ", keys[k]);
}
if(!any) ui_disable();
ui_label(row);
ui_enable();
FREE(row);
ui_label(buf);
}
return 0;
@ -10136,6 +10146,7 @@ AUTORUN {
// ----------------------------------------------------------------------------
// ease
float ease_nop(float t) { return 0; }
float ease_linear(float t) { return t; }
float ease_out_sine(float t) { return sinf(t*(C_PI*0.5f)); }
@ -10176,7 +10187,6 @@ float ease_inout_perlin(float t) { float t3=t*t*t,t4=t3*t,t5=t4*t; return 6*t5-1
float ease(float t01, unsigned mode) {
typedef float (*easing)(float);
easing modes[] = {
ease_linear,
ease_out_sine,
ease_out_quad,
ease_out_cubic,
@ -10188,7 +10198,6 @@ float ease(float t01, unsigned mode) {
ease_out_elastic,
ease_out_bounce,
ease_linear,
ease_in_sine,
ease_in_quad,
ease_in_cubic,
@ -10200,7 +10209,6 @@ float ease(float t01, unsigned mode) {
ease_in_elastic,
ease_in_bounce,
ease_linear,
ease_inout_sine,
ease_inout_quad,
ease_inout_cubic,
@ -10212,6 +10220,8 @@ float ease(float t01, unsigned mode) {
ease_inout_elastic,
ease_inout_bounce,
ease_nop,
ease_linear,
ease_inout_perlin,
};
return modes[clampi(mode, 0, countof(modes))](clampf(t01,0,1));
@ -10224,7 +10234,6 @@ float ease_pong_ping(float t, unsigned fn1, unsigned fn2) { return 1 - ease_ping
const char **ease_enums() {
static const char *list[] = {
"ease_linear",
"ease_out_sine",
"ease_out_quad",
"ease_out_cubic",
@ -10236,7 +10245,6 @@ const char **ease_enums() {
"ease_out_elastic",
"ease_out_bounce",
"ease_linear",
"ease_in_sine",
"ease_in_quad",
"ease_in_cubic",
@ -10248,7 +10256,6 @@ const char **ease_enums() {
"ease_in_elastic",
"ease_in_bounce",
"ease_linear",
"ease_inout_sine",
"ease_inout_quad",
"ease_inout_cubic",
@ -10260,6 +10267,8 @@ const char **ease_enums() {
"ease_inout_elastic",
"ease_inout_bounce",
"ease_nop",
"ease_linear",
"ease_inout_perlin",
0
};
@ -10304,6 +10313,10 @@ const char *ease_enum(unsigned mode) {
ENUM(EASE_BACK|EASE_INOUT);
ENUM(EASE_ELASTIC|EASE_INOUT);
ENUM(EASE_BOUNCE|EASE_INOUT);
ENUM(EASE_NOP);
ENUM(EASE_LINEAR);
ENUM(EASE_INOUT_PERLIN);
};*/
// ----------------------------------------------------------------------------
@ -10317,12 +10330,12 @@ tween_t tween() {
float tween_update(tween_t *tw, float dt) {
if (!array_count(tw->keyframes)) return 0.0f;
for (size_t i = 0; i < array_count(tw->keyframes) - 1; ++i) {
for( int i = 0, end = array_count(tw->keyframes) - 1; i < end; ++i ) {
tween_keyframe_t *kf1 = &tw->keyframes[i];
tween_keyframe_t *kf2 = &tw->keyframes[i + 1];
if (tw->time >= kf1->t && tw->time <= kf2->t) {
float localT = (tw->time - kf1->t) / (kf2->t - kf1->t);
float easedT = ease(localT, kf1->easing_mode);
float easedT = ease(localT, kf1->ease);
tw->result = mix3(kf1->v, kf2->v, easedT);
break;
}
@ -10350,25 +10363,21 @@ int tween_comp_keyframes(const void *a, const void *b) {
return (t1 > t2) - (t1 < t2);
}
void tween_keyframe_set(tween_t *tw, float t, int mode, vec3 v) {
tween_keyframe_t keyframe = { mode, t, v };
void tween_setkey(tween_t *tw, float t, vec3 v, unsigned mode) {
tween_keyframe_t keyframe = { t, v, mode };
array_push(tw->keyframes, keyframe);
array_sort(tw->keyframes, tween_comp_keyframes);
tw->duration = array_back(tw->keyframes)->t;
}
void tween_keyframe_unset(tween_t *tw, float t) { // @todo: untested
int id = -1;
for (int i = 0; i < array_count(tw->keyframes); i++) {
void tween_delkey(tween_t *tw, float t) { // @todo: untested
for( int i = 0, end = array_count(tw->keyframes); i < end; i++ ) {
if (tw->keyframes[i].t == t) {
id = i;
break;
array_erase_slow(tw->keyframes, i);
tw->duration = array_back(tw->keyframes)->t;
return;
}
}
if (id == -1) return;
array_erase_slow(tw->keyframes, id);
tw->duration = array_back(tw->keyframes)->t;
}
#line 0
@ -13244,12 +13253,12 @@ const char* symbol(const char *s) {
void type_inscribe(const char *TY,unsigned TYsz,const char *infos) {
reflect_init();
unsigned TYid = intern(TY = symbol(TY));
map_find_or_add(reflects, TYid, ((reflect_t){TYid, 0, TYsz, TY, infos}));
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(E));
map_find_or_add(reflects, Eid, ((reflect_t){Eid,0, Eval, E,infos}));
map_find_or_add(reflects, Eid, ((reflect_t){Eid,0, Eval, STRDUP(E),infos})); // @leak
}
unsigned enum_find(const char *E) {
reflect_init();
@ -13259,7 +13268,7 @@ unsigned enum_find(const char *E) {
void function_inscribe(const char *F,void *func,const char *infos) {
reflect_init();
unsigned Fid = intern(F = symbol(F));
map_find_or_add(reflects, Fid, ((reflect_t){Fid,0, 0, F,infos, func}));
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) {
@ -13270,18 +13279,31 @@ void *function_find(const char *F) {
void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
reflect_init();
unsigned Tid = intern(T = symbol(T));
map_find_or_add(reflects, Tid, ((reflect_t){Tid, OBJTYPEid, Tsz, T, infos}));
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) {
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(T));
unsigned Mid = intern(M = symbol(M));
type = symbol(type);
map_find_or_add(reflects, (Mid<<16)|Tid, ((reflect_t){Mid, 0, Msz, M, infos, NULL, Tid, 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);
array_push(*found, ((reflect_t){Mid, 0, Msz, M, infos, NULL, Tid, type, bytes }));
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();
@ -13312,9 +13334,17 @@ void ui_reflect_(const reflect_t *R, const char *filter, int mask) {
if( buf ) *buf = '\0';
struct nk_context *ui_ctx = (struct nk_context *)ui_handle();
/*for ui_push_hspace(16)*/ {
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)) ui_reflect_(it,filter,'M'); }
/**/ 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));
@ -18269,7 +18299,7 @@ void ddraw_flush_projview(mat44 proj, mat44 view) {
int count = array_count(list);
if(!count) continue;
// color
vec3 rgbf = {((rgb>>16)&255)/255.f,((rgb>>8)&255)/255.f,((rgb>>0)&255)/255.f};
vec3 rgbf = {((rgb>>0)&255)/255.f,((rgb>>8)&255)/255.f,((rgb>>16)&255)/255.f};
glUniform3fv(dd_u_color, GL_TRUE, &rgbf.x);
// config vertex data
glBufferData(GL_ARRAY_BUFFER, count * 3 * 4, list, GL_STATIC_DRAW);
@ -18301,7 +18331,7 @@ void ddraw_flush_projview(mat44 proj, mat44 view) {
int count = array_count(list);
if(!count) continue;
// color
vec3 rgbf = {((rgb>>16)&255)/255.f,((rgb>>8)&255)/255.f,((rgb>>0)&255)/255.f};
vec3 rgbf = {((rgb>>0)&255)/255.f,((rgb>>8)&255)/255.f,((rgb>>16)&255)/255.f};
glUniform3fv(dd_u_color, GL_TRUE, &rgbf.x);
// config vertex data
glBufferData(GL_ARRAY_BUFFER, count * 3 * 4, list, GL_STATIC_DRAW);
@ -20590,9 +20620,9 @@ void tty_color(unsigned color) {
#endif
if( color ) {
// if( color == RED ) alert("break on error message (RED)"), breakpoint(); // debug
unsigned r = (color >> 16) & 255;
unsigned r = (color >> 0) & 255;
unsigned g = (color >> 8) & 255;
unsigned b = (color >> 0) & 255;
unsigned b = (color >> 16) & 255;
// 24-bit console ESC[ … 38;2;<r>;<g>;<b> … m Select RGB foreground color
// 256-color console ESC[38;5;<fgcode>m
// 0x00-0x07: standard colors (as in ESC [ 30..37 m)
@ -21133,7 +21163,7 @@ nk_text_width(struct nk_context *ctx, const char *str, unsigned len) {
const struct nk_style *style = &ctx->style;
const struct nk_user_font *f = style->font;
float pixels_width = f->width(f->userdata, f->height, str, len ? (int)len : (int)strlen(str));
return pixels_width;
return pixels_width + 10; // 10 -> internal widget padding
}
static nk_bool
@ -21203,6 +21233,7 @@ nk_hovered_text(struct nk_context *ctx, const char *str, int len,
int glyphs = strlen(TEXT) / 4 /*3:MD,4:MDI*/; CHOICE *= !!clicked_x * (CHOICE <= glyphs); } while(0)
// menu macros that work not only standalone but also contained within a panel or window
static int ui_using_v2_menubar = 0;
#define UI_MENU(N, ...) do { \
enum { MENUROW_HEIGHT = 25 }; \
int embedded = !!ui_ctx->current; \
@ -21210,6 +21241,7 @@ nk_hovered_text(struct nk_context *ctx, const char *str, int len,
if( embedded ) total_space = nk_window_get_bounds(ui_ctx), total_space.w -= 10; \
int created = !embedded && nk_begin(ui_ctx, "MENU_" STRINGIZE(__COUNTER__), nk_rect(0, 0, window_width(), UI_MENUROW_HEIGHT), NK_WINDOW_NO_SCROLLBAR); \
if ( embedded || created ) { \
ui_using_v2_menubar = 1; \
int align = NK_TEXT_LEFT, Nth = (N), ITEM_WIDTH = 30, span = 0; \
nk_menubar_begin(ui_ctx); \
nk_layout_row_begin(ui_ctx, NK_STATIC, MENUROW_HEIGHT, Nth); \
@ -21518,7 +21550,7 @@ int ui_menu_editbox(char *buf, int bufcap) {
}
int ui_has_menubar() {
return !!ui_items; // array_count(ui_items) > 0;
return ui_using_v2_menubar || !!ui_items; // array_count(ui_items) > 0;
}
static
@ -21810,6 +21842,7 @@ int ui_set_enable_(int enabled) {
off.text.color.a *= alpha;
#if 0
off.button.normal.data.color.a *= alpha;
off.button.hover.data.color.a *= alpha;
off.button.active.data.color.a *= alpha;
@ -21827,7 +21860,7 @@ int ui_set_enable_(int enabled) {
off.contextual_button.text_normal.a *= alpha;
off.contextual_button.text_hover.a *= alpha;
off.contextual_button.text_active.a *= alpha;
#endif
off.menu_button.normal.data.color.a *= alpha;
off.menu_button.hover.data.color.a *= alpha;
off.menu_button.active.data.color.a *= alpha;
@ -21836,7 +21869,7 @@ int ui_set_enable_(int enabled) {
off.menu_button.text_normal.a *= alpha;
off.menu_button.text_hover.a *= alpha;
off.menu_button.text_active.a *= alpha;
#if 0
off.option.normal.data.color.a *= alpha;
off.option.hover.data.color.a *= alpha;
off.option.active.data.color.a *= alpha;
@ -21909,7 +21942,7 @@ int ui_set_enable_(int enabled) {
off.progress.cursor_hover.data.color.a *= alpha;
off.progress.cursor_active.data.color.a *= alpha;
off.progress.cursor_border_color.a *= alpha;
#endif
off.property.normal.data.color.a *= alpha;
off.property.hover.data.color.a *= alpha;
off.property.active.data.color.a *= alpha;
@ -21964,7 +21997,7 @@ int ui_set_enable_(int enabled) {
off.edit.selected_hover.a *= alpha;
off.edit.selected_text_normal.a *= alpha;
off.edit.selected_text_hover.a *= alpha;
#if 0
off.chart.background.data.color.a *= alpha;
off.chart.border_color.a *= alpha;
off.chart.selected_color.a *= alpha;
@ -21991,7 +22024,7 @@ int ui_set_enable_(int enabled) {
off.tab.background.data.color.a *= alpha;
off.tab.border_color.a *= alpha;
off.tab.text.a *= alpha;
#endif
off.combo.normal.data.color.a *= alpha;
off.combo.hover.data.color.a *= alpha;
off.combo.active.data.color.a *= alpha;
@ -22010,7 +22043,7 @@ int ui_set_enable_(int enabled) {
off.combo.button.text_normal.a *= alpha;
off.combo.button.text_hover.a *= alpha;
off.combo.button.text_active.a *= alpha;
#if 0
off.window.fixed_background.data.color.a *= alpha;
off.window.background.a *= alpha;
off.window.border_color.a *= alpha;
@ -22024,6 +22057,7 @@ int ui_set_enable_(int enabled) {
off.window.header.normal.data.color.a *= alpha;
off.window.header.hover.data.color.a *= alpha;
off.window.header.active.data.color.a *= alpha;
#endif
}
static struct nk_input input;
if (!enabled) {
@ -23872,14 +23906,16 @@ void (ui_profiler)() {
double fps = window_fps();
profile_setstat("Render.num_fps", fps);
// draw fps-meter: 300 samples, [0..70] range each, 70px height plot.
nk_layout_row_dynamic(ui_ctx, 70, 1);
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 ) {
@ -23900,6 +23936,7 @@ void (ui_profiler)() {
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) ) {
@ -24671,7 +24708,7 @@ int window_frame_begin() {
ui_create();
#if !ENABLE_RETAIL
bool has_menu = 0; // ui_has_menubar();
bool has_menu = ui_has_menubar();
bool may_render_debug_panel = 1;
if( have_tools() ) {
@ -24914,9 +24951,9 @@ void window_title(const char *title_) {
if( !title[0] ) glfwSetWindowTitle(window, title);
}
void window_color(unsigned color) {
unsigned b = (color >> 0) & 255;
unsigned r = (color >> 0) & 255;
unsigned g = (color >> 8) & 255;
unsigned r = (color >> 16) & 255;
unsigned b = (color >> 16) & 255;
unsigned a = (color >> 24) & 255;
winbgcolor = vec4(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
}
@ -25235,6 +25272,9 @@ obj_vtable_null(draw, int );
obj_vtable_null(lerp, int );
obj_vtable_null(edit, int ); // OSC cmds: argc,argv "undo","redo","cut","copy","paste","edit","view","menu"
obj_vtable_null(menu, int );
obj_vtable_null(aabb, int );
obj_vtable_null(icon, char* );
// ----------------------------------------------------------------------------
@ -27152,6 +27192,18 @@ int ui_debug() {
#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.
@ -27239,18 +27291,6 @@ int ui_debug() {
EDITOR_UI_COLLAPSE(ICON_MD_MOVIE " FXs", "Debug.FXs") {
ui_fxs();
}
EDITOR_UI_COLLAPSE(ICON_MD_VIEW_QUILT " UI", "Debug.UI") {
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_SAVINGS " Budgets", "Debug.Budgets") {

View File

@ -104,7 +104,7 @@ extern "C" {
#endif
#ifndef ENABLE_PROFILER
#define ENABLE_PROFILER ifdef(debug, 1, 0) ///+
#define ENABLE_PROFILER ifdef(retail, 0, 1) ///+
#endif
#ifndef ENABLE_SELFIES
@ -514,11 +514,6 @@ typedef char bool;
// data structures and utils: array, set, map, hash, sort.
// - rlyeh, public domain
// -----------------------------------------------------------------------------
// sort
API int sort_64(const void *a, const void *b);
// -----------------------------------------------------------------------------
// less
@ -527,6 +522,12 @@ 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
@ -1233,6 +1234,7 @@ API void print44( float *m );
// ----------------------------------------------------------------------------
// ease
API float ease_nop(float t);
API float ease_linear(float t);
API float ease_out_sine(float t);
@ -1271,7 +1273,6 @@ API float ease_inout_bounce(float t);
API float ease_inout_perlin(float t);
enum EASE_FLAGS {
EASE_LINEAR,
EASE_SINE,
EASE_QUAD,
EASE_CUBIC,
@ -1284,8 +1285,14 @@ enum EASE_FLAGS {
EASE_BOUNCE,
EASE_IN,
EASE_INOUT = EASE_IN * 2,
EASE_OUT = 0,
EASE_INOUT = EASE_IN * 2,
EASE_NOP = EASE_INOUT | (EASE_BOUNCE + 1),
EASE_LINEAR,
EASE_INOUT_PERLIN,
EASE_NUM
};
API float ease(float t01, unsigned fn); // / 0-to-1
@ -1300,9 +1307,9 @@ API const char**ease_enums();
// tween
typedef struct tween_keyframe_t {
int easing_mode;
float t;
vec3 v;
unsigned ease;
} tween_keyframe_t;
typedef struct tween_t {
@ -1314,12 +1321,12 @@ typedef struct tween_t {
} tween_t;
API tween_t tween();
API void tween_setkey(tween_t *tw, float t, vec3 v, unsigned easing_mode);
API void tween_delkey(tween_t *tw, float t);
API float tween_update(tween_t *tw, float dt);
API void tween_reset(tween_t *tw);
API void tween_destroy(tween_t *tw);
API void tween_keyframe_set(tween_t *tw, float t, int easing_mode, vec3 v);
API void tween_keyframe_unset(tween_t *tw, float t);
#line 0
#line 1 "engine/split/v4k_ai.h"
@ -2468,8 +2475,7 @@ 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_ESC,
KEY_TICK, KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9,KEY_0, KEY_BS,
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,
@ -2886,9 +2892,13 @@ void* obj_free(void *o);
#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 EXTEND obj_extend
#define obj_extend(T,func) (obj_##func[OBJTYPE(T)] = (void*)T##_##func)
#define obj_method(method,o,...) (obj_##method[((obj*)(o))->objtype](o,##__VA_ARGS__)) // (obj_##method[((obj*)(o))->objtype]((o), ##__VA_ARGS__))
@ -2915,6 +2925,7 @@ API extern int (*obj_draw[256])(); ///-
API extern int (*obj_lerp[256])(); ///-
API extern int (*obj_edit[256])(); ///-
API extern int (*obj_aabb[256])(); ///-
// ----------------------------------------------------------------------------
// core
@ -3179,23 +3190,13 @@ API unsigned bgraf( float b, float g, float r, float a );
API unsigned alpha( unsigned rgba );
#define RGBX(rgb,x) ( ((rgb)&0xFFFFFF) | (((unsigned)(x))<<24) )
#define RGB3(r,g,b) ( (255<<24) | ((r)<<16) | ((g)<<8) | (b) )
#define RGB4(r,g,b,a) RGBX(RGB3(r,g,b),a)
#define RGB3(r,g,b) ( (255<<24) | ((b)<<16) | ((g)<<8) | (r) )
#define RGB4(r,g,b,a) ( ((a)<<24) | ((b)<<16) | ((g)<<8) | (r) )
#define BLACK RGBX(0x000000,255)
#define WHITE RGBX(0xFFF1E8,255)
#define WHITE RGBX(0xE8F1FF,255)
#if 0
#define RED RGBX(0xFF004D,255)
#define GREEN RGBX(0x00B543,255)
#define BLUE RGBX(0x065AB5,255)
#define ORANGE RGBX(0xFF6C24,255)
#define CYAN RGBX(0x29ADFF,255)
#define PURPLE RGBX(0x7E2553,255)
#define YELLOW RGBX(0xFFEC27,255)
#define GRAY RGBX(0x725158,255)
#else
#define RED RGB3( 255,48,48 )
#define RED RGB3( 255, 0,48 )
#define GREEN RGB3( 144,255,48 )
#define CYAN RGB3( 0,192,255 )
#define ORANGE RGB3( 255,144,48 )
@ -3206,8 +3207,7 @@ API unsigned alpha( unsigned rgba );
#define PINK RGB3( 255,48,144 )
#define AQUA RGB3( 48,255,144 )
#define BLUE RGBX(0x065AB5,255)
#endif
#define BLUE RGBX(0xB55A06,255)
// -----------------------------------------------------------------------------
// images

View File

@ -1,4 +1,4 @@
// playground tests for FWK
// playground tests for V4K
// - rlyeh, public domain
//
// # quickstart

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

View File

@ -1,56 +1,8 @@
// ## Editor long-term plan
// - editor = tree of nodes. levels and objects are nodes, and their widgets are also nodes
// - you can perform actions on nodes, with or without descendants, top-bottom or bottom-top
// - these operations include load/save, undo/redo, reset, play/render, ddraw, etc
// - nodes are saved to disk as a filesystem layout: parents are folders, and leafs are files
// - network replication can be done by external tools by comparing the filesystems and by sending the resulting diff zipped
//
// ## Editor roadmap
// - Gizmos✱, scene tree, property editor✱, load/save✱, undo/redo✱, copy/paste, on/off (vis,tick,ddraw,log), vcs.
// - Scenenode pass: node singleton display, node console, node labels, node outlines✱.<!-- node == gameobj ? -->
// - Render pass: billboards✱, materials, un/lit, cast shadows, wireframe, skybox✱/mie✱, fog/atmosphere
// - Level pass: volumes, triggers, platforms, level streaming, collide✱, physics
// - Edit pass: Procedural content, brushes, noise and CSG.
// - GUI pass: timeline and data tracks, node graphs. <!-- worthy: will be reused into materials, animgraphs and blueprints -->
// ## Alt plan
// editor is a database + window/tile manager + ui toolkit; all network driven.
// to be precise, editor is a dumb app and ...
// - does not know a thing about what it stores.
// - does not know how to render the game graphics.
// - does not know how to run the game logic.
//
// the editor will create a canvas for your game to render.
// your game will be responsible to tick the logic and render the window inside the editor.
//
// that being said, editor...
// - can store datas hierarchically.
// - can perform diffs and merges, and version the datas into repositories.
// - can be instructed to render UI on top of game and window views.
// - can download new .natvis and plugins quickly.
// - can dump whole project in a filesystem form (zip).
// - editor reflects database contents up-to-date.
// - database can be queried and modified via OSC(UDP) commands.
// editor database uses one table, and stores two kind of payload types:
// - classes: defines typename and dna. class names are prefixed by '@'
// - instances: defines typename and datas. instance names are as-is, not prefixed.
//
// every save contains 5Ws: what, who, when, where, how,
// every save can be diffed/merged.
// ----------------------------------------------------------------------------
#define COOK_ON_DEMAND 1 // @fixme: these directives should be on client, not within v4k.dll
#define ENABLE_AUTOTESTS 1
#define V4K_IMPLEMENTATION
#include "v4k.c"
#include "3rd_icon_mdi.h"
//#include "objtests.h"
#include "editor3.h"
#define EXTEND obj_extend
int editor_timeline();
#include "v4k.h"
#include "objtests.h"
// ----------------------------------------------------------------------------
@ -113,796 +65,119 @@ static __thread char mpoutbuf[256];
#define UNPACKMSG(ptr,fmt,...) (mpin = (char*)ptr, mpinlen = strlen(ptr), mpout = mpoutbuf, mpoutlen = sizeof(mpoutbuf), mpoutlen = cobs_decode(mpin, mpinlen, mpout, mpoutlen), msgunpack_new(mpout, mpoutlen) && msgunpack(fmt, __VA_ARGS__))
#endif
// ----------------------------------------------------------------------------
array(void*) editor_persist_kv;
#define editor_new_property(property_name,T,defaults) \
typedef map(void*,T) editor_##property_name##_map_t; \
editor_##property_name##_map_t *editor_##property_name##_map() { \
static editor_##property_name##_map_t map = 0; do_once map_init_ptr(map); \
return &map; \
} \
T editor_##property_name(const void *obj) { \
return *map_find_or_add(*editor_##property_name##_map(), (void*)obj, ((T) defaults)); \
} \
void editor_set##property_name(const void *obj, T value) { \
*map_find_or_add(*editor_##property_name##_map(), (void*)obj, ((T) value)) = ((T) value); \
} \
void editor_alt##property_name(const void *obj) { \
T* found = map_find_or_add(*editor_##property_name##_map(), (void*)obj, ((T) defaults)); \
*found = (T)(uintptr_t)!(*found); \
} \
void editor_no##property_name(void *obj) { \
T* found = map_find_or_add(*editor_##property_name##_map(), (void*)obj, ((T) defaults)); \
map_erase(*editor_##property_name##_map(), (void*)obj); \
} \
AUTORUN { array_push(editor_persist_kv, #T); array_push(editor_persist_kv, editor_##property_name##_map()); }
editor_new_property(open, int, 0);
editor_new_property(selected, int, 0);
editor_new_property(changed, int, 0);
editor_new_property(bookmarked, int, 0);
editor_new_property(visible, int, 0);
editor_new_property(script, int, 0);
editor_new_property(event, int, 0);
editor_new_property(iconinstance, char*, 0);
editor_new_property(iconclass, char*, 0);
editor_new_property(treeoffsety, int, 0);
// new prop: breakpoint: request to break on any given node
void editor_no_properties(void *o) {
editor_noopen(o);
editor_noselected(o);
editor_nochanged(o);
editor_nobookmarked(o);
editor_novisible(o);
editor_noscript(o);
editor_noevent(o);
editor_noiconinstance(o);
editor_noiconclass(o);
editor_notreeoffsety(o);
}
void editor_load_on_boot(void) {
puts("@todo: load editor");
}
void editor_save_on_quit(void) {
puts("@todo: save editor");
}
AUTORUN {
editor_load_on_boot();
(atexit)(editor_save_on_quit);
}
// ----------------------------------------------------------------------------
typedef int(*subeditor)();
struct editor_t {
// time
unsigned frame;
double t, dt, slomo;
// controls
int attached;
int active; // focus? does_grabinput instead?
int key;
vec2 mouse; // 2d coord for ray/picking
bool gamepad; // mask instead? |1|2|4|8
int hz;
int hz_mid;
int hz_low;
int filter;
bool powersave;
bool lit;
bool ddraw;
// event root nodes
obj* root;
obj* init;
obj* tick;
obj* draw;
obj* edit;
obj* quit;
// all of them
array(obj*) objs; // @todo:set() world?
array(char*) cmds;
// subeditors
array(subeditor) subeditors;
} editor = {
.active = 1,
.gamepad = 1,
.hz = 60,
.hz_mid = 18,
.hz_low = 5,
.lit = 1,
.ddraw = 1,
};
bool editor_active() {
return ui_hover() || ui_active() || gizmo_active() ? editor.active : 0;
}
int editor_filter() {
if( editor.filter ) {
if (nk_begin(ui_ctx, "Filter", nk_rect(window_width()-window_width()*0.33,32, window_width()*0.33, 40),
NK_WINDOW_NO_SCROLLBAR)) {
char *bak = ui_filter; ui_filter = 0;
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &bak);
ui_filter = bak;
if( input(KEY_ESC) || ( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 )) {
if( ui_filter ) ui_filter[0] = '\0';
editor.filter = 0;
}
}
nk_end(ui_ctx);
}
return editor.filter;
}
static
int editor_select_(void *o, const char *mask) {
int matches = 0;
int off = mask[0] == '!', inv = mask[0] == '~';
int match = strmatchi(obj_type(o), mask+off+inv) || strmatchi(obj_name(o), mask+off+inv);
if( match ) {
editor_setselected(o, inv ? editor_selected(o) ^ 1 : !off);
++matches;
}
for each_objchild(o, obj*, oo) {
matches += editor_select_(oo, mask);
}
return matches;
}
void editor_select(const char *mask) {
for each_array( editor.objs, obj*, o )
editor_select_(o, mask);
}
void editor_unselect() { // same than editor_select("!**");
for each_map_ptr(*editor_selected_map(), void*,o, int, k) {
if( *k ) *k = 0;
}
}
static obj* active_ = 0;
static void editor_selectgroup_(obj *o, obj *first, obj *last) {
// printf("%s (looking for %s in [%s..%s])\n", obj_name(o), active_ ? obj_name(active_) : "", obj_name(first), obj_name(last));
if( !active_ ) if( o == first || o == last ) active_ = o == first ? last : first;
if( active_ ) editor_setselected(o, 1);
if( o == active_ ) active_ = 0;
for each_objchild(o, obj*, oo) {
editor_selectgroup_(oo, first, last);
}
}
void editor_selectgroup(obj *first, obj *last) {
if( last ) {
if( !first ) first = array_count(editor.objs) ? editor.objs[0] : NULL;
if( !first ) editor_setselected(last, 1);
else {
active_ = 0;
for each_array(editor.objs,obj*,o) {
editor_selectgroup_(o, first, last);
}
}
}
}
static obj *find_any_selected_(obj *o) {
if( editor_selected(o) ) return o;
for each_objchild(o,obj*,oo) {
obj *ooo = find_any_selected_(oo);
if( ooo )
return ooo;
}
return 0;
}
void* editor_first_selected() {
for each_array(editor.objs,obj*,o) {
obj *oo = find_any_selected_(o);
// if( oo ) printf("1st found: %s\n", obj_name(oo));
if( oo ) return oo;
}
return 0;
}
static obj *find_last_selected_(obj *o) {
void *last = 0;
if( editor_selected(o) ) last = o;
for each_objchild(o,obj*,oo) {
obj *ooo = find_last_selected_(oo);
if( ooo )
last = ooo;
}
return last;
}
void* editor_last_selected() {
void *last = 0;
for each_array(editor.objs,obj*,o) {
obj *oo = find_last_selected_(o);
// if( oo ) printf("last found: %s\n", obj_name(oo));
if( oo ) last = oo;
}
return last;
}
// ----------------------------------------------------------------------------------------
void editor_watch(const void *o) {
array_push(editor.objs, (obj*)o);
obj_push(o); // save state
}
void* editor_spawn(const char *ini) { // deprecate?
obj *o = obj_make(ini);
editor_watch(o);
return o;
}
void editor_spawn1() {
obj *selected = editor_first_selected();
obj *o = selected ? obj_make(obj_saveini(selected)) : obj_new(obj);
if( selected ) obj_attach(selected, o), editor_setopen(selected, 1);
else
editor_watch(o);
editor_unselect();
editor_setselected(o, 1);
}
typedef set(obj*) set_objp_t;
static
void editor_glob_recurse(set_objp_t*list, obj *o) {
set_find_or_add(*list, o);
for each_objchild(o,obj*,oo) {
editor_glob_recurse(list, oo);
}
}
void editor_destroy_selected() {
set_objp_t list = 0;
set_init_ptr(list);
for each_map_ptr(*editor_selected_map(), obj*,o, int,selected) {
if( *selected ) { editor_glob_recurse(&list, *o); }
}
for each_set(list, obj*, o) {
obj_detach(o);
}
for each_set(list, obj*, o) {
// printf("deleting %p %s\n", o, obj_name(o));
// remove from watched items
for (int i = 0, end = array_count(editor.objs); i < end; ++i) {
if (editor.objs[i] == o) {
editor.objs[i] = 0;
array_erase_slow(editor.objs, i);
--end;
--i;
}
}
// delete properties + obj
editor_no_properties(o);
obj_free(o);
}
set_free(list);
}
void editor_inspect(obj *o) {
ui_section(va("%s (%s)", obj_type(o), obj_name(o)));
if( obj_edit[obj_typeid(o)] ) {
obj_edit(o);
}
for each_objmember(o,TYPE,NAME,PTR) {
if( !editor_changed(PTR) ) {
obj_push(o);
}
ui_label_icon_highlight = editor_changed(PTR); // @hack: remove ui_label_icon_highlight hack
char *label = va(ICON_MD_UNDO "%s", NAME);
int changed = 0;
/**/ if( !strcmp(TYPE,"float") ) changed = ui_float(label, PTR);
else if( !strcmp(TYPE,"vec2") ) changed = ui_float2(label, PTR);
else if( !strcmp(TYPE,"vec3") ) changed = ui_float3(label, PTR);
else if( !strcmp(TYPE,"vec4") ) changed = ui_float4(label, PTR);
else if( !strcmp(TYPE,"color") ) changed = ui_color4(label, PTR);
else if( !strcmp(TYPE,"char*") ) changed = ui_string(label, PTR);
else ui_label2(label, va("(%s)", TYPE)); // INFO instead of (TYPE)?
if( changed ) {
editor_setchanged(PTR, 1);
}
if( ui_label_icon_highlight )
if( ui_label_icon_clicked_L.x >= 6 && ui_label_icon_clicked_L.x <= 26 ) { // @hack: if clicked on UNDO icon (1st icon)
editor_setchanged(PTR, 0);
}
if( !editor_changed(PTR) ) {
obj_pop(o);
}
}
}
enum {
TREE_RECURSE = 1,
TREE_SELECTION = 2,
TREE_CHECKBOX = 4,
TREE_INDENT = 8,
TREE_ALL = ~0u
};
static
void editor_tree_(obj *o, unsigned flags) {
static unsigned tabs = ~0u;
++tabs;
if( o ) {
unsigned do_tags = 1;
unsigned do_indent = !!(flags & TREE_INDENT);
unsigned do_checkbox = !!(flags & TREE_CHECKBOX);
unsigned do_recurse = !!(flags & TREE_RECURSE);
unsigned do_selection = !!(flags & TREE_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 treeoffset = {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;
treeoffset = vec2i(bounds.x+offsx+lenx-spacx,bounds.y+offsy);
editor_settreeoffsety(o, treeoffset.y);
for( obj *p = obj_parent(o); p ; p = 0 )
nk_stroke_line(canvas, treeoffset.x-6,treeoffset.y, treeoffset.x-spacx,treeoffset.y, thickness, color),
nk_stroke_line(canvas, treeoffset.x-spacx,treeoffset.y,treeoffset.x-spacx,editor_treeoffsety(p)+4, thickness, color);
}
if( ui_contextual() ) {
API int editor_send(const char *);
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_tree_(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,treeoffset.x-bounds.x+UI_ICON_FONTSIZE/2,bounds.h })) ) {
editor_altopen( o );
}
}
}
--tabs;
}
void editor_tree() {
// #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("tree.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 "Bookmark.toggles", "DEBUG:BOOKMARK")) {
for each_array( bookmarks, obj*, o )
editor_tree_( o, TREE_ALL & ~(TREE_RECURSE|TREE_INDENT|TREE_CHECKBOX) );
ui_collapse_end();
}
array_free(bookmarks);
editor_tree_( editor.root, TREE_ALL );
for each_array( editor.objs, obj*, o )
editor_tree_( o, TREE_ALL );
ui_separator();
// edit selection
for each_map(*editor_selected_map(), void*,o, int, k) {
if( k ) editor_inspect(o);
}
}
// ----------------------------------------------------------------------------------------
// tty
static thread_mutex_t *console_lock;
static array(char*) editor_jobs;
int editor_send(const char *cmd) { // return job-id
int skip = strspn(cmd, " \t\r\n");
char *buf = STRDUP(cmd + skip);
strswap(buf, "\r\n", "");
int jobid;
do_threadlock(console_lock) {
array_push(editor_jobs, buf);
jobid = array_count(editor_jobs) - 1;
}
return jobid;
}
const char* editor_recv(int jobid, double timeout_ss) {
char *answer = 0;
while(!answer && timeout_ss >= 0 ) {
do_threadlock(console_lock) {
if( editor_jobs[jobid][0] == '\0' )
answer = editor_jobs[jobid];
}
timeout_ss -= 0.1;
if( timeout_ss > 0 ) sleep_ms(100); // thread_yield()
}
return answer + 1;
}
typedef struct editor_bind_t {
const char *command;
const char *bindings;
void (*fn)();
} editor_bind_t;
array(editor_bind_t) editor_binds;
#define EDITOR_BIND(CMD,KEYS,...) void macro(editor_bind_fn_)() { __VA_ARGS__ }; AUTORUN { array_push(editor_binds, ((editor_bind_t){CMD,KEYS,macro(editor_bind_fn_)}) ); }
EDITOR_BIND("play", "held(CTRL) & down(SPC)", { window_pause(0); /* if(!editor.slomo) editor.active = 0; */ editor.slomo = 1; } );
EDITOR_BIND("slomo", "", { window_pause(0); editor.slomo = maxf(fmod(editor.slomo * 2, 16), 0.125); } );
EDITOR_BIND("stop", "(held(ALT)|held(SHIFT))&down(ESC)", { window_pause(1), editor.frame = 0, editor.t = 0, editor.dt = 0, editor.slomo = 0, editor.active = 1; editor_select("**"); editor_destroy_selected(); } );
EDITOR_BIND("pause", "down(ESC)", { window_pause( window_has_pause() ^ 1 ); } );
EDITOR_BIND("frame", "held(CTRL) & held(RIGHT)", { window_pause(1); editor.frame++, editor.t += (editor.dt = 1/60.f); } );
EDITOR_BIND("eject", "held(SHIFT) & down(F1)", { editor.active ^= 1; } );
EDITOR_BIND("reload", "down(F5)", { window_reload(); } );
EDITOR_BIND("quit", "held(ALT) & down(F4)", { record_stop(), exit(0); } );
EDITOR_BIND("battery", "held(ALT) & down(B)", { editor.powersave ^= 1; } );
EDITOR_BIND("browser", "down(F1)", { ui_show("Browser", ui_visible("Browser") ^ true); } );
EDITOR_BIND("outliner", "held(ALT) & down(O)", { ui_show("Outliner", ui_visible("Outliner") ^ true); } );
EDITOR_BIND("profiler", "held(ALT) & down(P)", { ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true)); } );
EDITOR_BIND("fullscreen", "down(F11)", { record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); } ); // close any recording before framebuffer resizing, which would corrupt video stream
EDITOR_BIND("mute", "held(ALT) & down(M)", { audio_volume_master( 1 ^ !!audio_volume_master(-1) ); } );
EDITOR_BIND("filter", "held(CTRL) & down(F)", { editor.filter ^= 1; } );
EDITOR_BIND("gamepad", "held(ALT) & down(G)", { editor.gamepad ^= 1; } );
EDITOR_BIND("lit", "held(ALT) & down(L)", { editor.lit ^= 1; } );
EDITOR_BIND("ddraw", "held(ALT) & down(D)", { editor.ddraw ^= 1; } );
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("tree.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("screenshot", "held(ALT) & down(X)", { char *name = file_counter(va("%s.png",app_name())); window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string()); } );
EDITOR_BIND("record", "held(ALT) & down(Z)", { if(record_active()) record_stop(), ui_notify(va("Video recorded"), date_string()); else { char *name = file_counter(va("%s.mp4",app_name())); app_beep(), window_record(name); } } );
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); \
} );
void editor_pump() {
for each_array(editor_binds,editor_bind_t,b) {
if( input_eval(b.bindings) ) {
editor_send(b.command);
}
}
do_threadlock(console_lock) {
for each_array_ptr(editor_jobs,char*,cmd) {
if( (*cmd)[0] ) {
int found = 0;
for each_array(editor_binds,editor_bind_t,b) {
if( !strcmpi(b.command, *cmd)) {
b.fn();
found = 1;
break;
}
}
if( !found ) {
// alert(va("Editor: could not handle `%s` command.", *cmd));
(*cmd)[0] = '\0'; strcatf(&(*cmd), "\1%s\n", "Err\n"); (*cmd)[0] = '\0';
}
if( (*cmd)[0] ) {
(*cmd)[0] = '\0'; strcatf(&(*cmd), "\1%s\n", "Ok\n"); (*cmd)[0] = '\0';
}
}
}
}
}
// ----------------------------------------------------------------------------------------
void editor_frame( void (*game)(unsigned, float, double) ) {
do_once {
//set_init_ptr(editor.world);
//set_init_ptr(editor.selection);
profiler_enable( false );
window_pause( true );
window_cursor_shape(CURSOR_SW_AUTO);
fx_load("editorOutline.fs");
fx_enable(0, 1);
obj_setname(editor.root = obj_new(obj), "Signals");
obj_setname(editor.init = obj_new(obj), "Init");
obj_setname(editor.tick = obj_new(obj), "Tick");
obj_setname(editor.draw = obj_new(obj), "Draw");
obj_setname(editor.quit = obj_new(obj), "Quit");
// obj_setname(editor.book = obj_new(obj), "Bookmark.toggles");
obj_attach(editor.root, editor.init);
obj_attach(editor.root, editor.tick);
obj_attach(editor.root, editor.draw);
obj_attach(editor.root, editor.quit);
editor_seticoninstance(editor.root, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.init, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.tick, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.draw, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.quit, ICON_MDI_SIGNAL_VARIANT);
// editor_seticoninstance(editor.book, ICON_MD_BOOKMARK_ADDED);
editor_seticonclass(obj_type(editor.root), ICON_MDI_CUBE_OUTLINE);
}
// game tick
game(editor.frame, editor.dt, editor.t);
// timing
editor.dt = clampf(window_delta(), 0, 1/60.f) * !window_has_pause() * editor.slomo;
editor.t += editor.dt;
editor.frame += !window_has_pause();
editor.frame += !editor.frame;
// process inputs & messages
editor_pump();
// draw menubar
static double last_fps = 0; if(!window_has_pause()) last_fps = window_fps();
const char *TITLE = va("%02dm:%02ds:%03dms:%02dF %5.2f/%dfps x%4.3f",
(int)editor.t / 60, (int)fmod(editor.t, 60), (int)(1000 * (editor.t - (int)editor.t)),
editor.frame % ((int)window_fps_target() + !(int)window_fps_target()),
last_fps, (int)window_fps_target(), editor.slomo);
const char *ICON_PL4Y = window_has_pause() ? ICON_MDI_PLAY : ICON_MDI_PAUSE;
const char *ICON_SKIP = window_has_pause() ? ICON_MDI_STEP_FORWARD/*ICON_MDI_SKIP_NEXT*/ : ICON_MDI_FAST_FORWARD;
#define EDITOR_TOOLBAR \
UI_MENU(10, \
UI_MENU_POPUP(ICON_MD_SETTINGS, vec2(0.33,1.00), ui_debug()) \
UI_MENU_ITEM(ICON_PL4Y, editor_send(window_has_pause() ? "play" : "pause")) \
UI_MENU_ITEM(ICON_SKIP, editor_send(window_has_pause() ? "frame" : "slomo")) \
UI_MENU_ITEM(ICON_MDI_STOP, editor_send("stop")) \
UI_MENU_ITEM(ICON_MDI_EJECT, editor_send("eject")) \
UI_MENU_ITEM(TITLE,) \
UI_MENU_ALIGN_RIGHT(30+30+30) \
UI_MENU_ITEM(ICON_MD_FOLDER_SPECIAL, editor_send("browser")) \
UI_MENU_ITEM(ICON_MD_SEARCH, editor_send("filter")) \
UI_MENU_ITEM(ICON_MD_CLOSE, editor_send("quit")) \
);
EDITOR_TOOLBAR
if(! editor.active ) return;
// draw silhouettes
sprite_flush();
for each_map_ptr(*editor_selected_map(),void*,o,int,selected) {
if(*selected && obj_draw[obj_typeid(*o)]) {
fx_begin();
obj_draw(*o);
sprite_flush();
fx_end();
}
}
// draw ui
if( ui_panel("Console " ICON_MDI_CONSOLE, 0)) {
// allocate 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;
}
ui_panel_end();
}
if( ui_panel("Scene " ICON_MDI_FILE_TREE, PANEL_OPEN)) {
editor_tree();
ui_panel_end();
}
// draw ui browser
if( ui_window("Browser", 0) ) {
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));
}
ui_window_end();
}
// draw subeditors
for each_array(editor.subeditors, subeditor, fn) {
fn();
}
// draw ui filter (note: render at end-of-frame, so it's hopefully on-top)
editor_filter();
}
#include "3rd_icon_mdi.h"
#include "v4k_editor.h"
// ----------------------------------------------------------------------------
// demo
typedef struct my_sprite { OBJ
char *filename;
vec3 position;
float tilt;
vec4 tint;
typedef struct kid { OBJ
int kid;
vec2 pos;
vec2 vel;
float angle;
vec4 color;
int controllerid;
// --- private
char *filename;
unsigned rgba_;
texture_t texture_;
} my_sprite;
} kid;
OBJTYPEDEF(my_sprite,201);
void kid_ctor(kid *obj) {
obj->kid = randi(0,3);
obj->pos = vec2(randi(0, window_width()), randi(0, window_height()));
obj->vel.x = obj->vel.y = 100 + 200 * randf();
obj->controllerid = randi(0,3);
void my_sprite_ctor(my_sprite *obj) {
obj->texture_ = texture(obj->filename, TEXTURE_RGBA);
obj->rgba_ = rgbaf( obj->tint.x/255.0, obj->tint.y/255.0, obj->tint.z/255.0, obj->tint.w/255.0 );
obj->texture_ = texture(obj->filename, TEXTURE_RGBA|TEXTURE_LINEAR);
obj->rgba_ = rgbaf( obj->color.x/255.0, obj->color.y/255.0, obj->color.z/255.0, obj->color.w/255.0 );
}
void my_sprite_draw(my_sprite *obj) {
obj->rgba_ = rgbaf( obj->tint.x/255.0, obj->tint.y/255.0, obj->tint.z/255.0, obj->tint.w/255.0 ); // @fixme: del me
sprite( obj->texture_, &(obj->position.x), obj->tilt, obj->rgba_ );
void kid_tick(kid *obj, float dt) {
// add velocity to position
vec2 off = vec2( input(KEY_RIGHT)-input(KEY_LEFT), input(KEY_DOWN)-input(KEY_UP) );
obj->pos = add2(obj->pos, scale2(mul2(obj->vel, off), dt * (obj->controllerid == 0)));
// wrap at screen boundaries
const int appw = window_width(), apph = window_height();
if( obj->pos.x < 0 ) obj->pos.x += appw; else if( obj->pos.x > appw ) obj->pos.x -= appw;
if( obj->pos.y < 0 ) obj->pos.y += apph; else if( obj->pos.y > apph ) obj->pos.y -= apph;
}
void my_sprite_edit(my_sprite *obj) {
void kid_draw(kid *obj) {
// 4x4 tilesheet
int col = (((int)obj->kid) % 4);
int row = (((int)obj->pos.x / 10 ^ (int)obj->pos.y / 10) % 4);
float position[3] = {obj->pos.x,obj->pos.y,obj->pos.y}; // position(x,y,depth: sort by Y)
float offset[2]={0,0}, scale[2]={1,1};
float coords[3]={col * 4 + row,4,4}; // num_frame(x) in a 4x4(y,z) spritesheet
sprite_sheet(obj->texture_, coords, position, obj->angle*TO_DEG, offset, scale,
0, obj->rgba_, 0); // is_additive, tint color, resolution independant
}
int kid_aabb(kid *obj, aabb *box) {
*box = aabb( vec3(obj->pos.x-16,obj->pos.y-16,0), vec3(obj->pos.x+16,obj->pos.y+16,1) );
return 1;
}
void kid_menu(kid *obj, const char *argv) {
ui_label("Private section");
ui_color4("Tint_", &obj->tint.x);
ui_color4("Color_", &obj->color.x);
ui_texture("Texture_", obj->texture_);
ui_separator();
obj->rgba_ = rgbaf( obj->color.x/255.0, obj->color.y/255.0, obj->color.z/255.0, obj->color.w/255.0 );
}
const char* kid_icon(kid *obj) {
if(obj->controllerid == 0) {
editor_symbol(obj->pos.x+16,obj->pos.y-16,ICON_MD_VIDEOGAME_ASSET);
return ICON_MD_VIDEOGAME_ASSET;
}
return NULL;
}
OBJTYPEDEF(kid,201);
AUTORUN {
// reflect
STRUCT( my_sprite, char*, filename, "Filename" );
STRUCT( my_sprite, vec3, position, "Position" );
STRUCT( my_sprite, float, tilt, "Tilt degrees" );
STRUCT( my_sprite, vec4, tint, "Tint color" );
// extend
EXTEND(my_sprite,ctor);
EXTEND(my_sprite,draw);
EXTEND(my_sprite,edit);
STRUCT(kid, int, kid);
STRUCT(kid, vec2, pos);
STRUCT(kid, vec2, vel);
STRUCT(kid, float, angle, "Tilt degrees");
STRUCT(kid, vec4, color, "Tint color");
STRUCT(kid, char*, filename, "Filename" );
EXTEND(kid, ctor,tick,draw,aabb,menu,icon);
}
void game(unsigned frame, float dt, double t) {
static my_sprite *root;
static my_sprite *o1;
static my_sprite *o2;
static kid *root;
static kid *o1;
static kid *o2;
static camera_t cam;
if( !frame ) {
// init camera (x,y) (z = zoom)
cam = camera();
cam.position = vec3(window_width()/2,window_height()/2,1);
camera_enable(&cam);
root = obj_make(
"[my_sprite]\n"
"filename=cat.png\n"
"position=5,2,100\n"
"tilt=46 + 45 -90\n"
"tint=255, 255, 0, 255\n"
"[kid]\n"
"filename=spriteSheetExample.png\n"
"pos=5,2\n"
"angle=pi/12\n"
"color=255, 255, 192, 255\n"
);
o1 = obj_make(
"[my_sprite]\n"
"filename=cat.png\n"
"position=1,2,100\n"
"tilt=45 + 45 -90\n"
"tint=255, 0, 0, 255\n"
"[kid]\n"
"filename=spriteSheetExample.png\n"
"pos=1,100\n"
"angle=pi/12\n"
"color=255, 192, 192, 255\n"
);
o2 = obj_make(
"[my_sprite]\n"
"filename=cat.png\n"
"position=1,2,100\n"
"tilt=45\n"
"tint=0, 0, 255, 255\n"
"[kid]\n"
"filename=spriteSheetExample.png\n"
"pos=50,200\n"
"angle=pi/12\n"
"color=192, 192, 255, 255\n"
);
//obj_setname(root, "root");
@ -922,19 +197,41 @@ void game(unsigned frame, float dt, double t) {
editor_watch(root);
}
// draw game
my_sprite_draw(root);
my_sprite_draw(o1);
my_sprite_draw(o2);
// camera panning (x,y) & zooming (z)
if(0)
if( !ui_hover() && !ui_active() ) {
if( input(MOUSE_L) ) cam.position.x -= input_diff(MOUSE_X);
if( input(MOUSE_L) ) cam.position.y -= input_diff(MOUSE_Y);
cam.position.z += input_diff(MOUSE_W) * 0.1; // cam.p.z += 0.001f; for tests
}
// tick game
root->tilt = 5 * sin(t+dt);
if( dt ) {
kid_tick(root, dt);
kid_tick(o1, dt);
kid_tick(o2, dt);
root->angle = 5 * sin(t+dt);
}
// draw world
ddraw_ontop_push(0);
ddraw_grid(0);
ddraw_ontop_pop();
ddraw_flush();
// draw game
kid_draw(root);
kid_draw(o1);
kid_draw(o2);
}
int main(){
for( window_create(flag("--transparent") ? 101 : 80,0); window_swap(); ) {
window_title("Editor " EDITOR_VERSION);
window_create(flag("--transparent") ? 101 : 80,0);
window_icon("logo.png");
while( window_swap() ) {
editor_frame(game);
}
}
#include "editor3timeline.h"

View File

@ -1,42 +1,17 @@
#define EDITOR_VERSION "2023.9"
#if 0
#define EDITOR_PRINTF PRINTF
#ifdef ICON_MDI_CUBE
#define ICON_OBJECT ICON_MDI_CUBE_OUTLINE
#define ICON_OBJECT_ALT ICON_MDI_CUBE
#define ICON_DOT ICON_MDI_CIRCLE_SMALL
#define ICON_EVENT ICON_MDI_CALENDAR
#else
#define ICON_OBJECT ICON_MD_VIEW_IN_AR
#define ICON_DOT " · " // ICON_CANCEL // ICON_MD_WIFI_1_BAR // ICON_MD_RADIO_BUTTON_UNCHECKED // ICON_MD_LENS_BLUR
#define ICON_EVENT ICON_MD_FLAG
#endif
//#define ICON_CIRCLE ICON_MDI_CIRCLE_OUTLINE
//#define ICON_CIRCLE_ALT ICON_MDI_CIRCLE
#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
@ -47,23 +22,13 @@
#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_SCRIPT ICON_MD_CONTENT_PASTE
#define ICON_POWER ICON_MD_BOLT // ICON_MD_POWER
#define ICON_BATTERY_CHARGING ICON_MD_BATTERY_CHARGING_FULL
@ -76,24 +41,8 @@
#endif
enum editor_keys {
key_screenshot, // @todo: add meta-info in exif or invisibile pixels (cam details, player details, map level, map location, level state, etc)
};
#if 0
void editor_menubar() {
do_once editor_init();
if( input_down(KEY_F11) ) editor_key = key_fullscreen;
if( input_down(KEY_PAUSE) ) editor_key = key_pause;
if( input_down(KEY_PRINT) ) editor_key = (mods ? key_recording : key_screenshot);
// if( input_down(KEY_W) && input_held(KEY_LCTRL) ) editor_key = key_quit;
if( ctrls ) {
/**/ if( input_down(KEY_Z) ) editor_key = key_undo;
else if( input_down(KEY_Y) ) editor_key = key_redo;
else if( input_down(KEY_S) ) editor_key = key_save_disk;
}
key_screenshot, // @todo: add meta-info in exif or invisibile pixels (cam details, player details, map level, map location, level state, etc)
if( !editor_key && editor_selected_obj ) {
if( input_up(MOUSE_L) ) editor_key = key_save_mem;
@ -107,32 +56,25 @@ void editor_menubar() {
// menubar
if( ui_menu( ICON_SETTINGS "@Preferences;"
if( ui_menu(
ICON_LANGUAGE " Language;"
ICON_PERSONA " Profile;" // editor account, but also fake profile and 1st party credentials
ICON_SOCIAL " Social;"
ICON_SOCIAL " Social;" // name,email,icon,link,github
ICON_GAME " Game;" //
ICON_WIFI " Network;"
ICON_BUDGET " Budget;" // mem,gfx,net,hdd,... also logging
ICON_NEW_FOLDER " Folders;" // including project folders
ICON_RESTART " Restart;"
ICON_QUIT " Quit;"
"-" ICON_MD_RECYCLING " Reset all preferences;" ICON_MD_SAVE_AS " Save all preferences"
) ) {
if( ui_item() == 3 ) {} // key mappings
if( ui_item() == 4 ) {} // sensitivity, invert xylrw
if( ui_item() == 5 ) {} // sensitivity, invert xy,ab, deadzones
if( ui_item() == 7 ) {} // name,email,icon,link,github
if( ui_item() == 13) editor_key = key_reload;
if( ui_item() == 14) editor_key = key_quit;
if( ui_item() == 3 ) {} // keyboard: key mappings
if( ui_item() == 4 ) {} // mouse: sensitivity, invert xylrw
if( ui_item() == 5 ) {} // gamepad: sensitivity, invert xy,ab, deadzones
}
static char game_args[16] = "--game-args"; // @fixme @todo remove '_' special char to signal that ui_menu() is writeable (inputbox)
if( ui_menu_editbox( game_args, 16 ) ) {}
// ICON_MD_TROUBLESHOOT -> PROFILER
// ICON_MD_SCHEMA -> GRAPHNODES
// ICON_MD_ACCOUNT_TREE -> GRAPHNODES
// ICON_MD_TIPS_AND_UPDATES -> BULB
// if( ui_menu( ICON_MD_MENU )) {}
@ -156,7 +98,6 @@ void editor_menubar() {
// @todo: combine-in-1? cycle mem -> cpu/profiler -> network mon -> debugger
// bug report, profile, warnings, time/chrono (@todo: alarm/snooze? calendar?)
if( ui_menu( ICON_MD_BUG_REPORT /*"^"*/ "0" ) ) {}
if( ui_menu( ICON_MD_FACE /*"^"*/ "3" ) ) {} // @todo: do both messaging/warnings + profile settings here
{
static double base = 0, tap = 0;
@ -176,39 +117,277 @@ void editor_menubar() {
}
}
#if 0
for each_map_ptr(editor_state, void *, o, editor_state_t, ed) {
profile_incstat("Editor.num_objects", +1);
void *obj = *o;
#endif
#if 1
#elif 0
// auto-load from disk during init. @fixme kvdb database
if( array_count(ed->history) == 0 )
if( editor_load_disk(obj, editor_obj_string(obj, ".path")) )
{}
// ## Roadmaps
// ### v2 roadmap (mid-term)
// - [ ] art/ vs prefabs/ discrimination: prefabs/ are archetypes (composed types); ie, data-classes. art/ contains data files.
// - [ ] can prefabs be done with ecs maybe?
// - [ ] example: levels are prefabs, composed of other sub-prefabs or art assets.
// - [ ] example: hitboxes+events. girl=pivot(p,r,s)+model(mesh,tex)+curframe
// - [ ] extend widgets vec3 as range;customized mesh,texture,audio,any other asset,combo of anything)
//
// ### organization: world as a filesystem
// - [ ] anything can be serialized into disk. any object, any entity, any property or any widget can be serialized into disk.
// - [ ] groups of them as well. the whole world state can be serialized into disk as a filesystem snapshot:
// - [ ] - objects are folders. you can attach nodes on nodes (ie, create folders inside folders).
// - [ ] - systems are dlls/scripts. you can modify them on the fly and they should reload.
// - [ ] - components are data files. each component is a file. some components may be pure datas (ie, raw textures) but some others can be human-readable and editable.
// inside of that, every line is a JSON/INI property that you can tweak, modify or inspect.
//
// ### replication: diffing zips
// - [ ] the whole world/filesystem will be compressed into a zipfile and delivered to the network when sharding/replicating in a network scenario.
// - [ ] clients will be diffing/patching their filesystems on every received packet. there will be 3 operations to support internally that will reflect what the E/C/S world is doing behind the curtains:
// - [ ] - added files/folders [+] : when creating entities/components/systems
// - [ ] - deleted files/folders [-] : when removing entities/components/systems
// - [ ] - modifying files/folders [*] : when altering entities/components/systems
// auto-save in-mem during first edit
if( array_count(ed->history) == 0 )
editor_save_mem(obj);
#endif
// plugins
// vcs
// google/midjourney placeholders
// @todo: continue if obj not found in selection set
if( obj != editor_selected_obj )
continue;
// prefabs
// - [ ] Level objects: ~~volumes, triggers, platforms, streaming~~.
// - level: emitters: particles, lights, lightmaps, sound sources, triggers, etc
// - level: box triggers, start/end, spawn, streaming, checkpoints,
// - level: jump, shoots, platforms, collisions
// - level: 60s, 70s, 80s, 90s
if( editor_key == key_debugger ) { breakpoint("User requested breakpoint on this object"); }
#if 1
#elif 0
if( editor_key == key_reset ) { const char *ok = editor_reset(obj) ? "ok" : "err"; EDITOR_PRINTF("reset: %s\n", ok); }
if( editor_key == key_save_mem ) { const char *ok = editor_save_mem(obj) ? "ok" : "err"; EDITOR_PRINTF("mem saved: %s\n", ok); }
if( editor_key == key_undo ) { const char *ok = editor_undo(obj) ? "ok" : "err"; EDITOR_PRINTF("undo: %s\n", ok); }
if( editor_key == key_redo ) { const char *ok = editor_redo(obj) ? "ok" : "err"; EDITOR_PRINTF("redo: %s\n", ok); }
if( editor_key == key_save_disk ) { const char *ok = editor_save_disk(obj, editor_obj_string(obj, ".path")) ? "ok" : "err"; EDITOR_PRINTF("save: %s\n", ok); }
if( editor_key == key_load_disk ) { const char *ok = editor_load_disk(obj, editor_obj_string(obj, ".path")) ? "ok" : "err"; EDITOR_PRINTF("load: %s\n", ok); }
#endif
}
#endif
// ## old notes below
// ==================
// - gizmo: proportional, orbit/arcball XY (+shift for Z/tilt)
// - vcs
// - [ ] Core: wecs+replication
// - modules: script or dll + ram load/save/diff/patch + play/stop/init/ + attach/detach
// - logic tree/ << [] |> || >>
// - - scene |>
// - - enemies
// - ecs: sys are modules, ecs: char *messaging, ecs: filesystem (e/dir,c/files,s/dll)
// - world: streaming, migration
// key_screenshot, // @todo: add meta-info in exif or invisibile pixels (cam details, player details, map level, map location, level state, etc)
// fn_init,
// fn_load,
// fn_tick,
// fn_draw,
// fn_aabb, // hitboxes
// fn_edit, // call for debug ui (like loggers and sliders)
// fn_save,
// fn_quit,
if( !editor_key /*&& !input_anykey()*/ && editor_selected_obj ) {
if( input_up(MOUSE_L) ) editor_key = key_save_mem;
if( input_down(MOUSE_R) ) ui_contextual("Properties", true);
}
void editor_render_windows() {
// Scene/nodes
if( ui_window("Outliner", 0) ) {
#if 0
static unsigned tabs = 0xFF;
int choice = ui_toolbar(
"LV@Level tree: hierarchical logic datas used when ticking game.;"
"RN@Rendering tree: hierarchical rendering datas used when drawing game.;"
"VS@Visibility tree: hierarchical visibility datas used when ticking game and editor. Also collisions.;"
"ST@Streaming tree: hierarchical streaming datas used when streaming content off disk.;"
"PS@Persist tree: hierarchical storage datas within different game sessions.;"
"PR@Prefabs tree: hierarchical datas of prefabs definitions.;"
"ED@Editor tree: hierarchical datas used when ticking editor.;"
);
#endif
//
for( int c = ui_collapse(va(ICON_MD_FACTORY " Prefabs/ (%d)", map_count(editor_state)), "PRF"); c; ui_collapse_end(), c = 0)
// others
for( int c = ui_collapse(ICON_MD_PRECISION_MANUFACTURING " Editors/", "EDT"); c; ui_collapse_end(), c = 0) {
// dynamic/static bounds: depth + bounds + visibility
do_context_cmd = 0;
do_context_obj = 0;
for( int c = ui_collapse(va(ICON_MD_ACCOUNT_TREE " Levels/ (%d)", map_count(editor_children)), "LVL"); c; ui_collapse_end(), c = 0)
for each_map_ptr(editor_children, void*, o, array(void*), objs) {
void *k = *o;
editor_obj_render_properties_recursively(k, mask);
}
if( do_context_cmd == cc4(l,i,s,t) && do_context_obj ) {
printf("list [%p]\n", do_context_obj);
}
// draw: depth + state (alpha0=off)
// @fixme: make it a tree
for( int c = ui_collapse(va(ICON_MD_PALETTE " Rendering/ (%d)", map_count(editor_children_draw)), "GPU"); c; ui_collapse_end(), c = 0)
for each_map_ptr(editor_children_draw, void*, o, array(void*), objs) {
void *k = *o;
editor_draw_objs_recursively(k, DRAW_DO_UI);
}
// tick: depth + rate (00=off) --> logic
// @todo: physics tick group? anim tick group? any other tick group?
// @fixme: make it a tree
for( int c = ui_collapse(va(ICON_MD_FLAG " Ticking/ (%d)", map_count(editor_children_tick)), "CPU"); c; ui_collapse_end(), c = 0)
for each_map_ptr(editor_children_tick, void*, o, array(void*), objs) {
void *k = *o;
editor_tick_objs_recursively(k, TICK_DO_UI);
}
// init/quit: depth + prio
// @fixme: make it a tree
for( int c = ui_collapse(ICON_MD_CLOUD " Streaming/", "BVH"); c; ui_collapse_end(), c = 0) {}
// save/load: depth + savetomem?yn + savetodisk?yn + quant + lossy/lossless
// @fixme: make it a tree
for( int c = ui_collapse(va(ICON_MD_SD_CARD " Storage/ (%d)", map_count(editor_dicts)), "DSK"); c; ui_collapse_end(), c = 0)
for each_map_ptr(editor_dicts, void*, o, editor_dict_t, d) {
void *k = *o;
for( int p = ui_collapse(editor_obj_string(k,".name"), va("DSK%p",k)); p; ui_collapse_end(), p = 0) {
for each_map(*d, char*, s, char *, v) {
ui_label(va("%s: %s", s, v));
}
}
}
for( int c = ui_collapse(ICON_MD_INFO " Help", "NFO"); c; ui_collapse_end(), c = 0) {
ui_label("=*General");
ui_label2("*ESC", ">Editor on/off");
ui_label2("*F11", ">Fullscreen on/off");
ui_label2("*F5", ">Refresh");
ui_separator();
ui_label("=*Edit");
ui_label2("*^Z, ^Y", ">Undo, Redo");
ui_label2("*^X, ^C, ^V", ">Cut, Copy, Paste");
ui_label2("*^S, ^L, ^R", ">Save, Load*, Restart*");
ui_separator();
ui_label("=*Select");
ui_label2("*LMB, ^A, ^D", ">Select, All, None");
ui_label2("*RMB", ">Contextual menu*");
ui_label2("*SPACE@Cycle transform gizmo: position, rotation, scale.", ">Cycle transform gizmo");
ui_separator();
ui_label("=*Camera");
ui_label2("*Q,E,C", ">Camera elevation");
ui_label2("*W,A,S,D", ">Camera move");
ui_label2("*LMB/RMB+drag", ">Camera view");
ui_label2("*WMB", ">Camera speed");
}
ui_window_end();
}
}
ray *editor_pickup() {
// if(!window_has_cursor()) return NULL;
// pick entity
bool any_active = ui_active() || ui_hover() || gizmo_active() || gizmo_hover() || input_touch_active();
if( editor_enabled && !any_active && input_down(MOUSE_L) ) {
editor_mouse = vec2(input(MOUSE_X), input(MOUSE_Y));
vec3 out = editor_pick(editor_mouse.x, editor_mouse.y); // unprj 2d as 3d coord
vec3 from = camera_get_active()->position, to = out;
static ray last_ray;
last_ray = ray(from, to);
return &last_ray;
}
return 0;
}
void editor_camera_fps(void) {
static camera_t cam = {0};
cam = *camera_get_active();
vec3 move = {0};
vec2 view = {0};
// show/hide cursor
bool dragging = input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
bool any_active = ui_active() || ui_hover() || gizmo_active() || input_touch_active();
if( any_active ) dragging = false;
window_cursor( !dragging );
// keyboard/mouse
if( dragging ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_Q)||input(KEY_C)),input(KEY_W)-input(KEY_S)), cam.speed * !any_active);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * dragging * !any_active);
if( !input(KEY_LCTRL) && !input(KEY_RCTRL) ) // invalidate keys if pressing ctrl (ie, when saving CTRL-S)
move = add3(move, wasdec);
view = add2(view, mouse);
// gamepad
if(0) {
vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f /*15% deadzone*/);
vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), 0.15f /*15% deadzone*/);
vec3 gamepad_move = scale3(vec3(filtered_lpad.x, input(GAMEPAD_LT) - input(GAMEPAD_RT), filtered_lpad.y), 1.0f);
vec2 gamepad_view = scale2(filtered_rpad, 1.0f);
move = add3(move, gamepad_move);
view = add2(view, gamepad_view);
}
// multi-touch
vec2 touch_move = input_touch_delta_from_origin(0, 0.0125f /*sensitivityFwd*/); // button #0 (left border)
vec2 touch_view = input_touch_delta(1, 0.125f /*sensitivityRot*/); // button #1 (right border)
move = add3(move, vec3(touch_move.x, 0, -touch_move.y));
view = add2(view, vec2(touch_view.x, -touch_view.y));
// apply inputs
camera_moveby(&cam, move);
camera_fps(&cam, view.x,view.y);
}
// my game
// fps camera
if( /*editor_attached ||*/ editor_enabled ) {
profile("Editor.Camera") {
editor_camera_fps();
}
} else {
profile("Game.Camera") {
camera_t *cam = camera_get_active();
static vec3 source;
do_once source = cam->position;
vec3 target = add3(girl_p, vec3(0,10,0));
target = add3(target, scale3(norm3(sub3(source, target)), 10.0));
source = mix3(source, target, 1-0.99f);
camera_teleport(cam, source);
camera_lookat(cam, vec3(girl_p.x,0,girl_p.z));
// @todo: orbit cam w/ right pad
}
}
camera_t *cam = camera_get_active();
if(!editor_enabled) continue;
profile("Editor.Draw outline") {
// handle (multi-)selection
ray *r = editor_pickup();
if( r ) {
bool found = false;
bool multi_selection = input(KEY_LCTRL) || input(KEY_RCTRL);
for each_map_ptr(editor_state, void*, o, editor_state_t, ed) {
void *obj = *o;
if( obj == &ground_size ) continue; // @fixme: add ray+plane. also, bvh
aabb *box = editor_obj_call0(obj, fn_aabb);
if( ray_hit_aabb(*r, *box)) {
editor_select(obj, multi_selection);
found = true;
}
}
if( !found )
if( ray_hit_plane(*r, plane(vec3(0,0,0), vec3(0,1,0)) )) {
editor_select(&ground_size, multi_selection);
}
}
if(!set_count(editor_selection)) continue;
#endif

View File

@ -0,0 +1,827 @@
#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 obj_hasmethod(o,func) (obj_typeid(o)[obj_##func])
#ifndef ICON_MD_LIGHT_OFF
#define ICON_MD_LIGHT_OFF "\xEE\xA6\xB8"
#endif
// ## Editor long-term plan
// - editor = tree of nodes. levels and objects are nodes, and their widgets are also nodes
// - you can perform actions on nodes, with or without descendants, top-bottom or bottom-top
// - these operations include load/save, undo/redo, reset, play/render, ddraw, etc
// - nodes are saved to disk as a filesystem layout: parents are folders, and leafs are files
// - network replication can be done by external tools by comparing the filesystems and by sending the resulting diff zipped
//
// ## Editor roadmap
// - Gizmos✱, scene tree, property editor✱, load/save✱, undo/redo✱, copy/paste, on/off (vis,tick,ddraw,log), vcs.
// - Scenenode pass: node singleton display, node console, node labels, node outlines✱.<!-- node == gameobj ? -->
// - Render pass: billboards✱, materials, un/lit, cast shadows, wireframe, skybox✱/mie✱, fog/atmosphere
// - Level pass: volumes, triggers, platforms, level streaming, collide✱, physics
// - Edit pass: Procedural content, brushes, noise and CSG.
// - GUI pass: timeline and data tracks, node graphs. <!-- worthy: will be reused into materials, animgraphs and blueprints -->
// ## Alt plan
// editor is a database + window/tile manager + ui toolkit; all network driven.
// to be precise, editor is a dumb app and ...
// - does not know a thing about what it stores.
// - does not know how to render the game graphics.
// - does not know how to run the game logic.
//
// the editor will create a canvas for your game to render.
// your game will be responsible to tick the logic and render the window inside the editor.
//
// that being said, editor...
// - can store datas hierarchically.
// - can perform diffs and merges, and version the datas into repositories.
// - can be instructed to render UI on top of game and window views.
// - can download new .natvis and plugins quickly.
// - can dump whole project in a filesystem form (zip).
// - editor reflects database contents up-to-date.
// - database can be queried and modified via OSC(UDP) commands.
// editor database uses one table, and stores two kind of payload types:
// - classes: defines typename and dna. class names are prefixed by '@'
// - instances: defines typename and datas. instance names are as-is, not prefixed.
//
// every save contains 5Ws: what, who, when, where, how,
// every save can be diffed/merged.
// ----------------------------------------------------------------------------
#define EDITOR_VERSION "2023.10"
// ----------------------------------------------------------------------------
typedef struct editor_bind_t {
const char *command;
const char *bindings;
void (*fn)();
} editor_bind_t;
array(editor_bind_t) editor_binds;
#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_)}) ); }
// ----------------------------------------------------------------------------
typedef void (*editor_no_property)(void *);
array(void*) editor_persist_kv;
array(editor_no_property) editor_no_properties;
#define EDITOR_PROPERTY(property_name,T,defaults) \
typedef map(void*,T) editor_##property_name##_map_t; \
editor_##property_name##_map_t *editor_##property_name##_map() { \
static editor_##property_name##_map_t map = 0; do_once map_init_ptr(map); \
return &map; \
} \
T editor_##property_name(const void *obj) { \
return *map_find_or_add(*editor_##property_name##_map(), (void*)obj, ((T) defaults)); \
} \
void editor_set##property_name(const void *obj, T value) { \
*map_find_or_add(*editor_##property_name##_map(), (void*)obj, ((T) value)) = ((T) value); \
} \
void editor_alt##property_name(const void *obj) { \
T* found = map_find_or_add(*editor_##property_name##_map(), (void*)obj, ((T) defaults)); \
*found = (T)(uintptr_t)!(*found); \
} \
void editor_no##property_name(void *obj) { \
T* found = map_find_or_add(*editor_##property_name##_map(), (void*)obj, ((T) defaults)); \
map_erase(*editor_##property_name##_map(), (void*)obj); \
} \
AUTORUN { array_push(editor_persist_kv, #T); array_push(editor_persist_kv, editor_##property_name##_map()); array_push(editor_no_properties, editor_no##property_name); }
EDITOR_PROPERTY(open, int, 0); // whether object is tree opened in tree editor
EDITOR_PROPERTY(selected, int, 0); // whether object is displaying a contextual popup or not
EDITOR_PROPERTY(changed, int, 0); // whether object is displaying a contextual popup or not
EDITOR_PROPERTY(popup, int, 0); // whether object is displaying a contextual popup or not
EDITOR_PROPERTY(visible, int, 0);
EDITOR_PROPERTY(script, int, 0);
EDITOR_PROPERTY(event, int, 0);
EDITOR_PROPERTY(iconinstance, char*, 0);
EDITOR_PROPERTY(iconclass, char*, 0);
EDITOR_PROPERTY(treeoffsety, int, 0);
// new prop: breakpoint: request to break on any given node
// new prop: persist: objects with this property will be saved on disk
void editor_destroy_properties(void *o) {
for each_array(editor_no_properties,editor_no_property,fn) {
fn(o);
}
}
void editor_load_on_boot(void) {
puts("@todo: load editor");
}
void editor_save_on_quit(void) {
puts("@todo: save editor");
}
AUTORUN {
editor_load_on_boot();
(atexit)(editor_save_on_quit);
}
// ----------------------------------------------------------------------------
typedef int(*subeditor)(int mode);
struct editor_t {
// time
unsigned frame;
double t, dt, slomo;
// controls
int transparent;
int attached;
int active; // focus? does_grabinput instead?
int key;
vec2 mouse; // 2d coord for ray/picking
bool gamepad; // mask instead? |1|2|4|8
int hz_high, hz_medium, hz_low;
int filter;
bool battery; // battery mode: low fps
bool unlit;
bool ddraw;
// event root nodes
obj* root;
obj* on_init;
obj* on_tick;
obj* on_draw;
obj* on_edit;
obj* on_quit;
// all of them (hierarchical)
array(obj*) objs; // @todo:set() world?
// all of them (flat)
set(obj*) world;
//
array(char*) cmds;
// subeditors
array(subeditor) subeditors;
} editor = {
.active = 1,
.gamepad = 1,
.hz_high = 60, .hz_medium = 18, .hz_low = 5,
};
enum {
EDITOR_PANEL,
EDITOR_WINDOW,
EDITOR_WINDOW_NK,
EDITOR_WINDOW_NK_SMALL,
};
int editor_begin(const char *title, int mode) {
if( mode == 0 ) return ui_panel(title, PANEL_OPEN);
if( mode == 1 ) return ui_window(title, 0);
int ww = window_width(); int w = ww * 0.66;
int hh = window_height(); int h = hh * 0.66;
struct nk_rect position = { (ww-w)/2,(hh-h)/2, w,h };
nk_flags win_flags = NK_WINDOW_TITLE | NK_WINDOW_BORDER |
NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE |
NK_WINDOW_CLOSABLE | NK_WINDOW_MINIMIZABLE |
// NK_WINDOW_SCALE_LEFT|NK_WINDOW_SCALE_TOP| //< @fixme: move this logic into nuklear
// NK_WINDOW_MAXIMIZABLE | NK_WINDOW_PINNABLE |
0; // NK_WINDOW_SCROLL_AUTO_HIDE;
if( mode == 3 ) {
mode = 2, position.x = input(MOUSE_X), position.w = w/3, win_flags =
NK_WINDOW_TITLE|NK_WINDOW_CLOSABLE|
NK_WINDOW_SCALABLE|NK_WINDOW_MOVABLE| //< nuklear requires these two to `remember` popup pos
0;
}
if( mode == 2 || mode == 3 )
if (nk_begin(ui_ctx, title, position, win_flags))
return 1;
else
return nk_end(ui_ctx), 0;
return 0;
}
int editor_end(int mode) {
if( mode == 0 ) return ui_panel_end();
if( mode == 1 ) return ui_window_end();
if( mode == 2 ) nk_end(ui_ctx);
if( mode == 3 ) nk_end(ui_ctx);
return 0;
}
#if 0 // deprecate
bool editor_active() {
return ui_hover() || ui_active() || gizmo_active() ? editor.active : 0;
}
#endif
int editor_filter() {
if( editor.filter ) {
if (nk_begin(ui_ctx, "Filter", nk_rect(window_width()-window_width()*0.33,32, window_width()*0.33, 40),
NK_WINDOW_NO_SCROLLBAR)) {
char *bak = ui_filter; ui_filter = 0;
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &bak);
ui_filter = bak;
if( input(KEY_ESC) || ( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 )) {
if( ui_filter ) ui_filter[0] = '\0';
editor.filter = 0;
}
}
nk_end(ui_ctx);
}
return editor.filter;
}
static
int editor_select_(void *o, const char *mask) {
int matches = 0;
int off = mask[0] == '!', inv = mask[0] == '~';
int match = strmatchi(obj_type(o), mask+off+inv) || strmatchi(obj_name(o), mask+off+inv);
if( match ) {
editor_setselected(o, inv ? editor_selected(o) ^ 1 : !off);
++matches;
}
for each_objchild(o, obj*, oo) {
matches += editor_select_(oo, mask);
}
return matches;
}
void editor_select(const char *mask) {
for each_array( editor.objs, obj*, o )
editor_select_(o, mask);
}
void editor_unselect() { // same than editor_select("!**");
for each_map_ptr(*editor_selected_map(), void*,o, int, k) {
if( *k ) *k = 0;
}
}
void editor_select_aabb(aabb box) {
int is_inv = input_held(KEY_CTRL);
int is_add = input_held(KEY_SHIFT);
if( !is_inv && !is_add ) editor_unselect();
aabb item = {0};
for each_set_ptr( editor.world, obj*, o ) {
if( obj_hasmethod(*o,aabb) ) {
if( obj_aabb(*o, &item) && aabb_test_aabb(item, box) ) {
if( is_inv )
editor_altselected(*o);
else
editor_setselected(*o, 1);
}
}
}
}
static obj* active_ = 0;
static void editor_selectgroup_(obj *o, obj *first, obj *last) {
// printf("%s (looking for %s in [%s..%s])\n", obj_name(o), active_ ? obj_name(active_) : "", obj_name(first), obj_name(last));
if( !active_ ) if( o == first || o == last ) active_ = o == first ? last : first;
if( active_ ) editor_setselected(o, 1);
if( o == active_ ) active_ = 0;
for each_objchild(o, obj*, oo) {
editor_selectgroup_(oo, first, last);
}
}
void editor_selectgroup(obj *first, obj *last) {
if( last ) {
if( !first ) first = array_count(editor.objs) ? editor.objs[0] : NULL;
if( !first ) editor_setselected(last, 1);
else {
active_ = 0;
for each_array(editor.objs,obj*,o) {
editor_selectgroup_(o, first, last);
}
}
}
}
static obj *find_any_selected_(obj *o) {
if( editor_selected(o) ) return o;
for each_objchild(o,obj*,oo) {
obj *ooo = find_any_selected_(oo);
if( ooo )
return ooo;
}
return 0;
}
void* editor_first_selected() {
for each_array(editor.objs,obj*,o) {
obj *oo = find_any_selected_(o);
// if( oo ) printf("1st found: %s\n", obj_name(oo));
if( oo ) return oo;
}
return 0;
}
static obj *find_last_selected_(obj *o) {
void *last = 0;
if( editor_selected(o) ) last = o;
for each_objchild(o,obj*,oo) {
obj *ooo = find_last_selected_(oo);
if( ooo )
last = ooo;
}
return last;
}
void* editor_last_selected() {
void *last = 0;
for each_array(editor.objs,obj*,o) {
obj *oo = find_last_selected_(o);
// if( oo ) printf("last found: %s\n", obj_name(oo));
if( oo ) last = oo;
}
return last;
}
// ----------------------------------------------------------------------------------------
void editor_addtoworld(obj *o) {
set_find_or_add(editor.world, o);
for each_objchild(o, obj*, oo) {
editor_addtoworld(oo);
}
}
void editor_watch(const void *o) {
array_push(editor.objs, (obj*)o);
obj_push(o); // save state
editor_addtoworld((obj*)o);
}
void* editor_spawn(const char *ini) { // deprecate?
obj *o = obj_make(ini);
editor_watch(o);
return o;
}
void editor_spawn1() {
obj *selected = editor_first_selected();
obj *o = selected ? obj_make(obj_saveini(selected)) : obj_new(obj);
if( selected ) obj_attach(selected, o), editor_setopen(selected, 1);
else
editor_watch(o);
editor_unselect();
editor_setselected(o, 1);
}
typedef set(obj*) set_objp_t;
static
void editor_glob_recurse(set_objp_t*list, obj *o) {
set_find_or_add(*list, o);
for each_objchild(o,obj*,oo) {
editor_glob_recurse(list, oo);
}
}
void editor_destroy_selected() {
set_objp_t list = 0;
set_init_ptr(list);
for each_map_ptr(*editor_selected_map(), obj*,o, int,selected) {
if( *selected ) { editor_glob_recurse(&list, *o); }
}
for each_set(list, obj*, o) {
obj_detach(o);
}
for each_set(list, obj*, o) {
// printf("deleting %p %s\n", o, obj_name(o));
// remove from watched items
for (int i = 0, end = array_count(editor.objs); i < end; ++i) {
if (editor.objs[i] == o) {
editor.objs[i] = 0;
array_erase_slow(editor.objs, i);
--end;
--i;
}
}
// delete from world
set_erase(editor.world, o);
// delete properties + obj
editor_destroy_properties(o);
obj_free(o);
}
set_free(list);
}
void editor_inspect(obj *o) {
ui_section(va("%s (%s)", obj_type(o), obj_name(o)));
if( obj_hasmethod(o, menu) ) {
obj_menu(o);
}
for each_objmember(o,TYPE,NAME,PTR) {
if( !editor_changed(PTR) ) {
obj_push(o);
}
ui_label_icon_highlight = editor_changed(PTR); // @hack: remove ui_label_icon_highlight hack
char *label = va(ICON_MD_UNDO "%s", NAME);
int changed = 0;
/**/ if( !strcmp(TYPE,"float") ) changed = ui_float(label, PTR);
else if( !strcmp(TYPE,"vec2") ) changed = ui_float2(label, PTR);
else if( !strcmp(TYPE,"vec3") ) changed = ui_float3(label, PTR);
else if( !strcmp(TYPE,"vec4") ) changed = ui_float4(label, PTR);
else if( !strcmp(TYPE,"color") ) changed = ui_color4(label, PTR);
else if( !strcmp(TYPE,"char*") ) changed = ui_string(label, PTR);
else ui_label2(label, va("(%s)", TYPE)); // INFO instead of (TYPE)?
if( changed ) {
editor_setchanged(PTR, 1);
}
if( ui_label_icon_highlight )
if( ui_label_icon_clicked_L.x >= 6 && ui_label_icon_clicked_L.x <= 26 ) { // @hack: if clicked on UNDO icon (1st icon)
editor_setchanged(PTR, 0);
}
if( !editor_changed(PTR) ) {
obj_pop(o);
}
}
}
// ----------------------------------------------------------------------------------------
// tty
static thread_mutex_t *console_lock;
static array(char*) editor_jobs;
int editor_send(const char *cmd) { // return job-id
int skip = strspn(cmd, " \t\r\n");
char *buf = STRDUP(cmd + skip);
strswap(buf, "\r\n", "");
int jobid;
do_threadlock(console_lock) {
array_push(editor_jobs, buf);
jobid = array_count(editor_jobs) - 1;
}
return jobid;
}
const char* editor_recv(int jobid, double timeout_ss) {
char *answer = 0;
while(!answer && timeout_ss >= 0 ) {
do_threadlock(console_lock) {
if( editor_jobs[jobid][0] == '\0' )
answer = editor_jobs[jobid];
}
timeout_ss -= 0.1;
if( timeout_ss > 0 ) sleep_ms(100); // thread_yield()
}
return answer + 1;
}
// plain and ctrl keys
EDITOR_BIND(play, "down(F5)", { window_pause(0); /* if(!editor.slomo) editor.active = 0; */ editor.slomo = 1; } );
EDITOR_BIND(stop, "down(ESC)", { if(editor.t > 0) { window_pause(1), editor.frame = 0, editor.t = 0, editor.dt = 0, editor.slomo = 0, editor.active = 1; editor_select("**"); editor_destroy_selected(); }} );
EDITOR_BIND(eject, "down(F1)", { /*window_pause(!editor.active); editor.slomo = !!editor.active;*/ editor.active ^= 1; } );
EDITOR_BIND(pause, "(held(CTRL) & down(P)) | down(PAUSE)", { window_pause( window_has_pause() ^ 1 ); } );
EDITOR_BIND(frame, "held(CTRL) & down(LEFT)", { window_pause(1); editor.frame++, editor.t += (editor.dt = 1/60.f); } );
EDITOR_BIND(slomo, "held(CTRL) & down(RIGHT)", { window_pause(0); editor.slomo = maxf(fmod(editor.slomo * 2, 16), 0.125); } );
EDITOR_BIND(reload, "held(CTRL) & down(F5)", { window_reload(); } );
EDITOR_BIND(filter, "held(CTRL) & down(F)", { editor.filter ^= 1; } );
// alt keys
EDITOR_BIND(quit, "held(ALT) & down(F4)", { record_stop(), exit(0); } );
EDITOR_BIND(mute, "held(ALT) & down(M)", { audio_volume_master( 1 ^ !!audio_volume_master(-1) ); } );
EDITOR_BIND(gamepad, "held(ALT) & down(G)", { editor.gamepad ^= 1; } );
EDITOR_BIND(transparent, "held(ALT) & down(T)", { editor.transparent ^= 1; } );
EDITOR_BIND(record, "held(ALT) & down(Z)", { if(record_active()) record_stop(), ui_notify(va("Video recorded"), date_string()); else { char *name = file_counter(va("%s.mp4",app_name())); app_beep(), window_record(name); } } );
EDITOR_BIND(screenshot, "held(ALT) & down(S)", { char *name = file_counter(va("%s.png",app_name())); window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string()); } );
EDITOR_BIND(battery, "held(ALT) & down(B)", { editor.battery ^= 1; } );
EDITOR_BIND(outliner, "held(ALT) & down(O)", { ui_show("Outliner", ui_visible("Outliner") ^ true); } );
EDITOR_BIND(profiler, "held(ALT) & down(P)", { ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true)); } );
EDITOR_BIND(fullscreen, "(held(ALT)&down(ENTER))|down(F11)",{ record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); } ); // close any recording before framebuffer resizing, which would corrupt video stream
EDITOR_BIND(unlit, "held(ALT) & down(U)", { editor.unlit ^= 1; } );
EDITOR_BIND(ddraw, "held(ALT) & down(D)", { editor.ddraw ^= 1; } );
void editor_pump() {
for each_array(editor_binds,editor_bind_t,b) {
if( input_eval(b.bindings) ) {
editor_send(b.command);
}
}
do_threadlock(console_lock) {
for each_array_ptr(editor_jobs,char*,cmd) {
if( (*cmd)[0] ) {
int found = 0;
for each_array(editor_binds,editor_bind_t,b) {
if( !strcmpi(b.command, *cmd)) {
b.fn();
found = 1;
break;
}
}
if( !found ) {
// alert(va("Editor: could not handle `%s` command.", *cmd));
(*cmd)[0] = '\0'; strcatf(&(*cmd), "\1%s\n", "Err\n"); (*cmd)[0] = '\0';
}
if( (*cmd)[0] ) {
(*cmd)[0] = '\0'; strcatf(&(*cmd), "\1%s\n", "Ok\n"); (*cmd)[0] = '\0';
}
}
}
}
}
// ----------------------------------------------------------------------------------------
void editor_symbol(int x, int y, const char *sym) {
#define FONT_SYMBOLS FONT_FACE2
#define FONT_WHITE FONT_COLOR1
#define FONT_YELLOW FONT_COLOR2
#define FONT_ORANGE FONT_COLOR3
#define FONT_CYAN FONT_COLOR4
// style: atlas size, unicode ranges and 6 font faces max
do_once font_face(FONT_SYMBOLS, "MaterialIconsSharp-Regular.otf", 24.f, FONT_EM|FONT_1024);
// style: 10 colors max
do_once font_color(FONT_WHITE, WHITE);
do_once font_color(FONT_YELLOW, YELLOW);
do_once font_color(FONT_CYAN, CYAN);
do_once font_color(FONT_ORANGE, ORANGE);
font_goto(x,y);
font_print(va(FONT_SYMBOLS FONT_WHITE FONT_H1 "%s", sym));
}
#if 0
void editor_gizmos() {
// draw gizmos, aabbs, markers, etc
for each_map_ptr(*editor_selected_map(),void*,o,int,selected) {
if(*selected && obj_draw[obj_typeid(*o)]) {} else continue;
void *obj = *o;
// get transform
vec3 *p = NULL;
vec3 *r = NULL;
vec3 *s = NULL;
aabb *a = NULL;
for each_objmember(o,TYPE,NAME,PTR) {
/**/ if( !strcmp(NAME, "position") ) p = PTR;
else if( !strcmp(NAME, "rotation") ) r = PTR;
else if( !strcmp(NAME, "scale") ) s = PTR;
else if( !strcmp(NAME, "aabb") ) a = PTR;
}
// debugdraw
ddraw_ontop_push(0);
#if 0
// bounding box
aabb *box = obj_aabb(obj);
if( box ) {
ddraw_color_push(YELLOW);
ddraw_aabb(box->min, box->max);
ddraw_color_pop();
}
#endif
// position marker
if( p ) {
static map(void*, vec3) prev_dir = 0;
do_once map_init_ptr(prev_dir);
vec3* dir = map_find_or_add(prev_dir, obj, vec3(1,0,0));
static map(void*, vec3) prev_pos = 0;
do_once map_init_ptr(prev_pos);
vec3* found = map_find_or_add(prev_pos, obj, *p), fwd = sub3(*p, *found);
if( (fwd.y = 0, len3sq(fwd)) ) {
*found = *p;
*dir = norm3(fwd);
}
// float diameter = len2( sub2(vec2(box->max.x,box->max.z), vec2(box->min.x,box->min.z) ));
// float radius = diameter * 0.5;
ddraw_position_dir(*p, *dir, 1);
}
ddraw_ontop(1);
// transform gizmo
if( p && r && s ) {
gizmo(p,r,s);
}
ddraw_ontop_pop();
}
}
#endif
void editor_frame( void (*game)(unsigned, float, double) ) {
do_once {
set_init_ptr(editor.world);
//set_init_ptr(editor.selection);
profiler_enable( false );
window_pause( true );
window_cursor_shape(CURSOR_SW_AUTO);
fx_load("editorOutline.fs");
fx_enable(0, 1);
obj_setname(editor.root = obj_new(obj), "Signals");
obj_setname(editor.on_init = obj_new(obj), "onInit");
obj_setname(editor.on_tick = obj_new(obj), "onTick");
obj_setname(editor.on_draw = obj_new(obj), "onDraw");
obj_setname(editor.on_edit = obj_new(obj), "onEdit");
obj_setname(editor.on_quit = obj_new(obj), "onQuit");
obj_attach(editor.root, editor.on_init);
obj_attach(editor.root, editor.on_tick);
obj_attach(editor.root, editor.on_draw);
obj_attach(editor.root, editor.on_edit);
obj_attach(editor.root, editor.on_quit);
editor_seticoninstance(editor.root, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.on_init, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.on_tick, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.on_draw, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.on_edit, ICON_MDI_SIGNAL_VARIANT);
editor_seticoninstance(editor.on_quit, ICON_MDI_SIGNAL_VARIANT);
editor_seticonclass(obj_type(editor.root), ICON_MDI_CUBE_OUTLINE);
}
// game tick
game(editor.frame, editor.dt, editor.t);
// timing
editor.dt = clampf(window_delta(), 0, 1/60.f) * !window_has_pause() * editor.slomo;
editor.t += editor.dt;
editor.frame += !window_has_pause();
editor.frame += !editor.frame;
// process inputs & messages
editor_pump();
// adaptive framerate
int app_on_background = !window_has_focus();
int hz = app_on_background ? editor.hz_low : editor.battery ? editor.hz_medium : editor.hz_high;
window_fps_lock( hz < 5 ? 5 : hz );
// draw menubar
static int stats_mode = 1;
static double last_fps = 0; if(!window_has_pause()) last_fps = window_fps();
const char *STATS = va("x%4.3f %03d.%03dss %02dF %s",
editor.slomo, (int)editor.t, (int)(1000 * (editor.t - (int)editor.t)),
(editor.frame-1) % ((int)window_fps_target() + !(int)window_fps_target()),
stats_mode == 1 ? va("%5.2f/%dfps", last_fps, (int)window_fps_target()) : stats_mode == 0 ? "0/0 KiB" : xstats());
const char *ICON_PL4Y = window_has_pause() ? ICON_MDI_PLAY : ICON_MDI_PAUSE;
const char *ICON_SKIP = window_has_pause() ? ICON_MDI_STEP_FORWARD/*ICON_MDI_SKIP_NEXT*/ : ICON_MDI_FAST_FORWARD;
int ingame = !editor.active;
UI_MENU(13, \
if(ingame) ui_disable(); \
UI_MENU_POPUP(ICON_MD_SETTINGS, vec2(0.33,1.00), ui_debug()) \
if(ingame) ui_enable(); \
UI_MENU_ITEM(ICON_PL4Y, if(editor.t == 0) editor_send("eject"); editor_send(window_has_pause() ? "play" : "pause")) \
UI_MENU_ITEM(ICON_SKIP, editor_send(window_has_pause() ? "frame" : "slomo")) \
UI_MENU_ITEM(ICON_MDI_STOP, editor_send("stop")) \
UI_MENU_ITEM(ICON_MDI_EJECT, editor_send("eject")) \
UI_MENU_ITEM(STATS, stats_mode = (++stats_mode) % 3) \
UI_MENU_ALIGN_RIGHT(32+32+32+32+32+34) \
if(ingame) ui_disable(); \
UI_MENU_ITEM(ICON_MDI_FILE_TREE, editor_send("scene")) \
UI_MENU_ITEM(ICON_MD_FOLDER_SPECIAL, editor_send("browser")) \
UI_MENU_ITEM(ICON_MDI_CHART_TIMELINE, editor_send("timeline")) \
UI_MENU_ITEM(ICON_MDI_CONSOLE, editor_send("console")) \
UI_MENU_ITEM(ICON_MD_SEARCH, editor_send("filter")) \
if(ingame) ui_enable(); \
UI_MENU_ITEM(ICON_MD_CLOSE, editor_send("quit")) \
);
if( !editor.active ) return;
// draw edit view (gizmos, position markers, etc).
for each_set_ptr(editor.world,obj*,o) {
if( obj_hasmethod(*o,edit) ) {
obj_edit(*o);
}
if( obj_hasmethod(*o,icon) ) {
obj_icon(*o);
}
}
#if 0
font_print(FONT_CENTER FONT_MIDDLE FONT_H1 FONT_SYMBOLS FONT_WHITE ""
ICON_MD_WB_IRIDESCENT
ICON_MD_WB_INCANDESCENT
ICON_MD_FLARE
ICON_MD_WB_SUNNY
ICON_MD_LIGHT_MODE
ICON_MD_LIGHT
ICON_MD_FLASHLIGHT_OFF
ICON_MD_FLASHLIGHT_ON
ICON_MD_HIGHLIGHT
ICON_MD_HIGHLIGHT_ALT
ICON_MD_LIGHTBULB
ICON_MD_LIGHTBULB_OUTLINE
ICON_MD_NIGHTLIGHT
ICON_MD_NIGHTLIGHT_ROUND
);
#endif
// draw silhouettes
sprite_flush();
for each_map_ptr(*editor_selected_map(),void*,o,int,selected) {
if( *selected && obj_hasmethod(*o,draw) ) {
fx_begin();
obj_draw(*o);
sprite_flush();
fx_end();
}
}
// draw box selection
if( !ui_active() ) { //< check that we're not moving a window
static vec2 from = {0}, to = {0};
if( input_down(MOUSE_L) ) to = vec2(input(MOUSE_X), input(MOUSE_Y)), from = to;
if( input(MOUSE_L) ) to = vec2(input(MOUSE_X), input(MOUSE_Y));
if( len2sq(sub2(from,to)) > 0 ) {
vec2 a = min2(from, to), b = max2(from, to);
ddraw_push_2d();
ddraw_color_push(YELLOW);
ddraw_line( vec3(a.x,a.y,0),vec3(b.x-1,a.y,0) );
ddraw_line( vec3(b.x,a.y,0),vec3(b.x,b.y-1,0) );
ddraw_line( vec3(b.x,b.y,0),vec3(a.x-1,b.y,0) );
ddraw_line( vec3(a.x,b.y,0),vec3(a.x,a.y-1,0) );
ddraw_color_pop();
ddraw_pop_2d();
}
if( input_up(MOUSE_L) ) {
vec2 a = min2(from, to), b = max2(from, to);
from = to = vec2(0,0);
editor_select_aabb(aabb(vec3(a.x,a.y,0),vec3(b.x,b.y,0)));
}
}
//
aabb box = {0};
aabb mouse = { vec3(input(MOUSE_X),input(MOUSE_Y),0), vec3(input(MOUSE_X),input(MOUSE_Y),1)};
for each_set(editor.world,obj*,o) {
if( !obj_hasmethod(o, aabb) ) continue;
if( !obj_aabb(o, &box) ) continue;
// draw aabb
if( 1 ) {
ddraw_color_push(YELLOW);
ddraw_push_2d();
ddraw_aabb(box.min, box.max);
ddraw_aabb(mouse.min, mouse.max);
ddraw_pop_2d();
ddraw_color_pop();
}
// trigger contextual inspector
if( input_down(MOUSE_R) ) {
int selected = editor_selected(o);
editor_setpopup(o, selected);
}
// draw contextual inspector
if( editor_popup(o) ) {
if( editor_begin(va("%s (%s)", obj_name(o), obj_type(o)),3) ) {
ui_label2(obj_name(o), obj_type(o));
editor_inspect(o);
editor_end(3);
} else {
editor_setpopup(o, 0);
}
}
}
// draw subeditors
static int preferred_window_mode = 1; // panel(0), window(1), nk_window(2), nk_popup(3)
static struct nk_color bak, *on = 0; do_once bak = ui_ctx->style.window.fixed_background.data.color; // ui_ctx->style.window.fixed_background.data.color = !!(on = (on ? NULL : &bak)) ? AS_NKCOLOR(0) : bak; };
if( editor.transparent ) ui_ctx->style.window.fixed_background.data.color = AS_NKCOLOR(0);
for each_array(editor.subeditors, subeditor, fn) {
fn(preferred_window_mode);
}
ui_ctx->style.window.fixed_background.data.color = bak;
// draw ui filter (note: render at end-of-frame, so it's hopefully on-top)
editor_filter();
}
#include "v4k_editor1_scene.h"
#include "v4k_editor2_browser.h"
#include "v4k_editor3_timeline.h"
#include "v4k_editor4_console.h"

View File

@ -0,0 +1,184 @@
#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_PROPERTY(bookmarked, int, 0);
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() ) {
API int editor_send(const char *);
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 = 1; // 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

@ -0,0 +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 = 1; // 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,50 +1,7 @@
int ui_window_nk(const char *title, void *open) {
int ww = window_width(); int w = ww * 0.66;
int hh = window_height(); int h = hh * 0.66;
#define TIMELINE_ICON ICON_MDI_CHART_TIMELINE
#define TIMELINE_TITLE "Timeline " TIMELINE_ICON
nk_flags flags = NK_WINDOW_TITLE | NK_WINDOW_BORDER |
NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE |
NK_WINDOW_CLOSABLE | NK_WINDOW_MINIMIZABLE | NK_WINDOW_MAXIMIZABLE |
NK_WINDOW_PINNABLE |
0; // NK_WINDOW_SCROLL_AUTO_HIDE;
if (nk_begin(ui_ctx, title, nk_rect( (ww-w)/2,(hh-h)/2, w,h), flags))
return 1;
nk_end(ui_ctx);
return 0;
}
int ui_window_nk_end() {
nk_end(ui_ctx);
return 0;
}
#define AS_NKCOLOR(color) \
((struct nk_color){ ((color>>16))&255,((color>>8))&255,((color>>0))&255,((color>>24))&255 })
/*
typedef struct tween_keyframe_t {
int easing_mode;
float t;
vec3 v;
} tween_keyframe_t;
typedef struct tween_t {
array(tween_keyframe_t) keyframes;
vec3 result;
float time;
float duration;
} tween_t;
API tween_t tween();
API float tween_update(tween_t *tw, float dt);
API void tween_reset(tween_t *tw);
API void tween_destroy(tween_t *tw);
API void tween_keyframe_set(tween_t *tw, float t, int easing_mode, vec3 v);
API void tween_keyframe_unset(tween_t *tw, float t);
*/
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;
@ -115,15 +72,15 @@ int ui_tween(const char *label, tween_t *t) {
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, c); // AS_NKCOLOR(track_color));
nk_fill_rect(canvas, pos, ROUNDING, k->ease == EASE_NOP ? 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( int i = 0, j = 0; i < baseline.w; i += PIXELS_PER_SECOND/10, ++j ) {
int len = !(j%10) ? MARKER10_HEIGHT : !(j%5) ? MARKER5_HEIGHT : MARKER1_HEIGHT;
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));
}
@ -146,14 +103,13 @@ int ui_tween(const char *label, tween_t *t) {
}
// keys ui
static int num_eases = 0; if(!num_eases) while(num_eases[ease_enums()]) ++num_eases;
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("easing", ease_enums(), num_eases, &k->easing_mode );
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();
}
}
@ -166,7 +122,7 @@ tween_t* rand_tween() {
int num_keys = randi(2,8);
double t = 0;
for( int i = 0; i < num_keys; ++i) {
tween_keyframe_set(&demo, t, randi(0,33), scale3(vec3(randf(),randf(),randf()),randi(-5,5)) );
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));
@ -174,9 +130,9 @@ tween_t* rand_tween() {
return p;
}
static array(tween_t*) tweens;
int editor_timeline(int window_mode) {
static array(tween_t*) tweens = 0;
int editor_timeline() {
do_once {
array_push(tweens, rand_tween());
}
@ -191,8 +147,7 @@ int editor_timeline() {
}
static void *selected = NULL;
static int open = 1;
if( ui_window/*_nk*/("Timeline " ICON_MDI_CHART_TIMELINE, &open) ) {
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());
@ -207,7 +162,7 @@ int editor_timeline() {
if(ui_label_icon_clicked_L.x) selected = (t != selected) ? t : NULL;
}
ui_window_end/*_nk_end*/();
editor_end(window_mode);
}
return 0;
}

View File

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

View File

@ -0,0 +1,396 @@
AUTOTEST {
test_obj_core();
}
// ----------------------------------------------------------------------------
typedef struct orc { OBJ
char name[8];
} orc;
//#pragma pack(push, 1)
typedef struct boy { OBJ
char name[8];
float hp;
} boy;
//#pragma pack(pop)
OBJTYPEDEF(orc,100)
int orc_ctor(orc* self) { strcpy(self->name, "orc!"); printf("i'm orc %p\n", self); return 0; }
int orc_tick(orc* self) { printf("%p orc tick\n", self); return 0; }
char* orc_save(orc *self) { return obj_saveini(self); } // PACKMSG("ss", "orc_v1", self->name); }
OBJTYPEDEF(boy,101)
int boy_ctor(boy* self) { strcpy(self->name, "boy!"); printf("i'm boy %p\n", self); self->hp = 0x80; return 0; }
int boy_tick(boy* self) { printf("%p boy tick, hp:%f\n", self, self->hp); return 0; }
char* boy_save(boy *self) { return obj_saveini(self); } // PACKMSG("ssf", "boy_v1", self->name, self->hp); }
AUTOTEST {
obj_extend(orc, ctor);
obj_extend(orc, tick);
obj_extend(orc, save);
obj_extend(boy, ctor);
obj_extend(boy, tick);
obj_extend(boy, save);
}
AUTOTEST {
// instance gameobjs
boy *obj1 = obj_new(boy, "boy", 123);
orc *obj2 = obj_new(orc, "orc");
orc *obj3 = obj_new(orc, "orc");
printf("%p\n", obj_datac(obj1));
printf("%d vs %d vs %d\n", (int)sizeof(boy), obj_sizeof(obj1), obj_size(obj1));
// generics
obj_tick(obj1);
obj_tick(obj2);
obj_tick(obj3);
obj_hexdump(obj1);
obj_free(obj1); // will free
obj_ref(obj2);
obj_unref(obj2); // will free
obj_ref(obj3);
obj_free(obj3); // will do nothing
obj_unref(obj3); // will free
// make a dangling reference. this will be printed at end of program.
static int dangling;
obj1 = obj_new(boy, "boy", 123);
obj_ref(obj1);
}
// --- scene
AUTOTEST {
test_obj_scene();
}
// --- comps
struct WorldTravellerComponent {
unsigned world_source;
unsigned world_target;
};
struct TransformComponent {
vec3 position;
quat rotation;
vec3 scale;
};
struct VelocityComponent {
float speed;
};
struct LookComponent {
float sensitivity; // _and_polarity;
vec2 rotation;
};
struct PhysicsComponent {
float gravity;
vec3 velocity;
vec3 acceleration;
vec3 rotationVelocity;
vec3 rotationAcceleration;
};
struct RenderComponent {
aabb box;
mesh_t mesh;
texture_t texture;
};
AUTOTEST {
test_obj_ecs();
}
// generics
// --- example: new class
// declare new object
TYPEDEF_STRUCT(box,102,
int x,y,w,h;
);
// /* same as: */
// typedef struct box { OBJ
// int x,y,w,h;
// } box;
// OBJTYPEDEF(box,102)
// implement a few built-in interfaces
#define box(...) obj(box, __VA_ARGS__)
char* box_save(const box *b) { return obj_saveini(b); } // PACKMSG("siiii", "box_v1", b->x, b->y, b->w, b->h); }
bool box_load(box *b, const char* s) { return !!obj_loadini(b, s); } // char *header; return UNPACKMSG(s, "siiii", &header, &b->x, &b->y, &b->w, &b->h) && !strcmp(header, "box_v1"); }
int box_test(box *b) { return b->w > 0 && b->h > 0; }
void box_dtor(box *b) { puts("bye box!"); }
// create a new obj interface (not only for box! valid for every other obj that would extend on this)
obj_vtable(area, float, { return 0; });
#define obj_area(o,...) obj_method(area, o, ##__VA_ARGS__)
// implement area interface for box object
float box_area(box *b) { return b->w * b->h; }
AUTOTEST {
// reflect
STRUCT(box,int,x);
STRUCT(box,int,y);
STRUCT(box,int,w);
STRUCT(box,int,h);
// extend
obj_extend(box,save);
obj_extend(box,load);
obj_extend(box,test);
obj_extend(box,dtor);
obj_extend(box,area);
// -- example
box b = box(0,0,2,3);
box *c = obj_new(box, 1,1,3,4);
test( obj_test(&b) );
test( obj_test(c) );
test( obj_area(&b) == 6 );
test( obj_area(c) == 12 );
// serialization tests
test_obj_serialization(&b, c);
test_obj_similarity(&b, c);
char *sav = obj_save(c);
obj_load(&b, sav);
// obj_load(&b, obj_save(c)); // @fixme: this expression in mingw
test_obj_equality(&b, c);
}
// --- reflection
typedef struct MyVec3 { OBJ
float x,y,z;
} MyVec3;
OBJTYPEDEF(MyVec3,77)
typedef struct MyTransform {
MyVec3 location; ///R @todo: ideally, we would want self-reflection to inscribe these members
MyVec3 rotation; ///R @todo: ideally, we would want self-reflection to inscribe these members
float scale; ///R @todo: ideally, we would want self-reflection to inscribe these members
} MyTransform;
/*
// @todo: which is technically like doing
AUTORUN {
STRUCT(MyTransform, MyVec3, location);
STRUCT(MyTransform, MyVec3, rotation);
STRUCT(MyTransform, float, scale);
}
*/
OBJTYPEDEF(MyTransform,78);
typedef struct MyObject { OBJ
char *id;
int spawnx,spawny;
float time;
struct MyObject *next;
MyTransform tr;
} MyObject;
OBJTYPEDEF(MyObject,79);
AUTOTEST {
STRUCT( MyVec3, float, x, "Right" );
STRUCT( MyVec3, float, y, "Forward" );
STRUCT( MyVec3, float, z, "Up" );
STRUCT( MyTransform, MyVec3, location, "World location (absolute)" );
STRUCT( MyTransform, MyVec3, rotation, "Local rotation (in degrees)" );
STRUCT( MyTransform, float, scale, "Local scale (in centimeters)" );
STRUCT( MyObject, int, spawnx, "Actor Spawn X" );
STRUCT( MyObject, int, spawny, "Actor Spawn Y" );
STRUCT( MyObject, string, id, "Actor name" );
STRUCT( MyObject, MyTransform, tr, "Actor transform" );
STRUCT( MyObject, float, time, "Actor time" );
STRUCT( MyObject, MyObject *, next, "Next actor in seq" );
// reflect_print("MyVec3");
// reflect_print("MyTransform");
// reflect_print("MyObject");
// construct a type from a reflected struct
MyVec3 *o = obj_new(MyVec3, 1,2,-3);
test( 12 == obj_size(o) );
MyVec3 *o2 = obj_make("[MyVec3]\nfloat.y=2\nfloat.x=1\nfloat.z=-3");
test( !obj_comp(o,o2) ) || obj_hexdump(o), obj_hexdump(o2);
for each_objmember(o,type,name,ptr) {
/**/ if( !strcmp(type, "float") ) printf("%s %s = %f\n", type, name, *(float*)ptr );
else if( !strcmp(type, "double") ) printf("%s %s = %f\n", type, name, *(double*)ptr );
}
test_obj_console(o);
test_obj_console(o2);
// Setup objects
MyObject *root = obj_make("[MyObject]");
obj_hexdump(root);
MyObject *oo = obj_make("[MyObject]\nid=\"An identifier!\"\nx=123\ny=256\nrotation=90\nnext=root\n");
obj_hexdump(oo);
// Dump contents of our objects
obj_print(oo);
puts("---");
obj_hexdump(oo);
puts("---");
// Save to mem
char *sav = obj_savebin(oo);
test( sav && strlen(sav) > 0 );
// Clear
obj_zero(oo);
obj_hexdump(oo);
puts("---");
// Reload
obj_loadbin(oo, sav);
obj_hexdump(oo);
}
// --- Benchmarks for call overhead.
AUTOTEST {
// Here, we're using a blank ctor call as a method to test/stress call overhead.
//
// results (old i5-4300/1.90Ghz laptop):
// v1: 427 million calls/s. compiled with "cl /Ox /Os /MT /DNDEBUG /GL /GF /arch:AVX2"
// v2: 450 million calls/s. compiled with "cl /Ox /O2 /MT /DNDEBUG /GF /arch:AVX2"
double t;
enum { N = 100000000 };
t = -time_ss();
MyVec3 o = obj(MyVec3, 1,2,3); obj_setname(&o, "MyVec3");
for( unsigned i = 0; i < N; ++i ) {
obj_ctor(&o);
}
t += time_ss();
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
t = -time_ss();
MyVec3 *op = obj_new(MyVec3, 1,2,3);
for( unsigned i = 0; i < N; ++i ) {
obj_ctor(op);
}
t += time_ss();
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
}
// --- metas
AUTOTEST {
box b = box(1,2,3,4);
test( !strcmp("box", obj_type(&b)) );
test( !strcmp("box", obj_name(&b)) );
b = box(1,2,3,4);
obj_setname(&b, "MyBox1");
test( !strcmp("box", obj_type(&b)) );
test( !strcmp("MyBox1", obj_name(&b)) );
test_obj_metadatas(&b);
obj_free(&b);
}
TYPEDEF_STRUCT(test_vec3_serial, __COUNTER__,
vec3 position;
vec3 accel;
);
AUTOTEST {
STRUCT(test_vec3_serial, vec3, position);
STRUCT(test_vec3_serial, vec3, accel);
test_vec3_serial v = obj(test_vec3_serial, {1,2,3},{4,5,6}), z = obj(test_vec3_serial);
test(obj_comp(&v,&z) != 0) || obj_print(&v) & obj_print(&z);
obj_loadini(&z, obj_saveini(&v));
test(obj_comp(&v,&z) == 0) || obj_print(&z);
}
TYPEDEF_STRUCT(MyObject2, __COUNTER__,
const char* id;
int x,y;
float rotation;
struct MyObject2 *next;
);
AUTORUN {
STRUCT(MyObject2, const char *, id);
STRUCT(MyObject2, int, x);
STRUCT(MyObject2, int, y);
STRUCT(MyObject2, float, rotation);
STRUCT(MyObject2, struct MyObject2*, next);
// Construct two objects
MyObject2 *root = obj_new(MyObject2);
MyObject2 *o = obj_new(MyObject2, "An identifier!", 0x11, 0x22, 3.1415f, root );
// Copy tests
{
printf("%d vs %d vs %d\n", (int)sizeof(MyObject2), obj_size(o), (int)sizeof(obj) + obj_size(o) + (int)sizeof(array(void*)));
MyObject2 *clone = obj_clone(o);
test(obj_comp(clone,o) == 0) || obj_print(o) & obj_print(clone) & obj_hexdump(o) & obj_hexdump(clone);
test(obj_free(clone) == 0);
}
{
MyObject2 *copy = obj_new(MyObject2);
test(obj_copy(copy, o));
test(obj_comp(copy,o)==0) || obj_print(copy);
test(obj_free(copy) == 0);
}
{
MyObject2 *copy = obj_new(MyObject2, "A different identifier!", 0x33, 0x44, 0.0f, root );
test(obj_copy(copy, o));
test(obj_comp(copy,o)==0) || obj_print(copy);
test(obj_free(copy) == 0);
}
{
void *copy = obj_malloc(100); // untyped class
obj_mutate(copy, o);
obj_print(copy);
obj_copy(copy, o);
obj_print(copy);
obj_free(copy);
}
}

View File

@ -1,14 +1,8 @@
[myWindow]
x=0.166667
y=0.194444
w=0.666667
h=0.666667
visible=1
[Outliner]
x=0.000000
y=0.106026
w=0.295716
h=0.893974
x=-0.000000
y=0.052117
w=0.292969
h=0.946206
visible=1
[Properties]
x=0.723415
@ -16,3 +10,9 @@ y=0.052117
w=0.276585
h=0.946236
visible=1
[Scene 󰙅]
x=-0.000000
y=0.052117
w=0.209707
h=0.946108
visible=1