main
Dominik Madarász 2023-11-19 14:12:48 +01:00
parent ceae5e2fe0
commit e807e4bef8
38 changed files with 892 additions and 1919 deletions

View File

@ -75,6 +75,13 @@ if "%1"=="cook" (
exit /b
)
if "%1"=="build_cook" (
pushd tools
cl cook.c -I..\engine /openmp /Os /Ox /O2 /Oy /MT /DNDEBUG /GL /GF /Gw /arch:AVX2 /link /OPT:ICF /LTCG
popd
exit /b
)
rem generate bindings
if "%1"=="bind" (
rem luajit
@ -324,7 +331,7 @@ if "%1"=="fwk_prep" (
xcopy /y "engine\split\3rd_*" "_fwk\engine\split"
xcopy /y "engine\art\shaders\*" "_fwk\engine\art\shaders"
xcopy /y "demos" "_fwk\demos"
xcopy /y "engine\editor.c" "_fwk\engine\editor.c"
copy /y "engine\editor.c" "_fwk\engine\editor.c"
rem xcopy /y/E "tools "_fwk\tools"
for %%f in ("engine\split\v4k*") do (
set "filename=%%~nf"
@ -371,6 +378,7 @@ if "%1"=="back" (
xcopy /y "_fwk" "."
xcopy /y "_fwk\engine\split\3rd_*" "engine\split"
xcopy /y "_fwk\engine\art\shaders\*" "engine\art\shaders"
del "demos\*.c"
xcopy /y "_fwk\demos" "demos"
xcopy /y "_fwk\engine\editor.c" "engine\editor.c"
tools\fwkren.exe "engine\editor.c" to

View File

@ -1,6 +1,8 @@
#include "v4k.h" // Minimal C sample
int main() {
window_create(75.0, 0); // 75% size, no extra flags
while( window_swap() && !input(KEY_ESC) ) { // game loop
puts("hello V4K from C!");
}

View File

@ -1,86 +0,0 @@
// hello ui: config, window, system, ui, video
// - rlyeh, public domain.
//
// Compile with:
// `make demos\01-ui.c` (windows)
// `sh MAKE.bat demos/01-ui.c` (linux, osx)
#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();
}
}
}
// this demo supersedes following old sources:
// https://github.com/r-lyeh/V4K/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-hello.c
// https://github.com/r-lyeh/V4K/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-video.c

View File

@ -1,218 +0,0 @@
// sprite routines
// - rlyeh,
//
// credits: original lovely demo by rxi (MIT License).
// see https://github.com/rxi/autobatch/tree/master/demo/cats
#include "v4k.h"
texture_t kids, catImage, shadowImage, inputs;
int NUM_SPRITES = 100, NUM_SPRITES_CHANGED = 1;
typedef struct Cat {
int cat, flip;
double x, y;
double vx, vy;
double animSpeed;
double moveTimer;
double elapsed;
} Cat;
void demo_cats() {
static array(Cat) cats = 0;
// init
if( NUM_SPRITES_CHANGED ) {
NUM_SPRITES_CHANGED = 0;
array_resize(cats, NUM_SPRITES); int i = 0;
for each_array_ptr(cats, Cat, c) {
randset(i++);
c->x = randf() * window_width();
c->y = randf() * window_height();
c->vx = c->vy = 0;
c->cat = randi(0, 4);
c->flip = randf() < 0.5;
c->animSpeed = 0.8 + randf() * 0.3;
c->moveTimer = 0;
c->elapsed = 0;
}
}
// move
const float dt = 1/120.f;
const int appw = window_width(), apph = window_height();
enum { yscale = 1 };
for( int i = 0; i < NUM_SPRITES; ++i ) {
Cat *c = &cats[i];
// Add velocity to position //and wrap to screen
c->x += yscale * c->vx * dt; // % ;
c->y += yscale * c->vy * dt; // % (int)window_height();
if( c->x < 0 ) c->x += appw; else if( c->x > appw ) c->x -= appw;
if( c->y < 0 ) c->y += apph; else if( c->y > apph ) c->y -= apph;
// Faster animation if walking
int boost = c->vx == 0 && c->vy == 0 ? 1 : 3;
// Update elapsed time
c->elapsed += dt * boost;
// Update move timer -- if we hit zero then change or zero velocity
c->moveTimer -= dt * boost;
if (c->moveTimer < 0) {
if (randf() < .2) {
c->vx = (randf() * 2 - 1) * 30 * 2;
c->vy = (randf() * 2 - 1) * 15 * 2;
c->flip = c->vx < 0;
} else {
c->vx = c->vy = 0;
}
c->moveTimer = 1 + randf() * 5;
}
}
// render
uint32_t white = rgba(255,255,255,255);
uint32_t alpha = rgba(255,255,255,255*0.6);
for( int i = 0; i < NUM_SPRITES; ++i ) {
Cat *c = &cats[i];
// Get current animation frame (8x4 tilesheet)
double e = c->elapsed * c->animSpeed;
double frame_num = c->cat * 8 + floor( ((int)(e * 8)) % 4 );
frame_num = c->vx != 0 || c->vy != 0 ? frame_num + 4 : frame_num;
// Get x scale based on flip flag
int xscale = yscale * (c->flip ? -1 : 1);
// Draw
float angle = 0; //fmod(window_time()*360/5, 360);
float scale[2] = { 2*xscale, 2*yscale };
float position[3] = { c->x,c->y,c->y }, no_offset[2] = {0,0}, spritesheet[3] = { frame_num,8,4 };
sprite_sheet(catImage,
spritesheet, // frame_number in a 8x4 spritesheet
position, angle, // position(x,y,depth: sort by Y), angle
no_offset, scale, // offset(x,y), scale(x,y)
0,white,0 // is_additive, tint color, resolution independant
);
float position_neg_sort[3] = { c->x,c->y,-c->y }, offset[2] = {-1,5}, no_spritesheet[3] = {0,0,0};
sprite_sheet(shadowImage,
no_spritesheet, // no frame_number (0x0 spritesheet)
position_neg_sort, angle, // position(x,y,depth: sort by Y), angle
offset, scale, // offset(x,y), scale(x,y)
0,alpha,0 // is_additive, tint color, resolution independant
);
}
}
void demo_kids() {
static int angle; //++angle;
static int *x, *y, *v;
// init
if( NUM_SPRITES_CHANGED ) {
NUM_SPRITES_CHANGED = 0;
y = (int*)REALLOC(y, 0 );
x = (int*)REALLOC(x, NUM_SPRITES * sizeof(int) );
y = (int*)REALLOC(y, NUM_SPRITES * sizeof(int) );
v = (int*)REALLOC(v, NUM_SPRITES * sizeof(int) );
for( int i = 0; i < NUM_SPRITES; ++i ) {
randset(i);
x[i] = randi(0, window_width());
y[i] = randi(0, window_height());
v[i] = randi(1, 3);
}
}
// config
const int appw = window_width(), apph = window_height();
// move & render
for( int i = 0; i < NUM_SPRITES; ++i ) {
y[i] = (y[i] + v[i]) % (apph + 128);
int col = ((x[i] / 10) % 4); // 4x4 tilesheet
int row = ((y[i] / 10) % 4);
int num_frame = col * 4 + row;
float position[3] = {x[i],y[i],y[i]}, offset[2]={0,0}, scale[2]={1,1}, spritesheet[3]={num_frame,4,4};
sprite_sheet(kids,
spritesheet, // num_frame in a 4x4 spritesheet
position, angle, // position(x,y,depth: sort by Y), angle
offset, scale, // offset(x,y), scale(x,y)
0, ~0u, 0 // is_additive, tint color, resolution independant
);
}
}
int main(int argc, char **argv) {
window_create(75.f, 0);
window_title("V4K - Sprite");
window_color( SILVER );
// options
int do_cats = 1;
NUM_SPRITES = optioni("--num_sprites,-N", NUM_SPRITES);
if(do_cats) NUM_SPRITES/=2; // cat-sprite+cat-shadow == 2 sprites
// load sprites and sheets
kids = texture( "spriteSheetExample.png", TEXTURE_LINEAR );
catImage = texture( "cat.png", TEXTURE_LINEAR ); //
shadowImage = texture( "cat-shadow.png", TEXTURE_LINEAR );
inputs = texture( "prompts_tilemap_34x24_16x16x1.png", TEXTURE_LINEAR );
// load all fx files, including subdirs
fx_load("fx**.fs");
// init camera (x,y) (z = zoom)
camera_t cam = camera();
cam.position = vec3(window_width()/2,window_height()/2,1);
camera_enable(&cam);
while(window_swap()) {
if( input(KEY_F5)) window_reload();
if( input(KEY_F11)) window_fullscreen( window_has_fullscreen() ^ 1);
if( input(KEY_ESC) ) break;
// camera panning (x,y) & zooming (z)
if( !ui_hover() && !ui_active() ) {
if( input(MOUSE_L) ) cam.position.x -= input_diff(MOUSE_X);
if( input(MOUSE_L) ) cam.position.y -= input_diff(MOUSE_Y);
cam.position.z += input_diff(MOUSE_W) * 0.1; // cam.p.z += 0.001f; for tests
}
// apply post-fxs from here
fx_begin();
profile("Sprite batching") {
if(do_cats) demo_cats(); else demo_kids();
}
// flush retained renderer, so we ensure the fbos are up to date before fx_end()
profile("Sprite flushing") {
sprite_flush();
}
// post-fxs end here
fx_end();
// draw pixel-art hud, 16x16 ui element, scaled and positioned in resolution-independant way
{
vec3 old_pos = camera_get_active()->position;
sprite_flush();
camera_get_active()->position = vec3(window_width()/2,window_height()/2,1);
float zindex = window_height(); // large number, on top
float spritesheet[3] = {17,34,24}, offset[2] = {0, - 2*absf(sin(window_time()*5))}; // sprite cell and animation
float scale[2] = {3, 3}, tile_w = 16 * scale[0], tile_h = 16 * scale[1]; // scaling
float position[3] = {window_width() - tile_w, window_height() - tile_h, zindex }; // position in screen-coordinates
sprite_sheet(inputs, spritesheet, position, 0/*rotation*/, offset, scale, false/*is_additive*/, WHITE/*color*/, false/*resolution_independant*/);
sprite_flush();
camera_get_active()->position = old_pos;
}
if( ui_panel("Sprite", 0) ) {
const char *labels[] = {"Kids","Cats"};
if( ui_list("Sprite type", labels, countof(labels), &do_cats) ) NUM_SPRITES_CHANGED = 1;
if( ui_int("Number of Sprites", &NUM_SPRITES) ) NUM_SPRITES_CHANGED = 1;
if( ui_clampf("Zoom", &cam.position.z, 0.1, 10));
ui_panel_end();
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,216 +0,0 @@
// scene demo
// - rlyeh, public domain
#include "v4k.h"
int main() {
// options
bool do_twosided = 1;
bool do_wireframe = 0;
bool do_billboard_x = 1, do_billboard_y = 1, do_billboard_z = 1;
// window (80% sized, MSAA x4 flag)
window_create(80, WINDOW_MSAA4);
window_title(__FILE__);
// load all postfx files in all subdirs
fx_load("fx**.fs");
// scene loading
#define SCENE(...) #__VA_ARGS__
const char *my_scene = SCENE([
{
skybox: 'cubemaps/stardust/',
},
{
position:[-5.0,-2.0,2.0],
rotation: [90.0,0.0,180.0],
scale:0.20,
//anchor/pivot:[],
// vertex:'p3 t2',
mesh:'models/witch/witch.obj',
texture:'models/witch/witch_diffuse.tga.png',
// swapzy:true,
flipuv:false,
},
{
position:[-5.0,-2.0,2.0],
rotation: [90.0,0.0,180.0],
scale:2.20,
//anchor/pivot:[],
// vertex:'p3 t2',
mesh:'models/witch/witch_object.obj',
texture:'models/witch/witch_object_diffuse.tga.png',
// swapzy:true,
flipuv:false,
},
]);
int num_spawned = scene_merge(my_scene);
object_t *obj1 = scene_index(0);
object_t *obj2 = scene_index(1);
// manual spawn & loading
model_t m1 = model("kgirl/kgirls01.fbx", 0); //MODEL_NO_ANIMS);
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_RGB);
object_t* obj3 = scene_spawn();
object_model(obj3, m1);
object_diffuse(obj3, t1);
object_scale(obj3, vec3(3,3,3));
object_move(obj3, vec3(-10,0,-10));
object_pivot(obj3, vec3(-90+180,180,0));
// camera
camera_t cam = camera();
// demo loop
while (window_swap())
{
// input
if( input_down(KEY_ESC) ) break;
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
window_cursor( !active );
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
// queue model scale bounces
float t = fmod(window_time(), 0.3) / 0.3;
float s = 0.01f * ease_ping_pong(t, EASE_IN|EASE_CUBIC,EASE_OUT|EASE_CUBIC);
object_scale(obj1, vec3(0.20f - s,0.20f + s,0.20f - s));
object_scale(obj2, vec3(0.20f - s,0.20f + s,0.20f - s));
// queue model billboard
object_billboard(obj1, (do_billboard_x << 2)|(do_billboard_y << 1)|(do_billboard_z << 0));
object_billboard(obj2, (do_billboard_x << 2)|(do_billboard_y << 1)|(do_billboard_z << 0));
// queue model rotation
//object_rotate(obj3, vec3(0,1*window_time() * 20,0));
// flush render scene (background objects: skybox)
profile("Scene background") {
scene_render(SCENE_BACKGROUND);
}
// queue debug drawcalls
profile("Debugdraw") {
ddraw_ground(0);
ddraw_color(YELLOW);
ddraw_text(vec3(+1,+1,-1), 0.04f, va("(%f,%f,%f)", cam.position.x,cam.position.y,cam.position.z));
ddraw_color(YELLOW);
ddraw_flush();
}
// apply post-fxs from here
fx_begin();
// render scene (foreground objects) with post-effects
profile("Scene foreground") {
int scene_flags = 0;
scene_flags |= do_wireframe ? SCENE_WIREFRAME : 0;
scene_flags |= do_twosided ? 0 : SCENE_CULLFACE;
scene_render(SCENE_FOREGROUND | scene_flags);
}
profile("Skeletal update") if(!window_has_pause()) {
float delta = window_delta() * 30 ; // 30fps anim
m1.curframe = model_animate(m1, m1.curframe + delta);
ddraw_text(vec3(-10,5,-10), 0.05, va("Frame: %.1f", m1.curframe));
}
// post-fxs end here
fx_end();
// queue ui
if( ui_panel("Scene", 0)) {
if(ui_toggle("Billboard X", &do_billboard_x)) {}
if(ui_toggle("Billboard Y", &do_billboard_y)) {}
if(ui_toggle("Billboard Z", &do_billboard_z)) {}
if(ui_separator()) {}
if(ui_bool("Wireframe", &do_wireframe)) {}
if(ui_bool("Two sided", &do_twosided)) {}
ui_panel_end();
}
}
}
#if 0
// ----------------------------------------------------------------------------
// material demo
// - rlyeh, public domain
//
// @todo: object_print(obj, "");
// create camera
camera_t cam = camera();
// load video, RGB texture, no audio
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_NO_AUDIO | VIDEO_LOOP ); video_seek(v, 30);
// load texture
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_RGB);
texture_t t2 = texture("matcaps/material3", 0);
// load model
model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS);
model_t m2 = model("suzanne.obj", MODEL_NO_ANIMATIONS|MODEL_MATCAPS);
// spawn object1 (diffuse)
object_t* obj1 = scene_spawn();
object_model(obj1, m1);
object_diffuse(obj1, t1);
object_scale(obj1, vec3(3,3,3));
object_move(obj1, vec3(-10+5*0,0,-10));
object_pivot(obj1, vec3(0,90,0));
// spawn object2 (matcap)
object_t* obj2 = scene_spawn();
object_model(obj2, m2);
object_diffuse(obj2, t2);
object_scale(obj2, vec3(3,3,3));
object_move(obj2, vec3(-10+5*2,0,-10));
object_pivot(obj2, vec3(0,90,0));
// spawn object2 (video)
object_t* obj3 = scene_spawn();
object_model(obj3, m1);
object_diffuse(obj3, video_textures(v)[0]);
object_scale(obj3, vec3(3,3,3));
object_move(obj3, vec3(-10+5*1,0,-10));
object_pivot(obj3, vec3(0,90,0));
// @todo: add shadertoy material
static model_t cube; do_once cube = model("cube.obj", 0);
static shadertoy_t s; do_once s = shadertoy("shadertoys/4ttGWM.fs", 256);
model_set_texture(cube, shadertoy_render(&s, window_delta())->tx);
model_render(cube, cam.proj, cam.view, cube.pivot, 0);
while(window_swap() && !input(KEY_ESC)) {
// draw environment
viewport_color( RGB3(22,22,32) );
ddraw_grid(0);
ddraw_flush();
// update video
video_decode( v );
// draw scene
scene_render(SCENE_FOREGROUND);
}
// load static scene
// model_t sponza = model("sponza.obj", MODEL_MATCAPS);
// model_set_texture(sponza, texture("matcaps/normals", 0));
// translation44(sponza.pivot, 0,-1,0);
// rotate44(sponza.pivot, -90,1,0,0);
// scale44(sponza.pivot, 10,10,10);
// model_render(sponza, cam.proj, cam.view, sponza.pivot, 0);
// this demo supersedes following old sources:
// https://github.com/r-lyeh/V4K/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-material.c
// https://github.com/r-lyeh/V4K/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-shadertoy.c
#endif

View File

@ -1,280 +0,0 @@
// full controller demo: anims, input, collide; @todo: gamepad, input opts, easing on hits, notify on gamepad connect
// - rlyeh, public domain.
//
// Compile with:
// `make demos\99-controller.c` (windows)
// `sh MAKE.bat demos/99-controller.c` (linux, osx)
#include "v4k.h"
int main() {
// 75% window, MSAAx2 flag
window_create(75, WINDOW_MSAA2);
// fx: load all post fx files in all subdirs
fx_load("fx**.fs");
// create a camera
camera_t cam = camera();
camera_enable(&cam);
// config 3d model #1
model_t witch = model("witch/witch.obj", 0);
model_set_texture(witch, texture("witch/witch_diffuse.tga.png", 0));
mat44 witch_pivot; vec3 witch_p = {-5,0,-5}, witch_r={-180,180,0}, witch_s={0.1,-0.1,0.1};
// config 3d model #2
model_t girl = model("kgirl/kgirls01.fbx", 0);
mat44 girl_pivot; vec3 girl_p = {0,0,0}, girl_r = {270,0,0}, girl_s = {2,2,2};
// skybox
skybox_t sky = skybox("cubemaps/stardust", 0);
// BGM
audio_play( audio_stream("waterworld-map.fur"), 0 );
// editor loop
while( window_swap() ) {
// game camera
profile("Game.Camera") {
camera_t *cam = camera_get_active();
static vec3 source;
do_once source = cam->position;
vec3 target = add3(girl_p, vec3(0,10,0));
target = add3(target, scale3(norm3(sub3(source, target)), 10.0));
source = mix3(source, target, 1-0.99f);
camera_teleport(cam, source);
camera_lookat(cam, vec3(girl_p.x,0,girl_p.z));
}
// render begin (postfx)
fx_begin();
// skybox
skybox_render(&sky, cam.proj, cam.view);
// world
ddraw_grid(0);
ddraw_flush();
// models
compose44(girl.pivot, girl_p, eulerq(girl_r), girl_s);
model_render(girl, cam.proj, cam.view, girl.pivot, 0);
compose44(witch.pivot, witch_p, eulerq(witch_r), witch_s);
model_render(witch, cam.proj, cam.view, witch.pivot, 0);
// render end (postfx)
fx_end();
// input controllers
double GAME_JUMP_DOWN = input_down(KEY_Z);
double GAME_FIRE_DOWN = input_down(KEY_X);
double GAME_JUMP = input(KEY_Z);
double GAME_FIRE = input(KEY_X);
double GAME_LEFT = input(KEY_J);
double GAME_RIGHT = input(KEY_L);
double GAME_UP = input(KEY_I);
double GAME_DOWN = input(KEY_K);
double GAME_AXISX = input(KEY_L) - input(KEY_J);
double GAME_AXISY = input(KEY_I) - input(KEY_K);
if( !input_anykey() ) {
if( input(GAMEPAD_CONNECTED) ) {
vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f /*15% deadzone*/);
GAME_JUMP_DOWN = input_down(GAMEPAD_A);
GAME_FIRE_DOWN = input_down(GAMEPAD_B) || input_down(GAMEPAD_X) || input_down(GAMEPAD_Y);
GAME_JUMP = input(GAMEPAD_A);
GAME_FIRE = input(GAMEPAD_B) || input(GAMEPAD_X) || input(GAMEPAD_Y);
GAME_AXISX = filtered_lpad.x;
GAME_AXISY = filtered_lpad.y;
}
}
// animation controllers
profile("Game.Animate scene") if( !window_has_pause() ) {
float delta = window_delta() * 30; // 30fps anim
// animate girl
girl.curframe = model_animate(girl, girl.curframe + delta);
// jump controller: jump duration=1.5s, jump height=6 units, anims (expo->circ)
float jump_delta = 1.0;
static double jump_timer = 0, jump_ss = 1.5, jump_h = 6;
if( GAME_JUMP_DOWN ) if( jump_timer == 0 ) jump_timer = time_ss();
jump_delta = clampf(time_ss() - jump_timer, 0, jump_ss) * (1.0/jump_ss);
if( jump_delta >= 1 ) { jump_timer = 0; }
float y = ease_ping_pong( jump_delta, EASE_OUT|EASE_EXPO, EASE_OUT|EASE_CIRC);
girl_p.y = y * jump_h;
// punch controller
float punch_delta = 1;
if( jump_delta >= 1 ) {
static vec3 origin;
static double punch_timer = 0, punch_ss = 0.5;
if( GAME_FIRE_DOWN ) if( punch_timer == 0 ) punch_timer = time_ss(), origin = girl_p;
punch_delta = clampf(time_ss() - punch_timer, 0, punch_ss) * (1.0/punch_ss);
if( punch_delta >= 1 ) { punch_timer = 0; }
else {
float x = ease_out_expo( punch_delta );
vec3 fwd = rotate3q(vec3(0,0,1), eulerq(vec3(girl_r.x - 170,girl_r.y,girl_r.z)));
vec3 mix = mix3(girl_p, add3(origin,scale3(fwd,x*2)), x);
girl_p.x = mix.x, girl_p.z = mix.z;
}
}
int modern_controller = 1;
int running = 0;
// girl controller
// locomotion vars
float speed = 0.2f * delta;
float yaw_boost = GAME_AXISY > 0 ? 1.0 : 1.75;
if(punch_delta < 1) yaw_boost = 0.0; // if firing...
else if(punch_delta <= 0.1) yaw_boost = 4.0; // unless initial punch chaining, extra yaw
// old fashioned locomotion controller (boat controller)
if(!modern_controller) {
running = GAME_AXISY > 0;
girl_r.x -= 170;
quat q = eulerq(girl_r); // += custom.pivot
vec3 rgt = rotate3q(vec3(1,0,0), q);
vec3 up = rotate3q(vec3(0,1,0), q);
vec3 fwd = rotate3q(vec3(0,0,1), q);
vec3 dir = scale3(fwd, speed * GAME_AXISY * (GAME_AXISY > 0 ? 2.0 : 0.5));
girl_r.x += speed * 20.0 * yaw_boost * GAME_AXISX; // yaw
girl_p = add3(girl_p, dir);
girl_r.x += 170;
}
// modern locomotion controller (mario 3d)
if(modern_controller) {
running = GAME_AXISX != 0 || GAME_AXISY != 0;
camera_t *cam = camera_get_active();
vec3 fwd = sub3(girl_p, cam->position); fwd.y = 0; fwd = norm3(fwd);
vec3 rgt = norm3(cross3(fwd, vec3(0,1,0)));
// target
vec3 dir = add3(
scale3(fwd, GAME_AXISY),
scale3(rgt, GAME_AXISX)
); dir.y = 0; dir = norm3(dir);
// smoothing
static vec3 olddir; do_once olddir = dir;
dir = mix3(dir, olddir, 1 - (yaw_boost / 4.0) * 0.85);
olddir = dir;
// vis
// ddraw_arrow(girl_p, add3(girl_p,scale3(dir,10)));
// apply direction
girl_p = add3(girl_p, scale3(dir, speed * 2));
// apply rotation
{
girl_r.x -= 170;
quat q = eulerq(girl_r);
vec3 fwdg = rotate3q(vec3(0,0,1), q);
girl_r.x += 170;
//float cosAngle = dot3(dir,fwdg);
//float angle = acos(cosAngle) * TO_DEG;
float angle = TO_DEG * ( atan2(fwdg.z, fwdg.x) - atan2(dir.z, dir.x));
if( !isnan(angle) ) {
girl_r.x -= angle;
while(girl_r.x> 180) girl_r.x-=360;
while(girl_r.x<-180) girl_r.x+=360;
}
}
}
// anim loops
if( jump_delta < 1 ) { // jump/kick anim
#if 0
girl.curframe = clampf(girl.curframe, 184, 202);
if( girl.curframe > 202-4 && GAME_FIRE_DOWN ) girl.curframe = 184+4;
#else
#define loopf(frame, min, max) (frame < min ? min : frame > max ? min + frame - max : frame)
if(girl.curframe >= 203)
girl.curframe = loopf(girl.curframe, 203, 220);
else
girl.curframe = clampf(girl.curframe, 184, 202);
if( girl.curframe > 202-4 && girl.curframe < 208 && GAME_FIRE_DOWN ) girl.curframe = 203;
#endif
}
else if( punch_delta < 1 ) { // punch anim
girl.curframe = clampf(girl.curframe, 90, 101);
if( girl.curframe > 101-6 && GAME_FIRE_DOWN ) girl.curframe = 101-6;
}
else if( running ) {
// loop running anim
if( girl.curframe < 65 ) girl.curframe = 65;
if( girl.curframe > 85 ) girl.curframe = 65;
}
else { // loop idle anim
if( girl.curframe > 59 ) girl.curframe = 0;
}
}
// Game collisions
profile("Game.collisions") {
bool punching = girl.curframe >= 90 && girl.curframe < 101;
bool air_kicking = girl.curframe >= 184 && girl.curframe < 202;
bool jump_kicking = girl.curframe >= 203 && girl.curframe < 220;
bool attacking = punching || air_kicking || jump_kicking;
if( attacking ) {
aabb boxg = model_aabb(girl, girl_pivot);
aabb boxw = model_aabb(witch, witch_pivot);
#if 0 // halve aabb. ok
{
vec3 diag = sub3(boxg.max, boxg.min);
vec3 halve = scale3(diag, 0.25);
vec3 center = scale3(add3(boxg.min, boxg.max), 0.5);
boxg.min = sub3(center, halve);
boxg.max = add3(center, halve);
}
#endif
hit* h = aabb_hit_aabb(boxg, boxw);
if( h && GAME_FIRE ) {
vec3 dir = norm3(sub3(witch_p, girl_p));
witch_p = add3(witch_p, mul3(dir,vec3(1,0,1)));
}
}
}
// ui
if( ui_panel("Input", 0) ) { // @todo: showcase input binding
ui_section("Controllers");
ui_label("Gamepad #1");
ui_label("Keys I/J/K/L + Z/X");
ui_panel_end();
}
}
}
// vec2 do_gamepad_polarity = vec2(+1,+1);
// vec2 do_gamepad_sensitivity = vec2(0.1f,0.1f);
// vec2 do_mouse_polarity = vec2(+1,-1);
// vec2 do_mouse_sensitivity = vec2(0.2f,0.2f);
// float do_gamepad_deadzone = 0.15f;//
//
// if(ui_separator()) {}
// if(ui_slider("Gamepad deadzone", &do_gamepad_deadzone)) {}
// if(ui_float2("Gamepad polarity", do_gamepad_polarity.v2)) {}
// if(ui_float2("Gamepad sensitivity", do_gamepad_sensitivity.v2)) {}
// if(ui_separator()) {}
// if(ui_float2("Mouse polarity", do_mouse_polarity.v2)) {}
// if(ui_float2("Mouse sensitivity", do_mouse_sensitivity.v2)) {}//

View File

@ -1,877 +0,0 @@
#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){60,60,60,192}) // 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 64.0f
#define GRID_COLOR ((struct nk_color)NK_RGB3(80,80,120))
#define GRID_THICKNESS 1.0f
// 4 colors: top-left, top-right, bottom-right, bottom-left
#define GRID_BG_COLORS ((struct nk_color){30,30,30,255}), ((struct nk_color){40,20,0,255}), ((struct nk_color){30,30,30,255}), ((struct nk_color){20,30,40,255})
#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;
//nk_fill_rect(canvas, size, 0/*rounding*/, ((struct nk_color){30,30,30,255})); // 20,30,40,255
nk_fill_rect_multi_color(canvas, size, GRID_BG_COLORS);
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();
}
}
}

View File

@ -0,0 +1,123 @@
https://pixelfrog-assets.itch.io/treasure-hunters
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@ -0,0 +1,98 @@
goblins.png
size: 1024, 128
filter: Linear, Linear
pma: true
dagger
bounds: 2, 18, 26, 108
goblin/eyes-closed
bounds: 2, 4, 34, 12
goblin/head
bounds: 113, 23, 103, 66
rotate: 90
goblin/left-arm
bounds: 937, 89, 37, 35
rotate: 90
goblin/left-foot
bounds: 609, 61, 65, 31
rotate: 90
goblin/left-hand
bounds: 840, 21, 36, 41
goblin/left-lower-leg
bounds: 504, 56, 33, 70
goblin/left-shoulder
bounds: 745, 17, 29, 44
goblin/left-upper-leg
bounds: 397, 53, 33, 73
goblin/neck
bounds: 862, 85, 36, 41
goblin/pelvis
bounds: 776, 18, 62, 43
goblin/right-arm
bounds: 181, 5, 23, 50
rotate: 90
goblin/right-foot
bounds: 747, 63, 63, 33
rotate: 90
goblin/right-hand
bounds: 878, 3, 36, 37
goblin/right-lower-leg
bounds: 321, 50, 36, 76
goblin/right-shoulder
bounds: 663, 14, 39, 45
goblin/right-upper-leg
bounds: 675, 63, 34, 63
goblin/torso
bounds: 181, 30, 68, 96
goblin/undie-straps
bounds: 38, 2, 55, 19
goblin/undies
bounds: 974, 97, 36, 29
goblingirl/eyes-closed
bounds: 397, 30, 37, 21
goblingirl/head
bounds: 30, 23, 103, 81
rotate: 90
goblingirl/left-arm
bounds: 916, 8, 37, 35
rotate: 90
goblingirl/left-foot
bounds: 642, 61, 65, 31
rotate: 90
goblingirl/left-hand
bounds: 900, 86, 35, 40
goblingirl/left-lower-leg
bounds: 539, 56, 33, 70
goblingirl/left-shoulder
bounds: 633, 13, 28, 46
goblingirl/left-upper-leg
bounds: 574, 56, 33, 70
goblingirl/neck
bounds: 878, 42, 35, 41
goblingirl/pelvis
bounds: 817, 64, 62, 43
rotate: 90
goblingirl/right-arm
bounds: 603, 4, 28, 50
goblingirl/right-foot
bounds: 782, 63, 63, 33
rotate: 90
goblingirl/right-hand
bounds: 915, 47, 36, 37
goblingirl/right-lower-leg
bounds: 359, 50, 36, 76
goblingirl/right-shoulder
bounds: 704, 16, 39, 45
goblingirl/right-upper-leg
bounds: 711, 63, 34, 63
goblingirl/torso
bounds: 251, 30, 68, 96
goblingirl/undie-straps
bounds: 95, 2, 55, 19
goblingirl/undies
bounds: 974, 66, 36, 29
shield
bounds: 432, 54, 70, 72
spear
bounds: 233, 6, 22, 368
rotate: 90

View File

@ -0,0 +1,627 @@
{
"skeleton": {
"hash": "djttFmlR6Co",
"spine": "4.1.17",
"x": -92.53,
"y": -5.3,
"width": 234.03,
"height": 354.91,
"images": "./images/",
"audio": ""
},
"bones": [
{ "name": "root" },
{ "name": "hip", "parent": "root", "x": 0.65, "y": 114.41, "color": "ffd300ff" },
{
"name": "torso",
"parent": "hip",
"length": 85.83,
"rotation": 93.93,
"x": -6.42,
"y": 1.98,
"color": "ffd300ff"
},
{
"name": "neck",
"parent": "torso",
"length": 18.38,
"rotation": -1.52,
"x": 81.68,
"y": -6.35,
"color": "ffd300ff"
},
{
"name": "head",
"parent": "neck",
"length": 68.29,
"rotation": -13.92,
"x": 20.94,
"y": 11.59,
"color": "ffd300ff"
},
{
"name": "left-shoulder",
"parent": "torso",
"length": 35.43,
"rotation": -156.96,
"x": 74.05,
"y": -20.39,
"color": "ff0000ff"
},
{
"name": "left-arm",
"parent": "left-shoulder",
"length": 35.62,
"rotation": 28.17,
"x": 37.86,
"y": -2.35,
"color": "ff0000ff"
},
{
"name": "left-upper-leg",
"parent": "hip",
"length": 50.4,
"rotation": -89.1,
"x": 14.45,
"y": 2.81,
"color": "ff0000ff"
},
{
"name": "left-lower-leg",
"parent": "left-upper-leg",
"length": 49.9,
"rotation": -16.66,
"x": 56.34,
"y": 0.99,
"color": "ff0000ff"
},
{
"name": "left-foot",
"parent": "left-lower-leg",
"length": 46.5,
"rotation": 102.43,
"x": 58.94,
"y": -7.61,
"color": "ff0000ff"
},
{
"name": "left-hand",
"parent": "left-arm",
"length": 11.52,
"rotation": 2.7,
"x": 35.62,
"y": 0.08,
"color": "ff0000ff"
},
{ "name": "pelvis", "parent": "hip", "x": 1.41, "y": -6.58, "color": "ffd300ff" },
{
"name": "right-shoulder",
"parent": "torso",
"length": 37.25,
"rotation": 133.89,
"x": 76.02,
"y": 18.15,
"color": "54ff00ff"
},
{
"name": "right-arm",
"parent": "right-shoulder",
"length": 36.75,
"rotation": 36.33,
"x": 37.61,
"y": 0.31,
"color": "54ff00ff"
},
{
"name": "right-upper-leg",
"parent": "hip",
"length": 42.46,
"rotation": -97.5,
"x": -20.08,
"y": -6.84,
"color": "54ff00ff"
},
{
"name": "right-lower-leg",
"parent": "right-upper-leg",
"length": 58.53,
"rotation": -14.34,
"x": 43,
"y": -0.62,
"color": "54ff00ff"
},
{
"name": "right-foot",
"parent": "right-lower-leg",
"length": 45.46,
"rotation": 110.31,
"x": 64.89,
"y": 0.04,
"color": "54ff00ff"
},
{
"name": "right-hand",
"parent": "right-arm",
"length": 15.32,
"rotation": 2.36,
"x": 36.9,
"y": 0.35,
"color": "54ff00ff"
}
],
"slots": [
{ "name": "left-shoulder", "bone": "left-shoulder", "attachment": "left-shoulder" },
{ "name": "left-arm", "bone": "left-arm", "attachment": "left-arm" },
{ "name": "left-hand-item", "bone": "left-hand", "attachment": "spear" },
{ "name": "left-hand", "bone": "left-hand", "attachment": "left-hand" },
{ "name": "left-foot", "bone": "left-foot", "attachment": "left-foot" },
{ "name": "left-lower-leg", "bone": "left-lower-leg", "attachment": "left-lower-leg" },
{ "name": "left-upper-leg", "bone": "left-upper-leg", "attachment": "left-upper-leg" },
{ "name": "neck", "bone": "neck", "attachment": "neck" },
{ "name": "torso", "bone": "torso", "attachment": "torso" },
{ "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" },
{ "name": "right-foot", "bone": "right-foot", "attachment": "right-foot" },
{ "name": "right-lower-leg", "bone": "right-lower-leg", "attachment": "right-lower-leg" },
{ "name": "undie-straps", "bone": "pelvis", "attachment": "undie-straps" },
{ "name": "undies", "bone": "pelvis", "attachment": "undies" },
{ "name": "right-upper-leg", "bone": "right-upper-leg", "attachment": "right-upper-leg" },
{ "name": "head", "bone": "head", "attachment": "head" },
{ "name": "eyes", "bone": "head" },
{ "name": "right-shoulder", "bone": "right-shoulder", "attachment": "right-shoulder" },
{ "name": "right-arm", "bone": "right-arm", "attachment": "right-arm" },
{ "name": "right-hand-item", "bone": "right-hand" },
{ "name": "right-hand", "bone": "right-hand", "attachment": "right-hand" },
{ "name": "right-hand-item-top", "bone": "right-hand", "attachment": "shield" }
],
"skins": [
{
"name": "default",
"attachments": {
"left-hand-item": {
"dagger": { "x": 7.88, "y": -23.46, "rotation": 10.48, "width": 26, "height": 108 },
"spear": { "x": -4.56, "y": 39.2, "rotation": 13.05, "width": 22, "height": 368 }
},
"right-hand-item": {
"dagger": { "x": 6.52, "y": -24.16, "rotation": -8.06, "width": 26, "height": 108 }
},
"right-hand-item-top": {
"shield": { "rotation": 93.5, "width": 70, "height": 72 }
}
}
},
{
"name": "goblin",
"attachments": {
"eyes": {
"eyes-closed": { "name": "goblin/eyes-closed", "x": 32.22, "y": -21.27, "rotation": -88.93, "width": 34, "height": 12 }
},
"head": {
"head": { "name": "goblin/head", "x": 25.74, "y": 2.33, "rotation": -92.29, "width": 103, "height": 66 }
},
"left-arm": {
"left-arm": {
"name": "goblin/left-arm",
"x": 16.7,
"y": -1.69,
"scaleX": 1.0573,
"scaleY": 1.0573,
"rotation": 33.85,
"width": 37,
"height": 35
}
},
"left-foot": {
"left-foot": { "name": "goblin/left-foot", "x": 24.85, "y": 8.75, "rotation": 3.32, "width": 65, "height": 31 }
},
"left-hand": {
"left-hand": {
"name": "goblin/left-hand",
"x": 3.47,
"y": 3.41,
"scaleX": 0.8922,
"scaleY": 0.8922,
"rotation": 31.14,
"width": 36,
"height": 41
}
},
"left-lower-leg": {
"left-lower-leg": { "name": "goblin/left-lower-leg", "x": 23.59, "y": -2.07, "rotation": 105.76, "width": 33, "height": 70 }
},
"left-shoulder": {
"left-shoulder": { "name": "goblin/left-shoulder", "x": 15.56, "y": -2.27, "rotation": 62.01, "width": 29, "height": 44 }
},
"left-upper-leg": {
"left-upper-leg": { "name": "goblin/left-upper-leg", "x": 29.69, "y": -3.87, "rotation": 89.1, "width": 33, "height": 73 }
},
"neck": {
"neck": { "name": "goblin/neck", "x": 10.1, "y": 0.42, "rotation": -93.7, "width": 36, "height": 41 }
},
"pelvis": {
"pelvis": { "name": "goblin/pelvis", "x": -5.62, "y": 0.77, "width": 62, "height": 43 }
},
"right-arm": {
"right-arm": { "name": "goblin/right-arm", "x": 16.44, "y": -1.04, "rotation": 94.32, "width": 23, "height": 50 }
},
"right-foot": {
"right-foot": { "name": "goblin/right-foot", "x": 23.57, "y": 9.8, "rotation": 1.53, "width": 63, "height": 33 }
},
"right-hand": {
"right-hand": { "name": "goblin/right-hand", "x": 7.89, "y": 2.78, "rotation": 91.96, "width": 36, "height": 37 }
},
"right-lower-leg": {
"right-lower-leg": { "name": "goblin/right-lower-leg", "x": 25.68, "y": -3.16, "rotation": 111.84, "width": 36, "height": 76 }
},
"right-shoulder": {
"right-shoulder": { "name": "goblin/right-shoulder", "x": 15.68, "y": -1.03, "rotation": 130.65, "width": 39, "height": 45 }
},
"right-upper-leg": {
"right-upper-leg": { "name": "goblin/right-upper-leg", "x": 20.35, "y": 1.48, "rotation": 97.5, "width": 34, "height": 63 }
},
"torso": {
"torso": { "name": "goblin/torso", "x": 38.1, "y": -3.87, "rotation": -94.95, "width": 68, "height": 96 }
},
"undie-straps": {
"undie-straps": { "name": "goblin/undie-straps", "x": -3.88, "y": 13.11, "scaleX": 1.0896, "width": 55, "height": 19 }
},
"undies": {
"undies": { "name": "goblin/undies", "x": 6.3, "y": 0.13, "rotation": 0.92, "width": 36, "height": 29 }
}
}
},
{
"name": "goblingirl",
"attachments": {
"eyes": {
"eyes-closed": { "name": "goblingirl/eyes-closed", "x": 28, "y": -25.55, "rotation": -87.05, "width": 37, "height": 21 }
},
"head": {
"head": { "name": "goblingirl/head", "x": 27.72, "y": -4.32, "rotation": -85.58, "width": 103, "height": 81 }
},
"left-arm": {
"left-arm": { "name": "goblingirl/left-arm", "x": 19.64, "y": -2.43, "rotation": 33.05, "width": 37, "height": 35 }
},
"left-foot": {
"left-foot": { "name": "goblingirl/left-foot", "x": 25.18, "y": 7.92, "rotation": 3.32, "width": 65, "height": 31 }
},
"left-hand": {
"left-hand": {
"name": "goblingirl/left-hand",
"x": 4.34,
"y": 2.39,
"scaleX": 0.8965,
"scaleY": 0.8965,
"rotation": 30.35,
"width": 35,
"height": 40
}
},
"left-lower-leg": {
"left-lower-leg": { "name": "goblingirl/left-lower-leg", "x": 25.02, "y": -0.61, "rotation": 105.76, "width": 33, "height": 70 }
},
"left-shoulder": {
"left-shoulder": { "name": "goblingirl/left-shoulder", "x": 19.81, "y": -0.43, "rotation": 61.22, "width": 28, "height": 46 }
},
"left-upper-leg": {
"left-upper-leg": { "name": "goblingirl/left-upper-leg", "x": 30.22, "y": -2.95, "rotation": 89.1, "width": 33, "height": 70 }
},
"neck": {
"neck": { "name": "goblingirl/neck", "x": 6.16, "y": -3.15, "rotation": -98.86, "width": 35, "height": 41 }
},
"pelvis": {
"pelvis": { "name": "goblingirl/pelvis", "x": -3.88, "y": 3.19, "width": 62, "height": 43 }
},
"right-arm": {
"right-arm": { "name": "goblingirl/right-arm", "x": 16.85, "y": -0.66, "rotation": 93.53, "width": 28, "height": 50 }
},
"right-foot": {
"right-foot": { "name": "goblingirl/right-foot", "x": 23.46, "y": 9.66, "rotation": 1.53, "width": 63, "height": 33 }
},
"right-hand": {
"right-hand": { "name": "goblingirl/right-hand", "x": 7.22, "y": 3.44, "rotation": 91.17, "width": 36, "height": 37 }
},
"right-lower-leg": {
"right-lower-leg": { "name": "goblingirl/right-lower-leg", "x": 26.15, "y": -3.28, "rotation": 111.84, "width": 36, "height": 76 }
},
"right-shoulder": {
"right-shoulder": { "name": "goblingirl/right-shoulder", "x": 14.46, "y": 0.46, "rotation": 129.85, "width": 39, "height": 45 }
},
"right-upper-leg": {
"right-upper-leg": { "name": "goblingirl/right-upper-leg", "x": 19.7, "y": 2.13, "rotation": 97.5, "width": 34, "height": 63 }
},
"torso": {
"torso": { "name": "goblingirl/torso", "x": 36.28, "y": -5.14, "rotation": -95.75, "width": 68, "height": 96 }
},
"undie-straps": {
"undie-straps": { "name": "goblingirl/undie-straps", "x": -1.52, "y": 14.19, "width": 55, "height": 19 }
},
"undies": {
"undies": { "name": "goblingirl/undies", "x": 5.4, "y": 1.71, "width": 36, "height": 29 }
}
}
}
],
"animations": {
"walk": {
"slots": {
"eyes": {
"attachment": [
{ "time": 0.7, "name": "eyes-closed" },
{ "time": 0.8 }
]
}
},
"bones": {
"left-upper-leg": {
"rotate": [
{ "value": -26.56 },
{ "time": 0.1333, "value": -8.79 },
{ "time": 0.2333, "value": 9.51 },
{ "time": 0.3667, "value": 30.74 },
{ "time": 0.5, "value": 25.34 },
{ "time": 0.6333, "value": 26.12 },
{ "time": 0.7333, "value": -7.71 },
{ "time": 0.8667, "value": -21.19 },
{ "time": 1, "value": -26.56 }
],
"translate": [
{ "x": -1.32, "y": 1.71 },
{ "time": 0.3667, "x": -0.06, "y": 2.43 },
{ "time": 1, "x": -1.32, "y": 1.71 }
]
},
"right-upper-leg": {
"rotate": [
{ "value": 42.45 },
{ "time": 0.1333, "value": 52.11 },
{ "time": 0.2333, "value": 8.54 },
{ "time": 0.5, "value": -16.94 },
{ "time": 0.6333, "value": 1.9 },
{
"time": 0.7333,
"value": 28.06,
"curve": [ 0.795, 31.71, 0.867, 58.69 ]
},
{
"time": 0.8667,
"value": 58.69,
"curve": [ 0.933, 58.35, 1, 42.45 ]
},
{ "time": 1, "value": 42.45 }
],
"translate": [
{ "x": 6.24 },
{ "time": 0.2333, "x": 2.14, "y": 2.4 },
{ "time": 0.5, "x": 2.44, "y": 4.8 },
{ "time": 1, "x": 6.24 }
]
},
"left-lower-leg": {
"rotate": [
{ "value": -22.98 },
{ "time": 0.1333, "value": -63.51 },
{ "time": 0.2333, "value": -73.76 },
{ "time": 0.5, "value": 5.12 },
{ "time": 0.6333, "value": -28.3 },
{ "time": 0.7333, "value": 4.08 },
{ "time": 0.8667, "value": 3.53 },
{ "time": 1, "value": -22.98 }
],
"translate": [
{},
{ "time": 0.2333, "x": 2.56, "y": -0.47 },
{ "time": 0.5 }
]
},
"left-foot": {
"rotate": [
{ "value": -3.69 },
{ "time": 0.1333, "value": -10.42 },
{ "time": 0.2333, "value": -5.01 },
{ "time": 0.3667, "value": 3.87 },
{ "time": 0.5, "value": -3.88 },
{ "time": 0.6333, "value": 2.78 },
{ "time": 0.7333, "value": 1.68 },
{ "time": 0.8667, "value": -8.54 },
{ "time": 1, "value": -3.69 }
]
},
"right-shoulder": {
"rotate": [
{
"value": 5.29,
"curve": [ 0.167, 5.29, 0.475, 6.65 ]
},
{ "time": 0.6333, "value": 6.65 },
{ "time": 1, "value": 5.29 }
]
},
"right-arm": {
"rotate": [
{
"value": -4.03,
"curve": [ 0.169, -3.91, 0.51, 19.66 ]
},
{
"time": 0.6333,
"value": 19.79,
"curve": [ 0.746, 19.75, 0.922, -3.91 ]
},
{ "time": 1, "value": -4.03 }
]
},
"right-hand": {
"rotate": [
{ "value": 8.99 },
{ "time": 0.6333, "value": 0.51 },
{ "time": 1, "value": 8.99 }
]
},
"left-shoulder": {
"rotate": [
{
"value": 6.26,
"curve": [ 0.17, 6.26, 0.342, -11.79 ]
},
{
"time": 0.5,
"value": -11.79,
"curve": [ 0.641, -11.79, 0.843, 6.16 ]
},
{ "time": 1, "value": 6.26 }
],
"translate": [
{ "x": 1.15, "y": 0.24 }
]
},
"left-hand": {
"rotate": [
{
"value": -21.24,
"curve": [ 0.148, -21.24, 0.378, -27.21 ]
},
{
"time": 0.5,
"value": -27.28,
"curve": [ 0.621, -27.28, 0.875, -21.4 ]
},
{ "time": 1, "value": -21.24 }
]
},
"left-arm": {
"rotate": [
{
"value": 28.38,
"curve": [ 0.17, 28.38, 0.342, 60.09 ]
},
{
"time": 0.5,
"value": 60.09,
"curve": [ 0.641, 60.09, 0.843, 28.54 ]
},
{ "time": 1, "value": 28.38 }
]
},
"torso": {
"rotate": [
{ "value": -10.28 },
{
"time": 0.1333,
"value": -15.39,
"curve": [ 0.261, -15.36, 0.324, -9.78 ]
},
{
"time": 0.3667,
"value": -9.78,
"curve": [ 0.521, -10.8, 0.545, -15.72 ]
},
{
"time": 0.6333,
"value": -15.75,
"curve": [ 0.688, -15.66, 0.819, -7.07 ]
},
{
"time": 0.8667,
"value": -7.07,
"curve": [ 0.895, -7.07, 0.975, -10.25 ]
},
{ "time": 1, "value": -10.28 }
],
"translate": [
{ "x": -1.29, "y": 1.69 }
]
},
"right-foot": {
"rotate": [
{ "value": -5.25 },
{ "time": 0.2333, "value": -1.91 },
{ "time": 0.3667, "value": -6.45 },
{ "time": 0.5, "value": -5.4 },
{ "time": 0.7333, "value": -11.69 },
{ "time": 0.8667, "value": 0.46 },
{ "time": 1, "value": -5.25 }
]
},
"right-lower-leg": {
"rotate": [
{
"value": -3.39,
"curve": [ 0.042, -4.05, 0.099, -45.1 ]
},
{
"time": 0.1333,
"value": -45.53,
"curve": [ 0.156, -45.53, 0.207, -5.89 ]
},
{ "time": 0.2333, "value": -4.83 },
{ "time": 0.5, "value": -19.53 },
{ "time": 0.6333, "value": -64.8 },
{
"time": 0.7333,
"value": -82.56,
"curve": [ 0.882, -68.28, 1, -3.39 ]
},
{ "time": 1, "value": -3.39 }
],
"translate": [
{ "time": 0.5 },
{ "time": 0.6333, "x": 2.19, "y": 0.21 },
{ "time": 1 }
]
},
"hip": {
"translate": [
{ "y": -4.16 },
{
"time": 0.1333,
"y": -7.06,
"curve": [ 0.217, 0, 0.284, 0, 0.217, -0.53, 0.284, 3.27 ]
},
{ "time": 0.3667, "y": 6.78 },
{ "time": 0.5, "y": -6.14 },
{
"time": 0.6333,
"y": -7.06,
"curve": [ 0.717, 0, 0.784, 0, 0.717, -0.53, 0.784, 3.27 ]
},
{ "time": 0.8667, "y": 6.78 },
{ "time": 1, "y": -4.16 }
]
},
"neck": {
"rotate": [
{ "value": 3.6 },
{ "time": 0.1333, "value": 17.5 },
{ "time": 0.2333, "value": 6.11 },
{ "time": 0.3667, "value": 3.46 },
{ "time": 0.5, "value": 5.18 },
{ "time": 0.6333, "value": 18.36 },
{ "time": 0.7333, "value": 6.09 },
{ "time": 0.8667, "value": 2.29 },
{ "time": 1, "value": 3.6 }
]
},
"head": {
"rotate": [
{
"value": 3.6,
"curve": [ 0, 3.6, 0.094, -0.89 ]
},
{ "time": 0.1333, "value": -0.21 },
{ "time": 0.2333, "value": 6.11 },
{ "time": 0.3667, "value": 3.46 },
{
"time": 0.5,
"value": 5.18,
"curve": [ 0.5, 5.18, 0.617, -1.4 ]
},
{ "time": 0.6667, "value": 1.11 },
{ "time": 0.7333, "value": 6.09 },
{ "time": 0.8667, "value": 2.29 },
{ "time": 1, "value": 3.6 }
]
}
}
}
}
}

View File

@ -0,0 +1,8 @@
Copyright (c) 2013, Esoteric Software
The images in this project may be redistributed as long as they are accompanied
by this license file. The images may not be used for commercial use of any
kind.
The project file is released into the public domain. It may be used as the basis
for derivative work.

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.9" tiledversion="1.9.2" orientation="orthogonal" renderorder="right-down" width="37" height="38" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="1">
<tileset firstgid="1" source="castle.tsx"/>
<layer id="2" name="Tile BG" width="37" height="38">
<data encoding="base64" compression="zlib">
eJztk8sKwjAQRfMb1sderYLPrd/hqnTT//8Ep9DAdXAmkb4o3AOHkpCEQ9qG8M1uJqtgw6blNxXgWtyoucI5v12/hT1x/xD3pBustl+uOlP9fZssU61jfk+5jVM2xa5/3uEUTfq+cu7SOuftNDWdQzel/rfaaGrAdv41kxV06aa9eBBP4hnGR7GEZ5z31uu93thruohX8SE+YXwT7/CM8956vdcbYxPifftjYzURQgghhBBCCMnjAxltRQI=
</data>
</layer>
<layer id="1" name="Tile FG" width="37" height="38">
<data encoding="base64" compression="zlib">
eJztlE1Lw0AQhicxoLd605taP489xsb6EZH6hYiKCIKFXvr//4Ez+A4Z102y0oMe5oWHdGd3Ju/OkBKlKwd9KhLOzANmoO/9RfBUZaAAdp2ZM0Xk3LKeTpkzcI66NqbxWCyL5GeRuO71edLeXAdxyXln1phpS33NC/On1MxU9+aIHyV4KnDO6piaOawHtUUT43Nh+DBon4i+z22U4CnHOdGj8aTaxPtWsR4yB8weYosA0RazjbsW8FIHnrokOSVTMW+IXUQ8DbAem72B+a2z0h5n5q6/9ZTjPnlA6GnDeFJfGpuY87bHI2rmW+OZ4klUom5tsN+y1Lqkn9+6xGSWu/Q1S5Htcdnhqc2X1F7Bc4i6Dy2+bjrudMvcMffU9Gls7mD/l1L6JJ4q4+OV4v3aYa6YfeaQOUFccp+YZ+aFmj5p3sx4qhI9/YXcU5r+oyeXy+VyuVwul8vlci2nTwIxM6E=
</data>
</layer>
</map>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.9" tiledversion="1.9.2" name="castle" tilewidth="16" tileheight="16" tilecount="128" columns="8">
<image source="castle-tileset-by-RottingPixels-(CC0).png" width="128" height="256"/>
</tileset>

View File

@ -56,7 +56,7 @@ TODO("obj: free obj_components()/payload2");
TODO("pack: mp2json, json2mp");
TODO("pack: simplify msgpack API, make it growth similar to va()")
#if 0 // fwk_pack proposal
#if 0 // v4k_pack proposal
static __thread char* mpin;
static __thread unsigned mpinlen;
static __thread char mpinbuf[256];

View File

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

Binary file not shown.

View File

@ -7,7 +7,7 @@
; syntax: symbols are defined in KEY=value form, as seen below.
TOOLS=./ ; folder where our pipeline tools are located
ART=../demos/art/,../engine/art/,../tools/editor/art/ ; comma-separated folder(s) that store all our asset files
ART=../demos/art/,../engine/art/,../editor/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.
; we are defining here some symbols differently for each platform.
@ -25,9 +25,9 @@ ART=../demos/art/,../engine/art/,../tools/editor/art/ ; comma-separated folder(
; also, once a symbol is found, it is replaced by its value always.
; some predefined symbols: INPUT (input filename), OUTPUT (output filename), PRETTY (clean input filename), PROGRESS (cook progress).
@windows `echo Cooking PROGRESS% PRETTY...`
@linux `echo "Cooking PROGRESS% PRETTY..."`
@osx `echo "Cooking PROGRESS% PRETTY..."`
;@windows `echo Cooking PROGRESS% PRETTY...`
;@linux `echo "Cooking PROGRESS% PRETTY..."`
;@osx `echo "Cooking PROGRESS% PRETTY..."`
; ------------------------------------------------------------------------------
; groups below are collection of files that we want to cook, and then package.