sync with FWK
parent
4e871b7612
commit
5edc8228c7
43
MAKE.bat
43
MAKE.bat
|
@ -590,6 +590,17 @@ if "!cc!"=="cl" (
|
||||||
echo build=!build!, type=!dll!, cc=!cc!, other=!other!, args=!args!
|
echo build=!build!, type=!dll!, cc=!cc!, other=!other!, args=!args!
|
||||||
echo import=!import!, export=!export!
|
echo import=!import!, export=!export!
|
||||||
|
|
||||||
|
rem set BUILD_VERSION symbol
|
||||||
|
git describe --tags --abbrev=0 > info.obj
|
||||||
|
set /p VERSION=<info.obj
|
||||||
|
git rev-list --count --first-parent HEAD > info.obj
|
||||||
|
set /p GIT_REVISION=<info.obj
|
||||||
|
git rev-parse --abbrev-ref HEAD > info.obj
|
||||||
|
set /p GIT_BRANCH=<info.obj
|
||||||
|
date /t > info.obj
|
||||||
|
set /p LAST_MODIFIED=<info.obj
|
||||||
|
set args=-DBUILD_VERSION="\"!GIT_BRANCH!-!GIT_REVISION!-!build!-!dll!\"" !args!
|
||||||
|
|
||||||
if "!cc!"=="tcc" set "cc=call tools\tcc"
|
if "!cc!"=="tcc" set "cc=call tools\tcc"
|
||||||
|
|
||||||
rem detect wether user-defined sources use single-header distro
|
rem detect wether user-defined sources use single-header distro
|
||||||
|
@ -657,16 +668,28 @@ for %%f in ("workbench\plugins\*.c") do (
|
||||||
|
|
||||||
rem demos
|
rem demos
|
||||||
if "!demos!"=="yes" (
|
if "!demos!"=="yes" (
|
||||||
!echo! 00-ui && !cc! !o! 00-ui.exe demos\00-ui.c !import! !args! || set rc=1
|
!echo! 00-ui && !cc! !o! 00-ui.exe demos\00-ui.c !import! !args! || set rc=1
|
||||||
!echo! 01-sprite && !cc! !o! 01-sprite.exe demos\01-sprite.c !import! !args! || set rc=1
|
!echo! 00-loop && !cc! !o! 00-loop.exe demos\00-loop.c !import! !args! || set rc=1
|
||||||
!echo! 02-ddraw && !cc! !o! 02-ddraw.exe demos\02-ddraw.c !import! !args! || set rc=1
|
!echo! 00-script && !cc! !o! 00-script.exe demos\00-script.c !import! !args! || set rc=1
|
||||||
!echo! 02-frustum && !cc! !o! 02-frustum.exe demos\02-frustum.c !import! !args! || set rc=1
|
!echo! 00-hello && !cc! !o! 00-hello.exe demos\00-hello.c !import! !args! || set rc=1
|
||||||
!echo! 03-anims && !cc! !o! 03-anims.exe demos\03-anims.c !import! !args! || set rc=1
|
!echo! 01-sprite && !cc! !o! 01-sprite.exe demos\01-sprite.c !import! !args! || set rc=1
|
||||||
!echo! 04-actor && !cc! !o! 04-actor.exe demos\04-actor.c !import! !args! || set rc=1
|
!echo! 01-demo2d && !cc! !o! 01-demo2d.exe demos\01-demo2d.c !import! !args! || set rc=1
|
||||||
!echo! 04-lod && !cc! !o! 04-lod.exe demos\04-lod.c !import! !args! || set rc=1
|
!echo! 01-easing && !cc! !o! 01-easing.exe demos\01-easing.c !import! !args! || set rc=1
|
||||||
!echo! 05-scene && !cc! !o! 05-scene.exe demos\05-scene.c !import! !args! || set rc=1
|
!echo! 01-font && !cc! !o! 01-font.exe demos\01-font.c !import! !args! || set rc=1
|
||||||
!echo! 06-controller && !cc! !o! 06-controller.exe demos\06-controller.c !import! !args! || set rc=1
|
!echo! 02-ddraw && !cc! !o! 02-ddraw.exe demos\02-ddraw.c !import! !args! || set rc=1
|
||||||
!echo! 07-network && !cc! !o! 07-network.exe demos\07-network.c !import! !args! || set rc=1
|
!echo! 02-frustum && !cc! !o! 02-frustum.exe demos\02-frustum.c !import! !args! || set rc=1
|
||||||
|
!echo! 03-anims && !cc! !o! 03-anims.exe demos\03-anims.c !import! !args! || set rc=1
|
||||||
|
!echo! 04-actor && !cc! !o! 04-actor.exe demos\04-actor.c !import! !args! || set rc=1
|
||||||
|
!echo! 04-lod && !cc! !o! 04-lod.exe demos\04-lod.c !import! !args! || set rc=1
|
||||||
|
!echo! 05-scene && !cc! !o! 05-scene.exe demos\05-scene.c !import! !args! || set rc=1
|
||||||
|
!echo! 06-controller && !cc! !o! 06-controller.exe demos\06-controller.c !import! !args! || set rc=1
|
||||||
|
!echo! 06-material && !cc! !o! 06-material.exe demos\06-material.c !import! !args! || set rc=1
|
||||||
|
!echo! 07-network && !cc! !o! 07-network.exe demos\07-network.c !import! !args! || set rc=1
|
||||||
|
!echo! 07-netsync && !cc! !o! 07-netsync.exe demos\07-netsync.c !import! !args! || set rc=1
|
||||||
|
!echo! 08-audio && !cc! !o! 08-audio.exe demos\08-audio.c !import! !args! || set rc=1
|
||||||
|
!echo! 08-video && !cc! !o! 08-video.exe demos\08-video.c !import! !args! || set rc=1
|
||||||
|
!echo! 09-cubemap && !cc! !o! 09-cubemap.exe demos\09-cubemap.c !import! !args! || set rc=1
|
||||||
|
!echo! 09-shadertoy && !cc! !o! 09-shadertoy.exe demos\09-shadertoy.c !import! !args! || set rc=1
|
||||||
)
|
)
|
||||||
|
|
||||||
rem lab
|
rem lab
|
||||||
|
|
|
@ -128,10 +128,6 @@ int main() {
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
if( ui_panel("Audio", 0)) {
|
if( ui_panel("Audio", 0)) {
|
||||||
static float bgm = 1, sfx = 1, master = 1;
|
|
||||||
if( ui_slider2("BGM", &bgm, va("%.2f", bgm))) audio_volume_stream(bgm);
|
|
||||||
if( ui_slider2("SFX", &sfx, va("%.2f", sfx))) audio_volume_clip(sfx);
|
|
||||||
if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master);
|
|
||||||
if( ui_label2_toolbar("BGM: Track #1", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream1, AUDIO_SINGLE_INSTANCE);
|
if( ui_label2_toolbar("BGM: Track #1", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream1, AUDIO_SINGLE_INSTANCE);
|
||||||
if( ui_label2_toolbar("BGM: Track #2", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream2, AUDIO_SINGLE_INSTANCE);
|
if( ui_label2_toolbar("BGM: Track #2", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream2, AUDIO_SINGLE_INSTANCE);
|
||||||
if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(clip1, 0);
|
if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(clip1, 0);
|
||||||
|
|
|
@ -0,0 +1,871 @@
|
||||||
|
#define V4K_IMPLEMENTATION
|
||||||
|
#include "joint/v4k.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
A basic node-based UI built with Nuklear.
|
||||||
|
Builds on the node editor example included in Nuklear v1.00, with the aim of
|
||||||
|
being used as a prototype for implementing a functioning node editor.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Nodes of different types. Currently their implementations are #included in
|
||||||
|
the main file, but they could easily be turned into eg. a plugin system.
|
||||||
|
- Pins/pins of different types -- currently float values and colors.
|
||||||
|
- Adding and removing nodes.
|
||||||
|
- Linking nodes, with validation (one link per input, only link similar pins).
|
||||||
|
- Detaching and moving links.
|
||||||
|
- Evaluation of output values of connected nodes.
|
||||||
|
- Memory management based on fixed size arrays for links and node pointers
|
||||||
|
- Multiple node types
|
||||||
|
- Multiple pin types
|
||||||
|
- Linking between pins of the same type
|
||||||
|
- Detaching and reattaching links
|
||||||
|
- Getting value from linked node if pin is connected
|
||||||
|
|
||||||
|
Todo:
|
||||||
|
- Complete pin types.
|
||||||
|
- Allow dragging from output to input pin.
|
||||||
|
- Cut link by CTRL+clicking input pin.
|
||||||
|
- Cut link by drawing intersect line on a link.
|
||||||
|
- Group elemnts together with mouse, or LSHIFT+clicking.
|
||||||
|
- Drag groups.
|
||||||
|
- DEL elements.
|
||||||
|
- DEL groups.
|
||||||
|
- CTRL-C/CTRL-V/CTRL-X elements.
|
||||||
|
- CTRL-C/CTRL-V/CTRL-X groups.
|
||||||
|
- CTRL-Z,CTRL-Y.
|
||||||
|
- CTRL-N.
|
||||||
|
- CTRL-L,CTRL-S.
|
||||||
|
- CTRL-F.
|
||||||
|
- CTRL-Wheel Zooming.
|
||||||
|
- Allow to extend node types from Lua.
|
||||||
|
|
||||||
|
Extra todo:
|
||||||
|
- Execution Flow (see: nk_stroke_triangle, nk_fill_triangle)
|
||||||
|
- Complete missing nodes (see: nk_draw_image, nk_draw_text)
|
||||||
|
- Right-click could visualize node/board diagram as Lua script.
|
||||||
|
- Once that done, copy/pasting scripts should work within editor.
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
- https://github.com/Immediate-Mode-UI/Nuklear/pull/561
|
||||||
|
- https://github.com/vurtun/nuklear/blob/master/demo/node_editor.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum pin_type_t {
|
||||||
|
type_flow,
|
||||||
|
type_int,type_float,
|
||||||
|
type_block,type_texture,type_image,
|
||||||
|
type_color,
|
||||||
|
/*
|
||||||
|
type_bool,
|
||||||
|
type_char, type_string,
|
||||||
|
type_int2, type_int3, type_int4,
|
||||||
|
type_float2, type_float3, type_float4,
|
||||||
|
type_array, type_map,
|
||||||
|
*/
|
||||||
|
|
||||||
|
type_total
|
||||||
|
} pin_type_t;
|
||||||
|
|
||||||
|
struct node_pin {
|
||||||
|
pin_type_t pin_type;
|
||||||
|
nk_bool is_connected;
|
||||||
|
struct node* connected_node;
|
||||||
|
int connected_pin;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node {
|
||||||
|
int ID;
|
||||||
|
char name[32];
|
||||||
|
struct nk_rect bounds;
|
||||||
|
int input_count;
|
||||||
|
int output_count;
|
||||||
|
struct node_pin *inputs;
|
||||||
|
struct node_pin *outputs;
|
||||||
|
struct {
|
||||||
|
float in_padding_x;
|
||||||
|
float in_padding_y;
|
||||||
|
float in_spacing_y;
|
||||||
|
float out_padding_x;
|
||||||
|
float out_padding_y;
|
||||||
|
float out_spacing_y;
|
||||||
|
} pin_spacing; /* Maybe this should be called "node_layout" and include the bounds? */
|
||||||
|
struct node *next; /* Z ordering only */
|
||||||
|
struct node *prev; /* Z ordering only */
|
||||||
|
|
||||||
|
void* (*eval_func)(struct node*, int oIndex);
|
||||||
|
void (*display_func)(struct nk_context*, struct node*);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_link {
|
||||||
|
struct node* input_node;
|
||||||
|
int input_pin;
|
||||||
|
struct node* output_node;
|
||||||
|
int output_pin;
|
||||||
|
nk_bool is_active;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_linking {
|
||||||
|
int active;
|
||||||
|
struct node *node;
|
||||||
|
int input_id;
|
||||||
|
int input_pin;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_editor {
|
||||||
|
int initialized;
|
||||||
|
struct node *node_buf[32];
|
||||||
|
struct node_link links[64];
|
||||||
|
struct node *output_node;
|
||||||
|
struct node *begin;
|
||||||
|
struct node *end;
|
||||||
|
int node_count;
|
||||||
|
int link_count;
|
||||||
|
struct nk_rect bounds;
|
||||||
|
struct node *selected;
|
||||||
|
int show_grid;
|
||||||
|
struct nk_vec2 scrolling;
|
||||||
|
struct node_linking linking;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* === PROTOTYPES === */
|
||||||
|
/* The node implementations need these two functions. */
|
||||||
|
/* These could/should go in a header file along with the node and node_pin structs and be #included in the node implementations */
|
||||||
|
|
||||||
|
struct node* node_editor_add(struct node_editor *editor, size_t nodeSize, const char *name, struct nk_rect bounds, int in_count, int out_count);
|
||||||
|
void* node_editor_eval_connected(struct node *node, int input_pin_number);
|
||||||
|
/* ================== */
|
||||||
|
|
||||||
|
/* === NODE TYPE IMPLEMENTATIONS === */
|
||||||
|
|
||||||
|
#define NODE_DEFAULT_ROW_HEIGHT 25
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
// #include "node_output.h"
|
||||||
|
|
||||||
|
struct node_type_output {
|
||||||
|
struct node node;
|
||||||
|
struct nk_colorf input_val;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nk_colorf *node_output_get(struct node* node) {
|
||||||
|
struct node_type_output *output_node = (struct node_type_output*)node;
|
||||||
|
if (!node->inputs[0].is_connected) {
|
||||||
|
struct nk_colorf black = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
output_node->input_val = black;
|
||||||
|
}
|
||||||
|
return &output_node->input_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void node_output_display(struct nk_context *ctx, struct node *node) {
|
||||||
|
if (node->inputs[0].is_connected) {
|
||||||
|
struct node_type_output *output_node = (struct node_type_output*)node;
|
||||||
|
output_node->input_val = *(struct nk_colorf*)node_editor_eval_connected(node, 0);
|
||||||
|
nk_layout_row_dynamic(ctx, 60, 1);
|
||||||
|
nk_button_color(ctx, nk_rgba_cf(output_node->input_val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* node_output_create(struct node_editor *editor, struct nk_vec2 position) {
|
||||||
|
struct node_type_output *output_node = (struct node_type_output*)node_editor_add(editor, sizeof(struct node_type_output), "Output", nk_rect(position.x, position.y, 100, 100), 1, 0);
|
||||||
|
if (output_node){
|
||||||
|
output_node->node.inputs[0].pin_type = type_color;
|
||||||
|
output_node->node.display_func = node_output_display;
|
||||||
|
}
|
||||||
|
return (struct node*)output_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
// #include "node_float.h"
|
||||||
|
|
||||||
|
struct node_type_float {
|
||||||
|
struct node node;
|
||||||
|
float output_val;
|
||||||
|
};
|
||||||
|
|
||||||
|
static float *node_float_eval(struct node* node, int oIndex) {
|
||||||
|
struct node_type_float *float_node = (struct node_type_float*)node;
|
||||||
|
NK_ASSERT(oIndex == 0);
|
||||||
|
return &float_node->output_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void node_float_draw(struct nk_context *ctx, struct node *node) {
|
||||||
|
struct node_type_float *float_node = (struct node_type_float*)node;
|
||||||
|
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
|
||||||
|
float_node->output_val = nk_propertyf(ctx, "#Value:", 0.0f, float_node->output_val, 1.0f, 0.01f, 0.01f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_float_create(struct node_editor *editor, struct nk_vec2 position) {
|
||||||
|
struct node_type_float *float_node = (struct node_type_float*)node_editor_add(editor, sizeof(struct node_type_float), "Float", nk_rect(position.x, position.y, 180, 75), 0, 1);
|
||||||
|
if (float_node)
|
||||||
|
{
|
||||||
|
float_node->output_val = 1.0f;
|
||||||
|
float_node->node.display_func = node_float_draw;
|
||||||
|
float_node->node.eval_func = (void*(*)(struct node*, int)) node_float_eval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
// #include "node_color.h"
|
||||||
|
|
||||||
|
struct node_type_color {
|
||||||
|
struct node node;
|
||||||
|
float input_val[4];
|
||||||
|
struct nk_colorf output_val;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nk_colorf *node_color_eval(struct node* node, int oIndex)
|
||||||
|
{
|
||||||
|
struct node_type_color *color_node = (struct node_type_color*)node;
|
||||||
|
NK_ASSERT(oIndex == 0); /* only one output connector */
|
||||||
|
|
||||||
|
return &color_node->output_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void node_color_draw(struct nk_context *ctx, struct node *node)
|
||||||
|
{
|
||||||
|
struct node_type_color *color_node = (struct node_type_color*)node;
|
||||||
|
float eval_result; /* Get the values from connected nodes into this so the inputs revert on disconnect */
|
||||||
|
const char* labels[4] = {"#R:","#G:","#B:","#A:"};
|
||||||
|
float color_val[4]; /* Because we can't just loop through the struct... */
|
||||||
|
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
|
||||||
|
nk_button_color(ctx, nk_rgba_cf(color_node->output_val));
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (color_node->node.inputs[i].is_connected) {
|
||||||
|
eval_result = *(float*)node_editor_eval_connected(node, i);
|
||||||
|
eval_result = nk_propertyf(ctx, labels[i], eval_result, eval_result, eval_result, 0.01f, 0.01f);
|
||||||
|
color_val[i] = eval_result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
color_node->input_val[i] = nk_propertyf(ctx, labels[i], 0.0f, color_node->input_val[i], 1.0f, 0.01f, 0.01f);
|
||||||
|
color_val[i] = color_node->input_val[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
color_node->output_val.r = color_val[0];
|
||||||
|
color_node->output_val.g = color_val[1];
|
||||||
|
color_node->output_val.b = color_val[2];
|
||||||
|
color_node->output_val.a = color_val[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_color_create(struct node_editor *editor, struct nk_vec2 position)
|
||||||
|
{
|
||||||
|
struct node_type_color *color_node = (struct node_type_color*)node_editor_add(editor, sizeof(struct node_type_color), "Color", nk_rect(position.x, position.y, 180, 190), 4, 1);
|
||||||
|
if (color_node)
|
||||||
|
{
|
||||||
|
const struct nk_colorf black = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
|
||||||
|
for (int i = 0; i < color_node->node.input_count; i++)
|
||||||
|
color_node->node.inputs[i].pin_type = type_float;
|
||||||
|
color_node->node.outputs[0].pin_type = type_color;
|
||||||
|
|
||||||
|
color_node->node.pin_spacing.in_padding_y += NODE_DEFAULT_ROW_HEIGHT;
|
||||||
|
|
||||||
|
color_node->input_val[0] = 0.0f;
|
||||||
|
color_node->input_val[1] = 0.0f;
|
||||||
|
color_node->input_val[2] = 0.0f;
|
||||||
|
color_node->input_val[3] = 1.0f;
|
||||||
|
|
||||||
|
color_node->output_val = black;
|
||||||
|
|
||||||
|
color_node->node.display_func = node_color_draw;
|
||||||
|
color_node->node.eval_func = (void*(*)(struct node*, int)) node_color_eval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
// #include "node_blend.h"
|
||||||
|
|
||||||
|
struct node_type_blend {
|
||||||
|
struct node node;
|
||||||
|
struct nk_colorf input_val[2];
|
||||||
|
struct nk_colorf output_val;
|
||||||
|
float blend_val;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nk_colorf *node_blend_eval(struct node *node, int oIndex) {
|
||||||
|
struct node_type_blend* blend_node = (struct node_type_blend*)node;
|
||||||
|
return &blend_node->output_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void node_blend_display(struct nk_context *ctx, struct node *node) {
|
||||||
|
struct node_type_blend *blend_node = (struct node_type_blend*)node;
|
||||||
|
const struct nk_colorf blank = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
float blend_amnt;
|
||||||
|
|
||||||
|
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
|
||||||
|
for (int i = 0; i < 2; i++){
|
||||||
|
if(node->inputs[i].is_connected) {
|
||||||
|
blend_node->input_val[i] = *(struct nk_colorf*)node_editor_eval_connected(node, i);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
blend_node->input_val[i] = blank;
|
||||||
|
}
|
||||||
|
nk_button_color(ctx, nk_rgba_cf(blend_node->input_val[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->inputs[2].is_connected) {
|
||||||
|
blend_amnt = *(float*)node_editor_eval_connected(node, 2);
|
||||||
|
blend_amnt = nk_propertyf(ctx, "#Blend", blend_amnt, blend_amnt, blend_amnt, 0.01f, 0.01f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
blend_node->blend_val = nk_propertyf(ctx, "#Blend", 0.0f, blend_node->blend_val, 1.0f, 0.01f, 0.01f);
|
||||||
|
blend_amnt = blend_node->blend_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(node->inputs[0].is_connected && node->inputs[1].is_connected) {
|
||||||
|
blend_node->output_val.r = blend_node->input_val[0].r * (1.0f-blend_amnt) + blend_node->input_val[1].r * blend_amnt;
|
||||||
|
blend_node->output_val.g = blend_node->input_val[0].g * (1.0f-blend_amnt) + blend_node->input_val[1].g * blend_amnt;
|
||||||
|
blend_node->output_val.b = blend_node->input_val[0].b * (1.0f-blend_amnt) + blend_node->input_val[1].b * blend_amnt;
|
||||||
|
blend_node->output_val.a = blend_node->input_val[0].a * (1.0f-blend_amnt) + blend_node->input_val[1].a * blend_amnt;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
blend_node->output_val = blank;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_blend_create(struct node_editor *editor, struct nk_vec2 position) {
|
||||||
|
struct node_type_blend* blend_node = (struct node_type_blend*)node_editor_add(editor, sizeof(struct node_type_blend), "Blend", nk_rect(position.x, position.y, 180, 130), 3, 1);
|
||||||
|
if (blend_node) {
|
||||||
|
const struct nk_colorf blank = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
for (int i = 0; i < (int)NK_LEN(blend_node->input_val); i++)
|
||||||
|
blend_node->node.inputs[i].pin_type = type_color;
|
||||||
|
blend_node->node.outputs[0].pin_type = type_color;
|
||||||
|
|
||||||
|
// blend_node->node.pin_spacing.in_padding_y = 42.0f;
|
||||||
|
// blend_node->node.pin_spacing.in_spacing_y = 29.0f;
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)NK_LEN(blend_node->input_val); i++)
|
||||||
|
blend_node->input_val[i] = blank;
|
||||||
|
blend_node->output_val = blank;
|
||||||
|
|
||||||
|
blend_node->blend_val = 0.5f;
|
||||||
|
|
||||||
|
blend_node->node.display_func = node_blend_display;
|
||||||
|
blend_node->node.eval_func = (void*(*)(struct node*, int)) node_blend_eval;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================= */
|
||||||
|
|
||||||
|
#define NK_RGB3(r,g,b) {r,g,b,255}
|
||||||
|
#define BG_COLOR ((struct nk_color){0,0,0,80}) // nk_rgba(0,0,0,192)
|
||||||
|
|
||||||
|
static
|
||||||
|
struct editor_node_style {
|
||||||
|
int pin_type;
|
||||||
|
const char *shape;
|
||||||
|
struct nk_color color_idle;
|
||||||
|
struct nk_color color_hover;
|
||||||
|
} styles[] = {
|
||||||
|
// order matters:
|
||||||
|
{ type_flow, "triangle_right", NK_RGB3(200,200,200), NK_RGB3(255,255,255) }, // if .num_links == 0
|
||||||
|
{ type_int, "circle", NK_RGB3(33,227,175), NK_RGB3(135,239,195) },
|
||||||
|
{ type_float, "circle", NK_RGB3(156,253,65), NK_RGB3(144,225,137) },
|
||||||
|
{ type_block, "circle", NK_RGB3(6,165,239), NK_RGB3(137,196,247) },
|
||||||
|
{ type_texture, "circle", NK_RGB3(148,0,0), NK_RGB3(183,137,137) },
|
||||||
|
{ type_image, "circle", NK_RGB3(200,130,255), NK_RGB3(220,170,255) },
|
||||||
|
{ type_color, "circle", NK_RGB3(252,200,35), NK_RGB3(255,217,140) },
|
||||||
|
};
|
||||||
|
|
||||||
|
#define COLOR_FLOW_HI styles[type_flow].color_hover
|
||||||
|
#define COLOR_FLOW_LO styles[type_flow].color_idle
|
||||||
|
|
||||||
|
#define GRID_SIZE 32.0f
|
||||||
|
#define GRID_COLOR ((struct nk_color)NK_RGB3(60,60,80))
|
||||||
|
#define GRID_THICKNESS 1.0f
|
||||||
|
|
||||||
|
#define LINK_THICKNESS 1.0f
|
||||||
|
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
|
||||||
|
vec2 a = (POINT_A); \
|
||||||
|
vec2 b = (POINT_B); \
|
||||||
|
nk_stroke_line(canvas, a.x, a.y, b.x, b.y, LINK_THICKNESS, COLOR); \
|
||||||
|
} while(0)
|
||||||
|
#undef LINK_DRAW
|
||||||
|
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
|
||||||
|
vec2 a = (POINT_A); \
|
||||||
|
vec2 b = (POINT_B); \
|
||||||
|
nk_stroke_curve(canvas, a.x, a.y, a.x+50, a.y, b.x-50, b.y, b.x, b.y, LINK_THICKNESS, COLOR); \
|
||||||
|
} while(0)
|
||||||
|
#undef LINK_DRAW
|
||||||
|
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
|
||||||
|
vec2 a = (POINT_A); \
|
||||||
|
vec2 b = (POINT_B); \
|
||||||
|
float dist2 = len2( sub2( ptr2(&b.x), ptr2(&a.x) ) ); \
|
||||||
|
vec2 mid_a = mix2( ptr2(&a.x), ptr2(&b.x), 0.25 ); mid_a.y += dist2/2; \
|
||||||
|
vec2 mid_b = mix2( ptr2(&a.x), ptr2(&b.x), 0.75 ); mid_b.y += dist2/3; \
|
||||||
|
nk_stroke_curve(canvas, a.x, a.y, mid_a.x, mid_a.y, mid_b.x, mid_b.y, b.x, b.y, LINK_THICKNESS, COLOR); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
#define PIN_RADIUS 12
|
||||||
|
#define PIN_THICKNESS 1.0f
|
||||||
|
#define PIN_DRAW(PIN_ADDR,POINT,RADIUS) do { \
|
||||||
|
circle.x = (POINT).x - (RADIUS) / 2; \
|
||||||
|
circle.y = (POINT).y - (RADIUS) / 2; \
|
||||||
|
circle.w = circle.h = (RADIUS); \
|
||||||
|
struct nk_color color = node_get_type_color((PIN_ADDR).pin_type); \
|
||||||
|
if((PIN_ADDR).is_connected) \
|
||||||
|
nk_fill_circle(canvas, circle, color); \
|
||||||
|
else \
|
||||||
|
nk_stroke_circle(canvas, circle, PIN_THICKNESS, color); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
static struct nk_color node_get_type_color(unsigned pin_type) {
|
||||||
|
for( int i = 0; i < type_total; ++i )
|
||||||
|
if( styles[i].pin_type == pin_type )
|
||||||
|
return styles[i].color_idle;
|
||||||
|
return ((struct nk_color)NK_RGB3(255,0,255));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void node_editor_push(struct node_editor *editor, struct node *node) {
|
||||||
|
if (!editor->begin) {
|
||||||
|
node->next = NULL;
|
||||||
|
node->prev = NULL;
|
||||||
|
editor->begin = node;
|
||||||
|
editor->end = node;
|
||||||
|
} else {
|
||||||
|
node->prev = editor->end;
|
||||||
|
if (editor->end)
|
||||||
|
editor->end->next = node;
|
||||||
|
node->next = NULL;
|
||||||
|
editor->end = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void node_editor_pop(struct node_editor *editor, struct node *node) {
|
||||||
|
if (node->next)
|
||||||
|
node->next->prev = node->prev;
|
||||||
|
if (node->prev)
|
||||||
|
node->prev->next = node->next;
|
||||||
|
if (editor->end == node)
|
||||||
|
editor->end = node->prev;
|
||||||
|
if (editor->begin == node)
|
||||||
|
editor->begin = node->next;
|
||||||
|
node->next = NULL;
|
||||||
|
node->prev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct node* node_editor_find_by_id(struct node_editor *editor, int ID) {
|
||||||
|
struct node *iter = editor->begin;
|
||||||
|
while (iter) {
|
||||||
|
if (iter->ID == ID)
|
||||||
|
return iter;
|
||||||
|
iter = iter->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct node_link* node_editor_find_link_by_output(struct node_editor *editor, struct node *output_node, int node_input_connector) {
|
||||||
|
for( int i = 0; i < editor->link_count; i++ ) {
|
||||||
|
if (editor->links[i].output_node == output_node &&
|
||||||
|
editor->links[i].output_pin == node_input_connector &&
|
||||||
|
editor->links[i].is_active == nk_true) {
|
||||||
|
return &editor->links[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct node_link* node_editor_find_link_by_input(struct node_editor *editor, struct node *input_node, int node_output_connector) {
|
||||||
|
for( int i = 0; i < editor->link_count; i++ ) {
|
||||||
|
if (editor->links[i].input_node == input_node &&
|
||||||
|
editor->links[i].input_pin == node_output_connector &&
|
||||||
|
editor->links[i].is_active == nk_true) {
|
||||||
|
return &editor->links[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void node_editor_delete_link(struct node_link *link) {
|
||||||
|
link->is_active = nk_false;
|
||||||
|
link->input_node->outputs[link->input_pin].is_connected = nk_false;
|
||||||
|
link->output_node->inputs[link->output_pin].is_connected = nk_false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* node_editor_add(struct node_editor *editor, size_t nodeSize, const char *name, struct nk_rect bounds, int in_count, int out_count) {
|
||||||
|
static int IDs = 0;
|
||||||
|
struct node *node = NULL;
|
||||||
|
|
||||||
|
if ((nk_size)editor->node_count < NK_LEN(editor->node_buf)) {
|
||||||
|
/* node_buf has unused pins */
|
||||||
|
node = MALLOC(nodeSize);
|
||||||
|
editor->node_buf[editor->node_count++] = node;
|
||||||
|
node->ID = IDs++;
|
||||||
|
} else {
|
||||||
|
/* check for freed up pins in node_buf */
|
||||||
|
for (int i = 0; i < editor->node_count; i++) {
|
||||||
|
if (editor->node_buf[i] == NULL) {
|
||||||
|
node = MALLOC(nodeSize);
|
||||||
|
editor->node_buf[i] = node;
|
||||||
|
node->ID = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node == NULL) {
|
||||||
|
puts("Node creation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->bounds = bounds;
|
||||||
|
|
||||||
|
node->input_count = in_count;
|
||||||
|
node->output_count = out_count;
|
||||||
|
|
||||||
|
node->inputs = MALLOC(node->input_count * sizeof(struct node_pin));
|
||||||
|
node->outputs = MALLOC(node->output_count * sizeof(struct node_pin));
|
||||||
|
|
||||||
|
for (int i = 0; i < node->input_count; i++) {
|
||||||
|
node->inputs[i].is_connected = nk_false;
|
||||||
|
node->inputs[i].pin_type = type_float; /* default pin type */
|
||||||
|
}
|
||||||
|
for (int i = 0; i < node->output_count; i++) {
|
||||||
|
node->outputs[i].is_connected = nk_false;
|
||||||
|
node->outputs[i].pin_type = type_float; /* default pin type */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* default pin spacing */
|
||||||
|
node->pin_spacing.in_padding_x = 2;
|
||||||
|
node->pin_spacing.in_padding_y = 32 + 25/2 + 6; // titlebar height + next half row + adjust
|
||||||
|
node->pin_spacing.in_spacing_y = 25; // row height+border
|
||||||
|
node->pin_spacing.out_padding_x = 3;
|
||||||
|
node->pin_spacing.out_padding_y = 32 + 25/2 + 6; // titlebar height + next half row + adjust
|
||||||
|
node->pin_spacing.out_spacing_y = 25; // row height+border
|
||||||
|
|
||||||
|
strcpy(node->name, name);
|
||||||
|
node_editor_push(editor, node);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *node_editor_eval_connected(struct node* node, int input_pin_number) {
|
||||||
|
NK_ASSERT(node->inputs[input_pin_number].is_connected);
|
||||||
|
return node->inputs[input_pin_number].connected_node->eval_func(node->inputs[input_pin_number].connected_node, node->inputs[input_pin_number].connected_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void node_editor_link(struct node_editor *editor, struct node *in_node, int in_pin, struct node *out_node, int out_pin) {
|
||||||
|
/* Confusingly, in and out nodes/pins here refer to the inputs and outputs OF THE LINK ITSELF, not the nodes */
|
||||||
|
struct node_link *link = NULL;
|
||||||
|
|
||||||
|
if ((nk_size)editor->link_count < NK_LEN(editor->links)) {
|
||||||
|
link = &editor->links[editor->link_count++];
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < (int)NK_LEN(editor->links); i++)
|
||||||
|
{
|
||||||
|
if (editor->links[i].is_active == nk_false) {
|
||||||
|
link = &editor->links[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (link) {
|
||||||
|
out_node->inputs[out_pin].is_connected = nk_true;
|
||||||
|
in_node->outputs[in_pin].is_connected = nk_true;
|
||||||
|
out_node->inputs[out_pin].connected_node = in_node;
|
||||||
|
out_node->inputs[out_pin].connected_pin = in_pin;
|
||||||
|
|
||||||
|
link->input_node = in_node;
|
||||||
|
link->input_pin = in_pin;
|
||||||
|
link->output_node = out_node;
|
||||||
|
link->output_pin = out_pin;
|
||||||
|
link->is_active = nk_true;
|
||||||
|
} else {
|
||||||
|
puts("Too many links");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void node_editor_init(struct node_editor *editor) {
|
||||||
|
if (editor->initialized) return;
|
||||||
|
|
||||||
|
struct nk_rect total_space = nk_window_get_content_region(ui_ctx);
|
||||||
|
struct nk_vec2 output_node_position = { total_space.w*2/3, total_space.h/3 };
|
||||||
|
struct nk_vec2 color_node_position = { total_space.w*1/4, total_space.h/3 };
|
||||||
|
|
||||||
|
memset(editor, 0, sizeof(*editor));
|
||||||
|
|
||||||
|
editor->output_node = node_output_create(editor, output_node_position);
|
||||||
|
node_color_create(editor, color_node_position);
|
||||||
|
editor->show_grid = nk_true;
|
||||||
|
|
||||||
|
editor->initialized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int node_editor(struct node_editor *editor) {
|
||||||
|
int n = 0;
|
||||||
|
struct nk_rect total_space;
|
||||||
|
const struct nk_input *in = &ui_ctx->input;
|
||||||
|
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
|
||||||
|
struct node *updated = 0;
|
||||||
|
|
||||||
|
node_editor_init(editor);
|
||||||
|
|
||||||
|
{
|
||||||
|
/* allocate complete window space */
|
||||||
|
total_space = nk_window_get_content_region(ui_ctx);
|
||||||
|
nk_layout_space_begin(ui_ctx, NK_STATIC, total_space.h, editor->node_count);
|
||||||
|
{
|
||||||
|
struct node *it = editor->begin;
|
||||||
|
struct nk_rect size = nk_layout_space_bounds(ui_ctx);
|
||||||
|
struct nk_panel *nodePanel = 0;
|
||||||
|
|
||||||
|
if (editor->show_grid) {
|
||||||
|
/* display grid */
|
||||||
|
for (float x = (float)fmod(size.x - editor->scrolling.x, GRID_SIZE); x < size.w; x += GRID_SIZE)
|
||||||
|
nk_stroke_line(canvas, x+size.x, size.y, x+size.x, size.y+size.h, GRID_THICKNESS, GRID_COLOR);
|
||||||
|
for (float y = (float)fmod(size.y - editor->scrolling.y, GRID_SIZE); y < size.h; y += GRID_SIZE)
|
||||||
|
nk_stroke_line(canvas, size.x, y+size.y, size.x+size.w, y+size.y, GRID_THICKNESS, GRID_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* execute each node as a movable group */
|
||||||
|
/* loop through nodes */
|
||||||
|
while (it) {
|
||||||
|
/* Output node window should not have a close button */
|
||||||
|
nk_flags nodePanel_flags = NK_WINDOW_MOVABLE|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_TITLE;
|
||||||
|
if (it != editor->output_node)
|
||||||
|
nodePanel_flags |= NK_WINDOW_CLOSABLE;
|
||||||
|
|
||||||
|
/* calculate scrolled node window position and size */
|
||||||
|
nk_layout_space_push(ui_ctx, nk_rect(it->bounds.x - editor->scrolling.x,
|
||||||
|
it->bounds.y - editor->scrolling.y, it->bounds.w, it->bounds.h));
|
||||||
|
|
||||||
|
/* execute node window */
|
||||||
|
char *name = va(" " ICON_MD_MENU " %s",it->name); //< @r-lyeh added some spacing+icon because of our UI customizations
|
||||||
|
|
||||||
|
struct nk_color bak = ui_ctx->style.window.fixed_background.data.color;
|
||||||
|
ui_ctx->style.window.fixed_background.data.color = BG_COLOR;
|
||||||
|
|
||||||
|
if (nk_group_begin(ui_ctx, name, nodePanel_flags))
|
||||||
|
{
|
||||||
|
/* always have last selected node on top */
|
||||||
|
|
||||||
|
nodePanel = nk_window_get_panel(ui_ctx);
|
||||||
|
if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nodePanel->bounds) &&
|
||||||
|
(!(it->prev && nk_input_mouse_clicked(in, NK_BUTTON_LEFT,
|
||||||
|
nk_layout_space_rect_to_screen(ui_ctx, nodePanel->bounds)))) &&
|
||||||
|
editor->end != it)
|
||||||
|
{
|
||||||
|
updated = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((nodePanel->flags & NK_WINDOW_HIDDEN)) /* Node close button has been clicked */
|
||||||
|
{
|
||||||
|
/* Delete node */
|
||||||
|
struct node_link *link_remove;
|
||||||
|
node_editor_pop(editor, it);
|
||||||
|
for (int n = 0; n < it->input_count; n++) {
|
||||||
|
if ((link_remove = node_editor_find_link_by_output(editor, it, n)))
|
||||||
|
{
|
||||||
|
node_editor_delete_link(link_remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int n = 0; n < it -> output_count; n++) {
|
||||||
|
while((link_remove = node_editor_find_link_by_input(editor, it, n)))
|
||||||
|
{
|
||||||
|
node_editor_delete_link(link_remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NK_ASSERT(editor->node_buf[it->ID] == it);
|
||||||
|
editor->node_buf[it->ID] = NULL;
|
||||||
|
FREE(it->inputs);
|
||||||
|
FREE(it->outputs);
|
||||||
|
FREE(it);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
/* ================= NODE CONTENT ===================== */
|
||||||
|
|
||||||
|
it->display_func(ui_ctx, it);
|
||||||
|
|
||||||
|
/* ==================================================== */
|
||||||
|
|
||||||
|
}
|
||||||
|
nk_group_end(ui_ctx);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_ctx->style.window.fixed_background.data.color = bak;
|
||||||
|
|
||||||
|
if (!(nodePanel->flags & NK_WINDOW_HIDDEN))
|
||||||
|
{
|
||||||
|
/* node pin and linking */
|
||||||
|
struct nk_rect bounds;
|
||||||
|
bounds = nk_layout_space_rect_to_local(ui_ctx, nodePanel->bounds);
|
||||||
|
bounds.x += editor->scrolling.x;
|
||||||
|
bounds.y += editor->scrolling.y;
|
||||||
|
it->bounds = bounds;
|
||||||
|
|
||||||
|
/* output pins */
|
||||||
|
for (int n = 0; n < it->output_count; ++n) {
|
||||||
|
struct nk_rect circle;
|
||||||
|
struct nk_vec2 pt = {nodePanel->bounds.x, nodePanel->bounds.y};
|
||||||
|
pt.x += nodePanel->bounds.w - PIN_RADIUS / 2 + it->pin_spacing.out_padding_x;
|
||||||
|
pt.y += it->pin_spacing.out_padding_y + it->pin_spacing.out_spacing_y * (n);
|
||||||
|
PIN_DRAW(it->outputs[n],pt,PIN_RADIUS);
|
||||||
|
|
||||||
|
/* start linking process */
|
||||||
|
/* set linking active */
|
||||||
|
if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true)) {
|
||||||
|
editor->linking.active = nk_true;
|
||||||
|
editor->linking.node = it;
|
||||||
|
editor->linking.input_id = it->ID;
|
||||||
|
editor->linking.input_pin = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw link being dragged (from linked pin to mouse position) */
|
||||||
|
if (editor->linking.active && editor->linking.node == it &&
|
||||||
|
editor->linking.input_pin == n) {
|
||||||
|
LINK_DRAW(vec2(circle.x+3,circle.y+3),ptr2(&in->mouse.pos.x),COLOR_FLOW_HI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* input pins */
|
||||||
|
for (int n = 0; n < it->input_count; ++n) {
|
||||||
|
struct nk_rect circle;
|
||||||
|
struct nk_vec2 pt = {nodePanel->bounds.x, nodePanel->bounds.y};
|
||||||
|
pt.x += it->pin_spacing.in_padding_x;
|
||||||
|
pt.y += it->pin_spacing.in_padding_y + it->pin_spacing.in_spacing_y * (n);
|
||||||
|
PIN_DRAW(it->inputs[n],pt,PIN_RADIUS);
|
||||||
|
|
||||||
|
/* Detach link */
|
||||||
|
if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true) &&
|
||||||
|
editor->linking.active == nk_false &&
|
||||||
|
it->inputs[n].is_connected == nk_true) {
|
||||||
|
struct node_link *node_relink = node_editor_find_link_by_output(editor, it, n);
|
||||||
|
editor->linking.active = nk_true;
|
||||||
|
editor->linking.node = node_relink->input_node;
|
||||||
|
editor->linking.input_id = node_relink->input_node->ID;
|
||||||
|
editor->linking.input_pin = node_relink->input_pin;
|
||||||
|
node_editor_delete_link(node_relink);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create link */
|
||||||
|
if (nk_input_is_mouse_released(in, NK_BUTTON_LEFT) &&
|
||||||
|
nk_input_is_mouse_hovering_rect(in, circle) &&
|
||||||
|
editor->linking.active &&
|
||||||
|
editor->linking.node != it &&
|
||||||
|
it->inputs[n].pin_type == editor->linking.node->outputs[editor->linking.input_pin].pin_type &&
|
||||||
|
it->inputs[n].is_connected != nk_true) {
|
||||||
|
editor->linking.active = nk_false;
|
||||||
|
|
||||||
|
node_editor_link(editor, editor->linking.node,
|
||||||
|
editor->linking.input_pin, it, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset (output) linking connection */
|
||||||
|
if (editor->linking.active && (!!input(KEY_LCTRL) || !!input(KEY_RCTRL) || nk_input_is_mouse_released(in, NK_BUTTON_LEFT))) {
|
||||||
|
editor->linking.active = nk_false;
|
||||||
|
editor->linking.node = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw each static link */
|
||||||
|
for (int n = 0; n < editor->link_count; ++n) {
|
||||||
|
struct node_link *link = &editor->links[n];
|
||||||
|
if (link->is_active == nk_true){
|
||||||
|
struct node *ni = link->input_node;
|
||||||
|
struct node *no = link->output_node;
|
||||||
|
struct nk_vec2 l0 = nk_layout_space_to_screen(ui_ctx, nk_vec2(ni->bounds.x + ni->bounds.w + ni->pin_spacing.out_padding_x, 3.0f + ni->bounds.y + ni->pin_spacing.out_padding_y + ni->pin_spacing.out_spacing_y * (link->input_pin)));
|
||||||
|
struct nk_vec2 l1 = nk_layout_space_to_screen(ui_ctx, nk_vec2(no->bounds.x + no->pin_spacing.in_padding_x, 3.0f + no->bounds.y + no->pin_spacing.in_padding_y + no->pin_spacing.in_spacing_y * (link->output_pin)));
|
||||||
|
|
||||||
|
l0.x -= editor->scrolling.x;
|
||||||
|
l0.y -= editor->scrolling.y;
|
||||||
|
l1.x -= editor->scrolling.x;
|
||||||
|
l1.y -= editor->scrolling.y;
|
||||||
|
|
||||||
|
struct nk_color color = node_get_type_color(no->inputs[link->output_pin].pin_type);
|
||||||
|
LINK_DRAW(ptr2(&l0.x), ptr2(&l1.x), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
/* reshuffle nodes to have least recently selected node on top */
|
||||||
|
node_editor_pop(editor, updated);
|
||||||
|
node_editor_push(editor, updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* node selection */
|
||||||
|
if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nk_layout_space_bounds(ui_ctx))) {
|
||||||
|
it = editor->begin;
|
||||||
|
editor->selected = NULL;
|
||||||
|
editor->bounds = nk_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200);
|
||||||
|
while (it) {
|
||||||
|
struct nk_rect b = nk_layout_space_rect_to_screen(ui_ctx, it->bounds);
|
||||||
|
b.x -= editor->scrolling.x;
|
||||||
|
b.y -= editor->scrolling.y;
|
||||||
|
if (nk_input_is_mouse_hovering_rect(in, b))
|
||||||
|
editor->selected = it;
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* contextual menu */
|
||||||
|
if (nk_contextual_begin(ui_ctx, 0, nk_vec2(150, 220), nk_window_get_bounds(ui_ctx))) {
|
||||||
|
struct nk_vec2 wincoords = { in->mouse.pos.x-total_space.x-50, in->mouse.pos.y-total_space.y-32 };
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static char *filter = 0;
|
||||||
|
static int do_filter = 0;
|
||||||
|
if( input_down(KEY_F) ) if( input(KEY_LCTRL) || input(KEY_RCTRL) ) do_filter ^= 1;
|
||||||
|
int choice = ui_toolbar(ICON_MD_SEARCH ";");
|
||||||
|
if( choice == 1 ) do_filter = 1;
|
||||||
|
if( do_filter ) {
|
||||||
|
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &filter);
|
||||||
|
if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon)
|
||||||
|
do_filter = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if( filter ) filter[0] = '\0';
|
||||||
|
}
|
||||||
|
char *filter_mask = filter && filter[0] ? va("*%s*", filter) : "*";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ui_label_filtered(lbl) (strmatchi(lbl,filter_mask) && ui_label(lbl))
|
||||||
|
|
||||||
|
int close = 0;
|
||||||
|
if (ui_label_filtered("=Add Color node")) close=1,node_color_create(editor, wincoords);
|
||||||
|
if (ui_label_filtered("=Add Float node")) close=1,node_float_create(editor, wincoords);
|
||||||
|
if (ui_label_filtered("=Add Blend Node")) close=1,node_blend_create(editor, wincoords);
|
||||||
|
if (ui_label_filtered(editor->show_grid ? "=Hide Grid" : "=Show Grid"))
|
||||||
|
close=1,editor->show_grid = !editor->show_grid;
|
||||||
|
if(close) do_filter = 0, (filter ? filter[0] = '\0' : '\0'), nk_contextual_close(ui_ctx);
|
||||||
|
nk_contextual_end(ui_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nk_layout_space_end(ui_ctx);
|
||||||
|
|
||||||
|
/* window content scrolling */
|
||||||
|
if (nk_input_is_mouse_hovering_rect(in, nk_window_get_bounds(ui_ctx)) &&
|
||||||
|
nk_input_is_mouse_down(in, NK_BUTTON_MIDDLE)) {
|
||||||
|
editor->scrolling.x += in->mouse.delta.x;
|
||||||
|
editor->scrolling.y += in->mouse.delta.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !nk_window_is_closed(ui_ctx, "NodeEdit");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
static int open = 1;
|
||||||
|
window_create(0.75, 0);
|
||||||
|
while(window_swap() && open) {
|
||||||
|
if( ui_window("Node editor",&open) ) {
|
||||||
|
static struct node_editor nodeEditor = {0};
|
||||||
|
node_editor(&nodeEditor);
|
||||||
|
ui_window_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
// fitting polynomials to matplotlib colormaps by mattz
|
||||||
|
//
|
||||||
|
// License CC0 (public domain)
|
||||||
|
// https://creativecommons.org/share-your-work/public-domain/cc0/
|
||||||
|
//
|
||||||
|
// feel free to use these in your own work!
|
||||||
|
//
|
||||||
|
// similar to https://www.shadertoy.com/view/XtGGzG but with a couple small differences:
|
||||||
|
//
|
||||||
|
// - use degree 6 instead of degree 5 polynomials
|
||||||
|
// - use nested horner representation for polynomials
|
||||||
|
// - polynomials were fitted to minimize maximum error (as opposed to least squares)
|
||||||
|
//
|
||||||
|
// data fitted from https://github.com/BIDS/colormap/blob/master/colormaps.py
|
||||||
|
// (which is licensed CC0)
|
||||||
|
|
||||||
|
uniform int use_colormap; /// set:2 min:0 max:4 tip:"0:off,1:inferno,2:viridis,3:plasma,4:magma"
|
||||||
|
|
||||||
|
vec3 inferno_color_map(float t) {
|
||||||
|
const vec3 c0 = vec3(0.0002189403691192265, 0.001651004631001012, -0.01948089843709184);
|
||||||
|
const vec3 c1 = vec3(0.1065134194856116, 0.5639564367884091, 3.932712388889277);
|
||||||
|
const vec3 c2 = vec3(11.60249308247187, -3.972853965665698, -15.9423941062914);
|
||||||
|
const vec3 c3 = vec3(-41.70399613139459, 17.43639888205313, 44.35414519872813);
|
||||||
|
const vec3 c4 = vec3(77.162935699427, -33.40235894210092, -81.80730925738993);
|
||||||
|
const vec3 c5 = vec3(-71.31942824499214, 32.62606426397723, 73.20951985803202);
|
||||||
|
const vec3 c6 = vec3(25.13112622477341, -12.24266895238567, -23.07032500287172);
|
||||||
|
|
||||||
|
return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 viridis_color_map(float t) {
|
||||||
|
const vec3 c0 = vec3(0.2777273272234177, 0.005407344544966578, 0.3340998053353061);
|
||||||
|
const vec3 c1 = vec3(0.1050930431085774, 1.404613529898575, 1.384590162594685);
|
||||||
|
const vec3 c2 = vec3(-0.3308618287255563, 0.214847559468213, 0.09509516302823659);
|
||||||
|
const vec3 c3 = vec3(-4.634230498983486, -5.799100973351585, -19.33244095627987);
|
||||||
|
const vec3 c4 = vec3(6.228269936347081, 14.17993336680509, 56.69055260068105);
|
||||||
|
const vec3 c5 = vec3(4.776384997670288, -13.74514537774601, -65.35303263337234);
|
||||||
|
const vec3 c6 = vec3(-5.435455855934631, 4.645852612178535, 26.3124352495832);
|
||||||
|
|
||||||
|
return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 plasma_color_map(float t) {
|
||||||
|
const vec3 c0 = vec3(0.05873234392399702, 0.02333670892565664, 0.5433401826748754);
|
||||||
|
const vec3 c1 = vec3(2.176514634195958, 0.2383834171260182, 0.7539604599784036);
|
||||||
|
const vec3 c2 = vec3(-2.689460476458034, -7.455851135738909, 3.110799939717086);
|
||||||
|
const vec3 c3 = vec3(6.130348345893603, 42.3461881477227, -28.51885465332158);
|
||||||
|
const vec3 c4 = vec3(-11.10743619062271, -82.66631109428045, 60.13984767418263);
|
||||||
|
const vec3 c5 = vec3(10.02306557647065, 71.41361770095349, -54.07218655560067);
|
||||||
|
const vec3 c6 = vec3(-3.658713842777788, -22.93153465461149, 18.19190778539828);
|
||||||
|
|
||||||
|
return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 magma_color_map(float t) {
|
||||||
|
const vec3 c0 = vec3(-0.002136485053939582, -0.000749655052795221, -0.005386127855323933);
|
||||||
|
const vec3 c1 = vec3(0.2516605407371642, 0.6775232436837668, 2.494026599312351);
|
||||||
|
const vec3 c2 = vec3(8.353717279216625, -3.577719514958484, 0.3144679030132573);
|
||||||
|
const vec3 c3 = vec3(-27.66873308576866, 14.26473078096533, -13.64921318813922);
|
||||||
|
const vec3 c4 = vec3(52.17613981234068, -27.94360607168351, 12.94416944238394);
|
||||||
|
const vec3 c5 = vec3(-50.76852536473588, 29.04658282127291, 4.23415299384598);
|
||||||
|
const vec3 c6 = vec3(18.65570506591883, -11.48977351997711, -5.601961508734096);
|
||||||
|
|
||||||
|
return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
|
||||||
|
vec2 uv = fragCoord.xy / iResolution.xy;
|
||||||
|
vec4 src = texture(iChannel0, uv);
|
||||||
|
|
||||||
|
if(use_colormap == 0) { fragColor = src; return; }
|
||||||
|
|
||||||
|
float luma = dot(src.rgb, vec3(0.299, 0.587, 0.114));
|
||||||
|
if(use_colormap == 1) { fragColor = vec4(inferno_color_map(luma), src.a); return; }
|
||||||
|
if(use_colormap == 2) { fragColor = vec4(viridis_color_map(luma), src.a); return; }
|
||||||
|
if(use_colormap == 3) { fragColor = vec4(plasma_color_map(luma), src.a); return; }
|
||||||
|
if(use_colormap == 4) { fragColor = vec4(magma_color_map(luma), src.a); return; }
|
||||||
|
}
|
BIN
engine/bind/lua
BIN
engine/bind/lua
Binary file not shown.
|
@ -939,6 +939,12 @@ ffi.cdef([[
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
||||||
//lcpp INF [0000] vec4: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
//lcpp INF [0000] vec4: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
||||||
|
//lcpp INF [0000] vec2i: macro name but used as C declaration in:API void print2i( vec2i v );
|
||||||
|
//lcpp INF [0000] vec2i: macro name but used as C declaration in:STATIC void print2i( vec2i v );
|
||||||
|
//lcpp INF [0000] vec2i: macro name but used as C declaration in: void print2i( vec2i v );
|
||||||
|
//lcpp INF [0000] vec3i: macro name but used as C declaration in:API void print3i( vec3i v );
|
||||||
|
//lcpp INF [0000] vec3i: macro name but used as C declaration in:STATIC void print3i( vec3i v );
|
||||||
|
//lcpp INF [0000] vec3i: macro name but used as C declaration in: void print3i( vec3i v );
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API void print2( vec2 v );
|
//lcpp INF [0000] vec2: macro name but used as C declaration in:API void print2( vec2 v );
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC void print2( vec2 v );
|
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC void print2( vec2 v );
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in: void print2( vec2 v );
|
//lcpp INF [0000] vec2: macro name but used as C declaration in: void print2( vec2 v );
|
||||||
|
@ -1215,12 +1221,6 @@ ffi.cdef([[
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
|
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
|
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
|
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
|
||||||
//lcpp INF [0000] ctor: macro name but used as C declaration in:API extern void (*ctor[256])();
|
|
||||||
//lcpp INF [0000] ctor: macro name but used as C declaration in:STATIC extern void (*ctor[256])();
|
|
||||||
//lcpp INF [0000] ctor: macro name but used as C declaration in: extern void (*ctor[256])();
|
|
||||||
//lcpp INF [0000] dtor: macro name but used as C declaration in:API extern void (*dtor[256])();
|
|
||||||
//lcpp INF [0000] dtor: macro name but used as C declaration in:STATIC extern void (*dtor[256])();
|
|
||||||
//lcpp INF [0000] dtor: macro name but used as C declaration in: extern void (*dtor[256])();
|
|
||||||
//lcpp INF [0000] map: macro name but used as C declaration in:typedef struct { map base; struct { pair p; char * key; struct profile_t val; } tmp, *ptr; struct profile_t* tmpval; int (*typed_cmp)(char *, char *); uint64_t (*typed_hash)(char *); } * profiler_t;
|
//lcpp INF [0000] map: macro name but used as C declaration in:typedef struct { map base; struct { pair p; char * key; struct profile_t val; } tmp, *ptr; struct profile_t* tmpval; int (*typed_cmp)(char *, char *); uint64_t (*typed_hash)(char *); } * profiler_t;
|
||||||
//lcpp INF [0000] vec4: macro name but used as C declaration in:vec4 color;
|
//lcpp INF [0000] vec4: macro name but used as C declaration in:vec4 color;
|
||||||
//lcpp INF [0000] vec4: macro name but used as C declaration in:API void sprite_rect( texture_t t, vec4 rect, float zindex, vec3 pos, float tilt_deg, unsigned tint_rgba);
|
//lcpp INF [0000] vec4: macro name but used as C declaration in:API void sprite_rect( texture_t t, vec4 rect, float zindex, vec3 pos, float tilt_deg, unsigned tint_rgba);
|
||||||
|
@ -1494,6 +1494,10 @@ ffi.cdef([[
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void light_dir(light_t* l, vec3 dir);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void light_dir(light_t* l, vec3 dir);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void light_dir(light_t* l, vec3 dir);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void light_dir(light_t* l, vec3 dir);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: void light_dir(light_t* l, vec3 dir);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: void light_dir(light_t* l, vec3 dir);
|
||||||
|
//lcpp INF [0000] vec3i: macro name but used as C declaration in:typedef vec3i guid;
|
||||||
|
//lcpp INF [0000] test: macro name but used as C declaration in:API int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
|
//lcpp INF [0000] test: macro name but used as C declaration in:STATIC int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
|
//lcpp INF [0000] test: macro name but used as C declaration in: int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 ui_get_dims();
|
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 ui_get_dims();
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 ui_get_dims();
|
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 ui_get_dims();
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 ui_get_dims();
|
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 ui_get_dims();
|
||||||
|
@ -1567,10 +1571,13 @@ pair** sorted;
|
||||||
void (map_gc)(map *m);
|
void (map_gc)(map *m);
|
||||||
bool (map_sort)(map* m);
|
bool (map_sort)(map* m);
|
||||||
void (map_clear)(map* m);
|
void (map_clear)(map* m);
|
||||||
unsigned cc4(const char *id);
|
|
||||||
uint64_t cc8(const char *id);
|
|
||||||
char *cc4str(unsigned cc);
|
char *cc4str(unsigned cc);
|
||||||
char *cc8str(uint64_t cc);
|
char *cc8str(uint64_t cc);
|
||||||
|
enum {
|
||||||
|
cc_1 = '1', cc_2, cc_3, cc_4, cc_5, cc_6,cc_7, cc_8, cc_9, cc_0, cc__, cc_ = ' ',
|
||||||
|
cc_A = 'A', cc_B, cc_C, cc_D, cc_E, cc_F,cc_G, cc_H, cc_I, cc_J, cc_K,cc_L, cc_M, cc_N, cc_O, cc_P,cc_Q, cc_R, cc_S, cc_T, cc_U,cc_V, cc_W, cc_X, cc_Y, cc_Z,
|
||||||
|
cc_a = 'a', cc_b, cc_c, cc_d, cc_e, cc_f,cc_g, cc_h, cc_i, cc_j, cc_k,cc_l, cc_m, cc_n, cc_o, cc_p,cc_q, cc_r, cc_s, cc_t, cc_u,cc_v, cc_w, cc_x, cc_y, cc_z,
|
||||||
|
};
|
||||||
typedef union vec2i{ struct { int X,Y; }; struct { int x,y; }; struct { int r,g; }; struct { int w,h; }; struct { int min,max; }; struct { int from,to; }; struct { int src,dst; }; int v2[2]; int array[1]; } vec2i;
|
typedef union vec2i{ struct { int X,Y; }; struct { int x,y; }; struct { int r,g; }; struct { int w,h; }; struct { int min,max; }; struct { int from,to; }; struct { int src,dst; }; int v2[2]; int array[1]; } vec2i;
|
||||||
typedef union vec3i{ struct { int X,Y,Z; }; struct { int x,y,z; }; struct { int r,g,b; }; struct { int w,h,d; }; struct { int min,max; }; struct { int from,to,step; }; struct { int src,dst; }; int v3[3]; int array[1]; } vec3i;
|
typedef union vec3i{ struct { int X,Y,Z; }; struct { int x,y,z; }; struct { int r,g,b; }; struct { int w,h,d; }; struct { int min,max; }; struct { int from,to,step; }; struct { int src,dst; }; int v3[3]; int array[1]; } vec3i;
|
||||||
typedef union vec2 { struct { float X,Y; }; struct { float x,y; }; struct { float r,g; }; struct { float w,h; }; struct { float min,max; }; struct { float from,to; }; struct { float src,dst; }; float v2[2]; float array[1]; } vec2;
|
typedef union vec2 { struct { float X,Y; }; struct { float x,y; }; struct { float r,g; }; struct { float w,h; }; struct { float min,max; }; struct { float from,to; }; struct { float src,dst; }; float v2[2]; float array[1]; } vec2;
|
||||||
|
@ -1809,6 +1816,8 @@ EASE_OUT = 0,
|
||||||
vec3 transform344(const mat44 m, const vec3 p);
|
vec3 transform344(const mat44 m, const vec3 p);
|
||||||
vec4 transform444(const mat44 m, const vec4 p);
|
vec4 transform444(const mat44 m, const vec4 p);
|
||||||
bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
||||||
|
void print2i( vec2i v );
|
||||||
|
void print3i( vec3i v );
|
||||||
void print2( vec2 v );
|
void print2( vec2 v );
|
||||||
void print3( vec3 v );
|
void print3( vec3 v );
|
||||||
void print4( vec4 v );
|
void print4( vec4 v );
|
||||||
|
@ -2289,39 +2298,23 @@ enum { NETWORK_USERID = 7, NETWORK_COUNT , NETWORK_CAPACITY };
|
||||||
void server_send_bin(int64_t handle, const void *ptr, int len);
|
void server_send_bin(int64_t handle, const void *ptr, int len);
|
||||||
void server_drop(int64_t handle);
|
void server_drop(int64_t handle);
|
||||||
int64_t client_join(const char *ip, int port);
|
int64_t client_join(const char *ip, int port);
|
||||||
void* obj_malloc( int sz, ... );
|
int semver( int major, int minor, int patch );
|
||||||
void* obj_calloc( int sz, ... );
|
int semvercmp( int v1, int v2 );
|
||||||
void obj_free( void *obj );
|
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||||
bool obj_typeeq( const void *obj1, const void *obj2 );
|
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||||
const char* obj_typeof( const void *obj );
|
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||||
unsigned obj_typeid( const void *obj );
|
typedef struct int2 { int x,y; } int2;
|
||||||
unsigned obj_typeid_from_name( const char *name );
|
typedef struct int3 { int x,y,z; } int3;
|
||||||
void obj_new( const char *type, ... );
|
typedef struct int4 { int x,y,z,w; } int4;
|
||||||
void obj_del( void *obj );
|
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||||
void* obj_ref( void *obj );
|
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||||
void* obj_unref( void *obj );
|
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||||
void obj_extend( const char *dstclass, const char *srcclass );
|
typedef struct float2 { float x,y; } float2;
|
||||||
void obj_override( const char *objclass, void (**vtable)(), void(*fn)() );
|
typedef struct float3 { float x,y,z; } float3;
|
||||||
unsigned obj_load(void *obj, const char* buffer);
|
typedef struct float4 { float x,y,z,w; } float4;
|
||||||
unsigned obj_load_file(void *obj, FILE *fp);
|
typedef struct double2 { double x,y; } double2;
|
||||||
unsigned obj_load_inplace(void *obj, const void *src, unsigned srclen);
|
typedef struct double3 { double x,y,z; } double3;
|
||||||
char* obj_save(const void *obj);
|
typedef struct double4 { double x,y,z,w; } double4;
|
||||||
unsigned obj_save_file(FILE *fp, const void *obj);
|
|
||||||
unsigned obj_save_inplace(void *dst, unsigned cap, const void *obj);
|
|
||||||
unsigned obj_instances( const void *obj );
|
|
||||||
void obj_zero( void *obj );
|
|
||||||
unsigned obj_sizeof( const void *obj );
|
|
||||||
void obj_hexdump( const void *obj );
|
|
||||||
void obj_hexdumpf( FILE *out, const void *obj );
|
|
||||||
void obj_printf( void *obj, const char *text );
|
|
||||||
const char* obj_output( const void *obj );
|
|
||||||
void * obj_clone(const void *obj);
|
|
||||||
void * obj_copy(void **dst, const void *src);
|
|
||||||
void * obj_mutate(void **dst, const void *src);
|
|
||||||
extern void (*ctor[256])();
|
|
||||||
extern void (*dtor[256])();
|
|
||||||
void* obj_initialize( void **ptr, char *type_and_info );
|
|
||||||
void *obj_tmpalloc;
|
|
||||||
int profiler_enable(bool on);
|
int profiler_enable(bool on);
|
||||||
struct profile_t { double stat; int32_t cost, avg; };
|
struct profile_t { double stat; int32_t cost, avg; };
|
||||||
typedef struct { map base; struct { pair p; char * key; struct profile_t val; } tmp, *ptr; struct profile_t* tmpval; int (*typed_cmp)(char *, char *); uint64_t (*typed_hash)(char *); } * profiler_t;
|
typedef struct { map base; struct { pair p; char * key; struct profile_t val; } tmp, *ptr; struct profile_t* tmpval; int (*typed_cmp)(char *, char *); uint64_t (*typed_hash)(char *); } * profiler_t;
|
||||||
|
@ -2893,27 +2886,11 @@ int u_coefficients_sh;
|
||||||
char* strjoin(char** list, const char *separator);
|
char* strjoin(char** list, const char *separator);
|
||||||
char * string8(const wchar_t *str);
|
char * string8(const wchar_t *str);
|
||||||
uint32_t* string32( const char *utf8 );
|
uint32_t* string32( const char *utf8 );
|
||||||
void* thread( int (*thread_func)(void* user_data), void* user_data );
|
unsigned intern( const char *string );
|
||||||
void thread_destroy( void *thd );
|
const char *quark( unsigned key );
|
||||||
int argc();
|
typedef char* quarks_db;
|
||||||
char* argv(int);
|
unsigned quark_intern( quarks_db*, const char *string );
|
||||||
int flag(const char *commalist);
|
const char *quark_string( quarks_db*, unsigned key );
|
||||||
const char* option(const char *commalist, const char *defaults);
|
|
||||||
int optioni(const char *commalist, int defaults);
|
|
||||||
float optionf(const char *commalist, float defaults);
|
|
||||||
void tty_color(unsigned color);
|
|
||||||
void tty_reset();
|
|
||||||
void tty_attach();
|
|
||||||
void tty_detach();
|
|
||||||
const char* app_exec(const char *command);
|
|
||||||
int app_spawn(const char *command);
|
|
||||||
int app_cores();
|
|
||||||
int app_battery();
|
|
||||||
const char* app_name();
|
|
||||||
const char* app_path();
|
|
||||||
const char* app_cache();
|
|
||||||
const char* app_temp();
|
|
||||||
const char* app_cmdline();
|
|
||||||
uint64_t date();
|
uint64_t date();
|
||||||
uint64_t date_epoch();
|
uint64_t date_epoch();
|
||||||
char* date_string();
|
char* date_string();
|
||||||
|
@ -2929,6 +2906,34 @@ int u_coefficients_sh;
|
||||||
void sleep_ns(double us);
|
void sleep_ns(double us);
|
||||||
unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
|
unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
|
||||||
void timer_destroy(unsigned timer_handle);
|
void timer_destroy(unsigned timer_handle);
|
||||||
|
typedef vec3i guid;
|
||||||
|
guid guid_create();
|
||||||
|
void* thread( int (*thread_func)(void* user_data), void* user_data );
|
||||||
|
void thread_destroy( void *thd );
|
||||||
|
int argc();
|
||||||
|
char* argv(int);
|
||||||
|
int flag(const char *commalist);
|
||||||
|
const char* option(const char *commalist, const char *defaults);
|
||||||
|
int optioni(const char *commalist, int defaults);
|
||||||
|
float optionf(const char *commalist, float defaults);
|
||||||
|
void tty_attach();
|
||||||
|
void tty_detach();
|
||||||
|
void tty_color(unsigned color);
|
||||||
|
void tty_reset();
|
||||||
|
const char* app_exec(const char *command);
|
||||||
|
int app_spawn(const char *command);
|
||||||
|
int app_cores();
|
||||||
|
int app_battery();
|
||||||
|
const char* app_name();
|
||||||
|
const char* app_path();
|
||||||
|
const char* app_cache();
|
||||||
|
const char* app_temp();
|
||||||
|
const char* app_cmdline();
|
||||||
|
void app_beep();
|
||||||
|
void app_hang();
|
||||||
|
void app_crash();
|
||||||
|
void app_singleton(const char *guid);
|
||||||
|
bool app_open(const char *folder_file_or_url);
|
||||||
char* callstack( int traces );
|
char* callstack( int traces );
|
||||||
int callstackf( FILE *fp, int traces );
|
int callstackf( FILE *fp, int traces );
|
||||||
void die(const char *message);
|
void die(const char *message);
|
||||||
|
@ -2937,12 +2942,12 @@ int u_coefficients_sh;
|
||||||
void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
|
void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
|
||||||
void breakpoint(const char *optional_reason);
|
void breakpoint(const char *optional_reason);
|
||||||
bool has_debugger();
|
bool has_debugger();
|
||||||
void signal_hooks(void);
|
void trap_install(void);
|
||||||
void signal_handler_ignore(int signal);
|
const char *trap_name(int signal);
|
||||||
void signal_handler_quit(int signal);
|
void trap_on_ignore(int signal);
|
||||||
void signal_handler_abort(int signal);
|
void trap_on_quit(int signal);
|
||||||
void signal_handler_debug(int signal);
|
void trap_on_abort(int signal);
|
||||||
const char *signal_name(int signal);
|
void trap_on_debug(int signal);
|
||||||
uint16_t lil16(uint16_t n);
|
uint16_t lil16(uint16_t n);
|
||||||
uint32_t lil32(uint32_t n);
|
uint32_t lil32(uint32_t n);
|
||||||
float lil32f(float n);
|
float lil32f(float n);
|
||||||
|
@ -2965,6 +2970,7 @@ int u_coefficients_sh;
|
||||||
double* big64pf(void *n, int sz);
|
double* big64pf(void *n, int sz);
|
||||||
int (PANIC)(const char *error, const char *file, int line);
|
int (PANIC)(const char *error, const char *file, int line);
|
||||||
int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
|
int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
|
||||||
|
int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
enum PANEL_FLAGS {
|
enum PANEL_FLAGS {
|
||||||
PANEL_OPEN = 1,
|
PANEL_OPEN = 1,
|
||||||
};
|
};
|
||||||
|
|
1667
engine/joint/v4k.h
1667
engine/joint/v4k.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -29136,7 +29136,7 @@ nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len,
|
||||||
else
|
else
|
||||||
label.w = header.w - 2 * style->combo.content_padding.x;
|
label.w = header.w - 2 * style->combo.content_padding.x;
|
||||||
nk_widget_text(&win->buffer, label, selected, len, &text,
|
nk_widget_text(&win->buffer, label, selected, len, &text,
|
||||||
NK_TEXT_LEFT, ctx->style.font);
|
NK_TEXT_CENTERED, ctx->style.font); //< @r-lyeh NK_TEXT_LEFT>CENTERED
|
||||||
|
|
||||||
/* draw open/close button */
|
/* draw open/close button */
|
||||||
if (draw_button_symbol)
|
if (draw_button_symbol)
|
||||||
|
|
|
@ -139,6 +139,8 @@
|
||||||
|
|
||||||
{{FILE:v4k_script.c}}
|
{{FILE:v4k_script.c}}
|
||||||
|
|
||||||
|
{{FILE:v4k_time.c}}
|
||||||
|
|
||||||
{{FILE:v4k_system.c}}
|
{{FILE:v4k_system.c}}
|
||||||
|
|
||||||
{{FILE:v4k_ui.c}}
|
{{FILE:v4k_ui.c}}
|
||||||
|
|
|
@ -144,6 +144,8 @@ extern "C" {
|
||||||
|
|
||||||
{{FILE:v4k_string.h}}
|
{{FILE:v4k_string.h}}
|
||||||
|
|
||||||
|
{{FILE:v4k_time.h}}
|
||||||
|
|
||||||
{{FILE:v4k_system.h}}
|
{{FILE:v4k_system.h}}
|
||||||
|
|
||||||
{{FILE:v4k_ui.h}}
|
{{FILE:v4k_ui.h}}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{{FILE:3rd_glad.h}}
|
{{FILE:3rd_glad.h}}
|
||||||
{{FILE:3rd_font_md.h}}
|
{{FILE:3rd_icon_md.h}}
|
||||||
|
|
||||||
#ifdef V4K_3RD
|
#ifdef V4K_3RD
|
||||||
|
|
||||||
|
@ -175,4 +175,5 @@ errno_t fopen_s(
|
||||||
#undef Token
|
#undef Token
|
||||||
#undef Table
|
#undef Table
|
||||||
#undef rehash
|
#undef rehash
|
||||||
|
#undef NB
|
||||||
#endif // V4K_3RD
|
#endif // V4K_3RD
|
||||||
|
|
|
@ -20,7 +20,7 @@ bt_t bt(const char *ini_file, unsigned flags) {
|
||||||
array(char*) m = strsplit(vfs_read(ini_file), "\r\n");
|
array(char*) m = strsplit(vfs_read(ini_file), "\r\n");
|
||||||
|
|
||||||
bt_t *self = &root;
|
bt_t *self = &root;
|
||||||
self->type = cc8(root);
|
self->type = cc4(r,o,o,t);
|
||||||
//self->parent = self;
|
//self->parent = self;
|
||||||
|
|
||||||
for( int i = 0; i < array_count(m); ++i ) {
|
for( int i = 0; i < array_count(m); ++i ) {
|
||||||
|
@ -56,20 +56,20 @@ bt_t bt(const char *ini_file, unsigned flags) {
|
||||||
int bt_run(bt_t *b) {
|
int bt_run(bt_t *b) {
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
/**/ if( b->type == cc8( run) ) { return b->action ? b->action() : 0; }
|
/**/ if( b->type == cc3( r,u,n) ) { return b->action ? b->action() : 0; }
|
||||||
else if( b->type == cc8( not) ) { return !bt_run(b->children + 0); }
|
else if( b->type == cc3( n,o,t) ) { return !bt_run(b->children + 0); }
|
||||||
else if( b->type == cc8( sleep) ) { return sleep_ss(b->argf), bt_run(b->children + 0); }
|
else if( b->type == cc5( s,l,e,e,p) ) { return sleep_ss(b->argf), bt_run(b->children + 0); }
|
||||||
else if( b->type == cc8( defer) ) { rc = bt_run(b->children + 0); return sleep_ss(b->argf), rc; }
|
else if( b->type == cc5( d,e,f,e,r) ) { rc = bt_run(b->children + 0); return sleep_ss(b->argf), rc; }
|
||||||
else if( b->type == cc8( loop) ) { int rc; for(int i = 0; i < b->argf; ++i) rc = bt_run(b->children + 0); return rc; }
|
else if( b->type == cc4( l,o,o,p) ) { int rc; for(int i = 0; i < b->argf; ++i) rc = bt_run(b->children + 0); return rc; }
|
||||||
else if( b->type == cc8( once) ) { return b->argf ? 0 : (b->argf = 1), bt_run(b->children + 0); }
|
else if( b->type == cc4( o,n,c,e) ) { return b->argf ? 0 : (b->argf = 1), bt_run(b->children + 0); }
|
||||||
else if( b->type == cc8( count) ) { return b->argf <= 0 ? 0 : --b->argf, bt_run(b->children + 0); }
|
else if( b->type == cc5( c,o,u,n,t) ) { return b->argf <= 0 ? 0 : --b->argf, bt_run(b->children + 0); }
|
||||||
else if( b->type == cc8( pass) ) { return bt_run(b->children + 0), 1; }
|
else if( b->type == cc4( p,a,s,s) ) { return bt_run(b->children + 0), 1; }
|
||||||
else if( b->type == cc8( fail) ) { return bt_run(b->children + 0), 0; }
|
else if( b->type == cc4( f,a,i,l) ) { return bt_run(b->children + 0), 0; }
|
||||||
else if( b->type == cc8( result) ) { return bt_run(b->children + 0), !!b->argf; }
|
else if( b->type == cc6( r,e,s,u,l,t) ) { return bt_run(b->children + 0), !!b->argf; }
|
||||||
else if( b->type == cc8( all) ) { for( int i = 0; i < array_count(b->children); ++i ) if(!bt_run(b->children+i)) return 0; return 1; }
|
else if( b->type == cc3( a,l,l) ) { for( int i = 0; i < array_count(b->children); ++i ) if(!bt_run(b->children+i)) return 0; return 1; }
|
||||||
else if( b->type == cc8( any) ) { for( int i = 0; i < array_count(b->children); ++i ) if( bt_run(b->children+i)) return 1; return 0; }
|
else if( b->type == cc3( a,n,y) ) { for( int i = 0; i < array_count(b->children); ++i ) if( bt_run(b->children+i)) return 1; return 0; }
|
||||||
else if( b->type == cc8( root) ) { for( int i = 0; i < array_count(b->children); ++i ) rc|=bt_run(b->children+i); return rc; }
|
else if( b->type == cc4( r,o,o,t) ) { for( int i = 0; i < array_count(b->children); ++i ) rc|=bt_run(b->children+i); return rc; }
|
||||||
else if( b->type == cc8(rootfail) ) { rc = 1; for( int i = 0; i < array_count(b->children); ++i ) rc&=~bt_run(b->children+i); return rc; }
|
else if( b->type == cc8(r,o,o,t,f,a,i,l) ) { rc = 1; for( int i = 0; i < array_count(b->children); ++i ) rc&=~bt_run(b->children+i); return rc; }
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@
|
||||||
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
|
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
|
||||||
|
|
||||||
#define concat(a,b) conc4t(a,b)
|
#define concat(a,b) conc4t(a,b)
|
||||||
#define conc4t(a,b) a##b
|
#define conc4t(a,b) a##b ///-
|
||||||
|
|
||||||
#define macro(name) concat(name, __LINE__)
|
#define macro(name) concat(name, __LINE__)
|
||||||
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
|
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
|
||||||
|
@ -149,16 +149,16 @@
|
||||||
|
|
||||||
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
|
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
|
||||||
#define STRINGIZE(x) STRINGIZ3(x)
|
#define STRINGIZE(x) STRINGIZ3(x)
|
||||||
#define STRINGIZ3(x) #x
|
#define STRINGIZ3(x) #x ///-
|
||||||
|
|
||||||
#define EXPAND(name, ...) EXPAND_QUOTE(EXPAND_JOIN(name, EXPAND_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
|
#define EXPAND(name, ...) EXPAND_QUOTE(EXPAND_JOIN(name, EXPAND_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
|
||||||
#define EXPAND_QUOTE(x, y) x y
|
#define EXPAND_QUOTE(x, y) x y ///-
|
||||||
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count)
|
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count) ///-
|
||||||
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count)
|
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count) ///-
|
||||||
#define EXPAND_J01N(name, count) name##count
|
#define EXPAND_J01N(name, count) name##count ///-
|
||||||
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
|
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) ///-
|
||||||
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args
|
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args ///-
|
||||||
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count
|
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count ///-
|
||||||
|
|
||||||
#if is(cl) && !is(cpp)
|
#if is(cl) && !is(cpp)
|
||||||
#define INLINE __inline
|
#define INLINE __inline
|
||||||
|
@ -182,10 +182,13 @@
|
||||||
|
|
||||||
// usage: #define vec2(...) C_CAST(vec2, __VA_ARGS__)
|
// usage: #define vec2(...) C_CAST(vec2, __VA_ARGS__)
|
||||||
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
|
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
|
||||||
#if is(cpp)
|
#define C_CAST(type, ...) ( ifdef(c,(type),type) { __VA_ARGS__ } )
|
||||||
#define C_CAST(type, ...) ( type { __VA_ARGS__ } )
|
|
||||||
#else
|
// -----------------------------------------------------------------------------
|
||||||
#define C_CAST(type, ...) ((type){ __VA_ARGS__ } )
|
// build info
|
||||||
|
|
||||||
|
#ifndef BUILD_VERSION
|
||||||
|
#define BUILD_VERSION ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -193,15 +196,9 @@
|
||||||
|
|
||||||
// win32 users would need to -DAPI=IMPORT/EXPORT as needed when using/building V4K as DLL.
|
// win32 users would need to -DAPI=IMPORT/EXPORT as needed when using/building V4K as DLL.
|
||||||
|
|
||||||
#if is(win32)
|
#define IMPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport)))
|
||||||
#define IMPORT ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport))
|
#define EXPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport)))
|
||||||
#define EXPORT ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport))
|
|
||||||
#define STATIC
|
#define STATIC
|
||||||
#else
|
|
||||||
#define IMPORT
|
|
||||||
#define EXPORT
|
|
||||||
#define STATIC
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef API
|
#ifndef API
|
||||||
#define API STATIC
|
#define API STATIC
|
||||||
|
|
|
@ -428,13 +428,29 @@ API bool (map_sort)(map* m);
|
||||||
API void (map_clear)(map* m);
|
API void (map_clear)(map* m);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// four-cc, eight-cc
|
// compile-time fourcc, eightcc
|
||||||
|
|
||||||
API unsigned cc4(const char *id);
|
|
||||||
API uint64_t cc8(const char *id);
|
|
||||||
API char *cc4str(unsigned cc);
|
API char *cc4str(unsigned cc);
|
||||||
API char *cc8str(uint64_t cc);
|
API char *cc8str(uint64_t cc);
|
||||||
|
|
||||||
// fast path
|
enum {
|
||||||
#define cc4(abcd) ( *(unsigned*) #abcd " " ) // lil32() ?
|
# define _(a,b,c,d,e) cc_##a, cc_##b, cc_##c, cc_##d, cc_##e
|
||||||
#define cc8(abcdefgh) ( *(uint64_t*) #abcdefgh " " ) // lil64() ?
|
cc_1 = '1', _(2,3,4,5,6),_(7,8,9,0,_), cc_ = ' ',
|
||||||
|
cc_A = 'A', _(B,C,D,E,F),_(G,H,I,J,K),_(L,M,N,O,P),_(Q,R,S,T,U),_(V,W,X,Y,Z),
|
||||||
|
cc_a = 'a', _(b,c,d,e,f),_(g,h,i,j,k),_(l,m,n,o,p),_(q,r,s,t,u),_(v,w,x,y,z),
|
||||||
|
# undef _
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef BIG
|
||||||
|
#define cc4(a,b,c,d) ((uint32_t)(cc_##a<<24) | (cc_##b<<16) | (cc_##c<<8) | (cc_##d<<0))
|
||||||
|
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(a,b,c,d) << 32ULL) | cc4(e,f,g,h))
|
||||||
|
#else
|
||||||
|
#define cc4(a,b,c,d) ((uint32_t)(cc_##d<<24) | (cc_##c<<16) | (cc_##b<<8) | (cc_##a<<0))
|
||||||
|
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(e,f,g,h) << 32ULL) | cc4(a,b,c,d))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define cc3(a,b,c) cc4(,a,b,c)
|
||||||
|
#define cc5(a,b,c,d,e) cc6(,a,b,c,d,e)
|
||||||
|
#define cc6(a,b,c,d,e,f) cc7(,a,b,c,d,e,f)
|
||||||
|
#define cc7(a,b,c,d,e,f,g) cc8(,a,b,c,d,e,f,g)
|
||||||
|
|
||||||
|
|
|
@ -629,7 +629,7 @@ const char** vfs_list(const char *masks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
char *vfs_unpack(const char *pathfile, int *size) { // must free() after use
|
char *vfs_unpack(const char *pathfile, int *size) { // must FREE() after use
|
||||||
// @todo: add cache here
|
// @todo: add cache here
|
||||||
char *data = NULL;
|
char *data = NULL;
|
||||||
for(archive_dir *dir = dir_mount; dir && !data; dir = dir->next) {
|
for(archive_dir *dir = dir_mount; dir && !data; dir = dir->next) {
|
||||||
|
|
|
@ -65,7 +65,7 @@ void v4k_quit(void) {
|
||||||
void v4k_init() {
|
void v4k_init() {
|
||||||
do_once {
|
do_once {
|
||||||
// install signal handlers
|
// install signal handlers
|
||||||
ifdef(debug, signal_hooks());
|
ifdef(debug, trap_install());
|
||||||
|
|
||||||
// init panic handler
|
// init panic handler
|
||||||
panic_oom_reserve = SYS_REALLOC(panic_oom_reserve, 1<<20); // 1MiB
|
panic_oom_reserve = SYS_REALLOC(panic_oom_reserve, 1<<20); // 1MiB
|
||||||
|
|
|
@ -978,7 +978,13 @@ vec3 transform_scaling (const mat44 m, const vec3 scaling) {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// !!! for debugging
|
// !!! for debugging
|
||||||
|
|
||||||
#include <stdio.h>
|
void printi_( int *m, int ii, int jj ) {
|
||||||
|
for( int j = 0; j < jj; ++j ) {
|
||||||
|
for( int i = 0; i < ii; ++i ) printf("%10d ", *m++);
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
// puts("---");
|
||||||
|
}
|
||||||
void print_( float *m, int ii, int jj ) {
|
void print_( float *m, int ii, int jj ) {
|
||||||
for( int j = 0; j < jj; ++j ) {
|
for( int j = 0; j < jj; ++j ) {
|
||||||
for( int i = 0; i < ii; ++i ) printf("%8.3f", *m++);
|
for( int i = 0; i < ii; ++i ) printf("%8.3f", *m++);
|
||||||
|
@ -986,6 +992,8 @@ void print_( float *m, int ii, int jj ) {
|
||||||
}
|
}
|
||||||
// puts("---");
|
// puts("---");
|
||||||
}
|
}
|
||||||
|
void print2i( vec2i v ) { printi_(&v.x,2,1); }
|
||||||
|
void print3i( vec3i v ) { printi_(&v.x,3,1); }
|
||||||
void print2( vec2 v ) { print_(&v.x,2,1); }
|
void print2( vec2 v ) { print_(&v.x,2,1); }
|
||||||
void print3( vec3 v ) { print_(&v.x,3,1); }
|
void print3( vec3 v ) { print_(&v.x,3,1); }
|
||||||
void print4( vec4 v ) { print_(&v.x,4,1); }
|
void print4( vec4 v ) { print_(&v.x,4,1); }
|
||||||
|
|
|
@ -330,6 +330,8 @@ API bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// debugging and utils
|
// debugging and utils
|
||||||
|
|
||||||
|
API void print2i( vec2i v );
|
||||||
|
API void print3i( vec3i v );
|
||||||
API void print2( vec2 v );
|
API void print2( vec2 v );
|
||||||
API void print3( vec3 v );
|
API void print3( vec3 v );
|
||||||
API void print4( vec4 v );
|
API void print4( vec4 v );
|
||||||
|
|
|
@ -27,7 +27,7 @@ rpc_call rpc_new_call(const char *signature, rpc_function function) {
|
||||||
#if RPC_DEBUG
|
#if RPC_DEBUG
|
||||||
printf("%p %p %s `%s` %s(", function, (void*)hash, rettype, hash_sig, method); for(int i = 0, end = array_count(tokens); i < end; ++i) printf("%s%s", tokens[i], i == (end-1)? "":", "); puts(");");
|
printf("%p %p %s `%s` %s(", function, (void*)hash, rettype, hash_sig, method); for(int i = 0, end = array_count(tokens); i < end; ++i) printf("%s%s", tokens[i], i == (end-1)? "":", "); puts(");");
|
||||||
#endif
|
#endif
|
||||||
return (rpc_call) { strdup(method), function, hash }; // LEAK
|
return (rpc_call) { STRDUP(method), function, hash }; // LEAK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (rpc_call) {0};
|
return (rpc_call) {0};
|
||||||
|
|
|
@ -1,504 +1,44 @@
|
||||||
void *obj_initialize( void **ptr, char *type ) {
|
// -----------------------------------------------------------------------------
|
||||||
unsigned payload = ((unsigned char)type[0]) << 8; type[0] = '\1'; // @fixme: actually use this '\1' custom tag
|
// semantic versioning in a single byte (octal)
|
||||||
#ifndef NDEBUG
|
// - rlyeh, public domain.
|
||||||
strcatf(&type, "%s", callstack(+16)); // debug: for debugging purposes only
|
|
||||||
#endif
|
|
||||||
ptr[0] = OBJBOX((void *)type, payload);
|
|
||||||
|
|
||||||
return &ptr[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
void obj_free( void *obj ) {
|
|
||||||
void *ptr = *((void**)obj - 1);
|
|
||||||
FREE( OBJUNBOX(ptr) );
|
|
||||||
obj = (void**)obj - 1;
|
|
||||||
FREE( obj );
|
|
||||||
}
|
|
||||||
|
|
||||||
void obj_del( void *obj ) {
|
|
||||||
unsigned type = obj_typeid(obj);
|
|
||||||
(type[dtor])(obj);
|
|
||||||
obj_free( obj );
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned obj_instances( const void *obj ) {
|
|
||||||
return OBJPAYLOAD3(obj) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
const char *obj_output( const void *obj ) {
|
|
||||||
void* ptr = *((void**)obj - 1);
|
|
||||||
char *str = OBJUNBOX(ptr);
|
|
||||||
|
|
||||||
while( *str != '\n' ) ++str;
|
|
||||||
return (const char *)str + 1;
|
|
||||||
}
|
|
||||||
void (obj_printf)( void *obj, const char *text ) {
|
|
||||||
void* ptr = *((void**)obj - 1);
|
|
||||||
char* str = OBJUNBOX(ptr);
|
|
||||||
unsigned payload = OBJPAYLOAD16(ptr);
|
|
||||||
|
|
||||||
strcatf(&str, "%s", text);
|
|
||||||
|
|
||||||
*((void**)obj - 1) = OBJBOX(str, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
const char *obj_typeof( const void *obj ) {
|
|
||||||
void* ptr = *((void**)obj - 1);
|
|
||||||
ptr = OBJUNBOX(ptr);
|
|
||||||
|
|
||||||
char name[256]; sscanf((const char*)ptr + 1, "%[^\n]", name); // @fixme: overflow
|
|
||||||
return va("%s", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned obj_typeid(const void *obj) {
|
|
||||||
void* ptr = *((void**)obj - 1);
|
|
||||||
unsigned payload = OBJPAYLOAD16(ptr);
|
|
||||||
return payload >> 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obj_typeeq( const void *obj1, const void *obj2 ) {
|
|
||||||
if( obj_typeid(obj1) != obj_typeid(obj2) ) return false;
|
|
||||||
return !strcmp( obj_typeof(obj1), obj_typeof(obj2) );
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned obj_typeid_from_name(const char *name) {
|
|
||||||
name += strbegi(name, "struct ") ? 8 : strbegi(name, "union ") ? 7 : 0;
|
|
||||||
unsigned typeid = hash_str(name) & 255; // @fixme: increase bits / decrease colliders (256 types only!)
|
|
||||||
ASSERT( typeid, "Name of given class has an empty (zeroed) hash. Limitation by design" );
|
|
||||||
|
|
||||||
static map(unsigned, char *) registered; // @fixme: add mutex
|
|
||||||
do_once map_init(registered, less_int, hash_int);
|
|
||||||
do_once map_insert(registered, 1, "(typeless)");
|
|
||||||
char **found = map_find(registered, typeid);
|
|
||||||
if(!found) map_insert(registered, typeid, STRDUP(name));
|
|
||||||
else ASSERT( !strcmp(name, *found), "Uh-oh, types collided. Please rename one of these two classes '%s'/'%s'", name, *found);
|
|
||||||
|
|
||||||
return typeid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
unsigned obj_sizeof(const void *obj) {
|
|
||||||
void *ptr = (void**)obj - 1;
|
|
||||||
return ALLOCSIZE(ptr) - sizeof(void*);
|
|
||||||
}
|
|
||||||
|
|
||||||
void obj_zero(void *obj) {
|
|
||||||
// clear console log
|
|
||||||
void* ptr = *((void**)obj - 1);
|
|
||||||
char* str = OBJUNBOX(ptr);
|
|
||||||
if( str[0] ) {
|
|
||||||
unsigned payload = OBJPAYLOAD16(ptr);
|
|
||||||
|
|
||||||
unsigned namelen = strlen(obj_typeof(obj));
|
|
||||||
str = REALLOC(str, 1+namelen+2); // preserve \1+name+\n+\0
|
|
||||||
str[1+namelen+2-1] = '\0';
|
|
||||||
|
|
||||||
*((void**)obj - 1) = OBJBOX(str, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset data
|
|
||||||
dtor(obj);
|
|
||||||
memset(obj, 0, obj_sizeof(obj));
|
|
||||||
ctor(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void obj_hexdumpf(FILE *out, const void *obj) {
|
|
||||||
hexdumpf(out, obj, obj_sizeof(obj), 16);
|
|
||||||
|
|
||||||
const char *output = obj_output(obj);
|
|
||||||
fprintf( out, "; ptr=[%p] sizeof=%d typeof=%s typeid=%#x refs=%d\n%s%s\n",
|
|
||||||
obj, obj_sizeof(obj), obj_typeof(obj), obj_typeid(obj), (int)(OBJPAYLOAD16(obj) & 0xFF),
|
|
||||||
output[0] ? output : "(no output)", output[0] ? "---" : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void obj_hexdump(const void *obj) {
|
|
||||||
obj_hexdumpf( stdout, obj );
|
|
||||||
}
|
|
||||||
|
|
||||||
// object: load/save
|
|
||||||
|
|
||||||
unsigned obj_load_buffer(void *obj, const void *src, unsigned srclen) {
|
|
||||||
unsigned objlen = obj_sizeof(obj);
|
|
||||||
if( srclen > objlen ) return 0; // @fixme: do something clever
|
|
||||||
memcpy(obj, src, srclen); // @fixme: do something clever
|
|
||||||
return objlen;
|
|
||||||
}
|
|
||||||
unsigned obj_load(void *obj, const array(char) buffer) {
|
|
||||||
unsigned bytes = buffer ? obj_load_buffer(obj, buffer, array_count((char*)buffer)) : 0;
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
unsigned obj_load_file(void *obj, FILE *fp) {
|
|
||||||
unsigned len = obj_sizeof(obj);
|
|
||||||
char *buffer = va("%*.s", len, "");
|
|
||||||
unsigned read = fread(buffer, 1, len, fp);
|
|
||||||
if( read != (1*len) ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
unsigned bytes = obj_load_buffer(obj, buffer, len);
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned obj_save_buffer(void *dst, unsigned cap, const void *obj) {
|
|
||||||
unsigned len = obj_sizeof(obj);
|
|
||||||
if( len > cap ) return 0;
|
|
||||||
memcpy(dst, obj, len); // @fixme: do something clever
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
array(char) obj_save(const void *obj) { // empty if error. must array_free() after use
|
|
||||||
array(char) data = 0;
|
|
||||||
unsigned len = obj_sizeof(obj);
|
|
||||||
array_resize(data, len);
|
|
||||||
unsigned bytes = obj_save_buffer(data, len, obj);
|
|
||||||
array_resize(data, bytes);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
unsigned obj_save_file(FILE *fp, const void *obj) {
|
|
||||||
unsigned len = obj_sizeof(obj);
|
|
||||||
char *buffer = va("%*.s", len, "");
|
|
||||||
unsigned bytes = obj_save_buffer(buffer, len, obj);
|
|
||||||
if( bytes > 0 ) {
|
|
||||||
unsigned written = fwrite(buffer, 1, len, fp);
|
|
||||||
if( written == (1*len) ) {
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0; // error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
static int __thread global_ref_count; // @fixme: make it atomic
|
|
||||||
|
|
||||||
static void objref_check_atexit(void) {
|
|
||||||
if(global_ref_count > 0) fprintf(stderr, "Warn! Possible memory_leaks: %d refs not destroyed\n", global_ref_count);
|
|
||||||
if(global_ref_count < 0) fprintf(stderr, "Warn! Possible double free: %d refs double destroyed\n", -global_ref_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* obj_ref(void *obj) {
|
|
||||||
do_once atexit(objref_check_atexit);
|
|
||||||
|
|
||||||
if( obj ) {
|
|
||||||
void *ptr = *((void**)obj - 1);
|
|
||||||
unsigned payload = OBJPAYLOAD16(ptr);
|
|
||||||
unsigned ref_count = payload & 255;
|
|
||||||
ASSERT(ref_count < 255, "Object cannot hold more than 256 refs. Limitation by design.");
|
|
||||||
|
|
||||||
*((void**)obj - 1) = OBJBOX(OBJUNBOX(ptr), payload + 1);
|
|
||||||
global_ref_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* obj_unref(void *obj) {
|
|
||||||
if( obj ) {
|
|
||||||
void *ptr = *((void**)obj - 1);
|
|
||||||
unsigned payload = OBJPAYLOAD16(ptr);
|
|
||||||
unsigned ref_count = payload & 255;
|
|
||||||
|
|
||||||
*((void**)obj - 1) = OBJBOX(OBJUNBOX(ptr), payload - 1);
|
|
||||||
global_ref_count--;
|
|
||||||
|
|
||||||
if( ref_count <= 1 ) {
|
|
||||||
obj_del(obj);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
void dummy1() {}
|
|
||||||
#define dummy8 dummy1,dummy1,dummy1,dummy1,dummy1,dummy1,dummy1,dummy1
|
|
||||||
#define dummy64 dummy8,dummy8,dummy8,dummy8,dummy8,dummy8,dummy8,dummy8
|
|
||||||
#define dummy256 dummy64,dummy64,dummy64,dummy64
|
|
||||||
|
|
||||||
void (*dtor[256])() = { dummy256 };
|
|
||||||
void (*ctor[256])() = { dummy256 };
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
static set(uintptr_t) vtables; // @fixme: add mutex
|
|
||||||
|
|
||||||
void (obj_override)(const char *objclass, void (**vtable)(), void(*fn)()) {
|
|
||||||
do_once set_init(vtables, less_64, hash_64);
|
|
||||||
set_find_or_add(vtables, (uintptr_t)vtable);
|
|
||||||
|
|
||||||
vtable[ obj_typeid_from_name(objclass) ] = fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void (obj_extend)(const char *dstclass, const char *srcclass) { // wip, @testme
|
|
||||||
unsigned dst_id = obj_typeid_from_name(dstclass);
|
|
||||||
unsigned src_id = obj_typeid_from_name(srcclass);
|
|
||||||
|
|
||||||
// iterate src vtables, and assign them to dst
|
|
||||||
if(dst_id != src_id)
|
|
||||||
for each_set(vtables, void **, src_table) {
|
|
||||||
if( src_table[src_id] ) {
|
|
||||||
src_table[dst_id] = (void(*)()) (src_table[ src_id ]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
void *obj_clone(const void *obj) { // @fixme: clone object console as well?
|
|
||||||
unsigned sz = obj_sizeof(obj);
|
|
||||||
const char *nm = obj_typeof(obj);
|
|
||||||
unsigned id = obj_typeid(obj);
|
|
||||||
|
|
||||||
void *obj2 = obj_initialize((void**)MALLOC(sizeof(void*)+sz), stringf("%c%s\n" "cloned" "\n", id, nm)); // STRDUP( OBJUNBOX(*((void**)obj - 1)) ));
|
|
||||||
memcpy(obj2, obj, sz);
|
|
||||||
return obj2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *obj_copy(void **dst, const void *src) {
|
|
||||||
if(!*dst) return *dst = obj_clone(src);
|
|
||||||
|
|
||||||
if( obj_typeeq(*dst, src) ) {
|
|
||||||
return memcpy(*dst, src, obj_sizeof(src));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *obj_mutate(void **dst_, const void *src) {
|
|
||||||
// mutate a class. ie, convert a given object class into a different one,
|
|
||||||
// while preserving the original metas and references as much as possible.
|
|
||||||
//
|
//
|
||||||
// @fixme: systems might be tracking objects in the future. the fact that we
|
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||||
// can reallocate a pointer (and hence, change its address) seems way too dangerous,
|
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||||
// as the tracking systems could crash when referencing a mutated object.
|
// - comparison checks only major.minor tuple as per convention.
|
||||||
// solutions: do not reallocate if sizeof(new_class) > sizeof(old_class) maybe? good enough?
|
|
||||||
// also, optimization hint: no need to reallocate if both sizes matches, just copy contents.
|
|
||||||
|
|
||||||
if(!*dst_) return *dst_ = obj_clone(src);
|
int semver( int major, int minor, int patch ) {
|
||||||
|
return SEMVER(major, minor, patch);
|
||||||
void *dst = *dst_;
|
}
|
||||||
dtor(dst);
|
int semvercmp( int v1, int v2 ) {
|
||||||
|
return SEMVERCMP(v1, v2);
|
||||||
unsigned src_sz = obj_sizeof(src);
|
|
||||||
unsigned src_id = obj_typeid(src);
|
|
||||||
|
|
||||||
void *dst_ptr = *((void**)dst - 1);
|
|
||||||
unsigned payload = (OBJPAYLOAD16(dst_ptr) & 255) | src_id << 8;
|
|
||||||
FREE( OBJUNBOX(dst_ptr) );
|
|
||||||
*((void**)dst - 1) = OBJBOX( STRDUP( OBJUNBOX(*((void**)src - 1)) ), payload);
|
|
||||||
|
|
||||||
void *base = (void*)((void**)dst - 1);
|
|
||||||
base = REALLOC(base, src_sz + sizeof(void*));
|
|
||||||
*dst_ = (char*)base + sizeof(void*);
|
|
||||||
dst = (char*)base + sizeof(void*);
|
|
||||||
memcpy(dst, src, src_sz);
|
|
||||||
|
|
||||||
ctor(dst);
|
|
||||||
return dst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
AUTORUN {
|
||||||
|
for( int i= 0; i <= 255; ++i) printf(SEMVERFMT ",", i);
|
||||||
|
puts("");
|
||||||
|
|
||||||
#ifdef OBJ_DEMO
|
printf(SEMVERFMT "\n", semver(3,7,7));
|
||||||
|
printf(SEMVERFMT "\n", semver(2,7,7));
|
||||||
|
printf(SEMVERFMT "\n", semver(1,7,7));
|
||||||
|
printf(SEMVERFMT "\n", semver(0,7,7));
|
||||||
|
|
||||||
typedef struct MyObject {
|
printf(SEMVERFMT "\n", semver(3,7,1));
|
||||||
char* id;
|
printf(SEMVERFMT "\n", semver(2,5,3));
|
||||||
int x,y;
|
printf(SEMVERFMT "\n", semver(1,3,5));
|
||||||
float rotation;
|
printf(SEMVERFMT "\n", semver(0,1,7));
|
||||||
struct MyObject *next;
|
|
||||||
} MyObject;
|
|
||||||
|
|
||||||
void tests1() {
|
|
||||||
// Construct two objects
|
|
||||||
MyObject *root = obj_new(MyObject, 0);
|
|
||||||
MyObject *obj = obj_new(MyObject, "An identifier!", 0x11, 0x22, 3.1415f, root );
|
|
||||||
|
|
||||||
// Log some lines
|
|
||||||
obj_printf(root, "this is a logline #1\n");
|
|
||||||
obj_printf(root, "this is a logline #2\n");
|
|
||||||
obj_printf(root, "this is a logline #3\n");
|
|
||||||
|
|
||||||
obj_printf(obj, "yet another logline #1\n");
|
|
||||||
obj_printf(obj, "yet another logline #2\n");
|
|
||||||
|
|
||||||
// Dump contents of our objects
|
|
||||||
|
|
||||||
obj_hexdump(root);
|
|
||||||
obj_hexdump(obj);
|
|
||||||
|
|
||||||
// Save to mem
|
|
||||||
|
|
||||||
array(char) buffer = obj_save(obj);
|
|
||||||
printf("%d bytes\n", (int)array_count(buffer));
|
|
||||||
|
|
||||||
// Clear
|
|
||||||
|
|
||||||
obj_zero( obj );
|
|
||||||
obj_hexdump( obj );
|
|
||||||
|
|
||||||
// Reload
|
|
||||||
|
|
||||||
obj_load( obj, buffer );
|
|
||||||
obj_hexdump( obj );
|
|
||||||
|
|
||||||
// Copy tests
|
|
||||||
|
|
||||||
{
|
|
||||||
MyObject *clone = obj_clone(obj);
|
|
||||||
obj_hexdump(clone);
|
|
||||||
obj_del(clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
MyObject *copy = 0;
|
|
||||||
obj_copy(©, obj);
|
|
||||||
obj_hexdump(copy);
|
|
||||||
obj_del(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
MyObject *copy = obj_new(MyObject, "A different identifier!", 0x33, 0x44, 0.0f, root );
|
|
||||||
obj_copy(©, obj);
|
|
||||||
obj_hexdump(copy);
|
|
||||||
obj_del(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
void *copy = obj_malloc(100, "an untyped class" );
|
|
||||||
obj_mutate(©, obj);
|
|
||||||
obj_hexdump(copy);
|
|
||||||
obj_copy(©, obj);
|
|
||||||
obj_hexdump(copy);
|
|
||||||
obj_del(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmarking call overhead.
|
|
||||||
// We're here using dtor as a method to test. Since there is actually no
|
|
||||||
// destructor associated to this class, it will be safe to call it extensively (no double frees).
|
|
||||||
//
|
|
||||||
// results:
|
|
||||||
// 427 million calls/s @ old i5-4300/1.90Ghz laptop. compiled with "cl /Ox /Os /MT /DNDEBUG /GL /GF /arch:AVX2"
|
|
||||||
|
|
||||||
#ifndef N
|
|
||||||
#define N (INT32_MAX-1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
double t = (puts("benchmarking..."), -clock() / (double)CLOCKS_PER_SEC);
|
|
||||||
for( int i = 0; i < N; ++i ) {
|
|
||||||
dtor(root);
|
|
||||||
}
|
|
||||||
t += clock() / (double)CLOCKS_PER_SEC;
|
|
||||||
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
|
|
||||||
|
|
||||||
|
assert( semvercmp( 0357, 0300 ) > 0 );
|
||||||
|
assert( semvercmp( 0277, 0300 ) < 0 );
|
||||||
|
assert( semvercmp( 0277, 0200 ) > 0 );
|
||||||
|
assert( semvercmp( 0277, 0100 ) < 0 );
|
||||||
|
assert( semvercmp( 0076, 0070 ) == 0 );
|
||||||
|
assert( semvercmp( 0076, 0077 ) == 0 );
|
||||||
|
assert( semvercmp( 0176, 0170 ) == 0 );
|
||||||
|
assert( semvercmp( 0176, 0177 ) == 0 );
|
||||||
|
assert( semvercmp( 0276, 0270 ) == 0 );
|
||||||
|
assert( semvercmp( 0276, 0277 ) == 0 );
|
||||||
|
assert( semvercmp( 0376, 0370 ) == 0 );
|
||||||
|
assert( semvercmp( 0376, 0377 ) == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------
|
|
||||||
|
|
||||||
#define dump(obj) dump[obj_typeid(obj)](obj)
|
|
||||||
#define area(obj) area[obj_typeid(obj)](obj)
|
|
||||||
|
|
||||||
extern void (*dump[256])();
|
|
||||||
extern float (*area[256])();
|
|
||||||
|
|
||||||
void (*dump[256])() = {0};
|
|
||||||
float (*area[256])() = {0};
|
|
||||||
|
|
||||||
// --------------
|
|
||||||
|
|
||||||
typedef struct box {
|
|
||||||
float w;
|
|
||||||
} box;
|
|
||||||
|
|
||||||
void box_ctor(box *b) { printf("box already constructed: box-w:%f\n", b->w); }
|
|
||||||
void box_dtor(box *b) { puts("deleting box..."); }
|
|
||||||
void box_dump(box *b) { printf("box-w:%f\n", b->w); }
|
|
||||||
float box_area(box *b) { return b->w * b->w; }
|
|
||||||
|
|
||||||
#define REGISTER_BOX \
|
|
||||||
obj_override(box, ctor); \
|
|
||||||
obj_override(box, dump); \
|
|
||||||
obj_override(box, area); \
|
|
||||||
obj_override(box, dtor);
|
|
||||||
|
|
||||||
typedef struct rect {
|
|
||||||
float w, h;
|
|
||||||
} rect;
|
|
||||||
|
|
||||||
void rect_dump(rect *r) { printf("rect-w:%f rect-h:%f\n", r->w, r->h); }
|
|
||||||
float rect_area(rect *r) { return r->w * r->h; }
|
|
||||||
void rect_dtor(rect *r) { puts("deleting rect..."); }
|
|
||||||
|
|
||||||
#define REGISTER_RECT \
|
|
||||||
obj_override(rect, dump); \
|
|
||||||
obj_override(rect, area); \
|
|
||||||
obj_override(rect, dtor);
|
|
||||||
|
|
||||||
void tests2() {
|
|
||||||
REGISTER_BOX
|
|
||||||
REGISTER_RECT
|
|
||||||
|
|
||||||
box *b = obj_new(box, 100);
|
|
||||||
rect *r = obj_new(rect, 100, 200);
|
|
||||||
|
|
||||||
dump(b);
|
|
||||||
dump(r);
|
|
||||||
|
|
||||||
printf("%f\n", area(b));
|
|
||||||
printf("%f\n", area(r));
|
|
||||||
|
|
||||||
obj_del(b);
|
|
||||||
obj_ref(r); obj_unref(r); //obj_del(r);
|
|
||||||
|
|
||||||
int *untyped = obj_malloc( sizeof(int) );
|
|
||||||
int *my_number = obj_malloc( sizeof(int), "a comment about my_number" );
|
|
||||||
char *my_text = obj_malloc( 32, "some debug info here" );
|
|
||||||
|
|
||||||
*untyped = 100;
|
|
||||||
*my_number = 123;
|
|
||||||
sprintf( my_text, "hello world" );
|
|
||||||
|
|
||||||
struct my_bitmap { int w, h, bpp; const char *pixels; };
|
|
||||||
struct my_bitmap *my_bitmap = obj_new(struct my_bitmap, 2,2,8, "\1\2\3\4");
|
|
||||||
|
|
||||||
printf( "%p(%s,%u)\n", my_bitmap, obj_typeof(my_bitmap), obj_typeid(my_bitmap) );
|
|
||||||
printf( "%d(%s,%d)\n", *untyped, obj_typeof(untyped), obj_typeid(untyped) );
|
|
||||||
printf( "%d(%s,%d)\n", *my_number, obj_typeof(my_number), obj_typeid(my_number) );
|
|
||||||
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
|
|
||||||
|
|
||||||
obj_printf(my_text, "hello world #1\n");
|
|
||||||
obj_printf(my_text, "hello world #2\n");
|
|
||||||
puts(obj_output(my_text));
|
|
||||||
|
|
||||||
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
|
|
||||||
|
|
||||||
printf( "equal?:%d\n", obj_typeeq(my_number, untyped) );
|
|
||||||
printf( "equal?:%d\n", obj_typeeq(my_number, my_number) );
|
|
||||||
printf( "equal?:%d\n", obj_typeeq(my_number, my_text) );
|
|
||||||
printf( "equal?:%d\n", obj_typeeq(my_number, my_bitmap) );
|
|
||||||
|
|
||||||
obj_free( untyped );
|
|
||||||
obj_free( my_text );
|
|
||||||
obj_free( my_bitmap );
|
|
||||||
obj_del( my_number ); // should not crash, even if allocated with obj_malloc()
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
tests1();
|
|
||||||
tests2();
|
|
||||||
puts("ok");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
MEMBER( MyObject, char*, id );
|
|
||||||
MEMBER( MyObject, int, x );
|
|
||||||
MEMBER( MyObject, int, y );
|
|
||||||
MEMBER( MyObject, float, rotation, "(degrees)" );
|
|
||||||
MEMBER( MyObject, MyObject*, next, "(linked list)" );
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define main main__
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,131 +1,95 @@
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// C object framework (constructors/destructors, methods, rtti, refcounting)
|
// semantic versioning in a single byte (octal)
|
||||||
// - rlyeh, public domain.
|
// - rlyeh, public domain.
|
||||||
//
|
//
|
||||||
// ## object api (low level)
|
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||||
|
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||||
|
// - comparison checks only major.minor tuple as per convention.
|
||||||
|
|
||||||
|
API int semver( int major, int minor, int patch );
|
||||||
|
API int semvercmp( int v1, int v2 );
|
||||||
|
|
||||||
|
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
|
||||||
|
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
|
||||||
|
#define SEMVERFMT "%03o"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// autorun initializers for C
|
||||||
|
// - rlyeh, public domain
|
||||||
//
|
//
|
||||||
// - [ ] make object from reflected type (factory)
|
// note: based on code by Joe Lowe (public domain).
|
||||||
// - [x] make object (if debug, include callstack as well)
|
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
|
||||||
// - [x] ctor method (optional, ref to constructor)
|
|
||||||
// - [x] dtor method (optional, ref to deleter)
|
|
||||||
// - [x] zero mem object
|
|
||||||
// - [x] object logger
|
|
||||||
// - [ ] iterate members in a struct
|
|
||||||
//
|
|
||||||
// - [x] clone/copy/mutate classes
|
|
||||||
// - [x] load/save objects from/to memory/disk
|
|
||||||
// - [ ] diff/patch objects
|
|
||||||
// - [ ] experimental: support for AoSoA layout (using objcnt, 3bits)
|
|
||||||
//
|
|
||||||
// ## object decomposition
|
|
||||||
//
|
|
||||||
// <---------|--------->
|
|
||||||
// OBJ-SHADOW (64-bits) | OBJ CONTENT (N bytes)
|
|
||||||
// +-----+-----+-------------+-----------+-----+-----+-----+-----+--
|
|
||||||
// |TYPE |REFS.| OBJ NAME | obj cnt | ... | ... | ... | ... | .
|
|
||||||
// +-----+-----+-------------+-----------+-----+-----+-----+-----+--
|
|
||||||
// \-16-bits--/\---45-bits--/\--3-bits--/\-------N-bytes-----------
|
|
||||||
//
|
|
||||||
// OBJ TYPE+NAME format:
|
|
||||||
// - [type] custom tags at 0x0
|
|
||||||
// - [1..N] name
|
|
||||||
// - [\n] blank separator
|
|
||||||
// - [comments, logger, infos, etc] << obj_printf();
|
|
||||||
//
|
|
||||||
// ## object limitations
|
|
||||||
// - 256 classes max
|
|
||||||
// - 256 references max
|
|
||||||
// - 8-byte overhead per object
|
|
||||||
// - 2 total allocs per object (could be flattened into 1 with some more work)
|
|
||||||
//
|
|
||||||
// @todo: obj_extend( "class_src", "class_dst" ); call[super(obj)]()
|
|
||||||
// @todo: preferred load/save format: [ver:1,user:2,type:1] ([eof|size:7/15/23/31][blob:N])+ [crc:1/2/3/4]
|
|
||||||
// @todo: more serious loading/saving spec
|
|
||||||
|
|
||||||
// object api (heap+rtti)
|
#ifdef __cplusplus
|
||||||
|
#define AUTORUN \
|
||||||
|
static void AUTORUN_U(f)(void); \
|
||||||
|
static const int AUTORUN_J(AUTORUN_U(f),__1) = (AUTORUN_U(f)(), 1); \
|
||||||
|
static void AUTORUN_U(f)(void)
|
||||||
|
#elif _MSC_VER
|
||||||
|
#define AUTORUN \
|
||||||
|
static void AUTORUN_U(f)(void); \
|
||||||
|
static int AUTORUN_J(AUTORUN_U(f),__1) (){ AUTORUN_U(f)(); return 0; } \
|
||||||
|
__pragma(section(".CRT$XIU", long, read)) \
|
||||||
|
__declspec(allocate(".CRT$XIU")) \
|
||||||
|
static int(* AUTORUN_J(AUTORUN_U(f),__2) )() = AUTORUN_J(AUTORUN_U(f),__1); \
|
||||||
|
static void AUTORUN_U(f)(void)
|
||||||
|
#else
|
||||||
|
#define AUTORUN \
|
||||||
|
__attribute__((constructor)) \
|
||||||
|
static void AUTORUN_U(f)(void)
|
||||||
|
#endif
|
||||||
|
|
||||||
API void* obj_malloc( int sz, ... );
|
// join + unique macro utils
|
||||||
API void* obj_calloc( int sz, ... );
|
|
||||||
API void obj_free( void *obj );
|
|
||||||
|
|
||||||
API bool obj_typeeq( const void *obj1, const void *obj2 );
|
#define AUTORUN_j(a, b) a##b
|
||||||
API const char* obj_typeof( const void *obj );
|
#define AUTORUN_J(a, b) AUTORUN_j(a, b)
|
||||||
API unsigned obj_typeid( const void *obj );
|
#define AUTORUN_U(x) AUTORUN_J(x, __LINE__)
|
||||||
API unsigned obj_typeid_from_name( const char *name );
|
|
||||||
|
|
||||||
// object api (ctor/dtor, refcounting, oop)
|
#if 0 // autorun demo
|
||||||
|
void byebye(void) { puts("seen after main()"); }
|
||||||
|
AUTORUN { puts("seen before main()"); }
|
||||||
|
AUTORUN { puts("seen before main() too"); atexit( byebye ); }
|
||||||
|
#endif
|
||||||
|
|
||||||
API void obj_new( const char *type, ... );
|
// -----------------------------------------------------------------------------
|
||||||
API void obj_del( void *obj );
|
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
|
||||||
|
|
||||||
API void* obj_ref( void *obj );
|
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||||
API void* obj_unref( void *obj );
|
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||||
|
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||||
|
|
||||||
API void obj_extend( const char *dstclass, const char *srcclass );
|
typedef struct int2 { int x,y; } int2;
|
||||||
API void obj_override( const char *objclass, void (**vtable)(), void(*fn)() );
|
typedef struct int3 { int x,y,z; } int3;
|
||||||
|
typedef struct int4 { int x,y,z,w; } int4;
|
||||||
|
|
||||||
// object: serialize
|
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||||
|
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||||
|
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||||
|
|
||||||
API unsigned obj_load(void *obj, const array(char) buffer);
|
typedef struct float2 { float x,y; } float2;
|
||||||
API unsigned obj_load_file(void *obj, FILE *fp);
|
typedef struct float3 { float x,y,z; } float3;
|
||||||
API unsigned obj_load_inplace(void *obj, const void *src, unsigned srclen);
|
typedef struct float4 { float x,y,z,w; } float4;
|
||||||
|
|
||||||
API array(char) obj_save(const void *obj); // empty if error. must array_free() after use
|
typedef struct double2 { double x,y; } double2;
|
||||||
API unsigned obj_save_file(FILE *fp, const void *obj);
|
typedef struct double3 { double x,y,z; } double3;
|
||||||
API unsigned obj_save_inplace(void *dst, unsigned cap, const void *obj);
|
typedef struct double4 { double x,y,z,w; } double4;
|
||||||
|
|
||||||
// object: utils
|
#define byte2(x,y) M_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
|
||||||
|
#define byte3(x,y,z) M_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
|
||||||
|
#define byte4(x,y,z,w) M_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
|
||||||
|
|
||||||
API unsigned obj_instances( const void *obj );
|
#define int2(x,y) M_CAST(int2, (int)(x), (int)(y) )
|
||||||
|
#define int3(x,y,z) M_CAST(int3, (int)(x), (int)(y), (int)(z) )
|
||||||
|
#define int4(x,y,z,w) M_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
|
||||||
|
|
||||||
API void obj_zero( void *obj );
|
#define uint2(x,y) M_CAST(uint2, (unsigned)(x), (unsigned)(y) )
|
||||||
API unsigned obj_sizeof( const void *obj );
|
#define uint3(x,y,z) M_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
|
||||||
|
#define uint4(x,y,z,w) M_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
|
||||||
|
|
||||||
API void obj_hexdump( const void *obj );
|
#define float2(x,y) M_CAST(float2, (float)(x), (float)(y) )
|
||||||
API void obj_hexdumpf( FILE *out, const void *obj );
|
#define float3(x,y,z) M_CAST(float3, (float)(x), (float)(y), (float)(z) )
|
||||||
|
#define float4(x,y,z,w) M_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
|
||||||
|
|
||||||
API void obj_printf( void *obj, const char *text );
|
#define double2(x,y) M_CAST(double2, (double)(x), (double)(y) )
|
||||||
API const char* obj_output( const void *obj );
|
#define double3(x,y,z) M_CAST(double3, (double)(x), (double)(y), (double)(z) )
|
||||||
|
#define double4(x,y,z,w) M_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
|
||||||
API void * obj_clone(const void *obj);
|
|
||||||
API void * obj_copy(void **dst, const void *src);
|
|
||||||
API void * obj_mutate(void **dst, const void *src);
|
|
||||||
|
|
||||||
// object: method dispatch tables
|
|
||||||
|
|
||||||
#define ctor(obj) obj_method0(obj, ctor) // ctor[obj_typeid(obj)](obj)
|
|
||||||
#define dtor(obj) obj_method0(obj, dtor) // dtor[obj_typeid(obj)](obj)
|
|
||||||
|
|
||||||
API extern void (*ctor[256])(); ///-
|
|
||||||
API extern void (*dtor[256])(); ///-
|
|
||||||
|
|
||||||
// object: syntax sugars
|
|
||||||
|
|
||||||
#define obj_malloc(sz, ...) obj_initialize((void**)MALLOC( sizeof(void*)+sz), stringf("\1untyped\n%s\n", "" #__VA_ARGS__))
|
|
||||||
#define obj_calloc(sz, ...) obj_initialize((void**)CALLOC(1,sizeof(void*)+sz), stringf("\1untyped\n%s\n", "" #__VA_ARGS__))
|
|
||||||
|
|
||||||
#define obj_new0(type) obj_new(type, 0)
|
|
||||||
#define obj_new(type, ...) ( \
|
|
||||||
obj_tmpalloc = obj_initialize((void**)CALLOC(1, sizeof(void*)+sizeof(type)), stringf("%c" #type "\n", (char)obj_typeid_from_name(#type))), \
|
|
||||||
(*(type*)obj_tmpalloc = (type){ __VA_ARGS__ }), \
|
|
||||||
ctor(obj_tmpalloc), \
|
|
||||||
(type*)obj_tmpalloc )
|
|
||||||
|
|
||||||
#define obj_override(class, method) obj_override(#class, (void(**)())method, (void(*)())class##_##method)
|
|
||||||
#define obj_method0(obj, method) method[obj_typeid(obj)]((obj))
|
|
||||||
#define obj_method(obj, method, ...) method[obj_typeid(obj)]((obj), __VA_ARGS__)
|
|
||||||
|
|
||||||
#define obj_printf(obj, ...) obj_printf(obj, va(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define obj_extend(dstclass, srcclass) obj_extend(#dstclass, #srcclass)
|
|
||||||
|
|
||||||
// object: implementation details
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/16198700/using-the-extra-16-bits-in-64-bit-pointers (note: using 19-bits here)
|
|
||||||
#define OBJBOX(ptr, payload16) (void*)(((long long unsigned)(payload16) << 48) | (long long unsigned)(ptr))
|
|
||||||
#define OBJUNBOX(ptr) (void*)((long long unsigned)(ptr) & 0x0000FFFFFFFFFFFFull)
|
|
||||||
#define OBJPAYLOAD16(ptr) (((long long unsigned)(ptr)) >> 48)
|
|
||||||
#define OBJPAYLOAD3(ptr) (((long long unsigned)(ptr)) & 7)
|
|
||||||
|
|
||||||
API void* obj_initialize( void **ptr, char *type_and_info );
|
|
||||||
static __thread void *obj_tmpalloc;
|
|
||||||
|
|
|
@ -2188,7 +2188,7 @@ bool spine_(spine_t *t, const char *file_json, const char *file_atlas, unsigned
|
||||||
array_push(t->skins[i].rects, t->skins[0].rects[j]);
|
array_push(t->skins[i].rects, t->skins[0].rects[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @leak @fixme: free(t->skins[0])
|
// @leak @fixme: FREE(t->skins[0])
|
||||||
for( int i = 0; i < array_count(t->skins)-1; ++i ) {
|
for( int i = 0; i < array_count(t->skins)-1; ++i ) {
|
||||||
t->skins[i] = t->skins[i+1];
|
t->skins[i] = t->skins[i+1];
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,23 +182,26 @@ void camera_orbit( camera_t *cam, float yaw, float pitch, float inc_distance ) {
|
||||||
|
|
||||||
int ui_camera( camera_t *cam ) {
|
int ui_camera( camera_t *cam ) {
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
changed |= ui_float("Speed", &cam->speed);
|
|
||||||
ui_separator();
|
|
||||||
changed |= ui_bool("Damping", &cam->damping);
|
changed |= ui_bool("Damping", &cam->damping);
|
||||||
if( !cam->damping ) ui_disable();
|
if( !cam->damping ) ui_disable();
|
||||||
changed |= ui_slider2("Move friction", &cam->move_friction, va("%5.2f", cam->move_friction));
|
changed |= ui_slider2("Move friction", &cam->move_friction, va("%5.3f", cam->move_friction));
|
||||||
changed |= ui_slider2("Move damping", &cam->move_damping, va("%5.2f", cam->move_damping));
|
changed |= ui_slider2("Move damping", &cam->move_damping, va("%5.3f", cam->move_damping));
|
||||||
changed |= ui_slider2("View driction", &cam->look_friction, va("%5.2f", cam->look_friction));
|
changed |= ui_slider2("View friction", &cam->look_friction, va("%5.3f", cam->look_friction));
|
||||||
changed |= ui_slider2("View damping", &cam->look_damping, va("%5.2f", cam->look_damping));
|
changed |= ui_slider2("View damping", &cam->look_damping, va("%5.3f", cam->look_damping));
|
||||||
if( !cam->damping ) ui_enable();
|
if( !cam->damping ) ui_enable();
|
||||||
ui_separator();
|
ui_separator();
|
||||||
changed |= ui_float3("Position", &cam->position.x);
|
changed |= ui_float("Speed", &cam->speed);
|
||||||
changed |= ui_float3("LookDir", &cam->lookdir.x);
|
changed |= ui_float3("Position", cam->position.v3);
|
||||||
changed |= ui_float3("UpDir", &cam->updir.x);
|
changed |= ui_float3("LookDir", cam->lookdir.v3);
|
||||||
|
changed |= ui_float3("UpDir", cam->updir.v3);
|
||||||
|
ui_disable();
|
||||||
changed |= ui_mat44("View matrix", cam->view);
|
changed |= ui_mat44("View matrix", cam->view);
|
||||||
|
ui_enable();
|
||||||
ui_separator();
|
ui_separator();
|
||||||
changed |= ui_float("FOV (degrees)", &cam->fov);
|
changed |= ui_float("FOV (degrees)", &cam->fov);
|
||||||
|
ui_disable();
|
||||||
changed |= ui_mat44("Projection matrix", cam->proj);
|
changed |= ui_mat44("Projection matrix", cam->proj);
|
||||||
|
ui_enable();
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -331,3 +331,50 @@ array(uint32_t) string32( const char *utf8 ) {
|
||||||
return out[slot];
|
return out[slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// quarks
|
||||||
|
|
||||||
|
unsigned quark_intern( quarks_db *quarks, const char *string ) {
|
||||||
|
if( !*quarks ) {
|
||||||
|
// copy null string on init
|
||||||
|
array_push(*quarks, '\0');
|
||||||
|
}
|
||||||
|
if( string && string[0] ) {
|
||||||
|
int slen = strlen(string)+1;
|
||||||
|
int qlen = array_count(*quarks);
|
||||||
|
array_resize(*quarks, qlen + slen);
|
||||||
|
memcpy( array_back(*quarks) + 1 - slen, string, slen );
|
||||||
|
return qlen;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const char *quark_string( quarks_db *quarks, unsigned key ) {
|
||||||
|
assert( *quarks );
|
||||||
|
return *quarks + key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __thread quarks_db qdb = 0;
|
||||||
|
unsigned intern( const char *string ) {
|
||||||
|
return quark_intern( &qdb, string );
|
||||||
|
}
|
||||||
|
const char *quark( unsigned key ) {
|
||||||
|
return quark_string( &qdb, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
AUTORUN {
|
||||||
|
assert( !intern(NULL) ); // quark #0, cannot intern null string
|
||||||
|
assert( !intern("") ); // quark #0, ok to intern empty string
|
||||||
|
assert( !quark(0)[0] ); // empty string for quark #0
|
||||||
|
|
||||||
|
unsigned q1 = intern("Hello"); // -> quark #1
|
||||||
|
unsigned q2 = intern("cruel"); // -> quark #2
|
||||||
|
unsigned q3 = intern("world."); // -> quark #3
|
||||||
|
|
||||||
|
char buf[256];
|
||||||
|
sprintf(buf, "%s %s %s", quark(q1), quark(q2), quark(q3));
|
||||||
|
assert( !strcmp("Hello cruel world.", buf) );
|
||||||
|
|
||||||
|
assert(~puts("Ok"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -73,3 +73,14 @@ API char* strjoin(array(char*) list, const char *separator);
|
||||||
|
|
||||||
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
||||||
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// ## string interning (quarks)
|
||||||
|
// - rlyeh, public domain.
|
||||||
|
|
||||||
|
unsigned intern( const char *string );
|
||||||
|
const char *quark( unsigned key );
|
||||||
|
|
||||||
|
typedef array(char) quarks_db;
|
||||||
|
unsigned quark_intern( quarks_db*, const char *string );
|
||||||
|
const char *quark_string( quarks_db*, unsigned key );
|
||||||
|
|
|
@ -252,9 +252,9 @@ int callstackf( FILE *fp, int traces ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// signals --------------------------------------------------------------------
|
// trap signals ---------------------------------------------------------------
|
||||||
|
|
||||||
const char *signal_name(int signal) {
|
const char *trap_name(int signal) {
|
||||||
if(signal == SIGABRT) return "SIGABRT - \"abort\", abnormal termination";
|
if(signal == SIGABRT) return "SIGABRT - \"abort\", abnormal termination";
|
||||||
if(signal == SIGFPE) return "SIGFPE - floating point exception";
|
if(signal == SIGFPE) return "SIGFPE - floating point exception";
|
||||||
if(signal == SIGILL) return "SIGILL - \"illegal\", invalid instruction";
|
if(signal == SIGILL) return "SIGILL - \"illegal\", invalid instruction";
|
||||||
|
@ -266,49 +266,41 @@ const char *signal_name(int signal) {
|
||||||
ifndef(win32, if(signal == SIGQUIT) return "SIGQUIT");
|
ifndef(win32, if(signal == SIGQUIT) return "SIGQUIT");
|
||||||
return "??";
|
return "??";
|
||||||
}
|
}
|
||||||
void signal_handler_ignore(int signal_) {
|
void trap_on_ignore(int signal_) {
|
||||||
signal(signal_, signal_handler_ignore);
|
signal(signal_, trap_on_ignore);
|
||||||
}
|
}
|
||||||
void signal_handler_quit(int signal) {
|
void trap_on_quit(int signal) {
|
||||||
// fprintf(stderr, "Ok: caught signal %s (%d)\n", signal_name(signal), signal);
|
// fprintf(stderr, "Ok: caught signal %s (%d)\n", trap_name(signal), signal);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
void signal_handler_abort(int signal) {
|
void trap_on_abort(int signal) {
|
||||||
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", signal_name(signal), signal, callstack(16));
|
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", trap_name(signal), signal, callstack(16));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
void signal_handler_debug(int signal) {
|
void trap_on_debug(int signal) {
|
||||||
breakpoint("Error: unexpected signal");
|
breakpoint("Error: unexpected signal");
|
||||||
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", signal_name(signal), signal, callstack(16));
|
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", trap_name(signal), signal, callstack(16));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
void signal_hooks(void) {
|
void trap_install(void) {
|
||||||
// expected signals
|
// expected signals
|
||||||
signal(SIGINT, signal_handler_quit);
|
signal(SIGINT, trap_on_quit);
|
||||||
signal(SIGTERM, signal_handler_quit);
|
signal(SIGTERM, trap_on_quit);
|
||||||
ifndef(win32, signal(SIGQUIT, signal_handler_quit));
|
ifndef(win32, signal(SIGQUIT, trap_on_quit));
|
||||||
// unexpected signals
|
// unexpected signals
|
||||||
signal(SIGABRT, signal_handler_abort);
|
signal(SIGABRT, trap_on_abort);
|
||||||
signal(SIGFPE, signal_handler_abort);
|
signal(SIGFPE, trap_on_abort);
|
||||||
signal(SIGILL, signal_handler_abort);
|
signal(SIGILL, trap_on_abort);
|
||||||
signal(SIGSEGV, signal_handler_abort);
|
signal(SIGSEGV, trap_on_abort);
|
||||||
ifndef(win32, signal(SIGBUS, signal_handler_abort));
|
ifndef(win32, signal(SIGBUS, trap_on_abort));
|
||||||
ifdef(linux, signal(SIGSTKFLT, signal_handler_abort));
|
ifdef(linux, signal(SIGSTKFLT, trap_on_abort));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SIGNAL_DEMO
|
#ifdef TRAP_DEMO
|
||||||
void crash() {
|
AUTORUN {
|
||||||
char *ptr = 0;
|
trap_install();
|
||||||
*ptr = 1;
|
app_crash(); // app_hang();
|
||||||
}
|
}
|
||||||
void hang() {
|
|
||||||
for(;;);
|
|
||||||
}
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
signal_hooks();
|
|
||||||
crash(); // hang();
|
|
||||||
}
|
|
||||||
#define main main__
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// endian ----------------------------------------------------------------------
|
// endian ----------------------------------------------------------------------
|
||||||
|
@ -498,230 +490,6 @@ int app_battery() {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// time
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
uint64_t time_gpu() {
|
|
||||||
GLint64 t = 123456789;
|
|
||||||
glGetInteger64v(GL_TIMESTAMP, &t);
|
|
||||||
return (uint64_t)t;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
uint64_t date() {
|
|
||||||
time_t epoch = time(0);
|
|
||||||
struct tm *ti = localtime(&epoch);
|
|
||||||
return atoi64(va("%04d%02d%02d%02d%02d%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec));
|
|
||||||
}
|
|
||||||
char *date_string() {
|
|
||||||
time_t epoch = time(0);
|
|
||||||
struct tm *ti = localtime(&epoch);
|
|
||||||
return va("%04d-%02d-%02d %02d:%02d:%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec);
|
|
||||||
}
|
|
||||||
uint64_t date_epoch() {
|
|
||||||
time_t epoch = time(0);
|
|
||||||
return epoch;
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
double time_ss() {
|
|
||||||
return glfwGetTime();
|
|
||||||
}
|
|
||||||
double time_ms() {
|
|
||||||
return glfwGetTime() * 1000.0;
|
|
||||||
}
|
|
||||||
uint64_t time_us() {
|
|
||||||
return (uint64_t)(glfwGetTime() * 1000000.0); // @fixme: use a high resolution timer instead, or time_gpu below
|
|
||||||
}
|
|
||||||
uint64_t sleep_us(uint64_t us) { // @fixme: use a high resolution sleeper instead
|
|
||||||
return sleep_ms( us / 1000.0 );
|
|
||||||
}
|
|
||||||
double sleep_ms(double ms) {
|
|
||||||
double now = time_ms();
|
|
||||||
if( ms <= 0 ) {
|
|
||||||
#if is(win32)
|
|
||||||
Sleep(0); // yield
|
|
||||||
#else
|
|
||||||
usleep(0);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
#if is(win32)
|
|
||||||
Sleep(ms);
|
|
||||||
#else
|
|
||||||
usleep(ms * 1000);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return time_ms() - now;
|
|
||||||
}
|
|
||||||
double sleep_ss(double ss) {
|
|
||||||
return sleep_ms( ss * 1000 ) / 1000.0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// high-perf functions
|
|
||||||
|
|
||||||
#define TIMER_E3 1000ULL
|
|
||||||
#define TIMER_E6 1000000ULL
|
|
||||||
#define TIMER_E9 1000000000ULL
|
|
||||||
|
|
||||||
#ifdef CLOCK_MONOTONIC_RAW
|
|
||||||
#define TIME_MONOTONIC CLOCK_MONOTONIC_RAW
|
|
||||||
#elif defined CLOCK_MONOTONIC
|
|
||||||
#define TIME_MONOTONIC CLOCK_MONOTONIC
|
|
||||||
#else
|
|
||||||
// #define TIME_MONOTONIC CLOCK_REALTIME // untested
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static uint64_t nanotimer(uint64_t *out_freq) {
|
|
||||||
if( out_freq ) {
|
|
||||||
#if is(win32)
|
|
||||||
LARGE_INTEGER li;
|
|
||||||
QueryPerformanceFrequency(&li);
|
|
||||||
*out_freq = li.QuadPart;
|
|
||||||
//#elif is(ANDROID)
|
|
||||||
// *out_freq = CLOCKS_PER_SEC;
|
|
||||||
#elif defined TIME_MONOTONIC
|
|
||||||
*out_freq = TIMER_E9;
|
|
||||||
#else
|
|
||||||
*out_freq = TIMER_E6;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#if is(win32)
|
|
||||||
LARGE_INTEGER li;
|
|
||||||
QueryPerformanceCounter(&li);
|
|
||||||
return (uint64_t)li.QuadPart;
|
|
||||||
//#elif is(ANDROID)
|
|
||||||
// return (uint64_t)clock();
|
|
||||||
#elif defined TIME_MONOTONIC
|
|
||||||
struct timespec ts;
|
|
||||||
clock_gettime(TIME_MONOTONIC, &ts);
|
|
||||||
return (TIMER_E9 * (uint64_t)ts.tv_sec) + ts.tv_nsec;
|
|
||||||
#else
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
return (TIMER_E6 * (uint64_t)tv.tv_sec) + tv.tv_usec;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t time_ns() {
|
|
||||||
static uint64_t epoch = 0;
|
|
||||||
static uint64_t freq = 0;
|
|
||||||
if( !freq ) {
|
|
||||||
epoch = nanotimer(&freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t a = nanotimer(NULL) - epoch;
|
|
||||||
uint64_t b = TIMER_E9;
|
|
||||||
uint64_t c = freq;
|
|
||||||
|
|
||||||
// Computes (a*b)/c without overflow, as long as both (a*b) and the overall result fit into 64-bits.
|
|
||||||
// [ref] https://github.com/rust-lang/rust/blob/3809bbf47c8557bd149b3e52ceb47434ca8378d5/src/libstd/sys_common/mod.rs#L124
|
|
||||||
uint64_t q = a / c;
|
|
||||||
uint64_t r = a % c;
|
|
||||||
return q * b + r * b / c;
|
|
||||||
}
|
|
||||||
uint64_t time_us() {
|
|
||||||
return time_ns() / TIMER_E3;
|
|
||||||
}
|
|
||||||
uint64_t time_ms() {
|
|
||||||
return time_ns() / TIMER_E6;
|
|
||||||
}
|
|
||||||
double time_ss() {
|
|
||||||
return time_ns() / 1e9; // TIMER_E9;
|
|
||||||
}
|
|
||||||
double time_mm() {
|
|
||||||
return time_ss() / 60;
|
|
||||||
}
|
|
||||||
double time_hh() {
|
|
||||||
return time_mm() / 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleep_ns( double ns ) {
|
|
||||||
#if is(win32)
|
|
||||||
if( ns >= 100 ) {
|
|
||||||
LARGE_INTEGER li; // Windows sleep in 100ns units
|
|
||||||
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
|
|
||||||
li.QuadPart = (LONGLONG)(__int64)(-ns/100); // Negative for relative time
|
|
||||||
SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE);
|
|
||||||
WaitForSingleObject(timer, INFINITE);
|
|
||||||
CloseHandle(timer);
|
|
||||||
#else
|
|
||||||
if( ns > 0 ) {
|
|
||||||
struct timespec wait = {0};
|
|
||||||
wait.tv_sec = ns / 1e9;
|
|
||||||
wait.tv_nsec = ns - wait.tv_sec * 1e9;
|
|
||||||
nanosleep(&wait, NULL);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
#if is(win32)
|
|
||||||
Sleep(0); // yield, Sleep(0), SwitchToThread
|
|
||||||
#else
|
|
||||||
usleep(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void sleep_us( double us ) {
|
|
||||||
sleep_ns(us * 1e3);
|
|
||||||
}
|
|
||||||
void sleep_ms( double ms ) {
|
|
||||||
sleep_ns(ms * 1e6);
|
|
||||||
}
|
|
||||||
void sleep_ss( double ss ) {
|
|
||||||
sleep_ns(ss * 1e9);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// timer
|
|
||||||
|
|
||||||
struct timer_internal_t {
|
|
||||||
unsigned ms;
|
|
||||||
unsigned (*callback)(unsigned interval, void *arg);
|
|
||||||
void *arg;
|
|
||||||
thread_ptr_t thd;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int timer_func(void *arg) {
|
|
||||||
struct timer_internal_t *p = (struct timer_internal_t*)arg;
|
|
||||||
|
|
||||||
sleep_ms( p->ms );
|
|
||||||
|
|
||||||
for( ;; ) {
|
|
||||||
unsigned then = time_ms();
|
|
||||||
|
|
||||||
p->ms = p->callback(p->ms, p->arg);
|
|
||||||
if( !p->ms ) break;
|
|
||||||
|
|
||||||
unsigned now = time_ms();
|
|
||||||
unsigned lapse = now - then;
|
|
||||||
int diff = p->ms - lapse;
|
|
||||||
sleep_ms( diff <= 0 ? 0 : diff );
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_exit(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __thread array(struct timer_internal_t *) timers;
|
|
||||||
|
|
||||||
unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg) {
|
|
||||||
struct timer_internal_t *p = MALLOC( sizeof(struct timer_internal_t) );
|
|
||||||
p->ms = ms;
|
|
||||||
p->callback = callback;
|
|
||||||
p->arg = arg;
|
|
||||||
p->thd = thread_init( timer_func, p, "", 0 );
|
|
||||||
|
|
||||||
array_push(timers, p);
|
|
||||||
return array_count(timers);
|
|
||||||
}
|
|
||||||
void timer_destroy(unsigned i) {
|
|
||||||
if( i-- ) {
|
|
||||||
thread_join(timers[i]->thd);
|
|
||||||
thread_term(timers[i]->thd);
|
|
||||||
FREE(timers[i]);
|
|
||||||
timers[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// argc/v
|
// argc/v
|
||||||
|
|
||||||
|
@ -1071,3 +839,75 @@ void thread_destroy( void *thd ) {
|
||||||
thread_term(thd);
|
thread_term(thd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void app_hang() {
|
||||||
|
for(;;);
|
||||||
|
}
|
||||||
|
void app_crash() {
|
||||||
|
int *p = 0;
|
||||||
|
*p = 42;
|
||||||
|
}
|
||||||
|
void app_beep() {
|
||||||
|
fputc('\x7', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_singleton(const char *guid) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
do_once {
|
||||||
|
char buffer[128];
|
||||||
|
snprintf(buffer, 128, "Global\\{%s}", guid);
|
||||||
|
static HANDLE app_mutex = 0;
|
||||||
|
app_mutex = CreateMutexA(NULL, FALSE, buffer);
|
||||||
|
if( ERROR_ALREADY_EXISTS == GetLastError() ) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool app_open_folder(const char *file) {
|
||||||
|
char buf[1024];
|
||||||
|
#ifdef _WIN32
|
||||||
|
snprintf(buf, sizeof(buf), "start \"\" \"%s\"", file);
|
||||||
|
#elif __APPLE__
|
||||||
|
snprintf(buf, sizeof(buf), "%s \"%s\"", is_directory(file) ? "open" : "open --reveal", file);
|
||||||
|
#else
|
||||||
|
snprintf(buf, sizeof(buf), "xdg-open \"%s\"", file);
|
||||||
|
#endif
|
||||||
|
return system(buf) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool app_open_file(const char *file) {
|
||||||
|
char buf[1024];
|
||||||
|
#ifdef _WIN32
|
||||||
|
snprintf(buf, sizeof(buf), "start \"\" \"%s\"", file);
|
||||||
|
#elif __APPLE__
|
||||||
|
snprintf(buf, sizeof(buf), "open \"%s\"", file);
|
||||||
|
#else
|
||||||
|
snprintf(buf, sizeof(buf), "xdg-open \"%s\"", file);
|
||||||
|
#endif
|
||||||
|
return system(buf) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool app_open_url(const char *url) {
|
||||||
|
return app_open_file(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool app_open(const char *link) {
|
||||||
|
if( file_directory(link) ) return app_open_folder(link);
|
||||||
|
if( file_exist(link) ) return app_open_file(link);
|
||||||
|
return app_open_url(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// tests
|
||||||
|
|
||||||
|
static __thread int test_oks, test_errs, test_once;
|
||||||
|
static void test_exit(void) { printf("%d/%d tests passed\n", test_oks, test_oks+test_errs); }
|
||||||
|
int (test)(const char *file, int line, const char *expr, bool result) {
|
||||||
|
test_once = test_once || !(atexit)(test_exit);
|
||||||
|
test_oks += result, test_errs += !result;
|
||||||
|
return fputc("F."[result], stdout) && (result || fprintf(stderr, "(%s %s:%d)", expr, file, line) );
|
||||||
|
}
|
||||||
|
|
|
@ -12,19 +12,19 @@ API int argc();
|
||||||
API char* argv(int);
|
API char* argv(int);
|
||||||
|
|
||||||
API int flag(const char *commalist); // --arg // app_flag?
|
API int flag(const char *commalist); // --arg // app_flag?
|
||||||
API const char* option(const char *commalist, const char *defaults); // --arg=value or --arg value
|
API const char* option(const char *commalist, const char *defaults); // --arg=string or --arg string
|
||||||
API int optioni(const char *commalist, int defaults); // argvi() ?
|
API int optioni(const char *commalist, int defaults); // --arg=integer or --arg integer // argvi() ?
|
||||||
API float optionf(const char *commalist, float defaults); // app_option?
|
API float optionf(const char *commalist, float defaults); // --arg=float or --arg float // flagf() ?
|
||||||
|
|
||||||
API void tty_color(unsigned color);
|
|
||||||
API void tty_reset();
|
|
||||||
API void tty_attach();
|
API void tty_attach();
|
||||||
API void tty_detach();
|
API void tty_detach();
|
||||||
|
API void tty_color(unsigned color);
|
||||||
|
API void tty_reset();
|
||||||
|
|
||||||
API const char* app_exec(const char *command); // returns ("%15d %s", retcode, output_last_line)
|
API const char* app_exec(const char *command); // returns ("%15d %s", retcode, output_last_line)
|
||||||
API int app_spawn(const char *command);
|
API int app_spawn(const char *command);
|
||||||
API int app_cores();
|
API int app_cores();
|
||||||
API int app_battery(); /// return battery level [1..100]. also positive if charging (+), negative if discharging (-), and 0 if no battery is present.
|
API int app_battery(); /// returns battery level [1..100]. also positive if charging (+), negative if discharging (-), and 0 if no battery is present.
|
||||||
|
|
||||||
API const char* app_name();
|
API const char* app_name();
|
||||||
API const char* app_path();
|
API const char* app_path();
|
||||||
|
@ -32,22 +32,11 @@ API const char* app_cache();
|
||||||
API const char* app_temp();
|
API const char* app_temp();
|
||||||
API const char* app_cmdline();
|
API const char* app_cmdline();
|
||||||
|
|
||||||
API uint64_t date(); // YYYYMMDDhhmmss
|
API void app_beep();
|
||||||
API uint64_t date_epoch(); // linux epoch
|
API void app_hang();
|
||||||
API char* date_string(); // "YYYY-MM-DD hh:mm:ss"
|
API void app_crash();
|
||||||
API double time_hh();
|
API void app_singleton(const char *guid);
|
||||||
API double time_mm();
|
API bool app_open(const char *folder_file_or_url);
|
||||||
API double time_ss();
|
|
||||||
API uint64_t time_ms();
|
|
||||||
API uint64_t time_us();
|
|
||||||
API uint64_t time_ns();
|
|
||||||
API void sleep_ss(double ss);
|
|
||||||
API void sleep_ms(double ms);
|
|
||||||
API void sleep_us(double us);
|
|
||||||
API void sleep_ns(double us);
|
|
||||||
|
|
||||||
API unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
|
|
||||||
API void timer_destroy(unsigned timer_handle);
|
|
||||||
|
|
||||||
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
|
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
|
||||||
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.
|
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.
|
||||||
|
@ -59,12 +48,12 @@ API void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
|
||||||
API void breakpoint(const char *optional_reason);
|
API void breakpoint(const char *optional_reason);
|
||||||
API bool has_debugger();
|
API bool has_debugger();
|
||||||
|
|
||||||
API void signal_hooks(void);
|
API void trap_install(void);
|
||||||
API void signal_handler_ignore(int signal);
|
API const char *trap_name(int signal); // helper util
|
||||||
API void signal_handler_quit(int signal);
|
API void trap_on_ignore(int signal); // helper util
|
||||||
API void signal_handler_abort(int signal);
|
API void trap_on_quit(int signal); // helper util
|
||||||
API void signal_handler_debug(int signal);
|
API void trap_on_abort(int signal); // helper util
|
||||||
API const char *signal_name(int signal); // helper util
|
API void trap_on_debug(int signal); // helper util
|
||||||
|
|
||||||
API uint16_t lil16(uint16_t n); // swap16 as lil
|
API uint16_t lil16(uint16_t n); // swap16 as lil
|
||||||
API uint32_t lil32(uint32_t n); // swap32 as lil
|
API uint32_t lil32(uint32_t n); // swap32 as lil
|
||||||
|
@ -93,3 +82,7 @@ API int (PANIC)(const char *error, const char *file, int line);
|
||||||
|
|
||||||
#define PRINTF(...) PRINTF(va(__VA_ARGS__), 1[#__VA_ARGS__] == '!' ? callstack(+48) : "", __FILE__, __LINE__, __FUNCTION__)
|
#define PRINTF(...) PRINTF(va(__VA_ARGS__), 1[#__VA_ARGS__] == '!' ? callstack(+48) : "", __FILE__, __LINE__, __FUNCTION__)
|
||||||
API int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
|
API int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
|
||||||
|
|
||||||
|
#define test(expr) test(__FILE__,__LINE__,#expr,!!(expr))
|
||||||
|
API int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
|
// AUTORUN { test(1<2); }
|
||||||
|
|
|
@ -0,0 +1,248 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// time
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
uint64_t time_gpu() {
|
||||||
|
GLint64 t = 123456789;
|
||||||
|
glGetInteger64v(GL_TIMESTAMP, &t);
|
||||||
|
return (uint64_t)t;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
uint64_t date() {
|
||||||
|
time_t epoch = time(0);
|
||||||
|
struct tm *ti = localtime(&epoch);
|
||||||
|
return atoi64(va("%04d%02d%02d%02d%02d%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec));
|
||||||
|
}
|
||||||
|
char *date_string() {
|
||||||
|
time_t epoch = time(0);
|
||||||
|
struct tm *ti = localtime(&epoch);
|
||||||
|
return va("%04d-%02d-%02d %02d:%02d:%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec);
|
||||||
|
}
|
||||||
|
uint64_t date_epoch() {
|
||||||
|
time_t epoch = time(0);
|
||||||
|
return epoch;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
double time_ss() {
|
||||||
|
return glfwGetTime();
|
||||||
|
}
|
||||||
|
double time_ms() {
|
||||||
|
return glfwGetTime() * 1000.0;
|
||||||
|
}
|
||||||
|
uint64_t time_us() {
|
||||||
|
return (uint64_t)(glfwGetTime() * 1000000.0); // @fixme: use a high resolution timer instead, or time_gpu below
|
||||||
|
}
|
||||||
|
uint64_t sleep_us(uint64_t us) { // @fixme: use a high resolution sleeper instead
|
||||||
|
return sleep_ms( us / 1000.0 );
|
||||||
|
}
|
||||||
|
double sleep_ms(double ms) {
|
||||||
|
double now = time_ms();
|
||||||
|
if( ms <= 0 ) {
|
||||||
|
#if is(win32)
|
||||||
|
Sleep(0); // yield
|
||||||
|
#else
|
||||||
|
usleep(0);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
#if is(win32)
|
||||||
|
Sleep(ms);
|
||||||
|
#else
|
||||||
|
usleep(ms * 1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return time_ms() - now;
|
||||||
|
}
|
||||||
|
double sleep_ss(double ss) {
|
||||||
|
return sleep_ms( ss * 1000 ) / 1000.0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// high-perf functions
|
||||||
|
|
||||||
|
#define TIMER_E3 1000ULL
|
||||||
|
#define TIMER_E6 1000000ULL
|
||||||
|
#define TIMER_E9 1000000000ULL
|
||||||
|
|
||||||
|
#ifdef CLOCK_MONOTONIC_RAW
|
||||||
|
#define TIME_MONOTONIC CLOCK_MONOTONIC_RAW
|
||||||
|
#elif defined CLOCK_MONOTONIC
|
||||||
|
#define TIME_MONOTONIC CLOCK_MONOTONIC
|
||||||
|
#else
|
||||||
|
// #define TIME_MONOTONIC CLOCK_REALTIME // untested
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint64_t nanotimer(uint64_t *out_freq) {
|
||||||
|
if( out_freq ) {
|
||||||
|
#if is(win32)
|
||||||
|
LARGE_INTEGER li;
|
||||||
|
QueryPerformanceFrequency(&li);
|
||||||
|
*out_freq = li.QuadPart;
|
||||||
|
//#elif is(ANDROID)
|
||||||
|
// *out_freq = CLOCKS_PER_SEC;
|
||||||
|
#elif defined TIME_MONOTONIC
|
||||||
|
*out_freq = TIMER_E9;
|
||||||
|
#else
|
||||||
|
*out_freq = TIMER_E6;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if is(win32)
|
||||||
|
LARGE_INTEGER li;
|
||||||
|
QueryPerformanceCounter(&li);
|
||||||
|
return (uint64_t)li.QuadPart;
|
||||||
|
//#elif is(ANDROID)
|
||||||
|
// return (uint64_t)clock();
|
||||||
|
#elif defined TIME_MONOTONIC
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(TIME_MONOTONIC, &ts);
|
||||||
|
return (TIMER_E9 * (uint64_t)ts.tv_sec) + ts.tv_nsec;
|
||||||
|
#else
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return (TIMER_E6 * (uint64_t)tv.tv_sec) + tv.tv_usec;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t time_ns() {
|
||||||
|
static uint64_t epoch = 0;
|
||||||
|
static uint64_t freq = 0;
|
||||||
|
if( !freq ) {
|
||||||
|
epoch = nanotimer(&freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t a = nanotimer(NULL) - epoch;
|
||||||
|
uint64_t b = TIMER_E9;
|
||||||
|
uint64_t c = freq;
|
||||||
|
|
||||||
|
// Computes (a*b)/c without overflow, as long as both (a*b) and the overall result fit into 64-bits.
|
||||||
|
// [ref] https://github.com/rust-lang/rust/blob/3809bbf47c8557bd149b3e52ceb47434ca8378d5/src/libstd/sys_common/mod.rs#L124
|
||||||
|
uint64_t q = a / c;
|
||||||
|
uint64_t r = a % c;
|
||||||
|
return q * b + r * b / c;
|
||||||
|
}
|
||||||
|
uint64_t time_us() {
|
||||||
|
return time_ns() / TIMER_E3;
|
||||||
|
}
|
||||||
|
uint64_t time_ms() {
|
||||||
|
return time_ns() / TIMER_E6;
|
||||||
|
}
|
||||||
|
double time_ss() {
|
||||||
|
return time_ns() / 1e9; // TIMER_E9;
|
||||||
|
}
|
||||||
|
double time_mm() {
|
||||||
|
return time_ss() / 60;
|
||||||
|
}
|
||||||
|
double time_hh() {
|
||||||
|
return time_mm() / 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleep_ns( double ns ) {
|
||||||
|
#if is(win32)
|
||||||
|
if( ns >= 100 ) {
|
||||||
|
LARGE_INTEGER li; // Windows sleep in 100ns units
|
||||||
|
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||||
|
li.QuadPart = (LONGLONG)(__int64)(-ns/100); // Negative for relative time
|
||||||
|
SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE);
|
||||||
|
WaitForSingleObject(timer, INFINITE);
|
||||||
|
CloseHandle(timer);
|
||||||
|
#else
|
||||||
|
if( ns > 0 ) {
|
||||||
|
struct timespec wait = {0};
|
||||||
|
wait.tv_sec = ns / 1e9;
|
||||||
|
wait.tv_nsec = ns - wait.tv_sec * 1e9;
|
||||||
|
nanosleep(&wait, NULL);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
#if is(win32)
|
||||||
|
Sleep(0); // yield, Sleep(0), SwitchToThread
|
||||||
|
#else
|
||||||
|
usleep(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void sleep_us( double us ) {
|
||||||
|
sleep_ns(us * 1e3);
|
||||||
|
}
|
||||||
|
void sleep_ms( double ms ) {
|
||||||
|
sleep_ns(ms * 1e6);
|
||||||
|
}
|
||||||
|
void sleep_ss( double ss ) {
|
||||||
|
sleep_ns(ss * 1e9);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// timer
|
||||||
|
|
||||||
|
struct timer_internal_t {
|
||||||
|
unsigned ms;
|
||||||
|
unsigned (*callback)(unsigned interval, void *arg);
|
||||||
|
void *arg;
|
||||||
|
thread_ptr_t thd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int timer_func(void *arg) {
|
||||||
|
struct timer_internal_t *p = (struct timer_internal_t*)arg;
|
||||||
|
|
||||||
|
sleep_ms( p->ms );
|
||||||
|
|
||||||
|
for( ;; ) {
|
||||||
|
unsigned then = time_ms();
|
||||||
|
|
||||||
|
p->ms = p->callback(p->ms, p->arg);
|
||||||
|
if( !p->ms ) break;
|
||||||
|
|
||||||
|
unsigned now = time_ms();
|
||||||
|
unsigned lapse = now - then;
|
||||||
|
int diff = p->ms - lapse;
|
||||||
|
sleep_ms( diff <= 0 ? 0 : diff );
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_exit(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __thread array(struct timer_internal_t *) timers;
|
||||||
|
|
||||||
|
unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg) {
|
||||||
|
struct timer_internal_t *p = MALLOC( sizeof(struct timer_internal_t) );
|
||||||
|
p->ms = ms;
|
||||||
|
p->callback = callback;
|
||||||
|
p->arg = arg;
|
||||||
|
p->thd = thread_init( timer_func, p, "", 0 );
|
||||||
|
|
||||||
|
array_push(timers, p);
|
||||||
|
return array_count(timers);
|
||||||
|
}
|
||||||
|
void timer_destroy(unsigned i) {
|
||||||
|
if( i-- ) {
|
||||||
|
thread_join(timers[i]->thd);
|
||||||
|
thread_term(timers[i]->thd);
|
||||||
|
FREE(timers[i]);
|
||||||
|
timers[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// guid
|
||||||
|
|
||||||
|
//typedef vec3i guid;
|
||||||
|
|
||||||
|
guid guid_create() {
|
||||||
|
static __thread unsigned counter = 0;
|
||||||
|
static uint64_t appid = 0; do_once appid = hash_str(app_name());
|
||||||
|
|
||||||
|
union conv {
|
||||||
|
struct {
|
||||||
|
unsigned timestamp : 32;
|
||||||
|
unsigned threadid : 16; // inverted order in LE
|
||||||
|
unsigned appid : 16; //
|
||||||
|
unsigned counter : 32;
|
||||||
|
};
|
||||||
|
vec3i v3;
|
||||||
|
} c;
|
||||||
|
c.timestamp = date_epoch() - 0x65000000;
|
||||||
|
c.appid = (unsigned)appid;
|
||||||
|
c.threadid = (unsigned)(uintptr_t)thread_current_thread_id();
|
||||||
|
c.counter = ++counter;
|
||||||
|
|
||||||
|
return c.v3;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// time framework utils
|
||||||
|
// - rlyeh, public domain.
|
||||||
|
|
||||||
|
API uint64_t date(); // YYYYMMDDhhmmss
|
||||||
|
API uint64_t date_epoch(); // linux epoch
|
||||||
|
API char* date_string(); // "YYYY-MM-DD hh:mm:ss"
|
||||||
|
API double time_hh();
|
||||||
|
API double time_mm();
|
||||||
|
API double time_ss();
|
||||||
|
API uint64_t time_ms();
|
||||||
|
API uint64_t time_us();
|
||||||
|
API uint64_t time_ns();
|
||||||
|
API void sleep_ss(double ss);
|
||||||
|
API void sleep_ms(double ms);
|
||||||
|
API void sleep_us(double us);
|
||||||
|
API void sleep_ns(double us);
|
||||||
|
|
||||||
|
API unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
|
||||||
|
API void timer_destroy(unsigned timer_handle);
|
||||||
|
|
||||||
|
// time sortable unique identifier (similar to ksuid/tuid; others: sno/xid/cuid/ulid)
|
||||||
|
// - rlyeh, public domain.
|
||||||
|
//
|
||||||
|
// also similar to a mongo object id, 12 bytes as follows:
|
||||||
|
// - 4-byte timestamp (ss). epoch: Tuesday, 12 September 2023 6:06:56
|
||||||
|
// - 2-byte (machine or app hash)
|
||||||
|
// - 2-byte (thread-id)
|
||||||
|
// - 4-byte (rand counter, that gets increased at every id creation)
|
||||||
|
|
||||||
|
typedef vec3i guid;
|
||||||
|
|
||||||
|
API guid guid_create();
|
||||||
|
|
||||||
|
/*
|
||||||
|
AUTORUN {
|
||||||
|
guid g1 = guid_create();
|
||||||
|
guid g2 = guid_create();
|
||||||
|
print3i(g1);
|
||||||
|
hexdump(&g1, sizeof(g1));
|
||||||
|
print3i(g2);
|
||||||
|
hexdump(&g2, sizeof(g2));
|
||||||
|
}
|
||||||
|
*/
|
|
@ -194,6 +194,7 @@ table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = hover_hue; // nk_rgba(255, 0, 0, 255);
|
||||||
s->window.scrollbar_size = nk_vec2(5,5);
|
s->window.scrollbar_size = nk_vec2(5,5);
|
||||||
s->property.rounding = 0;
|
s->property.rounding = 0;
|
||||||
s->combo.border = 0;
|
s->combo.border = 0;
|
||||||
|
s->combo.button_padding.x = -18;
|
||||||
s->button.border = 1;
|
s->button.border = 1;
|
||||||
s->edit.border = 0;
|
s->edit.border = 0;
|
||||||
|
|
||||||
|
@ -1905,11 +1906,16 @@ int ui_clampf(const char *label, float *v, float minf, float maxf) {
|
||||||
return prev != v[0];
|
return prev != v[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ui_float_sign = 0;
|
||||||
|
|
||||||
int ui_float2(const char *label, float *v) {
|
int ui_float2(const char *label, float *v) {
|
||||||
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
||||||
ui_label_(label, NK_TEXT_LEFT);
|
ui_label_(label, NK_TEXT_LEFT);
|
||||||
|
|
||||||
char *buffer = va("%.2f, %.2f", v[0], v[1]);
|
char *buffer = ui_float_sign ?
|
||||||
|
--ui_float_sign, va("%+.3f %+.3f", v[0], v[1]) :
|
||||||
|
va("%.3f, %.3f", v[0], v[1]);
|
||||||
|
|
||||||
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
||||||
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
||||||
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
||||||
|
@ -1924,7 +1930,10 @@ int ui_float3(const char *label, float *v) {
|
||||||
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
||||||
ui_label_(label, NK_TEXT_LEFT);
|
ui_label_(label, NK_TEXT_LEFT);
|
||||||
|
|
||||||
char *buffer = va("%.2f, %.2f, %.2f", v[0], v[1], v[2]);
|
char *buffer = ui_float_sign ?
|
||||||
|
--ui_float_sign, va("%+.2f %+.2f %+.2f", v[0], v[1], v[2]) :
|
||||||
|
va("%.2f, %.2f, %.2f", v[0], v[1], v[2]);
|
||||||
|
|
||||||
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
||||||
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
||||||
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
||||||
|
@ -1940,7 +1949,10 @@ int ui_float4(const char *label, float *v) {
|
||||||
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
||||||
ui_label_(label, NK_TEXT_LEFT);
|
ui_label_(label, NK_TEXT_LEFT);
|
||||||
|
|
||||||
char *buffer = va("%.2f, %.2f, %.2f, %.2f", v[0], v[1], v[2], v[3]);
|
char *buffer = ui_float_sign ?
|
||||||
|
--ui_float_sign, va("%+.2f %+.2f %+.2f %+.2f", v[0], v[1], v[2], v[3]) :
|
||||||
|
va("%.2f,%.2f,%.2f,%.2f", v[0], v[1], v[2], v[3]);
|
||||||
|
|
||||||
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
||||||
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
||||||
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
||||||
|
@ -1950,10 +1962,12 @@ int ui_float4(const char *label, float *v) {
|
||||||
nk_combo_end(ui_ctx);
|
nk_combo_end(ui_ctx);
|
||||||
return prev0 != v[0] || prev1 != v[1] || prev2 != v[2] || prev3 != v[3];
|
return prev0 != v[0] || prev1 != v[1] || prev2 != v[2] || prev3 != v[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ui_mat33(const char *label, float M[9]) {
|
int ui_mat33(const char *label, float M[9]) {
|
||||||
|
ui_float_sign = 3;
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
changed |= ui_label(label);
|
changed |= ui_label(label);
|
||||||
changed |= ui_float3(NULL, M);
|
changed |= ui_float3(NULL, M);
|
||||||
|
@ -1962,6 +1976,7 @@ int ui_mat33(const char *label, float M[9]) {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
int ui_mat34(const char *label, float M[12]) {
|
int ui_mat34(const char *label, float M[12]) {
|
||||||
|
ui_float_sign = 3;
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
changed |= ui_label(label);
|
changed |= ui_label(label);
|
||||||
changed |= ui_float4(NULL, M);
|
changed |= ui_float4(NULL, M);
|
||||||
|
@ -1970,6 +1985,7 @@ int ui_mat34(const char *label, float M[12]) {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
int ui_mat44(const char *label, float M[16]) {
|
int ui_mat44(const char *label, float M[16]) {
|
||||||
|
ui_float_sign = 4;
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
changed |= ui_label(label);
|
changed |= ui_label(label);
|
||||||
changed |= ui_float4(NULL, M);
|
changed |= ui_float4(NULL, M);
|
||||||
|
|
|
@ -375,6 +375,7 @@ bool window_create_from_handle(void *handle, float scale, unsigned flags) {
|
||||||
glfwSwapInterval(interval);
|
glfwSwapInterval(interval);
|
||||||
|
|
||||||
const GLFWvidmode *mode = glfwGetVideoMode(monitor ? monitor : glfwGetPrimaryMonitor());
|
const GLFWvidmode *mode = glfwGetVideoMode(monitor ? monitor : glfwGetPrimaryMonitor());
|
||||||
|
PRINTF("Build version: %s\n", BUILD_VERSION);
|
||||||
PRINTF("Monitor: %s (%dHz, vsync=%d)\n", glfwGetMonitorName(monitor ? monitor : glfwGetPrimaryMonitor()), mode->refreshRate, interval);
|
PRINTF("Monitor: %s (%dHz, vsync=%d)\n", glfwGetMonitorName(monitor ? monitor : glfwGetPrimaryMonitor()), mode->refreshRate, interval);
|
||||||
PRINTF("GPU device: %s\n", glGetString(GL_RENDERER));
|
PRINTF("GPU device: %s\n", glGetString(GL_RENDERER));
|
||||||
PRINTF("GPU driver: %s\n", glGetString(GL_VERSION));
|
PRINTF("GPU driver: %s\n", glGetString(GL_VERSION));
|
||||||
|
@ -482,7 +483,11 @@ char* window_stats() {
|
||||||
|
|
||||||
// @todo: print %used/%avail kib mem, %used/%avail objs as well
|
// @todo: print %used/%avail kib mem, %used/%avail objs as well
|
||||||
static char buf[256];
|
static char buf[256];
|
||||||
snprintf(buf, 256, "%s | boot %.2fs | %5.2ffps (%.2fms)%s%s", title, !boot_time ? now : boot_time, fps, (now - prev_frame) * 1000.f, cmdline[0] ? " | ":"", cmdline[0] ? cmdline:"");
|
snprintf(buf, 256, "%s%s%s%s | boot %.2fs | %5.2ffps (%.2fms)%s%s",
|
||||||
|
title, BUILD_VERSION[0] ? " (":"", BUILD_VERSION[0] ? BUILD_VERSION:"", BUILD_VERSION[0] ? ")":"",
|
||||||
|
!boot_time ? now : boot_time,
|
||||||
|
fps, (now - prev_frame) * 1000.f,
|
||||||
|
cmdline[0] ? " | ":"", cmdline[0] ? cmdline:"");
|
||||||
|
|
||||||
prev_frame = now;
|
prev_frame = now;
|
||||||
++num_frames;
|
++num_frames;
|
||||||
|
|
|
@ -11688,7 +11688,7 @@ int gladLoadGL( GLADloadfunc load) {
|
||||||
|
|
||||||
#endif /* __EMSCRIPTEN__ */
|
#endif /* __EMSCRIPTEN__ */
|
||||||
#line 0
|
#line 0
|
||||||
#line 1 "3rd_font_md.h"
|
#line 1 "3rd_icon_md.h"
|
||||||
// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++
|
// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++
|
||||||
// from https://github.com/google/material-design-icons/raw/master/font/MaterialIcons-Regular.codepoints
|
// from https://github.com/google/material-design-icons/raw/master/font/MaterialIcons-Regular.codepoints
|
||||||
// for use with https://github.com/google/material-design-icons/blob/master/font/MaterialIcons-Regular.ttf
|
// for use with https://github.com/google/material-design-icons/blob/master/font/MaterialIcons-Regular.ttf
|
||||||
|
@ -218881,7 +218881,7 @@ nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len,
|
||||||
else
|
else
|
||||||
label.w = header.w - 2 * style->combo.content_padding.x;
|
label.w = header.w - 2 * style->combo.content_padding.x;
|
||||||
nk_widget_text(&win->buffer, label, selected, len, &text,
|
nk_widget_text(&win->buffer, label, selected, len, &text,
|
||||||
NK_TEXT_LEFT, ctx->style.font);
|
NK_TEXT_CENTERED, ctx->style.font); //< @r-lyeh NK_TEXT_LEFT>CENTERED
|
||||||
|
|
||||||
/* draw open/close button */
|
/* draw open/close button */
|
||||||
if (draw_button_symbol)
|
if (draw_button_symbol)
|
||||||
|
@ -562506,4 +562506,5 @@ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
|
||||||
#undef Token
|
#undef Token
|
||||||
#undef Table
|
#undef Table
|
||||||
#undef rehash
|
#undef rehash
|
||||||
|
#undef NB
|
||||||
#endif // V4K_3RD
|
#endif // V4K_3RD
|
||||||
|
|
1278
engine/v4k.c
1278
engine/v4k.c
File diff suppressed because it is too large
Load Diff
384
engine/v4k.h
384
engine/v4k.h
|
@ -219,7 +219,7 @@ extern "C" {
|
||||||
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
|
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
|
||||||
|
|
||||||
#define concat(a,b) conc4t(a,b)
|
#define concat(a,b) conc4t(a,b)
|
||||||
#define conc4t(a,b) a##b
|
#define conc4t(a,b) a##b ///-
|
||||||
|
|
||||||
#define macro(name) concat(name, __LINE__)
|
#define macro(name) concat(name, __LINE__)
|
||||||
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
|
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
|
||||||
|
@ -247,16 +247,16 @@ extern "C" {
|
||||||
|
|
||||||
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
|
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
|
||||||
#define STRINGIZE(x) STRINGIZ3(x)
|
#define STRINGIZE(x) STRINGIZ3(x)
|
||||||
#define STRINGIZ3(x) #x
|
#define STRINGIZ3(x) #x ///-
|
||||||
|
|
||||||
#define EXPAND(name, ...) EXPAND_QUOTE(EXPAND_JOIN(name, EXPAND_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
|
#define EXPAND(name, ...) EXPAND_QUOTE(EXPAND_JOIN(name, EXPAND_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
|
||||||
#define EXPAND_QUOTE(x, y) x y
|
#define EXPAND_QUOTE(x, y) x y ///-
|
||||||
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count)
|
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count) ///-
|
||||||
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count)
|
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count) ///-
|
||||||
#define EXPAND_J01N(name, count) name##count
|
#define EXPAND_J01N(name, count) name##count ///-
|
||||||
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
|
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) ///-
|
||||||
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args
|
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args ///-
|
||||||
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count
|
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count ///-
|
||||||
|
|
||||||
#if is(cl) && !is(cpp)
|
#if is(cl) && !is(cpp)
|
||||||
#define INLINE __inline
|
#define INLINE __inline
|
||||||
|
@ -280,10 +280,13 @@ extern "C" {
|
||||||
|
|
||||||
// usage: #define vec2(...) C_CAST(vec2, __VA_ARGS__)
|
// usage: #define vec2(...) C_CAST(vec2, __VA_ARGS__)
|
||||||
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
|
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
|
||||||
#if is(cpp)
|
#define C_CAST(type, ...) ( ifdef(c,(type),type) { __VA_ARGS__ } )
|
||||||
#define C_CAST(type, ...) ( type { __VA_ARGS__ } )
|
|
||||||
#else
|
// -----------------------------------------------------------------------------
|
||||||
#define C_CAST(type, ...) ((type){ __VA_ARGS__ } )
|
// build info
|
||||||
|
|
||||||
|
#ifndef BUILD_VERSION
|
||||||
|
#define BUILD_VERSION ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -291,15 +294,9 @@ extern "C" {
|
||||||
|
|
||||||
// win32 users would need to -DAPI=IMPORT/EXPORT as needed when using/building V4K as DLL.
|
// win32 users would need to -DAPI=IMPORT/EXPORT as needed when using/building V4K as DLL.
|
||||||
|
|
||||||
#if is(win32)
|
#define IMPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport)))
|
||||||
#define IMPORT ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport))
|
#define EXPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport)))
|
||||||
#define EXPORT ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport))
|
|
||||||
#define STATIC
|
#define STATIC
|
||||||
#else
|
|
||||||
#define IMPORT
|
|
||||||
#define EXPORT
|
|
||||||
#define STATIC
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef API
|
#ifndef API
|
||||||
#define API STATIC
|
#define API STATIC
|
||||||
|
@ -822,16 +819,32 @@ API bool (map_sort)(map* m);
|
||||||
API void (map_clear)(map* m);
|
API void (map_clear)(map* m);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// four-cc, eight-cc
|
// compile-time fourcc, eightcc
|
||||||
|
|
||||||
API unsigned cc4(const char *id);
|
|
||||||
API uint64_t cc8(const char *id);
|
|
||||||
API char *cc4str(unsigned cc);
|
API char *cc4str(unsigned cc);
|
||||||
API char *cc8str(uint64_t cc);
|
API char *cc8str(uint64_t cc);
|
||||||
|
|
||||||
// fast path
|
enum {
|
||||||
#define cc4(abcd) ( *(unsigned*) #abcd " " ) // lil32() ?
|
# define _(a,b,c,d,e) cc_##a, cc_##b, cc_##c, cc_##d, cc_##e
|
||||||
#define cc8(abcdefgh) ( *(uint64_t*) #abcdefgh " " ) // lil64() ?
|
cc_1 = '1', _(2,3,4,5,6),_(7,8,9,0,_), cc_ = ' ',
|
||||||
|
cc_A = 'A', _(B,C,D,E,F),_(G,H,I,J,K),_(L,M,N,O,P),_(Q,R,S,T,U),_(V,W,X,Y,Z),
|
||||||
|
cc_a = 'a', _(b,c,d,e,f),_(g,h,i,j,k),_(l,m,n,o,p),_(q,r,s,t,u),_(v,w,x,y,z),
|
||||||
|
# undef _
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef BIG
|
||||||
|
#define cc4(a,b,c,d) ((uint32_t)(cc_##a<<24) | (cc_##b<<16) | (cc_##c<<8) | (cc_##d<<0))
|
||||||
|
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(a,b,c,d) << 32ULL) | cc4(e,f,g,h))
|
||||||
|
#else
|
||||||
|
#define cc4(a,b,c,d) ((uint32_t)(cc_##d<<24) | (cc_##c<<16) | (cc_##b<<8) | (cc_##a<<0))
|
||||||
|
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(e,f,g,h) << 32ULL) | cc4(a,b,c,d))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define cc3(a,b,c) cc4(,a,b,c)
|
||||||
|
#define cc5(a,b,c,d,e) cc6(,a,b,c,d,e)
|
||||||
|
#define cc6(a,b,c,d,e,f) cc7(,a,b,c,d,e,f)
|
||||||
|
#define cc7(a,b,c,d,e,f,g) cc8(,a,b,c,d,e,f,g)
|
||||||
|
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "v4k_math.h"
|
#line 1 "v4k_math.h"
|
||||||
|
@ -1167,6 +1180,8 @@ API bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// debugging and utils
|
// debugging and utils
|
||||||
|
|
||||||
|
API void print2i( vec2i v );
|
||||||
|
API void print3i( vec3i v );
|
||||||
API void print2( vec2 v );
|
API void print2( vec2 v );
|
||||||
API void print3( vec3 v );
|
API void print3( vec3 v );
|
||||||
API void print4( vec4 v );
|
API void print4( vec4 v );
|
||||||
|
@ -2299,136 +2314,100 @@ API int64_t client_join(const char *ip, int port);
|
||||||
|
|
||||||
#line 1 "v4k_obj.h"
|
#line 1 "v4k_obj.h"
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// C object framework (constructors/destructors, methods, rtti, refcounting)
|
// semantic versioning in a single byte (octal)
|
||||||
// - rlyeh, public domain.
|
// - rlyeh, public domain.
|
||||||
//
|
//
|
||||||
// ## object api (low level)
|
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||||
|
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||||
|
// - comparison checks only major.minor tuple as per convention.
|
||||||
|
|
||||||
|
API int semver( int major, int minor, int patch );
|
||||||
|
API int semvercmp( int v1, int v2 );
|
||||||
|
|
||||||
|
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
|
||||||
|
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
|
||||||
|
#define SEMVERFMT "%03o"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// autorun initializers for C
|
||||||
|
// - rlyeh, public domain
|
||||||
//
|
//
|
||||||
// - [ ] make object from reflected type (factory)
|
// note: based on code by Joe Lowe (public domain).
|
||||||
// - [x] make object (if debug, include callstack as well)
|
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
|
||||||
// - [x] ctor method (optional, ref to constructor)
|
|
||||||
// - [x] dtor method (optional, ref to deleter)
|
|
||||||
// - [x] zero mem object
|
|
||||||
// - [x] object logger
|
|
||||||
// - [ ] iterate members in a struct
|
|
||||||
//
|
|
||||||
// - [x] clone/copy/mutate classes
|
|
||||||
// - [x] load/save objects from/to memory/disk
|
|
||||||
// - [ ] diff/patch objects
|
|
||||||
// - [ ] experimental: support for AoSoA layout (using objcnt, 3bits)
|
|
||||||
//
|
|
||||||
// ## object decomposition
|
|
||||||
//
|
|
||||||
// <---------|--------->
|
|
||||||
// OBJ-SHADOW (64-bits) | OBJ CONTENT (N bytes)
|
|
||||||
// +-----+-----+-------------+-----------+-----+-----+-----+-----+--
|
|
||||||
// |TYPE |REFS.| OBJ NAME | obj cnt | ... | ... | ... | ... | .
|
|
||||||
// +-----+-----+-------------+-----------+-----+-----+-----+-----+--
|
|
||||||
// \-16-bits--/\---45-bits--/\--3-bits--/\-------N-bytes-----------
|
|
||||||
//
|
|
||||||
// OBJ TYPE+NAME format:
|
|
||||||
// - [type] custom tags at 0x0
|
|
||||||
// - [1..N] name
|
|
||||||
// - [\n] blank separator
|
|
||||||
// - [comments, logger, infos, etc] << obj_printf();
|
|
||||||
//
|
|
||||||
// ## object limitations
|
|
||||||
// - 256 classes max
|
|
||||||
// - 256 references max
|
|
||||||
// - 8-byte overhead per object
|
|
||||||
// - 2 total allocs per object (could be flattened into 1 with some more work)
|
|
||||||
//
|
|
||||||
// @todo: obj_extend( "class_src", "class_dst" ); call[super(obj)]()
|
|
||||||
// @todo: preferred load/save format: [ver:1,user:2,type:1] ([eof|size:7/15/23/31][blob:N])+ [crc:1/2/3/4]
|
|
||||||
// @todo: more serious loading/saving spec
|
|
||||||
|
|
||||||
// object api (heap+rtti)
|
#ifdef __cplusplus
|
||||||
|
#define AUTORUN \
|
||||||
|
static void AUTORUN_U(f)(void); \
|
||||||
|
static const int AUTORUN_J(AUTORUN_U(f),__1) = (AUTORUN_U(f)(), 1); \
|
||||||
|
static void AUTORUN_U(f)(void)
|
||||||
|
#elif _MSC_VER
|
||||||
|
#define AUTORUN \
|
||||||
|
static void AUTORUN_U(f)(void); \
|
||||||
|
static int AUTORUN_J(AUTORUN_U(f),__1) (){ AUTORUN_U(f)(); return 0; } \
|
||||||
|
__pragma(section(".CRT$XIU", long, read)) \
|
||||||
|
__declspec(allocate(".CRT$XIU")) \
|
||||||
|
static int(* AUTORUN_J(AUTORUN_U(f),__2) )() = AUTORUN_J(AUTORUN_U(f),__1); \
|
||||||
|
static void AUTORUN_U(f)(void)
|
||||||
|
#else
|
||||||
|
#define AUTORUN \
|
||||||
|
__attribute__((constructor)) \
|
||||||
|
static void AUTORUN_U(f)(void)
|
||||||
|
#endif
|
||||||
|
|
||||||
API void* obj_malloc( int sz, ... );
|
// join + unique macro utils
|
||||||
API void* obj_calloc( int sz, ... );
|
|
||||||
API void obj_free( void *obj );
|
|
||||||
|
|
||||||
API bool obj_typeeq( const void *obj1, const void *obj2 );
|
#define AUTORUN_j(a, b) a##b
|
||||||
API const char* obj_typeof( const void *obj );
|
#define AUTORUN_J(a, b) AUTORUN_j(a, b)
|
||||||
API unsigned obj_typeid( const void *obj );
|
#define AUTORUN_U(x) AUTORUN_J(x, __LINE__)
|
||||||
API unsigned obj_typeid_from_name( const char *name );
|
|
||||||
|
|
||||||
// object api (ctor/dtor, refcounting, oop)
|
#if 0 // autorun demo
|
||||||
|
void byebye(void) { puts("seen after main()"); }
|
||||||
|
AUTORUN { puts("seen before main()"); }
|
||||||
|
AUTORUN { puts("seen before main() too"); atexit( byebye ); }
|
||||||
|
#endif
|
||||||
|
|
||||||
API void obj_new( const char *type, ... );
|
// -----------------------------------------------------------------------------
|
||||||
API void obj_del( void *obj );
|
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
|
||||||
|
|
||||||
API void* obj_ref( void *obj );
|
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||||
API void* obj_unref( void *obj );
|
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||||
|
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||||
|
|
||||||
API void obj_extend( const char *dstclass, const char *srcclass );
|
typedef struct int2 { int x,y; } int2;
|
||||||
API void obj_override( const char *objclass, void (**vtable)(), void(*fn)() );
|
typedef struct int3 { int x,y,z; } int3;
|
||||||
|
typedef struct int4 { int x,y,z,w; } int4;
|
||||||
|
|
||||||
// object: serialize
|
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||||
|
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||||
|
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||||
|
|
||||||
API unsigned obj_load(void *obj, const array(char) buffer);
|
typedef struct float2 { float x,y; } float2;
|
||||||
API unsigned obj_load_file(void *obj, FILE *fp);
|
typedef struct float3 { float x,y,z; } float3;
|
||||||
API unsigned obj_load_inplace(void *obj, const void *src, unsigned srclen);
|
typedef struct float4 { float x,y,z,w; } float4;
|
||||||
|
|
||||||
API array(char) obj_save(const void *obj); // empty if error. must array_free() after use
|
typedef struct double2 { double x,y; } double2;
|
||||||
API unsigned obj_save_file(FILE *fp, const void *obj);
|
typedef struct double3 { double x,y,z; } double3;
|
||||||
API unsigned obj_save_inplace(void *dst, unsigned cap, const void *obj);
|
typedef struct double4 { double x,y,z,w; } double4;
|
||||||
|
|
||||||
// object: utils
|
#define byte2(x,y) M_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
|
||||||
|
#define byte3(x,y,z) M_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
|
||||||
|
#define byte4(x,y,z,w) M_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
|
||||||
|
|
||||||
API unsigned obj_instances( const void *obj );
|
#define int2(x,y) M_CAST(int2, (int)(x), (int)(y) )
|
||||||
|
#define int3(x,y,z) M_CAST(int3, (int)(x), (int)(y), (int)(z) )
|
||||||
|
#define int4(x,y,z,w) M_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
|
||||||
|
|
||||||
API void obj_zero( void *obj );
|
#define uint2(x,y) M_CAST(uint2, (unsigned)(x), (unsigned)(y) )
|
||||||
API unsigned obj_sizeof( const void *obj );
|
#define uint3(x,y,z) M_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
|
||||||
|
#define uint4(x,y,z,w) M_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
|
||||||
|
|
||||||
API void obj_hexdump( const void *obj );
|
#define float2(x,y) M_CAST(float2, (float)(x), (float)(y) )
|
||||||
API void obj_hexdumpf( FILE *out, const void *obj );
|
#define float3(x,y,z) M_CAST(float3, (float)(x), (float)(y), (float)(z) )
|
||||||
|
#define float4(x,y,z,w) M_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
|
||||||
|
|
||||||
API void obj_printf( void *obj, const char *text );
|
#define double2(x,y) M_CAST(double2, (double)(x), (double)(y) )
|
||||||
API const char* obj_output( const void *obj );
|
#define double3(x,y,z) M_CAST(double3, (double)(x), (double)(y), (double)(z) )
|
||||||
|
#define double4(x,y,z,w) M_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
|
||||||
API void * obj_clone(const void *obj);
|
|
||||||
API void * obj_copy(void **dst, const void *src);
|
|
||||||
API void * obj_mutate(void **dst, const void *src);
|
|
||||||
|
|
||||||
// object: method dispatch tables
|
|
||||||
|
|
||||||
#define ctor(obj) obj_method0(obj, ctor) // ctor[obj_typeid(obj)](obj)
|
|
||||||
#define dtor(obj) obj_method0(obj, dtor) // dtor[obj_typeid(obj)](obj)
|
|
||||||
|
|
||||||
API extern void (*ctor[256])(); ///-
|
|
||||||
API extern void (*dtor[256])(); ///-
|
|
||||||
|
|
||||||
// object: syntax sugars
|
|
||||||
|
|
||||||
#define obj_malloc(sz, ...) obj_initialize((void**)MALLOC( sizeof(void*)+sz), stringf("\1untyped\n%s\n", "" #__VA_ARGS__))
|
|
||||||
#define obj_calloc(sz, ...) obj_initialize((void**)CALLOC(1,sizeof(void*)+sz), stringf("\1untyped\n%s\n", "" #__VA_ARGS__))
|
|
||||||
|
|
||||||
#define obj_new0(type) obj_new(type, 0)
|
|
||||||
#define obj_new(type, ...) ( \
|
|
||||||
obj_tmpalloc = obj_initialize((void**)CALLOC(1, sizeof(void*)+sizeof(type)), stringf("%c" #type "\n", (char)obj_typeid_from_name(#type))), \
|
|
||||||
(*(type*)obj_tmpalloc = (type){ __VA_ARGS__ }), \
|
|
||||||
ctor(obj_tmpalloc), \
|
|
||||||
(type*)obj_tmpalloc )
|
|
||||||
|
|
||||||
#define obj_override(class, method) obj_override(#class, (void(**)())method, (void(*)())class##_##method)
|
|
||||||
#define obj_method0(obj, method) method[obj_typeid(obj)]((obj))
|
|
||||||
#define obj_method(obj, method, ...) method[obj_typeid(obj)]((obj), __VA_ARGS__)
|
|
||||||
|
|
||||||
#define obj_printf(obj, ...) obj_printf(obj, va(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define obj_extend(dstclass, srcclass) obj_extend(#dstclass, #srcclass)
|
|
||||||
|
|
||||||
// object: implementation details
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/16198700/using-the-extra-16-bits-in-64-bit-pointers (note: using 19-bits here)
|
|
||||||
#define OBJBOX(ptr, payload16) (void*)(((long long unsigned)(payload16) << 48) | (long long unsigned)(ptr))
|
|
||||||
#define OBJUNBOX(ptr) (void*)((long long unsigned)(ptr) & 0x0000FFFFFFFFFFFFull)
|
|
||||||
#define OBJPAYLOAD16(ptr) (((long long unsigned)(ptr)) >> 48)
|
|
||||||
#define OBJPAYLOAD3(ptr) (((long long unsigned)(ptr)) & 7)
|
|
||||||
|
|
||||||
API void* obj_initialize( void **ptr, char *type_and_info );
|
|
||||||
static __thread void *obj_tmpalloc;
|
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "v4k_profile.h"
|
#line 1 "v4k_profile.h"
|
||||||
|
@ -3461,42 +3440,23 @@ API char* strjoin(array(char*) list, const char *separator);
|
||||||
|
|
||||||
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
||||||
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// ## string interning (quarks)
|
||||||
|
// - rlyeh, public domain.
|
||||||
|
|
||||||
|
unsigned intern( const char *string );
|
||||||
|
const char *quark( unsigned key );
|
||||||
|
|
||||||
|
typedef array(char) quarks_db;
|
||||||
|
unsigned quark_intern( quarks_db*, const char *string );
|
||||||
|
const char *quark_string( quarks_db*, unsigned key );
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "v4k_system.h"
|
#line 1 "v4k_time.h"
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// system framework utils
|
// time framework utils
|
||||||
// - rlyeh, public domain.
|
// - rlyeh, public domain.
|
||||||
//
|
|
||||||
// Note: Windows users add `/Zi` compilation flags, else add `-g` and/or `-ldl` flags
|
|
||||||
// Note: If you are linking your binary using GNU ld you need to add --export-dynamic
|
|
||||||
|
|
||||||
API void* thread( int (*thread_func)(void* user_data), void* user_data );
|
|
||||||
API void thread_destroy( void *thd );
|
|
||||||
|
|
||||||
API int argc();
|
|
||||||
API char* argv(int);
|
|
||||||
|
|
||||||
API int flag(const char *commalist); // --arg // app_flag?
|
|
||||||
API const char* option(const char *commalist, const char *defaults); // --arg=value or --arg value
|
|
||||||
API int optioni(const char *commalist, int defaults); // argvi() ?
|
|
||||||
API float optionf(const char *commalist, float defaults); // app_option?
|
|
||||||
|
|
||||||
API void tty_color(unsigned color);
|
|
||||||
API void tty_reset();
|
|
||||||
API void tty_attach();
|
|
||||||
API void tty_detach();
|
|
||||||
|
|
||||||
API const char* app_exec(const char *command); // returns ("%15d %s", retcode, output_last_line)
|
|
||||||
API int app_spawn(const char *command);
|
|
||||||
API int app_cores();
|
|
||||||
API int app_battery(); /// return battery level [1..100]. also positive if charging (+), negative if discharging (-), and 0 if no battery is present.
|
|
||||||
|
|
||||||
API const char* app_name();
|
|
||||||
API const char* app_path();
|
|
||||||
API const char* app_cache();
|
|
||||||
API const char* app_temp();
|
|
||||||
API const char* app_cmdline();
|
|
||||||
|
|
||||||
API uint64_t date(); // YYYYMMDDhhmmss
|
API uint64_t date(); // YYYYMMDDhhmmss
|
||||||
API uint64_t date_epoch(); // linux epoch
|
API uint64_t date_epoch(); // linux epoch
|
||||||
|
@ -3515,6 +3475,72 @@ API void sleep_ns(double us);
|
||||||
API unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
|
API unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
|
||||||
API void timer_destroy(unsigned timer_handle);
|
API void timer_destroy(unsigned timer_handle);
|
||||||
|
|
||||||
|
// time sortable unique identifier (similar to ksuid/tuid; others: sno/xid/cuid/ulid)
|
||||||
|
// - rlyeh, public domain.
|
||||||
|
//
|
||||||
|
// also similar to a mongo object id, 12 bytes as follows:
|
||||||
|
// - 4-byte timestamp (ss). epoch: Tuesday, 12 September 2023 6:06:56
|
||||||
|
// - 2-byte (machine or app hash)
|
||||||
|
// - 2-byte (thread-id)
|
||||||
|
// - 4-byte (rand counter, that gets increased at every id creation)
|
||||||
|
|
||||||
|
typedef vec3i guid;
|
||||||
|
|
||||||
|
API guid guid_create();
|
||||||
|
|
||||||
|
/*
|
||||||
|
AUTORUN {
|
||||||
|
guid g1 = guid_create();
|
||||||
|
guid g2 = guid_create();
|
||||||
|
print3i(g1);
|
||||||
|
hexdump(&g1, sizeof(g1));
|
||||||
|
print3i(g2);
|
||||||
|
hexdump(&g2, sizeof(g2));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#line 0
|
||||||
|
|
||||||
|
#line 1 "v4k_system.h"
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// system framework utils
|
||||||
|
// - rlyeh, public domain.
|
||||||
|
//
|
||||||
|
// Note: Windows users add `/Zi` compilation flags, else add `-g` and/or `-ldl` flags
|
||||||
|
// Note: If you are linking your binary using GNU ld you need to add --export-dynamic
|
||||||
|
|
||||||
|
API void* thread( int (*thread_func)(void* user_data), void* user_data );
|
||||||
|
API void thread_destroy( void *thd );
|
||||||
|
|
||||||
|
API int argc();
|
||||||
|
API char* argv(int);
|
||||||
|
|
||||||
|
API int flag(const char *commalist); // --arg // app_flag?
|
||||||
|
API const char* option(const char *commalist, const char *defaults); // --arg=string or --arg string
|
||||||
|
API int optioni(const char *commalist, int defaults); // --arg=integer or --arg integer // argvi() ?
|
||||||
|
API float optionf(const char *commalist, float defaults); // --arg=float or --arg float // flagf() ?
|
||||||
|
|
||||||
|
API void tty_attach();
|
||||||
|
API void tty_detach();
|
||||||
|
API void tty_color(unsigned color);
|
||||||
|
API void tty_reset();
|
||||||
|
|
||||||
|
API const char* app_exec(const char *command); // returns ("%15d %s", retcode, output_last_line)
|
||||||
|
API int app_spawn(const char *command);
|
||||||
|
API int app_cores();
|
||||||
|
API int app_battery(); /// returns battery level [1..100]. also positive if charging (+), negative if discharging (-), and 0 if no battery is present.
|
||||||
|
|
||||||
|
API const char* app_name();
|
||||||
|
API const char* app_path();
|
||||||
|
API const char* app_cache();
|
||||||
|
API const char* app_temp();
|
||||||
|
API const char* app_cmdline();
|
||||||
|
|
||||||
|
API void app_beep();
|
||||||
|
API void app_hang();
|
||||||
|
API void app_crash();
|
||||||
|
API void app_singleton(const char *guid);
|
||||||
|
API bool app_open(const char *folder_file_or_url);
|
||||||
|
|
||||||
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
|
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
|
||||||
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.
|
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.
|
||||||
|
|
||||||
|
@ -3525,12 +3551,12 @@ API void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
|
||||||
API void breakpoint(const char *optional_reason);
|
API void breakpoint(const char *optional_reason);
|
||||||
API bool has_debugger();
|
API bool has_debugger();
|
||||||
|
|
||||||
API void signal_hooks(void);
|
API void trap_install(void);
|
||||||
API void signal_handler_ignore(int signal);
|
API const char *trap_name(int signal); // helper util
|
||||||
API void signal_handler_quit(int signal);
|
API void trap_on_ignore(int signal); // helper util
|
||||||
API void signal_handler_abort(int signal);
|
API void trap_on_quit(int signal); // helper util
|
||||||
API void signal_handler_debug(int signal);
|
API void trap_on_abort(int signal); // helper util
|
||||||
API const char *signal_name(int signal); // helper util
|
API void trap_on_debug(int signal); // helper util
|
||||||
|
|
||||||
API uint16_t lil16(uint16_t n); // swap16 as lil
|
API uint16_t lil16(uint16_t n); // swap16 as lil
|
||||||
API uint32_t lil32(uint32_t n); // swap32 as lil
|
API uint32_t lil32(uint32_t n); // swap32 as lil
|
||||||
|
@ -3559,6 +3585,10 @@ API int (PANIC)(const char *error, const char *file, int line);
|
||||||
|
|
||||||
#define PRINTF(...) PRINTF(va(__VA_ARGS__), 1[#__VA_ARGS__] == '!' ? callstack(+48) : "", __FILE__, __LINE__, __FUNCTION__)
|
#define PRINTF(...) PRINTF(va(__VA_ARGS__), 1[#__VA_ARGS__] == '!' ? callstack(+48) : "", __FILE__, __LINE__, __FUNCTION__)
|
||||||
API int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
|
API int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
|
||||||
|
|
||||||
|
#define test(expr) test(__FILE__,__LINE__,#expr,!!(expr))
|
||||||
|
API int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
|
// AUTORUN { test(1<2); }
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "v4k_ui.h"
|
#line 1 "v4k_ui.h"
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include "v4k.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
float app_volume = 1.00f;
|
||||||
|
unsigned app_size = flag("--fullscreen") ? 100 : 75;
|
||||||
|
unsigned app_flags = flag("--msaa") ? WINDOW_MSAA4 : 0;
|
||||||
|
unsigned app_target_fps = optioni("--fps", 60); // --fps=30, --fps=45, etc. defaults to 60.
|
||||||
|
|
||||||
|
// window api (fullscreen or 75% sized, optional MSAA flags)
|
||||||
|
window_create(app_size, app_flags);
|
||||||
|
window_title(__FILE__);
|
||||||
|
window_fps_lock(app_target_fps);
|
||||||
|
|
||||||
|
// load video
|
||||||
|
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_LOOP );
|
||||||
|
|
||||||
|
// app loop
|
||||||
|
while( window_swap() ) {
|
||||||
|
// input controls
|
||||||
|
if( input(KEY_ESC) ) break;
|
||||||
|
|
||||||
|
// profile api
|
||||||
|
texture_t *textures;
|
||||||
|
profile( "Video decoder" ) {
|
||||||
|
// video api: decode frame and get associated textures (audio is sent to audiomixer automatically)
|
||||||
|
textures = video_decode( v );
|
||||||
|
// fullscreen video
|
||||||
|
// if(video_is_rgb(v)) fullscreen_quad_rgb( textures[0], 1.3f );
|
||||||
|
// else fullscreen_quad_ycbcr( textures, 1.3f );
|
||||||
|
}
|
||||||
|
|
||||||
|
// create menubar on top
|
||||||
|
int choice1 = ui_menu("File;Shell;Exit");
|
||||||
|
int choice2 = ui_menu("Help;About");
|
||||||
|
if( choice1 == 1 ) system(ifdef(win32, "start \"\" cmd", ifdef(osx, "open sh", "xdg-open sh")));
|
||||||
|
if( choice1 == 2 ) exit(0);
|
||||||
|
|
||||||
|
// showcase a few ui widgets
|
||||||
|
ui_demo(0);
|
||||||
|
|
||||||
|
// create ui panel
|
||||||
|
if( ui_panel("myPanel", PANEL_OPEN) ) {
|
||||||
|
// Print some numbers
|
||||||
|
ui_section("Stats");
|
||||||
|
ui_label2("FPS", va("%5.2f", window_fps()));
|
||||||
|
ui_separator();
|
||||||
|
|
||||||
|
// add some buttons
|
||||||
|
ui_section("Buttons");
|
||||||
|
if( ui_button("Screenshot") ) window_screenshot(__FILE__ ".png"), ui_notify(0,ICON_MD_WARNING "Screenshot");
|
||||||
|
if( ui_button("Record Video") ) window_record(__FILE__ ".mp4"), ui_notify(0,ICON_MD_WARNING "Recoding video");
|
||||||
|
if( ui_button("Toggle fullscreen") ) window_fullscreen( !window_has_fullscreen() );
|
||||||
|
ui_separator();
|
||||||
|
|
||||||
|
// some more video controls
|
||||||
|
ui_section("Video");
|
||||||
|
if( ui_button("Rewind") ) video_seek(v, video_position(v) - 3);
|
||||||
|
if( ui_button("Pause") ) video_pause(v, video_is_paused(v) ^ 1);
|
||||||
|
if( ui_button("Forward") ) video_seek(v, video_position(v) + 3);
|
||||||
|
if( ui_slider2("Volume", &app_volume, va("%.2f", app_volume))) audio_volume_master(app_volume);
|
||||||
|
|
||||||
|
// end of panel. must be enclosed within same if() branch.
|
||||||
|
ui_panel_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create window
|
||||||
|
static int open = 1;
|
||||||
|
if( ui_window("myWindow", &open) ) {
|
||||||
|
// present decoded texture in a widget, without any label (NULL)
|
||||||
|
ui_texture( NULL, textures[0] );
|
||||||
|
// end of window. must be enclosed within same if() branch.
|
||||||
|
ui_window_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -7,7 +7,7 @@
|
||||||
; syntax: symbols are defined in KEY=value form, as seen below.
|
; syntax: symbols are defined in KEY=value form, as seen below.
|
||||||
|
|
||||||
TOOLS=./ ; folder where our pipeline tools are located
|
TOOLS=./ ; folder where our pipeline tools are located
|
||||||
ART=../demos/art/,../engine/art/ ; comma-separated folder(s) that store all our asset files
|
ART=../demos/art/,../engine/art/,../labs/slayer/art ; comma-separated folder(s) that store all our asset files
|
||||||
|
|
||||||
; lines starting with @windows, @linux or @osx will be processed only where OS matches.
|
; lines starting with @windows, @linux or @osx will be processed only where OS matches.
|
||||||
; we are defining here some symbols differently for each platform.
|
; we are defining here some symbols differently for each platform.
|
||||||
|
|
|
@ -0,0 +1,740 @@
|
||||||
|
#pragma comment(lib, "winmm")
|
||||||
|
/* ========================================================================
|
||||||
|
$File: tools/ctime/ctime.c $
|
||||||
|
$Date: 2016/05/08 04:16:55PM $
|
||||||
|
$Revision: 7 $
|
||||||
|
$Creator: Casey Muratori $
|
||||||
|
$Notice:
|
||||||
|
|
||||||
|
The author of this software MAKES NO WARRANTY as to the RELIABILITY,
|
||||||
|
SUITABILITY, or USABILITY of this software. USE IT AT YOUR OWN RISK.
|
||||||
|
|
||||||
|
This is a simple timing utility. It is in the public domain.
|
||||||
|
Anyone can use it, modify it, roll'n'smoke hardcopies of the source
|
||||||
|
code, sell it to the terrorists, etc.
|
||||||
|
|
||||||
|
But the author makes absolutely no warranty as to the reliability,
|
||||||
|
suitability, or usability of the software. There might be bad bugs
|
||||||
|
in here. It could delete all your files. It could format your
|
||||||
|
hard drive. I have no idea. If you lose all your files from using
|
||||||
|
it, it is your fault.
|
||||||
|
|
||||||
|
$
|
||||||
|
|
||||||
|
ctime is a simple utility that helps you keep track of how much time
|
||||||
|
you spend building your projects. You use it the same way you would
|
||||||
|
use a begin/end block profiler in your normal code, only instead of
|
||||||
|
profiling your code, you profile your build.
|
||||||
|
|
||||||
|
BASIC INSTRUCTIONS
|
||||||
|
------------------
|
||||||
|
|
||||||
|
On the very first line of your build script, you do something like this:
|
||||||
|
|
||||||
|
ctime -begin timings_file_for_this_build.ctm
|
||||||
|
|
||||||
|
and then on the very last line of your build script, you do
|
||||||
|
|
||||||
|
ctime -end timings_file_for_this_build.ctm
|
||||||
|
|
||||||
|
That's all there is to it! ctime will keep track of every build you
|
||||||
|
do, when you did it, and how long it took. Later, when you'd like to
|
||||||
|
get a feel for how your build times have evolved, you can type
|
||||||
|
|
||||||
|
ctime -stats timings_file_for_this_build.ctm
|
||||||
|
|
||||||
|
and it will tell you a number of useful statistics!
|
||||||
|
|
||||||
|
|
||||||
|
ADVANCED INSTRUCTIONS
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
ctime has the ability to track the difference between _failed_ builds
|
||||||
|
and _successful_ builds. If you would like it to do so, you can capture
|
||||||
|
the error status in your build script at whatever point you want,
|
||||||
|
for example:
|
||||||
|
|
||||||
|
set LastError=%ERRORLEVEL%
|
||||||
|
|
||||||
|
and then when you eventually call ctime to end the profiling, you simply
|
||||||
|
pass that error code to it:
|
||||||
|
|
||||||
|
ctime -end timings_file_for_this_build.ctm %LastError%
|
||||||
|
|
||||||
|
ctime can also dump all timings from a timing file into a textual
|
||||||
|
format for use in other types of tools. To get a CSV you can import
|
||||||
|
into a graphing program or database, use:
|
||||||
|
|
||||||
|
ctime -csv timings_file_for_this_build.ctm
|
||||||
|
|
||||||
|
Also, you may want to do things like timing multiple builds separately,
|
||||||
|
or timing builds based on what compiler flags are active. To do this,
|
||||||
|
you can use separate timing files for each configuration by using
|
||||||
|
the shell variables for the build at the filename, eg.:
|
||||||
|
|
||||||
|
ctime -begin timings_for_%BUILD_NAME%.ctm
|
||||||
|
...
|
||||||
|
ctime -end timings_for_%BUILD_NAME%.ctm
|
||||||
|
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
#define _CRT_SECURE_NO_DEPRECATE
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
|
||||||
|
#define MAGIC_VALUE 0xCA5E713F
|
||||||
|
typedef struct timing_file_header
|
||||||
|
{
|
||||||
|
int unsigned MagicValue;
|
||||||
|
} timing_file_header;
|
||||||
|
|
||||||
|
typedef struct timing_file_date
|
||||||
|
{
|
||||||
|
long long unsigned E;
|
||||||
|
} timing_file_date;
|
||||||
|
|
||||||
|
enum timing_file_entry_flag
|
||||||
|
{
|
||||||
|
TFEF_Complete = 0x1,
|
||||||
|
TFEF_NoErrors = 0x2,
|
||||||
|
};
|
||||||
|
typedef struct timing_file_entry
|
||||||
|
{
|
||||||
|
timing_file_date StartDate;
|
||||||
|
int unsigned Flags;
|
||||||
|
int unsigned MillisecondsElapsed;
|
||||||
|
} timing_file_entry;
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
typedef struct timing_entry_array
|
||||||
|
{
|
||||||
|
int EntryCount;
|
||||||
|
timing_file_entry *Entries;
|
||||||
|
} timing_entry_array;
|
||||||
|
|
||||||
|
//
|
||||||
|
// TODO(casey): More platforms? Sadly, ANSI C doesn't support high-resolution timing across runs of a process AFAICT :(
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
static int unsigned
|
||||||
|
GetClock(void)
|
||||||
|
{
|
||||||
|
if(sizeof(int unsigned) != sizeof(DWORD))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unexpected integer size - timing will not work on this platform!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return(timeGetTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// This was written for Linux
|
||||||
|
|
||||||
|
static int unsigned
|
||||||
|
GetClock(void)
|
||||||
|
{
|
||||||
|
struct timespec TimeSpec;
|
||||||
|
int unsigned Result;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_REALTIME, &TimeSpec);
|
||||||
|
Result = TimeSpec.tv_sec * 1000 + TimeSpec.tv_nsec / 1000000;
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
static timing_file_date
|
||||||
|
GetDate(void)
|
||||||
|
{
|
||||||
|
timing_file_date Result = {0};
|
||||||
|
Result.E = time(NULL);
|
||||||
|
return(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintDate(timing_file_date Date)
|
||||||
|
{
|
||||||
|
time_t Time;
|
||||||
|
struct tm *LocalTime;
|
||||||
|
char Str[256];
|
||||||
|
|
||||||
|
Time = Date.E;
|
||||||
|
LocalTime = localtime(&Time);
|
||||||
|
strftime(Str, 256, "%Y-%m-%d %H:%M:%S", LocalTime);
|
||||||
|
fprintf(stdout, "%s", Str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long long unsigned
|
||||||
|
SecondDifference(timing_file_date A, timing_file_date B)
|
||||||
|
{
|
||||||
|
long long unsigned Result = A.E - B.E;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unsigned
|
||||||
|
DayIndex(timing_file_date A)
|
||||||
|
{
|
||||||
|
time_t Time;
|
||||||
|
struct tm *LocalTime;
|
||||||
|
|
||||||
|
Time = A.E;
|
||||||
|
LocalTime = localtime(&Time);
|
||||||
|
return LocalTime->tm_yday;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "CTime v1.0 by Casey Muratori\n");
|
||||||
|
fprintf(stderr, "Usage:\n");
|
||||||
|
fprintf(stderr, " ctime -begin <timing file>\n");
|
||||||
|
fprintf(stderr, " ctime -end <timing file> [error level]\n");
|
||||||
|
fprintf(stderr, " ctime -stats <timing file>\n");
|
||||||
|
fprintf(stderr, " ctime -csv <timing file>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static timing_entry_array
|
||||||
|
ReadAllEntries(FILE* Handle)
|
||||||
|
{
|
||||||
|
timing_entry_array Result = {0};
|
||||||
|
|
||||||
|
int EntriesBegin = sizeof(timing_file_header);
|
||||||
|
int FileSize;
|
||||||
|
if((fseek(Handle, 0, SEEK_END) == 0) && ((FileSize = ftell(Handle)) >= 0))
|
||||||
|
{
|
||||||
|
int EntriesSize = FileSize - EntriesBegin;
|
||||||
|
Result.Entries = (timing_file_entry *)malloc(EntriesSize);
|
||||||
|
if(Result.Entries)
|
||||||
|
{
|
||||||
|
fseek(Handle, EntriesBegin, SEEK_SET);
|
||||||
|
int ReadSize = (int)fread(Result.Entries, 1, EntriesSize, Handle);
|
||||||
|
if(ReadSize == EntriesSize)
|
||||||
|
{
|
||||||
|
Result.EntryCount = EntriesSize / sizeof(timing_file_entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to read timing entries from file.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to allocate %d for storing timing entries.\n", EntriesSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to determine file size of timing file.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FreeAllEntries(timing_entry_array Array)
|
||||||
|
{
|
||||||
|
if(Array.Entries)
|
||||||
|
{
|
||||||
|
free(Array.Entries);
|
||||||
|
Array.EntryCount = 0;
|
||||||
|
Array.Entries = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
CSV(timing_entry_array Array, char *TimingFileName)
|
||||||
|
{
|
||||||
|
int EntryIndex;
|
||||||
|
timing_file_entry *Entry = Array.Entries;
|
||||||
|
|
||||||
|
fprintf(stdout, "%s Timings\n", TimingFileName);
|
||||||
|
fprintf(stdout, "ordinal, date, duration, status\n");
|
||||||
|
{for(EntryIndex = 0;
|
||||||
|
EntryIndex < Array.EntryCount;
|
||||||
|
++EntryIndex, ++Entry)
|
||||||
|
{
|
||||||
|
fprintf(stdout, "%d, ", EntryIndex);
|
||||||
|
PrintDate(Entry->StartDate);
|
||||||
|
if(Entry->Flags & TFEF_Complete)
|
||||||
|
{
|
||||||
|
fprintf(stdout, ", %0.3fs, %s", (double)Entry->MillisecondsElapsed / 1000.0,
|
||||||
|
(Entry->Flags & TFEF_NoErrors) ? "succeeded" : "failed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stdout, ", (never completed), failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct time_part
|
||||||
|
{
|
||||||
|
char *Name;
|
||||||
|
double MillisecondsPer;
|
||||||
|
} time_part;
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintTime(double Milliseconds)
|
||||||
|
{
|
||||||
|
double MillisecondsPerSecond = 1000;
|
||||||
|
double MillisecondsPerMinute = 60*MillisecondsPerSecond;
|
||||||
|
double MillisecondsPerHour = 60*MillisecondsPerMinute;
|
||||||
|
double MillisecondsPerDay = 24*MillisecondsPerHour;
|
||||||
|
double MillisecondsPerWeek = 7*MillisecondsPerDay;
|
||||||
|
time_part Parts[] =
|
||||||
|
{
|
||||||
|
{"week", MillisecondsPerWeek},
|
||||||
|
{"day", MillisecondsPerDay},
|
||||||
|
{"hour", MillisecondsPerHour},
|
||||||
|
{"minute", MillisecondsPerMinute},
|
||||||
|
};
|
||||||
|
int unsigned PartIndex;
|
||||||
|
double Q = Milliseconds;
|
||||||
|
|
||||||
|
for(PartIndex = 0;
|
||||||
|
PartIndex < (sizeof(Parts)/sizeof(Parts[0]));
|
||||||
|
++PartIndex)
|
||||||
|
{
|
||||||
|
double MsPer = Parts[PartIndex].MillisecondsPer;
|
||||||
|
double This = (double)(int)(Q / MsPer);
|
||||||
|
|
||||||
|
if(This > 0)
|
||||||
|
{
|
||||||
|
fprintf(stdout, "%d %s%s, ", (int)This, Parts[PartIndex].Name,
|
||||||
|
(This != 1) ? "s" : "");
|
||||||
|
}
|
||||||
|
Q -= This*MsPer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "%0.3f seconds", (double)Q / 1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintTimeStat(char *Name, int unsigned Milliseconds)
|
||||||
|
{
|
||||||
|
fprintf(stdout, "%s: ", Name);
|
||||||
|
PrintTime((double)Milliseconds);
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct stat_group
|
||||||
|
{
|
||||||
|
int unsigned Count;
|
||||||
|
|
||||||
|
int unsigned SlowestMs;
|
||||||
|
int unsigned FastestMs;
|
||||||
|
double TotalMs;
|
||||||
|
|
||||||
|
} stat_group;
|
||||||
|
|
||||||
|
#define GRAPH_HEIGHT 10
|
||||||
|
#define GRAPH_WIDTH 30
|
||||||
|
typedef struct graph
|
||||||
|
{
|
||||||
|
stat_group Buckets[GRAPH_WIDTH];
|
||||||
|
} graph;
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintStatGroup(char *Title, stat_group *Group)
|
||||||
|
{
|
||||||
|
int unsigned AverageMs = 0;
|
||||||
|
if(Group->Count >= 1)
|
||||||
|
{
|
||||||
|
AverageMs = (int unsigned)(Group->TotalMs / (double)Group->Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Group->Count > 0)
|
||||||
|
{
|
||||||
|
fprintf(stdout, "%s (%d):\n", Title, Group->Count);
|
||||||
|
PrintTimeStat(" Slowest", Group->SlowestMs);
|
||||||
|
PrintTimeStat(" Fastest", Group->FastestMs);
|
||||||
|
PrintTimeStat(" Average", AverageMs);
|
||||||
|
PrintTimeStat(" Total", (int unsigned)Group->TotalMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
UpdateStatGroup(stat_group *Group, timing_file_entry *Entry)
|
||||||
|
{
|
||||||
|
if(Group->SlowestMs < Entry->MillisecondsElapsed)
|
||||||
|
{
|
||||||
|
Group->SlowestMs = Entry->MillisecondsElapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Group->FastestMs > Entry->MillisecondsElapsed)
|
||||||
|
{
|
||||||
|
Group->FastestMs = Entry->MillisecondsElapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Group->TotalMs += (double)Entry->MillisecondsElapsed;
|
||||||
|
|
||||||
|
++Group->Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
MapToDiscrete(double Value, double InMax, double OutMax)
|
||||||
|
{
|
||||||
|
int Result;
|
||||||
|
|
||||||
|
if(InMax == 0)
|
||||||
|
{
|
||||||
|
InMax = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result = (int)((Value / InMax) * OutMax);
|
||||||
|
|
||||||
|
return(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintGraph(char *Title, double DaySpan, graph *Graph)
|
||||||
|
{
|
||||||
|
int BucketIndex;
|
||||||
|
int LineIndex;
|
||||||
|
int unsigned MaxCountInBucket = 0;
|
||||||
|
int unsigned SlowestMs = 0;
|
||||||
|
double DPB = DaySpan / (double)GRAPH_WIDTH;
|
||||||
|
|
||||||
|
for(BucketIndex = 0;
|
||||||
|
BucketIndex < GRAPH_WIDTH;
|
||||||
|
++BucketIndex)
|
||||||
|
{
|
||||||
|
stat_group *Group = Graph->Buckets + BucketIndex;
|
||||||
|
|
||||||
|
if(Group->Count)
|
||||||
|
{
|
||||||
|
// double AverageMs = Group->TotalMs / (double)Group->Count;
|
||||||
|
if(MaxCountInBucket < Group->Count)
|
||||||
|
{
|
||||||
|
MaxCountInBucket = Group->Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SlowestMs < Group->SlowestMs)
|
||||||
|
{
|
||||||
|
SlowestMs = Group->SlowestMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "\n%s (%f day%s/bucket):\n", Title, DPB, (DPB == 1) ? "" : "s");
|
||||||
|
for(LineIndex = GRAPH_HEIGHT - 1;
|
||||||
|
LineIndex >= 0;
|
||||||
|
--LineIndex)
|
||||||
|
{
|
||||||
|
fputc('|', stdout);
|
||||||
|
for(BucketIndex = 0;
|
||||||
|
BucketIndex < GRAPH_WIDTH;
|
||||||
|
++BucketIndex)
|
||||||
|
{
|
||||||
|
stat_group *Group = Graph->Buckets + BucketIndex;
|
||||||
|
int This = -1;
|
||||||
|
if(Group->Count)
|
||||||
|
{
|
||||||
|
// double AverageMs = Group->TotalMs / (double)Group->Count;
|
||||||
|
This = MapToDiscrete(Group->SlowestMs, SlowestMs, GRAPH_HEIGHT - 1);
|
||||||
|
}
|
||||||
|
fputc((This >= LineIndex) ? '*' : ' ', stdout);
|
||||||
|
}
|
||||||
|
if(LineIndex == (GRAPH_HEIGHT - 1))
|
||||||
|
{
|
||||||
|
fputc(' ', stdout);
|
||||||
|
PrintTime(SlowestMs);
|
||||||
|
}
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
fputc('+', stdout);
|
||||||
|
for(BucketIndex = 0; BucketIndex < GRAPH_WIDTH; ++BucketIndex) {fputc('-', stdout);}
|
||||||
|
fputc(' ', stdout);
|
||||||
|
PrintTime(0);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
for(LineIndex = GRAPH_HEIGHT - 1;
|
||||||
|
LineIndex >= 0;
|
||||||
|
--LineIndex)
|
||||||
|
{
|
||||||
|
fputc('|', stdout);
|
||||||
|
for(BucketIndex = 0;
|
||||||
|
BucketIndex < GRAPH_WIDTH;
|
||||||
|
++BucketIndex)
|
||||||
|
{
|
||||||
|
stat_group *Group = Graph->Buckets + BucketIndex;
|
||||||
|
int This = -1;
|
||||||
|
if(Group->Count)
|
||||||
|
{
|
||||||
|
This = MapToDiscrete(Group->Count, MaxCountInBucket, GRAPH_HEIGHT - 1);
|
||||||
|
}
|
||||||
|
fputc((This >= LineIndex) ? '*' : ' ', stdout);
|
||||||
|
}
|
||||||
|
if(LineIndex == (GRAPH_HEIGHT - 1))
|
||||||
|
{
|
||||||
|
fprintf(stdout, " %u", MaxCountInBucket);
|
||||||
|
}
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
fputc('+', stdout);
|
||||||
|
for(BucketIndex = 0; BucketIndex < GRAPH_WIDTH; ++BucketIndex) {fputc('-', stdout);}
|
||||||
|
fprintf(stdout, " 0\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Stats(timing_entry_array Array, char *TimingFileName)
|
||||||
|
{
|
||||||
|
stat_group WithErrors = {0};
|
||||||
|
stat_group NoErrors = {0};
|
||||||
|
stat_group AllStats = {0};
|
||||||
|
|
||||||
|
int unsigned IncompleteCount = 0;
|
||||||
|
int unsigned DaysWithTimingCount = 0;
|
||||||
|
int unsigned DaySpanCount = 0;
|
||||||
|
|
||||||
|
int EntryIndex;
|
||||||
|
|
||||||
|
timing_file_entry *Entry = Array.Entries;
|
||||||
|
int unsigned LastDayIndex = 0;
|
||||||
|
|
||||||
|
double AllMs = 0;
|
||||||
|
|
||||||
|
double FirstDayAt = 0;
|
||||||
|
double LastDayAt = 0;
|
||||||
|
double DaySpan = 0;
|
||||||
|
|
||||||
|
graph TotalGraph = {0};
|
||||||
|
graph RecentGraph = {0};
|
||||||
|
|
||||||
|
WithErrors.FastestMs = 0xFFFFFFFF;
|
||||||
|
NoErrors.FastestMs = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
if(Array.EntryCount >= 2)
|
||||||
|
{
|
||||||
|
long long unsigned SecondD = SecondDifference(Array.Entries[Array.EntryCount - 1].StartDate, Array.Entries[0].StartDate);
|
||||||
|
DaySpanCount = (int unsigned)(SecondD / (60 * 60 * 24));
|
||||||
|
|
||||||
|
FirstDayAt = (double)DayIndex(Array.Entries[0].StartDate);
|
||||||
|
LastDayAt = (double)DayIndex(Array.Entries[Array.EntryCount - 1].StartDate);
|
||||||
|
DaySpan = (LastDayAt - FirstDayAt);
|
||||||
|
}
|
||||||
|
DaySpan += 1;
|
||||||
|
|
||||||
|
for(EntryIndex = 0;
|
||||||
|
EntryIndex < Array.EntryCount;
|
||||||
|
++EntryIndex, ++Entry)
|
||||||
|
{
|
||||||
|
if(Entry->Flags & TFEF_Complete)
|
||||||
|
{
|
||||||
|
stat_group *Group = (Entry->Flags & TFEF_NoErrors) ? &NoErrors : &WithErrors;
|
||||||
|
|
||||||
|
int unsigned ThisDayIndex = DayIndex(Entry->StartDate);
|
||||||
|
if(LastDayIndex != ThisDayIndex)
|
||||||
|
{
|
||||||
|
LastDayIndex = ThisDayIndex;
|
||||||
|
++DaysWithTimingCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateStatGroup(Group, Entry);
|
||||||
|
UpdateStatGroup(&AllStats, Entry);
|
||||||
|
|
||||||
|
AllMs += (double)Entry->MillisecondsElapsed;
|
||||||
|
|
||||||
|
{
|
||||||
|
int GraphIndex = (int)(((double)(ThisDayIndex-FirstDayAt)/DaySpan)*(double)GRAPH_WIDTH);
|
||||||
|
UpdateStatGroup(TotalGraph.Buckets + GraphIndex, Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int GraphIndex = (int)(ThisDayIndex - (LastDayAt - GRAPH_WIDTH + 1));
|
||||||
|
if(GraphIndex >= 0)
|
||||||
|
{
|
||||||
|
UpdateStatGroup(RecentGraph.Buckets + GraphIndex, Entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++IncompleteCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "\n%s Statistics\n\n", TimingFileName);
|
||||||
|
fprintf(stdout, "Total complete timings: %d\n", WithErrors.Count + NoErrors.Count);
|
||||||
|
fprintf(stdout, "Total incomplete timings: %d\n", IncompleteCount);
|
||||||
|
fprintf(stdout, "Days with timings: %d\n", DaysWithTimingCount);
|
||||||
|
fprintf(stdout, "Days between first and last timing: %d\n", DaySpanCount);
|
||||||
|
PrintStatGroup("Timings marked successful", &NoErrors);
|
||||||
|
PrintStatGroup("Timings marked failed", &WithErrors);
|
||||||
|
|
||||||
|
PrintGraph("All", (LastDayAt - FirstDayAt), &TotalGraph);
|
||||||
|
PrintGraph("Recent", GRAPH_WIDTH, &RecentGraph);
|
||||||
|
|
||||||
|
fprintf(stdout, "\nTotal time spent: ");
|
||||||
|
PrintTime(AllMs);
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int ArgCount, char **Args)
|
||||||
|
{
|
||||||
|
// TODO(casey): It would be nice if this supported 64-bit file sizes, but I can't really
|
||||||
|
// tell right now if "ANSI C" supports this. I feel like it should by now, but the
|
||||||
|
// MSVC docs seem to suggest you have to use __int64 to do 64-bit stuff with the CRT
|
||||||
|
// low-level IO routines, and I'm pretty sure that isn't a portable type :(
|
||||||
|
|
||||||
|
// NOTE(casey): We snap the clock time right on entry, to minimize any overhead on
|
||||||
|
// "end" times that might occur from opening the file.
|
||||||
|
int unsigned EntryClock = GetClock();
|
||||||
|
|
||||||
|
if((ArgCount == 3) || (ArgCount == 4))
|
||||||
|
{
|
||||||
|
char *Mode = Args[1];
|
||||||
|
int ModeIsBegin = (strcmp(Mode, "-begin") == 0);
|
||||||
|
char *TimingFileName = Args[2];
|
||||||
|
timing_file_header Header = {0};
|
||||||
|
|
||||||
|
FILE* Handle = fopen(TimingFileName, "r+b");
|
||||||
|
if(Handle != NULL)
|
||||||
|
{
|
||||||
|
// NOTE(casey): The file exists - check the magic value
|
||||||
|
fread(&Header, sizeof(Header), 1, Handle);
|
||||||
|
if(Header.MagicValue == MAGIC_VALUE)
|
||||||
|
{
|
||||||
|
// NOTE(casey): The file is at least nominally valid.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to verify that \"%s\" is actually a ctime-compatible file.\n", TimingFileName);
|
||||||
|
|
||||||
|
fclose(Handle);
|
||||||
|
Handle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(ModeIsBegin)
|
||||||
|
{
|
||||||
|
// NOTE(casey): The file doesn't exist and we're starting a new timing, so create it.
|
||||||
|
|
||||||
|
Handle = fopen(TimingFileName, "w+b");
|
||||||
|
if(Handle != NULL)
|
||||||
|
{
|
||||||
|
Header.MagicValue = MAGIC_VALUE;
|
||||||
|
if(fwrite(&Header, sizeof(Header), 1, Handle) == 1)
|
||||||
|
{
|
||||||
|
// NOTE(casey): File creation was (presumably) successful.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to write header to \"%s\".\n", TimingFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to create timing file \"%s\".\n", TimingFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Handle != NULL)
|
||||||
|
{
|
||||||
|
if(ModeIsBegin)
|
||||||
|
{
|
||||||
|
timing_file_entry NewEntry = {0};
|
||||||
|
NewEntry.StartDate = GetDate();
|
||||||
|
NewEntry.MillisecondsElapsed = GetClock();
|
||||||
|
if((fseek(Handle, 0, SEEK_END) == 0) &&
|
||||||
|
(fwrite(&NewEntry, sizeof(NewEntry), 1, Handle) == 1))
|
||||||
|
{
|
||||||
|
// NOTE(casey): Timer begin entry was written successfully.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to append new entry to file \"%s\".\n", TimingFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(Mode, "-end") == 0)
|
||||||
|
{
|
||||||
|
timing_file_entry LastEntry = {0};
|
||||||
|
if((fseek(Handle, -(int)sizeof(timing_file_entry), SEEK_END) == 0) &&
|
||||||
|
(fread(&LastEntry, sizeof(LastEntry), 1, Handle) == 1))
|
||||||
|
{
|
||||||
|
if(!(LastEntry.Flags & TFEF_Complete))
|
||||||
|
{
|
||||||
|
int unsigned StartClockD = LastEntry.MillisecondsElapsed;
|
||||||
|
int unsigned EndClockD = EntryClock;
|
||||||
|
LastEntry.Flags |= TFEF_Complete;
|
||||||
|
LastEntry.MillisecondsElapsed = 0;
|
||||||
|
if(StartClockD < EndClockD)
|
||||||
|
{
|
||||||
|
LastEntry.MillisecondsElapsed = (EndClockD - StartClockD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ArgCount == 3) ||
|
||||||
|
((ArgCount == 4) && (atoi(Args[3]) == 0)))
|
||||||
|
{
|
||||||
|
LastEntry.Flags |= TFEF_NoErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((fseek(Handle, -(int)sizeof(timing_file_entry), SEEK_END) == 0) &&
|
||||||
|
(fwrite(&LastEntry, sizeof(LastEntry), 1, Handle) == 1))
|
||||||
|
{
|
||||||
|
fprintf(stdout, "CTIME: ");
|
||||||
|
PrintTime(LastEntry.MillisecondsElapsed);
|
||||||
|
fprintf(stdout, " (%s)\n", TimingFileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to rewrite last entry to file \"%s\".\n", TimingFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Last entry in file \"%s\" is already closed - unbalanced/overlapped calls?\n", TimingFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unable to read last entry from file \"%s\".\n", TimingFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(Mode, "-stats") == 0)
|
||||||
|
{
|
||||||
|
timing_entry_array Array = ReadAllEntries(Handle);
|
||||||
|
Stats(Array, TimingFileName);
|
||||||
|
FreeAllEntries(Array);
|
||||||
|
}
|
||||||
|
else if(strcmp(Mode, "-csv") == 0)
|
||||||
|
{
|
||||||
|
timing_entry_array Array = ReadAllEntries(Handle);
|
||||||
|
CSV(Array, TimingFileName);
|
||||||
|
FreeAllEntries(Array);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Unrecognized command \"%s\".\n", Mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(Handle);
|
||||||
|
Handle = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Cannnot open file \"%s\".\n", TimingFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Usage();
|
||||||
|
}
|
||||||
|
}
|
|
@ -830,7 +830,7 @@ int main(int argc, char **argv) {
|
||||||
section = NULL;
|
section = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strendi(line, "/""//-")) continue; // discard explicitly excluded lines
|
if(strstr/*strendi*/(line, "/""//-")) continue; // discard explicitly excluded lines
|
||||||
|
|
||||||
learn(line);
|
learn(line);
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,7 @@ static void* editor_selected_obj = 0;
|
||||||
static int editor_key = 0;
|
static int editor_key = 0;
|
||||||
static vec2 editor_mouse = {0}; // 2d coord for ray/picking
|
static vec2 editor_mouse = {0}; // 2d coord for ray/picking
|
||||||
static bool editor_gamepad = 1;
|
static bool editor_gamepad = 1;
|
||||||
static int editor_hz = 144;
|
static int editor_hz = 60;
|
||||||
static int editor_hz_mid = 18;
|
static int editor_hz_mid = 18;
|
||||||
static int editor_hz_low = 5;
|
static int editor_hz_low = 5;
|
||||||
static bool editor_power_saving = 0;
|
static bool editor_power_saving = 0;
|
||||||
|
@ -1014,7 +1014,7 @@ void editor_render_menubar() {
|
||||||
break; case key_outliner: ui_show("Outliner", ui_visible("Outliner") ^ true);
|
break; case key_outliner: ui_show("Outliner", ui_visible("Outliner") ^ true);
|
||||||
break; case key_recording: name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string());
|
break; case key_recording: name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string());
|
||||||
break; case key_screenshot: name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
|
break; case key_screenshot: name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
|
||||||
break; case key_profiler: ui_show("Profiler", profile_enable(ui_visible("Profiler") ^ true));
|
break; case key_profiler: ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
|
||||||
break; case key_fullscreen: record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
|
break; case key_fullscreen: record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
|
||||||
break; case key_gamepad: editor_gamepad ^= 1;
|
break; case key_gamepad: editor_gamepad ^= 1;
|
||||||
break; case key_lit: editor_lit ^= 1;
|
break; case key_lit: editor_lit ^= 1;
|
||||||
|
@ -1040,12 +1040,12 @@ void editor_obj_render_properties_recursively(void *obj, const char *mask) {
|
||||||
|
|
||||||
// contextual menu (open)
|
// contextual menu (open)
|
||||||
if( ui_contextual() ) {
|
if( ui_contextual() ) {
|
||||||
if( ui_button_transparent("<Load" ) ) do_context_obj = obj, do_context_cmd = cc4(load);
|
if( ui_button_transparent("<Load" ) ) do_context_obj = obj, do_context_cmd = cc4(l,o,a,d);
|
||||||
if( ui_button_transparent("<Save" ) ) do_context_obj = obj, do_context_cmd = cc4(save);
|
if( ui_button_transparent("<Save" ) ) do_context_obj = obj, do_context_cmd = cc4(s,a,v,e);
|
||||||
if( ui_button_transparent("<Merge") ) do_context_obj = obj, do_context_cmd = cc4(merge);
|
if( ui_button_transparent("<Merge") ) do_context_obj = obj, do_context_cmd = cc5(m,e,r,g,e);
|
||||||
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc4(cut);
|
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc3(c,u,t);
|
||||||
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(copy);
|
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(c,o,p,y);
|
||||||
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc4(paste);
|
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc5(p,a,s,t,e);
|
||||||
ui_contextual_end();
|
ui_contextual_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,12 +1061,12 @@ void editor_obj_render_properties_recursively(void *obj, const char *mask) {
|
||||||
|
|
||||||
// contextual menu (close)
|
// contextual menu (close)
|
||||||
if( !open && ui_contextual() ) {
|
if( !open && ui_contextual() ) {
|
||||||
if( ui_button_transparent("<Load" ) ) do_context_obj = obj, do_context_cmd = cc4(load);
|
if( ui_button_transparent("<Load" ) ) do_context_obj = obj, do_context_cmd = cc4(l,o,a,d);
|
||||||
if( ui_button_transparent("<Save" ) ) do_context_obj = obj, do_context_cmd = cc4(save);
|
if( ui_button_transparent("<Save" ) ) do_context_obj = obj, do_context_cmd = cc4(s,a,v,e);
|
||||||
if( ui_button_transparent("<Merge") ) do_context_obj = obj, do_context_cmd = cc4(merge);
|
if( ui_button_transparent("<Merge") ) do_context_obj = obj, do_context_cmd = cc5(m,e,r,g,e);
|
||||||
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc4(cut);
|
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc3(c,u,t);
|
||||||
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(copy);
|
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(c,o,p,y);
|
||||||
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc4(paste);
|
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc5(p,a,s,t,e);
|
||||||
ui_contextual_end();
|
ui_contextual_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1218,7 +1218,7 @@ void editor_render_windows() {
|
||||||
void *k = *o;
|
void *k = *o;
|
||||||
editor_obj_render_properties_recursively(k, mask);
|
editor_obj_render_properties_recursively(k, mask);
|
||||||
}
|
}
|
||||||
if( do_context_cmd == cc4(list) && do_context_obj ) {
|
if( do_context_cmd == cc4(l,i,s,t) && do_context_obj ) {
|
||||||
printf("list [%p]\n", do_context_obj);
|
printf("list [%p]\n", do_context_obj);
|
||||||
}
|
}
|
||||||
// draw: depth + state (alpha0=off)
|
// draw: depth + state (alpha0=off)
|
||||||
|
@ -1330,7 +1330,7 @@ ray *editor_pickup() {
|
||||||
static
|
static
|
||||||
void editor_init() {
|
void editor_init() {
|
||||||
// enable outlines
|
// enable outlines
|
||||||
do_once fx_load("editor/art/fx/fxOutline.fs");
|
do_once fx_load("editorOutline.fs");
|
||||||
do_once fx_enable(0, 1);
|
do_once fx_enable(0, 1);
|
||||||
|
|
||||||
// init editor
|
// init editor
|
||||||
|
@ -1400,7 +1400,7 @@ void editor_camera_fps(void) {
|
||||||
view = add2(view, vec2(touch_view.x, -touch_view.y));
|
view = add2(view, vec2(touch_view.x, -touch_view.y));
|
||||||
|
|
||||||
// apply inputs
|
// apply inputs
|
||||||
camera_move(&cam, move.x,move.y,move.z);
|
camera_moveby(&cam, move);
|
||||||
camera_fps(&cam, view.x,view.y);
|
camera_fps(&cam, view.x,view.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue