sync fwk
8
MAKE.bat
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)) {}//
|
|
@ -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,12 +510,9 @@ 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);
|
||||
sprite_flush();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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++ );
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 983 B After Width: | Height: | Size: 983 B |
|
@ -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
|
|
@ -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
|