sync fwk
parent
6aa3fc1d4a
commit
fb515681b1
27
bind/v4k.lua
27
bind/v4k.lua
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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* );
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) ) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
588
engine/v4k
588
engine/v4k
File diff suppressed because it is too large
Load Diff
258
engine/v4k.c
258
engine/v4k.c
|
@ -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") {
|
||||
|
|
58
engine/v4k.h
58
engine/v4k.h
|
@ -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
|
||||
|
|
2
hello.c
2
hello.c
|
@ -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 |
|
@ -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 ↦ \
|
||||
} \
|
||||
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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ↦ \
|
||||
} \
|
||||
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"
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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,11 +162,11 @@ 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;
|
||||
}
|
||||
|
||||
AUTORUN {
|
||||
array_push(editor.subeditors, editor_timeline);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue