main
Dominik Madarász 2023-11-19 13:07:28 +01:00
parent 144983571c
commit ceae5e2fe0
213 changed files with 51818 additions and 8448 deletions

View File

@ -324,6 +324,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"
rem xcopy /y/E "tools "_fwk\tools"
for %%f in ("engine\split\v4k*") do (
set "filename=%%~nf"
@ -371,6 +372,10 @@ if "%1"=="back" (
xcopy /y "_fwk\engine\split\3rd_*" "engine\split"
xcopy /y "_fwk\engine\art\shaders\*" "engine\art\shaders"
xcopy /y "_fwk\demos" "demos"
xcopy /y "_fwk\engine\editor.c" "engine\editor.c"
tools\fwkren.exe "engine\editor.c" to
tools\fwkren.exe "hello.c" to
rem xcopy /y/E "_fwk\tools "tools"
for %%f in ("_fwk\engine\split\fwk*") do (
set "filename=%%~nf"
@ -804,7 +809,8 @@ if "!v4k!"=="yes" (
rem editor
if "!editor!"=="yes" (
set edit=-DCOOK_ON_DEMAND
!echo! editor && !cc! !o! editor.exe tools\editor\editor.c !edit! -Iengine/joint !args! || set rc=1
rem set edit=-DUI_LESSER_SPACING -DUI_ICONS_SMALL !edit!
!echo! editor && !cc! !o! editor.exe engine\editor.c !edit! -Iengine/joint !args! || set rc=1
rem if "!cc!"=="cl" (
rem set plug_export=/LD

View File

@ -32,7 +32,7 @@ void demo_kids(vec3 offs) {
spritesheet, // num_frame in a 4x4 spritesheet
position, 0, // position(x,y,depth:sort-by-Y), angle
offset, scale, // offset(x,y), scale(x,y)
false, WHITE, false // is_additive, tint color, resolution-independent
WHITE, 0 // tint_color, no flags
);
}
}
@ -43,7 +43,7 @@ void demo_hud() {
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, window_height() }; // position in screen-coordinates (x,y,z-index)
sprite_sheet(inputs, spritesheet, position, 0/*deg*/, offset, scale, false, WHITE, 1);
sprite_sheet(inputs, spritesheet, position, 0/*deg*/, offset, scale, WHITE, SPRITE_RESOLUTION_INDEPENDANT);
}
int main() {
@ -140,7 +140,7 @@ int main() {
ui_panel_end();
}
/*if( ui_panel("Spine", 0)) {
spine_ui(spn);
ui_spine(spn);
ui_panel_end();
}*/
}

86
demos/01-ui.c 100644
View File

@ -0,0 +1,86 @@
// 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

216
demos/06-scene.c 100644
View File

@ -0,0 +1,216 @@
// scene demo
// - rlyeh, public domain
#include "v4k.h"
int main() {
// options
bool do_twosided = 1;
bool do_wireframe = 0;
bool do_billboard_x = 0, do_billboard_y = 0, do_billboard_z = 0;
// 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

@ -0,0 +1,280 @@
// 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)) {}//

234
demos/99-lod.c 100644

File diff suppressed because one or more lines are too long

View File

@ -394,8 +394,8 @@ void spine_render(spine_t *p, vec3 offset, unsigned flags) {
float offsy = 0; // /*-(rect.z * p->texture.h)*2*/ -p->atlas[self->atlas_id].h - (self->rect_id ? p->skins[p->skin].rects[self->rect_id].h/2 : 0);
float deg_rect = self->rect_id ? p->skins[p->skin].rects[self->rect_id].deg : 0;
float tilt = p->atlas[self->atlas_id].deg + self->deg2 - deg_rect; // + self->deg2 + deg_rect + p->atlas[self->atlas_id].deg
unsigned tint = ~0u;
sprite_rect(p->texture, rect, zindex, add4(vec4(target.x,target.y,1,1),vec4(offsx,offsy,0,0)), tilt, tint);
unsigned tint = ~0u, flags = 0;
sprite_rect(p->texture, rect, vec4(target.x,target.y,0,zindex), vec4(1,1,offsx,offsy), tilt, tint, flags);
}
}
@ -510,11 +510,8 @@ void ui_spine(spine_t *p) {
spine_atlas_t *r = p->atlas + b->atlas_id;
sprite_flush();
camera_get_active()->position = vec3(0,0,2);
vec4 rect = ptr4(&r->x); float zindex = 0; vec3 xy_zoom = vec3(0,0,0); unsigned tint = ~0u;
sprite_rect(p->texture,
// rect: vec4(r->x*1.0/p->texture.w,r->y*1.0/p->texture.h,(r->x+r->w)*1.0/p->texture.w,(r->y+r->h)*1.0/p->texture.h),
ptr4(&r->x), // atlas
0, vec4(0,0,1,1), r->deg + tilt, tint);
vec4 rect = ptr4(&r->x); float zindex = 0; vec4 scale_offset = vec4(1,1,0,0);
sprite_rect(p->texture, ptr4(&r->x), vec4(0,0,0,zindex), scale_offset, r->deg + tilt, ~0u, 0);
sprite_flush();
camera_get_active()->position = vec3(+window_width()/3,window_height()/2.25,2);
}

218
demos/99-sprite.c 100644
View File

@ -0,0 +1,218 @@
// 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)
white,0 // tint_color, flags
);
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)
alpha,0 // tint_color, flags
);
}
}
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)
~0u, 0 // tint color, flags
);
}
}
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, WHITE, SPRITE_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();
}
}
}

118
demos/99-sprite2.c 100644
View File

@ -0,0 +1,118 @@
#include "v4k.h"
void game(unsigned frame) {
static camera_t cam2d,cam3d;
static sprite_t *s1 = 0;
static sprite_t *s2 = 0;
static sprite_t *s3 = 0;
static sprite_t *s4 = 0;
if( !frame ) {
cam3d = camera();
cam3d.damping = true;
// camera center(x,y) zoom(z)
cam2d = camera();
cam2d.position = vec3(window_width()/2,window_height()/2,8);
camera_enable(&cam2d);
sprite_del(s1);
sprite_del(s2);
sprite_del(s3);
sprite_del(s4);
s1 = sprite_new("Captain Clown Nose.ase", (int[6]){KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT,KEY_A,KEY_S});
s2 = sprite_new("Crew-Crabby.ase", (int[6]){KEY_I,KEY_K,KEY_J,KEY_L} );
s3 = sprite_new("Props-Shooter Traps.ase", (int[6]){0} );
s4 = sprite_new("Crew-Fierce Tooth.ase", (int[6]){0,0,KEY_N,KEY_M} );
obj_setname(s1, "Cap");
obj_setname(s2, "Crab");
obj_setname(s3, "Cannon");
obj_setname(s4, "Shark");
// 2d pos and z-order
// s1->pos = vec3(window_width()/2, window_height()/2, 2);
// s2->pos = vec3(window_width()/2, window_height()/2, 1);
// s3->pos = vec3(window_width()/2, window_height()/2, 1);
// s4->pos = vec3(window_width()/2, window_height()/2, 1);
// 3d position+scale
s1->pos = s2->pos = s3->pos = s4->pos = vec4(0,1.5,0,0);
s1->sca = s2->sca = s3->sca = s4->sca = vec2(0.125,0.125);
// 3d zindex
s1->pos.w = 0; s2->pos.w = 0.1; s3->pos.w = 0.2; s4->pos.w = 0.3;
s4->flipped ^= 1;
}
// draw world
camera_enable(&cam3d);
ddraw_grid(0);
ddraw_flush();
// camera logic
enum { is_editing = 0 };
if( is_editing ) {
if( !ui_hover() && !ui_active() ) {
// camera panning (x,y) & zooming (z)
if( input(MOUSE_L) ) cam2d.position.x -= input_diff(MOUSE_X);
if( input(MOUSE_L) ) cam2d.position.y -= input_diff(MOUSE_Y);
cam2d.position.z += input_diff(MOUSE_W) * 0.1; // cam2d.p.z += 0.001f; for tests
}
camera_enable(&cam2d);
} else {
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
if( active ) {
cam3d.speed = clampf(cam3d.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)), cam3d.speed);
camera_moveby(&cam3d, wasdecq);
camera_fps(&cam3d, mouse.x,mouse.y);
} else {
camera_teleport(&cam3d, add3(s1->pos.xyz, vec3(0,10,20)));
camera_lookat(&cam3d, s1->pos.xyz);
}
window_cursor( !active );
}
obj_tick(s1);
obj_draw(s1);
obj_tick(s2);
obj_draw(s2);
obj_tick(s3);
obj_draw(s3);
obj_tick(s4);
obj_draw(s4);
if( ui_panel("Help", 0) ) {
ui_label2("Freecam", ICON_MD_MOUSE " RMB");
ui_label2("Captain", ICON_MD_KEYBOARD " Cursor keys");
ui_label2("Crab", ICON_MD_KEYBOARD " I/K/J/L keys");
ui_label2("Shark", ICON_MD_KEYBOARD " N/M");
ui_panel_end();
}
if( ui_panel("Sprites", PANEL_OPEN)) {
obj_edit(s1);
obj_edit(s2);
obj_edit(s3);
obj_edit(s4);
ui_panel_end();
}
}
int main() {
unsigned frame = 0;
for( window_create(0.75, 0); window_swap(); ) {
if( input_down(KEY_Z) && input(KEY_ALT) ) window_record(file_counter(va("%s.mp4",app_name())));
if( input_down(KEY_F5) ) frame = 0;
game( frame++ );
}
}

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 983 B

After

Width:  |  Height:  |  Size: 983 B

View File

@ -0,0 +1,284 @@
local core = require "core"
local common = require "core.common"
local config = require "core.config"
local command = require "core.command"
local style = require "core.style"
local keymap = require "core.keymap"
local translate = require "core.doc.translate"
local RootView = require "core.rootview"
local DocView = require "core.docview"
config.autocomplete_max_suggestions = 6
local autocomplete = {}
autocomplete.map = {}
local mt = { __tostring = function(t) return t.text end }
function autocomplete.add(t)
local items = {}
for text, info in pairs(t.items) do
info = (type(info) == "string") and info
table.insert(items, setmetatable({ text = text, info = info }, mt))
end
autocomplete.map[t.name] = { files = t.files or ".*", items = items }
end
core.add_thread(function()
local cache = setmetatable({}, { __mode = "k" })
local function get_symbols(doc)
local i = 1
local s = {}
while i < #doc.lines do
for sym in doc.lines[i]:gmatch(config.symbol_pattern) do
s[sym] = true
end
i = i + 1
if i % 100 == 0 then coroutine.yield() end
end
return s
end
local function cache_is_valid(doc)
local c = cache[doc]
return c and c.last_change_id == doc:get_change_id()
end
while true do
local symbols = {}
-- lift all symbols from all docs
for _, doc in ipairs(core.docs) do
-- update the cache if the doc has changed since the last iteration
if not cache_is_valid(doc) then
cache[doc] = {
last_change_id = doc:get_change_id(),
symbols = get_symbols(doc)
}
end
-- update symbol set with doc's symbol set
for sym in pairs(cache[doc].symbols) do
symbols[sym] = true
end
coroutine.yield()
end
-- update symbols list
autocomplete.add { name = "open-docs", items = symbols }
-- wait for next scan
local valid = true
while valid do
coroutine.yield(1)
for _, doc in ipairs(core.docs) do
if not cache_is_valid(doc) then
valid = false
end
end
end
end
end)
local partial = ""
local suggestions_idx = 1
local suggestions = {}
local last_line, last_col
local function reset_suggestions()
suggestions_idx = 1
suggestions = {}
end
local function update_suggestions()
local doc = core.active_view.doc
local filename = doc and doc.filename or ""
-- get all relevant suggestions for given filename
local items = {}
for _, v in pairs(autocomplete.map) do
if common.match_pattern(filename, v.files) then
for _, item in pairs(v.items) do
table.insert(items, item)
end
end
end
-- fuzzy match, remove duplicates and store
items = common.fuzzy_match(items, partial)
local j = 1
for i = 1, config.autocomplete_max_suggestions do
suggestions[i] = items[j]
while items[j] and items[i].text == items[j].text do
items[i].info = items[i].info or items[j].info
j = j + 1
end
end
end
local function get_partial_symbol()
local doc = core.active_view.doc
local line2, col2 = doc:get_selection()
local line1, col1 = doc:position_offset(line2, col2, translate.start_of_word)
return doc:get_text(line1, col1, line2, col2)
end
local function get_active_view()
if getmetatable(core.active_view) == DocView then
return core.active_view
end
end
local function get_suggestions_rect(av)
if #suggestions == 0 then
return 0, 0, 0, 0
end
local line, col = av.doc:get_selection()
local x, y = av:get_line_screen_position(line)
x = x + av:get_col_x_offset(line, col - #partial)
y = y + av:get_line_height() + style.padding.y
local font = av:get_font()
local th = font:get_height()
local max_width = 0
for _, s in ipairs(suggestions) do
local w = font:get_width(s.text)
if s.info then
w = w + style.font:get_width(s.info) + style.padding.x
end
max_width = math.max(max_width, w)
end
return
x - style.padding.x,
y - style.padding.y,
max_width + style.padding.x * 2,
#suggestions * (th + style.padding.y) + style.padding.y
end
local function draw_suggestions_box(av)
-- draw background rect
local rx, ry, rw, rh = get_suggestions_rect(av)
renderer.draw_rect(rx, ry, rw, rh, style.background3)
-- draw text
local font = av:get_font()
local lh = font:get_height() + style.padding.y
local y = ry + style.padding.y / 2
for i, s in ipairs(suggestions) do
local color = (i == suggestions_idx) and style.accent or style.text
common.draw_text(font, color, s.text, "left", rx + style.padding.x, y, rw, lh)
if s.info then
color = (i == suggestions_idx) and style.text or style.dim
common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
end
y = y + lh
end
end
-- patch event logic into RootView
local on_text_input = RootView.on_text_input
local update = RootView.update
local draw = RootView.draw
RootView.on_text_input = function(...)
on_text_input(...)
local av = get_active_view()
if av then
-- update partial symbol and suggestions
partial = get_partial_symbol()
if #partial >= 3 then
update_suggestions()
last_line, last_col = av.doc:get_selection()
else
reset_suggestions()
end
-- scroll if rect is out of bounds of view
local _, y, _, h = get_suggestions_rect(av)
local limit = av.position.y + av.size.y
if y + h > limit then
av.scroll.to.y = av.scroll.y + y + h - limit
end
end
end
RootView.update = function(...)
update(...)
local av = get_active_view()
if av then
-- reset suggestions if caret was moved
local line, col = av.doc:get_selection()
if line ~= last_line or col ~= last_col then
reset_suggestions()
end
end
end
RootView.draw = function(...)
draw(...)
local av = get_active_view()
if av then
-- draw suggestions box after everything else
core.root_view:defer_draw(draw_suggestions_box, av)
end
end
local function predicate()
return get_active_view() and #suggestions > 0
end
command.add(predicate, {
["autocomplete:complete"] = function()
local doc = core.active_view.doc
local line, col = doc:get_selection()
local text = suggestions[suggestions_idx].text
doc:insert(line, col, text)
doc:remove(line, col, line, col - #partial)
doc:set_selection(line, col + #text - #partial)
reset_suggestions()
end,
["autocomplete:previous"] = function()
suggestions_idx = math.max(suggestions_idx - 1, 1)
end,
["autocomplete:next"] = function()
suggestions_idx = math.min(suggestions_idx + 1, #suggestions)
end,
["autocomplete:cancel"] = function()
reset_suggestions()
end,
})
keymap.add {
["tab"] = "autocomplete:complete",
["up"] = "autocomplete:previous",
["down"] = "autocomplete:next",
["escape"] = "autocomplete:cancel",
}
return autocomplete

View File

@ -0,0 +1,61 @@
local core = require "core"
local config = require "core.config"
local Doc = require "core.doc"
local times = setmetatable({}, { __mode = "k" })
local function update_time(doc)
local info = system.get_file_info(doc.filename)
times[doc] = info.modified
end
local function reload_doc(doc)
local fp = io.open(doc.filename, "r")
local text = fp:read("*a")
fp:close()
local sel = { doc:get_selection() }
doc:remove(1, 1, math.huge, math.huge)
doc:insert(1, 1, text:gsub("\r", ""):gsub("\n$", ""))
doc:set_selection(table.unpack(sel))
update_time(doc)
doc:clean()
core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename)
end
core.add_thread(function()
while true do
-- check all doc modified times
for _, doc in ipairs(core.docs) do
local info = system.get_file_info(doc.filename or "")
if info and times[doc] ~= info.modified then
reload_doc(doc)
end
coroutine.yield()
end
-- wait for next scan
coroutine.yield(config.project_scan_rate)
end
end)
-- patch `Doc.save|load` to store modified time
local load = Doc.load
local save = Doc.save
Doc.load = function(self, ...)
local res = load(self, ...)
update_time(self)
return res
end
Doc.save = function(self, ...)
local res = save(self, ...)
update_time(self)
return res
end

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