fix CRLF
parent
b14c31b4e0
commit
b841064174
|
@ -1,25 +1,25 @@
|
||||||
default
|
default
|
||||||
.art*.zip
|
.art*.zip
|
||||||
__pycache__
|
__pycache__
|
||||||
.vs
|
.vs
|
||||||
_deploy
|
_deploy
|
||||||
tools/steamcmd
|
tools/steamcmd
|
||||||
!tools/steamcmd/steamcmd.exe
|
!tools/steamcmd/steamcmd.exe
|
||||||
_builder_output
|
_builder_output
|
||||||
emsdk
|
emsdk
|
||||||
demos/html5/*.data
|
demos/html5/*.data
|
||||||
demos/html5/*.js
|
demos/html5/*.js
|
||||||
demos/html5/*.html
|
demos/html5/*.html
|
||||||
demos/ports/*/*.exe
|
demos/ports/*/*.exe
|
||||||
demos/ports/doom/.doomrc
|
demos/ports/doom/.doomrc
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
engine/v4k.html
|
engine/v4k.html
|
||||||
*.exe.manifest
|
*.exe.manifest
|
||||||
*.log
|
*.log
|
||||||
v4k.osx
|
v4k.osx
|
||||||
libv4k.*
|
libv4k.*
|
||||||
*.dSYM
|
*.dSYM
|
||||||
.DS_store
|
.DS_store
|
||||||
*.raddbg
|
*.raddbg
|
||||||
plugins
|
plugins
|
||||||
tools/assimp-vc*-mt.lib
|
tools/assimp-vc*-mt.lib
|
||||||
|
|
4382
bind/v4k.lua
4382
bind/v4k.lua
File diff suppressed because it is too large
Load Diff
|
@ -1,134 +1,134 @@
|
||||||
// material demo
|
// material demo
|
||||||
// - rlyeh, public domain
|
// - rlyeh, public domain
|
||||||
//
|
//
|
||||||
// @todo: object_print(obj, "");
|
// @todo: object_print(obj, "");
|
||||||
|
|
||||||
#include "v4k.h"
|
#include "v4k.h"
|
||||||
|
|
||||||
|
|
||||||
const char *skyboxes[][2] = { // reflection, env, metadata
|
const char *skyboxes[][2] = { // reflection, env, metadata
|
||||||
{"hdr/Tokyo_BigSight_1k.hdr","hdr/Tokyo_BigSight_Env.hdr"},
|
{"hdr/Tokyo_BigSight_1k.hdr","hdr/Tokyo_BigSight_Env.hdr"},
|
||||||
{"hdr/graffiti_shelter_4k.hdr","hdr/graffiti_shelter_Env.hdr"},
|
{"hdr/graffiti_shelter_4k.hdr","hdr/graffiti_shelter_Env.hdr"},
|
||||||
{"hdr/music_hall_01_4k.hdr","hdr/music_hall_01_Env.hdr"},
|
{"hdr/music_hall_01_4k.hdr","hdr/music_hall_01_Env.hdr"},
|
||||||
{"hdr/the_sky_is_on_fire_2k.hdr","hdr/the_sky_is_on_fire_Env.hdr"},
|
{"hdr/the_sky_is_on_fire_2k.hdr","hdr/the_sky_is_on_fire_Env.hdr"},
|
||||||
{"hdr/GCanyon_C_YumaPoint_1k.hdr","hdr/GCanyon_C_YumaPoint_Env.hdr"},
|
{"hdr/GCanyon_C_YumaPoint_1k.hdr","hdr/GCanyon_C_YumaPoint_Env.hdr"},
|
||||||
{"hdr/Factory_Catwalk_1k.hdr","hdr/Factory_Catwalk_Env.hdr"},
|
{"hdr/Factory_Catwalk_1k.hdr","hdr/Factory_Catwalk_Env.hdr"},
|
||||||
{"hdr/MonValley_G_DirtRoad_1k.hdr","hdr/MonValley_G_DirtRoad_Env.hdr"},
|
{"hdr/MonValley_G_DirtRoad_1k.hdr","hdr/MonValley_G_DirtRoad_Env.hdr"},
|
||||||
{"hdr/Shiodome_Stairs_1k.hdr","hdr/Shiodome_Stairs_Env.hdr"},
|
{"hdr/Shiodome_Stairs_1k.hdr","hdr/Shiodome_Stairs_Env.hdr"},
|
||||||
{"hdr/mesto.hdr","hdr/mesto_Env.hdr"},
|
{"hdr/mesto.hdr","hdr/mesto_Env.hdr"},
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// create the window
|
// create the window
|
||||||
window_create( 0.75f, WINDOW_MSAA8 );
|
window_create( 0.75f, WINDOW_MSAA8 );
|
||||||
window_color( GRAY );
|
window_color( GRAY );
|
||||||
|
|
||||||
// create camera
|
// create camera
|
||||||
camera_t cam = camera();
|
camera_t cam = camera();
|
||||||
|
|
||||||
// fx: load all post fx files in all subdirs.
|
// fx: load all post fx files in all subdirs.
|
||||||
fx_load("fx**.fs");
|
fx_load("fx**.fs");
|
||||||
|
|
||||||
// load video, RGB texture, no audio
|
// 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);
|
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_NO_AUDIO | VIDEO_LOOP ); video_seek(v, 30);
|
||||||
// load texture
|
// load texture
|
||||||
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_SRGB);
|
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_SRGB);
|
||||||
texture_t t2 = texture("matcaps/material3", TEXTURE_SRGB);
|
texture_t t2 = texture("matcaps/material3", TEXTURE_SRGB);
|
||||||
// load model
|
// load model
|
||||||
model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS);
|
model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS);
|
||||||
model_t m2 = model("suzanne.obj", MODEL_NO_ANIMATIONS|MODEL_MATCAPS);
|
model_t m2 = model("suzanne.obj", MODEL_NO_ANIMATIONS|MODEL_MATCAPS);
|
||||||
// model_t m3 = model("damagedhelmet.gltf", MODEL_NO_ANIMATIONS|MODEL_PBR);
|
// model_t m3 = model("damagedhelmet.gltf", MODEL_NO_ANIMATIONS|MODEL_PBR);
|
||||||
model_t m3 = model("Scutum_low.fbx", MODEL_NO_ANIMATIONS|MODEL_PBR);
|
model_t m3 = model("Scutum_low.fbx", MODEL_NO_ANIMATIONS|MODEL_PBR);
|
||||||
// model_t m4 = model("avp/scene.gltf", MODEL_NO_ANIMATIONS|MODEL_PBR);
|
// model_t m4 = model("avp/scene.gltf", MODEL_NO_ANIMATIONS|MODEL_PBR);
|
||||||
// model_t m3 = model("Cerberus_LP.FBX", MODEL_NO_ANIMATIONS|MODEL_PBR);
|
// model_t m3 = model("Cerberus_LP.FBX", MODEL_NO_ANIMATIONS|MODEL_PBR);
|
||||||
|
|
||||||
// spawn object1 (diffuse)
|
// spawn object1 (diffuse)
|
||||||
object_t* obj1 = scene_spawn();
|
object_t* obj1 = scene_spawn();
|
||||||
object_model(obj1, m1);
|
object_model(obj1, m1);
|
||||||
object_diffuse(obj1, t1);
|
object_diffuse(obj1, t1);
|
||||||
object_scale(obj1, vec3(3,3,3));
|
object_scale(obj1, vec3(3,3,3));
|
||||||
object_move(obj1, vec3(-10+5*0,0,-10));
|
object_move(obj1, vec3(-10+5*0,0,-10));
|
||||||
object_pivot(obj1, vec3(0,90,0));
|
object_pivot(obj1, vec3(0,90,0));
|
||||||
|
|
||||||
// spawn object2 (matcap)
|
// spawn object2 (matcap)
|
||||||
object_t* obj2 = scene_spawn();
|
object_t* obj2 = scene_spawn();
|
||||||
object_model(obj2, m2);
|
object_model(obj2, m2);
|
||||||
object_diffuse(obj2, t2);
|
object_diffuse(obj2, t2);
|
||||||
object_scale(obj2, vec3(3,3,3));
|
object_scale(obj2, vec3(3,3,3));
|
||||||
object_move(obj2, vec3(-10+5*2,0,-10));
|
object_move(obj2, vec3(-10+5*2,0,-10));
|
||||||
object_pivot(obj2, vec3(0,90,0));
|
object_pivot(obj2, vec3(0,90,0));
|
||||||
|
|
||||||
// spawn object3 (video)
|
// spawn object3 (video)
|
||||||
object_t* obj3 = scene_spawn();
|
object_t* obj3 = scene_spawn();
|
||||||
object_model(obj3, m1);
|
object_model(obj3, m1);
|
||||||
object_diffuse(obj3, video_textures(v)[0]);
|
object_diffuse(obj3, video_textures(v)[0]);
|
||||||
object_scale(obj3, vec3(3,3,3));
|
object_scale(obj3, vec3(3,3,3));
|
||||||
object_move(obj3, vec3(-10+5*1,0,-10));
|
object_move(obj3, vec3(-10+5*1,0,-10));
|
||||||
object_pivot(obj3, vec3(0,90,0));
|
object_pivot(obj3, vec3(0,90,0));
|
||||||
|
|
||||||
// spawn object4 (pbr)
|
// spawn object4 (pbr)
|
||||||
object_t* obj4 = scene_spawn();
|
object_t* obj4 = scene_spawn();
|
||||||
object_model(obj4, m3);
|
object_model(obj4, m3);
|
||||||
object_scale(obj4, vec3(3,3,3));
|
object_scale(obj4, vec3(3,3,3));
|
||||||
object_move(obj4, vec3(-10+6*3,0,-10));
|
object_move(obj4, vec3(-10+6*3,0,-10));
|
||||||
object_pivot(obj4, vec3(0,0,90));
|
object_pivot(obj4, vec3(0,0,90));
|
||||||
|
|
||||||
// spawn object5 (pbr)
|
// spawn object5 (pbr)
|
||||||
// object_t* obj5 = scene_spawn();
|
// object_t* obj5 = scene_spawn();
|
||||||
// object_model(obj5, m4);
|
// object_model(obj5, m4);
|
||||||
// object_scale(obj5, vec3(3,3,3));
|
// object_scale(obj5, vec3(3,3,3));
|
||||||
// object_move(obj5, vec3(-10+6*3,0,-10));
|
// object_move(obj5, vec3(-10+6*3,0,-10));
|
||||||
// object_pivot(obj5, vec3(0,0,90));
|
// object_pivot(obj5, vec3(0,0,90));
|
||||||
|
|
||||||
// create point light
|
// create point light
|
||||||
scene_spawn_light(); // sun
|
scene_spawn_light(); // sun
|
||||||
light_t* l = scene_spawn_light();
|
light_t* l = scene_spawn_light();
|
||||||
light_type(l, LIGHT_POINT);
|
light_type(l, LIGHT_POINT);
|
||||||
l->diffuse = vec3(1,0,0);
|
l->diffuse = vec3(1,0,0);
|
||||||
|
|
||||||
// load skybox
|
// load skybox
|
||||||
scene_get_active()->skybox = skybox_pbr(skyboxes[0][0], skyboxes[0][0], skyboxes[0][1]);
|
scene_get_active()->skybox = skybox_pbr(skyboxes[0][0], skyboxes[0][0], skyboxes[0][1]);
|
||||||
|
|
||||||
|
|
||||||
while(window_swap() && !input(KEY_ESC)) {
|
while(window_swap() && !input(KEY_ESC)) {
|
||||||
// draw environment
|
// draw environment
|
||||||
ddraw_grid(0);
|
ddraw_grid(0);
|
||||||
|
|
||||||
// update video
|
// update video
|
||||||
video_decode( v );
|
video_decode( v );
|
||||||
|
|
||||||
// update light position
|
// update light position
|
||||||
light_teleport(l, cam.position);
|
light_teleport(l, cam.position);
|
||||||
|
|
||||||
// draw scene
|
// draw scene
|
||||||
fx_begin();
|
fx_begin();
|
||||||
scene_render(SCENE_FOREGROUND|SCENE_BACKGROUND|SCENE_UPDATE_SH_COEF);
|
scene_render(SCENE_FOREGROUND|SCENE_BACKGROUND|SCENE_UPDATE_SH_COEF);
|
||||||
fx_end();
|
fx_end();
|
||||||
|
|
||||||
// fps camera
|
// fps camera
|
||||||
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
|
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
|
||||||
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
|
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
|
||||||
vec2 mouselook = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
|
vec2 mouselook = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
|
||||||
vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed);
|
vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed);
|
||||||
camera_moveby(&cam, wasdec);
|
camera_moveby(&cam, wasdec);
|
||||||
camera_fps(&cam, mouselook.x,mouselook.y);
|
camera_fps(&cam, mouselook.x,mouselook.y);
|
||||||
window_cursor( !active );
|
window_cursor( !active );
|
||||||
|
|
||||||
if (ui_panel("FXs", 0)) {
|
if (ui_panel("FXs", 0)) {
|
||||||
ui_fxs();
|
ui_fxs();
|
||||||
ui_panel_end();
|
ui_panel_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ui_panel( "Viewer", 0 ) ) {
|
if( ui_panel( "Viewer", 0 ) ) {
|
||||||
for( int i = 0; i < countof(skyboxes); i++ ) {
|
for( int i = 0; i < countof(skyboxes); i++ ) {
|
||||||
const char *filename = skyboxes[i][0];
|
const char *filename = skyboxes[i][0];
|
||||||
// bool selected = !strcmp(g_skybox.reflection->filename, file_name(filename));
|
// bool selected = !strcmp(g_skybox.reflection->filename, file_name(filename));
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
if( ui_bool( filename, &selected ) ) {
|
if( ui_bool( filename, &selected ) ) {
|
||||||
scene_get_active()->skybox = skybox_pbr(skyboxes[i][0], skyboxes[i][0], skyboxes[i][1]);
|
scene_get_active()->skybox = skybox_pbr(skyboxes[i][0], skyboxes[i][0], skyboxes[i][1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui_panel_end();
|
ui_panel_end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
158
demos/99-lmap.c
158
demos/99-lmap.c
|
@ -1,79 +1,79 @@
|
||||||
#include "v4k.h"
|
#include "v4k.h"
|
||||||
|
|
||||||
model_t litm;
|
model_t litm;
|
||||||
|
|
||||||
void bakedrawmodel(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata) {
|
void bakedrawmodel(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata) {
|
||||||
shader_bind(lm->shader);
|
shader_bind(lm->shader);
|
||||||
model_render(*m, proj, view, m->pivot, lm->shader);
|
model_render(*m, proj, view, m->pivot, lm->shader);
|
||||||
shader_float("u_litboost", 4.0);
|
shader_float("u_litboost", 4.0);
|
||||||
model_render(litm, proj, view, litm.pivot, lm->shader);
|
model_render(litm, proj, view, litm.pivot, lm->shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
void progressupdate(float progress) {
|
void progressupdate(float progress) {
|
||||||
static double lastUpdateTime = 0.0;
|
static double lastUpdateTime = 0.0;
|
||||||
double time = time_ss();
|
double time = time_ss();
|
||||||
if (time - lastUpdateTime > 1.0) {
|
if (time - lastUpdateTime > 1.0) {
|
||||||
lastUpdateTime = time;
|
lastUpdateTime = time;
|
||||||
PRINTF("progress: %.02f%%", progress*100);
|
PRINTF("progress: %.02f%%", progress*100);
|
||||||
}
|
}
|
||||||
// window_swap();
|
// window_swap();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
window_create(0.5, 0);
|
window_create(0.5, 0);
|
||||||
window_title(__FILE__);
|
window_title(__FILE__);
|
||||||
camera_t cam = camera();
|
camera_t cam = camera();
|
||||||
skybox_t sky = skybox(0, 0); skybox_mie_calc_sh(&sky, 2.0f);
|
skybox_t sky = skybox(0, 0); skybox_mie_calc_sh(&sky, 2.0f);
|
||||||
model_t mdl = model(option("--model","gazebo.obj"), 0);
|
model_t mdl = model(option("--model","gazebo.obj"), 0);
|
||||||
litm = model("cube.obj", 0);
|
litm = model("cube.obj", 0);
|
||||||
{
|
{
|
||||||
mat44 lp; scaling44(lp, 0.3, 0.3, 0.3); translate44(lp, 8,4,0);
|
mat44 lp; scaling44(lp, 0.3, 0.3, 0.3); translate44(lp, 8,4,0);
|
||||||
copy44(litm.pivot, lp);
|
copy44(litm.pivot, lp);
|
||||||
}
|
}
|
||||||
rotate44(mdl.pivot, -90, 1, 0, 0);
|
rotate44(mdl.pivot, -90, 1, 0, 0);
|
||||||
scale44(mdl.pivot, 4,4,4);
|
scale44(mdl.pivot, 4,4,4);
|
||||||
shader_bind(mdl.program);
|
shader_bind(mdl.program);
|
||||||
shader_vec3v("u_coefficients_sh", 9, sky.cubemap.sh);
|
shader_vec3v("u_coefficients_sh", 9, sky.cubemap.sh);
|
||||||
// shader_bool("u_texmod", 0);
|
// shader_bool("u_texmod", 0);
|
||||||
|
|
||||||
unsigned char emissive[] = { 255, 180, 0, 255 };
|
unsigned char emissive[] = { 255, 180, 0, 255 };
|
||||||
texture_t emission = texture_create(1,1,4,emissive,TEXTURE_LINEAR);
|
texture_t emission = texture_create(1,1,4,emissive,TEXTURE_LINEAR);
|
||||||
model_set_texture(litm, emission);
|
model_set_texture(litm, emission);
|
||||||
|
|
||||||
lightmap_t baker = lightmap(64, 0.01, 100, vec3(0,0,0), 2, 0.01, 0.0);
|
lightmap_t baker = lightmap(64, 0.01, 100, vec3(0,0,0), 2, 0.01, 0.0);
|
||||||
lightmap_setup(&baker, 512, 512);
|
lightmap_setup(&baker, 512, 512);
|
||||||
array_push(baker.models, &mdl);
|
array_push(baker.models, &mdl);
|
||||||
|
|
||||||
bool do_bake=0;
|
bool do_bake=0;
|
||||||
int b=1;
|
int b=1;
|
||||||
|
|
||||||
while (window_swap() && !input(KEY_ESC)) {
|
while (window_swap() && !input(KEY_ESC)) {
|
||||||
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
|
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
|
||||||
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
|
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);
|
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);
|
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_moveby(&cam, wasdecq);
|
||||||
camera_fps(&cam, mouse.x,mouse.y);
|
camera_fps(&cam, mouse.x,mouse.y);
|
||||||
window_cursor( !active );
|
window_cursor( !active );
|
||||||
|
|
||||||
skybox_render(&sky, cam.proj, cam.view);
|
skybox_render(&sky, cam.proj, cam.view);
|
||||||
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
|
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
|
||||||
model_render(litm, cam.proj, cam.view, litm.pivot, 0);
|
model_render(litm, cam.proj, cam.view, litm.pivot, 0);
|
||||||
|
|
||||||
if( ui_panel("Lightmapper", PANEL_OPEN) ) {
|
if( ui_panel("Lightmapper", PANEL_OPEN) ) {
|
||||||
ui_label2("Freecam", "Mouse + W/A/S/D/E/Q keys");
|
ui_label2("Freecam", "Mouse + W/A/S/D/E/Q keys");
|
||||||
ui_label("Warning " ICON_MD_WARNING "@This will take a few seconds and bake a lightmap illuminated by: The mesh itself (initially black) + A white sky (1.0f, 1.0f, 1.0f)");
|
ui_label("Warning " ICON_MD_WARNING "@This will take a few seconds and bake a lightmap illuminated by: The mesh itself (initially black) + A white sky (1.0f, 1.0f, 1.0f)");
|
||||||
ui_int("Bounces", &b);
|
ui_int("Bounces", &b);
|
||||||
if( ui_button(va("Bake %d light bounce(s)", b)) ) {
|
if( ui_button(va("Bake %d light bounce(s)", b)) ) {
|
||||||
do_bake=1;
|
do_bake=1;
|
||||||
}
|
}
|
||||||
ui_panel_end();
|
ui_panel_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_bake) {
|
if (do_bake) {
|
||||||
do_bake=0;
|
do_bake=0;
|
||||||
lightmap_bake(&baker, b, bakedrawmodel, progressupdate, 0);
|
lightmap_bake(&baker, b, bakedrawmodel, progressupdate, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
frame: 0-50 AlienArmature|Alien_Clapping
|
frame: 0-50 AlienArmature|Alien_Clapping
|
||||||
frame: 51-106 AlienArmature|Alien_Death
|
frame: 51-106 AlienArmature|Alien_Death
|
||||||
frame: 107-207 AlienArmature|Alien_Idle
|
frame: 107-207 AlienArmature|Alien_Idle
|
||||||
frame: 208-308 AlienArmature|Alien_IdleHold
|
frame: 208-308 AlienArmature|Alien_IdleHold
|
||||||
frame: 309-334 AlienArmature|Alien_Jump
|
frame: 309-334 AlienArmature|Alien_Jump
|
||||||
frame: 335-357 AlienArmature|Alien_Punch
|
frame: 335-357 AlienArmature|Alien_Punch
|
||||||
frame: 358-379 AlienArmature|Alien_Run
|
frame: 358-379 AlienArmature|Alien_Run
|
||||||
frame: 380-401 AlienArmature|Alien_RunHold
|
frame: 380-401 AlienArmature|Alien_RunHold
|
||||||
frame: 402-432 AlienArmature|Alien_RunningJump
|
frame: 402-432 AlienArmature|Alien_RunningJump
|
||||||
frame: 433-459 AlienArmature|Alien_Sitting
|
frame: 433-459 AlienArmature|Alien_Sitting
|
||||||
frame: 460-475 AlienArmature|Alien_Standing
|
frame: 460-475 AlienArmature|Alien_Standing
|
||||||
frame: 476-508 AlienArmature|Alien_Swimming
|
frame: 476-508 AlienArmature|Alien_Swimming
|
||||||
frame: 509-534 AlienArmature|Alien_SwordSlash
|
frame: 509-534 AlienArmature|Alien_SwordSlash
|
||||||
frame: 535-560 AlienArmature|Alien_Walk
|
frame: 535-560 AlienArmature|Alien_Walk
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
frame: 0-50 AlienArmature|Alien_Clapping
|
frame: 0-50 AlienArmature|Alien_Clapping
|
||||||
frame: 51-106 AlienArmature|Alien_Death
|
frame: 51-106 AlienArmature|Alien_Death
|
||||||
frame: 107-207 AlienArmature|Alien_Idle
|
frame: 107-207 AlienArmature|Alien_Idle
|
||||||
frame: 208-308 AlienArmature|Alien_IdleHold
|
frame: 208-308 AlienArmature|Alien_IdleHold
|
||||||
frame: 309-334 AlienArmature|Alien_Jump
|
frame: 309-334 AlienArmature|Alien_Jump
|
||||||
frame: 335-357 AlienArmature|Alien_Punch
|
frame: 335-357 AlienArmature|Alien_Punch
|
||||||
frame: 358-379 AlienArmature|Alien_Run
|
frame: 358-379 AlienArmature|Alien_Run
|
||||||
frame: 380-401 AlienArmature|Alien_RunHold
|
frame: 380-401 AlienArmature|Alien_RunHold
|
||||||
frame: 402-432 AlienArmature|Alien_RunningJump
|
frame: 402-432 AlienArmature|Alien_RunningJump
|
||||||
frame: 433-459 AlienArmature|Alien_Sitting
|
frame: 433-459 AlienArmature|Alien_Sitting
|
||||||
frame: 460-475 AlienArmature|Alien_Standing
|
frame: 460-475 AlienArmature|Alien_Standing
|
||||||
frame: 476-508 AlienArmature|Alien_Swimming
|
frame: 476-508 AlienArmature|Alien_Swimming
|
||||||
frame: 509-534 AlienArmature|Alien_SwordSlash
|
frame: 509-534 AlienArmature|Alien_SwordSlash
|
||||||
frame: 535-560 AlienArmature|Alien_Walk
|
frame: 535-560 AlienArmature|Alien_Walk
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
frame: 0-506 Take 001
|
frame: 0-506 Take 001
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
frame: 0-100 Idle
|
frame: 0-100 Idle
|
||||||
frame: 101-125 Dance
|
frame: 101-125 Dance
|
||||||
frame: 126-169 Death
|
frame: 126-169 Death
|
||||||
frame: 170-215 Hello
|
frame: 170-215 Hello
|
||||||
frame: 216-228 HitRecieve_1
|
frame: 216-228 HitRecieve_1
|
||||||
frame: 229-241 HitRecieve_2
|
frame: 229-241 HitRecieve_2
|
||||||
frame: 242-268 Jump
|
frame: 242-268 Jump
|
||||||
frame: 269-310 No
|
frame: 269-310 No
|
||||||
frame: 311-353 Pickup
|
frame: 311-353 Pickup
|
||||||
frame: 354-371 Punch
|
frame: 354-371 Punch
|
||||||
frame: 372-397 Run
|
frame: 372-397 Run
|
||||||
frame: 398-423 Run_Holding
|
frame: 398-423 Run_Holding
|
||||||
frame: 424-449 Run_Tall
|
frame: 424-449 Run_Tall
|
||||||
frame: 450-465 Shoot
|
frame: 450-465 Shoot
|
||||||
frame: 466-484 SwordSlash
|
frame: 466-484 SwordSlash
|
||||||
frame: 485-510 Walk
|
frame: 485-510 Walk
|
||||||
frame: 511-536 Walk_Holding
|
frame: 511-536 Walk_Holding
|
||||||
frame: 537-562 Walk_Tall
|
frame: 537-562 Walk_Tall
|
||||||
frame: 563-604 Yes
|
frame: 563-604 Yes
|
||||||
frame: 605-623 Kick
|
frame: 605-623 Kick
|
||||||
frame: 624-648 RobotArmature|Dance
|
frame: 624-648 RobotArmature|Dance
|
||||||
frame: 649-692 RobotArmature|Death
|
frame: 649-692 RobotArmature|Death
|
||||||
frame: 693-738 RobotArmature|Hello
|
frame: 693-738 RobotArmature|Hello
|
||||||
frame: 739-751 RobotArmature|HitRecieve_1
|
frame: 739-751 RobotArmature|HitRecieve_1
|
||||||
frame: 752-764 RobotArmature|HitRecieve_2
|
frame: 752-764 RobotArmature|HitRecieve_2
|
||||||
frame: 765-865 RobotArmature|Idle
|
frame: 765-865 RobotArmature|Idle
|
||||||
frame: 866-892 RobotArmature|Jump
|
frame: 866-892 RobotArmature|Jump
|
||||||
frame: 893-911 RobotArmature|Kick
|
frame: 893-911 RobotArmature|Kick
|
||||||
frame: 912-953 RobotArmature|No
|
frame: 912-953 RobotArmature|No
|
||||||
frame: 954-996 RobotArmature|Pickup
|
frame: 954-996 RobotArmature|Pickup
|
||||||
frame: 997-1014 RobotArmature|Punch
|
frame: 997-1014 RobotArmature|Punch
|
||||||
frame: 1015-1040 RobotArmature|Run
|
frame: 1015-1040 RobotArmature|Run
|
||||||
frame: 1041-1066 RobotArmature|Run_Holding
|
frame: 1041-1066 RobotArmature|Run_Holding
|
||||||
frame: 1067-1092 RobotArmature|Run_Tall
|
frame: 1067-1092 RobotArmature|Run_Tall
|
||||||
frame: 1093-1108 RobotArmature|Shoot
|
frame: 1093-1108 RobotArmature|Shoot
|
||||||
frame: 1109-1127 RobotArmature|SwordSlash
|
frame: 1109-1127 RobotArmature|SwordSlash
|
||||||
frame: 1128-1153 RobotArmature|Walk
|
frame: 1128-1153 RobotArmature|Walk
|
||||||
frame: 1154-1179 RobotArmature|Walk_Holding
|
frame: 1154-1179 RobotArmature|Walk_Holding
|
||||||
frame: 1180-1205 RobotArmature|Walk_Tall
|
frame: 1180-1205 RobotArmature|Walk_Tall
|
||||||
frame: 1206-1247 RobotArmature|Yes
|
frame: 1206-1247 RobotArmature|Yes
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
frame: 0-24 Dance
|
frame: 0-24 Dance
|
||||||
frame: 25-68 Death
|
frame: 25-68 Death
|
||||||
frame: 69-114 Hello
|
frame: 69-114 Hello
|
||||||
frame: 115-127 HitRecieve_1
|
frame: 115-127 HitRecieve_1
|
||||||
frame: 128-140 HitRecieve_2
|
frame: 128-140 HitRecieve_2
|
||||||
frame: 141-241 Idle
|
frame: 141-241 Idle
|
||||||
frame: 242-268 Jump
|
frame: 242-268 Jump
|
||||||
frame: 269-287 Kick
|
frame: 269-287 Kick
|
||||||
frame: 288-329 No
|
frame: 288-329 No
|
||||||
frame: 330-372 Pickup
|
frame: 330-372 Pickup
|
||||||
frame: 373-390 Punch
|
frame: 373-390 Punch
|
||||||
frame: 391-416 Run
|
frame: 391-416 Run
|
||||||
frame: 417-442 Run_Tall
|
frame: 417-442 Run_Tall
|
||||||
frame: 443-458 Shoot
|
frame: 443-458 Shoot
|
||||||
frame: 459-477 SwordSlash
|
frame: 459-477 SwordSlash
|
||||||
frame: 478-503 Walk
|
frame: 478-503 Walk
|
||||||
frame: 504-529 Walk_Tall
|
frame: 504-529 Walk_Tall
|
||||||
frame: 530-572 Yes
|
frame: 530-572 Yes
|
||||||
frame: 573-597 RobotArmature|Dance
|
frame: 573-597 RobotArmature|Dance
|
||||||
frame: 598-641 RobotArmature|Death
|
frame: 598-641 RobotArmature|Death
|
||||||
frame: 642-687 RobotArmature|Hello
|
frame: 642-687 RobotArmature|Hello
|
||||||
frame: 688-700 RobotArmature|HitRecieve_1
|
frame: 688-700 RobotArmature|HitRecieve_1
|
||||||
frame: 701-713 RobotArmature|HitRecieve_2
|
frame: 701-713 RobotArmature|HitRecieve_2
|
||||||
frame: 714-814 RobotArmature|Idle
|
frame: 714-814 RobotArmature|Idle
|
||||||
frame: 815-841 RobotArmature|Jump
|
frame: 815-841 RobotArmature|Jump
|
||||||
frame: 842-860 RobotArmature|Kick
|
frame: 842-860 RobotArmature|Kick
|
||||||
frame: 861-902 RobotArmature|No
|
frame: 861-902 RobotArmature|No
|
||||||
frame: 903-945 RobotArmature|Pickup
|
frame: 903-945 RobotArmature|Pickup
|
||||||
frame: 946-963 RobotArmature|Punch
|
frame: 946-963 RobotArmature|Punch
|
||||||
frame: 964-989 RobotArmature|Run
|
frame: 964-989 RobotArmature|Run
|
||||||
frame: 990-1015 RobotArmature|Run_Tall
|
frame: 990-1015 RobotArmature|Run_Tall
|
||||||
frame: 1016-1031 RobotArmature|Shoot
|
frame: 1016-1031 RobotArmature|Shoot
|
||||||
frame: 1032-1050 RobotArmature|SwordSlash
|
frame: 1032-1050 RobotArmature|SwordSlash
|
||||||
frame: 1051-1076 RobotArmature|Walk
|
frame: 1051-1076 RobotArmature|Walk
|
||||||
frame: 1077-1102 RobotArmature|Walk_Tall
|
frame: 1077-1102 RobotArmature|Walk_Tall
|
||||||
frame: 1103-1145 RobotArmature|Yes
|
frame: 1103-1145 RobotArmature|Yes
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
frame: 0-100 Idle
|
frame: 0-100 Idle
|
||||||
frame: 101-125 Dance
|
frame: 101-125 Dance
|
||||||
frame: 126-169 Death
|
frame: 126-169 Death
|
||||||
frame: 170-215 Hello
|
frame: 170-215 Hello
|
||||||
frame: 216-228 HitRecieve_1
|
frame: 216-228 HitRecieve_1
|
||||||
frame: 229-241 HitRecieve_2
|
frame: 229-241 HitRecieve_2
|
||||||
frame: 242-268 Jump
|
frame: 242-268 Jump
|
||||||
frame: 269-311 No
|
frame: 269-311 No
|
||||||
frame: 312-354 Pickup
|
frame: 312-354 Pickup
|
||||||
frame: 355-372 Punch
|
frame: 355-372 Punch
|
||||||
frame: 373-398 Run
|
frame: 373-398 Run
|
||||||
frame: 399-424 Run_Holding
|
frame: 399-424 Run_Holding
|
||||||
frame: 425-440 Shoot
|
frame: 425-440 Shoot
|
||||||
frame: 441-459 SwordSlash
|
frame: 441-459 SwordSlash
|
||||||
frame: 460-485 Walk
|
frame: 460-485 Walk
|
||||||
frame: 486-528 Yes
|
frame: 486-528 Yes
|
||||||
frame: 529-547 Kick
|
frame: 529-547 Kick
|
||||||
frame: 548-573 Walk_Holding
|
frame: 548-573 Walk_Holding
|
||||||
frame: 574-598 RobotArmature|Dance
|
frame: 574-598 RobotArmature|Dance
|
||||||
frame: 599-642 RobotArmature|Death
|
frame: 599-642 RobotArmature|Death
|
||||||
frame: 643-688 RobotArmature|Hello
|
frame: 643-688 RobotArmature|Hello
|
||||||
frame: 689-701 RobotArmature|HitRecieve_1
|
frame: 689-701 RobotArmature|HitRecieve_1
|
||||||
frame: 702-714 RobotArmature|HitRecieve_2
|
frame: 702-714 RobotArmature|HitRecieve_2
|
||||||
frame: 715-815 RobotArmature|Idle
|
frame: 715-815 RobotArmature|Idle
|
||||||
frame: 816-842 RobotArmature|Jump
|
frame: 816-842 RobotArmature|Jump
|
||||||
frame: 843-861 RobotArmature|Kick
|
frame: 843-861 RobotArmature|Kick
|
||||||
frame: 862-904 RobotArmature|No
|
frame: 862-904 RobotArmature|No
|
||||||
frame: 905-947 RobotArmature|Pickup
|
frame: 905-947 RobotArmature|Pickup
|
||||||
frame: 948-965 RobotArmature|Punch
|
frame: 948-965 RobotArmature|Punch
|
||||||
frame: 966-991 RobotArmature|Run
|
frame: 966-991 RobotArmature|Run
|
||||||
frame: 992-1017 RobotArmature|Run_Holding
|
frame: 992-1017 RobotArmature|Run_Holding
|
||||||
frame: 1018-1033 RobotArmature|Shoot
|
frame: 1018-1033 RobotArmature|Shoot
|
||||||
frame: 1034-1052 RobotArmature|SwordSlash
|
frame: 1034-1052 RobotArmature|SwordSlash
|
||||||
frame: 1053-1078 RobotArmature|Walk
|
frame: 1053-1078 RobotArmature|Walk
|
||||||
frame: 1079-1104 RobotArmature|Walk_Holding
|
frame: 1079-1104 RobotArmature|Walk_Holding
|
||||||
frame: 1105-1147 RobotArmature|Yes
|
frame: 1105-1147 RobotArmature|Yes
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
frame: 0-100 Idle
|
frame: 0-100 Idle
|
||||||
frame: 101-125 Dance
|
frame: 101-125 Dance
|
||||||
frame: 126-169 Death
|
frame: 126-169 Death
|
||||||
frame: 170-215 Hello
|
frame: 170-215 Hello
|
||||||
frame: 216-228 HitRecieve_1
|
frame: 216-228 HitRecieve_1
|
||||||
frame: 229-241 HitRecieve_2
|
frame: 229-241 HitRecieve_2
|
||||||
frame: 242-268 Jump
|
frame: 242-268 Jump
|
||||||
frame: 269-311 No
|
frame: 269-311 No
|
||||||
frame: 312-354 Pickup
|
frame: 312-354 Pickup
|
||||||
frame: 355-372 Punch
|
frame: 355-372 Punch
|
||||||
frame: 373-398 Run
|
frame: 373-398 Run
|
||||||
frame: 399-424 Run_Holding
|
frame: 399-424 Run_Holding
|
||||||
frame: 425-440 Shoot
|
frame: 425-440 Shoot
|
||||||
frame: 441-459 SwordSlash
|
frame: 441-459 SwordSlash
|
||||||
frame: 460-485 Walk
|
frame: 460-485 Walk
|
||||||
frame: 486-528 Yes
|
frame: 486-528 Yes
|
||||||
frame: 529-547 Kick
|
frame: 529-547 Kick
|
||||||
frame: 548-573 Walk_Holding
|
frame: 548-573 Walk_Holding
|
||||||
frame: 574-598 RobotArmature|Dance
|
frame: 574-598 RobotArmature|Dance
|
||||||
frame: 599-642 RobotArmature|Death
|
frame: 599-642 RobotArmature|Death
|
||||||
frame: 643-688 RobotArmature|Hello
|
frame: 643-688 RobotArmature|Hello
|
||||||
frame: 689-701 RobotArmature|HitRecieve_1
|
frame: 689-701 RobotArmature|HitRecieve_1
|
||||||
frame: 702-714 RobotArmature|HitRecieve_2
|
frame: 702-714 RobotArmature|HitRecieve_2
|
||||||
frame: 715-815 RobotArmature|Idle
|
frame: 715-815 RobotArmature|Idle
|
||||||
frame: 816-842 RobotArmature|Jump
|
frame: 816-842 RobotArmature|Jump
|
||||||
frame: 843-861 RobotArmature|Kick
|
frame: 843-861 RobotArmature|Kick
|
||||||
frame: 862-904 RobotArmature|No
|
frame: 862-904 RobotArmature|No
|
||||||
frame: 905-947 RobotArmature|Pickup
|
frame: 905-947 RobotArmature|Pickup
|
||||||
frame: 948-965 RobotArmature|Punch
|
frame: 948-965 RobotArmature|Punch
|
||||||
frame: 966-991 RobotArmature|Run
|
frame: 966-991 RobotArmature|Run
|
||||||
frame: 992-1017 RobotArmature|Run_Holding
|
frame: 992-1017 RobotArmature|Run_Holding
|
||||||
frame: 1018-1033 RobotArmature|Shoot
|
frame: 1018-1033 RobotArmature|Shoot
|
||||||
frame: 1034-1052 RobotArmature|SwordSlash
|
frame: 1034-1052 RobotArmature|SwordSlash
|
||||||
frame: 1053-1078 RobotArmature|Walk
|
frame: 1053-1078 RobotArmature|Walk
|
||||||
frame: 1079-1104 RobotArmature|Walk_Holding
|
frame: 1079-1104 RobotArmature|Walk_Holding
|
||||||
frame: 1105-1147 RobotArmature|Yes
|
frame: 1105-1147 RobotArmature|Yes
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
frame: 0-0 Idle
|
frame: 0-0 Idle
|
||||||
|
|
|
@ -1,182 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<!-- This file license is CC0 (https://creativecommons.org/publicdomain/zero/1.0/). -->
|
|
||||||
<!-- Original code by @SpartanJ https://github.com/SpartanJ/eepp/blob/8552941da19380d7a629c4da80a976aec5d39e5c/bin/emscripten-fs.html -->
|
|
||||||
<html lang="en-us">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
|
|
||||||
<!-- <title>Example</title> -->
|
|
||||||
<style>
|
|
||||||
body { margin: 0; background-color: black }
|
|
||||||
.emscripten {
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
border: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
display: block;
|
|
||||||
image-rendering: optimizeSpeed;
|
|
||||||
image-rendering: -moz-crisp-edges;
|
|
||||||
image-rendering: -o-crisp-edges;
|
|
||||||
image-rendering: -webkit-optimize-contrast;
|
|
||||||
image-rendering: optimize-contrast;
|
|
||||||
image-rendering: crisp-edges;
|
|
||||||
image-rendering: pixelated;
|
|
||||||
-ms-interpolation-mode: nearest-neighbor;
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: inline-block;
|
|
||||||
border-top: 3px solid #3daee9;
|
|
||||||
border-right: 3px solid transparent;
|
|
||||||
box-sizing: border-box;
|
|
||||||
animation: rotation 1s linear infinite;
|
|
||||||
}
|
|
||||||
.loader-cont {
|
|
||||||
display: flex;
|
|
||||||
width: 100vw;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100vh;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
@keyframes rotation {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
|
||||||
<div id="cont" class="loader-cont">
|
|
||||||
<span class="loader"></span>
|
|
||||||
</div>
|
|
||||||
<script type='text/javascript'>
|
|
||||||
var loaderCont;
|
|
||||||
|
|
||||||
function getDemoScript(name) {
|
|
||||||
if (name)
|
|
||||||
return name;
|
|
||||||
return "index.js";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getParameter(name) {
|
|
||||||
let url_string = window.location.href;
|
|
||||||
let url = new URL(url_string);
|
|
||||||
return url.searchParams.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadScript(url, callback) {
|
|
||||||
loaderCont = document.getElementById('cont');
|
|
||||||
let head = document.head;
|
|
||||||
let script = document.createElement('script');
|
|
||||||
script.type = 'text/javascript';
|
|
||||||
script.src = url;
|
|
||||||
script.onreadystatechange = callback;
|
|
||||||
script.onload = callback;
|
|
||||||
head.appendChild(script);
|
|
||||||
}
|
|
||||||
|
|
||||||
var Module = {
|
|
||||||
preRun: [],
|
|
||||||
postRun: [],
|
|
||||||
print: (function() {
|
|
||||||
return function(text) {
|
|
||||||
text = Array.prototype.slice.call(arguments).join(' ');
|
|
||||||
console.log(text);
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
printErr: function(text) {
|
|
||||||
text = Array.prototype.slice.call(arguments).join(' ');
|
|
||||||
console.error(text);
|
|
||||||
},
|
|
||||||
canvas: (function() {
|
|
||||||
var canvas = document.getElementById('canvas');
|
|
||||||
var dpi = window.devicePixelRatio;
|
|
||||||
canvas.width = window.innerWidth*dpi;
|
|
||||||
canvas.height = window.innerHeight*dpi;
|
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
if (canvas.canResize) {
|
|
||||||
dpi = window.devicePixelRatio;
|
|
||||||
canvas.width = window.innerWidth*dpi;
|
|
||||||
canvas.height = window.innerHeight*dpi;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return canvas;
|
|
||||||
})(),
|
|
||||||
setStatus: function(text) {
|
|
||||||
console.log("status: " + text);
|
|
||||||
if (text == "Running...")
|
|
||||||
loaderCont.style.display = 'none';
|
|
||||||
},
|
|
||||||
monitorRunDependencies: function(left) {
|
|
||||||
// no run dependencies to log
|
|
||||||
},
|
|
||||||
arguments: window.location.search.substr(1).split('&')
|
|
||||||
};
|
|
||||||
window.onerror = function() {
|
|
||||||
console.log("onerror: " + event);
|
|
||||||
};
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
loadScript(getDemoScript(getParameter("run")));
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{{ SCRIPT }}}
|
|
||||||
|
|
||||||
<button onclick="openFullscreen();" style="position:relative; z-index: 1000; float: right;">⛶</button>
|
|
||||||
<!-- <button id="btn-audio" onclick="toggleAudio();" style="position:relative; z-index: 1000; float: right;">M</button> -->
|
|
||||||
<script>
|
|
||||||
function openFullscreen() {
|
|
||||||
var canvas = document.getElementById("canvas");
|
|
||||||
if (canvas.requestFullscreen) {
|
|
||||||
canvas.requestFullscreen();
|
|
||||||
} else if (canvas.webkitRequestFullscreen) { /* Safari */
|
|
||||||
canvas.webkitRequestFullscreen();
|
|
||||||
} else if (canvas.msRequestFullscreen) { /* IE11 */
|
|
||||||
canvas.msRequestFullscreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- <script type='text/javascript'>
|
|
||||||
var audioBtn = document.querySelector('#btn-audio');
|
|
||||||
|
|
||||||
// An array of all contexts to resume on the page
|
|
||||||
const audioContexList = [];
|
|
||||||
(function() {
|
|
||||||
// A proxy object to intercept AudioContexts and
|
|
||||||
// add them to the array for tracking and resuming later
|
|
||||||
self.AudioContext = new Proxy(self.AudioContext, {
|
|
||||||
construct(target, args) {
|
|
||||||
const result = new target(...args);
|
|
||||||
audioContexList.push(result);
|
|
||||||
if (result.state == "suspended") audioBtn.value = "RESUME";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
function toggleAudio() {
|
|
||||||
var resumed = false;
|
|
||||||
audioContexList.forEach(ctx => {
|
|
||||||
if (ctx.state == "suspended") { ctx.resume(); resumed = true; }
|
|
||||||
else if (ctx.state == "running") ctx.suspend();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (resumed) audioBtn.value = "SUSPEND";
|
|
||||||
else audioBtn.value = "RESUME";
|
|
||||||
}
|
|
||||||
</script> -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,284 +0,0 @@
|
||||||
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
|
|
|
@ -1,114 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local translate = require "core.doc.translate"
|
|
||||||
local config = require "core.config"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
local command = require "core.command"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
|
|
||||||
|
|
||||||
config.autoinsert_map = {
|
|
||||||
["["] = "]",
|
|
||||||
["{"] = "}",
|
|
||||||
["("] = ")",
|
|
||||||
['"'] = '"',
|
|
||||||
["'"] = "'",
|
|
||||||
["`"] = "`",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local function is_closer(chr)
|
|
||||||
for _, v in pairs(config.autoinsert_map) do
|
|
||||||
if v == chr then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function count_char(text, chr)
|
|
||||||
local count = 0
|
|
||||||
for _ in text:gmatch(chr) do
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
return count
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local on_text_input = DocView.on_text_input
|
|
||||||
|
|
||||||
function DocView:on_text_input(text)
|
|
||||||
local mapping = config.autoinsert_map[text]
|
|
||||||
|
|
||||||
-- prevents plugin from operating on `CommandView`
|
|
||||||
if getmetatable(self) ~= DocView then
|
|
||||||
return on_text_input(self, text)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- wrap selection if we have a selection
|
|
||||||
if mapping and self.doc:has_selection() then
|
|
||||||
local l1, c1, l2, c2, swap = self.doc:get_selection(true)
|
|
||||||
self.doc:insert(l2, c2, mapping)
|
|
||||||
self.doc:insert(l1, c1, text)
|
|
||||||
self.doc:set_selection(l1, c1, l2, c2 + 2, swap)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- skip inserting closing text
|
|
||||||
local chr = self.doc:get_char(self.doc:get_selection())
|
|
||||||
if text == chr and is_closer(chr) then
|
|
||||||
self.doc:move_to(1)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- don't insert closing quote if we have a non-even number on this line
|
|
||||||
local line = self.doc:get_selection()
|
|
||||||
if text == mapping and count_char(self.doc.lines[line], text) % 2 == 1 then
|
|
||||||
return on_text_input(self, text)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- auto insert closing bracket
|
|
||||||
if mapping and (chr:find("%s") or is_closer(chr) and chr ~= '"') then
|
|
||||||
on_text_input(self, text)
|
|
||||||
on_text_input(self, mapping)
|
|
||||||
self.doc:move_to(-1)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
on_text_input(self, text)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local function predicate()
|
|
||||||
return getmetatable(core.active_view) == DocView
|
|
||||||
and not core.active_view.doc:has_selection()
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add(predicate, {
|
|
||||||
["autoinsert:backspace"] = function()
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
local l, c = doc:get_selection()
|
|
||||||
local chr = doc:get_char(l, c)
|
|
||||||
if config.autoinsert_map[doc:get_char(l, c - 1)] and is_closer(chr) then
|
|
||||||
doc:delete_to(1)
|
|
||||||
end
|
|
||||||
command.perform "doc:backspace"
|
|
||||||
end,
|
|
||||||
|
|
||||||
["autoinsert:delete-to-previous-word-start"] = function()
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
local le, ce = translate.previous_word_start(doc, doc:get_selection())
|
|
||||||
while true do
|
|
||||||
local l, c = doc:get_selection()
|
|
||||||
if l == le and c == ce then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
command.perform "autoinsert:backspace"
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["backspace"] = "autoinsert:backspace",
|
|
||||||
["ctrl+backspace"] = "autoinsert:delete-to-previous-word-start",
|
|
||||||
["ctrl+shift+backspace"] = "autoinsert:delete-to-previous-word-start",
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
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
|
|
|
@ -1,35 +0,0 @@
|
||||||
require "plugins.reflow"
|
|
||||||
local config = require "core.config"
|
|
||||||
local command = require "core.command"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
config.autowrap_files = { "%.md$", "%.txt$" }
|
|
||||||
|
|
||||||
|
|
||||||
local on_text_input = DocView.on_text_input
|
|
||||||
|
|
||||||
DocView.on_text_input = function(self, ...)
|
|
||||||
on_text_input(self, ...)
|
|
||||||
|
|
||||||
-- early-exit if the filename does not match a file type pattern
|
|
||||||
local filename = self.doc.filename or ""
|
|
||||||
local matched = false
|
|
||||||
for _, ptn in ipairs(config.autowrap_files) do
|
|
||||||
if filename:match(ptn) then
|
|
||||||
matched = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not matched then return end
|
|
||||||
|
|
||||||
-- do automatic reflow on line if we're typing at the end of the line and have
|
|
||||||
-- reached the line limit
|
|
||||||
local line, col = self.doc:get_selection()
|
|
||||||
local text = self.doc:get_text(line, 1, line, math.huge)
|
|
||||||
if #text >= config.line_limit and col > #text then
|
|
||||||
command.perform("doc:select-lines")
|
|
||||||
command.perform("reflow:reflow")
|
|
||||||
command.perform("doc:move-to-next-char")
|
|
||||||
command.perform("doc:move-to-previous-char")
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,117 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local style = require "core.style"
|
|
||||||
local command = require "core.command"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
local bracket_maps = {
|
|
||||||
-- [ ] ( ) { }
|
|
||||||
{ [91] = 93, [40] = 41, [123] = 125, step = 1 },
|
|
||||||
-- ] [ ) ( } {
|
|
||||||
{ [93] = 91, [41] = 40, [125] = 123, step = -1 },
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local function get_matching_bracket(doc, line, col, line_limit, open_byte, close_byte, step)
|
|
||||||
local end_line = line + line_limit * step
|
|
||||||
local depth = 0
|
|
||||||
|
|
||||||
while line ~= end_line do
|
|
||||||
local byte = doc.lines[line]:byte(col)
|
|
||||||
if byte == open_byte then
|
|
||||||
depth = depth + 1
|
|
||||||
elseif byte == close_byte then
|
|
||||||
depth = depth - 1
|
|
||||||
if depth == 0 then return line, col end
|
|
||||||
end
|
|
||||||
|
|
||||||
local prev_line, prev_col = line, col
|
|
||||||
line, col = doc:position_offset(line, col, step)
|
|
||||||
if line == prev_line and col == prev_col then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local state = {}
|
|
||||||
|
|
||||||
local function update_state(line_limit)
|
|
||||||
line_limit = line_limit or math.huge
|
|
||||||
|
|
||||||
-- reset if we don't have a document (eg. DocView isn't focused)
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
if not doc then
|
|
||||||
state = {}
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- early exit if nothing has changed since the last call
|
|
||||||
local line, col = doc:get_selection()
|
|
||||||
local change_id = doc:get_change_id()
|
|
||||||
if state.doc == doc and state.line == line and state.col == col
|
|
||||||
and state.change_id == change_id and state.limit == line_limit then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- find matching bracket if we're on a bracket
|
|
||||||
local line2, col2
|
|
||||||
for _, map in ipairs(bracket_maps) do
|
|
||||||
for i = 0, -1, -1 do
|
|
||||||
local line, col = doc:position_offset(line, col, i)
|
|
||||||
local open = doc.lines[line]:byte(col)
|
|
||||||
local close = map[open]
|
|
||||||
if close then
|
|
||||||
line2, col2 = get_matching_bracket(doc, line, col, line_limit, open, close, map.step)
|
|
||||||
goto found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
::found::
|
|
||||||
|
|
||||||
-- update
|
|
||||||
state = {
|
|
||||||
change_id = change_id,
|
|
||||||
doc = doc,
|
|
||||||
line = line,
|
|
||||||
col = col,
|
|
||||||
line2 = line2,
|
|
||||||
col2 = col2,
|
|
||||||
limit = line_limit,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local update = DocView.update
|
|
||||||
|
|
||||||
function DocView:update(...)
|
|
||||||
update(self, ...)
|
|
||||||
update_state(100)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local draw_line_text = DocView.draw_line_text
|
|
||||||
|
|
||||||
function DocView:draw_line_text(idx, x, y)
|
|
||||||
draw_line_text(self, idx, x, y)
|
|
||||||
|
|
||||||
if self.doc == state.doc and idx == state.line2 then
|
|
||||||
local color = style.bracketmatch_color or style.syntax["function"]
|
|
||||||
local x1 = x + self:get_col_x_offset(idx, state.col2)
|
|
||||||
local x2 = x + self:get_col_x_offset(idx, state.col2 + 1)
|
|
||||||
local h = math.ceil(1 * SCALE)
|
|
||||||
renderer.draw_rect(x1, y + self:get_line_height() - h, x2 - x1, h, color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["bracket-match:move-to-matching"] = function()
|
|
||||||
update_state()
|
|
||||||
if state.line2 then
|
|
||||||
core.active_view.doc:set_selection(state.line2, state.col2)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add { ["ctrl+m"] = "bracket-match:move-to-matching" }
|
|
|
@ -1,53 +0,0 @@
|
||||||
-- CloseConfirmX plugin for lite text editor
|
|
||||||
-- implementation by chekoopa
|
|
||||||
|
|
||||||
local core = require "core"
|
|
||||||
local config = require "core.config"
|
|
||||||
|
|
||||||
config.closeconfirmx_use_legacy = false
|
|
||||||
config.closeconfirmx_use_short_name = true
|
|
||||||
|
|
||||||
local legacy_confirm = core.confirm_close_all
|
|
||||||
|
|
||||||
local function commandful_confirm()
|
|
||||||
local dirty_count = 0
|
|
||||||
local dirty_name
|
|
||||||
for _, doc in ipairs(core.docs) do
|
|
||||||
if doc:is_dirty() then
|
|
||||||
dirty_count = dirty_count + 1
|
|
||||||
dirty_name = doc:get_name()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if dirty_count > 0 then
|
|
||||||
local text
|
|
||||||
if dirty_count == 1 then
|
|
||||||
if config.closeconfirmx_use_short_name then
|
|
||||||
dirty_name = dirty_name:match("[^/%\\]*$")
|
|
||||||
end
|
|
||||||
text = string.format("Unsaved changes in \"%s\"; Confirm Exit", dirty_name)
|
|
||||||
else
|
|
||||||
text = string.format("Unsaved changes in %d docs; Confirm Exit", dirty_count)
|
|
||||||
end
|
|
||||||
core.command_view:enter(text, function(_, item)
|
|
||||||
if item.text:match("^[cC]") then
|
|
||||||
core.quit(true)
|
|
||||||
end
|
|
||||||
end, function(text)
|
|
||||||
local items = {}
|
|
||||||
if not text:find("^[^sS]") then table.insert(items, "Stay here") end
|
|
||||||
if not text:find("^[^cC]") then table.insert(items, "Close Without Saving") end
|
|
||||||
return items
|
|
||||||
end)
|
|
||||||
-- as we delegate a choice inside the callback,
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.confirm_close_all()
|
|
||||||
if config.closeconfirmx_use_legacy then
|
|
||||||
return legacy_confirm()
|
|
||||||
else
|
|
||||||
return commandful_confirm()
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,54 +0,0 @@
|
||||||
local common = require "core.common"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
|
|
||||||
local white = { common.color "#ffffff" }
|
|
||||||
local black = { common.color "#000000" }
|
|
||||||
local tmp = {}
|
|
||||||
|
|
||||||
|
|
||||||
local function draw_color_previews(self, idx, x, y, ptn, base, nibbles)
|
|
||||||
local text = self.doc.lines[idx]
|
|
||||||
local s, e = 0, 0
|
|
||||||
|
|
||||||
while true do
|
|
||||||
s, e = text:find(ptn, e + 1)
|
|
||||||
if not s then break end
|
|
||||||
|
|
||||||
local str = text:sub(s, e)
|
|
||||||
local r, g, b = str:match(ptn)
|
|
||||||
r, g, b = tonumber(r, base), tonumber(g, base), tonumber(b, base)
|
|
||||||
|
|
||||||
-- #123 becomes #112233
|
|
||||||
if nibbles then
|
|
||||||
r = r * 16
|
|
||||||
g = g * 16
|
|
||||||
b = b * 16
|
|
||||||
end
|
|
||||||
|
|
||||||
local x1 = x + self:get_col_x_offset(idx, s)
|
|
||||||
local x2 = x + self:get_col_x_offset(idx, e + 1)
|
|
||||||
local oy = self:get_line_text_y_offset()
|
|
||||||
|
|
||||||
local text_color = math.max(r, g, b) < 128 and white or black
|
|
||||||
tmp[1], tmp[2], tmp[3] = r, g, b
|
|
||||||
|
|
||||||
local l1, _, l2, _ = self.doc:get_selection(true)
|
|
||||||
|
|
||||||
if not (self.doc:has_selection() and idx >= l1 and idx <= l2) then
|
|
||||||
renderer.draw_rect(x1, y, x2 - x1, self:get_line_height(), tmp)
|
|
||||||
renderer.draw_text(self:get_font(), str, x1, y + oy, text_color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local draw_line_text = DocView.draw_line_text
|
|
||||||
|
|
||||||
function DocView:draw_line_text(idx, x, y)
|
|
||||||
draw_line_text(self, idx, x, y)
|
|
||||||
draw_color_previews(self, idx, x, y, "#(%x%x)(%x%x)(%x%x)%f[%W]", 16)
|
|
||||||
draw_color_previews(self, idx, x, y, "#(%x)(%x)(%x)%f[%W]", 16, true) -- support #fff css format
|
|
||||||
draw_color_previews(self, idx, x, y, "rgb?%((%d+)%D+(%d+)%D+(%d+).-%)", 10)
|
|
||||||
draw_color_previews(self, idx, x, y, "rgba?%((%d+)%D+(%d+)%D+(%d+).-%)", 10)
|
|
||||||
end
|
|
|
@ -1,383 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local command = require "core.command"
|
|
||||||
local common = require "core.common"
|
|
||||||
local config = require "core.config"
|
|
||||||
local style = require "core.style"
|
|
||||||
local View = require "core.view"
|
|
||||||
|
|
||||||
config.console_size = 250 * SCALE
|
|
||||||
config.max_console_lines = 200
|
|
||||||
config.autoscroll_console = true
|
|
||||||
|
|
||||||
local files = {
|
|
||||||
script = core.temp_filename(PLATFORM == "Windows" and ".bat"),
|
|
||||||
script2 = core.temp_filename(PLATFORM == "Windows" and ".bat"),
|
|
||||||
output = core.temp_filename(),
|
|
||||||
complete = core.temp_filename(),
|
|
||||||
}
|
|
||||||
|
|
||||||
local console = {}
|
|
||||||
|
|
||||||
local views = {}
|
|
||||||
local pending_threads = {}
|
|
||||||
local thread_active = false
|
|
||||||
local output = nil
|
|
||||||
local output_id = 0
|
|
||||||
local visible = false
|
|
||||||
|
|
||||||
function console.clear()
|
|
||||||
output = { { text = "", time = 0 } }
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function read_file(filename, offset)
|
|
||||||
local fp = io.open(filename, "rb")
|
|
||||||
fp:seek("set", offset or 0)
|
|
||||||
local res = fp:read("*a")
|
|
||||||
fp:close()
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function write_file(filename, text)
|
|
||||||
local fp = io.open(filename, "w")
|
|
||||||
fp:write(text)
|
|
||||||
fp:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function lines(text)
|
|
||||||
return (text .. "\n"):gmatch("(.-)\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function push_output(str, opt)
|
|
||||||
local first = true
|
|
||||||
for line in lines(str) do
|
|
||||||
if first then
|
|
||||||
line = table.remove(output).text .. line
|
|
||||||
end
|
|
||||||
line = line:gsub("\x1b%[[%d;]+m", "") -- strip ANSI colors
|
|
||||||
table.insert(output, {
|
|
||||||
text = line,
|
|
||||||
time = os.time(),
|
|
||||||
icon = line:find(opt.error_pattern) and "!"
|
|
||||||
or line:find(opt.warning_pattern) and "i",
|
|
||||||
file_pattern = opt.file_pattern,
|
|
||||||
})
|
|
||||||
if #output > config.max_console_lines then
|
|
||||||
table.remove(output, 1)
|
|
||||||
for view in pairs(views) do
|
|
||||||
view:on_line_removed()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
first = false
|
|
||||||
end
|
|
||||||
output_id = output_id + 1
|
|
||||||
core.redraw = true
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function init_opt(opt)
|
|
||||||
local res = {
|
|
||||||
command = "",
|
|
||||||
file_pattern = "[^?:%s]+%.[^?:%s]+",
|
|
||||||
error_pattern = "error",
|
|
||||||
warning_pattern = "warning",
|
|
||||||
on_complete = function() end,
|
|
||||||
}
|
|
||||||
for k, v in pairs(res) do
|
|
||||||
res[k] = opt[k] or v
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function console.run(opt)
|
|
||||||
opt = init_opt(opt)
|
|
||||||
|
|
||||||
local function thread()
|
|
||||||
-- init script file(s)
|
|
||||||
if PLATFORM == "Windows" then
|
|
||||||
write_file(files.script, opt.command .. "\n")
|
|
||||||
write_file(files.script2, string.format([[
|
|
||||||
@echo off
|
|
||||||
call %q >%q 2>&1
|
|
||||||
echo "" >%q
|
|
||||||
exit
|
|
||||||
]], files.script, files.output, files.complete))
|
|
||||||
system.exec(string.format("call %q", files.script2))
|
|
||||||
else
|
|
||||||
write_file(files.script, string.format([[
|
|
||||||
%s
|
|
||||||
touch %q
|
|
||||||
]], opt.command, files.complete))
|
|
||||||
system.exec(string.format("bash %q >%q 2>&1", files.script, files.output))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- checks output file for change and reads
|
|
||||||
local last_size = 0
|
|
||||||
local function check_output_file()
|
|
||||||
if PLATFORM == "Windows" then
|
|
||||||
local fp = io.open(files.output)
|
|
||||||
if fp then fp:close() end
|
|
||||||
end
|
|
||||||
local info = system.get_file_info(files.output)
|
|
||||||
if info and info.size > last_size then
|
|
||||||
local text = read_file(files.output, last_size)
|
|
||||||
push_output(text, opt)
|
|
||||||
last_size = info.size
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- read output file until we get a file indicating completion
|
|
||||||
while not system.get_file_info(files.complete) do
|
|
||||||
check_output_file()
|
|
||||||
coroutine.yield(0.1)
|
|
||||||
end
|
|
||||||
check_output_file()
|
|
||||||
if output[#output].text ~= "" then
|
|
||||||
push_output("\n", opt)
|
|
||||||
end
|
|
||||||
push_output("!DIVIDER\n", opt)
|
|
||||||
|
|
||||||
-- clean up and finish
|
|
||||||
for _, file in pairs(files) do
|
|
||||||
os.remove(file)
|
|
||||||
end
|
|
||||||
opt.on_complete()
|
|
||||||
|
|
||||||
-- handle pending thread
|
|
||||||
local pending = table.remove(pending_threads, 1)
|
|
||||||
if pending then
|
|
||||||
core.add_thread(pending)
|
|
||||||
else
|
|
||||||
thread_active = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- push/init thread
|
|
||||||
if thread_active then
|
|
||||||
table.insert(pending_threads, thread)
|
|
||||||
else
|
|
||||||
core.add_thread(thread)
|
|
||||||
thread_active = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- make sure static console is visible if it's the only ConsoleView
|
|
||||||
local count = 0
|
|
||||||
for _ in pairs(views) do count = count + 1 end
|
|
||||||
if count == 1 then visible = true end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local ConsoleView = View:extend()
|
|
||||||
|
|
||||||
function ConsoleView:new()
|
|
||||||
ConsoleView.super.new(self)
|
|
||||||
self.scrollable = true
|
|
||||||
self.hovered_idx = -1
|
|
||||||
views[self] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:try_close(...)
|
|
||||||
ConsoleView.super.try_close(self, ...)
|
|
||||||
views[self] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:get_name()
|
|
||||||
return "Console"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:get_line_height()
|
|
||||||
return style.code_font:get_height() * config.line_height
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:get_line_count()
|
|
||||||
return #output - (output[#output].text == "" and 1 or 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:get_scrollable_size()
|
|
||||||
return self:get_line_count() * self:get_line_height() + style.padding.y * 2
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:get_visible_line_range()
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
local min = math.max(1, math.floor(self.scroll.y / lh))
|
|
||||||
return min, min + math.floor(self.size.y / lh) + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:on_mouse_moved(mx, my, ...)
|
|
||||||
ConsoleView.super.on_mouse_moved(self, mx, my, ...)
|
|
||||||
self.hovered_idx = 0
|
|
||||||
for i, item, x,y,w,h in self:each_visible_line() do
|
|
||||||
if mx >= x and my >= y and mx < x + w and my < y + h then
|
|
||||||
if item.text:find(item.file_pattern) then
|
|
||||||
self.hovered_idx = i
|
|
||||||
end
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function resolve_file(name)
|
|
||||||
if system.get_file_info(name) then
|
|
||||||
return name
|
|
||||||
end
|
|
||||||
local filenames = {}
|
|
||||||
for _, f in ipairs(core.project_files) do
|
|
||||||
table.insert(filenames, f.filename)
|
|
||||||
end
|
|
||||||
local t = common.fuzzy_match(filenames, name)
|
|
||||||
return t[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:on_line_removed()
|
|
||||||
local diff = self:get_line_height()
|
|
||||||
self.scroll.y = self.scroll.y - diff
|
|
||||||
self.scroll.to.y = self.scroll.to.y - diff
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:on_mouse_pressed(...)
|
|
||||||
local caught = ConsoleView.super.on_mouse_pressed(self, ...)
|
|
||||||
if caught then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local item = output[self.hovered_idx]
|
|
||||||
if item then
|
|
||||||
local file, line, col = item.text:match(item.file_pattern)
|
|
||||||
local resolved_file = resolve_file(file)
|
|
||||||
if not resolved_file then
|
|
||||||
core.error("Couldn't resolve file \"%s\"", file)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
core.try(function()
|
|
||||||
core.set_active_view(core.last_active_view)
|
|
||||||
local dv = core.root_view:open_doc(core.open_doc(resolved_file))
|
|
||||||
if line then
|
|
||||||
dv.doc:set_selection(line, col or 0)
|
|
||||||
dv:scroll_to_line(line, false, true)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:each_visible_line()
|
|
||||||
return coroutine.wrap(function()
|
|
||||||
local x, y = self:get_content_offset()
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
local min, max = self:get_visible_line_range()
|
|
||||||
y = y + lh * (min - 1) + style.padding.y
|
|
||||||
max = math.min(max, self:get_line_count())
|
|
||||||
|
|
||||||
for i = min, max do
|
|
||||||
local item = output[i]
|
|
||||||
if not item then break end
|
|
||||||
coroutine.yield(i, item, x, y, self.size.x, lh)
|
|
||||||
y = y + lh
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:update(...)
|
|
||||||
if self.last_output_id ~= output_id then
|
|
||||||
if config.autoscroll_console then
|
|
||||||
self.scroll.to.y = self:get_scrollable_size()
|
|
||||||
end
|
|
||||||
self.last_output_id = output_id
|
|
||||||
end
|
|
||||||
ConsoleView.super.update(self, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ConsoleView:draw()
|
|
||||||
self:draw_background(style.background)
|
|
||||||
local icon_w = style.icon_font:get_width("!")
|
|
||||||
|
|
||||||
for i, item, x, y, w, h in self:each_visible_line() do
|
|
||||||
local tx = x + style.padding.x
|
|
||||||
local time = os.date("%H:%M:%S", item.time)
|
|
||||||
local color = style.text
|
|
||||||
if self.hovered_idx == i then
|
|
||||||
color = style.accent
|
|
||||||
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
|
||||||
end
|
|
||||||
if item.text == "!DIVIDER" then
|
|
||||||
local w = style.font:get_width(time)
|
|
||||||
renderer.draw_rect(tx, y + h / 2, w, math.ceil(SCALE * 1), style.dim)
|
|
||||||
else
|
|
||||||
tx = common.draw_text(style.font, style.dim, time, "left", tx, y, w, h)
|
|
||||||
tx = tx + style.padding.x
|
|
||||||
if item.icon then
|
|
||||||
common.draw_text(style.icon_font, color, item.icon, "left", tx, y, w, h)
|
|
||||||
end
|
|
||||||
tx = tx + icon_w + style.padding.x
|
|
||||||
common.draw_text(style.code_font, color, item.text, "left", tx, y, w, h)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:draw_scrollbar(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- init static bottom-of-screen console
|
|
||||||
local view = ConsoleView()
|
|
||||||
local node = core.root_view:get_active_node()
|
|
||||||
node:split("down", view, true)
|
|
||||||
|
|
||||||
function view:update(...)
|
|
||||||
local dest = visible and config.console_size or 0
|
|
||||||
self:move_towards(self.size, "y", dest)
|
|
||||||
ConsoleView.update(self, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local last_command = ""
|
|
||||||
|
|
||||||
command.add(nil, {
|
|
||||||
["console:reset-output"] = function()
|
|
||||||
output = { { text = "", time = 0 } }
|
|
||||||
end,
|
|
||||||
|
|
||||||
["console:open-console"] = function()
|
|
||||||
local node = core.root_view:get_active_node()
|
|
||||||
node:add_view(ConsoleView())
|
|
||||||
end,
|
|
||||||
|
|
||||||
["console:toggle"] = function()
|
|
||||||
visible = not visible
|
|
||||||
end,
|
|
||||||
|
|
||||||
["console:run"] = function()
|
|
||||||
core.command_view:set_text(last_command, true)
|
|
||||||
core.command_view:enter("Run Console Command", function(cmd)
|
|
||||||
console.run { command = cmd }
|
|
||||||
last_command = cmd
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["ctrl+."] = "console:toggle",
|
|
||||||
["ctrl+shift+."] = "console:run",
|
|
||||||
}
|
|
||||||
|
|
||||||
-- for `workspace` plugin:
|
|
||||||
package.loaded["plugins.console.view"] = ConsoleView
|
|
||||||
|
|
||||||
console.clear()
|
|
||||||
return console
|
|
|
@ -1,271 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local common = require "core.common"
|
|
||||||
local config = require "core.config"
|
|
||||||
local command = require "core.command"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local style = require "core.style"
|
|
||||||
local Object = require "core.object"
|
|
||||||
local RootView = require "core.rootview"
|
|
||||||
|
|
||||||
local border_width = 1
|
|
||||||
local divider_width = 1
|
|
||||||
local DIVIDER = {}
|
|
||||||
|
|
||||||
local ContextMenu = Object:extend()
|
|
||||||
|
|
||||||
ContextMenu.DIVIDER = DIVIDER
|
|
||||||
|
|
||||||
function ContextMenu:new()
|
|
||||||
self.itemset = {}
|
|
||||||
self.show_context_menu = false
|
|
||||||
self.selected = -1
|
|
||||||
self.height = 0
|
|
||||||
self.position = { x = 0, y = 0 }
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_item_size(item)
|
|
||||||
local lw, lh
|
|
||||||
if item == DIVIDER then
|
|
||||||
lw = 0
|
|
||||||
lh = divider_width
|
|
||||||
else
|
|
||||||
lw = style.font:get_width(item.text)
|
|
||||||
if item.info then
|
|
||||||
lw = lw + style.padding.x + style.font:get_width(item.info)
|
|
||||||
end
|
|
||||||
lh = style.font:get_height() + style.padding.y
|
|
||||||
end
|
|
||||||
return lw, lh
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:register(predicate, items)
|
|
||||||
if type(predicate) == "string" then
|
|
||||||
predicate = require(predicate)
|
|
||||||
end
|
|
||||||
if type(predicate) == "table" then
|
|
||||||
local class = predicate
|
|
||||||
predicate = function() return core.active_view:is(class) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local width, height = 0, 0 --precalculate the size of context menu
|
|
||||||
for i, item in ipairs(items) do
|
|
||||||
if item ~= DIVIDER then
|
|
||||||
item.info = keymap.reverse_map[item.command]
|
|
||||||
end
|
|
||||||
local lw, lh = get_item_size(item)
|
|
||||||
width = math.max(width, lw)
|
|
||||||
height = height + lh
|
|
||||||
end
|
|
||||||
width = width + style.padding.x * 2
|
|
||||||
items.width, items.height = width, height
|
|
||||||
table.insert(self.itemset, { predicate = predicate, items = items })
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:show(x, y)
|
|
||||||
self.items = nil
|
|
||||||
for _, items in ipairs(self.itemset) do
|
|
||||||
if items.predicate(x, y) then
|
|
||||||
self.items = items.items
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.items then
|
|
||||||
local w, h = self.items.width, self.items.height
|
|
||||||
|
|
||||||
-- by default the box is opened on the right and below
|
|
||||||
if x + w >= core.root_view.size.x then
|
|
||||||
x = x - w
|
|
||||||
end
|
|
||||||
if y + h >= core.root_view.size.y then
|
|
||||||
y = y - h
|
|
||||||
end
|
|
||||||
|
|
||||||
self.position.x, self.position.y = x, y
|
|
||||||
self.show_context_menu = true
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:hide()
|
|
||||||
self.show_context_menu = false
|
|
||||||
self.items = nil
|
|
||||||
self.selected = -1
|
|
||||||
self.height = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:each_item()
|
|
||||||
local x, y, w = self.position.x, self.position.y, self.items.width
|
|
||||||
local oy = y
|
|
||||||
return coroutine.wrap(function()
|
|
||||||
for i, item in ipairs(self.items) do
|
|
||||||
local _, lh = get_item_size(item)
|
|
||||||
if y - oy > self.height then break end
|
|
||||||
coroutine.yield(i, item, x, y, w, lh)
|
|
||||||
y = y + lh
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:on_mouse_moved(px, py)
|
|
||||||
if not self.show_context_menu then return end
|
|
||||||
|
|
||||||
for i, item, x, y, w, h in self:each_item() do
|
|
||||||
if px > x and px <= x + w and py > y and py <= y + h then
|
|
||||||
system.set_cursor("arrow")
|
|
||||||
self.selected = i
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.selected = -1
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:on_selected(item)
|
|
||||||
if type(item.command) == "string" then
|
|
||||||
command.perform(item.command)
|
|
||||||
else
|
|
||||||
item.command()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:on_mouse_pressed(button, x, y, clicks)
|
|
||||||
local selected = (self.items or {})[self.selected]
|
|
||||||
local caught = false
|
|
||||||
|
|
||||||
self:hide()
|
|
||||||
if button == "left" then
|
|
||||||
if selected then
|
|
||||||
self:on_selected(selected)
|
|
||||||
caught = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if button == "right" then
|
|
||||||
caught = self:show(x, y)
|
|
||||||
end
|
|
||||||
return caught
|
|
||||||
end
|
|
||||||
|
|
||||||
-- copied from core.docview
|
|
||||||
function ContextMenu:move_towards(t, k, dest, rate)
|
|
||||||
if type(t) ~= "table" then
|
|
||||||
return self:move_towards(self, t, k, dest, rate)
|
|
||||||
end
|
|
||||||
local val = t[k]
|
|
||||||
if math.abs(val - dest) < 0.5 then
|
|
||||||
t[k] = dest
|
|
||||||
else
|
|
||||||
t[k] = common.lerp(val, dest, rate or 0.5)
|
|
||||||
end
|
|
||||||
if val ~= dest then
|
|
||||||
core.redraw = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:update()
|
|
||||||
if self.show_context_menu then
|
|
||||||
self:move_towards("height", self.items.height)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:draw()
|
|
||||||
if not self.show_context_menu then return end
|
|
||||||
core.root_view:defer_draw(self.draw_context_menu, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ContextMenu:draw_context_menu()
|
|
||||||
if not self.items then return end
|
|
||||||
local bx, by, bw, bh = self.position.x, self.position.y, self.items.width, self.height
|
|
||||||
|
|
||||||
renderer.draw_rect(
|
|
||||||
bx - border_width,
|
|
||||||
by - border_width,
|
|
||||||
bw + (border_width * 2),
|
|
||||||
bh + (border_width * 2),
|
|
||||||
style.divider
|
|
||||||
)
|
|
||||||
renderer.draw_rect(bx, by, bw, bh, style.background3)
|
|
||||||
|
|
||||||
for i, item, x, y, w, h in self:each_item() do
|
|
||||||
if item == DIVIDER then
|
|
||||||
renderer.draw_rect(x, y, w, h, style.caret)
|
|
||||||
else
|
|
||||||
if i == self.selected then
|
|
||||||
renderer.draw_rect(x, y, w, h, style.selection)
|
|
||||||
end
|
|
||||||
|
|
||||||
common.draw_text(style.font, style.text, item.text, "left", x + style.padding.x, y, w, h)
|
|
||||||
if item.info then
|
|
||||||
common.draw_text(style.font, style.dim, item.info, "right", x, y, w - style.padding.x, h)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local menu = ContextMenu()
|
|
||||||
local root_view_on_mouse_pressed = RootView.on_mouse_pressed
|
|
||||||
local root_view_on_mouse_moved = RootView.on_mouse_moved
|
|
||||||
local root_view_update = RootView.update
|
|
||||||
local root_view_draw = RootView.draw
|
|
||||||
|
|
||||||
function RootView:on_mouse_moved(...)
|
|
||||||
if menu:on_mouse_moved(...) then return end
|
|
||||||
root_view_on_mouse_moved(self, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- copied from core.rootview
|
|
||||||
function RootView:on_mouse_pressed(button, x,y, clicks)
|
|
||||||
local div = self.root_node:get_divider_overlapping_point(x, y)
|
|
||||||
if div then
|
|
||||||
self.dragged_divider = div
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local node = self.root_node:get_child_overlapping_point(x, y)
|
|
||||||
local idx = node:get_tab_overlapping_point(x, y)
|
|
||||||
if idx then
|
|
||||||
node:set_active_view(node.views[idx])
|
|
||||||
if button == "right" then --< @r-lyeh middle>right
|
|
||||||
node:close_active_view(self.root_node)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
core.set_active_view(node.active_view)
|
|
||||||
-- send to context menu first
|
|
||||||
if not menu:on_mouse_pressed(button, x, y, clicks) then
|
|
||||||
node.active_view:on_mouse_pressed(button, x, y, clicks)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function RootView:update(...)
|
|
||||||
root_view_update(self, ...)
|
|
||||||
menu:update()
|
|
||||||
end
|
|
||||||
|
|
||||||
function RootView:draw(...)
|
|
||||||
root_view_draw(self, ...)
|
|
||||||
menu:draw()
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add(nil, {
|
|
||||||
["context:show"] = function()
|
|
||||||
menu:show(core.active_view.position.x, core.active_view.position.y)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["menu"] = "context:show"
|
|
||||||
}
|
|
||||||
|
|
||||||
-- register some sensible defaults
|
|
||||||
menu:register("core.docview", {
|
|
||||||
{ text = "Cut", command = "doc:cut" },
|
|
||||||
{ text = "Copy", command = "doc:copy" },
|
|
||||||
{ text = "Paste", command = "doc:paste" },
|
|
||||||
DIVIDER,
|
|
||||||
{ text = "Command Palette...", command = "core:find-command" }
|
|
||||||
})
|
|
||||||
|
|
||||||
return menu
|
|
|
@ -1,63 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local config = require "core.config"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
local Doc = require "core.doc"
|
|
||||||
|
|
||||||
local cache = setmetatable({}, { __mode = "k" })
|
|
||||||
|
|
||||||
|
|
||||||
local function detect_indent(doc)
|
|
||||||
for _, text in ipairs(doc.lines) do
|
|
||||||
local str = text:match("^ +")
|
|
||||||
if str then return "soft", #str end
|
|
||||||
local str = text:match("^\t+")
|
|
||||||
if str then return "hard" end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function update_cache(doc)
|
|
||||||
local type, size = detect_indent(doc)
|
|
||||||
if type then
|
|
||||||
cache[doc] = { type = type, size = size }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local new = Doc.new
|
|
||||||
function Doc:new(...)
|
|
||||||
new(self, ...)
|
|
||||||
update_cache(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
local clean = Doc.clean
|
|
||||||
function Doc:clean(...)
|
|
||||||
clean(self, ...)
|
|
||||||
update_cache(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function with_indent_override(doc, fn, ...)
|
|
||||||
local c = cache[doc]
|
|
||||||
if not c then
|
|
||||||
return fn(...)
|
|
||||||
end
|
|
||||||
local type, size = config.tab_type, config.indent_size
|
|
||||||
config.tab_type, config.indent_size = c.type, c.size or config.indent_size
|
|
||||||
local r1, r2, r3 = fn(...)
|
|
||||||
config.tab_type, config.indent_size = type, size
|
|
||||||
return r1, r2, r3
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local perform = command.perform
|
|
||||||
function command.perform(...)
|
|
||||||
return with_indent_override(core.active_view.doc, perform, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local draw = DocView.draw
|
|
||||||
function DocView:draw(...)
|
|
||||||
return with_indent_override(self.doc, draw, self, ...)
|
|
||||||
end
|
|
|
@ -1,37 +0,0 @@
|
||||||
local common = require "core.common"
|
|
||||||
local config = require "core.config"
|
|
||||||
local style = require "core.style"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
local command = require "core.command"
|
|
||||||
|
|
||||||
-- originally written by luveti
|
|
||||||
|
|
||||||
config.whitespace_map = { [" "] = "·", ["\t"] = "»" }
|
|
||||||
config.draw_whitespace = true
|
|
||||||
|
|
||||||
local draw_line_text = DocView.draw_line_text
|
|
||||||
|
|
||||||
function DocView:draw_line_text(idx, x, y)
|
|
||||||
draw_line_text(self, idx, x, y)
|
|
||||||
if not config.draw_whitespace then return end
|
|
||||||
|
|
||||||
local text = self.doc.lines[idx]
|
|
||||||
local tx, ty = x, y + self:get_line_text_y_offset()
|
|
||||||
local font = self:get_font()
|
|
||||||
local color = style.whitespace or style.syntax.comment
|
|
||||||
local map = config.whitespace_map
|
|
||||||
|
|
||||||
for chr in common.utf8_chars(text) do
|
|
||||||
local rep = map[chr]
|
|
||||||
if rep then
|
|
||||||
renderer.draw_text(font, rep, tx, ty, color)
|
|
||||||
end
|
|
||||||
tx = tx + font:get_width(chr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["draw-whitespace:toggle"] = function() config.draw_whitespace = not config.draw_whitespace end,
|
|
||||||
["draw-whitespace:disable"] = function() config.draw_whitespace = false end,
|
|
||||||
["draw-whitespace:enable"] = function() config.draw_whitespace = true end,
|
|
||||||
})
|
|
|
@ -1,29 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local Doc = require "core.doc"
|
|
||||||
|
|
||||||
local function eof_newline(doc)
|
|
||||||
local leof,neof = #doc.lines,#doc.lines
|
|
||||||
for i = leof,1,-1 do
|
|
||||||
if not string.match(doc.lines[i],"^%s*$") then break end
|
|
||||||
neof = i
|
|
||||||
end
|
|
||||||
local eol,_ = string.find(doc.lines[neof],"\n")
|
|
||||||
if eol then
|
|
||||||
doc:remove(neof,eol,math.huge,math.huge)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
doc:insert(neof,math.huge,"\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["eof-newline:eof-newline"] = function()
|
|
||||||
eof_newline(core.active_view.doc)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
local save = Doc.save
|
|
||||||
Doc.save = function(self, ...)
|
|
||||||
eof_newline(self)
|
|
||||||
save(self, ...)
|
|
||||||
end
|
|
|
@ -1,167 +0,0 @@
|
||||||
-- mod-version:1 -- lite-xl 1.16
|
|
||||||
local core = require "core"
|
|
||||||
local common = require "core.common"
|
|
||||||
local command = require "core.command"
|
|
||||||
|
|
||||||
local fsutils = {}
|
|
||||||
|
|
||||||
function fsutils.iterdir(dir)
|
|
||||||
local stack = { dir }
|
|
||||||
return function()
|
|
||||||
local path = table.remove(stack)
|
|
||||||
if not path then return end
|
|
||||||
|
|
||||||
for _, file in ipairs(system.list_dir(path) or {}) do
|
|
||||||
stack[#stack + 1] = path .. PATHSEP .. file
|
|
||||||
end
|
|
||||||
|
|
||||||
return path, system.get_file_info(path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function fsutils.delete(dir, yield)
|
|
||||||
local dirs = {}
|
|
||||||
local n = 0
|
|
||||||
for filename, stat in fsutils.iterdir(dir) do
|
|
||||||
if stat.type == "dir" then
|
|
||||||
-- this will later allow us to delete the dirs in correct sequence
|
|
||||||
table.insert(dirs, filename)
|
|
||||||
else
|
|
||||||
os.remove(filename)
|
|
||||||
if yield then
|
|
||||||
n = n + 1
|
|
||||||
coroutine.yield(n)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for i = #dirs, 1, -1 do
|
|
||||||
os.remove(dirs[i])
|
|
||||||
if yield then
|
|
||||||
n = n + 1
|
|
||||||
coroutine.yield(n)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function fsutils.move(oldname, newname)
|
|
||||||
os.rename(oldname, newname)
|
|
||||||
end
|
|
||||||
|
|
||||||
function fsutils.split(path)
|
|
||||||
local segments = {}
|
|
||||||
local pos = 1
|
|
||||||
while true do
|
|
||||||
local s, e = string.find(path, "[/\\]+", pos)
|
|
||||||
if not s then break end
|
|
||||||
table.insert(segments, string.sub(path, pos, s - 1))
|
|
||||||
pos = e + 1
|
|
||||||
end
|
|
||||||
table.insert(list, string.sub(path, pos))
|
|
||||||
|
|
||||||
if segments[#segments] == '' then
|
|
||||||
table.remove(segments)
|
|
||||||
end
|
|
||||||
|
|
||||||
return segments
|
|
||||||
end
|
|
||||||
|
|
||||||
function fsutils.normalize(path)
|
|
||||||
return table.concat(fsutils.split(path), PATHSEP)
|
|
||||||
end
|
|
||||||
|
|
||||||
function fsutils.normalize_posix(path)
|
|
||||||
return table.concat(fsutils.split(path), '/')
|
|
||||||
end
|
|
||||||
|
|
||||||
function fsutils.mkdir(path)
|
|
||||||
local segments = fsutils.split(path)
|
|
||||||
if system.mkdir then
|
|
||||||
local p = ""
|
|
||||||
for i = 1, #segments do
|
|
||||||
p = table.concat(segments, PATHSEP, 1, i)
|
|
||||||
end
|
|
||||||
|
|
||||||
if p == "" then
|
|
||||||
return nil, "path empty", p
|
|
||||||
end
|
|
||||||
|
|
||||||
local stat = system.get_file_info(p)
|
|
||||||
if stat and stat.type == "file" then
|
|
||||||
return nil, "path exists as a file", p
|
|
||||||
end
|
|
||||||
local ok, err = system.mkdir(p)
|
|
||||||
if not ok then
|
|
||||||
return nil, err, p
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- just wing it lol
|
|
||||||
system.exec(string.format(PLATFORM == "Windows" and "setlocal enableextensions & mkdir %q" or "mkdir -p %q", fsutils.normalize(path)))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function async_exec(f, cb)
|
|
||||||
cb = cb or function() end
|
|
||||||
local co = coroutine.create(f)
|
|
||||||
local function resolve(...)
|
|
||||||
local ok, exec_body = coroutine.resume(co, ...)
|
|
||||||
if not ok then
|
|
||||||
error(debug.traceback(co, exec_body))
|
|
||||||
end
|
|
||||||
if coroutine.status(co) ~= "dead" then
|
|
||||||
exec_body(resolve)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
resolve(cb)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function prompt(text, suggest)
|
|
||||||
return coroutine.yield(function(resolve)
|
|
||||||
core.command_view:enter(text, resolve, suggest)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add(nil, {
|
|
||||||
["files:delete"] = function()
|
|
||||||
async_exec(function()
|
|
||||||
local path = prompt("Delete", common.path_suggest)
|
|
||||||
|
|
||||||
core.add_thread(function()
|
|
||||||
-- we use a wrapping coroutine to get status
|
|
||||||
local function delete()
|
|
||||||
return coroutine.wrap(function() fsutils.delete(path, true) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
for n in delete() do
|
|
||||||
if n % 100 == 0 then
|
|
||||||
core.log("Deleted %d items...", n)
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
core.log("%q deleted.", path)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end,
|
|
||||||
["files:move"] = function()
|
|
||||||
async_exec(function()
|
|
||||||
local oldname = prompt("Move", common.path_suggest)
|
|
||||||
local newname = prompt("To", common.path_suggest)
|
|
||||||
|
|
||||||
fsutils.move(oldname, newname)
|
|
||||||
core.log("Moved %q to %q", oldname, newname)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
if not command.map["files:create-directory"] then
|
|
||||||
command.add(nil, {
|
|
||||||
["files:create-directory"] = function()
|
|
||||||
async_exec(function()
|
|
||||||
local path = prompt("Name", common.path_suggest)
|
|
||||||
fsutils.mkdir(path)
|
|
||||||
core.log("%q created.", path)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
return fsutils
|
|
|
@ -1,45 +0,0 @@
|
||||||
local style = require "core.style"
|
|
||||||
local config = require "core.config"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
|
|
||||||
local function get_line_spaces(doc, idx, dir)
|
|
||||||
local text = doc.lines[idx]
|
|
||||||
if not text then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
local s, e = text:find("^%s*")
|
|
||||||
if e == #text then
|
|
||||||
return get_line_spaces(doc, idx + dir, dir)
|
|
||||||
end
|
|
||||||
local n = 0
|
|
||||||
for i = s, e do
|
|
||||||
n = n + (text:byte(i) == 9 and config.indent_size or 1)
|
|
||||||
end
|
|
||||||
return n
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function get_line_indent_guide_spaces(doc, idx)
|
|
||||||
if doc.lines[idx]:find("^%s*\n") then
|
|
||||||
return math.max(
|
|
||||||
get_line_spaces(doc, idx - 1, -1),
|
|
||||||
get_line_spaces(doc, idx + 1, 1))
|
|
||||||
end
|
|
||||||
return get_line_spaces(doc, idx)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local draw_line_text = DocView.draw_line_text
|
|
||||||
|
|
||||||
function DocView:draw_line_text(idx, x, y)
|
|
||||||
local spaces = get_line_indent_guide_spaces(self.doc, idx)
|
|
||||||
local sw = self:get_font():get_width(" ")
|
|
||||||
local w = math.ceil(1 * SCALE)
|
|
||||||
local h = self:get_line_height()
|
|
||||||
for i = 0, spaces - 1, config.indent_size do
|
|
||||||
local color = style.guide or style.selection
|
|
||||||
renderer.draw_rect(x + sw * i, y, w, h, color)
|
|
||||||
end
|
|
||||||
draw_line_text(self, idx, x, y)
|
|
||||||
end
|
|
|
@ -1,64 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local config = require "core.config"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
|
|
||||||
config.lfautoinsert_map = {
|
|
||||||
["{%s*\n"] = "}",
|
|
||||||
["%(%s*\n"] = ")",
|
|
||||||
["%f[[]%[%s*\n"] = "]",
|
|
||||||
["%[%[%s*\n"] = "]]",
|
|
||||||
["=%s*\n"] = false,
|
|
||||||
[":%s*\n"] = false,
|
|
||||||
["^#if.*\n"] = "#endif",
|
|
||||||
["^#else.*\n"] = "#endif",
|
|
||||||
["%f[%w]do%s*\n"] = "end",
|
|
||||||
["%f[%w]then%s*\n"] = "end",
|
|
||||||
["%f[%w]else%s*\n"] = "end",
|
|
||||||
["%f[%w]repeat%s*\n"] = "until",
|
|
||||||
["%f[%w]function.*%)%s*\n"] = "end",
|
|
||||||
["^%s*<([^/][^%s>]*)[^>]*>%s*\n"] = "</$TEXT>",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local function indent_size(doc, line)
|
|
||||||
local text = doc.lines[line] or ""
|
|
||||||
local s, e = text:find("^[\t ]*")
|
|
||||||
return e - s
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["autoinsert:newline"] = function()
|
|
||||||
command.perform("doc:newline")
|
|
||||||
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
local line, col = doc:get_selection()
|
|
||||||
local text = doc.lines[line - 1]
|
|
||||||
|
|
||||||
for ptn, close in pairs(config.lfautoinsert_map) do
|
|
||||||
local s, _, str = text:find(ptn)
|
|
||||||
if s then
|
|
||||||
if close
|
|
||||||
and col == #doc.lines[line]
|
|
||||||
and indent_size(doc, line + 1) <= indent_size(doc, line - 1)
|
|
||||||
then
|
|
||||||
close = str and close:gsub("$TEXT", str) or close
|
|
||||||
command.perform("doc:newline")
|
|
||||||
core.active_view:on_text_input(close)
|
|
||||||
command.perform("doc:move-to-previous-line")
|
|
||||||
if doc.lines[line+1] == doc.lines[line+2] then
|
|
||||||
doc:remove(line+1, 1, line+2, 1)
|
|
||||||
end
|
|
||||||
elseif col < #doc.lines[line] then
|
|
||||||
command.perform("doc:newline")
|
|
||||||
command.perform("doc:move-to-previous-line")
|
|
||||||
end
|
|
||||||
command.perform("doc:indent")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["return"] = { "command:submit", "autoinsert:newline" }
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
local config = require "core.config"
|
|
||||||
local style = require "core.style"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
local draw = DocView.draw
|
|
||||||
|
|
||||||
function DocView:draw(...)
|
|
||||||
draw(self, ...)
|
|
||||||
|
|
||||||
local offset = self:get_font():get_width("n") * config.line_limit
|
|
||||||
local x = self:get_line_screen_position(1) + offset
|
|
||||||
local y = self.position.y
|
|
||||||
local w = math.ceil(SCALE * 1)
|
|
||||||
local h = self.size.y
|
|
||||||
|
|
||||||
local color = style.guide or style.selection
|
|
||||||
renderer.draw_rect(x, y, w, h, color)
|
|
||||||
end
|
|
|
@ -1,70 +0,0 @@
|
||||||
-- mod-version:3
|
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
|
|
||||||
local handled_events = {
|
|
||||||
["keypressed"] = true,
|
|
||||||
["keyreleased"] = true,
|
|
||||||
["textinput"] = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
local state = "stopped"
|
|
||||||
local event_buffer = {}
|
|
||||||
local modkeys = {}
|
|
||||||
|
|
||||||
local on_event = core.on_event
|
|
||||||
|
|
||||||
core.on_event = function(type, ...)
|
|
||||||
local res = on_event(type, ...)
|
|
||||||
if state == "recording" and handled_events[type] then
|
|
||||||
table.insert(event_buffer, { type, ... })
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function clone(t)
|
|
||||||
local res = {}
|
|
||||||
for k, v in pairs(t) do res[k] = v end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function predicate()
|
|
||||||
return state ~= "playing"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add(predicate, {
|
|
||||||
["macro:toggle-record"] = function()
|
|
||||||
if state == "stopped" then
|
|
||||||
state = "recording"
|
|
||||||
event_buffer = {}
|
|
||||||
modkeys = clone(keymap.modkeys)
|
|
||||||
core.log("Recording macro...")
|
|
||||||
else
|
|
||||||
state = "stopped"
|
|
||||||
core.log("Stopped recording macro (%d events)", #event_buffer)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
["macro:play"] = function()
|
|
||||||
state = "playing"
|
|
||||||
core.log("Playing macro... (%d events)", #event_buffer)
|
|
||||||
local mk = keymap.modkeys
|
|
||||||
keymap.modkeys = clone(modkeys)
|
|
||||||
for _, ev in ipairs(event_buffer) do
|
|
||||||
on_event(table.unpack(ev))
|
|
||||||
core.root_view:update()
|
|
||||||
end
|
|
||||||
keymap.modkeys = mk
|
|
||||||
state = "stopped"
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["ctrl+shift+;"] = "macro:toggle-record",
|
|
||||||
["ctrl+;"] = "macro:play",
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
-- Markers plugin for lite text editor
|
|
||||||
-- original implementation by Petri Häkkinen
|
|
||||||
|
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local style = require "core.style"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
local Doc = require "core.doc"
|
|
||||||
|
|
||||||
local cache = {} -- this table contains subtables for each document, each subtable is a set of line numbers
|
|
||||||
setmetatable(cache, {
|
|
||||||
__mode = "k",
|
|
||||||
__index = function(t, k)
|
|
||||||
t[k] = {}
|
|
||||||
return t[k]
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
local function shift_lines(doc, at, diff)
|
|
||||||
if diff == 0 then return end
|
|
||||||
local t = {}
|
|
||||||
for line in pairs(cache[doc]) do
|
|
||||||
line = line >= at and line + diff or line
|
|
||||||
t[line] = true
|
|
||||||
end
|
|
||||||
cache[doc] = t
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local raw_insert = Doc.raw_insert
|
|
||||||
|
|
||||||
function Doc:raw_insert(line, col, text, ...)
|
|
||||||
raw_insert(self, line, col, text, ...)
|
|
||||||
local line_count = 0
|
|
||||||
for _ in text:gmatch("\n") do
|
|
||||||
line_count = line_count + 1
|
|
||||||
end
|
|
||||||
shift_lines(self, line, line_count)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local raw_remove = Doc.raw_remove
|
|
||||||
|
|
||||||
function Doc:raw_remove(line1, col1, line2, col2, ...)
|
|
||||||
raw_remove(self, line1, col1, line2, col2, ...)
|
|
||||||
shift_lines(self, line2, line1 - line2)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local draw_line_gutter = DocView.draw_line_gutter
|
|
||||||
|
|
||||||
function DocView:draw_line_gutter(idx, x, y)
|
|
||||||
if cache[self.doc] and cache[self.doc][idx] then
|
|
||||||
local h = self:get_line_height()
|
|
||||||
renderer.draw_rect(x, y, style.padding.x * 0.4, h, style.selection)
|
|
||||||
end
|
|
||||||
draw_line_gutter(self, idx, x, y)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["markers:toggle-marker"] = function()
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
local line = doc:get_selection()
|
|
||||||
local markers = cache[doc]
|
|
||||||
|
|
||||||
if markers[line] then
|
|
||||||
markers[line] = nil
|
|
||||||
else
|
|
||||||
markers[line] = true
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
["markers:go-to-next-marker"] = function()
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
local line = doc:get_selection()
|
|
||||||
local markers = cache[doc]
|
|
||||||
|
|
||||||
local first_marker = math.huge
|
|
||||||
local next_marker = math.huge
|
|
||||||
for l, _ in pairs(markers) do
|
|
||||||
if l > line and l < next_marker then
|
|
||||||
next_marker = l
|
|
||||||
end
|
|
||||||
first_marker = math.min(first_marker, l)
|
|
||||||
end
|
|
||||||
if next_marker == math.huge then
|
|
||||||
next_marker = first_marker
|
|
||||||
end
|
|
||||||
if next_marker ~= math.huge then
|
|
||||||
doc:set_selection(next_marker, 1)
|
|
||||||
core.active_view:scroll_to_line(next_marker, true)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["ctrl+f2"] = "markers:toggle-marker",
|
|
||||||
["f2"] = "markers:go-to-next-marker",
|
|
||||||
}
|
|
|
@ -1,295 +0,0 @@
|
||||||
local command = require "core.command"
|
|
||||||
local common = require "core.common"
|
|
||||||
local config = require "core.config"
|
|
||||||
local style = require "core.style"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
-- General plugin settings
|
|
||||||
config.minimap_enabled = true
|
|
||||||
config.minimap_width = 100
|
|
||||||
config.minimap_instant_scroll = false
|
|
||||||
config.minimap_syntax_highlight = true
|
|
||||||
config.minimap_scale = 1
|
|
||||||
config.minimap_draw_background = true
|
|
||||||
|
|
||||||
-- Configure size for rendering each char in the minimap
|
|
||||||
local char_height = 1 * SCALE * config.minimap_scale
|
|
||||||
local char_spacing = 0.8 * SCALE * config.minimap_scale
|
|
||||||
local line_spacing = 2 * SCALE * config.minimap_scale
|
|
||||||
|
|
||||||
-- Overloaded since the default implementation adds a extra x3 size of hotspot for the mouse to hit the scrollbar.
|
|
||||||
local prev_scrollbar_overlaps_point = DocView.scrollbar_overlaps_point
|
|
||||||
DocView.scrollbar_overlaps_point = function(self, x, y)
|
|
||||||
if not config.minimap_enabled then return prev_scrollbar_overlaps_point(self, x, y) end
|
|
||||||
|
|
||||||
local sx, sy, sw, sh = self:get_scrollbar_rect()
|
|
||||||
return x >= sx and x < sx + sw and y >= sy and y < sy + sh
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper function to determine if current file is too large to be shown fully inside the minimap area.
|
|
||||||
local function is_file_too_large(self)
|
|
||||||
local line_count = #self.doc.lines
|
|
||||||
local _, _, _, sh = self:get_scrollbar_rect()
|
|
||||||
|
|
||||||
-- check if line count is too large to fit inside the minimap area
|
|
||||||
local max_minmap_lines = math.floor(sh / line_spacing)
|
|
||||||
return line_count > 1 and line_count > max_minmap_lines
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Overloaded with an extra check if the user clicked inside the minimap to automatically scroll to that line.
|
|
||||||
local prev_on_mouse_pressed = DocView.on_mouse_pressed
|
|
||||||
DocView.on_mouse_pressed = function(self, button, x, y, clicks)
|
|
||||||
if not config.minimap_enabled then return prev_on_mouse_pressed(self, button, x, y, clicks) end
|
|
||||||
|
|
||||||
-- check if user clicked in the minimap area and jump directly to that line
|
|
||||||
-- unless they are actually trying to perform a drag
|
|
||||||
local minimap_hit = self:scrollbar_overlaps_point(x, y)
|
|
||||||
if minimap_hit then
|
|
||||||
local line_count = #self.doc.lines
|
|
||||||
local minimap_height = line_count * line_spacing
|
|
||||||
|
|
||||||
-- check if line count is too large to fit inside the minimap area
|
|
||||||
local is_too_large = is_file_too_large(self)
|
|
||||||
if is_too_large then
|
|
||||||
local _, _, _, sh = self:get_scrollbar_rect()
|
|
||||||
minimap_height = sh
|
|
||||||
end
|
|
||||||
|
|
||||||
-- calc which line to jump to
|
|
||||||
local dy = y - self.position.y
|
|
||||||
local jump_to_line = math.floor((dy / minimap_height) * line_count) + 1
|
|
||||||
|
|
||||||
local _, cy, _, cy2 = self:get_content_bounds()
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
local visible_lines_count = math.max(1, (cy2 - cy) / lh)
|
|
||||||
local visible_lines_start = math.max(1, math.floor(cy / lh))
|
|
||||||
|
|
||||||
-- calc if user hit the currently visible area
|
|
||||||
local hit_visible_area = true
|
|
||||||
if is_too_large then
|
|
||||||
|
|
||||||
local visible_height = visible_lines_count * line_spacing
|
|
||||||
local scroll_pos = (visible_lines_start-1) / (line_count - visible_lines_count - 1)
|
|
||||||
scroll_pos = math.min(1.0, scroll_pos) -- 0..1
|
|
||||||
local visible_y = self.position.y + scroll_pos * (minimap_height - visible_height)
|
|
||||||
|
|
||||||
local t = (line_count - visible_lines_start) / visible_lines_count
|
|
||||||
if t <= 1 then
|
|
||||||
visible_y = visible_y + visible_height * (1.0 - t)
|
|
||||||
end
|
|
||||||
|
|
||||||
if y < visible_y or y > visible_y + visible_height then
|
|
||||||
hit_visible_area = false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
|
|
||||||
-- If the click is on the currently visible line numbers,
|
|
||||||
-- ignore it since then they probably want to initiate a drag instead.
|
|
||||||
if jump_to_line < visible_lines_start or jump_to_line > visible_lines_start + visible_lines_count then
|
|
||||||
hit_visible_area = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if user didn't click on the visible area (ie not dragging), scroll accordingly
|
|
||||||
if not hit_visible_area then
|
|
||||||
self:scroll_to_line(jump_to_line, false, config.minimap_instant_scroll)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
return prev_on_mouse_pressed(self, button, x, y, clicks)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Overloaded with pretty much the same logic as original DocView implementation,
|
|
||||||
-- with the exception of the dragging scrollbar delta. We want it to behave a bit snappier
|
|
||||||
-- since the "scrollbar" essentially represents the lines visible in the content view.
|
|
||||||
local prev_on_mouse_moved = DocView.on_mouse_moved
|
|
||||||
DocView.on_mouse_moved = function(self, x, y, dx, dy)
|
|
||||||
if not config.minimap_enabled then return prev_on_mouse_moved(self, x, y, dx, dy) end
|
|
||||||
|
|
||||||
if self.dragging_scrollbar then
|
|
||||||
local line_count = #self.doc.lines
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
local delta = lh / line_spacing * dy
|
|
||||||
|
|
||||||
if is_file_too_large(self) then
|
|
||||||
local _, sy, _, sh = self:get_scrollbar_rect()
|
|
||||||
delta = (line_count * lh) / sh * dy
|
|
||||||
end
|
|
||||||
|
|
||||||
self.scroll.to.y = self.scroll.to.y + delta
|
|
||||||
end
|
|
||||||
|
|
||||||
-- we need to "hide" that the scrollbar is dragging so that View doesnt does its own scrolling logic
|
|
||||||
local t = self.dragging_scrollbar
|
|
||||||
self.dragging_scrollbar = false
|
|
||||||
local r = prev_on_mouse_moved(self, x, y, dx, dy)
|
|
||||||
self.dragging_scrollbar = t
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Overloaded since we want the mouse to interact with the full size of the minimap area,
|
|
||||||
-- not juse the scrollbar.
|
|
||||||
local prev_get_scrollbar_rect = DocView.get_scrollbar_rect
|
|
||||||
DocView.get_scrollbar_rect = function (self)
|
|
||||||
if not config.minimap_enabled then return prev_get_scrollbar_rect(self) end
|
|
||||||
|
|
||||||
return
|
|
||||||
self.position.x + self.size.x - config.minimap_width * SCALE,
|
|
||||||
self.position.y,
|
|
||||||
config.minimap_width * SCALE,
|
|
||||||
self.size.y
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Overloaded so we can render the minimap in the "scrollbar area".
|
|
||||||
local prev_draw_scrollbar = DocView.draw_scrollbar
|
|
||||||
DocView.draw_scrollbar = function (self)
|
|
||||||
if not config.minimap_enabled then return prev_draw_scrollbar(self) end
|
|
||||||
|
|
||||||
local x, y, w, h = self:get_scrollbar_rect()
|
|
||||||
|
|
||||||
local highlight = self.hovered_scrollbar or self.dragging_scrollbar
|
|
||||||
local visual_color = highlight and style.scrollbar2 or style.scrollbar
|
|
||||||
|
|
||||||
local _, cy, _, cy2 = self:get_content_bounds()
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
local visible_lines_count = math.max(1, (cy2 - cy) / lh)
|
|
||||||
local visible_lines_start = math.max(1, math.floor(cy / lh))
|
|
||||||
local scroller_height = visible_lines_count * line_spacing
|
|
||||||
local line_count = #self.doc.lines
|
|
||||||
|
|
||||||
local visible_y = self.position.y + (visible_lines_start-1) * line_spacing
|
|
||||||
|
|
||||||
|
|
||||||
-- check if file is too large to fit inside the minimap area
|
|
||||||
local max_minmap_lines = math.floor(h / line_spacing)
|
|
||||||
local minimap_start_line = 1
|
|
||||||
if is_file_too_large(self) then
|
|
||||||
|
|
||||||
local scroll_pos = (visible_lines_start-1) / (line_count - visible_lines_count - 1)
|
|
||||||
scroll_pos = math.min(1.0, scroll_pos) -- 0..1, procent of visual area scrolled
|
|
||||||
|
|
||||||
local scroll_pos_pixels = scroll_pos * (h - scroller_height)
|
|
||||||
visible_y = self.position.y + scroll_pos_pixels
|
|
||||||
|
|
||||||
-- offset visible area if user is scrolling past end
|
|
||||||
local t = (line_count - visible_lines_start) / visible_lines_count
|
|
||||||
if t <= 1 then
|
|
||||||
visible_y = visible_y + scroller_height * (1.0 - t)
|
|
||||||
end
|
|
||||||
|
|
||||||
minimap_start_line = visible_lines_start - math.floor(scroll_pos_pixels / line_spacing)
|
|
||||||
minimap_start_line = math.max(1, math.min(minimap_start_line, line_count - max_minmap_lines))
|
|
||||||
end
|
|
||||||
|
|
||||||
if config.minimap_draw_background then
|
|
||||||
renderer.draw_rect(x, y, w, h, style.minimap_background or style.background)
|
|
||||||
end
|
|
||||||
-- draw visual rect
|
|
||||||
renderer.draw_rect(x, visible_y, w, scroller_height, visual_color)
|
|
||||||
|
|
||||||
-- time to draw the actual code, setup some local vars that are used in both highlighted and plain renderind.
|
|
||||||
local line_y = y
|
|
||||||
|
|
||||||
-- when not using syntax highlighted rendering, just use the normal color but dim it 50%.
|
|
||||||
local color = style.syntax["normal"]
|
|
||||||
color = { color[1],color[2],color[3],color[4] * 0.5 }
|
|
||||||
|
|
||||||
-- we try to "batch" characters so that they can be rendered as just one rectangle instead of one for each.
|
|
||||||
local batch_width = 0
|
|
||||||
local batch_start = x
|
|
||||||
local minimap_cutoff_x = x + config.minimap_width * SCALE
|
|
||||||
|
|
||||||
-- render lines with syntax highlighting
|
|
||||||
if config.minimap_syntax_highlight then
|
|
||||||
|
|
||||||
-- keep track of the highlight type, since this needs to break batches as well
|
|
||||||
local batch_syntax_type = nil
|
|
||||||
|
|
||||||
local function flush_batch(type)
|
|
||||||
if batch_width > 0 then
|
|
||||||
-- fetch and dim colors
|
|
||||||
color = style.syntax[batch_syntax_type]
|
|
||||||
color = { color[1], color[2], color[3], color[4] * 0.5 }
|
|
||||||
renderer.draw_rect(batch_start, line_y, batch_width, char_height, color)
|
|
||||||
end
|
|
||||||
batch_syntax_type = type
|
|
||||||
batch_start = batch_start + batch_width
|
|
||||||
batch_width = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- per line
|
|
||||||
local endidx = minimap_start_line + max_minmap_lines
|
|
||||||
endidx = math.min(endidx, line_count)
|
|
||||||
for idx=minimap_start_line,endidx do
|
|
||||||
batch_syntax_type = nil
|
|
||||||
batch_start = x
|
|
||||||
batch_width = 0
|
|
||||||
|
|
||||||
-- per token
|
|
||||||
for _, type, text in self.doc.highlighter:each_token(idx) do
|
|
||||||
-- flush prev batch
|
|
||||||
if not batch_syntax_type then batch_syntax_type = type end
|
|
||||||
if batch_syntax_type ~= type then
|
|
||||||
flush_batch(type)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- per character
|
|
||||||
for char in common.utf8_chars(text) do
|
|
||||||
if char == " " or char == "\n" then
|
|
||||||
flush_batch(type)
|
|
||||||
batch_start = batch_start + char_spacing
|
|
||||||
elseif batch_start + batch_width > minimap_cutoff_x then
|
|
||||||
flush_batch(type)
|
|
||||||
break
|
|
||||||
else
|
|
||||||
batch_width = batch_width + char_spacing
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
flush_batch(nil)
|
|
||||||
line_y = line_y + line_spacing
|
|
||||||
end
|
|
||||||
|
|
||||||
else -- render lines without syntax highlighting
|
|
||||||
|
|
||||||
local function flush_batch()
|
|
||||||
if batch_width > 0 then
|
|
||||||
renderer.draw_rect(batch_start, line_y, batch_width, char_height, color)
|
|
||||||
end
|
|
||||||
batch_start = batch_start + batch_width
|
|
||||||
batch_width = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
for idx=1,line_count-1 do
|
|
||||||
batch_start = x
|
|
||||||
batch_width = 0
|
|
||||||
|
|
||||||
for char in common.utf8_chars(self.doc.lines[idx]) do
|
|
||||||
if char == " " or char == "\n" then
|
|
||||||
flush_batch()
|
|
||||||
batch_start = batch_start + char_spacing
|
|
||||||
elseif batch_start + batch_width > minimap_cutoff_x then
|
|
||||||
flush_batch()
|
|
||||||
else
|
|
||||||
batch_width = batch_width + char_spacing
|
|
||||||
end
|
|
||||||
end
|
|
||||||
flush_batch()
|
|
||||||
line_y = line_y + line_spacing
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add(nil, {
|
|
||||||
["minimap:toggle-visibility"] = function()
|
|
||||||
config.minimap_enabled = not config.minimap_enabled
|
|
||||||
end,
|
|
||||||
["minimap:toggle-syntax-highlighting"] = function()
|
|
||||||
config.minimap_syntax_highlight = not config.minimap_syntax_highlight
|
|
||||||
end,
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local config = require "core.config"
|
|
||||||
local style = require "core.style"
|
|
||||||
local Doc = require "core.doc"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
config.motiontrail_steps = config.motiontrail_steps or 50
|
|
||||||
|
|
||||||
local function doc()
|
|
||||||
return core.active_view.doc
|
|
||||||
end
|
|
||||||
|
|
||||||
local function lerp(a, b, t)
|
|
||||||
return a + (b - a) * t
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function get_caret_rect(dv, idx)
|
|
||||||
local line1, col1, line2, col2 = doc():get_selection_idx(idx)
|
|
||||||
local x1, y1 = dv:get_line_screen_position(line1)
|
|
||||||
x1 = x1 + dv:get_col_x_offset(line1, col1)
|
|
||||||
return x1, y1, style.caret_width, dv:get_line_height()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local last_x = {}
|
|
||||||
local last_y = {}
|
|
||||||
local last_view = {}
|
|
||||||
|
|
||||||
local draw = DocView.draw
|
|
||||||
|
|
||||||
function DocView:draw(...)
|
|
||||||
draw(self, ...)
|
|
||||||
if self ~= core.active_view then return end
|
|
||||||
|
|
||||||
for idx, line1, col1, line2, col2 in doc():get_selections(true, true) do
|
|
||||||
--if line1 == line2 and col1 == col2 then return false end
|
|
||||||
|
|
||||||
local x, y, w, h = get_caret_rect(self, idx)
|
|
||||||
|
|
||||||
if last_view[idx] == self and (x ~= last_x[idx] or y ~= last_y[idx]) then
|
|
||||||
local lx = x
|
|
||||||
for i = 0, 1, 1 / config.motiontrail_steps do
|
|
||||||
local ix = lerp(x, last_x[idx], i)
|
|
||||||
local iy = lerp(y, last_y[idx], i)
|
|
||||||
local iw = math.max(w, math.ceil(math.abs(ix - lx)))
|
|
||||||
renderer.draw_rect(ix, iy, iw, h, style.caret)
|
|
||||||
lx = ix
|
|
||||||
end
|
|
||||||
core.redraw = true
|
|
||||||
end
|
|
||||||
|
|
||||||
last_view[idx], last_x[idx], last_y[idx] = self, x, y
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local config = require "core.config"
|
|
||||||
|
|
||||||
|
|
||||||
if PLATFORM == "Windows" then
|
|
||||||
config.filemanager = "explorer"
|
|
||||||
elseif PLATFORM == "Mac OS X" then
|
|
||||||
config.filemanager = "open"
|
|
||||||
else
|
|
||||||
config.filemanager = "xdg-open"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["open-file-location:open-file-location"] = function()
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
if not doc.filename then
|
|
||||||
core.error "Cannot open location of unsaved doc"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local folder = doc.filename:match("^(.*)[/\\].*$") or "."
|
|
||||||
core.log("Opening \"%s\"", folder)
|
|
||||||
if PLATFORM == "Windows" then
|
|
||||||
system.exec(string.format("%s %s", config.filemanager, folder))
|
|
||||||
else
|
|
||||||
system.exec(string.format("%s %q", config.filemanager, folder))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
|
@ -1,399 +0,0 @@
|
||||||
-- mod-version:3
|
|
||||||
local core = require "core"
|
|
||||||
local common = require "core.common"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local command = require "core.command"
|
|
||||||
local style = require "core.style"
|
|
||||||
local View = require "core.view"
|
|
||||||
|
|
||||||
---@class plugins.projectsearch.resultsview : core.view
|
|
||||||
local ResultsView = View:extend()
|
|
||||||
|
|
||||||
ResultsView.context = "session"
|
|
||||||
|
|
||||||
function ResultsView:new(path, text, fn)
|
|
||||||
ResultsView.super.new(self)
|
|
||||||
self.scrollable = true
|
|
||||||
self.brightness = 0
|
|
||||||
self:begin_search(path, text, fn)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:get_name()
|
|
||||||
return "Search Results"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function find_all_matches_in_file(t, filename, fn)
|
|
||||||
local fp = io.open(filename)
|
|
||||||
if not fp then return t end
|
|
||||||
local n = 1
|
|
||||||
for line in fp:lines() do
|
|
||||||
local s = fn(line)
|
|
||||||
if s then
|
|
||||||
-- Insert maximum 256 characters. If we insert more, for compiled files, which can have very long lines
|
|
||||||
-- things tend to get sluggish. If our line is longer than 80 characters, begin to truncate the thing.
|
|
||||||
local start_index = math.max(s - 80, 1)
|
|
||||||
table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s })
|
|
||||||
core.redraw = true
|
|
||||||
end
|
|
||||||
if n % 100 == 0 then coroutine.yield(0) end
|
|
||||||
n = n + 1
|
|
||||||
core.redraw = true
|
|
||||||
end
|
|
||||||
fp:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:begin_search(path, text, fn)
|
|
||||||
self.search_args = { path, text, fn }
|
|
||||||
self.results = {}
|
|
||||||
self.last_file_idx = 1
|
|
||||||
self.query = text
|
|
||||||
self.searching = true
|
|
||||||
self.selected_idx = 0
|
|
||||||
|
|
||||||
core.add_thread(function()
|
|
||||||
local i = 1
|
|
||||||
for dir_name, file in core.get_project_files() do
|
|
||||||
if file.type == "file" and (not path or (dir_name .. "/" .. file.filename):find(path, 1, true) == 1) then
|
|
||||||
local truncated_path = (dir_name == core.project_dir and "" or (dir_name .. PATHSEP))
|
|
||||||
find_all_matches_in_file(self.results, truncated_path .. file.filename, fn)
|
|
||||||
end
|
|
||||||
self.last_file_idx = i
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
self.searching = false
|
|
||||||
self.brightness = 100
|
|
||||||
core.redraw = true
|
|
||||||
end, self.results)
|
|
||||||
|
|
||||||
self.scroll.to.y = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:refresh()
|
|
||||||
self:begin_search(table.unpack(self.search_args))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:on_mouse_moved(mx, my, ...)
|
|
||||||
ResultsView.super.on_mouse_moved(self, mx, my, ...)
|
|
||||||
self.selected_idx = 0
|
|
||||||
for i, item, x,y,w,h in self:each_visible_result() do
|
|
||||||
if mx >= x and my >= y and mx < x + w and my < y + h then
|
|
||||||
self.selected_idx = i
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:on_mouse_pressed(...)
|
|
||||||
local caught = ResultsView.super.on_mouse_pressed(self, ...)
|
|
||||||
if not caught then
|
|
||||||
return self:open_selected_result()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:open_selected_result()
|
|
||||||
local res = self.results[self.selected_idx]
|
|
||||||
if not res then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
core.try(function()
|
|
||||||
local dv = core.root_view:open_doc(core.open_doc(res.file))
|
|
||||||
core.root_view.root_node:update_layout()
|
|
||||||
dv.doc:set_selection(res.line, res.col)
|
|
||||||
dv:scroll_to_line(res.line, false, true)
|
|
||||||
end)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:update()
|
|
||||||
self:move_towards("brightness", 0, 0.1)
|
|
||||||
ResultsView.super.update(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:get_results_yoffset()
|
|
||||||
return style.font:get_height() + style.padding.y * 3
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:get_line_height()
|
|
||||||
return style.padding.y + style.font:get_height()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:get_scrollable_size()
|
|
||||||
return self:get_results_yoffset() + #self.results * self:get_line_height()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:get_visible_results_range()
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
local oy = self:get_results_yoffset()
|
|
||||||
local min = math.max(1, math.floor((self.scroll.y - oy) / lh))
|
|
||||||
return min, min + math.floor(self.size.y / lh) + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:each_visible_result()
|
|
||||||
return coroutine.wrap(function()
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
local x, y = self:get_content_offset()
|
|
||||||
local min, max = self:get_visible_results_range()
|
|
||||||
y = y + self:get_results_yoffset() + lh * (min - 1)
|
|
||||||
for i = min, max do
|
|
||||||
local item = self.results[i]
|
|
||||||
if not item then break end
|
|
||||||
coroutine.yield(i, item, x, y, self.size.x, lh)
|
|
||||||
y = y + lh
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:scroll_to_make_selected_visible()
|
|
||||||
local h = self:get_line_height()
|
|
||||||
local y = self:get_results_yoffset() + h * (self.selected_idx - 1)
|
|
||||||
self.scroll.to.y = math.min(self.scroll.to.y, y)
|
|
||||||
self.scroll.to.y = math.max(self.scroll.to.y, y + h - self.size.y)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ResultsView:draw()
|
|
||||||
self:draw_background(style.background)
|
|
||||||
|
|
||||||
-- status
|
|
||||||
local ox, oy = self:get_content_offset()
|
|
||||||
local x, y = ox + style.padding.x, oy + style.padding.y
|
|
||||||
local files_number = core.project_files_number()
|
|
||||||
local per = common.clamp(files_number and self.last_file_idx / files_number or 1, 0, 1)
|
|
||||||
local text
|
|
||||||
if self.searching then
|
|
||||||
if files_number then
|
|
||||||
text = string.format("Searching %.f%% (%d of %d files, %d matches) for %q...",
|
|
||||||
per * 100, self.last_file_idx, files_number,
|
|
||||||
#self.results, self.query)
|
|
||||||
else
|
|
||||||
text = string.format("Searching (%d files, %d matches) for %q...",
|
|
||||||
self.last_file_idx, #self.results, self.query)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
text = string.format("Found %d matches for %q",
|
|
||||||
#self.results, self.query)
|
|
||||||
end
|
|
||||||
local color = common.lerp(style.text, style.accent, self.brightness / 100)
|
|
||||||
renderer.draw_text(style.font, text, x, y, color)
|
|
||||||
|
|
||||||
-- horizontal line
|
|
||||||
local yoffset = self:get_results_yoffset()
|
|
||||||
local x = ox + style.padding.x
|
|
||||||
local w = self.size.x - style.padding.x * 2
|
|
||||||
local h = style.divider_size
|
|
||||||
local color = common.lerp(style.dim, style.text, self.brightness / 100)
|
|
||||||
renderer.draw_rect(x, oy + yoffset - style.padding.y, w, h, color)
|
|
||||||
if self.searching then
|
|
||||||
renderer.draw_rect(x, oy + yoffset - style.padding.y, w * per, h, style.text)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- results
|
|
||||||
local y1, y2 = self.position.y, self.position.y + self.size.y
|
|
||||||
for i, item, x,y,w,h in self:each_visible_result() do
|
|
||||||
local color = style.text
|
|
||||||
if i == self.selected_idx then
|
|
||||||
color = style.accent
|
|
||||||
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
|
||||||
end
|
|
||||||
x = x + style.padding.x
|
|
||||||
local text = string.format("%s at line %d (col %d): ", item.file, item.line, item.col)
|
|
||||||
x = common.draw_text(style.font, style.dim, text, "left", x, y, w, h)
|
|
||||||
x = common.draw_text(style.code_font, color, item.text, "left", x, y, w, h)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:draw_scrollbar()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---@param path string
|
|
||||||
---@param text string
|
|
||||||
---@param fn fun(line_text:string):...
|
|
||||||
---@return plugins.projectsearch.resultsview?
|
|
||||||
local function begin_search(path, text, fn)
|
|
||||||
if text == "" then
|
|
||||||
core.error("Expected non-empty string")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local rv = ResultsView(path, text, fn)
|
|
||||||
core.root_view:get_active_node_default():add_view(rv)
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function get_selected_text()
|
|
||||||
local view = core.active_view
|
|
||||||
local doc = (view and view.doc) and view.doc or nil
|
|
||||||
if doc then
|
|
||||||
return doc:get_text(table.unpack({ doc:get_selection() }))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function normalize_path(path)
|
|
||||||
if not path then return nil end
|
|
||||||
path = common.normalize_path(path)
|
|
||||||
for i, project_dir in ipairs(core.project_directories) do
|
|
||||||
if common.path_belongs_to(path, project_dir.name) then
|
|
||||||
return project_dir.item.filename .. PATHSEP .. common.relative_path(project_dir.name, path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return path
|
|
||||||
end
|
|
||||||
|
|
||||||
---@class plugins.projectsearch
|
|
||||||
local projectsearch = {}
|
|
||||||
|
|
||||||
---@type plugins.projectsearch.resultsview
|
|
||||||
projectsearch.ResultsView = ResultsView
|
|
||||||
|
|
||||||
---@param text string
|
|
||||||
---@param path string
|
|
||||||
---@param insensitive? boolean
|
|
||||||
---@return plugins.projectsearch.resultsview?
|
|
||||||
function projectsearch.search_plain(text, path, insensitive)
|
|
||||||
if insensitive then text = text:lower() end
|
|
||||||
return begin_search(path, text, function(line_text)
|
|
||||||
if insensitive then
|
|
||||||
return line_text:lower():find(text, nil, true)
|
|
||||||
else
|
|
||||||
return line_text:find(text, nil, true)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param text string
|
|
||||||
---@param path string
|
|
||||||
---@param insensitive? boolean
|
|
||||||
---@return plugins.projectsearch.resultsview?
|
|
||||||
function projectsearch.search_regex(text, path, insensitive)
|
|
||||||
local re, errmsg
|
|
||||||
if insensitive then
|
|
||||||
re, errmsg = regex.compile(text, "i")
|
|
||||||
else
|
|
||||||
re, errmsg = regex.compile(text)
|
|
||||||
end
|
|
||||||
if not re then core.log("%s", errmsg) return end
|
|
||||||
return begin_search(path, text, function(line_text)
|
|
||||||
return regex.cmatch(re, line_text)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param text string
|
|
||||||
---@param path string
|
|
||||||
---@param insensitive? boolean
|
|
||||||
---@return plugins.projectsearch.resultsview?
|
|
||||||
function projectsearch.search_fuzzy(text, path, insensitive)
|
|
||||||
if insensitive then text = text:lower() end
|
|
||||||
return begin_search(path, text, function(line_text)
|
|
||||||
if insensitive then
|
|
||||||
return common.fuzzy_match(line_text:lower(), text) and 1
|
|
||||||
else
|
|
||||||
return common.fuzzy_match(line_text, text) and 1
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add(nil, {
|
|
||||||
["project-search:find"] = function(path)
|
|
||||||
core.command_view:enter("Find Text In " .. (normalize_path(path) or "Project"), {
|
|
||||||
text = get_selected_text(),
|
|
||||||
select_text = true,
|
|
||||||
submit = function(text)
|
|
||||||
projectsearch.search_plain(text, path, true)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:find-regex"] = function(path)
|
|
||||||
core.command_view:enter("Find Regex In " .. (normalize_path(path) or "Project"), {
|
|
||||||
submit = function(text)
|
|
||||||
projectsearch.search_regex(text, path, true)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:fuzzy-find"] = function(path)
|
|
||||||
core.command_view:enter("Fuzzy Find Text In " .. (normalize_path(path) or "Project"), {
|
|
||||||
text = get_selected_text(),
|
|
||||||
select_text = true,
|
|
||||||
submit = function(text)
|
|
||||||
projectsearch.search_fuzzy(text, path, true)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
command.add(ResultsView, {
|
|
||||||
["project-search:select-previous"] = function()
|
|
||||||
local view = core.active_view
|
|
||||||
view.selected_idx = math.max(view.selected_idx - 1, 1)
|
|
||||||
view:scroll_to_make_selected_visible()
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:select-next"] = function()
|
|
||||||
local view = core.active_view
|
|
||||||
view.selected_idx = math.min(view.selected_idx + 1, #view.results)
|
|
||||||
view:scroll_to_make_selected_visible()
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:open-selected"] = function()
|
|
||||||
core.active_view:open_selected_result()
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:refresh"] = function()
|
|
||||||
core.active_view:refresh()
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:move-to-previous-page"] = function()
|
|
||||||
local view = core.active_view
|
|
||||||
view.scroll.to.y = view.scroll.to.y - view.size.y
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:move-to-next-page"] = function()
|
|
||||||
local view = core.active_view
|
|
||||||
view.scroll.to.y = view.scroll.to.y + view.size.y
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:move-to-start-of-doc"] = function()
|
|
||||||
local view = core.active_view
|
|
||||||
view.scroll.to.y = 0
|
|
||||||
end,
|
|
||||||
|
|
||||||
["project-search:move-to-end-of-doc"] = function()
|
|
||||||
local view = core.active_view
|
|
||||||
view.scroll.to.y = view:get_scrollable_size()
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["f5"] = "project-search:refresh",
|
|
||||||
["ctrl+shift+f"] = "project-search:find",
|
|
||||||
["up"] = "project-search:select-previous",
|
|
||||||
["down"] = "project-search:select-next",
|
|
||||||
["return"] = "project-search:open-selected",
|
|
||||||
["pageup"] = "project-search:move-to-previous-page",
|
|
||||||
["pagedown"] = "project-search:move-to-next-page",
|
|
||||||
["ctrl+home"] = "project-search:move-to-start-of-doc",
|
|
||||||
["ctrl+end"] = "project-search:move-to-end-of-doc",
|
|
||||||
["home"] = "project-search:move-to-start-of-doc",
|
|
||||||
["end"] = "project-search:move-to-end-of-doc"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return projectsearch
|
|
|
@ -1,31 +0,0 @@
|
||||||
-- mod-version:3
|
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
|
|
||||||
|
|
||||||
local escapes = {
|
|
||||||
["\\"] = "\\\\",
|
|
||||||
["\""] = "\\\"",
|
|
||||||
["\n"] = "\\n",
|
|
||||||
["\r"] = "\\r",
|
|
||||||
["\t"] = "\\t",
|
|
||||||
["\b"] = "\\b",
|
|
||||||
}
|
|
||||||
|
|
||||||
local function replace(chr)
|
|
||||||
return escapes[chr] or string.format("\\x%02x", chr:byte())
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["quote:quote"] = function(dv)
|
|
||||||
dv.doc:replace(function(text)
|
|
||||||
return '"' .. text:gsub("[\0-\31\\\"]", replace) .. '"'
|
|
||||||
end)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["ctrl+'"] = "quote:quote",
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
local tokenizer = require "core.tokenizer"
|
|
||||||
local style = require "core.style"
|
|
||||||
local common = require "core.common"
|
|
||||||
|
|
||||||
local tokenize = tokenizer.tokenize
|
|
||||||
local closers = {
|
|
||||||
["("] = ")",
|
|
||||||
["["] = "]",
|
|
||||||
["{"] = "}"
|
|
||||||
}
|
|
||||||
local function parenstyle(parenstack)
|
|
||||||
return "paren" .. ((#parenstack % 5) + 1)
|
|
||||||
end
|
|
||||||
function tokenizer.tokenize(syntax, text, state)
|
|
||||||
state = state or {}
|
|
||||||
local res, istate = tokenize(syntax, text, state.istate)
|
|
||||||
local parenstack = state.parenstack or ""
|
|
||||||
local newres = {}
|
|
||||||
-- split parens out
|
|
||||||
-- the stock tokenizer can't do this because it merges identical adjacent tokens
|
|
||||||
for i, type, text in tokenizer.each_token(res) do
|
|
||||||
if type == "normal" or type == "symbol" then
|
|
||||||
for normtext1, paren, normtext2 in text:gmatch("([^%(%[{}%]%)]*)([%(%[{}%]%)]?)([^%(%[{}%]%)]*)") do
|
|
||||||
if #normtext1 > 0 then
|
|
||||||
table.insert(newres, type)
|
|
||||||
table.insert(newres, normtext1)
|
|
||||||
end
|
|
||||||
if #paren > 0 then
|
|
||||||
if paren == parenstack:sub(-1) then -- expected closer
|
|
||||||
parenstack = parenstack:sub(1, -2)
|
|
||||||
table.insert(newres, parenstyle(parenstack))
|
|
||||||
elseif closers[paren] then -- opener
|
|
||||||
table.insert(newres, parenstyle(parenstack))
|
|
||||||
parenstack = parenstack .. closers[paren]
|
|
||||||
else -- unexpected closer
|
|
||||||
table.insert(newres, "paren_unbalanced")
|
|
||||||
end
|
|
||||||
table.insert(newres, paren)
|
|
||||||
end
|
|
||||||
if #normtext2 > 0 then
|
|
||||||
table.insert(newres, type)
|
|
||||||
table.insert(newres, normtext2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(newres, type)
|
|
||||||
table.insert(newres, text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return newres, { parenstack = parenstack, istate = istate }
|
|
||||||
end
|
|
||||||
|
|
||||||
style.syntax.paren_unbalanced = style.syntax.paren_unbalanced or { common.color "#DC0408" }
|
|
||||||
style.syntax.paren1 = style.syntax.paren1 or { common.color "#FC6F71"}
|
|
||||||
style.syntax.paren2 = style.syntax.paren2 or { common.color "#fcb053"}
|
|
||||||
style.syntax.paren3 = style.syntax.paren3 or { common.color "#fcd476"}
|
|
||||||
style.syntax.paren4 = style.syntax.paren4 or { common.color "#52dab2"}
|
|
||||||
style.syntax.paren5 = style.syntax.paren5 or { common.color "#5a98cf"}
|
|
|
@ -1,63 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local config = require "core.config"
|
|
||||||
local command = require "core.command"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
|
|
||||||
|
|
||||||
local function wordwrap_text(text, limit)
|
|
||||||
local t = {}
|
|
||||||
local n = 0
|
|
||||||
|
|
||||||
for word in text:gmatch("%S+") do
|
|
||||||
if n + #word > limit then
|
|
||||||
table.insert(t, "\n")
|
|
||||||
n = 0
|
|
||||||
elseif #t > 0 then
|
|
||||||
table.insert(t, " ")
|
|
||||||
end
|
|
||||||
table.insert(t, word)
|
|
||||||
n = n + #word + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(t)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["reflow:reflow"] = function()
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
doc:replace(function(text)
|
|
||||||
local prefix_set = "[^%w\n%[%](){}`'\"]*"
|
|
||||||
|
|
||||||
-- get line prefix and trailing whitespace
|
|
||||||
local prefix1 = text:match("^\n*" .. prefix_set)
|
|
||||||
local prefix2 = text:match("\n(" .. prefix_set .. ")", #prefix1+1)
|
|
||||||
local trailing = text:match("%s*$")
|
|
||||||
if not prefix2 or prefix2 == "" then
|
|
||||||
prefix2 = prefix1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- strip all line prefixes and trailing whitespace
|
|
||||||
text = text:sub(#prefix1+1, -#trailing - 1):gsub("\n" .. prefix_set, "\n")
|
|
||||||
|
|
||||||
-- split into blocks, wordwrap and join
|
|
||||||
local line_limit = config.line_limit - #prefix1
|
|
||||||
local blocks = {}
|
|
||||||
text = text:gsub("\n\n", "\0")
|
|
||||||
for block in text:gmatch("%Z+") do
|
|
||||||
table.insert(blocks, wordwrap_text(block, line_limit))
|
|
||||||
end
|
|
||||||
text = table.concat(blocks, "\n\n")
|
|
||||||
|
|
||||||
-- add prefix to start of lines
|
|
||||||
text = prefix1 .. text:gsub("\n", "\n" .. prefix2) .. trailing
|
|
||||||
|
|
||||||
return text
|
|
||||||
end)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["ctrl+shift+q"] = "reflow:reflow"
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local common = require "core.common"
|
|
||||||
local command = require "core.command"
|
|
||||||
local config = require "core.config"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local style = require "core.style"
|
|
||||||
local RootView = require "core.rootview"
|
|
||||||
local CommandView = require "core.commandview"
|
|
||||||
|
|
||||||
config.scale_mode = "code"
|
|
||||||
config.scale_use_mousewheel = true
|
|
||||||
|
|
||||||
local font_cache = setmetatable({}, { __mode = "k" })
|
|
||||||
|
|
||||||
-- the following should be kept in sync with core.style's default font settings
|
|
||||||
font_cache[style.font] = { DATADIR .. "/data/fonts/font.ttf", 14 * SCALE }
|
|
||||||
font_cache[style.big_font] = { DATADIR .. "/data/fonts/font.ttf", 34 * SCALE }
|
|
||||||
font_cache[style.icon_font] = { DATADIR .. "/data/fonts/icons.ttf", 14 * SCALE }
|
|
||||||
font_cache[style.code_font] = { DATADIR .. "/data/fonts/monospace.ttf", 13.5 * SCALE }
|
|
||||||
|
|
||||||
|
|
||||||
local load_font = renderer.font.load
|
|
||||||
function renderer.font.load(...)
|
|
||||||
local res = load_font(...)
|
|
||||||
font_cache[res] = { ... }
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function scale_font(font, s)
|
|
||||||
local fc = font_cache[font]
|
|
||||||
return renderer.font.load(fc[1], fc[2] * s)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local current_scale = SCALE
|
|
||||||
local default = current_scale
|
|
||||||
|
|
||||||
|
|
||||||
local function get_scale() return current_scale end
|
|
||||||
|
|
||||||
|
|
||||||
local function set_scale(scale)
|
|
||||||
scale = common.clamp(scale, 0.2, 6)
|
|
||||||
|
|
||||||
-- save scroll positions
|
|
||||||
local scrolls = {}
|
|
||||||
for _, view in ipairs(core.root_view.root_node:get_children()) do
|
|
||||||
local n = view:get_scrollable_size()
|
|
||||||
if n ~= math.huge and not view:is(CommandView) then
|
|
||||||
scrolls[view] = view.scroll.y / (n - view.size.y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local s = scale / current_scale
|
|
||||||
current_scale = scale
|
|
||||||
|
|
||||||
if config.scale_mode == "ui" then
|
|
||||||
SCALE = current_scale
|
|
||||||
|
|
||||||
style.padding.x = style.padding.x * s
|
|
||||||
style.padding.y = style.padding.y * s
|
|
||||||
style.divider_size = style.divider_size * s
|
|
||||||
style.scrollbar_size = style.scrollbar_size * s
|
|
||||||
style.caret_width = style.caret_width * s
|
|
||||||
style.tab_width = style.tab_width * s
|
|
||||||
|
|
||||||
style.big_font = scale_font(style.big_font, s)
|
|
||||||
style.icon_font = scale_font(style.icon_font, s)
|
|
||||||
style.font = scale_font(style.font, s)
|
|
||||||
end
|
|
||||||
|
|
||||||
style.code_font = scale_font(style.code_font, s)
|
|
||||||
|
|
||||||
-- restore scroll positions
|
|
||||||
for view, n in pairs(scrolls) do
|
|
||||||
view.scroll.y = n * (view:get_scrollable_size() - view.size.y)
|
|
||||||
view.scroll.to.y = view.scroll.y
|
|
||||||
end
|
|
||||||
|
|
||||||
core.redraw = true
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local on_mouse_wheel = RootView.on_mouse_wheel
|
|
||||||
|
|
||||||
function RootView:on_mouse_wheel(d, ...)
|
|
||||||
if keymap.modkeys["ctrl"] and config.scale_use_mousewheel then
|
|
||||||
if d < 0 then command.perform "scale:decrease" end
|
|
||||||
if d > 0 then command.perform "scale:increase" end
|
|
||||||
else
|
|
||||||
return on_mouse_wheel(self, d, ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add(nil, {
|
|
||||||
["scale:reset" ] = function() set_scale(default) end,
|
|
||||||
["scale:decrease"] = function() set_scale(current_scale * 0.9) end,
|
|
||||||
["scale:increase"] = function() set_scale(current_scale * 1.1) end,
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["ctrl+0"] = "scale:reset",
|
|
||||||
["ctrl+-"] = "scale:decrease",
|
|
||||||
["ctrl++"] = "scale:increase",
|
|
||||||
}
|
|
||||||
|
|
||||||
return { get_scale = get_scale, set_scale = set_scale }
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
--[[
|
|
||||||
scalestatus.lua
|
|
||||||
displays current scale (zoom) in status view
|
|
||||||
version: 20200628_155804
|
|
||||||
originally by SwissalpS
|
|
||||||
|
|
||||||
Depends on plugin scale.lua version >= 20200628_154010
|
|
||||||
--]]
|
|
||||||
local scale = require "plugins.scale"
|
|
||||||
-- make sure plugin is installed and has get_scale field
|
|
||||||
if not scale.get_scale then
|
|
||||||
local core = require "core"
|
|
||||||
core.error("Plugin 'scale' needs to be updated, scalestatus inactive.")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local config = require "core.config"
|
|
||||||
local StatusView = require "core.statusview"
|
|
||||||
|
|
||||||
config.scalestatus_format = '%.0f%%'
|
|
||||||
|
|
||||||
local get_items = StatusView.get_items
|
|
||||||
function StatusView:get_items()
|
|
||||||
|
|
||||||
local left, right = get_items(self)
|
|
||||||
|
|
||||||
local t = {
|
|
||||||
self.separator,
|
|
||||||
string.format(config.scalestatus_format, scale.get_scale() * 100),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item in ipairs(t) do
|
|
||||||
table.insert(right, item)
|
|
||||||
end
|
|
||||||
|
|
||||||
return left, right
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
|
@ -1,37 +0,0 @@
|
||||||
local style = require "core.style"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
-- originally written by luveti
|
|
||||||
|
|
||||||
local function draw_box(x, y, w, h, color)
|
|
||||||
local r = renderer.draw_rect
|
|
||||||
local s = math.ceil(SCALE)
|
|
||||||
r(x, y, w, s, color)
|
|
||||||
r(x, y + h - s, w, s, color)
|
|
||||||
r(x, y + s, s, h - s * 2, color)
|
|
||||||
r(x + w - s, y + s, s, h - s * 2, color)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local draw_line_body = DocView.draw_line_body
|
|
||||||
|
|
||||||
function DocView:draw_line_body(idx, x, y)
|
|
||||||
local line1, col1, line2, col2 = self.doc:get_selection(true)
|
|
||||||
if line1 == line2 and col1 ~= col2 then
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
local selected_text = self.doc.lines[line1]:sub(col1, col2 - 1)
|
|
||||||
local current_line_text = self.doc.lines[idx]
|
|
||||||
local last_col = 1
|
|
||||||
while true do
|
|
||||||
local start_col, end_col = current_line_text:find(selected_text, last_col, true)
|
|
||||||
if start_col == nil then break end
|
|
||||||
local x1 = x + self:get_col_x_offset(idx, start_col)
|
|
||||||
local x2 = x + self:get_col_x_offset(idx, end_col + 1)
|
|
||||||
local color = style.selectionhighlight or style.syntax.comment
|
|
||||||
draw_box(x1, y, x2 - x1, lh, color)
|
|
||||||
last_col = end_col + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
draw_line_body(self, idx, x, y)
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
-- mod-version:2 -- lite-xl 2.0
|
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local translate = require "core.doc.translate"
|
|
||||||
|
|
||||||
local function split_lines(text)
|
|
||||||
local res = {}
|
|
||||||
for line in (text .. "\n"):gmatch("(.-)\n") do
|
|
||||||
table.insert(res, line)
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["sort:sort"] = function()
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
|
|
||||||
local l1, c1, l2, c2, swap = doc:get_selection(true)
|
|
||||||
l1, c1 = translate.start_of_line(doc, l1, c1)
|
|
||||||
l2, c2 = translate.end_of_line(doc, l2, c2)
|
|
||||||
doc:set_selection(l1, c1, l2, c2, swap)
|
|
||||||
|
|
||||||
doc:replace(function(text)
|
|
||||||
local head, body, foot = text:match("(\n*)(.-)(\n*)$")
|
|
||||||
local lines = split_lines(body)
|
|
||||||
table.sort(lines, function(a, b) return a:lower() < b:lower() end)
|
|
||||||
return head .. table.concat(lines, "\n") .. foot
|
|
||||||
end)
|
|
||||||
end,
|
|
||||||
})
|
|
|
@ -1,63 +0,0 @@
|
||||||
-- mod-version:3
|
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local translate = require "core.doc.translate"
|
|
||||||
|
|
||||||
|
|
||||||
local function gmatch_to_array(text, ptn)
|
|
||||||
local res = {}
|
|
||||||
for x in text:gmatch(ptn) do
|
|
||||||
table.insert(res, x)
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function tabularize_lines(lines, delim)
|
|
||||||
local rows = {}
|
|
||||||
local cols = {}
|
|
||||||
|
|
||||||
-- split lines at delimiters and get maximum width of columns
|
|
||||||
local ptn = "[^" .. delim:sub(1,1):gsub("%W", "%%%1") .. "]+"
|
|
||||||
for i, line in ipairs(lines) do
|
|
||||||
rows[i] = gmatch_to_array(line, ptn)
|
|
||||||
for j, col in ipairs(rows[i]) do
|
|
||||||
cols[j] = math.max(#col, cols[j] or 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- pad columns with space
|
|
||||||
for _, row in ipairs(rows) do
|
|
||||||
for i = 1, #row - 1 do
|
|
||||||
row[i] = row[i] .. string.rep(" ", cols[i] - #row[i])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- write columns back to lines array
|
|
||||||
for i, line in ipairs(lines) do
|
|
||||||
lines[i] = table.concat(rows[i], delim)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["tabularize:tabularize"] = function(dv)
|
|
||||||
core.command_view:enter("Tabularize On Delimiter", {
|
|
||||||
submit = function(delim)
|
|
||||||
if delim == "" then delim = " " end
|
|
||||||
|
|
||||||
local doc = dv.doc
|
|
||||||
local line1, col1, line2, col2, swap = doc:get_selection(true)
|
|
||||||
line1, col1 = doc:position_offset(line1, col1, translate.start_of_line)
|
|
||||||
line2, col2 = doc:position_offset(line2, col2, translate.end_of_line)
|
|
||||||
doc:set_selection(line1, col1, line2, col2, swap)
|
|
||||||
|
|
||||||
doc:replace(function(text)
|
|
||||||
local lines = gmatch_to_array(text, "[^\n]*\n?")
|
|
||||||
tabularize_lines(lines, delim)
|
|
||||||
return table.concat(lines)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
})
|
|
|
@ -1,389 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local common = require "core.common"
|
|
||||||
local command = require "core.command"
|
|
||||||
local config = require "core.config"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local style = require "core.style"
|
|
||||||
local View = require "core.view"
|
|
||||||
|
|
||||||
|
|
||||||
local TodoTreeView = View:extend()
|
|
||||||
|
|
||||||
config.todo_tags = { --"TODO", "BUG", "FIX", "FIXME", "IMPROVEMENT",
|
|
||||||
"@todo", "@fixme", "@testme", "@leak" } --< @r-lyeh
|
|
||||||
|
|
||||||
-- Paths or files to be ignored
|
|
||||||
config.todo_ignore_paths = {
|
|
||||||
"tools/tcc", --< @r-lyeh
|
|
||||||
"tools\\tcc", --< @r-lyeh
|
|
||||||
"engine/fwk", --< @r-lyeh
|
|
||||||
"engine\\fwk", --< @r-lyeh
|
|
||||||
"engine/joint", --< @r-lyeh
|
|
||||||
"engine\\joint", --< @r-lyeh
|
|
||||||
}
|
|
||||||
|
|
||||||
-- 'tag' mode can be used to group the todos by tags
|
|
||||||
-- 'file' mode can be used to group the todos by files
|
|
||||||
config.todo_mode = "tag"
|
|
||||||
|
|
||||||
-- Tells if the plugin should start with the nodes expanded. default: true for tag mode
|
|
||||||
config.todo_expanded = config.todo_mode == "tag"
|
|
||||||
|
|
||||||
-- list of allowed extensions: items must start and end with a dot character
|
|
||||||
config.todo_allowed_extensions = '.h.c.m.hh.cc.hpp.cpp.cxx.lua.py.cs.vs.fs.bat.' --< @r-lyeh
|
|
||||||
|
|
||||||
-- whether the sidebar treeview is initially visible or not
|
|
||||||
config.todo_visible = false
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:new()
|
|
||||||
TodoTreeView.super.new(self)
|
|
||||||
self.scrollable = true
|
|
||||||
self.focusable = false
|
|
||||||
self.visible = config.todo_visible
|
|
||||||
self.times_cache = {}
|
|
||||||
self.cache = {}
|
|
||||||
self.cache_updated = false
|
|
||||||
self.init_size = true
|
|
||||||
|
|
||||||
-- Items are generated from cache according to the mode
|
|
||||||
self.items = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_file_ignored(filename)
|
|
||||||
for _, path in ipairs(config.todo_ignore_paths) do
|
|
||||||
local s, _ = filename:find(path)
|
|
||||||
if s then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function TodoTreeView:refresh_cache()
|
|
||||||
local items = {}
|
|
||||||
if not next(self.items) then
|
|
||||||
items = self.items
|
|
||||||
end
|
|
||||||
self.updating_cache = true
|
|
||||||
|
|
||||||
core.add_thread(function()
|
|
||||||
for _, item in ipairs(core.project_files) do
|
|
||||||
local ignored = is_file_ignored(item.filename)
|
|
||||||
if not ignored and item.type == "file" then
|
|
||||||
local cached = self:get_cached(item)
|
|
||||||
|
|
||||||
if config.todo_mode == "file" then
|
|
||||||
items[cached.filename] = cached
|
|
||||||
else
|
|
||||||
for _, todo in ipairs(cached.todos) do
|
|
||||||
local tag = todo.tag
|
|
||||||
if not items[tag] then
|
|
||||||
local t = {}
|
|
||||||
t.expanded = config.todo_expanded
|
|
||||||
t.type = "group"
|
|
||||||
t.todos = {}
|
|
||||||
t.tag = tag
|
|
||||||
items[tag] = t
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(items[tag].todos, todo)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Copy expanded from old items
|
|
||||||
if config.todo_mode == "tag" and next(self.items) then
|
|
||||||
for tag, data in pairs(self.items) do
|
|
||||||
if items[tag] then
|
|
||||||
items[tag].expanded = data.expanded
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.items = items
|
|
||||||
core.redraw = true
|
|
||||||
self.cache_updated = true
|
|
||||||
self.updating_cache = false
|
|
||||||
end, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function find_file_todos(t, filename)
|
|
||||||
--< @r-lyeh
|
|
||||||
local ext = (filename:match "[^.]+$") .. '.'
|
|
||||||
if not string.find(config.todo_allowed_extensions,ext) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
--<
|
|
||||||
|
|
||||||
local fp = io.open(filename)
|
|
||||||
if not fp then return t end
|
|
||||||
|
|
||||||
--< @r-lyeh: optimized loops: early exit if quicksearch fails
|
|
||||||
local function lines(str)
|
|
||||||
local result = {}
|
|
||||||
for line in string.gmatch(str, "(.-)%c") do -- line in str:gmatch '[^\n]+' do
|
|
||||||
-- Add spaces at the start and end of line so the pattern will pick
|
|
||||||
-- tags at the start and at the end of lines
|
|
||||||
table.insert(result, " "..line.." ")
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
local before = #t
|
|
||||||
local content = fp:read("*all")
|
|
||||||
for _, todo_tag in ipairs(config.todo_tags) do
|
|
||||||
if string.find(content, todo_tag) then
|
|
||||||
local n = 0
|
|
||||||
for _, line in ipairs(lines(content)) do
|
|
||||||
n = n + 1
|
|
||||||
local match_str = todo_tag[1] == '@' and todo_tag or "[^a-zA-Z_\"'`]"..todo_tag.."[^a-zA-Z_\"'`]+"
|
|
||||||
local s, e = line:find(match_str)
|
|
||||||
if s then
|
|
||||||
local d = {}
|
|
||||||
d.tag = string.sub(string.upper(todo_tag), todo_tag:byte(1) == 64 and 2 or 1) .. 's'
|
|
||||||
d.filename = filename
|
|
||||||
d.text = line:sub(e+1)
|
|
||||||
if d.text == "" then
|
|
||||||
d.text = config.todo_mode == "tag" and filename:match("^.+[/\\](.+)$") or "blank"
|
|
||||||
end
|
|
||||||
d.line = n
|
|
||||||
d.col = s
|
|
||||||
table.insert(t, d)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
fp:close()
|
|
||||||
if #t ~= before then
|
|
||||||
coroutine.yield()
|
|
||||||
core.redraw = true
|
|
||||||
end
|
|
||||||
--<
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:get_cached(item)
|
|
||||||
local t = self.cache[item.filename]
|
|
||||||
if not t then
|
|
||||||
t = {}
|
|
||||||
t.expanded = config.todo_expanded
|
|
||||||
t.filename = item.filename
|
|
||||||
t.abs_filename = system.absolute_path(item.filename)
|
|
||||||
t.type = item.type
|
|
||||||
t.todos = {}
|
|
||||||
find_file_todos(t.todos, t.filename)
|
|
||||||
self.cache[t.filename] = t
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:get_name()
|
|
||||||
return "Todo Tree"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:get_item_height()
|
|
||||||
return style.font:get_height() + style.padding.y
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:get_cached_time(doc)
|
|
||||||
local t = self.times_cache[doc]
|
|
||||||
if not t then
|
|
||||||
local info = system.get_file_info(doc.filename)
|
|
||||||
if not info then return nil end
|
|
||||||
self.times_cache[doc] = info.modified
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:check_cache()
|
|
||||||
for _, doc in ipairs(core.docs) do
|
|
||||||
if doc.filename then
|
|
||||||
local info = system.get_file_info(doc.filename)
|
|
||||||
local cached = self:get_cached_time(doc)
|
|
||||||
if not info and cached then
|
|
||||||
-- document deleted
|
|
||||||
self.times_cache[doc] = nil
|
|
||||||
self.cache[doc.filename] = nil
|
|
||||||
self.cache_updated = false
|
|
||||||
elseif cached and cached ~= info.modified then
|
|
||||||
-- document modified
|
|
||||||
self.times_cache[doc] = info.modified
|
|
||||||
self.cache[doc.filename] = nil
|
|
||||||
self.cache_updated = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if core.project_files ~= self.last_project_files then
|
|
||||||
self.last_project_files = core.project_files
|
|
||||||
self.cache_updated = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function TodoTreeView:each_item()
|
|
||||||
self:check_cache()
|
|
||||||
if not self.updating_cache and not self.cache_updated then
|
|
||||||
self:refresh_cache()
|
|
||||||
end
|
|
||||||
|
|
||||||
return coroutine.wrap(function()
|
|
||||||
local ox, oy = self:get_content_offset()
|
|
||||||
local y = oy + style.padding.y
|
|
||||||
local w = self.size.x
|
|
||||||
local h = self:get_item_height()
|
|
||||||
|
|
||||||
for _, item in pairs(self.items) do
|
|
||||||
if #item.todos > 0 then
|
|
||||||
coroutine.yield(item, ox, y, w, h)
|
|
||||||
y = y + h
|
|
||||||
|
|
||||||
for _, todo in ipairs(item.todos) do
|
|
||||||
if item.expanded then
|
|
||||||
coroutine.yield(todo, ox, y, w, h)
|
|
||||||
y = y + h
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:on_mouse_moved(px, py)
|
|
||||||
self.hovered_item = nil
|
|
||||||
for item, x,y,w,h in self:each_item() do
|
|
||||||
if px > x and py > y and px <= x + w and py <= y + h then
|
|
||||||
self.hovered_item = item
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:on_mouse_pressed(button, x, y)
|
|
||||||
if not self.hovered_item then
|
|
||||||
return
|
|
||||||
elseif self.hovered_item.type == "file"
|
|
||||||
or self.hovered_item.type == "group" then
|
|
||||||
self.hovered_item.expanded = not self.hovered_item.expanded
|
|
||||||
else
|
|
||||||
core.try(function()
|
|
||||||
local i = self.hovered_item
|
|
||||||
local dv = core.root_view:open_doc(core.open_doc(i.filename))
|
|
||||||
core.root_view.root_node:update_layout()
|
|
||||||
dv.doc:set_selection(i.line, i.col)
|
|
||||||
dv:scroll_to_line(i.line, false, true)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:update()
|
|
||||||
self.scroll.to.y = math.max(0, self.scroll.to.y)
|
|
||||||
|
|
||||||
-- update width
|
|
||||||
local dest = self.visible and config.treeview_size or 0
|
|
||||||
if self.init_size then
|
|
||||||
self.size.x = dest
|
|
||||||
self.init_size = false
|
|
||||||
else
|
|
||||||
self:move_towards(self.size, "x", dest)
|
|
||||||
end
|
|
||||||
|
|
||||||
TodoTreeView.super.update(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TodoTreeView:draw()
|
|
||||||
self:draw_background(style.background2)
|
|
||||||
|
|
||||||
--local h = self:get_item_height()
|
|
||||||
local icon_width = style.icon_font:get_width("D")
|
|
||||||
local spacing = style.font:get_width(" ") * 2
|
|
||||||
local root_depth = 0
|
|
||||||
|
|
||||||
for item, x,y,w,h in self:each_item() do
|
|
||||||
local color = style.text
|
|
||||||
|
|
||||||
-- hovered item background
|
|
||||||
if item == self.hovered_item then
|
|
||||||
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
|
||||||
color = style.accent
|
|
||||||
end
|
|
||||||
|
|
||||||
-- icons
|
|
||||||
local item_depth = 0
|
|
||||||
x = x + (item_depth - root_depth) * style.padding.x + style.padding.x
|
|
||||||
if item.type == "file" then
|
|
||||||
local icon1 = item.expanded and "-" or "+"
|
|
||||||
common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h)
|
|
||||||
x = x + style.padding.x
|
|
||||||
common.draw_text(style.icon_font, color, "f", nil, x, y, 0, h)
|
|
||||||
x = x + icon_width
|
|
||||||
elseif item.type == "group" then
|
|
||||||
local icon1 = item.expanded and "-" or "+"
|
|
||||||
common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h)
|
|
||||||
x = x + icon_width / 2
|
|
||||||
else
|
|
||||||
if config.todo_mode == "tag" then
|
|
||||||
x = x + style.padding.x
|
|
||||||
else
|
|
||||||
x = x + style.padding.x * 1.5
|
|
||||||
end
|
|
||||||
common.draw_text(style.icon_font, color, "i", nil, x, y, 0, h)
|
|
||||||
x = x + icon_width
|
|
||||||
end
|
|
||||||
|
|
||||||
-- text
|
|
||||||
x = x + spacing
|
|
||||||
if item.type == "file" then
|
|
||||||
common.draw_text(style.font, color, item.filename, nil, x, y, 0, h)
|
|
||||||
elseif item.type == "group" then
|
|
||||||
common.draw_text(style.font, color, item.tag, nil, x, y, 0, h)
|
|
||||||
else
|
|
||||||
if config.todo_mode == "file" then
|
|
||||||
common.draw_text(style.font, color, item.tag.." - "..item.text, nil, x, y, 0, h)
|
|
||||||
else
|
|
||||||
common.draw_text(style.font, color, item.text, nil, x, y, 0, h)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- init
|
|
||||||
local view = TodoTreeView()
|
|
||||||
local node = core.root_view:get_active_node()
|
|
||||||
view.size.x = config.treeview_size
|
|
||||||
node:split("right", view, true)
|
|
||||||
|
|
||||||
-- register commands and keymap
|
|
||||||
command.add(nil, {
|
|
||||||
["todotreeview:toggle"] = function()
|
|
||||||
view.visible = not view.visible
|
|
||||||
end,
|
|
||||||
|
|
||||||
["todotreeview:expand-items"] = function()
|
|
||||||
for _, item in pairs(view.items) do
|
|
||||||
item.expanded = true
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
["todotreeview:hide-items"] = function()
|
|
||||||
for _, item in pairs(view.items) do
|
|
||||||
item.expanded = false
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add { ["ctrl+shift+t"] = "todotreeview:toggle" }
|
|
||||||
keymap.add { ["ctrl+shift+e"] = "todotreeview:expand-items" }
|
|
||||||
keymap.add { ["ctrl+shift+h"] = "todotreeview:hide-items" }
|
|
||||||
|
|
|
@ -1,294 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local common = require "core.common"
|
|
||||||
local command = require "core.command"
|
|
||||||
local config = require "core.config"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local style = require "core.style"
|
|
||||||
local View = require "core.view"
|
|
||||||
|
|
||||||
config.treeview_size = 200 * SCALE
|
|
||||||
|
|
||||||
local function get_depth(filename)
|
|
||||||
local n = 0
|
|
||||||
for sep in filename:gmatch("[\\/]") do
|
|
||||||
n = n + 1
|
|
||||||
end
|
|
||||||
return n
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local TreeView = View:extend()
|
|
||||||
|
|
||||||
function TreeView:new()
|
|
||||||
TreeView.super.new(self)
|
|
||||||
self.scrollable = true
|
|
||||||
self.visible = false --< @r-lyeh true>false
|
|
||||||
self.init_size = true
|
|
||||||
self.cache = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:get_cached(item)
|
|
||||||
local t = self.cache[item.filename]
|
|
||||||
if not t then
|
|
||||||
t = {}
|
|
||||||
t.filename = item.filename
|
|
||||||
t.abs_filename = system.absolute_path(item.filename)
|
|
||||||
t.name = t.filename:match("[^\\/]+$")
|
|
||||||
t.depth = get_depth(t.filename)
|
|
||||||
t.type = item.type
|
|
||||||
self.cache[t.filename] = t
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:get_name()
|
|
||||||
return "Project"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:get_item_height()
|
|
||||||
return style.font:get_height() + style.padding.y
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:check_cache()
|
|
||||||
-- invalidate cache's skip values if project_files has changed
|
|
||||||
if core.project_files ~= self.last_project_files then
|
|
||||||
for _, v in pairs(self.cache) do
|
|
||||||
v.skip = nil
|
|
||||||
end
|
|
||||||
self.last_project_files = core.project_files
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:each_item()
|
|
||||||
return coroutine.wrap(function()
|
|
||||||
self:check_cache()
|
|
||||||
local ox, oy = self:get_content_offset()
|
|
||||||
local y = oy + style.padding.y
|
|
||||||
local w = self.size.x
|
|
||||||
local h = self:get_item_height()
|
|
||||||
|
|
||||||
local i = 1
|
|
||||||
while i <= #core.project_files do
|
|
||||||
local item = core.project_files[i]
|
|
||||||
local cached = self:get_cached(item)
|
|
||||||
|
|
||||||
coroutine.yield(cached, ox, y, w, h)
|
|
||||||
y = y + h
|
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
if not cached.expanded then
|
|
||||||
if cached.skip then
|
|
||||||
i = cached.skip
|
|
||||||
else
|
|
||||||
local depth = cached.depth
|
|
||||||
while i <= #core.project_files do
|
|
||||||
local filename = core.project_files[i].filename
|
|
||||||
if get_depth(filename) <= depth then break end
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
cached.skip = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:on_mouse_moved(px, py)
|
|
||||||
self.hovered_item = nil
|
|
||||||
for item, x,y,w,h in self:each_item() do
|
|
||||||
if px > x and py > y and px <= x + w and py <= y + h then
|
|
||||||
self.hovered_item = item
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:on_mouse_pressed(button, x, y)
|
|
||||||
if not self.hovered_item then
|
|
||||||
return
|
|
||||||
elseif self.hovered_item.type == "dir" then
|
|
||||||
self.hovered_item.expanded = not self.hovered_item.expanded
|
|
||||||
else
|
|
||||||
core.try(function()
|
|
||||||
core.root_view:open_doc(core.open_doc(self.hovered_item.filename))
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:update()
|
|
||||||
-- update width
|
|
||||||
local dest = self.visible and config.treeview_size or 0
|
|
||||||
if self.init_size then
|
|
||||||
self.size.x = dest
|
|
||||||
self.init_size = false
|
|
||||||
else
|
|
||||||
self:move_towards(self.size, "x", dest)
|
|
||||||
end
|
|
||||||
|
|
||||||
TreeView.super.update(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:draw()
|
|
||||||
self:draw_background(style.background2)
|
|
||||||
|
|
||||||
local icon_width = style.icon_font:get_width("D")
|
|
||||||
local spacing = style.font:get_width(" ") * 2
|
|
||||||
|
|
||||||
local doc = core.active_view.doc
|
|
||||||
local active_filename = doc and system.absolute_path(doc.filename or "")
|
|
||||||
|
|
||||||
for item, x,y,w,h in self:each_item() do
|
|
||||||
local color = style.text
|
|
||||||
|
|
||||||
-- highlight active_view doc
|
|
||||||
if item.abs_filename == active_filename then
|
|
||||||
color = style.accent
|
|
||||||
end
|
|
||||||
|
|
||||||
-- hovered item background
|
|
||||||
if item == self.hovered_item then
|
|
||||||
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
|
||||||
color = style.accent
|
|
||||||
end
|
|
||||||
|
|
||||||
-- icons
|
|
||||||
x = x + item.depth * style.padding.x + style.padding.x
|
|
||||||
if item.type == "dir" then
|
|
||||||
local icon1 = item.expanded and "-" or "+"
|
|
||||||
local icon2 = item.expanded and "D" or "d"
|
|
||||||
common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h)
|
|
||||||
x = x + style.padding.x
|
|
||||||
common.draw_text(style.icon_font, color, icon2, nil, x, y, 0, h)
|
|
||||||
x = x + icon_width
|
|
||||||
else
|
|
||||||
x = x + style.padding.x
|
|
||||||
common.draw_text(style.icon_font, color, "f", nil, x, y, 0, h)
|
|
||||||
x = x + icon_width
|
|
||||||
end
|
|
||||||
|
|
||||||
-- text
|
|
||||||
x = x + spacing
|
|
||||||
x = common.draw_text(style.font, color, item.name, nil, x, y, 0, h)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- init
|
|
||||||
local view = TreeView()
|
|
||||||
local node = core.root_view:get_active_node()
|
|
||||||
node:split("left", view, true)
|
|
||||||
|
|
||||||
-- register commands and keymap
|
|
||||||
command.add(nil, {
|
|
||||||
["treeview:toggle"] = function()
|
|
||||||
view.visible = not view.visible
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add { ["ctrl+t"] = "treeview:toggle" } --< @r-lyeh ctrl+// > ctrl+t
|
|
||||||
|
|
||||||
-- register some context menu items, if available
|
|
||||||
local has_menu, menu = core.try(require, "plugins.contextmenu")
|
|
||||||
local has_fsutils, fsutils = core.try(require, "plugins.fsutils")
|
|
||||||
|
|
||||||
if has_menu and has_fsutils then
|
|
||||||
local function new_file_f(path)
|
|
||||||
command.perform "core:new-doc"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function new_file()
|
|
||||||
new_file_f(view.hovered_item.abs_filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function new_dir_f(path)
|
|
||||||
core.command_view:enter("New directory name", function(dir)
|
|
||||||
fsutils.mkdir(dir)
|
|
||||||
end)
|
|
||||||
core.command_view:set_text(path .. PATHSEP .. "New folder")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function new_dir()
|
|
||||||
new_dir_f(view.hovered_item.abs_filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function delete_f(path)
|
|
||||||
core.add_thread(function()
|
|
||||||
local function wrap()
|
|
||||||
return coroutine.wrap(function() fsutils.delete(path, true) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
for n in wrap() do
|
|
||||||
if n % 100 == 0 then
|
|
||||||
core.log("Deleted %d items.", n)
|
|
||||||
coroutine.yield(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
core.log("%q deleted.", path)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function delete()
|
|
||||||
local path = view.hovered_item.abs_filename
|
|
||||||
if view.hovered_item.type == "dir"
|
|
||||||
and system.show_confirm_dialog("Delete confirmation", string.format("Do you really want to delete %q ?", path)) then
|
|
||||||
delete_f(path)
|
|
||||||
else
|
|
||||||
delete_f(path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dirname(path)
|
|
||||||
local p = fsutils.split(path)
|
|
||||||
table.remove(p)
|
|
||||||
return table.concat(p, PATHSEP)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function rename()
|
|
||||||
local oldname = view.hovered_item.abs_filename
|
|
||||||
core.command_view:enter("Rename to", function(newname)
|
|
||||||
fsutils.move(oldname, newname)
|
|
||||||
core.log("Moved %q to %q", oldname, newname)
|
|
||||||
end, common.path_suggest)
|
|
||||||
core.command_view:set_text(dirname(oldname))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function copy_path()
|
|
||||||
system.set_clipboard(view.hovered_item.abs_filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
menu:register(function() return view.hovered_item and view.hovered_item.type == "dir" end, {
|
|
||||||
{ text = "New file", command = new_file },
|
|
||||||
{ text = "New folder", command = new_dir },
|
|
||||||
menu.DIVIDER,
|
|
||||||
{ text = "Rename", command = rename },
|
|
||||||
{ text = "Delete", command = delete },
|
|
||||||
menu.DIVIDER,
|
|
||||||
{ text = "Copy directory name", command = copy_path }
|
|
||||||
})
|
|
||||||
menu:register(function() return view.hovered_item and view.hovered_item.type == "file" end, {
|
|
||||||
{ text = "Rename", command = rename },
|
|
||||||
{ text = "Delete", command = delete },
|
|
||||||
menu.DIVIDER,
|
|
||||||
{ text = "Copy filename", command = copy_path }
|
|
||||||
})
|
|
||||||
-- general region of the treeview
|
|
||||||
menu:register(function(x, y)
|
|
||||||
local x1, y1, x2, y2 = view:get_content_bounds()
|
|
||||||
return not view.hovered_item and x > x1 and x <= x2 and y > y1 and y <= y2
|
|
||||||
end, {
|
|
||||||
{ text = "New file", command = function() new_file_f(system.absolute_path('.')) end },
|
|
||||||
{ text = "New folder", command = function() new_dir_f(system.absolute_path('.')) end }
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
return view --< @r-lyeh
|
|
|
@ -1,36 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local Doc = require "core.doc"
|
|
||||||
|
|
||||||
|
|
||||||
local function trim_trailing_whitespace(doc)
|
|
||||||
local cline, ccol = doc:get_selection()
|
|
||||||
for i = 1, #doc.lines do
|
|
||||||
local old_text = doc:get_text(i, 1, i, math.huge)
|
|
||||||
local new_text = old_text:gsub("%s*$", "")
|
|
||||||
|
|
||||||
-- don't remove whitespace which would cause the caret to reposition
|
|
||||||
if cline == i and ccol > #new_text then
|
|
||||||
new_text = old_text:sub(1, ccol - 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if old_text ~= new_text then
|
|
||||||
doc:insert(i, 1, new_text)
|
|
||||||
doc:remove(i, #new_text + 1, i, math.huge)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
|
||||||
["trim-whitespace:trim-trailing-whitespace"] = function()
|
|
||||||
trim_trailing_whitespace(core.active_view.doc)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
local save = Doc.save
|
|
||||||
Doc.save = function(self, ...)
|
|
||||||
trim_trailing_whitespace(self)
|
|
||||||
save(self, ...)
|
|
||||||
end
|
|
|
@ -1,179 +0,0 @@
|
||||||
local core = require "core"
|
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
local workspace_filename = ".lite_workspace.lua"
|
|
||||||
|
|
||||||
|
|
||||||
local function serialize(val)
|
|
||||||
if type(val) == "string" then
|
|
||||||
return string.format("%q", val)
|
|
||||||
elseif type(val) == "table" then
|
|
||||||
local t = {}
|
|
||||||
for k, v in pairs(val) do
|
|
||||||
table.insert(t, "[" .. serialize(k) .. "]=" .. serialize(v))
|
|
||||||
end
|
|
||||||
return "{" .. table.concat(t, ",") .. "}"
|
|
||||||
end
|
|
||||||
return tostring(val)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function has_no_locked_children(node)
|
|
||||||
if node.locked then return false end
|
|
||||||
if node.type == "leaf" then return true end
|
|
||||||
return has_no_locked_children(node.a) and has_no_locked_children(node.b)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function get_unlocked_root(node)
|
|
||||||
if node.type == "leaf" then
|
|
||||||
return not node.locked and node
|
|
||||||
end
|
|
||||||
if has_no_locked_children(node) then
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
return get_unlocked_root(node.a) or get_unlocked_root(node.b)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function save_view(view)
|
|
||||||
local mt = getmetatable(view)
|
|
||||||
if mt == DocView then
|
|
||||||
return {
|
|
||||||
type = "doc",
|
|
||||||
active = (core.active_view == view),
|
|
||||||
filename = view.doc.filename,
|
|
||||||
selection = { view.doc:get_selection() },
|
|
||||||
scroll = { x = view.scroll.to.x, y = view.scroll.to.y },
|
|
||||||
text = not view.doc.filename and view.doc:get_text(1, 1, math.huge, math.huge)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
for name, mod in pairs(package.loaded) do
|
|
||||||
if mod == mt then
|
|
||||||
return {
|
|
||||||
type = "view",
|
|
||||||
active = (core.active_view == view),
|
|
||||||
module = name
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function load_view(t)
|
|
||||||
if t.type == "doc" then
|
|
||||||
local ok, doc = pcall(core.open_doc, t.filename)
|
|
||||||
if not ok then
|
|
||||||
return DocView(core.open_doc())
|
|
||||||
end
|
|
||||||
local dv = DocView(doc)
|
|
||||||
if t.text then doc:insert(1, 1, t.text) end
|
|
||||||
doc:set_selection(table.unpack(t.selection))
|
|
||||||
dv.last_line, dv.last_col = doc:get_selection()
|
|
||||||
dv.scroll.x, dv.scroll.to.x = t.scroll.x, t.scroll.x
|
|
||||||
dv.scroll.y, dv.scroll.to.y = t.scroll.y, t.scroll.y
|
|
||||||
return dv
|
|
||||||
end
|
|
||||||
return require(t.module)()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function save_node(node)
|
|
||||||
local res = {}
|
|
||||||
res.type = node.type
|
|
||||||
if node.type == "leaf" then
|
|
||||||
res.views = {}
|
|
||||||
for _, view in ipairs(node.views) do
|
|
||||||
local t = save_view(view)
|
|
||||||
if t then
|
|
||||||
table.insert(res.views, t)
|
|
||||||
if node.active_view == view then
|
|
||||||
res.active_view = #res.views
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
res.divider = node.divider
|
|
||||||
res.a = save_node(node.a)
|
|
||||||
res.b = save_node(node.b)
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function load_node(node, t)
|
|
||||||
if t.type == "leaf" then
|
|
||||||
local res
|
|
||||||
for _, v in ipairs(t.views) do
|
|
||||||
local view = load_view(v)
|
|
||||||
if v.active then res = view end
|
|
||||||
node:add_view(view)
|
|
||||||
end
|
|
||||||
if t.active_view then
|
|
||||||
node:set_active_view(node.views[t.active_view])
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
else
|
|
||||||
node:split(t.type == "hsplit" and "right" or "down")
|
|
||||||
node.divider = t.divider
|
|
||||||
local res1 = load_node(node.a, t.a)
|
|
||||||
local res2 = load_node(node.b, t.b)
|
|
||||||
return res1 or res2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function save_workspace()
|
|
||||||
local root = get_unlocked_root(core.root_view.root_node)
|
|
||||||
local fp = io.open(workspace_filename, "w")
|
|
||||||
if fp then
|
|
||||||
fp:write("return ", serialize(save_node(root)), "\n")
|
|
||||||
fp:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function load_workspace()
|
|
||||||
local ok, t = pcall(dofile, workspace_filename)
|
|
||||||
os.remove(workspace_filename)
|
|
||||||
if ok then
|
|
||||||
local root = get_unlocked_root(core.root_view.root_node)
|
|
||||||
local active_view = load_node(root, t)
|
|
||||||
if active_view then
|
|
||||||
core.set_active_view(active_view)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local run = core.run
|
|
||||||
function core.run(...)
|
|
||||||
if #core.docs == 0 then
|
|
||||||
core.try(load_workspace)
|
|
||||||
|
|
||||||
local exit = os.exit
|
|
||||||
function os.exit(...)
|
|
||||||
save_workspace()
|
|
||||||
exit(...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
core.run = run
|
|
||||||
return core.run(...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local run1 = core.run1
|
|
||||||
function core.run1(...)
|
|
||||||
if #core.docs == 0 then
|
|
||||||
core.try(load_workspace)
|
|
||||||
|
|
||||||
local exit = os.exit
|
|
||||||
function os.exit(...)
|
|
||||||
save_workspace()
|
|
||||||
exit(...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
core.run1 = run1
|
|
||||||
return core.run1(...)
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +1,11 @@
|
||||||
uniform samplerCube u_cubemap;
|
uniform samplerCube u_cubemap;
|
||||||
|
|
||||||
|
|
||||||
in vec3 v_direction;
|
in vec3 v_direction;
|
||||||
out vec4 fragcolor;
|
out vec4 fragcolor;
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
fragcolor = vec4(texture(u_cubemap, v_direction).rgb, 1.0);
|
fragcolor = vec4(texture(u_cubemap, v_direction).rgb, 1.0);
|
||||||
fragcolor.rgb = pow(fragcolor.rgb, vec3(1.0/2.2));
|
fragcolor.rgb = pow(fragcolor.rgb, vec3(1.0/2.2));
|
||||||
}
|
}
|
|
@ -1,154 +1,154 @@
|
||||||
uniform vec3 uSunPos;
|
uniform vec3 uSunPos;
|
||||||
uniform vec3 uRayOrigin;
|
uniform vec3 uRayOrigin;
|
||||||
uniform float uSunIntensity;
|
uniform float uSunIntensity;
|
||||||
uniform float uPlanetRadius;
|
uniform float uPlanetRadius;
|
||||||
uniform float uAtmosphereRadius;
|
uniform float uAtmosphereRadius;
|
||||||
uniform vec3 uRayleighScattering;
|
uniform vec3 uRayleighScattering;
|
||||||
uniform float uMieScattering;
|
uniform float uMieScattering;
|
||||||
uniform float uRayleighScaleHeight;
|
uniform float uRayleighScaleHeight;
|
||||||
uniform float uMieScaleHeight;
|
uniform float uMieScaleHeight;
|
||||||
uniform float uMiePreferredDirection;
|
uniform float uMiePreferredDirection;
|
||||||
|
|
||||||
|
|
||||||
in vec3 v_direction;
|
in vec3 v_direction;
|
||||||
out vec4 fragcolor;
|
out vec4 fragcolor;
|
||||||
|
|
||||||
|
|
||||||
vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g);
|
vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g);
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 color = atmosphere(
|
vec3 color = atmosphere(
|
||||||
normalize(v_direction), // normalized ray direction
|
normalize(v_direction), // normalized ray direction
|
||||||
uRayOrigin, // ray origin
|
uRayOrigin, // ray origin
|
||||||
uSunPos, // position of the sun
|
uSunPos, // position of the sun
|
||||||
uSunIntensity, // intensity of the sun
|
uSunIntensity, // intensity of the sun
|
||||||
uPlanetRadius, // radius of the planet in meters
|
uPlanetRadius, // radius of the planet in meters
|
||||||
uAtmosphereRadius, // radius of the atmosphere in meters
|
uAtmosphereRadius, // radius of the atmosphere in meters
|
||||||
uRayleighScattering, // Rayleigh scattering coefficient
|
uRayleighScattering, // Rayleigh scattering coefficient
|
||||||
uMieScattering, // Mie scattering coefficient
|
uMieScattering, // Mie scattering coefficient
|
||||||
uRayleighScaleHeight, // Rayleigh scale height
|
uRayleighScaleHeight, // Rayleigh scale height
|
||||||
uMieScaleHeight, // Mie scale height
|
uMieScaleHeight, // Mie scale height
|
||||||
uMiePreferredDirection // Mie preferred scattering direction
|
uMiePreferredDirection // Mie preferred scattering direction
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply exposure.
|
// Apply exposure.
|
||||||
color = 1.0 - exp(-1.0 * color);
|
color = 1.0 - exp(-1.0 * color);
|
||||||
|
|
||||||
fragcolor = vec4(color, 1);
|
fragcolor = vec4(color, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// [src] https://github.com/wwwtyro/glsl-atmosphere by wwwtyro (Unlicensed)
|
// [src] https://github.com/wwwtyro/glsl-atmosphere by wwwtyro (Unlicensed)
|
||||||
// For more information, please refer to <http://unlicense.org>
|
// For more information, please refer to <http://unlicense.org>
|
||||||
|
|
||||||
|
|
||||||
#define PI 3.141592
|
#define PI 3.141592
|
||||||
#define iSteps 16
|
#define iSteps 16
|
||||||
#define jSteps 8
|
#define jSteps 8
|
||||||
|
|
||||||
|
|
||||||
vec2 rsi(vec3 r0, vec3 rd, float sr) {
|
vec2 rsi(vec3 r0, vec3 rd, float sr) {
|
||||||
// ray-sphere intersection that assumes
|
// ray-sphere intersection that assumes
|
||||||
// the sphere is centered at the origin.
|
// the sphere is centered at the origin.
|
||||||
// No intersection when result.x > result.y
|
// No intersection when result.x > result.y
|
||||||
float a = dot(rd, rd);
|
float a = dot(rd, rd);
|
||||||
float b = 2.0 * dot(rd, r0);
|
float b = 2.0 * dot(rd, r0);
|
||||||
float c = dot(r0, r0) - (sr * sr);
|
float c = dot(r0, r0) - (sr * sr);
|
||||||
float d = (b*b) - 4.0*a*c;
|
float d = (b*b) - 4.0*a*c;
|
||||||
if (d < 0.0) return vec2(1e5,-1e5);
|
if (d < 0.0) return vec2(1e5,-1e5);
|
||||||
return vec2(
|
return vec2(
|
||||||
(-b - sqrt(d))/(2.0*a),
|
(-b - sqrt(d))/(2.0*a),
|
||||||
(-b + sqrt(d))/(2.0*a)
|
(-b + sqrt(d))/(2.0*a)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) {
|
vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) {
|
||||||
// Normalize the sun and view directions.
|
// Normalize the sun and view directions.
|
||||||
pSun = normalize(pSun);
|
pSun = normalize(pSun);
|
||||||
r = normalize(r);
|
r = normalize(r);
|
||||||
|
|
||||||
// Calculate the step size of the primary ray.
|
// Calculate the step size of the primary ray.
|
||||||
vec2 p = rsi(r0, r, rAtmos);
|
vec2 p = rsi(r0, r, rAtmos);
|
||||||
if (p.x > p.y) return vec3(0,0,0);
|
if (p.x > p.y) return vec3(0,0,0);
|
||||||
p.y = min(p.y, rsi(r0, r, rPlanet).x);
|
p.y = min(p.y, rsi(r0, r, rPlanet).x);
|
||||||
float iStepSize = (p.y - p.x) / float(iSteps);
|
float iStepSize = (p.y - p.x) / float(iSteps);
|
||||||
|
|
||||||
// Initialize the primary ray time.
|
// Initialize the primary ray time.
|
||||||
float iTime = 0.0;
|
float iTime = 0.0;
|
||||||
|
|
||||||
// Initialize accumulators for Rayleigh and Mie scattering.
|
// Initialize accumulators for Rayleigh and Mie scattering.
|
||||||
vec3 totalRlh = vec3(0,0,0);
|
vec3 totalRlh = vec3(0,0,0);
|
||||||
vec3 totalMie = vec3(0,0,0);
|
vec3 totalMie = vec3(0,0,0);
|
||||||
|
|
||||||
// Initialize optical depth accumulators for the primary ray.
|
// Initialize optical depth accumulators for the primary ray.
|
||||||
float iOdRlh = 0.0;
|
float iOdRlh = 0.0;
|
||||||
float iOdMie = 0.0;
|
float iOdMie = 0.0;
|
||||||
|
|
||||||
// Calculate the Rayleigh and Mie phases.
|
// Calculate the Rayleigh and Mie phases.
|
||||||
float mu = dot(r, pSun);
|
float mu = dot(r, pSun);
|
||||||
float mumu = mu * mu;
|
float mumu = mu * mu;
|
||||||
float gg = g * g;
|
float gg = g * g;
|
||||||
float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
|
float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
|
||||||
float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg));
|
float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg));
|
||||||
|
|
||||||
// Sample the primary ray.
|
// Sample the primary ray.
|
||||||
for (int i = 0; i < iSteps; i++) {
|
for (int i = 0; i < iSteps; i++) {
|
||||||
|
|
||||||
// Calculate the primary ray sample position.
|
// Calculate the primary ray sample position.
|
||||||
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
|
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
|
||||||
|
|
||||||
// Calculate the height of the sample.
|
// Calculate the height of the sample.
|
||||||
float iHeight = length(iPos) - rPlanet;
|
float iHeight = length(iPos) - rPlanet;
|
||||||
|
|
||||||
// Calculate the optical depth of the Rayleigh and Mie scattering for this step.
|
// Calculate the optical depth of the Rayleigh and Mie scattering for this step.
|
||||||
float odStepRlh = exp(-iHeight / shRlh) * iStepSize;
|
float odStepRlh = exp(-iHeight / shRlh) * iStepSize;
|
||||||
float odStepMie = exp(-iHeight / shMie) * iStepSize;
|
float odStepMie = exp(-iHeight / shMie) * iStepSize;
|
||||||
|
|
||||||
// Accumulate optical depth.
|
// Accumulate optical depth.
|
||||||
iOdRlh += odStepRlh;
|
iOdRlh += odStepRlh;
|
||||||
iOdMie += odStepMie;
|
iOdMie += odStepMie;
|
||||||
|
|
||||||
// Calculate the step size of the secondary ray.
|
// Calculate the step size of the secondary ray.
|
||||||
float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps);
|
float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps);
|
||||||
|
|
||||||
// Initialize the secondary ray time.
|
// Initialize the secondary ray time.
|
||||||
float jTime = 0.0;
|
float jTime = 0.0;
|
||||||
|
|
||||||
// Initialize optical depth accumulators for the secondary ray.
|
// Initialize optical depth accumulators for the secondary ray.
|
||||||
float jOdRlh = 0.0;
|
float jOdRlh = 0.0;
|
||||||
float jOdMie = 0.0;
|
float jOdMie = 0.0;
|
||||||
|
|
||||||
// Sample the secondary ray.
|
// Sample the secondary ray.
|
||||||
for (int j = 0; j < jSteps; j++) {
|
for (int j = 0; j < jSteps; j++) {
|
||||||
|
|
||||||
// Calculate the secondary ray sample position.
|
// Calculate the secondary ray sample position.
|
||||||
vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5);
|
vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5);
|
||||||
|
|
||||||
// Calculate the height of the sample.
|
// Calculate the height of the sample.
|
||||||
float jHeight = length(jPos) - rPlanet;
|
float jHeight = length(jPos) - rPlanet;
|
||||||
|
|
||||||
// Accumulate the optical depth.
|
// Accumulate the optical depth.
|
||||||
jOdRlh += exp(-jHeight / shRlh) * jStepSize;
|
jOdRlh += exp(-jHeight / shRlh) * jStepSize;
|
||||||
jOdMie += exp(-jHeight / shMie) * jStepSize;
|
jOdMie += exp(-jHeight / shMie) * jStepSize;
|
||||||
|
|
||||||
// Increment the secondary ray time.
|
// Increment the secondary ray time.
|
||||||
jTime += jStepSize;
|
jTime += jStepSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate attenuation.
|
// Calculate attenuation.
|
||||||
vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh)));
|
vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh)));
|
||||||
|
|
||||||
// Accumulate scattering.
|
// Accumulate scattering.
|
||||||
totalRlh += odStepRlh * attn;
|
totalRlh += odStepRlh * attn;
|
||||||
totalMie += odStepMie * attn;
|
totalMie += odStepMie * attn;
|
||||||
|
|
||||||
// Increment the primary ray time.
|
// Increment the primary ray time.
|
||||||
iTime += iStepSize;
|
iTime += iStepSize;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate and return the final color.
|
// Calculate and return the final color.
|
||||||
return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie);
|
return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie);
|
||||||
}
|
}
|
|
@ -1,485 +1,485 @@
|
||||||
#version 400
|
#version 400
|
||||||
|
|
||||||
// original PBR shader by @seece (Public Domain). link: https://github.com/Gargaj/Foxotron/pull/12
|
// original PBR shader by @seece (Public Domain). link: https://github.com/Gargaj/Foxotron/pull/12
|
||||||
|
|
||||||
//#define textureQueryLod(t,c) vec2(0.0,0.0) // #version 400 required
|
//#define textureQueryLod(t,c) vec2(0.0,0.0) // #version 400 required
|
||||||
|
|
||||||
uniform vec2 resolution = vec2(640.0,480.0); // debug options below use this (USE_MAP_DEBUGGING, USE_AMBIENT_DEBUGGING)
|
uniform vec2 resolution = vec2(640.0,480.0); // debug options below use this (USE_MAP_DEBUGGING, USE_AMBIENT_DEBUGGING)
|
||||||
|
|
||||||
#define USE_BRUTEFORCE_IRRADIANCE false // Samples irradiance from tex_skysphere when enabled.
|
#define USE_BRUTEFORCE_IRRADIANCE false // Samples irradiance from tex_skysphere when enabled.
|
||||||
#define USE_WRAPAROUND_SPECULAR true // Makes silhouettes more reflective to avoid black pixels.
|
#define USE_WRAPAROUND_SPECULAR true // Makes silhouettes more reflective to avoid black pixels.
|
||||||
#define USE_SPECULAR_AO_ATTENUATION true // Dampens IBL specular ambient with AO if enabled.
|
#define USE_SPECULAR_AO_ATTENUATION true // Dampens IBL specular ambient with AO if enabled.
|
||||||
#define USE_NORMAL_VARIATION_TO_ROUGHNESS true // Increases roughness if normal map has variation and was minified.
|
#define USE_NORMAL_VARIATION_TO_ROUGHNESS true // Increases roughness if normal map has variation and was minified.
|
||||||
#define USE_MAP_DEBUGGING false // Shows all ColorMaps as horizontal bars
|
#define USE_MAP_DEBUGGING false // Shows all ColorMaps as horizontal bars
|
||||||
#define USE_AMBIENT_DEBUGGING false // Splits the screen in two and shows image-based specular (left), full shading (middle), diffuse shading (right).
|
#define USE_AMBIENT_DEBUGGING false // Splits the screen in two and shows image-based specular (left), full shading (middle), diffuse shading (right).
|
||||||
#define BOOST_LIGHTING 2.00f // Multiplies analytic light's color with this constant because otherwise they look really pathetic.
|
#define BOOST_LIGHTING 2.00f // Multiplies analytic light's color with this constant because otherwise they look really pathetic.
|
||||||
#define BOOST_SPECULAR 1.50f
|
#define BOOST_SPECULAR 1.50f
|
||||||
#define BOOST_NOISE 2.50f
|
#define BOOST_NOISE 2.50f
|
||||||
|
|
||||||
struct Light
|
struct Light
|
||||||
{
|
{
|
||||||
vec3 direction;
|
vec3 direction;
|
||||||
vec3 color;
|
vec3 color;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ColorMap
|
struct ColorMap
|
||||||
{
|
{
|
||||||
bool has_tex;
|
bool has_tex;
|
||||||
vec4 color;
|
vec4 color;
|
||||||
};
|
};
|
||||||
|
|
||||||
uniform ColorMap map_albedo; uniform sampler2D map_albedo_tex;
|
uniform ColorMap map_albedo; uniform sampler2D map_albedo_tex;
|
||||||
uniform ColorMap map_diffuse; uniform sampler2D map_diffuse_tex;
|
uniform ColorMap map_diffuse; uniform sampler2D map_diffuse_tex;
|
||||||
uniform ColorMap map_specular; uniform sampler2D map_specular_tex; // not used
|
uniform ColorMap map_specular; uniform sampler2D map_specular_tex; // not used
|
||||||
uniform ColorMap map_normals; uniform sampler2D map_normals_tex;
|
uniform ColorMap map_normals; uniform sampler2D map_normals_tex;
|
||||||
uniform ColorMap map_roughness; uniform sampler2D map_roughness_tex;
|
uniform ColorMap map_roughness; uniform sampler2D map_roughness_tex;
|
||||||
uniform ColorMap map_metallic; uniform sampler2D map_metallic_tex;
|
uniform ColorMap map_metallic; uniform sampler2D map_metallic_tex;
|
||||||
uniform ColorMap map_ao; uniform sampler2D map_ao_tex;
|
uniform ColorMap map_ao; uniform sampler2D map_ao_tex;
|
||||||
uniform ColorMap map_ambient; uniform sampler2D map_ambient_tex;
|
uniform ColorMap map_ambient; uniform sampler2D map_ambient_tex;
|
||||||
uniform ColorMap map_emissive; uniform sampler2D map_emissive_tex;
|
uniform ColorMap map_emissive; uniform sampler2D map_emissive_tex;
|
||||||
|
|
||||||
#define sample_colormap(ColorMap_, uv_) \
|
#define sample_colormap(ColorMap_, uv_) \
|
||||||
(ColorMap_.has_tex ? texture( ColorMap_##_tex, uv_ ) : ColorMap_.color)
|
(ColorMap_.has_tex ? texture( ColorMap_##_tex, uv_ ) : ColorMap_.color)
|
||||||
|
|
||||||
in vec3 out_normal;
|
in vec3 out_normal;
|
||||||
in vec3 out_tangent;
|
in vec3 out_tangent;
|
||||||
in vec3 out_binormal;
|
in vec3 out_binormal;
|
||||||
in vec2 out_texcoord;
|
in vec2 out_texcoord;
|
||||||
in vec3 out_worldpos;
|
in vec3 out_worldpos;
|
||||||
in vec3 out_to_camera;
|
in vec3 out_to_camera;
|
||||||
|
|
||||||
uniform float skysphere_rotation;
|
uniform float skysphere_rotation;
|
||||||
uniform float skysphere_mip_count;
|
uniform float skysphere_mip_count;
|
||||||
uniform float exposure;
|
uniform float exposure;
|
||||||
uniform uint frame_count;
|
uniform uint frame_count;
|
||||||
uniform float specular_shininess;
|
uniform float specular_shininess;
|
||||||
|
|
||||||
uniform vec3 camera_position;
|
uniform vec3 camera_position;
|
||||||
uniform Light lights[3];
|
uniform Light lights[3];
|
||||||
|
|
||||||
uniform sampler2D tex_skysphere;
|
uniform sampler2D tex_skysphere;
|
||||||
uniform sampler2D tex_skyenv;
|
uniform sampler2D tex_skyenv;
|
||||||
uniform sampler2D tex_brdf_lut;
|
uniform sampler2D tex_brdf_lut;
|
||||||
|
|
||||||
uniform bool has_tex_skysphere;
|
uniform bool has_tex_skysphere;
|
||||||
uniform bool has_tex_skyenv;
|
uniform bool has_tex_skyenv;
|
||||||
|
|
||||||
out vec4 frag_color;
|
out vec4 frag_color;
|
||||||
|
|
||||||
const float PI = 3.1415926536;
|
const float PI = 3.1415926536;
|
||||||
|
|
||||||
// MurMurHash 3 finalizer. Implementation is in public domain.
|
// MurMurHash 3 finalizer. Implementation is in public domain.
|
||||||
uint hash( uint h )
|
uint hash( uint h )
|
||||||
{
|
{
|
||||||
h ^= h >> 16;
|
h ^= h >> 16;
|
||||||
h *= 0x85ebca6bU;
|
h *= 0x85ebca6bU;
|
||||||
h ^= h >> 13;
|
h ^= h >> 13;
|
||||||
h *= 0xc2b2ae35U;
|
h *= 0xc2b2ae35U;
|
||||||
h ^= h >> 16;
|
h ^= h >> 16;
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Random function using the idea of StackOverflow user "Spatial" https://stackoverflow.com/a/17479300
|
// Random function using the idea of StackOverflow user "Spatial" https://stackoverflow.com/a/17479300
|
||||||
// Creates random 23 bits and puts them into the fraction bits of an 32-bit float.
|
// Creates random 23 bits and puts them into the fraction bits of an 32-bit float.
|
||||||
float random( uvec3 h )
|
float random( uvec3 h )
|
||||||
{
|
{
|
||||||
uint m = hash(h.x ^ hash( h.y ) ^ hash( h.z ));
|
uint m = hash(h.x ^ hash( h.y ) ^ hash( h.z ));
|
||||||
return uintBitsToFloat( ( m & 0x007FFFFFu ) | 0x3f800000u ) - 1.;
|
return uintBitsToFloat( ( m & 0x007FFFFFu ) | 0x3f800000u ) - 1.;
|
||||||
}
|
}
|
||||||
|
|
||||||
float random( vec3 v )
|
float random( vec3 v )
|
||||||
{
|
{
|
||||||
return random(floatBitsToUint( v ));
|
return random(floatBitsToUint( v ));
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 fresnel_schlick( vec3 H, vec3 V, vec3 F0 )
|
vec3 fresnel_schlick( vec3 H, vec3 V, vec3 F0 )
|
||||||
{
|
{
|
||||||
float cosTheta = clamp( dot( H, V ), 0., 1. );
|
float cosTheta = clamp( dot( H, V ), 0., 1. );
|
||||||
return F0 + ( vec3( 1.0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
|
return F0 + ( vec3( 1.0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Fresnel term that dampens rough specular reflections.
|
// A Fresnel term that dampens rough specular reflections.
|
||||||
// https://seblagarde.wordpress.com/2011/08/17/hello-world/
|
// https://seblagarde.wordpress.com/2011/08/17/hello-world/
|
||||||
vec3 fresnel_schlick_roughness( vec3 H, vec3 V, vec3 F0, float roughness )
|
vec3 fresnel_schlick_roughness( vec3 H, vec3 V, vec3 F0, float roughness )
|
||||||
{
|
{
|
||||||
float cosTheta = clamp( dot( H, V ), 0., 1. );
|
float cosTheta = clamp( dot( H, V ), 0., 1. );
|
||||||
return F0 + ( max( vec3( 1.0 - roughness ), F0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
|
return F0 + ( max( vec3( 1.0 - roughness ), F0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
float distribution_ggx( vec3 N, vec3 H, float roughness )
|
float distribution_ggx( vec3 N, vec3 H, float roughness )
|
||||||
{
|
{
|
||||||
float a = roughness * roughness;
|
float a = roughness * roughness;
|
||||||
float a2 = a * a;
|
float a2 = a * a;
|
||||||
float NdotH = max( 0., dot( N, H ) );
|
float NdotH = max( 0., dot( N, H ) );
|
||||||
float factor = NdotH * NdotH * ( a2 - 1. ) + 1.;
|
float factor = NdotH * NdotH * ( a2 - 1. ) + 1.;
|
||||||
|
|
||||||
return a2 / ( PI * factor * factor );
|
return a2 / ( PI * factor * factor );
|
||||||
}
|
}
|
||||||
|
|
||||||
float geometry_schlick_ggx( vec3 N, vec3 V, float k )
|
float geometry_schlick_ggx( vec3 N, vec3 V, float k )
|
||||||
{
|
{
|
||||||
float NdotV = max( 0., dot( N, V ) );
|
float NdotV = max( 0., dot( N, V ) );
|
||||||
return NdotV / (NdotV * ( 1. - k ) + k );
|
return NdotV / (NdotV * ( 1. - k ) + k );
|
||||||
}
|
}
|
||||||
|
|
||||||
float geometry_smith( vec3 N, vec3 V, vec3 L, float roughness )
|
float geometry_smith( vec3 N, vec3 V, vec3 L, float roughness )
|
||||||
{
|
{
|
||||||
#if 1 // original
|
#if 1 // original
|
||||||
float r = roughness + 1.;
|
float r = roughness + 1.;
|
||||||
float k = (r * r) / 8.;
|
float k = (r * r) / 8.;
|
||||||
#elif 0 // vries
|
#elif 0 // vries
|
||||||
float a = roughness;
|
float a = roughness;
|
||||||
float k = (a * a) / 2.0;
|
float k = (a * a) / 2.0;
|
||||||
#elif 0 // vries improved?
|
#elif 0 // vries improved?
|
||||||
float a = roughness * roughness;
|
float a = roughness * roughness;
|
||||||
float k = a / 2.0;
|
float k = a / 2.0;
|
||||||
#endif
|
#endif
|
||||||
return geometry_schlick_ggx( N, V, k ) * geometry_schlick_ggx( N, L, k );
|
return geometry_schlick_ggx( N, V, k ) * geometry_schlick_ggx( N, L, k );
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 sphere_to_polar( vec3 normal )
|
vec2 sphere_to_polar( vec3 normal )
|
||||||
{
|
{
|
||||||
normal = normalize( normal );
|
normal = normalize( normal );
|
||||||
return vec2( ( atan( normal.z, normal.x ) + skysphere_rotation ) / PI / 2.0 + 0.5, acos( normal.y ) / PI );
|
return vec2( ( atan( normal.z, normal.x ) + skysphere_rotation ) / PI / 2.0 + 0.5, acos( normal.y ) / PI );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our vertically GL_CLAMPed textures seem to blend towards black when sampling the half-pixel edge.
|
// Our vertically GL_CLAMPed textures seem to blend towards black when sampling the half-pixel edge.
|
||||||
// Not sure if it has a border, or this if is a driver bug, but can repro on multiple nvidia cards.
|
// Not sure if it has a border, or this if is a driver bug, but can repro on multiple nvidia cards.
|
||||||
// Knowing the texture height we can limit sampling to the centers of the top and bottom pixel rows.
|
// Knowing the texture height we can limit sampling to the centers of the top and bottom pixel rows.
|
||||||
vec2 sphere_to_polar_clamp_y( vec3 normal, float texture_height )
|
vec2 sphere_to_polar_clamp_y( vec3 normal, float texture_height )
|
||||||
{
|
{
|
||||||
normal = normalize( normal );
|
normal = normalize( normal );
|
||||||
return vec2( ( atan( normal.z, normal.x ) + skysphere_rotation ) / PI / 2.0 + 0.5, clamp(acos( normal.y ) / PI, 0.5 / texture_height, 1.0 - 0.5 / texture_height) );
|
return vec2( ( atan( normal.z, normal.x ) + skysphere_rotation ) / PI / 2.0 + 0.5, clamp(acos( normal.y ) / PI, 0.5 / texture_height, 1.0 - 0.5 / texture_height) );
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 sample_sky( vec3 normal )
|
vec3 sample_sky( vec3 normal )
|
||||||
{
|
{
|
||||||
vec2 polar = sphere_to_polar( normal );
|
vec2 polar = sphere_to_polar( normal );
|
||||||
return texture( tex_skysphere, polar ).rgb * exposure;
|
return texture( tex_skysphere, polar ).rgb * exposure;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes samples around the hemisphere, converts them to radiances via weighting and
|
// Takes samples around the hemisphere, converts them to radiances via weighting and
|
||||||
// returns a normalized sum.
|
// returns a normalized sum.
|
||||||
vec3 sample_irradiance_slow( vec3 normal, vec3 vertex_tangent )
|
vec3 sample_irradiance_slow( vec3 normal, vec3 vertex_tangent )
|
||||||
{
|
{
|
||||||
float delta = 0.10;
|
float delta = 0.10;
|
||||||
|
|
||||||
vec3 up = abs( normal.y ) < 0.999 ? vec3( 0., 1., 0. ) : vec3( 0., 0., 1. );
|
vec3 up = abs( normal.y ) < 0.999 ? vec3( 0., 1., 0. ) : vec3( 0., 0., 1. );
|
||||||
vec3 tangent_x = normalize( cross( up, normal ) );
|
vec3 tangent_x = normalize( cross( up, normal ) );
|
||||||
vec3 tangent_y = cross( normal, tangent_x );
|
vec3 tangent_y = cross( normal, tangent_x );
|
||||||
|
|
||||||
int numIrradianceSamples = 0;
|
int numIrradianceSamples = 0;
|
||||||
|
|
||||||
vec3 irradiance = vec3(0.);
|
vec3 irradiance = vec3(0.);
|
||||||
|
|
||||||
for ( float phi = 0.; phi < 2. * PI ; phi += delta )
|
for ( float phi = 0.; phi < 2. * PI ; phi += delta )
|
||||||
{
|
{
|
||||||
for ( float theta = 0.; theta < 0.5 * PI; theta += delta )
|
for ( float theta = 0.; theta < 0.5 * PI; theta += delta )
|
||||||
{
|
{
|
||||||
vec3 tangent_space = vec3(
|
vec3 tangent_space = vec3(
|
||||||
sin( theta ) * cos( phi ),
|
sin( theta ) * cos( phi ),
|
||||||
sin( theta ) * sin( phi ),
|
sin( theta ) * sin( phi ),
|
||||||
cos( theta ) );
|
cos( theta ) );
|
||||||
|
|
||||||
vec3 world_space = tangent_space.x * tangent_x + tangent_space.y + tangent_y + tangent_space.z * normal;
|
vec3 world_space = tangent_space.x * tangent_x + tangent_space.y + tangent_y + tangent_space.z * normal;
|
||||||
|
|
||||||
vec3 color = sample_sky( world_space );
|
vec3 color = sample_sky( world_space );
|
||||||
irradiance += color * cos( theta ) * sin( theta );
|
irradiance += color * cos( theta ) * sin( theta );
|
||||||
numIrradianceSamples++;
|
numIrradianceSamples++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
irradiance = PI * irradiance / float( numIrradianceSamples );
|
irradiance = PI * irradiance / float( numIrradianceSamples );
|
||||||
return irradiance;
|
return irradiance;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 sample_irradiance_fast( vec3 normal, vec3 vertex_tangent )
|
vec3 sample_irradiance_fast( vec3 normal, vec3 vertex_tangent )
|
||||||
{
|
{
|
||||||
// Sample the irradiance map if it exists, otherwise fall back to blurred reflection map.
|
// Sample the irradiance map if it exists, otherwise fall back to blurred reflection map.
|
||||||
if ( has_tex_skyenv )
|
if ( has_tex_skyenv )
|
||||||
{
|
{
|
||||||
vec2 polar = sphere_to_polar_clamp_y( normal, 180.0 );
|
vec2 polar = sphere_to_polar_clamp_y( normal, 180.0 );
|
||||||
return textureLod( tex_skyenv, polar, 0.0 ).rgb * exposure;
|
return textureLod( tex_skyenv, polar, 0.0 ).rgb * exposure;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vec2 polar = sphere_to_polar( normal );
|
vec2 polar = sphere_to_polar( normal );
|
||||||
return textureLod( tex_skysphere, polar, 0.80 * skysphere_mip_count ).rgb * exposure;
|
return textureLod( tex_skysphere, polar, 0.80 * skysphere_mip_count ).rgb * exposure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vec3 specular_ibl( vec3 V, vec3 N, float roughness, vec3 fresnel )
|
vec3 specular_ibl( vec3 V, vec3 N, float roughness, vec3 fresnel )
|
||||||
{
|
{
|
||||||
// What we'd like to do here is take a LOT of skybox samples around the reflection
|
// What we'd like to do here is take a LOT of skybox samples around the reflection
|
||||||
// vector R according to the BRDF lobe.
|
// vector R according to the BRDF lobe.
|
||||||
//
|
//
|
||||||
// Unfortunately it's not possible in real time so we use the following UE4 style approximations:
|
// Unfortunately it's not possible in real time so we use the following UE4 style approximations:
|
||||||
// 1. Integrate incoming light and BRDF separately ("split sum approximation")
|
// 1. Integrate incoming light and BRDF separately ("split sum approximation")
|
||||||
// 2. Assume V = R = N so that we can just blur the skybox and sample that.
|
// 2. Assume V = R = N so that we can just blur the skybox and sample that.
|
||||||
// 3. Bake the BRDF integral into a lookup texture so that it can be computed in constant time.
|
// 3. Bake the BRDF integral into a lookup texture so that it can be computed in constant time.
|
||||||
//
|
//
|
||||||
// Here we also simplify approximation #2 by using bilinear mipmaps with a magic formula instead
|
// Here we also simplify approximation #2 by using bilinear mipmaps with a magic formula instead
|
||||||
// of properly convolving it with a GGX lobe.
|
// of properly convolving it with a GGX lobe.
|
||||||
//
|
//
|
||||||
// For details, see Brian Karis, "Real Shading in Unreal Engine 4", 2013.
|
// For details, see Brian Karis, "Real Shading in Unreal Engine 4", 2013.
|
||||||
|
|
||||||
vec3 R = 2. * dot( V, N ) * N - V;
|
vec3 R = 2. * dot( V, N ) * N - V;
|
||||||
|
|
||||||
vec2 polar = sphere_to_polar( R );
|
vec2 polar = sphere_to_polar( R );
|
||||||
|
|
||||||
// Map roughness from range [0, 1] into a mip LOD [0, skysphere_mip_count].
|
// Map roughness from range [0, 1] into a mip LOD [0, skysphere_mip_count].
|
||||||
// The magic numbers were chosen empirically.
|
// The magic numbers were chosen empirically.
|
||||||
|
|
||||||
float mip = 0.9 * skysphere_mip_count * pow(roughness, 0.25 * BOOST_SPECULAR);
|
float mip = 0.9 * skysphere_mip_count * pow(roughness, 0.25 * BOOST_SPECULAR);
|
||||||
|
|
||||||
vec3 prefiltered = textureLod( tex_skysphere, polar, mip ).rgb * exposure;
|
vec3 prefiltered = textureLod( tex_skysphere, polar, mip ).rgb * exposure;
|
||||||
|
|
||||||
float NdotV = dot( N, V );
|
float NdotV = dot( N, V );
|
||||||
|
|
||||||
// dot( N, V ) seems to produce negative values so we can try to stretch it a bit behind the silhouette
|
// dot( N, V ) seems to produce negative values so we can try to stretch it a bit behind the silhouette
|
||||||
// to avoid black pixels.
|
// to avoid black pixels.
|
||||||
if (USE_WRAPAROUND_SPECULAR)
|
if (USE_WRAPAROUND_SPECULAR)
|
||||||
{
|
{
|
||||||
NdotV = NdotV * 0.9 + 0.1;
|
NdotV = NdotV * 0.9 + 0.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
NdotV = min(0.99, max(0.01, NdotV));
|
NdotV = min(0.99, max(0.01, NdotV));
|
||||||
|
|
||||||
// A precomputed lookup table contains a scale and a bias term for specular intensity (called "fresnel" here).
|
// A precomputed lookup table contains a scale and a bias term for specular intensity (called "fresnel" here).
|
||||||
// See equation (8) in Karis' course notes mentioned above.
|
// See equation (8) in Karis' course notes mentioned above.
|
||||||
vec2 envBRDF = texture( tex_brdf_lut, vec2(NdotV, 1.0-roughness) ).xy; // (NdotV,1-roughtness) for green top-left (NdotV,roughness) for green bottom-left
|
vec2 envBRDF = texture( tex_brdf_lut, vec2(NdotV, 1.0-roughness) ).xy; // (NdotV,1-roughtness) for green top-left (NdotV,roughness) for green bottom-left
|
||||||
vec3 specular = prefiltered * (fresnel * envBRDF.x + vec3(envBRDF.y));
|
vec3 specular = prefiltered * (fresnel * envBRDF.x + vec3(envBRDF.y));
|
||||||
|
|
||||||
return specular;
|
return specular;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
vec3 baseColor = vec3( 0.5, 0.5, 0.5 );
|
vec3 baseColor = vec3( 0.5, 0.5, 0.5 );
|
||||||
float roughness = 1.0;
|
float roughness = 1.0;
|
||||||
float metallic = 0.0;
|
float metallic = 0.0;
|
||||||
float ao = 1.0;
|
float ao = 1.0;
|
||||||
float alpha = 1.0;
|
float alpha = 1.0;
|
||||||
|
|
||||||
vec4 baseColor_alpha;
|
vec4 baseColor_alpha;
|
||||||
if ( map_albedo.has_tex )
|
if ( map_albedo.has_tex )
|
||||||
baseColor_alpha = sample_colormap( map_albedo, out_texcoord );
|
baseColor_alpha = sample_colormap( map_albedo, out_texcoord );
|
||||||
else
|
else
|
||||||
baseColor_alpha = sample_colormap( map_diffuse, out_texcoord );
|
baseColor_alpha = sample_colormap( map_diffuse, out_texcoord );
|
||||||
baseColor = baseColor_alpha.xyz;
|
baseColor = baseColor_alpha.xyz;
|
||||||
alpha = baseColor_alpha.w;
|
alpha = baseColor_alpha.w;
|
||||||
|
|
||||||
if( map_metallic.has_tex && map_roughness.has_tex ) {
|
if( map_metallic.has_tex && map_roughness.has_tex ) {
|
||||||
metallic = sample_colormap( map_metallic, out_texcoord ).x;
|
metallic = sample_colormap( map_metallic, out_texcoord ).x;
|
||||||
roughness = sample_colormap( map_roughness, out_texcoord ).x;
|
roughness = sample_colormap( map_roughness, out_texcoord ).x;
|
||||||
}
|
}
|
||||||
else if( map_roughness.has_tex ) {
|
else if( map_roughness.has_tex ) {
|
||||||
//< @r-lyeh, metalness B, roughness G, (@todo: self-shadowing occlusion R; for now, any of R/B are metallic)
|
//< @r-lyeh, metalness B, roughness G, (@todo: self-shadowing occlusion R; for now, any of R/B are metallic)
|
||||||
metallic = sample_colormap( map_roughness, out_texcoord ).b + sample_colormap( map_roughness, out_texcoord ).r;
|
metallic = sample_colormap( map_roughness, out_texcoord ).b + sample_colormap( map_roughness, out_texcoord ).r;
|
||||||
roughness = sample_colormap( map_roughness, out_texcoord ).g;
|
roughness = sample_colormap( map_roughness, out_texcoord ).g;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( map_ao.has_tex )
|
if ( map_ao.has_tex )
|
||||||
ao = sample_colormap( map_ao, out_texcoord ).x;
|
ao = sample_colormap( map_ao, out_texcoord ).x;
|
||||||
else if ( map_ambient.has_tex )
|
else if ( map_ambient.has_tex )
|
||||||
ao = sample_colormap( map_ambient, out_texcoord ).x;
|
ao = sample_colormap( map_ambient, out_texcoord ).x;
|
||||||
|
|
||||||
vec3 emissive = sample_colormap( map_emissive, out_texcoord ).rgb;
|
vec3 emissive = sample_colormap( map_emissive, out_texcoord ).rgb;
|
||||||
|
|
||||||
vec3 normalmap = texture( map_normals_tex, out_texcoord ).xyz * vec3(2.0) - vec3(1.0);
|
vec3 normalmap = texture( map_normals_tex, out_texcoord ).xyz * vec3(2.0) - vec3(1.0);
|
||||||
float normalmap_mip = textureQueryLod( map_normals_tex, out_texcoord ).x;
|
float normalmap_mip = textureQueryLod( map_normals_tex, out_texcoord ).x;
|
||||||
float normalmap_length = length(normalmap);
|
float normalmap_length = length(normalmap);
|
||||||
normalmap /= normalmap_length;
|
normalmap /= normalmap_length;
|
||||||
|
|
||||||
vec3 normal = out_normal;
|
vec3 normal = out_normal;
|
||||||
|
|
||||||
if ( map_normals.has_tex )
|
if ( map_normals.has_tex )
|
||||||
{
|
{
|
||||||
// Mikkelsen's tangent space normal map decoding. See http://mikktspace.com/ for rationale.
|
// Mikkelsen's tangent space normal map decoding. See http://mikktspace.com/ for rationale.
|
||||||
vec3 bi = cross( out_normal, out_tangent );
|
vec3 bi = cross( out_normal, out_tangent );
|
||||||
vec3 nmap = normalmap.xyz;
|
vec3 nmap = normalmap.xyz;
|
||||||
normal = nmap.x * out_tangent + nmap.y * bi + nmap.z * out_normal;
|
normal = nmap.x * out_tangent + nmap.y * bi + nmap.z * out_normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
normal = normalize( normal );
|
normal = normalize( normal );
|
||||||
|
|
||||||
if( USE_MAP_DEBUGGING && !USE_AMBIENT_DEBUGGING )
|
if( USE_MAP_DEBUGGING && !USE_AMBIENT_DEBUGGING )
|
||||||
{
|
{
|
||||||
vec3 c = vec3(1., 0., 0.);
|
vec3 c = vec3(1., 0., 0.);
|
||||||
float x = gl_FragCoord.x / resolution.x;
|
float x = gl_FragCoord.x / resolution.x;
|
||||||
float y = gl_FragCoord.y / resolution.y;
|
float y = gl_FragCoord.y / resolution.y;
|
||||||
if ( y < (7.0/7.0) ) c = vec3(.5) + .5*out_normal;
|
if ( y < (7.0/7.0) ) c = vec3(.5) + .5*out_normal;
|
||||||
if ( y < (6.0/7.0) ) c = vec3(.5) + .5*normalmap;
|
if ( y < (6.0/7.0) ) c = vec3(.5) + .5*normalmap;
|
||||||
if ( y < (5.0/7.0) ) c = vec3(ao);
|
if ( y < (5.0/7.0) ) c = vec3(ao);
|
||||||
if ( y < (4.0/7.0) ) c = vec3(emissive);
|
if ( y < (4.0/7.0) ) c = vec3(emissive);
|
||||||
if ( y < (3.0/7.0) ) c = vec3(metallic);
|
if ( y < (3.0/7.0) ) c = vec3(metallic);
|
||||||
if ( y < (2.0/7.0) ) c = vec3(roughness);
|
if ( y < (2.0/7.0) ) c = vec3(roughness);
|
||||||
if ( y < (1.0/7.0) ) c = baseColor;
|
if ( y < (1.0/7.0) ) c = baseColor;
|
||||||
frag_color = vec4(c, 1.);
|
frag_color = vec4(c, 1.);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (USE_NORMAL_VARIATION_TO_ROUGHNESS)
|
if (USE_NORMAL_VARIATION_TO_ROUGHNESS)
|
||||||
{
|
{
|
||||||
// Try to reduce specular aliasing by increasing roughness when minified normal maps have high variation.
|
// Try to reduce specular aliasing by increasing roughness when minified normal maps have high variation.
|
||||||
float variation = 1. - pow( normalmap_length, 8. );
|
float variation = 1. - pow( normalmap_length, 8. );
|
||||||
float minification = clamp( normalmap_mip - 2., 0., 1. );
|
float minification = clamp( normalmap_mip - 2., 0., 1. );
|
||||||
roughness = mix( roughness, 1.0, variation * minification );
|
roughness = mix( roughness, 1.0, variation * minification );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vec3 N = normal;
|
vec3 N = normal;
|
||||||
vec3 V = normalize( out_to_camera );
|
vec3 V = normalize( out_to_camera );
|
||||||
|
|
||||||
vec3 Lo = vec3(0.);
|
vec3 Lo = vec3(0.);
|
||||||
vec3 F0 = vec3(0.04);
|
vec3 F0 = vec3(0.04);
|
||||||
F0 = mix( F0, baseColor, metallic );
|
F0 = mix( F0, baseColor, metallic );
|
||||||
|
|
||||||
bool use_ibl = has_tex_skysphere;
|
bool use_ibl = has_tex_skysphere;
|
||||||
|
|
||||||
// Add contributions from analytic lights only if we don't have a skybox.
|
// Add contributions from analytic lights only if we don't have a skybox.
|
||||||
|
|
||||||
{
|
{
|
||||||
int num_lights = use_ibl ? 1 : lights.length();
|
int num_lights = use_ibl ? 1 : lights.length();
|
||||||
for ( int i = 0; i < num_lights; i++ )
|
for ( int i = 0; i < num_lights; i++ )
|
||||||
{
|
{
|
||||||
vec3 radiance = lights[ i ].color * BOOST_LIGHTING;
|
vec3 radiance = lights[ i ].color * BOOST_LIGHTING;
|
||||||
|
|
||||||
vec3 L = -normalize( lights[ i ].direction );
|
vec3 L = -normalize( lights[ i ].direction );
|
||||||
vec3 H = normalize( V + L );
|
vec3 H = normalize( V + L );
|
||||||
|
|
||||||
vec3 F = fresnel_schlick( H, V, F0 );
|
vec3 F = fresnel_schlick( H, V, F0 );
|
||||||
vec3 kS = F;
|
vec3 kS = F;
|
||||||
vec3 kD = vec3(1.0) - kS;
|
vec3 kD = vec3(1.0) - kS;
|
||||||
kD *= 1.0 - metallic;
|
kD *= 1.0 - metallic;
|
||||||
|
|
||||||
// Premultiplied alpha applied to the diffuse component only
|
// Premultiplied alpha applied to the diffuse component only
|
||||||
kD *= alpha;
|
kD *= alpha;
|
||||||
|
|
||||||
float D = distribution_ggx( N, H, roughness );
|
float D = distribution_ggx( N, H, roughness );
|
||||||
float G = geometry_smith( N, V, L, roughness );
|
float G = geometry_smith( N, V, L, roughness );
|
||||||
|
|
||||||
vec3 num = D * F * G;
|
vec3 num = D * F * G;
|
||||||
float denom = 4. * max( 0., dot( N, V ) ) * max( 0., dot( N, L ) );
|
float denom = 4. * max( 0., dot( N, V ) ) * max( 0., dot( N, L ) );
|
||||||
|
|
||||||
vec3 specular = kS * (num / max( 0.001, denom ));
|
vec3 specular = kS * (num / max( 0.001, denom ));
|
||||||
|
|
||||||
float NdotL = max( 0., dot( N, L ) );
|
float NdotL = max( 0., dot( N, L ) );
|
||||||
|
|
||||||
Lo += ( kD * ( baseColor / PI ) + specular ) * radiance * NdotL;
|
Lo += ( kD * ( baseColor / PI ) + specular ) * radiance * NdotL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 ambient = sample_colormap( map_ambient, out_texcoord ).xyz;
|
vec3 ambient = sample_colormap( map_ambient, out_texcoord ).xyz;
|
||||||
vec3 diffuse_ambient;
|
vec3 diffuse_ambient;
|
||||||
vec3 specular_ambient;
|
vec3 specular_ambient;
|
||||||
|
|
||||||
if ( use_ibl )
|
if ( use_ibl )
|
||||||
{
|
{
|
||||||
// Image based lighting.
|
// Image based lighting.
|
||||||
// Based on https://learnopengl.com/PBR/IBL/Diffuse-irradiance
|
// Based on https://learnopengl.com/PBR/IBL/Diffuse-irradiance
|
||||||
|
|
||||||
vec3 irradiance = vec3(0.);
|
vec3 irradiance = vec3(0.);
|
||||||
|
|
||||||
if ( USE_BRUTEFORCE_IRRADIANCE )
|
if ( USE_BRUTEFORCE_IRRADIANCE )
|
||||||
{
|
{
|
||||||
irradiance = sample_irradiance_slow( normal, out_tangent );
|
irradiance = sample_irradiance_slow( normal, out_tangent );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
irradiance = sample_irradiance_fast( normal, out_tangent );
|
irradiance = sample_irradiance_fast( normal, out_tangent );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the Fresnel term for a perfect mirror reflection with L = R.
|
// Compute the Fresnel term for a perfect mirror reflection with L = R.
|
||||||
// In this case the halfway vector H = N.
|
// In this case the halfway vector H = N.
|
||||||
//
|
//
|
||||||
// We use a modified Fresnel function that dampens specular reflections of very
|
// We use a modified Fresnel function that dampens specular reflections of very
|
||||||
// rough surfaces to avoid too bright pixels at grazing angles.
|
// rough surfaces to avoid too bright pixels at grazing angles.
|
||||||
vec3 F = fresnel_schlick_roughness( N, V, F0, roughness );
|
vec3 F = fresnel_schlick_roughness( N, V, F0, roughness );
|
||||||
vec3 kS = F;
|
vec3 kS = F;
|
||||||
|
|
||||||
// Subtract the amount of reflected light (specular) to get the energy left for
|
// Subtract the amount of reflected light (specular) to get the energy left for
|
||||||
// absorbed (diffuse) light.
|
// absorbed (diffuse) light.
|
||||||
vec3 kD = vec3(1.) - kS;
|
vec3 kD = vec3(1.) - kS;
|
||||||
|
|
||||||
// Metallic surfaces have only a specular reflection.
|
// Metallic surfaces have only a specular reflection.
|
||||||
kD *= 1.0 - metallic;
|
kD *= 1.0 - metallic;
|
||||||
|
|
||||||
// Premultiplied alpha applied to the diffuse component only
|
// Premultiplied alpha applied to the diffuse component only
|
||||||
kD *= alpha;
|
kD *= alpha;
|
||||||
|
|
||||||
// Modulate the incoming lighting with the diffuse color: some wavelengths get absorbed.
|
// Modulate the incoming lighting with the diffuse color: some wavelengths get absorbed.
|
||||||
diffuse_ambient = irradiance * baseColor;
|
diffuse_ambient = irradiance * baseColor;
|
||||||
|
|
||||||
// Ambient light also has a specular part.
|
// Ambient light also has a specular part.
|
||||||
specular_ambient = specular_ibl( V, normal, roughness, F );
|
specular_ambient = specular_ibl( V, normal, roughness, F );
|
||||||
|
|
||||||
// Ambient occlusion tells us the fraction of sky light that reaches this point.
|
// Ambient occlusion tells us the fraction of sky light that reaches this point.
|
||||||
if (USE_SPECULAR_AO_ATTENUATION)
|
if (USE_SPECULAR_AO_ATTENUATION)
|
||||||
{
|
{
|
||||||
ambient = ao * (kD * diffuse_ambient + specular_ambient);
|
ambient = ao * (kD * diffuse_ambient + specular_ambient);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// We don't attenuate specular_ambient ambient here with AO which might cause flickering in dark cavities.
|
// We don't attenuate specular_ambient ambient here with AO which might cause flickering in dark cavities.
|
||||||
ambient = ao * (kD * diffuse_ambient) + specular_ambient;
|
ambient = ao * (kD * diffuse_ambient) + specular_ambient;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 color = (ambient + Lo) + emissive;
|
vec3 color = (ambient + Lo) + emissive;
|
||||||
|
|
||||||
if ( USE_AMBIENT_DEBUGGING )
|
if ( USE_AMBIENT_DEBUGGING )
|
||||||
{
|
{
|
||||||
float y = gl_FragCoord.y / resolution.y;
|
float y = gl_FragCoord.y / resolution.y;
|
||||||
if( USE_MAP_DEBUGGING && y > 0.5 )
|
if( USE_MAP_DEBUGGING && y > 0.5 )
|
||||||
{
|
{
|
||||||
if ( (y-0.5) < (7.0/7.0/2.0) ) color = vec3(.5) + .5*out_normal;
|
if ( (y-0.5) < (7.0/7.0/2.0) ) color = vec3(.5) + .5*out_normal;
|
||||||
if ( (y-0.5) < (6.0/7.0/2.0) ) color = vec3(.5) + .5*normalmap;
|
if ( (y-0.5) < (6.0/7.0/2.0) ) color = vec3(.5) + .5*normalmap;
|
||||||
if ( (y-0.5) < (5.0/7.0/2.0) ) color = vec3(ao);
|
if ( (y-0.5) < (5.0/7.0/2.0) ) color = vec3(ao);
|
||||||
if ( (y-0.5) < (4.0/7.0/2.0) ) color = vec3(emissive);
|
if ( (y-0.5) < (4.0/7.0/2.0) ) color = vec3(emissive);
|
||||||
if ( (y-0.5) < (3.0/7.0/2.0) ) color = vec3(metallic);
|
if ( (y-0.5) < (3.0/7.0/2.0) ) color = vec3(metallic);
|
||||||
if ( (y-0.5) < (2.0/7.0/2.0) ) color = vec3(roughness);
|
if ( (y-0.5) < (2.0/7.0/2.0) ) color = vec3(roughness);
|
||||||
if ( (y-0.5) < (1.0/7.0/2.0) ) color = baseColor;
|
if ( (y-0.5) < (1.0/7.0/2.0) ) color = baseColor;
|
||||||
} else {
|
} else {
|
||||||
float x = gl_FragCoord.x / resolution.x;
|
float x = gl_FragCoord.x / resolution.x;
|
||||||
if ( x < 0.33 )
|
if ( x < 0.33 )
|
||||||
color = specular_ambient;
|
color = specular_ambient;
|
||||||
else if( x > 0.66 )
|
else if( x > 0.66 )
|
||||||
color = diffuse_ambient;
|
color = diffuse_ambient;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 // original
|
#if 0 // original
|
||||||
// basic tonemap and gamma correction
|
// basic tonemap and gamma correction
|
||||||
color = color / ( vec3(1.) + color );
|
color = color / ( vec3(1.) + color );
|
||||||
color = pow( color, vec3(1. / 2.2) );
|
color = pow( color, vec3(1. / 2.2) );
|
||||||
#elif 0
|
#elif 0
|
||||||
// filmic tonemapper
|
// filmic tonemapper
|
||||||
vec3 linearColor = color;
|
vec3 linearColor = color;
|
||||||
vec3 x = max(vec3(0.0), linearColor - 0.004);
|
vec3 x = max(vec3(0.0), linearColor - 0.004);
|
||||||
color = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06);
|
color = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06);
|
||||||
// gamma correction
|
// gamma correction
|
||||||
// color = pow( color, vec3(1. / 2.2) );
|
// color = pow( color, vec3(1. / 2.2) );
|
||||||
#elif 1
|
#elif 1
|
||||||
// aces film (CC0, src: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/)
|
// aces film (CC0, src: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/)
|
||||||
vec3 x = color;
|
vec3 x = color;
|
||||||
float a = 2.51f;
|
float a = 2.51f;
|
||||||
float b = 0.03f;
|
float b = 0.03f;
|
||||||
float c = 2.43f;
|
float c = 2.43f;
|
||||||
float d = 0.59f;
|
float d = 0.59f;
|
||||||
float e = 0.14f;
|
float e = 0.14f;
|
||||||
color = clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0);
|
color = clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0);
|
||||||
// gamma correction
|
// gamma correction
|
||||||
color = pow( color, vec3(1. / 2.2) );
|
color = pow( color, vec3(1. / 2.2) );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// dither with noise.
|
// dither with noise.
|
||||||
// float dither = random( uvec3( floatBitsToUint( gl_FragCoord.xy ), frame_count ) );
|
// float dither = random( uvec3( floatBitsToUint( gl_FragCoord.xy ), frame_count ) );
|
||||||
// color += BOOST_NOISE * vec3( (-1.0/256.) + (2./256.) * dither );
|
// color += BOOST_NOISE * vec3( (-1.0/256.) + (2./256.) * dither );
|
||||||
|
|
||||||
// Technically this alpha may be too transparent, if there is a lot of reflected light we wouldn't
|
// Technically this alpha may be too transparent, if there is a lot of reflected light we wouldn't
|
||||||
// see the background, maybe we can approximate it well enough by adding a fresnel term
|
// see the background, maybe we can approximate it well enough by adding a fresnel term
|
||||||
frag_color = vec4( color, alpha );
|
frag_color = vec4( color, alpha );
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,180 +1,180 @@
|
||||||
#ifndef MAX_BONES
|
#ifndef MAX_BONES
|
||||||
#define MAX_BONES 110
|
#define MAX_BONES 110
|
||||||
#endif
|
#endif
|
||||||
uniform mat3x4 vsBoneMatrix[MAX_BONES];
|
uniform mat3x4 vsBoneMatrix[MAX_BONES];
|
||||||
uniform bool SKINNED = false;
|
uniform bool SKINNED = false;
|
||||||
uniform mat4 M; // RIM
|
uniform mat4 M; // RIM
|
||||||
uniform mat4 VP;
|
uniform mat4 VP;
|
||||||
uniform mat4 P;
|
uniform mat4 P;
|
||||||
uniform int u_billboard;
|
uniform int u_billboard;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Fetch blend channels from all attached blend deformers.
|
// Fetch blend channels from all attached blend deformers.
|
||||||
for (size_t di = 0; di < mesh->blend_deformers.count; di++) {
|
for (size_t di = 0; di < mesh->blend_deformers.count; di++) {
|
||||||
ufbx_blend_deformer *deformer = mesh->blend_deformers.data[di];
|
ufbx_blend_deformer *deformer = mesh->blend_deformers.data[di];
|
||||||
for (size_t ci = 0; ci < deformer->channels.count; ci++) {
|
for (size_t ci = 0; ci < deformer->channels.count; ci++) {
|
||||||
ufbx_blend_channel *chan = deformer->channels.data[ci];
|
ufbx_blend_channel *chan = deformer->channels.data[ci];
|
||||||
if (chan->keyframes.count == 0) continue;
|
if (chan->keyframes.count == 0) continue;
|
||||||
if (num_blend_shapes < MAX_BLEND_SHAPES) {
|
if (num_blend_shapes < MAX_BLEND_SHAPES) {
|
||||||
blend_channels[num_blend_shapes] = chan;
|
blend_channels[num_blend_shapes] = chan;
|
||||||
vmesh->blend_channel_indices[num_blend_shapes] = (int32_t)chan->typed_id;
|
vmesh->blend_channel_indices[num_blend_shapes] = (int32_t)chan->typed_id;
|
||||||
num_blend_shapes++;
|
num_blend_shapes++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (num_blend_shapes > 0) {
|
if (num_blend_shapes > 0) {
|
||||||
vmesh->blend_shape_image = pack_blend_channels_to_image(mesh, blend_channels, num_blend_shapes);
|
vmesh->blend_shape_image = pack_blend_channels_to_image(mesh, blend_channels, num_blend_shapes);
|
||||||
vmesh->num_blend_shapes = num_blend_shapes;
|
vmesh->num_blend_shapes = num_blend_shapes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ubo.f_num_blend_shapes = (float)mesh->num_blend_shapes;
|
ubo.f_num_blend_shapes = (float)mesh->num_blend_shapes;
|
||||||
for (size_t i = 0; i < mesh->num_blend_shapes; i++) {
|
for (size_t i = 0; i < mesh->num_blend_shapes; i++) {
|
||||||
ubo.blend_weights[i] = view->scene.blend_channels[mesh->blend_channel_indices[i]].weight;
|
ubo.blend_weights[i] = view->scene.blend_channels[mesh->blend_channel_indices[i]].weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sg_image blend_shapes = mesh->num_blend_shapes > 0 ? mesh->blend_shape_image : view->empty_blend_shape_image;
|
sg_image blend_shapes = mesh->num_blend_shapes > 0 ? mesh->blend_shape_image : view->empty_blend_shape_image;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// for blendshapes
|
// for blendshapes
|
||||||
#ifndef MAX_BLENDSHAPES
|
#ifndef MAX_BLENDSHAPES
|
||||||
#define MAX_BLENDSHAPES 16
|
#define MAX_BLENDSHAPES 16
|
||||||
#endif
|
#endif
|
||||||
uniform vec4 blend_weights[MAX_BLENDSHAPES]; // @todo: implement me
|
uniform vec4 blend_weights[MAX_BLENDSHAPES]; // @todo: implement me
|
||||||
uniform float f_num_blend_shapes; // @todo: implement me
|
uniform float f_num_blend_shapes; // @todo: implement me
|
||||||
uniform sampler2DArray blend_shapes; // @todo: implement me
|
uniform sampler2DArray blend_shapes; // @todo: implement me
|
||||||
|
|
||||||
|
|
||||||
in vec3 att_position; // @todo: reorder ass2iqe to emit p3 n3 u2 t3 b3 c4B i4 w4 instead
|
in vec3 att_position; // @todo: reorder ass2iqe to emit p3 n3 u2 t3 b3 c4B i4 w4 instead
|
||||||
in vec2 att_texcoord;
|
in vec2 att_texcoord;
|
||||||
in vec3 att_normal;
|
in vec3 att_normal;
|
||||||
in vec4 att_tangent; // vec3 + bi sign
|
in vec4 att_tangent; // vec3 + bi sign
|
||||||
in mat4 att_instanced_matrix; // for instanced rendering
|
in mat4 att_instanced_matrix; // for instanced rendering
|
||||||
in vec4 att_indexes; // @fixme: gles might use ivec4 instead?
|
in vec4 att_indexes; // @fixme: gles might use ivec4 instead?
|
||||||
in vec4 att_weights; // @todo: downgrade from float to byte
|
in vec4 att_weights; // @todo: downgrade from float to byte
|
||||||
in float att_vertexindex; // for blendshapes
|
in float att_vertexindex; // for blendshapes
|
||||||
in vec4 att_color;
|
in vec4 att_color;
|
||||||
in vec3 att_bitangent; // @todo: remove? also, ass2iqe might output this
|
in vec3 att_bitangent; // @todo: remove? also, ass2iqe might output this
|
||||||
in vec2 att_texcoord2;
|
in vec2 att_texcoord2;
|
||||||
out vec4 v_color;
|
out vec4 v_color;
|
||||||
out vec3 v_position, v_position_ws;
|
out vec3 v_position, v_position_ws;
|
||||||
out vec3 v_normal, v_normal_ws;
|
out vec3 v_normal, v_normal_ws;
|
||||||
out vec2 v_texcoord, v_texcoord2;
|
out vec2 v_texcoord, v_texcoord2;
|
||||||
out vec3 v_tangent;
|
out vec3 v_tangent;
|
||||||
out vec3 v_binormal;
|
out vec3 v_binormal;
|
||||||
out vec3 v_viewpos;
|
out vec3 v_viewpos;
|
||||||
out vec3 v_to_camera;
|
out vec3 v_to_camera;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// shadow
|
// shadow
|
||||||
uniform mat4 model, view, inv_view;
|
uniform mat4 model, view, inv_view;
|
||||||
uniform mat4 cameraToShadowProjector;
|
uniform mat4 cameraToShadowProjector;
|
||||||
out vec4 vneye;
|
out vec4 vneye;
|
||||||
out vec4 vpeye;
|
out vec4 vpeye;
|
||||||
out vec4 sc;
|
out vec4 sc;
|
||||||
void do_shadow() {
|
void do_shadow() {
|
||||||
vneye = view * model * vec4(att_normal, 0.0f);
|
vneye = view * model * vec4(att_normal, 0.0f);
|
||||||
vpeye = view * model * vec4(att_position, 1.0);
|
vpeye = view * model * vec4(att_position, 1.0);
|
||||||
sc = cameraToShadowProjector * model * vec4(att_position, 1.0f);
|
sc = cameraToShadowProjector * model * vec4(att_position, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// blendshapes
|
// blendshapes
|
||||||
vec3 evaluate_blend_shape(int vertex_index) {
|
vec3 evaluate_blend_shape(int vertex_index) {
|
||||||
ivec2 coord = ivec2(vertex_index & (2048 - 1), vertex_index >> 11);
|
ivec2 coord = ivec2(vertex_index & (2048 - 1), vertex_index >> 11);
|
||||||
int num_blend_shapes = int(f_num_blend_shapes);
|
int num_blend_shapes = int(f_num_blend_shapes);
|
||||||
vec3 offset = vec3(0.0);
|
vec3 offset = vec3(0.0);
|
||||||
for (int i = 0; i < num_blend_shapes; i++) {
|
for (int i = 0; i < num_blend_shapes; i++) {
|
||||||
vec4 packedw = blend_weights[i >> 2];
|
vec4 packedw = blend_weights[i >> 2];
|
||||||
float weight = packedw[i & 3];
|
float weight = packedw[i & 3];
|
||||||
offset += weight * texelFetch(blend_shapes, ivec3(coord, i), 0).xyz;
|
offset += weight * texelFetch(blend_shapes, ivec3(coord, i), 0).xyz;
|
||||||
}
|
}
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 objPos;
|
vec3 objPos;
|
||||||
if(!SKINNED) {
|
if(!SKINNED) {
|
||||||
objPos = att_position;
|
objPos = att_position;
|
||||||
v_normal = att_normal;
|
v_normal = att_normal;
|
||||||
} else {
|
} else {
|
||||||
mat3x4 m = vsBoneMatrix[int(att_indexes.x)] * att_weights.x;
|
mat3x4 m = vsBoneMatrix[int(att_indexes.x)] * att_weights.x;
|
||||||
m += vsBoneMatrix[int(att_indexes.y)] * att_weights.y;
|
m += vsBoneMatrix[int(att_indexes.y)] * att_weights.y;
|
||||||
m += vsBoneMatrix[int(att_indexes.z)] * att_weights.z;
|
m += vsBoneMatrix[int(att_indexes.z)] * att_weights.z;
|
||||||
m += vsBoneMatrix[int(att_indexes.w)] * att_weights.w;
|
m += vsBoneMatrix[int(att_indexes.w)] * att_weights.w;
|
||||||
objPos = vec4(att_position, 1.0) * m;
|
objPos = vec4(att_position, 1.0) * m;
|
||||||
|
|
||||||
// blendshapes
|
// blendshapes
|
||||||
// objPos += evaluate_blend_shape(int(att_vertexindex));
|
// objPos += evaluate_blend_shape(int(att_vertexindex));
|
||||||
|
|
||||||
v_normal = vec4(att_normal, 0.0) * m;
|
v_normal = vec4(att_normal, 0.0) * m;
|
||||||
//@todo: tangents
|
//@todo: tangents
|
||||||
}
|
}
|
||||||
|
|
||||||
// vec3 tangent = att_tangent.xyz;
|
// vec3 tangent = att_tangent.xyz;
|
||||||
// vec3 bitangent = cross(att_normal, att_tangent.xyz) * att_tangent.w;
|
// vec3 bitangent = cross(att_normal, att_tangent.xyz) * att_tangent.w;
|
||||||
v_normal_ws = normalize(vec3(att_instanced_matrix * vec4(v_normal, 0.))); // normal to world/model space
|
v_normal_ws = normalize(vec3(att_instanced_matrix * vec4(v_normal, 0.))); // normal to world/model space
|
||||||
v_normal = normalize(v_normal);
|
v_normal = normalize(v_normal);
|
||||||
v_position = att_position;
|
v_position = att_position;
|
||||||
v_texcoord = att_texcoord;
|
v_texcoord = att_texcoord;
|
||||||
v_texcoord2 = att_texcoord2;
|
v_texcoord2 = att_texcoord2;
|
||||||
v_color = att_color;
|
v_color = att_color;
|
||||||
mat4 modelView = view * att_instanced_matrix;
|
mat4 modelView = view * att_instanced_matrix;
|
||||||
mat4 l_model = att_instanced_matrix;
|
mat4 l_model = att_instanced_matrix;
|
||||||
v_position_ws = (l_model * vec4( objPos, 1.0 )).xyz;
|
v_position_ws = (l_model * vec4( objPos, 1.0 )).xyz;
|
||||||
|
|
||||||
if(u_billboard > 0) {
|
if(u_billboard > 0) {
|
||||||
vec3 cameraPosition = -transpose(mat3(view)) * view[3].xyz;
|
vec3 cameraPosition = -transpose(mat3(view)) * view[3].xyz;
|
||||||
vec3 lookDir = normalize(cameraPosition - v_position_ws);
|
vec3 lookDir = normalize(cameraPosition - v_position_ws);
|
||||||
|
|
||||||
vec3 up = vec3(modelView[0][1], modelView[1][1], modelView[2][1]);
|
vec3 up = vec3(modelView[0][1], modelView[1][1], modelView[2][1]);
|
||||||
vec3 right = normalize(cross(up, lookDir));
|
vec3 right = normalize(cross(up, lookDir));
|
||||||
up = cross(lookDir, right);
|
up = cross(lookDir, right);
|
||||||
|
|
||||||
vec3 scale;
|
vec3 scale;
|
||||||
scale.x = length(vec3(l_model[0]));
|
scale.x = length(vec3(l_model[0]));
|
||||||
scale.y = length(vec3(l_model[1]));
|
scale.y = length(vec3(l_model[1]));
|
||||||
scale.z = length(vec3(l_model[2]));
|
scale.z = length(vec3(l_model[2]));
|
||||||
// scale.x *= sign(l_model[0][0]);
|
// scale.x *= sign(l_model[0][0]);
|
||||||
// scale.y *= sign(l_model[1][1]);
|
// scale.y *= sign(l_model[1][1]);
|
||||||
// scale.z *= sign(l_model[2][2]);
|
// scale.z *= sign(l_model[2][2]);
|
||||||
|
|
||||||
mat4 billboardRotation = mat4(
|
mat4 billboardRotation = mat4(
|
||||||
vec4(right * scale.x, 0.0),
|
vec4(right * scale.x, 0.0),
|
||||||
vec4(-up * scale.y, 0.0),
|
vec4(-up * scale.y, 0.0),
|
||||||
vec4(-lookDir * scale.z, 0.0),
|
vec4(-lookDir * scale.z, 0.0),
|
||||||
vec4(0.0, 0.0, 0.0, 1.0)
|
vec4(0.0, 0.0, 0.0, 1.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
if((u_billboard & 0x4) != 0) l_model[0] = billboardRotation[0];
|
if((u_billboard & 0x4) != 0) l_model[0] = billboardRotation[0];
|
||||||
if((u_billboard & 0x2) != 0) l_model[1] = billboardRotation[1];
|
if((u_billboard & 0x2) != 0) l_model[1] = billboardRotation[1];
|
||||||
if((u_billboard & 0x1) != 0) l_model[2] = billboardRotation[2];
|
if((u_billboard & 0x1) != 0) l_model[2] = billboardRotation[2];
|
||||||
modelView = view * l_model;
|
modelView = view * l_model;
|
||||||
}
|
}
|
||||||
v_position_ws = (l_model * vec4( objPos, 1.0 )).xyz;
|
v_position_ws = (l_model * vec4( objPos, 1.0 )).xyz;
|
||||||
v_tangent = normalize(mat3(att_instanced_matrix) * att_tangent.xyz);
|
v_tangent = normalize(mat3(att_instanced_matrix) * att_tangent.xyz);
|
||||||
#if 0
|
#if 0
|
||||||
// compute tangent T and bitangent B
|
// compute tangent T and bitangent B
|
||||||
vec3 Q1 = dFdx(att_position);
|
vec3 Q1 = dFdx(att_position);
|
||||||
vec3 Q2 = dFdy(att_position);
|
vec3 Q2 = dFdy(att_position);
|
||||||
vec2 st1 = dFdx(att_texcoord);
|
vec2 st1 = dFdx(att_texcoord);
|
||||||
vec2 st2 = dFdy(att_texcoord);
|
vec2 st2 = dFdy(att_texcoord);
|
||||||
|
|
||||||
vec3 T = normalize(Q1*st2.t - Q2*st1.t);
|
vec3 T = normalize(Q1*st2.t - Q2*st1.t);
|
||||||
vec3 B = normalize(-Q1*st2.s + Q2*st1.s);
|
vec3 B = normalize(-Q1*st2.s + Q2*st1.s);
|
||||||
vec3 binormal = B;
|
vec3 binormal = B;
|
||||||
#else
|
#else
|
||||||
vec3 binormal = cross(att_normal, att_tangent.xyz) * att_tangent.w;
|
vec3 binormal = cross(att_normal, att_tangent.xyz) * att_tangent.w;
|
||||||
#endif
|
#endif
|
||||||
v_binormal = normalize(mat3(att_instanced_matrix) * binormal);
|
v_binormal = normalize(mat3(att_instanced_matrix) * binormal);
|
||||||
vec4 finalPos = modelView * vec4( objPos, 1.0 );
|
vec4 finalPos = modelView * vec4( objPos, 1.0 );
|
||||||
vec3 to_camera = normalize( -finalPos.xyz );
|
vec3 to_camera = normalize( -finalPos.xyz );
|
||||||
v_to_camera = mat3( inv_view ) * to_camera;
|
v_to_camera = mat3( inv_view ) * to_camera;
|
||||||
gl_Position = P * finalPos;
|
gl_Position = P * finalPos;
|
||||||
do_shadow();
|
do_shadow();
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
frame: 0-0 Idle
|
frame: 0-0 Idle
|
||||||
|
|
769810
engine/joint/v4k.h
769810
engine/joint/v4k.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,140 +1,140 @@
|
||||||
// base64 de/encoder. Based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN).
|
// base64 de/encoder. Based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN).
|
||||||
// - rlyeh, public domain
|
// - rlyeh, public domain
|
||||||
|
|
||||||
#ifndef BASE64_H
|
#ifndef BASE64_H
|
||||||
#define BASE64_H
|
#define BASE64_H
|
||||||
|
|
||||||
unsigned base64_bounds(unsigned size);
|
unsigned base64_bounds(unsigned size);
|
||||||
char* base64_encode(const void *inp, unsigned inlen); // free() after use
|
char* base64_encode(const void *inp, unsigned inlen); // free() after use
|
||||||
char* base64_decode(const char *inp, unsigned inlen); // array_free() after use
|
char* base64_decode(const char *inp, unsigned inlen); // array_free() after use
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BASE64_C
|
#ifdef BASE64_C
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#define BASE64_ENCODE_OUT_SIZE(s) ((unsigned int)((((s) + 2) / 3) * 4 + 1))
|
#define BASE64_ENCODE_OUT_SIZE(s) ((unsigned int)((((s) + 2) / 3) * 4 + 1))
|
||||||
#define BASE64_DECODE_OUT_SIZE(s) ((unsigned int)(((s) / 4) * 3))
|
#define BASE64_DECODE_OUT_SIZE(s) ((unsigned int)(((s) / 4) * 3))
|
||||||
|
|
||||||
unsigned base64_bounds(unsigned size) {
|
unsigned base64_bounds(unsigned size) {
|
||||||
return BASE64_ENCODE_OUT_SIZE(size);
|
return BASE64_ENCODE_OUT_SIZE(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* base64_encode(const void *inp, unsigned inlen) { // free() after use
|
char* base64_encode(const void *inp, unsigned inlen) { // free() after use
|
||||||
unsigned outlen = base64_bounds(inlen);
|
unsigned outlen = base64_bounds(inlen);
|
||||||
char *out_ = MALLOC(outlen);
|
char *out_ = MALLOC(outlen);
|
||||||
out_[outlen] = 0;
|
out_[outlen] = 0;
|
||||||
|
|
||||||
uint_least32_t v;
|
uint_least32_t v;
|
||||||
unsigned ii, io, rem;
|
unsigned ii, io, rem;
|
||||||
char *out = (char *)out_;
|
char *out = (char *)out_;
|
||||||
const unsigned char *in = (const unsigned char *)inp;
|
const unsigned char *in = (const unsigned char *)inp;
|
||||||
const uint8_t base64enc_tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
const uint8_t base64enc_tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
for(io = 0, ii = 0, v = 0, rem = 0; ii < inlen; ii ++) {
|
for(io = 0, ii = 0, v = 0, rem = 0; ii < inlen; ii ++) {
|
||||||
unsigned char ch;
|
unsigned char ch;
|
||||||
ch = in[ii];
|
ch = in[ii];
|
||||||
v = (v << 8) | ch;
|
v = (v << 8) | ch;
|
||||||
rem += 8;
|
rem += 8;
|
||||||
while (rem >= 6) {
|
while (rem >= 6) {
|
||||||
rem -= 6;
|
rem -= 6;
|
||||||
if (io >= outlen)
|
if (io >= outlen)
|
||||||
return (FREE(out_), NULL); /* truncation is failure */
|
return (FREE(out_), NULL); /* truncation is failure */
|
||||||
out[io ++] = base64enc_tab[(v >> rem) & 63];
|
out[io ++] = base64enc_tab[(v >> rem) & 63];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rem) {
|
if (rem) {
|
||||||
v <<= (6 - rem);
|
v <<= (6 - rem);
|
||||||
if (io >= outlen)
|
if (io >= outlen)
|
||||||
return (FREE(out_), NULL); /* truncation is failure */
|
return (FREE(out_), NULL); /* truncation is failure */
|
||||||
out[io ++] = base64enc_tab[v & 63];
|
out[io ++] = base64enc_tab[v & 63];
|
||||||
}
|
}
|
||||||
while(io&3) {
|
while(io&3) {
|
||||||
if(io>=outlen) return (FREE(out_), NULL); /* truncation is failure */
|
if(io>=outlen) return (FREE(out_), NULL); /* truncation is failure */
|
||||||
out[io++]='=';
|
out[io++]='=';
|
||||||
}
|
}
|
||||||
if(io>=outlen) return (FREE(out_), NULL); /* no room for null terminator */
|
if(io>=outlen) return (FREE(out_), NULL); /* no room for null terminator */
|
||||||
out[io]=0;
|
out[io]=0;
|
||||||
return out_;
|
return out_;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef array_resize
|
#ifdef array_resize
|
||||||
array(char) base64_decode(const char *inp, unsigned inlen) { // array_free() after use
|
array(char) base64_decode(const char *inp, unsigned inlen) { // array_free() after use
|
||||||
#if 0
|
#if 0
|
||||||
unsigned long outlen = BASE64_DECODE_OUT_SIZE(inlen);
|
unsigned long outlen = BASE64_DECODE_OUT_SIZE(inlen);
|
||||||
array(char) out_ = 0; array_resize(out_, outlen+1);
|
array(char) out_ = 0; array_resize(out_, outlen+1);
|
||||||
|
|
||||||
if( base64_decodex((const unsigned char *)inp, (unsigned long)inlen, (unsigned char *)out_, &outlen) != CRYPT_OK ) {
|
if( base64_decodex((const unsigned char *)inp, (unsigned long)inlen, (unsigned char *)out_, &outlen) != CRYPT_OK ) {
|
||||||
array_free(out_);
|
array_free(out_);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
array_resize(out_, outlen);
|
array_resize(out_, outlen);
|
||||||
out_[outlen] = 0;
|
out_[outlen] = 0;
|
||||||
return out_;
|
return out_;
|
||||||
#else
|
#else
|
||||||
unsigned outlen = BASE64_DECODE_OUT_SIZE(inlen);
|
unsigned outlen = BASE64_DECODE_OUT_SIZE(inlen);
|
||||||
array(char) out_ = 0; array_resize(out_, outlen);
|
array(char) out_ = 0; array_resize(out_, outlen);
|
||||||
|
|
||||||
// based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN)
|
// based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN)
|
||||||
uint_least32_t v;
|
uint_least32_t v;
|
||||||
unsigned ii, io, rem;
|
unsigned ii, io, rem;
|
||||||
char *out = (char *)out_;
|
char *out = (char *)out_;
|
||||||
const unsigned char *in = (const unsigned char *)inp;
|
const unsigned char *in = (const unsigned char *)inp;
|
||||||
const uint8_t base64dec_tab[256]= {
|
const uint8_t base64dec_tab[256]= {
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
|
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
|
||||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
|
||||||
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
|
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||||
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
|
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
|
||||||
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
||||||
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||||
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255 };
|
255, 255, 255, 255 };
|
||||||
|
|
||||||
for (io = 0, ii = 0,v = 0, rem = 0; ii < inlen; ii ++) {
|
for (io = 0, ii = 0,v = 0, rem = 0; ii < inlen; ii ++) {
|
||||||
unsigned char ch;
|
unsigned char ch;
|
||||||
if (isspace(in[ii]))
|
if (isspace(in[ii]))
|
||||||
continue;
|
continue;
|
||||||
if ((in[ii]=='=') || (!in[ii]))
|
if ((in[ii]=='=') || (!in[ii]))
|
||||||
break; /* stop at = or null character*/
|
break; /* stop at = or null character*/
|
||||||
ch = base64dec_tab[(unsigned char)in[ii]];
|
ch = base64dec_tab[(unsigned char)in[ii]];
|
||||||
if (ch == 255)
|
if (ch == 255)
|
||||||
break; /* stop at a parse error */
|
break; /* stop at a parse error */
|
||||||
v = (v<<6) | ch;
|
v = (v<<6) | ch;
|
||||||
rem += 6;
|
rem += 6;
|
||||||
if (rem >= 8) {
|
if (rem >= 8) {
|
||||||
rem -= 8;
|
rem -= 8;
|
||||||
if (io >= outlen)
|
if (io >= outlen)
|
||||||
return (array_free(out_), NULL); /* truncation is failure */
|
return (array_free(out_), NULL); /* truncation is failure */
|
||||||
out[io ++] = (v >> rem) & 255;
|
out[io ++] = (v >> rem) & 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rem >= 8) {
|
if (rem >= 8) {
|
||||||
rem -= 8;
|
rem -= 8;
|
||||||
if (io >= outlen)
|
if (io >= outlen)
|
||||||
return (array_free(out_), NULL); /* truncation is failure */
|
return (array_free(out_), NULL); /* truncation is failure */
|
||||||
out[io ++] = (v >> rem) & 255;
|
out[io ++] = (v >> rem) & 255;
|
||||||
}
|
}
|
||||||
return (array_resize(out_, io), out_);
|
return (array_resize(out_, io), out_);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif // array_resize
|
#endif // array_resize
|
||||||
#endif // BASE64_C
|
#endif // BASE64_C
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
12220
engine/split/3rd_enet.h
12220
engine/split/3rd_enet.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
23360
engine/split/3rd_glad.h
23360
engine/split/3rd_glad.h
File diff suppressed because it is too large
Load Diff
78380
engine/split/3rd_glfw3.h
78380
engine/split/3rd_glfw3.h
File diff suppressed because it is too large
Load Diff
93958
engine/split/3rd_https.h
93958
engine/split/3rd_https.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,407 +1,407 @@
|
||||||
/* public domain Simple, Minimalistic MPEG Layer 1 decoder - http://jonolick.com
|
/* public domain Simple, Minimalistic MPEG Layer 1 decoder - http://jonolick.com
|
||||||
*
|
*
|
||||||
* Revision History:
|
* Revision History:
|
||||||
* 1.00 (2014-26-1) Initial release.
|
* 1.00 (2014-26-1) Initial release.
|
||||||
*
|
*
|
||||||
* Basic usage:
|
* Basic usage:
|
||||||
* int hz, channels, outputSize;
|
* int hz, channels, outputSize;
|
||||||
* short *output;
|
* short *output;
|
||||||
* jo_read_mp1(input, inputSize, output, outputSize, hz, channels);
|
* jo_read_mp1(input, inputSize, output, outputSize, hz, channels);
|
||||||
* // Do something with the data here
|
* // Do something with the data here
|
||||||
* free(output);
|
* free(output);
|
||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
|
|
||||||
#ifndef JO_INCLUDE_MP1_H
|
#ifndef JO_INCLUDE_MP1_H
|
||||||
#define JO_INCLUDE_MP1_H
|
#define JO_INCLUDE_MP1_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
extern bool jo_read_mp1(const void *input, int inputSize, short **output, int *outputSize, int *hz, int *channels);
|
extern bool jo_read_mp1(const void *input, int inputSize, short **output, int *outputSize, int *hz, int *channels);
|
||||||
|
|
||||||
#endif // JO_INCLUDE_MP1_H
|
#endif // JO_INCLUDE_MP1_H
|
||||||
|
|
||||||
#ifndef JO_MP1_HEADER_FILE_ONLY
|
#ifndef JO_MP1_HEADER_FILE_ONLY
|
||||||
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 0x1400
|
#if defined(_MSC_VER) && _MSC_VER >= 0x1400
|
||||||
#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen()
|
#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
static const double s_jo_multTbl[64] = {
|
static const double s_jo_multTbl[64] = {
|
||||||
2.000000,1.587401,1.259921,1.000000,0.793701,0.629961,0.500000,0.396850,0.314980,0.250000,0.198425,0.157490,0.125000,0.099213,0.078745,0.062500,
|
2.000000,1.587401,1.259921,1.000000,0.793701,0.629961,0.500000,0.396850,0.314980,0.250000,0.198425,0.157490,0.125000,0.099213,0.078745,0.062500,
|
||||||
0.049606,0.039373,0.031250,0.024803,0.019686,0.015625,0.012402,0.009843,0.007812,0.006201,0.004922,0.003906,0.003100,0.002461,0.001953,0.001550,
|
0.049606,0.039373,0.031250,0.024803,0.019686,0.015625,0.012402,0.009843,0.007812,0.006201,0.004922,0.003906,0.003100,0.002461,0.001953,0.001550,
|
||||||
0.001230,0.000977,0.000775,0.000615,0.000488,0.000388,0.000308,0.000244,0.000194,0.000154,0.000122,0.000097,0.000077,0.000061,0.000048,0.000038,
|
0.001230,0.000977,0.000775,0.000615,0.000488,0.000388,0.000308,0.000244,0.000194,0.000154,0.000122,0.000097,0.000077,0.000061,0.000048,0.000038,
|
||||||
0.000031,0.000024,0.000019,0.000015,0.000012,0.000010,0.000008,0.000006,0.000005,0.000004,0.000003,0.000002,0.000002,0.000002,0.000001,1e-20
|
0.000031,0.000024,0.000019,0.000015,0.000012,0.000010,0.000008,0.000006,0.000005,0.000004,0.000003,0.000002,0.000002,0.000002,0.000001,1e-20
|
||||||
};
|
};
|
||||||
|
|
||||||
// l = i - 256;
|
// l = i - 256;
|
||||||
// s = (i & 0x40) ? 1 : -1;
|
// s = (i & 0x40) ? 1 : -1;
|
||||||
// windowTbl[(i/16)|((i%16)<<5)] = s * 20 * exp(-(l/112)*-(l/112)) * sin(l * M_PI*2 / 112) / l;
|
// windowTbl[(i/16)|((i%16)<<5)] = s * 20 * exp(-(l/112)*-(l/112)) * sin(l * M_PI*2 / 112) / l;
|
||||||
static const double s_jo_windowTbl[512] = {
|
static const double s_jo_windowTbl[512] = {
|
||||||
-0.000000,-0.000443,0.003250,-0.007004,0.031082,-0.078629,0.100311,-0.572037,1.144989,0.572037,0.100311,0.078629,0.031082,0.007004,0.003250,0.000443,
|
-0.000000,-0.000443,0.003250,-0.007004,0.031082,-0.078629,0.100311,-0.572037,1.144989,0.572037,0.100311,0.078629,0.031082,0.007004,0.003250,0.000443,
|
||||||
-0.000015,-0.000473,0.003326,-0.007919,0.030518,-0.084183,0.090927,-0.600220,1.144287,0.543823,0.108856,0.073059,0.031479,0.006119,0.003174,0.000397,
|
-0.000015,-0.000473,0.003326,-0.007919,0.030518,-0.084183,0.090927,-0.600220,1.144287,0.543823,0.108856,0.073059,0.031479,0.006119,0.003174,0.000397,
|
||||||
-0.000015,-0.000534,0.003387,-0.008865,0.029785,-0.089706,0.080688,-0.628296,1.142212,0.515610,0.116577,0.067520,0.031738,0.005295,0.003082,0.000366,
|
-0.000015,-0.000534,0.003387,-0.008865,0.029785,-0.089706,0.080688,-0.628296,1.142212,0.515610,0.116577,0.067520,0.031738,0.005295,0.003082,0.000366,
|
||||||
-0.000015,-0.000580,0.003433,-0.009842,0.028885,-0.095169,0.069595,-0.656219,1.138763,0.487473,0.123474,0.061996,0.031845,0.004486,0.002991,0.000320,
|
-0.000015,-0.000580,0.003433,-0.009842,0.028885,-0.095169,0.069595,-0.656219,1.138763,0.487473,0.123474,0.061996,0.031845,0.004486,0.002991,0.000320,
|
||||||
-0.000015,-0.000626,0.003464,-0.010849,0.027802,-0.100540,0.057617,-0.683914,1.133926,0.459473,0.129578,0.056534,0.031815,0.003723,0.002899,0.000290,
|
-0.000015,-0.000626,0.003464,-0.010849,0.027802,-0.100540,0.057617,-0.683914,1.133926,0.459473,0.129578,0.056534,0.031815,0.003723,0.002899,0.000290,
|
||||||
-0.000015,-0.000687,0.003479,-0.011887,0.026535,-0.105820,0.044785,-0.711319,1.127747,0.431656,0.134888,0.051132,0.031662,0.003006,0.002792,0.000259,
|
-0.000015,-0.000687,0.003479,-0.011887,0.026535,-0.105820,0.044785,-0.711319,1.127747,0.431656,0.134888,0.051132,0.031662,0.003006,0.002792,0.000259,
|
||||||
-0.000015,-0.000748,0.003479,-0.012939,0.025085,-0.110947,0.031082,-0.738373,1.120224,0.404083,0.139450,0.045837,0.031387,0.002335,0.002686,0.000244,
|
-0.000015,-0.000748,0.003479,-0.012939,0.025085,-0.110947,0.031082,-0.738373,1.120224,0.404083,0.139450,0.045837,0.031387,0.002335,0.002686,0.000244,
|
||||||
-0.000031,-0.000809,0.003464,-0.014023,0.023422,-0.115921,0.016510,-0.765030,1.111374,0.376801,0.143265,0.040634,0.031006,0.001694,0.002579,0.000214,
|
-0.000031,-0.000809,0.003464,-0.014023,0.023422,-0.115921,0.016510,-0.765030,1.111374,0.376801,0.143265,0.040634,0.031006,0.001694,0.002579,0.000214,
|
||||||
-0.000031,-0.000885,0.003418,-0.015121,0.021576,-0.120697,0.001068,-0.791214,1.101212,0.349869,0.146362,0.035553,0.030533,0.001099,0.002457,0.000198,
|
-0.000031,-0.000885,0.003418,-0.015121,0.021576,-0.120697,0.001068,-0.791214,1.101212,0.349869,0.146362,0.035553,0.030533,0.001099,0.002457,0.000198,
|
||||||
-0.000031,-0.000961,0.003372,-0.016235,0.019531,-0.125259,-0.015228,-0.816864,1.089783,0.323318,0.148773,0.030609,0.029938,0.000549,0.002350,0.000168,
|
-0.000031,-0.000961,0.003372,-0.016235,0.019531,-0.125259,-0.015228,-0.816864,1.089783,0.323318,0.148773,0.030609,0.029938,0.000549,0.002350,0.000168,
|
||||||
-0.000031,-0.001038,0.003281,-0.017349,0.017258,-0.129562,-0.032379,-0.841949,1.077118,0.297211,0.150497,0.025818,0.029282,0.000031,0.002243,0.000153,
|
-0.000031,-0.001038,0.003281,-0.017349,0.017258,-0.129562,-0.032379,-0.841949,1.077118,0.297211,0.150497,0.025818,0.029282,0.000031,0.002243,0.000153,
|
||||||
-0.000046,-0.001114,0.003174,-0.018463,0.014801,-0.133591,-0.050354,-0.866364,1.063217,0.271591,0.151596,0.021179,0.028534,-0.000443,0.002121,0.000137,
|
-0.000046,-0.001114,0.003174,-0.018463,0.014801,-0.133591,-0.050354,-0.866364,1.063217,0.271591,0.151596,0.021179,0.028534,-0.000443,0.002121,0.000137,
|
||||||
-0.000046,-0.001205,0.003052,-0.019577,0.012115,-0.137299,-0.069168,-0.890091,1.048157,0.246506,0.152069,0.016708,0.027725,-0.000870,0.002014,0.000122,
|
-0.000046,-0.001205,0.003052,-0.019577,0.012115,-0.137299,-0.069168,-0.890091,1.048157,0.246506,0.152069,0.016708,0.027725,-0.000870,0.002014,0.000122,
|
||||||
-0.000061,-0.001297,0.002884,-0.020691,0.009232,-0.140671,-0.088776,-0.913055,1.031937,0.221985,0.151962,0.012421,0.026840,-0.001266,0.001907,0.000107,
|
-0.000061,-0.001297,0.002884,-0.020691,0.009232,-0.140671,-0.088776,-0.913055,1.031937,0.221985,0.151962,0.012421,0.026840,-0.001266,0.001907,0.000107,
|
||||||
-0.000061,-0.001389,0.002701,-0.021790,0.006134,-0.143677,-0.109161,-0.935196,1.014618,0.198059,0.151306,0.008316,0.025909,-0.001617,0.001785,0.000107,
|
-0.000061,-0.001389,0.002701,-0.021790,0.006134,-0.143677,-0.109161,-0.935196,1.014618,0.198059,0.151306,0.008316,0.025909,-0.001617,0.001785,0.000107,
|
||||||
-0.000076,-0.001480,0.002487,-0.022858,0.002823,-0.146255,-0.130310,-0.956482,0.996246,0.174789,0.150116,0.004395,0.024933,-0.001938,0.001694,0.000092,
|
-0.000076,-0.001480,0.002487,-0.022858,0.002823,-0.146255,-0.130310,-0.956482,0.996246,0.174789,0.150116,0.004395,0.024933,-0.001938,0.001694,0.000092,
|
||||||
-0.000076,-0.001587,0.002228,-0.023911,-0.000687,-0.148422,-0.152206,-0.976852,0.976852,0.152206,0.148422,0.000687,0.023911,-0.002228,0.001587,0.000076,
|
-0.000076,-0.001587,0.002228,-0.023911,-0.000687,-0.148422,-0.152206,-0.976852,0.976852,0.152206,0.148422,0.000687,0.023911,-0.002228,0.001587,0.000076,
|
||||||
-0.000092,-0.001694,0.001938,-0.024933,-0.004395,-0.150116,-0.174789,-0.996246,0.956482,0.130310,0.146255,-0.002823,0.022858,-0.002487,0.001480,0.000076,
|
-0.000092,-0.001694,0.001938,-0.024933,-0.004395,-0.150116,-0.174789,-0.996246,0.956482,0.130310,0.146255,-0.002823,0.022858,-0.002487,0.001480,0.000076,
|
||||||
-0.000107,-0.001785,0.001617,-0.025909,-0.008316,-0.151306,-0.198059,-1.014618,0.935196,0.109161,0.143677,-0.006134,0.021790,-0.002701,0.001389,0.000061,
|
-0.000107,-0.001785,0.001617,-0.025909,-0.008316,-0.151306,-0.198059,-1.014618,0.935196,0.109161,0.143677,-0.006134,0.021790,-0.002701,0.001389,0.000061,
|
||||||
-0.000107,-0.001907,0.001266,-0.026840,-0.012421,-0.151962,-0.221985,-1.031937,0.913055,0.088776,0.140671,-0.009232,0.020691,-0.002884,0.001297,0.000061,
|
-0.000107,-0.001907,0.001266,-0.026840,-0.012421,-0.151962,-0.221985,-1.031937,0.913055,0.088776,0.140671,-0.009232,0.020691,-0.002884,0.001297,0.000061,
|
||||||
-0.000122,-0.002014,0.000870,-0.027725,-0.016708,-0.152069,-0.246506,-1.048157,0.890091,0.069168,0.137299,-0.012115,0.019577,-0.003052,0.001205,0.000046,
|
-0.000122,-0.002014,0.000870,-0.027725,-0.016708,-0.152069,-0.246506,-1.048157,0.890091,0.069168,0.137299,-0.012115,0.019577,-0.003052,0.001205,0.000046,
|
||||||
-0.000137,-0.002121,0.000443,-0.028534,-0.021179,-0.151596,-0.271591,-1.063217,0.866364,0.050354,0.133591,-0.014801,0.018463,-0.003174,0.001114,0.000046,
|
-0.000137,-0.002121,0.000443,-0.028534,-0.021179,-0.151596,-0.271591,-1.063217,0.866364,0.050354,0.133591,-0.014801,0.018463,-0.003174,0.001114,0.000046,
|
||||||
-0.000153,-0.002243,-0.000031,-0.029282,-0.025818,-0.150497,-0.297211,-1.077118,0.841949,0.032379,0.129562,-0.017258,0.017349,-0.003281,0.001038,0.000031,
|
-0.000153,-0.002243,-0.000031,-0.029282,-0.025818,-0.150497,-0.297211,-1.077118,0.841949,0.032379,0.129562,-0.017258,0.017349,-0.003281,0.001038,0.000031,
|
||||||
-0.000168,-0.002350,-0.000549,-0.029938,-0.030609,-0.148773,-0.323318,-1.089783,0.816864,0.015228,0.125259,-0.019531,0.016235,-0.003372,0.000961,0.000031,
|
-0.000168,-0.002350,-0.000549,-0.029938,-0.030609,-0.148773,-0.323318,-1.089783,0.816864,0.015228,0.125259,-0.019531,0.016235,-0.003372,0.000961,0.000031,
|
||||||
-0.000198,-0.002457,-0.001099,-0.030533,-0.035553,-0.146362,-0.349869,-1.101212,0.791214,-0.001068,0.120697,-0.021576,0.015121,-0.003418,0.000885,0.000031,
|
-0.000198,-0.002457,-0.001099,-0.030533,-0.035553,-0.146362,-0.349869,-1.101212,0.791214,-0.001068,0.120697,-0.021576,0.015121,-0.003418,0.000885,0.000031,
|
||||||
-0.000214,-0.002579,-0.001694,-0.031006,-0.040634,-0.143265,-0.376801,-1.111374,0.765030,-0.016510,0.115921,-0.023422,0.014023,-0.003464,0.000809,0.000031,
|
-0.000214,-0.002579,-0.001694,-0.031006,-0.040634,-0.143265,-0.376801,-1.111374,0.765030,-0.016510,0.115921,-0.023422,0.014023,-0.003464,0.000809,0.000031,
|
||||||
-0.000244,-0.002686,-0.002335,-0.031387,-0.045837,-0.139450,-0.404083,-1.120224,0.738373,-0.031082,0.110947,-0.025085,0.012939,-0.003479,0.000748,0.000015,
|
-0.000244,-0.002686,-0.002335,-0.031387,-0.045837,-0.139450,-0.404083,-1.120224,0.738373,-0.031082,0.110947,-0.025085,0.012939,-0.003479,0.000748,0.000015,
|
||||||
-0.000259,-0.002792,-0.003006,-0.031662,-0.051132,-0.134888,-0.431656,-1.127747,0.711319,-0.044785,0.105820,-0.026535,0.011887,-0.003479,0.000687,0.000015,
|
-0.000259,-0.002792,-0.003006,-0.031662,-0.051132,-0.134888,-0.431656,-1.127747,0.711319,-0.044785,0.105820,-0.026535,0.011887,-0.003479,0.000687,0.000015,
|
||||||
-0.000290,-0.002899,-0.003723,-0.031815,-0.056534,-0.129578,-0.459473,-1.133926,0.683914,-0.057617,0.100540,-0.027802,0.010849,-0.003464,0.000626,0.000015,
|
-0.000290,-0.002899,-0.003723,-0.031815,-0.056534,-0.129578,-0.459473,-1.133926,0.683914,-0.057617,0.100540,-0.027802,0.010849,-0.003464,0.000626,0.000015,
|
||||||
-0.000320,-0.002991,-0.004486,-0.031845,-0.061996,-0.123474,-0.487473,-1.138763,0.656219,-0.069595,0.095169,-0.028885,0.009842,-0.003433,0.000580,0.000015,
|
-0.000320,-0.002991,-0.004486,-0.031845,-0.061996,-0.123474,-0.487473,-1.138763,0.656219,-0.069595,0.095169,-0.028885,0.009842,-0.003433,0.000580,0.000015,
|
||||||
-0.000366,-0.003082,-0.005295,-0.031738,-0.067520,-0.116577,-0.515610,-1.142212,0.628296,-0.080688,0.089706,-0.029785,0.008865,-0.003387,0.000534,0.000015,
|
-0.000366,-0.003082,-0.005295,-0.031738,-0.067520,-0.116577,-0.515610,-1.142212,0.628296,-0.080688,0.089706,-0.029785,0.008865,-0.003387,0.000534,0.000015,
|
||||||
-0.000397,-0.003174,-0.006119,-0.031479,-0.073059,-0.108856,-0.543823,-1.144287,0.600220,-0.090927,0.084183,-0.030518,0.007919,-0.003326,0.000473,0.000015,
|
-0.000397,-0.003174,-0.006119,-0.031479,-0.073059,-0.108856,-0.543823,-1.144287,0.600220,-0.090927,0.084183,-0.030518,0.007919,-0.003326,0.000473,0.000015,
|
||||||
};
|
};
|
||||||
|
|
||||||
// filterTbl[i][j] = cos(((M_PI/64*i+M_PI/4)*(2*j+1)));
|
// filterTbl[i][j] = cos(((M_PI/64*i+M_PI/4)*(2*j+1)));
|
||||||
static const double s_jo_filterTbl[64][32] = {
|
static const double s_jo_filterTbl[64][32] = {
|
||||||
{0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,
|
{0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,
|
||||||
0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107},
|
0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107},
|
||||||
{0.671559,-0.803208,-0.514103,0.903989,0.336890,-0.970031,-0.146730,0.998795,-0.049068,-0.989177,0.242980,0.941544,-0.427555,-0.857729,0.595699,0.740951,
|
{0.671559,-0.803208,-0.514103,0.903989,0.336890,-0.970031,-0.146730,0.998795,-0.049068,-0.989177,0.242980,0.941544,-0.427555,-0.857729,0.595699,0.740951,
|
||||||
-0.740951,-0.595699,0.857729,0.427555,-0.941544,-0.242980,0.989177,0.049068,-0.998795,0.146730,0.970031,-0.336890,-0.903989,0.514103,0.803208,-0.671559},
|
-0.740951,-0.595699,0.857729,0.427555,-0.941544,-0.242980,0.989177,0.049068,-0.998795,0.146730,0.970031,-0.336890,-0.903989,0.514103,0.803208,-0.671559},
|
||||||
{0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393,
|
{0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393,
|
||||||
-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393},
|
-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393},
|
||||||
{0.595699,-0.941544,-0.049068,0.970031,-0.514103,-0.671559,0.903989,0.146730,-0.989177,0.427555,0.740951,-0.857729,-0.242980,0.998795,-0.336890,-0.803208,
|
{0.595699,-0.941544,-0.049068,0.970031,-0.514103,-0.671559,0.903989,0.146730,-0.989177,0.427555,0.740951,-0.857729,-0.242980,0.998795,-0.336890,-0.803208,
|
||||||
0.803208,0.336890,-0.998795,0.242980,0.857729,-0.740951,-0.427555,0.989177,-0.146730,-0.903989,0.671559,0.514103,-0.970031,0.049068,0.941544,-0.595699},
|
0.803208,0.336890,-0.998795,0.242980,0.857729,-0.740951,-0.427555,0.989177,-0.146730,-0.903989,0.671559,0.514103,-0.970031,0.049068,0.941544,-0.595699},
|
||||||
{0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,
|
{0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,
|
||||||
0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570},
|
0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570},
|
||||||
{0.514103,-0.998795,0.427555,0.595699,-0.989177,0.336890,0.671559,-0.970031,0.242980,0.740951,-0.941544,0.146730,0.803208,-0.903989,0.049068,0.857729,
|
{0.514103,-0.998795,0.427555,0.595699,-0.989177,0.336890,0.671559,-0.970031,0.242980,0.740951,-0.941544,0.146730,0.803208,-0.903989,0.049068,0.857729,
|
||||||
-0.857729,-0.049068,0.903989,-0.803208,-0.146730,0.941544,-0.740951,-0.242980,0.970031,-0.671559,-0.336890,0.989177,-0.595699,-0.427555,0.998795,-0.514103},
|
-0.857729,-0.049068,0.903989,-0.803208,-0.146730,0.941544,-0.740951,-0.242980,0.970031,-0.671559,-0.336890,0.989177,-0.595699,-0.427555,0.998795,-0.514103},
|
||||||
{0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397,
|
{0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397,
|
||||||
-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397},
|
-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397},
|
||||||
{0.427555,-0.970031,0.803208,-0.049068,-0.740951,0.989177,-0.514103,-0.336890,0.941544,-0.857729,0.146730,0.671559,-0.998795,0.595699,0.242980,-0.903989,
|
{0.427555,-0.970031,0.803208,-0.049068,-0.740951,0.989177,-0.514103,-0.336890,0.941544,-0.857729,0.146730,0.671559,-0.998795,0.595699,0.242980,-0.903989,
|
||||||
0.903989,-0.242980,-0.595699,0.998795,-0.671559,-0.146730,0.857729,-0.941544,0.336890,0.514103,-0.989177,0.740951,0.049068,-0.803208,0.970031,-0.427555},
|
0.903989,-0.242980,-0.595699,0.998795,-0.671559,-0.146730,0.857729,-0.941544,0.336890,0.514103,-0.989177,0.740951,0.049068,-0.803208,0.970031,-0.427555},
|
||||||
{0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,
|
{0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,
|
||||||
0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683},
|
0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683},
|
||||||
{0.336890,-0.857729,0.989177,-0.671559,0.049068,0.595699,-0.970031,0.903989,-0.427555,-0.242980,0.803208,-0.998795,0.740951,-0.146730,-0.514103,0.941544,
|
{0.336890,-0.857729,0.989177,-0.671559,0.049068,0.595699,-0.970031,0.903989,-0.427555,-0.242980,0.803208,-0.998795,0.740951,-0.146730,-0.514103,0.941544,
|
||||||
-0.941544,0.514103,0.146730,-0.740951,0.998795,-0.803208,0.242980,0.427555,-0.903989,0.970031,-0.595699,-0.049068,0.671559,-0.989177,0.857729,-0.336890},
|
-0.941544,0.514103,0.146730,-0.740951,0.998795,-0.803208,0.242980,0.427555,-0.903989,0.970031,-0.595699,-0.049068,0.671559,-0.989177,0.857729,-0.336890},
|
||||||
{0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285,
|
{0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285,
|
||||||
-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285},
|
-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285},
|
||||||
{0.242980,-0.671559,0.941544,-0.989177,0.803208,-0.427555,-0.049068,0.514103,-0.857729,0.998795,-0.903989,0.595699,-0.146730,-0.336890,0.740951,-0.970031,
|
{0.242980,-0.671559,0.941544,-0.989177,0.803208,-0.427555,-0.049068,0.514103,-0.857729,0.998795,-0.903989,0.595699,-0.146730,-0.336890,0.740951,-0.970031,
|
||||||
0.970031,-0.740951,0.336890,0.146730,-0.595699,0.903989,-0.998795,0.857729,-0.514103,0.049068,0.427555,-0.803208,0.989177,-0.941544,0.671559,-0.242980},
|
0.970031,-0.740951,0.336890,0.146730,-0.595699,0.903989,-0.998795,0.857729,-0.514103,0.049068,0.427555,-0.803208,0.989177,-0.941544,0.671559,-0.242980},
|
||||||
{0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,
|
{0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,
|
||||||
0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090},
|
0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090},
|
||||||
{0.146730,-0.427555,0.671559,-0.857729,0.970031,-0.998795,0.941544,-0.803208,0.595699,-0.336890,0.049068,0.242980,-0.514103,0.740951,-0.903989,0.989177,
|
{0.146730,-0.427555,0.671559,-0.857729,0.970031,-0.998795,0.941544,-0.803208,0.595699,-0.336890,0.049068,0.242980,-0.514103,0.740951,-0.903989,0.989177,
|
||||||
-0.989177,0.903989,-0.740951,0.514103,-0.242980,-0.049068,0.336890,-0.595699,0.803208,-0.941544,0.998795,-0.970031,0.857729,-0.671559,0.427555,-0.146730},
|
-0.989177,0.903989,-0.740951,0.514103,-0.242980,-0.049068,0.336890,-0.595699,0.803208,-0.941544,0.998795,-0.970031,0.857729,-0.671559,0.427555,-0.146730},
|
||||||
{0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017,
|
{0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017,
|
||||||
-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017},
|
-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017},
|
||||||
{0.049068,-0.146730,0.242980,-0.336890,0.427555,-0.514103,0.595699,-0.671559,0.740951,-0.803208,0.857729,-0.903989,0.941544,-0.970031,0.989177,-0.998795,
|
{0.049068,-0.146730,0.242980,-0.336890,0.427555,-0.514103,0.595699,-0.671559,0.740951,-0.803208,0.857729,-0.903989,0.941544,-0.970031,0.989177,-0.998795,
|
||||||
0.998795,-0.989177,0.970031,-0.941544,0.903989,-0.857729,0.803208,-0.740951,0.671559,-0.595699,0.514103,-0.427555,0.336890,-0.242980,0.146730,-0.049068},
|
0.998795,-0.989177,0.970031,-0.941544,0.903989,-0.857729,0.803208,-0.740951,0.671559,-0.595699,0.514103,-0.427555,0.336890,-0.242980,0.146730,-0.049068},
|
||||||
{0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,
|
{0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,
|
||||||
0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000},
|
0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000},
|
||||||
{-0.049068,0.146730,-0.242980,0.336890,-0.427555,0.514103,-0.595699,0.671559,-0.740951,0.803208,-0.857729,0.903989,-0.941544,0.970031,-0.989177,0.998795,
|
{-0.049068,0.146730,-0.242980,0.336890,-0.427555,0.514103,-0.595699,0.671559,-0.740951,0.803208,-0.857729,0.903989,-0.941544,0.970031,-0.989177,0.998795,
|
||||||
-0.998795,0.989177,-0.970031,0.941544,-0.903989,0.857729,-0.803208,0.740951,-0.671559,0.595699,-0.514103,0.427555,-0.336890,0.242980,-0.146730,0.049068},
|
-0.998795,0.989177,-0.970031,0.941544,-0.903989,0.857729,-0.803208,0.740951,-0.671559,0.595699,-0.514103,0.427555,-0.336890,0.242980,-0.146730,0.049068},
|
||||||
{-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017,
|
{-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017,
|
||||||
0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017},
|
0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017},
|
||||||
{-0.146730,0.427555,-0.671559,0.857729,-0.970031,0.998795,-0.941544,0.803208,-0.595699,0.336890,-0.049068,-0.242980,0.514103,-0.740951,0.903989,-0.989177,
|
{-0.146730,0.427555,-0.671559,0.857729,-0.970031,0.998795,-0.941544,0.803208,-0.595699,0.336890,-0.049068,-0.242980,0.514103,-0.740951,0.903989,-0.989177,
|
||||||
0.989177,-0.903989,0.740951,-0.514103,0.242980,0.049068,-0.336890,0.595699,-0.803208,0.941544,-0.998795,0.970031,-0.857729,0.671559,-0.427555,0.146730},
|
0.989177,-0.903989,0.740951,-0.514103,0.242980,0.049068,-0.336890,0.595699,-0.803208,0.941544,-0.998795,0.970031,-0.857729,0.671559,-0.427555,0.146730},
|
||||||
{-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,
|
{-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,
|
||||||
-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090},
|
-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090},
|
||||||
{-0.242980,0.671559,-0.941544,0.989177,-0.803208,0.427555,0.049068,-0.514103,0.857729,-0.998795,0.903989,-0.595699,0.146730,0.336890,-0.740951,0.970031,
|
{-0.242980,0.671559,-0.941544,0.989177,-0.803208,0.427555,0.049068,-0.514103,0.857729,-0.998795,0.903989,-0.595699,0.146730,0.336890,-0.740951,0.970031,
|
||||||
-0.970031,0.740951,-0.336890,-0.146730,0.595699,-0.903989,0.998795,-0.857729,0.514103,-0.049068,-0.427555,0.803208,-0.989177,0.941544,-0.671559,0.242980},
|
-0.970031,0.740951,-0.336890,-0.146730,0.595699,-0.903989,0.998795,-0.857729,0.514103,-0.049068,-0.427555,0.803208,-0.989177,0.941544,-0.671559,0.242980},
|
||||||
{-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285,
|
{-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285,
|
||||||
0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285},
|
0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285},
|
||||||
{-0.336890,0.857729,-0.989177,0.671559,-0.049068,-0.595699,0.970031,-0.903989,0.427555,0.242980,-0.803208,0.998795,-0.740951,0.146730,0.514103,-0.941544,
|
{-0.336890,0.857729,-0.989177,0.671559,-0.049068,-0.595699,0.970031,-0.903989,0.427555,0.242980,-0.803208,0.998795,-0.740951,0.146730,0.514103,-0.941544,
|
||||||
0.941544,-0.514103,-0.146730,0.740951,-0.998795,0.803208,-0.242980,-0.427555,0.903989,-0.970031,0.595699,0.049068,-0.671559,0.989177,-0.857729,0.336890},
|
0.941544,-0.514103,-0.146730,0.740951,-0.998795,0.803208,-0.242980,-0.427555,0.903989,-0.970031,0.595699,0.049068,-0.671559,0.989177,-0.857729,0.336890},
|
||||||
{-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,
|
{-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,
|
||||||
-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683},
|
-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683},
|
||||||
{-0.427555,0.970031,-0.803208,0.049068,0.740951,-0.989177,0.514103,0.336890,-0.941544,0.857729,-0.146730,-0.671559,0.998795,-0.595699,-0.242980,0.903989,
|
{-0.427555,0.970031,-0.803208,0.049068,0.740951,-0.989177,0.514103,0.336890,-0.941544,0.857729,-0.146730,-0.671559,0.998795,-0.595699,-0.242980,0.903989,
|
||||||
-0.903989,0.242980,0.595699,-0.998795,0.671559,0.146730,-0.857729,0.941544,-0.336890,-0.514103,0.989177,-0.740951,-0.049068,0.803208,-0.970031,0.427555},
|
-0.903989,0.242980,0.595699,-0.998795,0.671559,0.146730,-0.857729,0.941544,-0.336890,-0.514103,0.989177,-0.740951,-0.049068,0.803208,-0.970031,0.427555},
|
||||||
{-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397,
|
{-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397,
|
||||||
0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397},
|
0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397},
|
||||||
{-0.514103,0.998795,-0.427555,-0.595699,0.989177,-0.336890,-0.671559,0.970031,-0.242980,-0.740951,0.941544,-0.146730,-0.803208,0.903989,-0.049068,-0.857729,
|
{-0.514103,0.998795,-0.427555,-0.595699,0.989177,-0.336890,-0.671559,0.970031,-0.242980,-0.740951,0.941544,-0.146730,-0.803208,0.903989,-0.049068,-0.857729,
|
||||||
0.857729,0.049068,-0.903989,0.803208,0.146730,-0.941544,0.740951,0.242980,-0.970031,0.671559,0.336890,-0.989177,0.595699,0.427555,-0.998795,0.514103},
|
0.857729,0.049068,-0.903989,0.803208,0.146730,-0.941544,0.740951,0.242980,-0.970031,0.671559,0.336890,-0.989177,0.595699,0.427555,-0.998795,0.514103},
|
||||||
{-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,
|
{-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,
|
||||||
-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570},
|
-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570},
|
||||||
{-0.595699,0.941544,0.049068,-0.970031,0.514103,0.671559,-0.903989,-0.146730,0.989177,-0.427555,-0.740951,0.857729,0.242980,-0.998795,0.336890,0.803208,
|
{-0.595699,0.941544,0.049068,-0.970031,0.514103,0.671559,-0.903989,-0.146730,0.989177,-0.427555,-0.740951,0.857729,0.242980,-0.998795,0.336890,0.803208,
|
||||||
-0.803208,-0.336890,0.998795,-0.242980,-0.857729,0.740951,0.427555,-0.989177,0.146730,0.903989,-0.671559,-0.514103,0.970031,-0.049068,-0.941544,0.595699},
|
-0.803208,-0.336890,0.998795,-0.242980,-0.857729,0.740951,0.427555,-0.989177,0.146730,0.903989,-0.671559,-0.514103,0.970031,-0.049068,-0.941544,0.595699},
|
||||||
{-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393,
|
{-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393,
|
||||||
0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393},
|
0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393},
|
||||||
{-0.671559,0.803208,0.514103,-0.903989,-0.336890,0.970031,0.146730,-0.998795,0.049068,0.989177,-0.242980,-0.941544,0.427555,0.857729,-0.595699,-0.740951,
|
{-0.671559,0.803208,0.514103,-0.903989,-0.336890,0.970031,0.146730,-0.998795,0.049068,0.989177,-0.242980,-0.941544,0.427555,0.857729,-0.595699,-0.740951,
|
||||||
0.740951,0.595699,-0.857729,-0.427555,0.941544,0.242980,-0.989177,-0.049068,0.998795,-0.146730,-0.970031,0.336890,0.903989,-0.514103,-0.803208,0.671559},
|
0.740951,0.595699,-0.857729,-0.427555,0.941544,0.242980,-0.989177,-0.049068,0.998795,-0.146730,-0.970031,0.336890,0.903989,-0.514103,-0.803208,0.671559},
|
||||||
{-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,
|
{-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,
|
||||||
-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107},
|
-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107},
|
||||||
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
|
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
|
||||||
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951},
|
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951},
|
||||||
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
|
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
|
||||||
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
|
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
|
||||||
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
|
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
|
||||||
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
|
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
|
||||||
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
|
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
|
||||||
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
|
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
|
||||||
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
|
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
|
||||||
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
|
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
|
||||||
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
|
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
|
||||||
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
|
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
|
||||||
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
|
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
|
||||||
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
|
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
|
||||||
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
|
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
|
||||||
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
|
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
|
||||||
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
|
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
|
||||||
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
|
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
|
||||||
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
|
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
|
||||||
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
|
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
|
||||||
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
|
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
|
||||||
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
|
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
|
||||||
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
|
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
|
||||||
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
|
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
|
||||||
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
|
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
|
||||||
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
|
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
|
||||||
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
|
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
|
||||||
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
|
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
|
||||||
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
|
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
|
||||||
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
|
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
|
||||||
{-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,
|
{-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,
|
||||||
-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000},
|
-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000},
|
||||||
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
|
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
|
||||||
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
|
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
|
||||||
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
|
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
|
||||||
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
|
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
|
||||||
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
|
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
|
||||||
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
|
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
|
||||||
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
|
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
|
||||||
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
|
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
|
||||||
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
|
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
|
||||||
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
|
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
|
||||||
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
|
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
|
||||||
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
|
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
|
||||||
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
|
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
|
||||||
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
|
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
|
||||||
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
|
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
|
||||||
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
|
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
|
||||||
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
|
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
|
||||||
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
|
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
|
||||||
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
|
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
|
||||||
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
|
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
|
||||||
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
|
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
|
||||||
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
|
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
|
||||||
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
|
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
|
||||||
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
|
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
|
||||||
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
|
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
|
||||||
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
|
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
|
||||||
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
|
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
|
||||||
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
|
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
|
||||||
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
|
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
|
||||||
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951}
|
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951}
|
||||||
};
|
};
|
||||||
|
|
||||||
// up to 32-bits
|
// up to 32-bits
|
||||||
static unsigned jo_readBits(const unsigned char *data, int *at, int num) {
|
static unsigned jo_readBits(const unsigned char *data, int *at, int num) {
|
||||||
unsigned r = 0;
|
unsigned r = 0;
|
||||||
// read partial starting bits
|
// read partial starting bits
|
||||||
int sc = (8 - (*at & 7)) & 7;
|
int sc = (8 - (*at & 7)) & 7;
|
||||||
sc = sc > num ? num : sc;
|
sc = sc > num ? num : sc;
|
||||||
if(sc) {
|
if(sc) {
|
||||||
r = (data[*at/8] >> (8 - (*at&7) - sc)) & ((1<<sc)-1);
|
r = (data[*at/8] >> (8 - (*at&7) - sc)) & ((1<<sc)-1);
|
||||||
num -= sc;
|
num -= sc;
|
||||||
*at += sc;
|
*at += sc;
|
||||||
}
|
}
|
||||||
// read full bytes
|
// read full bytes
|
||||||
while(num>=8) {
|
while(num>=8) {
|
||||||
r <<= 8;
|
r <<= 8;
|
||||||
r |= data[*at/8];
|
r |= data[*at/8];
|
||||||
*at += 8;
|
*at += 8;
|
||||||
num -= 8;
|
num -= 8;
|
||||||
}
|
}
|
||||||
// read partial ending bits
|
// read partial ending bits
|
||||||
if(num) {
|
if(num) {
|
||||||
r <<= num;
|
r <<= num;
|
||||||
r |= (data[*at/8] >> (8 - num)) & ((1<<num)-1);
|
r |= (data[*at/8] >> (8 - num)) & ((1<<num)-1);
|
||||||
*at += num;
|
*at += num;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool jo_read_mp1(const void *input, int inputSize, short **output_, int *outputSize_, int *hz_, int *channels_) {
|
bool jo_read_mp1(const void *input, int inputSize, short **output_, int *outputSize_, int *hz_, int *channels_) {
|
||||||
int outputSize = 0, hz, channels;
|
int outputSize = 0, hz, channels;
|
||||||
short *output = 0;
|
short *output = 0;
|
||||||
int outputMax = 0;
|
int outputMax = 0;
|
||||||
const unsigned char *data = (const unsigned char *)input;
|
const unsigned char *data = (const unsigned char *)input;
|
||||||
|
|
||||||
// Buffers for the lapped transform
|
// Buffers for the lapped transform
|
||||||
double buf[2][2 * 512] = { 0 };
|
double buf[2][2 * 512] = { 0 };
|
||||||
int bufOffset[2] = { 64,64 };
|
int bufOffset[2] = { 64,64 };
|
||||||
int at = 0; // Where in the stream are we?
|
int at = 0; // Where in the stream are we?
|
||||||
while (at < inputSize * 8) {
|
while (at < inputSize * 8) {
|
||||||
// Sync markers are byte aligned
|
// Sync markers are byte aligned
|
||||||
at = (at + 7)&-8;
|
at = (at + 7)&-8;
|
||||||
while (at < inputSize * 8 - 32) {
|
while (at < inputSize * 8 - 32) {
|
||||||
if (data[at / 8] == 0xFF && (data[at / 8 + 1] & 0xF0) == 0xF0) {
|
if (data[at / 8] == 0xFF && (data[at / 8 + 1] & 0xF0) == 0xF0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
at += 8;
|
at += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (at >= inputSize * 8 - 32) {
|
if (at >= inputSize * 8 - 32) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned header = jo_readBits(data, &at, 32);
|
unsigned header = jo_readBits(data, &at, 32);
|
||||||
//printf("header: %x.%x/%x: %08x\n", (at-32)/8, (at-32)&7, inputSize, header);
|
//printf("header: %x.%x/%x: %08x\n", (at-32)/8, (at-32)&7, inputSize, header);
|
||||||
|
|
||||||
// sync = 0xFFF
|
// sync = 0xFFF
|
||||||
// ID = 1
|
// ID = 1
|
||||||
// layer = 3 (layer 1)
|
// layer = 3 (layer 1)
|
||||||
if ((header & 0xFFFE0000) != 0xFFFE0000) {
|
if ((header & 0xFFFE0000) != 0xFFFE0000) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int bitrateTbl[16] = { 0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1 };
|
static const int bitrateTbl[16] = { 0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1 };
|
||||||
int kbps = bitrateTbl[(header >> 12) & 15];
|
int kbps = bitrateTbl[(header >> 12) & 15];
|
||||||
if (kbps < 0) {
|
if (kbps < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static const int hzTbl[4] = { 44100, 48000, 32000, 0 };
|
static const int hzTbl[4] = { 44100, 48000, 32000, 0 };
|
||||||
hz = hzTbl[(header >> 10) & 3];
|
hz = hzTbl[(header >> 10) & 3];
|
||||||
if (!hz) {
|
if (!hz) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mode 0 = stereo
|
// mode 0 = stereo
|
||||||
// mode 1 = joint stereo
|
// mode 1 = joint stereo
|
||||||
// mode 2 = dual channel (no idea what this does TODO)
|
// mode 2 = dual channel (no idea what this does TODO)
|
||||||
// mode 3 = mono
|
// mode 3 = mono
|
||||||
int mode = (header >> 6) & 3;
|
int mode = (header >> 6) & 3;
|
||||||
int modeExt = (header >> 4) & 3;
|
int modeExt = (header >> 4) & 3;
|
||||||
channels = mode == 3 ? 1 : 2;
|
channels = mode == 3 ? 1 : 2;
|
||||||
const int bound = mode == 1 ? (modeExt + 1) * 4 : 32;
|
const int bound = mode == 1 ? (modeExt + 1) * 4 : 32;
|
||||||
|
|
||||||
bool errorProtection = ((header >> 16) & 1) ^ 1; //< @r-lyeh extra parens
|
bool errorProtection = ((header >> 16) & 1) ^ 1; //< @r-lyeh extra parens
|
||||||
if (errorProtection) {
|
if (errorProtection) {
|
||||||
at += 16; // skip the CRC.
|
at += 16; // skip the CRC.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read bit allocations
|
// Read bit allocations
|
||||||
int bitAlloc[32][2] = { 0 };
|
int bitAlloc[32][2] = { 0 };
|
||||||
for (int i = 0; i < bound; ++i) {
|
for (int i = 0; i < bound; ++i) {
|
||||||
for (int ch = 0; ch < channels; ++ch) {
|
for (int ch = 0; ch < channels; ++ch) {
|
||||||
bitAlloc[i][ch] = jo_readBits(data, &at, 4);
|
bitAlloc[i][ch] = jo_readBits(data, &at, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = bound; i < 32; ++i) {
|
for (int i = bound; i < 32; ++i) {
|
||||||
bitAlloc[i][1] = bitAlloc[i][0] = jo_readBits(data, &at, 4);
|
bitAlloc[i][1] = bitAlloc[i][0] = jo_readBits(data, &at, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read scale indexes
|
// Read scale indexes
|
||||||
int scaleIdx[32][2];
|
int scaleIdx[32][2];
|
||||||
for (int i = 0; i < 32; ++i) {
|
for (int i = 0; i < 32; ++i) {
|
||||||
for (int ch = 0; ch < channels; ++ch) {
|
for (int ch = 0; ch < channels; ++ch) {
|
||||||
scaleIdx[i][ch] = bitAlloc[i][ch] ? jo_readBits(data, &at, 6) : 63;
|
scaleIdx[i][ch] = bitAlloc[i][ch] ? jo_readBits(data, &at, 6) : 63;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read & compute output samples
|
// Read & compute output samples
|
||||||
short pcm[12][2][32];
|
short pcm[12][2][32];
|
||||||
for (int s = 0; s < 12; ++s) {
|
for (int s = 0; s < 12; ++s) {
|
||||||
// Read normalized, quantized band samples
|
// Read normalized, quantized band samples
|
||||||
int samples[32][2] = { 0 };
|
int samples[32][2] = { 0 };
|
||||||
for (int i = 0; i < bound; ++i) {
|
for (int i = 0; i < bound; ++i) {
|
||||||
for (int ch = 0; ch < channels; ++ch) {
|
for (int ch = 0; ch < channels; ++ch) {
|
||||||
if (bitAlloc[i][ch]) {
|
if (bitAlloc[i][ch]) {
|
||||||
samples[i][ch] = jo_readBits(data, &at, bitAlloc[i][ch] + 1);
|
samples[i][ch] = jo_readBits(data, &at, bitAlloc[i][ch] + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = bound; i < 32; ++i) {
|
for (int i = bound; i < 32; ++i) {
|
||||||
if (bitAlloc[i][0]) {
|
if (bitAlloc[i][0]) {
|
||||||
samples[i][1] = samples[i][0] = jo_readBits(data, &at, bitAlloc[i][0] + 1);
|
samples[i][1] = samples[i][0] = jo_readBits(data, &at, bitAlloc[i][0] + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Compute bands: Dequantize & Denormalize
|
// Compute bands: Dequantize & Denormalize
|
||||||
double bandTbl[2][32] = { 0 };
|
double bandTbl[2][32] = { 0 };
|
||||||
for (int i = 0; i < 32; ++i) {
|
for (int i = 0; i < 32; ++i) {
|
||||||
for (int ch = 0; ch < channels; ++ch) {
|
for (int ch = 0; ch < channels; ++ch) {
|
||||||
int b = bitAlloc[i][ch];
|
int b = bitAlloc[i][ch];
|
||||||
if (b++) {
|
if (b++) {
|
||||||
int samp = samples[i][ch];
|
int samp = samples[i][ch];
|
||||||
double f = ((samp >> (b - 1)) & 1) ? 0 : -1;
|
double f = ((samp >> (b - 1)) & 1) ? 0 : -1;
|
||||||
f += (samp & ((1 << (b - 1)) - 1)) / (double)(1 << (b - 1));
|
f += (samp & ((1 << (b - 1)) - 1)) / (double)(1 << (b - 1));
|
||||||
f = (f + 1.0 / (1 << (b - 1))) * (1 << b) / ((1 << b) - 1.0);
|
f = (f + 1.0 / (1 << (b - 1))) * (1 << b) / ((1 << b) - 1.0);
|
||||||
f *= s_jo_multTbl[scaleIdx[i][ch]];
|
f *= s_jo_multTbl[scaleIdx[i][ch]];
|
||||||
bandTbl[ch][i] = f;
|
bandTbl[ch][i] = f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Convert subbands to PCM
|
// Convert subbands to PCM
|
||||||
for (int ch = 0; ch < channels; ++ch) {
|
for (int ch = 0; ch < channels; ++ch) {
|
||||||
bufOffset[ch] = (bufOffset[ch] + 0x3C0) & 0x3ff;
|
bufOffset[ch] = (bufOffset[ch] + 0x3C0) & 0x3ff;
|
||||||
double *bufOffsetPtr = buf[ch] + bufOffset[ch];
|
double *bufOffsetPtr = buf[ch] + bufOffset[ch];
|
||||||
const double *f = s_jo_filterTbl[0];
|
const double *f = s_jo_filterTbl[0];
|
||||||
for (int i = 0; i < 64; ++i) {
|
for (int i = 0; i < 64; ++i) {
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (int j = 0; j < 32; ++j) {
|
for (int j = 0; j < 32; ++j) {
|
||||||
sum += *f++ * bandTbl[ch][j];
|
sum += *f++ * bandTbl[ch][j];
|
||||||
}
|
}
|
||||||
bufOffsetPtr[i] = sum;
|
bufOffsetPtr[i] = sum;
|
||||||
}
|
}
|
||||||
const double *w = s_jo_windowTbl;
|
const double *w = s_jo_windowTbl;
|
||||||
for (int i = 0; i < 32; ++i) {
|
for (int i = 0; i < 32; ++i) {
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (int j = 0; j < 16; ++j) {
|
for (int j = 0; j < 16; ++j) {
|
||||||
int k = i | (j + (j + 1 & -2)) << 5;
|
int k = i | (j + (j + 1 & -2)) << 5;
|
||||||
sum += *w++ * buf[ch][(k + bufOffset[ch]) & 0x3ff];
|
sum += *w++ * buf[ch][(k + bufOffset[ch]) & 0x3ff];
|
||||||
}
|
}
|
||||||
int ss = (int)(sum * 0x8000);
|
int ss = (int)(sum * 0x8000);
|
||||||
ss = ss > SHRT_MAX ? SHRT_MAX : ss < SHRT_MIN ? SHRT_MIN : ss;
|
ss = ss > SHRT_MAX ? SHRT_MAX : ss < SHRT_MIN ? SHRT_MIN : ss;
|
||||||
pcm[s][ch][i] = ss;
|
pcm[s][ch][i] = ss;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (at > inputSize * 8) {
|
if (at > inputSize * 8) {
|
||||||
printf("file corruption?\n");
|
printf("file corruption?\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (outputMax == 0) {
|
if (outputMax == 0) {
|
||||||
// estimate total number of samples (may be totally wrong, but its better than nothing)
|
// estimate total number of samples (may be totally wrong, but its better than nothing)
|
||||||
at = (at + 7)&-8;
|
at = (at + 7)&-8;
|
||||||
outputMax = inputSize / (at / 8) * 384 * channels * sizeof(*output);
|
outputMax = inputSize / (at / 8) * 384 * channels * sizeof(*output);
|
||||||
output = (short*)REALLOC(output, outputMax);
|
output = (short*)REALLOC(output, outputMax);
|
||||||
}
|
}
|
||||||
if (outputSize * sizeof(*output) + 384 * channels * sizeof(*output) > outputMax) {
|
if (outputSize * sizeof(*output) + 384 * channels * sizeof(*output) > outputMax) {
|
||||||
outputMax += 384 * channels * sizeof(*output);
|
outputMax += 384 * channels * sizeof(*output);
|
||||||
output = (short*)REALLOC(output, outputMax);
|
output = (short*)REALLOC(output, outputMax);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 12; ++i) {
|
for (int i = 0; i < 12; ++i) {
|
||||||
for (int j = 0; j < 32; ++j) {
|
for (int j = 0; j < 32; ++j) {
|
||||||
for (int k = 0; k < channels; ++k) {
|
for (int k = 0; k < channels; ++k) {
|
||||||
output[outputSize++] = pcm[i][k][j];
|
output[outputSize++] = pcm[i][k][j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*outputSize_ = outputSize;
|
*outputSize_ = outputSize;
|
||||||
*hz_ = hz;
|
*hz_ = hz;
|
||||||
*channels_ = channels;
|
*channels_ = channels;
|
||||||
*output_ = output;
|
*output_ = output;
|
||||||
|
|
||||||
return outputSize && hz && channels && output;
|
return outputSize && hz && channels && output;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // JO_MP1_HEADER_FILE_ONLY
|
#endif // JO_MP1_HEADER_FILE_ONLY
|
||||||
|
|
||||||
|
|
|
@ -1,264 +1,264 @@
|
||||||
/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com
|
/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com
|
||||||
*
|
*
|
||||||
* Latest revisions:
|
* Latest revisions:
|
||||||
* 1.02c rgbx -> bgrx channel swap && vertical image flip && userdef components (@r-lyeh)
|
* 1.02c rgbx -> bgrx channel swap && vertical image flip && userdef components (@r-lyeh)
|
||||||
* 1.02 (22-03-2017) Fixed AC encoding bug.
|
* 1.02 (22-03-2017) Fixed AC encoding bug.
|
||||||
* Fixed color space bug (thx r- lyeh!)
|
* Fixed color space bug (thx r- lyeh!)
|
||||||
* 1.01 (18-10-2016) warning fixes
|
* 1.01 (18-10-2016) warning fixes
|
||||||
* 1.00 (25-09-2016) initial release
|
* 1.00 (25-09-2016) initial release
|
||||||
*
|
*
|
||||||
* Basic usage:
|
* Basic usage:
|
||||||
* char *frame = new char[width*height*4]; // 4 component. bgrx format, where X is unused
|
* char *frame = new char[width*height*4]; // 4 component. bgrx format, where X is unused
|
||||||
* FILE *fp = fopen("foo.mpg", "wb");
|
* FILE *fp = fopen("foo.mpg", "wb");
|
||||||
* jo_write_mpeg(fp, frame, width, height, 60); // frame 0
|
* jo_write_mpeg(fp, frame, width, height, 60); // frame 0
|
||||||
* jo_write_mpeg(fp, frame, width, height, 60); // frame 1
|
* jo_write_mpeg(fp, frame, width, height, 60); // frame 1
|
||||||
* jo_write_mpeg(fp, frame, width, height, 60); // frame 2
|
* jo_write_mpeg(fp, frame, width, height, 60); // frame 2
|
||||||
* ...
|
* ...
|
||||||
* fclose(fp);
|
* fclose(fp);
|
||||||
*
|
*
|
||||||
* Notes:
|
* Notes:
|
||||||
* Only supports 24, 25, 30, 50, or 60 fps
|
* Only supports 24, 25, 30, 50, or 60 fps
|
||||||
*
|
*
|
||||||
* I don't know if decoders support changing of fps, or dimensions for each frame.
|
* I don't know if decoders support changing of fps, or dimensions for each frame.
|
||||||
* Movie players *should* support it as the spec allows it, but ...
|
* Movie players *should* support it as the spec allows it, but ...
|
||||||
*
|
*
|
||||||
* MPEG-1/2 currently has no active patents as far as I am aware.
|
* MPEG-1/2 currently has no active patents as far as I am aware.
|
||||||
*
|
*
|
||||||
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
|
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
|
||||||
* http://www.cs.cornell.edu/dali/api/mpegvideo-c.html
|
* http://www.cs.cornell.edu/dali/api/mpegvideo-c.html
|
||||||
* */
|
* */
|
||||||
|
|
||||||
#ifndef JO_INCLUDE_MPEG_H
|
#ifndef JO_INCLUDE_MPEG_H
|
||||||
#define JO_INCLUDE_MPEG_H
|
#define JO_INCLUDE_MPEG_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
// To get a header file for this, either cut and paste the header,
|
// To get a header file for this, either cut and paste the header,
|
||||||
// or create jo_mpeg.h, #define JO_MPEG_HEADER_FILE_ONLY, and
|
// or create jo_mpeg.h, #define JO_MPEG_HEADER_FILE_ONLY, and
|
||||||
// then include jo_mpeg.c from it.
|
// then include jo_mpeg.c from it.
|
||||||
|
|
||||||
// Returns false on failure
|
// Returns false on failure
|
||||||
extern void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps);
|
extern void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps);
|
||||||
|
|
||||||
#endif // JO_INCLUDE_MPEG_H
|
#endif // JO_INCLUDE_MPEG_H
|
||||||
|
|
||||||
#ifndef JO_MPEG_HEADER_FILE_ONLY
|
#ifndef JO_MPEG_HEADER_FILE_ONLY
|
||||||
|
|
||||||
#ifndef JO_MPEG_COMPONENTS
|
#ifndef JO_MPEG_COMPONENTS
|
||||||
#define JO_MPEG_COMPONENTS 4
|
#define JO_MPEG_COMPONENTS 4
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
|
|
||||||
// Huffman tables
|
// Huffman tables
|
||||||
static const unsigned char s_jo_HTDC_Y[9][2] = {{4,3}, {0,2}, {1,2}, {5,3}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}};
|
static const unsigned char s_jo_HTDC_Y[9][2] = {{4,3}, {0,2}, {1,2}, {5,3}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}};
|
||||||
static const unsigned char s_jo_HTDC_C[9][2] = {{0,2}, {1,2}, {2,2}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}, {254,8}};
|
static const unsigned char s_jo_HTDC_C[9][2] = {{0,2}, {1,2}, {2,2}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}, {254,8}};
|
||||||
static const unsigned char s_jo_HTAC[32][40][2] = {
|
static const unsigned char s_jo_HTAC[32][40][2] = {
|
||||||
{{6,3},{8,5},{10,6},{12,8},{76,9},{66,9},{20,11},{58,13},{48,13},{38,13},{32,13},{52,14},{50,14},{48,14},{46,14},{62,15},{62,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,15},{32,15},{48,16},{46,16},{44,16},{42,16},{40,16},{38,16},{36,16},{34,16},{32,16},},
|
{{6,3},{8,5},{10,6},{12,8},{76,9},{66,9},{20,11},{58,13},{48,13},{38,13},{32,13},{52,14},{50,14},{48,14},{46,14},{62,15},{62,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,15},{32,15},{48,16},{46,16},{44,16},{42,16},{40,16},{38,16},{36,16},{34,16},{32,16},},
|
||||||
{{6,4},{12,7},{74,9},{24,11},{54,13},{44,14},{42,14},{62,16},{60,16},{58,16},{56,16},{54,16},{52,16},{50,16},{38,17},{36,17},{34,17},{32,17}},
|
{{6,4},{12,7},{74,9},{24,11},{54,13},{44,14},{42,14},{62,16},{60,16},{58,16},{56,16},{54,16},{52,16},{50,16},{38,17},{36,17},{34,17},{32,17}},
|
||||||
{{10,5},{8,8},{22,11},{40,13},{40,14}},
|
{{10,5},{8,8},{22,11},{40,13},{40,14}},
|
||||||
{{14,6},{72,9},{56,13},{38,14}},
|
{{14,6},{72,9},{56,13},{38,14}},
|
||||||
{{12,6},{30,11},{36,13}}, {{14,7},{18,11},{36,14}}, {{10,7},{60,13},{40,17}},
|
{{12,6},{30,11},{36,13}}, {{14,7},{18,11},{36,14}}, {{10,7},{60,13},{40,17}},
|
||||||
{{8,7},{42,13}}, {{14,8},{34,13}}, {{10,8},{34,14}}, {{78,9},{32,14}}, {{70,9},{52,17}}, {{68,9},{50,17}}, {{64,9},{48,17}}, {{28,11},{46,17}}, {{26,11},{44,17}}, {{16,11},{42,17}},
|
{{8,7},{42,13}}, {{14,8},{34,13}}, {{10,8},{34,14}}, {{78,9},{32,14}}, {{70,9},{52,17}}, {{68,9},{50,17}}, {{64,9},{48,17}}, {{28,11},{46,17}}, {{26,11},{44,17}}, {{16,11},{42,17}},
|
||||||
{{62,13}}, {{52,13}}, {{50,13}}, {{46,13}}, {{44,13}}, {{62,14}}, {{60,14}}, {{58,14}}, {{56,14}}, {{54,14}}, {{62,17}}, {{60,17}}, {{58,17}}, {{56,17}}, {{54,17}},
|
{{62,13}}, {{52,13}}, {{50,13}}, {{46,13}}, {{44,13}}, {{62,14}}, {{60,14}}, {{58,14}}, {{56,14}}, {{54,14}}, {{62,17}}, {{60,17}}, {{58,17}}, {{56,17}}, {{54,17}},
|
||||||
};
|
};
|
||||||
static const float s_jo_quantTbl[64] = {
|
static const float s_jo_quantTbl[64] = {
|
||||||
0.015625f,0.005632f,0.005035f,0.004832f,0.004808f,0.005892f,0.007964f,0.013325f,
|
0.015625f,0.005632f,0.005035f,0.004832f,0.004808f,0.005892f,0.007964f,0.013325f,
|
||||||
0.005632f,0.004061f,0.003135f,0.003193f,0.003338f,0.003955f,0.004898f,0.008828f,
|
0.005632f,0.004061f,0.003135f,0.003193f,0.003338f,0.003955f,0.004898f,0.008828f,
|
||||||
0.005035f,0.003135f,0.002816f,0.003013f,0.003299f,0.003581f,0.005199f,0.009125f,
|
0.005035f,0.003135f,0.002816f,0.003013f,0.003299f,0.003581f,0.005199f,0.009125f,
|
||||||
0.004832f,0.003484f,0.003129f,0.003348f,0.003666f,0.003979f,0.005309f,0.009632f,
|
0.004832f,0.003484f,0.003129f,0.003348f,0.003666f,0.003979f,0.005309f,0.009632f,
|
||||||
0.005682f,0.003466f,0.003543f,0.003666f,0.003906f,0.004546f,0.005774f,0.009439f,
|
0.005682f,0.003466f,0.003543f,0.003666f,0.003906f,0.004546f,0.005774f,0.009439f,
|
||||||
0.006119f,0.004248f,0.004199f,0.004228f,0.004546f,0.005062f,0.006124f,0.009942f,
|
0.006119f,0.004248f,0.004199f,0.004228f,0.004546f,0.005062f,0.006124f,0.009942f,
|
||||||
0.008883f,0.006167f,0.006096f,0.005777f,0.006078f,0.006391f,0.007621f,0.012133f,
|
0.008883f,0.006167f,0.006096f,0.005777f,0.006078f,0.006391f,0.007621f,0.012133f,
|
||||||
0.016780f,0.011263f,0.009907f,0.010139f,0.009849f,0.010297f,0.012133f,0.019785f,
|
0.016780f,0.011263f,0.009907f,0.010139f,0.009849f,0.010297f,0.012133f,0.019785f,
|
||||||
};
|
};
|
||||||
static const unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
|
static const unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int buf, cnt;
|
int buf, cnt;
|
||||||
} jo_bits_t;
|
} jo_bits_t;
|
||||||
|
|
||||||
static void jo_writeBits(jo_bits_t *b, int value, int count) {
|
static void jo_writeBits(jo_bits_t *b, int value, int count) {
|
||||||
b->cnt += count;
|
b->cnt += count;
|
||||||
b->buf |= value << (24 - b->cnt);
|
b->buf |= value << (24 - b->cnt);
|
||||||
while(b->cnt >= 8) {
|
while(b->cnt >= 8) {
|
||||||
unsigned char c = (b->buf >> 16) & 255;
|
unsigned char c = (b->buf >> 16) & 255;
|
||||||
putc(c, b->fp);
|
putc(c, b->fp);
|
||||||
b->buf <<= 8;
|
b->buf <<= 8;
|
||||||
b->cnt -= 8;
|
b->cnt -= 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jo_DCT(float *d0, float *d1, float *d2, float *d3, float *d4, float *d5, float *d6, float *d7) {
|
static void jo_DCT(float *d0, float *d1, float *d2, float *d3, float *d4, float *d5, float *d6, float *d7) {
|
||||||
float tmp0 = *d0 + *d7;
|
float tmp0 = *d0 + *d7;
|
||||||
float tmp7 = *d0 - *d7;
|
float tmp7 = *d0 - *d7;
|
||||||
float tmp1 = *d1 + *d6;
|
float tmp1 = *d1 + *d6;
|
||||||
float tmp6 = *d1 - *d6;
|
float tmp6 = *d1 - *d6;
|
||||||
float tmp2 = *d2 + *d5;
|
float tmp2 = *d2 + *d5;
|
||||||
float tmp5 = *d2 - *d5;
|
float tmp5 = *d2 - *d5;
|
||||||
float tmp3 = *d3 + *d4;
|
float tmp3 = *d3 + *d4;
|
||||||
float tmp4 = *d3 - *d4;
|
float tmp4 = *d3 - *d4;
|
||||||
|
|
||||||
// Even part
|
// Even part
|
||||||
float tmp10 = tmp0 + tmp3; // phase 2
|
float tmp10 = tmp0 + tmp3; // phase 2
|
||||||
float tmp13 = tmp0 - tmp3;
|
float tmp13 = tmp0 - tmp3;
|
||||||
float tmp11 = tmp1 + tmp2;
|
float tmp11 = tmp1 + tmp2;
|
||||||
float tmp12 = tmp1 - tmp2;
|
float tmp12 = tmp1 - tmp2;
|
||||||
|
|
||||||
*d0 = tmp10 + tmp11; // phase 3
|
*d0 = tmp10 + tmp11; // phase 3
|
||||||
*d4 = tmp10 - tmp11;
|
*d4 = tmp10 - tmp11;
|
||||||
|
|
||||||
float z1 = (tmp12 + tmp13) * 0.707106781f; // c4
|
float z1 = (tmp12 + tmp13) * 0.707106781f; // c4
|
||||||
*d2 = tmp13 + z1; // phase 5
|
*d2 = tmp13 + z1; // phase 5
|
||||||
*d6 = tmp13 - z1;
|
*d6 = tmp13 - z1;
|
||||||
|
|
||||||
// Odd part
|
// Odd part
|
||||||
tmp10 = tmp4 + tmp5; // phase 2
|
tmp10 = tmp4 + tmp5; // phase 2
|
||||||
tmp11 = tmp5 + tmp6;
|
tmp11 = tmp5 + tmp6;
|
||||||
tmp12 = tmp6 + tmp7;
|
tmp12 = tmp6 + tmp7;
|
||||||
|
|
||||||
// The rotator is modified from fig 4-8 to avoid extra negations.
|
// The rotator is modified from fig 4-8 to avoid extra negations.
|
||||||
float z5 = (tmp10 - tmp12) * 0.382683433f; // c6
|
float z5 = (tmp10 - tmp12) * 0.382683433f; // c6
|
||||||
float z2 = tmp10 * 0.541196100f + z5; // c2-c6
|
float z2 = tmp10 * 0.541196100f + z5; // c2-c6
|
||||||
float z4 = tmp12 * 1.306562965f + z5; // c2+c6
|
float z4 = tmp12 * 1.306562965f + z5; // c2+c6
|
||||||
float z3 = tmp11 * 0.707106781f; // c4
|
float z3 = tmp11 * 0.707106781f; // c4
|
||||||
|
|
||||||
float z11 = tmp7 + z3; // phase 5
|
float z11 = tmp7 + z3; // phase 5
|
||||||
float z13 = tmp7 - z3;
|
float z13 = tmp7 - z3;
|
||||||
|
|
||||||
*d5 = z13 + z2; // phase 6
|
*d5 = z13 + z2; // phase 6
|
||||||
*d3 = z13 - z2;
|
*d3 = z13 - z2;
|
||||||
*d1 = z11 + z4;
|
*d1 = z11 + z4;
|
||||||
*d7 = z11 - z4;
|
*d7 = z11 - z4;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9][2], int DC) {
|
static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9][2], int DC) {
|
||||||
for(int dataOff=0; dataOff<64; dataOff+=8) {
|
for(int dataOff=0; dataOff<64; dataOff+=8) {
|
||||||
jo_DCT(&A[dataOff], &A[dataOff+1], &A[dataOff+2], &A[dataOff+3], &A[dataOff+4], &A[dataOff+5], &A[dataOff+6], &A[dataOff+7]);
|
jo_DCT(&A[dataOff], &A[dataOff+1], &A[dataOff+2], &A[dataOff+3], &A[dataOff+4], &A[dataOff+5], &A[dataOff+6], &A[dataOff+7]);
|
||||||
}
|
}
|
||||||
for(int dataOff=0; dataOff<8; ++dataOff) {
|
for(int dataOff=0; dataOff<8; ++dataOff) {
|
||||||
jo_DCT(&A[dataOff], &A[dataOff+8], &A[dataOff+16], &A[dataOff+24], &A[dataOff+32], &A[dataOff+40], &A[dataOff+48], &A[dataOff+56]);
|
jo_DCT(&A[dataOff], &A[dataOff+8], &A[dataOff+16], &A[dataOff+24], &A[dataOff+32], &A[dataOff+40], &A[dataOff+48], &A[dataOff+56]);
|
||||||
}
|
}
|
||||||
int Q[64];
|
int Q[64];
|
||||||
for(int i=0; i<64; ++i) {
|
for(int i=0; i<64; ++i) {
|
||||||
float v = A[i]*s_jo_quantTbl[i];
|
float v = A[i]*s_jo_quantTbl[i];
|
||||||
Q[s_jo_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
|
Q[s_jo_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
DC = Q[0] - DC;
|
DC = Q[0] - DC;
|
||||||
int aDC = DC < 0 ? -DC : DC;
|
int aDC = DC < 0 ? -DC : DC;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
int tempval = aDC;
|
int tempval = aDC;
|
||||||
while(tempval) {
|
while(tempval) {
|
||||||
size++;
|
size++;
|
||||||
tempval >>= 1;
|
tempval >>= 1;
|
||||||
}
|
}
|
||||||
jo_writeBits(bits, htdc[size][0], htdc[size][1]);
|
jo_writeBits(bits, htdc[size][0], htdc[size][1]);
|
||||||
if(DC < 0) aDC ^= (1 << size) - 1;
|
if(DC < 0) aDC ^= (1 << size) - 1;
|
||||||
jo_writeBits(bits, aDC, size);
|
jo_writeBits(bits, aDC, size);
|
||||||
|
|
||||||
int endpos = 63;
|
int endpos = 63;
|
||||||
for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ }
|
for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ }
|
||||||
for(int i = 1; i <= endpos;) {
|
for(int i = 1; i <= endpos;) {
|
||||||
int run = 0;
|
int run = 0;
|
||||||
while (Q[i]==0 && i<endpos) {
|
while (Q[i]==0 && i<endpos) {
|
||||||
++run;
|
++run;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
int AC = Q[i++];
|
int AC = Q[i++];
|
||||||
int aAC = AC < 0 ? -AC : AC;
|
int aAC = AC < 0 ? -AC : AC;
|
||||||
int code = 0, size = 0;
|
int code = 0, size = 0;
|
||||||
if (run<32 && aAC<=40) {
|
if (run<32 && aAC<=40) {
|
||||||
code = s_jo_HTAC[run][aAC-1][0];
|
code = s_jo_HTAC[run][aAC-1][0];
|
||||||
size = s_jo_HTAC[run][aAC-1][1];
|
size = s_jo_HTAC[run][aAC-1][1];
|
||||||
if (AC < 0) code += 1;
|
if (AC < 0) code += 1;
|
||||||
}
|
}
|
||||||
if(!size) {
|
if(!size) {
|
||||||
jo_writeBits(bits, 1, 6);
|
jo_writeBits(bits, 1, 6);
|
||||||
jo_writeBits(bits, run, 6);
|
jo_writeBits(bits, run, 6);
|
||||||
if (AC < -127) {
|
if (AC < -127) {
|
||||||
jo_writeBits(bits, 128, 8);
|
jo_writeBits(bits, 128, 8);
|
||||||
} else if(AC > 127) {
|
} else if(AC > 127) {
|
||||||
jo_writeBits(bits, 0, 8);
|
jo_writeBits(bits, 0, 8);
|
||||||
}
|
}
|
||||||
code = AC&255;
|
code = AC&255;
|
||||||
size = 8;
|
size = 8;
|
||||||
}
|
}
|
||||||
jo_writeBits(bits, code, size);
|
jo_writeBits(bits, code, size);
|
||||||
}
|
}
|
||||||
jo_writeBits(bits, 2, 2);
|
jo_writeBits(bits, 2, 2);
|
||||||
|
|
||||||
return Q[0];
|
return Q[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps) {
|
void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps) {
|
||||||
int lastDCY = 128, lastDCCR = 128, lastDCCB = 128;
|
int lastDCY = 128, lastDCCR = 128, lastDCCB = 128;
|
||||||
jo_bits_t bits = {fp};
|
jo_bits_t bits = {fp};
|
||||||
|
|
||||||
// Sequence Header
|
// Sequence Header
|
||||||
fwrite("\x00\x00\x01\xB3", 4, 1, fp);
|
fwrite("\x00\x00\x01\xB3", 4, 1, fp);
|
||||||
// 12 bits for width, height
|
// 12 bits for width, height
|
||||||
putc((width>>4)&0xFF, fp);
|
putc((width>>4)&0xFF, fp);
|
||||||
putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp);
|
putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp);
|
||||||
putc(height & 0xFF, fp);
|
putc(height & 0xFF, fp);
|
||||||
// aspect ratio, framerate
|
// aspect ratio, framerate
|
||||||
if(fps <= 24) putc(0x12, fp);
|
if(fps <= 24) putc(0x12, fp);
|
||||||
else if(fps <= 25) putc(0x13, fp);
|
else if(fps <= 25) putc(0x13, fp);
|
||||||
else if(fps <= 30) putc(0x15, fp);
|
else if(fps <= 30) putc(0x15, fp);
|
||||||
else if(fps <= 50) putc(0x16, fp);
|
else if(fps <= 50) putc(0x16, fp);
|
||||||
else putc(0x18, fp); // 60fps
|
else putc(0x18, fp); // 60fps
|
||||||
fwrite("\xFF\xFF\xE0\xA0", 4, 1, fp);
|
fwrite("\xFF\xFF\xE0\xA0", 4, 1, fp);
|
||||||
|
|
||||||
fwrite("\x00\x00\x01\xB8\x80\x08\x00\x40", 8, 1, fp); // GOP header
|
fwrite("\x00\x00\x01\xB8\x80\x08\x00\x40", 8, 1, fp); // GOP header
|
||||||
fwrite("\x00\x00\x01\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header
|
fwrite("\x00\x00\x01\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header
|
||||||
fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header
|
fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header
|
||||||
jo_writeBits(&bits, 0x10, 6);
|
jo_writeBits(&bits, 0x10, 6);
|
||||||
|
|
||||||
for (int vblock=0; vblock<(height+15)/16; vblock++) {
|
for (int vblock=0; vblock<(height+15)/16; vblock++) {
|
||||||
for (int hblock=0; hblock<(width+15)/16; hblock++) {
|
for (int hblock=0; hblock<(width+15)/16; hblock++) {
|
||||||
jo_writeBits(&bits, 3, 2);
|
jo_writeBits(&bits, 3, 2);
|
||||||
|
|
||||||
float Y[256], CBx[256], CRx[256];
|
float Y[256], CBx[256], CRx[256];
|
||||||
for (int i=0; i<256; ++i) {
|
for (int i=0; i<256; ++i) {
|
||||||
int y = vblock*16+(i/16);
|
int y = vblock*16+(i/16);
|
||||||
int x = hblock*16+(i&15);
|
int x = hblock*16+(i&15);
|
||||||
x = x >= width ? width-1 : x;
|
x = x >= width ? width-1 : x;
|
||||||
y = y >= height ? height-1 : y;
|
y = y >= height ? height-1 : y;
|
||||||
int _4 = JO_MPEG_COMPONENTS;
|
int _4 = JO_MPEG_COMPONENTS;
|
||||||
// const unsigned char *c = bgrx + y*width*_4+x*_4; // original
|
// const unsigned char *c = bgrx + y*width*_4+x*_4; // original
|
||||||
const unsigned char *c = bgrx + ((height-1)-y)*width*_4+x*_4; // flipped
|
const unsigned char *c = bgrx + ((height-1)-y)*width*_4+x*_4; // flipped
|
||||||
float b = c[0], g = c[1], r = c[2]; // channel swap
|
float b = c[0], g = c[1], r = c[2]; // channel swap
|
||||||
Y[i] = ( 0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16;
|
Y[i] = ( 0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16;
|
||||||
CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128;
|
CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128;
|
||||||
CRx[i] = ( 0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
|
CRx[i] = ( 0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Downsample Cb,Cr (420 format)
|
// Downsample Cb,Cr (420 format)
|
||||||
float CB[64], CR[64];
|
float CB[64], CR[64];
|
||||||
for (int i=0; i<64; ++i) {
|
for (int i=0; i<64; ++i) {
|
||||||
int j =(i&7)*2 + (i&56)*4;
|
int j =(i&7)*2 + (i&56)*4;
|
||||||
CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f;
|
CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f;
|
||||||
CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f;
|
CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int k1=0; k1<2; ++k1) {
|
for (int k1=0; k1<2; ++k1) {
|
||||||
for (int k2=0; k2<2; ++k2) {
|
for (int k2=0; k2<2; ++k2) {
|
||||||
float block[64];
|
float block[64];
|
||||||
for (int i=0; i<64; i+=8) {
|
for (int i=0; i<64; i+=8) {
|
||||||
int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8;
|
int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8;
|
||||||
memcpy(block+i, Y+j, 8*sizeof(Y[0]));
|
memcpy(block+i, Y+j, 8*sizeof(Y[0]));
|
||||||
}
|
}
|
||||||
lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY);
|
lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB);
|
lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB);
|
||||||
lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR);
|
lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jo_writeBits(&bits, 0, 7);
|
jo_writeBits(&bits, 0, 7);
|
||||||
fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence
|
fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,440 +1,440 @@
|
||||||
// JSON5 + SJSON parser module
|
// JSON5 + SJSON parser module
|
||||||
//
|
//
|
||||||
// License:
|
// License:
|
||||||
// This software is dual-licensed to the public domain and under the following
|
// This software is dual-licensed to the public domain and under the following
|
||||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
// publish, and distribute this file as you see fit.
|
// publish, and distribute this file as you see fit.
|
||||||
// No warranty is implied, use at your own risk.
|
// No warranty is implied, use at your own risk.
|
||||||
//
|
//
|
||||||
// Credits:
|
// Credits:
|
||||||
// r-lyeh (fork)
|
// r-lyeh (fork)
|
||||||
// Dominik Madarasz (@zaklaus) (original code)
|
// Dominik Madarasz (@zaklaus) (original code)
|
||||||
|
|
||||||
#ifndef JSON5_H
|
#ifndef JSON5_H
|
||||||
#define JSON5_H
|
#define JSON5_H
|
||||||
|
|
||||||
#ifndef JSON5_ASSERT
|
#ifndef JSON5_ASSERT
|
||||||
#define JSON5_ASSERT do { printf("JSON5: Error L%d while parsing '%c' in '%.16s'\n", __LINE__, p[0], p); assert(0); } while(0)
|
#define JSON5_ASSERT do { printf("JSON5: Error L%d while parsing '%c' in '%.16s'\n", __LINE__, p[0], p); assert(0); } while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
typedef enum json5_type {
|
typedef enum json5_type {
|
||||||
JSON5_UNDEFINED, // 0
|
JSON5_UNDEFINED, // 0
|
||||||
JSON5_NULL, // 1
|
JSON5_NULL, // 1
|
||||||
JSON5_BOOL, // 2
|
JSON5_BOOL, // 2
|
||||||
JSON5_OBJECT, // 3
|
JSON5_OBJECT, // 3
|
||||||
JSON5_STRING, // 4
|
JSON5_STRING, // 4
|
||||||
JSON5_ARRAY, // 5
|
JSON5_ARRAY, // 5
|
||||||
JSON5_INTEGER, // 6
|
JSON5_INTEGER, // 6
|
||||||
JSON5_REAL, // 7
|
JSON5_REAL, // 7
|
||||||
} json5_type;
|
} json5_type;
|
||||||
|
|
||||||
typedef struct json5 {
|
typedef struct json5 {
|
||||||
char* name;
|
char* name;
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
unsigned type : 3;
|
unsigned type : 3;
|
||||||
#else
|
#else
|
||||||
json5_type type;
|
json5_type type;
|
||||||
#endif
|
#endif
|
||||||
unsigned count : 29;
|
unsigned count : 29;
|
||||||
union {
|
union {
|
||||||
struct json5* array;
|
struct json5* array;
|
||||||
struct json5* nodes;
|
struct json5* nodes;
|
||||||
int64_t integer;
|
int64_t integer;
|
||||||
double real;
|
double real;
|
||||||
char* string;
|
char* string;
|
||||||
int boolean;
|
int boolean;
|
||||||
};
|
};
|
||||||
} json5;
|
} json5;
|
||||||
|
|
||||||
char* json5_parse(json5 *root, char *source, int flags);
|
char* json5_parse(json5 *root, char *source, int flags);
|
||||||
void json5_write(FILE *fp, const json5 *root);
|
void json5_write(FILE *fp, const json5 *root);
|
||||||
void json5_free(json5 *root);
|
void json5_free(json5 *root);
|
||||||
|
|
||||||
#endif // JSON5_H
|
#endif // JSON5_H
|
||||||
|
|
||||||
// json5 ----------------------------------------------------------------------
|
// json5 ----------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef JSON5_C
|
#ifdef JSON5_C
|
||||||
//#pragma once
|
//#pragma once
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
char *json5__trim(char *p) {
|
char *json5__trim(char *p) {
|
||||||
while (*p) {
|
while (*p) {
|
||||||
/**/ if( isspace(*p) ) ++p;
|
/**/ if( isspace(*p) ) ++p;
|
||||||
else if( p[0] == '/' && p[1] == '*' ) { // skip C comment
|
else if( p[0] == '/' && p[1] == '*' ) { // skip C comment
|
||||||
for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {}
|
for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {}
|
||||||
if( *p ) p += 2;
|
if( *p ) p += 2;
|
||||||
}
|
}
|
||||||
else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment
|
else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment
|
||||||
for( p += 2; *p && p[0] != '\n'; ++p) {}
|
for( p += 2; *p && p[0] != '\n'; ++p) {}
|
||||||
if( *p ) ++p;
|
if( *p ) ++p;
|
||||||
}
|
}
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *json5__parse_value(json5 *obj, char *p, char **err_code);
|
char *json5__parse_value(json5 *obj, char *p, char **err_code);
|
||||||
|
|
||||||
char *json5__parse_string(json5 *obj, char *p, char **err_code) {
|
char *json5__parse_string(json5 *obj, char *p, char **err_code) {
|
||||||
assert(obj && p);
|
assert(obj && p);
|
||||||
|
|
||||||
if( *p == '"' || *p == '\'' || *p == '`' ) {
|
if( *p == '"' || *p == '\'' || *p == '`' ) {
|
||||||
obj->type = JSON5_STRING;
|
obj->type = JSON5_STRING;
|
||||||
obj->string = p + 1;
|
obj->string = p + 1;
|
||||||
|
|
||||||
char eos_char = *p, *b = obj->string, *e = b;
|
char eos_char = *p, *b = obj->string, *e = b;
|
||||||
while (*e) {
|
while (*e) {
|
||||||
/**/ if( *e == '\\' && (e[1] == eos_char) ) ++e;
|
/**/ if( *e == '\\' && (e[1] == eos_char) ) ++e;
|
||||||
else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' ';
|
else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' ';
|
||||||
else if( *e == eos_char ) break;
|
else if( *e == eos_char ) break;
|
||||||
++e;
|
++e;
|
||||||
}
|
}
|
||||||
|
|
||||||
*e = '\0';
|
*e = '\0';
|
||||||
return p = e + 1;
|
return p = e + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//JSON5_ASSERT; *err_code = "json5_error_invalid_value";
|
//JSON5_ASSERT; *err_code = "json5_error_invalid_value";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *json5__parse_object(json5 *obj, char *p, char **err_code) {
|
char *json5__parse_object(json5 *obj, char *p, char **err_code) {
|
||||||
assert(obj && p);
|
assert(obj && p);
|
||||||
|
|
||||||
if( 1 /* *p == '{' */ ) { /* <-- for SJSON */
|
if( 1 /* *p == '{' */ ) { /* <-- for SJSON */
|
||||||
int skip = *p == '{'; /* <-- for SJSON */
|
int skip = *p == '{'; /* <-- for SJSON */
|
||||||
|
|
||||||
obj->type = JSON5_OBJECT;
|
obj->type = JSON5_OBJECT;
|
||||||
obj->nodes = 0;
|
obj->nodes = 0;
|
||||||
obj->count = 0;
|
obj->count = 0;
|
||||||
|
|
||||||
while (*p) {
|
while (*p) {
|
||||||
json5 node = { 0 };
|
json5 node = { 0 };
|
||||||
|
|
||||||
do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' );
|
do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' );
|
||||||
|
|
||||||
if( *p == '}' ) {
|
if( *p == '}' ) {
|
||||||
++p;
|
++p;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// @todo: is_unicode() (s[0] == '\\' && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]))) {
|
// @todo: is_unicode() (s[0] == '\\' && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]))) {
|
||||||
else if( isalnum(*p) || *p == '_' || *p == '$' || *p == '.' ) { // also || is_unicode(p)
|
else if( isalnum(*p) || *p == '_' || *p == '$' || *p == '.' ) { // also || is_unicode(p)
|
||||||
node.name = p;
|
node.name = p;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
++p;
|
++p;
|
||||||
} while (*p && (isalnum(*p) || *p == '_' || *p == '$' || *p == '.') ); // also || is_unicode(p)
|
} while (*p && (isalnum(*p) || *p == '_' || *p == '$' || *p == '.') ); // also || is_unicode(p)
|
||||||
|
|
||||||
char *e = p;
|
char *e = p;
|
||||||
p = json5__trim(p);
|
p = json5__trim(p);
|
||||||
*e = '\0';
|
*e = '\0';
|
||||||
}
|
}
|
||||||
else { //if( *p == '"' || *p == '\'' || *p == '`' ) {
|
else { //if( *p == '"' || *p == '\'' || *p == '`' ) {
|
||||||
char *ps = json5__parse_string(&node, p, err_code);
|
char *ps = json5__parse_string(&node, p, err_code);
|
||||||
if( !ps ) {
|
if( !ps ) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
p = ps;
|
p = ps;
|
||||||
node.name = node.string;
|
node.name = node.string;
|
||||||
p = json5__trim(p);
|
p = json5__trim(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
|
// @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
|
||||||
if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) {
|
if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) {
|
||||||
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
|
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) {
|
if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) {
|
||||||
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
|
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
p = json5__trim(p + 1);
|
p = json5__trim(p + 1);
|
||||||
p = json5__parse_value(&node, p, err_code);
|
p = json5__parse_value(&node, p, err_code);
|
||||||
|
|
||||||
if( *err_code[0] ) {
|
if( *err_code[0] ) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( node.type != JSON5_UNDEFINED ) {
|
if( node.type != JSON5_UNDEFINED ) {
|
||||||
array_push(obj->nodes, node);
|
array_push(obj->nodes, node);
|
||||||
++obj->count;
|
++obj->count;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( *p == '}') { ++p; break; }
|
if( *p == '}') { ++p; break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
|
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *json5__parse_value(json5 *obj, char *p, char **err_code) {
|
char *json5__parse_value(json5 *obj, char *p, char **err_code) {
|
||||||
assert(obj && p);
|
assert(obj && p);
|
||||||
|
|
||||||
p = json5__trim(p);
|
p = json5__trim(p);
|
||||||
|
|
||||||
char *is_string = json5__parse_string(obj, p, err_code);
|
char *is_string = json5__parse_string(obj, p, err_code);
|
||||||
|
|
||||||
if( is_string ) {
|
if( is_string ) {
|
||||||
p = is_string;
|
p = is_string;
|
||||||
if( *err_code[0] ) {
|
if( *err_code[0] ) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( *p == '{' ) {
|
else if( *p == '{' ) {
|
||||||
p = json5__parse_object( obj, p, err_code );
|
p = json5__parse_object( obj, p, err_code );
|
||||||
if( *err_code[0] ) {
|
if( *err_code[0] ) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( *p == '[' ) {
|
else if( *p == '[' ) {
|
||||||
obj->type = JSON5_ARRAY;
|
obj->type = JSON5_ARRAY;
|
||||||
obj->array = 0;
|
obj->array = 0;
|
||||||
obj->count = 0;
|
obj->count = 0;
|
||||||
|
|
||||||
while (*p) {
|
while (*p) {
|
||||||
json5 elem = { 0 };
|
json5 elem = { 0 };
|
||||||
|
|
||||||
do { p = json5__trim(p + 1); } while( *p == ',' );
|
do { p = json5__trim(p + 1); } while( *p == ',' );
|
||||||
if( *p == ']') { ++p; break; }
|
if( *p == ']') { ++p; break; }
|
||||||
|
|
||||||
p = json5__parse_value(&elem, p, err_code);
|
p = json5__parse_value(&elem, p, err_code);
|
||||||
|
|
||||||
if( *err_code[0] ) {
|
if( *err_code[0] ) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( elem.type != JSON5_UNDEFINED ) {
|
if( elem.type != JSON5_UNDEFINED ) {
|
||||||
array_push(obj->array, elem);
|
array_push(obj->array, elem);
|
||||||
++obj->count;
|
++obj->count;
|
||||||
}
|
}
|
||||||
if (*p == ']') { ++p; break; }
|
if (*p == ']') { ++p; break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( isalpha(*p) || (*p == '-' && !isdigit(p[1])) ) {
|
else if( isalpha(*p) || (*p == '-' && !isdigit(p[1])) ) {
|
||||||
const char *labels[] = { "null", "on","true", "off","false", "nan","NaN", "-nan","-NaN", "inf","Infinity", "-inf","-Infinity", 0 };
|
const char *labels[] = { "null", "on","true", "off","false", "nan","NaN", "-nan","-NaN", "inf","Infinity", "-inf","-Infinity", 0 };
|
||||||
const int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 };
|
const int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 };
|
||||||
for( int i = 0; labels[i]; ++i ) {
|
for( int i = 0; labels[i]; ++i ) {
|
||||||
if( !strncmp(p, labels[i], lenghts[i] ) ) {
|
if( !strncmp(p, labels[i], lenghts[i] ) ) {
|
||||||
p += lenghts[i];
|
p += lenghts[i];
|
||||||
#ifdef _MSC_VER // somehow, NaN is apparently signed in MSC
|
#ifdef _MSC_VER // somehow, NaN is apparently signed in MSC
|
||||||
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? NAN :-NAN;
|
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? NAN :-NAN;
|
||||||
#else
|
#else
|
||||||
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN;
|
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN;
|
||||||
#endif
|
#endif
|
||||||
else if( i >= 1 ) obj->type = JSON5_BOOL, obj->boolean = i <= 2;
|
else if( i >= 1 ) obj->type = JSON5_BOOL, obj->boolean = i <= 2;
|
||||||
else obj->type = JSON5_NULL;
|
else obj->type = JSON5_NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( obj->type == JSON5_UNDEFINED ) {
|
if( obj->type == JSON5_UNDEFINED ) {
|
||||||
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
|
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) {
|
else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) {
|
||||||
char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0;
|
char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0;
|
||||||
while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) {
|
while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) {
|
||||||
is_hex |= (*p | 32) == 'x';
|
is_hex |= (*p | 32) == 'x';
|
||||||
is_dbl |= *p == '.';
|
is_dbl |= *p == '.';
|
||||||
*buf++ = *p++;
|
*buf++ = *p++;
|
||||||
}
|
}
|
||||||
obj->type = is_dbl ? JSON5_REAL : JSON5_INTEGER;
|
obj->type = is_dbl ? JSON5_REAL : JSON5_INTEGER;
|
||||||
long long unsigned int llu;
|
long long unsigned int llu;
|
||||||
long long int lli;
|
long long int lli;
|
||||||
/**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real );
|
/**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real );
|
||||||
else if( is_hex ) sscanf( buffer, "%llx", &llu ), obj->integer = llu; // SCNx64 -> inttypes.h
|
else if( is_hex ) sscanf( buffer, "%llx", &llu ), obj->integer = llu; // SCNx64 -> inttypes.h
|
||||||
else sscanf( buffer, "%lld", &lli ), obj->integer = lli; // SCNd64 -> inttypes.h
|
else sscanf( buffer, "%lld", &lli ), obj->integer = lli; // SCNd64 -> inttypes.h
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *json5_parse(json5 *root, char *p, int flags) {
|
char *json5_parse(json5 *root, char *p, int flags) {
|
||||||
char *err_code = "";
|
char *err_code = "";
|
||||||
*root = (json5) {0};
|
*root = (json5) {0};
|
||||||
|
|
||||||
if( p && p[0] ) {
|
if( p && p[0] ) {
|
||||||
p = json5__trim(p);
|
p = json5__trim(p);
|
||||||
if( *p == '[' ) { /* <-- for SJSON */
|
if( *p == '[' ) { /* <-- for SJSON */
|
||||||
json5__parse_value(root, p, &err_code);
|
json5__parse_value(root, p, &err_code);
|
||||||
} else {
|
} else {
|
||||||
json5__parse_object(root, p, &err_code); /* <-- for SJSON */
|
json5__parse_object(root, p, &err_code); /* <-- for SJSON */
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
root->type = JSON5_OBJECT;
|
root->type = JSON5_OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err_code[0] ? err_code : 0;
|
return err_code[0] ? err_code : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void json5_free(json5 *root) {
|
void json5_free(json5 *root) {
|
||||||
if( root->type == JSON5_ARRAY && root->array ) {
|
if( root->type == JSON5_ARRAY && root->array ) {
|
||||||
for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) {
|
for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) {
|
||||||
json5_free(root->array + i);
|
json5_free(root->array + i);
|
||||||
}
|
}
|
||||||
array_free(root->array);
|
array_free(root->array);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( root->type == JSON5_OBJECT && root->nodes ) {
|
if( root->type == JSON5_OBJECT && root->nodes ) {
|
||||||
for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) {
|
for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) {
|
||||||
json5_free(root->nodes + i);
|
json5_free(root->nodes + i);
|
||||||
}
|
}
|
||||||
array_free(root->nodes);
|
array_free(root->nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
*root = (json5) {0}; // needed?
|
*root = (json5) {0}; // needed?
|
||||||
}
|
}
|
||||||
|
|
||||||
void json5_write(FILE *fp, const json5 *o) {
|
void json5_write(FILE *fp, const json5 *o) {
|
||||||
static __thread int indent = 0;
|
static __thread int indent = 0;
|
||||||
int tabs = 1; // 0,1,2,4,8
|
int tabs = 1; // 0,1,2,4,8
|
||||||
if( o->name ) {
|
if( o->name ) {
|
||||||
fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":");
|
fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":");
|
||||||
}
|
}
|
||||||
/**/ if( o->type == JSON5_NULL ) fprintf(fp, "%s", "null");
|
/**/ if( o->type == JSON5_NULL ) fprintf(fp, "%s", "null");
|
||||||
else if( o->type == JSON5_BOOL ) fprintf(fp, "%s", o->boolean ? "true" : "false");
|
else if( o->type == JSON5_BOOL ) fprintf(fp, "%s", o->boolean ? "true" : "false");
|
||||||
else if( o->type == JSON5_INTEGER ) fprintf(fp, "%lld", (long long int)o->integer);
|
else if( o->type == JSON5_INTEGER ) fprintf(fp, "%lld", (long long int)o->integer);
|
||||||
else if( o->type == JSON5_REAL ) {
|
else if( o->type == JSON5_REAL ) {
|
||||||
/**/ if( isnan(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-nan" : "nan" );
|
/**/ if( isnan(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-nan" : "nan" );
|
||||||
else if( isinf(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-inf" : "inf" );
|
else if( isinf(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-inf" : "inf" );
|
||||||
else fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness
|
else fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
else if( o->type == JSON5_STRING ) { // write (escaped) string
|
else if( o->type == JSON5_STRING ) { // write (escaped) string
|
||||||
char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256];
|
char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256];
|
||||||
for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i];
|
for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i];
|
||||||
|
|
||||||
const char *b = o->string, *e = strpbrk(b, chars), *sep = "\"";
|
const char *b = o->string, *e = strpbrk(b, chars), *sep = "\"";
|
||||||
while( e ) {
|
while( e ) {
|
||||||
fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] );
|
fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] );
|
||||||
e = strpbrk( b = e + 1, chars);
|
e = strpbrk( b = e + 1, chars);
|
||||||
sep = "";
|
sep = "";
|
||||||
}
|
}
|
||||||
fprintf(fp, "%s%s\"", sep, b);
|
fprintf(fp, "%s%s\"", sep, b);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
else if( o->type == JSON5_STRING ) { // write string
|
else if( o->type == JSON5_STRING ) { // write string
|
||||||
fprintf(fp, "\"%s\"", o->string);
|
fprintf(fp, "\"%s\"", o->string);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if( o->type == JSON5_ARRAY ) {
|
else if( o->type == JSON5_ARRAY ) {
|
||||||
const char *sep = "";
|
const char *sep = "";
|
||||||
fprintf(fp, "%s", tabs ? "[ " : "[");
|
fprintf(fp, "%s", tabs ? "[ " : "[");
|
||||||
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
|
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
|
||||||
fprintf(fp, "%s", sep); sep = tabs ? ", " : ",";
|
fprintf(fp, "%s", sep); sep = tabs ? ", " : ",";
|
||||||
json5_write(fp, o->array + i);
|
json5_write(fp, o->array + i);
|
||||||
}
|
}
|
||||||
fprintf(fp, "%s", tabs ? " ]" : "]");
|
fprintf(fp, "%s", tabs ? " ]" : "]");
|
||||||
}
|
}
|
||||||
else if( o->type == JSON5_OBJECT ) {
|
else if( o->type == JSON5_OBJECT ) {
|
||||||
const char *sep = "";
|
const char *sep = "";
|
||||||
fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":"");
|
fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":"");
|
||||||
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
|
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
|
||||||
fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ",";
|
fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ",";
|
||||||
json5_write(fp, o->nodes + i);
|
json5_write(fp, o->nodes + i);
|
||||||
}
|
}
|
||||||
fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, "");
|
fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, "");
|
||||||
} else {
|
} else {
|
||||||
char p[16] = {0};
|
char p[16] = {0};
|
||||||
JSON5_ASSERT; /* "json5_error_invalid_value"; */
|
JSON5_ASSERT; /* "json5_error_invalid_value"; */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JSON5_BENCH
|
#ifdef JSON5_BENCH
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
int main() {
|
int main() {
|
||||||
// https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/
|
// https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/
|
||||||
char *content = 0;
|
char *content = 0;
|
||||||
for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) {
|
for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) {
|
||||||
fseek(fp, 0L, SEEK_END);
|
fseek(fp, 0L, SEEK_END);
|
||||||
size_t pos = ftell(fp);
|
size_t pos = ftell(fp);
|
||||||
fseek(fp, 0L, SEEK_SET);
|
fseek(fp, 0L, SEEK_SET);
|
||||||
content = (char*)malloc( pos + 1 );
|
content = (char*)malloc( pos + 1 );
|
||||||
fread(content, 1, pos, fp);
|
fread(content, 1, pos, fp);
|
||||||
content[pos] = 0;
|
content[pos] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( content ) {
|
if( content ) {
|
||||||
clock_t start = clock();
|
clock_t start = clock();
|
||||||
json5 root = {0};
|
json5 root = {0};
|
||||||
char *error = json5_parse(&root, content, 0);
|
char *error = json5_parse(&root, content, 0);
|
||||||
clock_t end = clock();
|
clock_t end = clock();
|
||||||
double delta = ( end - start ) / (double)CLOCKS_PER_SEC;
|
double delta = ( end - start ) / (double)CLOCKS_PER_SEC;
|
||||||
|
|
||||||
if( !error ) {
|
if( !error ) {
|
||||||
printf("Parsing time: %.3fms\n", delta*1000);
|
printf("Parsing time: %.3fms\n", delta*1000);
|
||||||
printf("Total nodes: %d\n", array_count(root.array));
|
printf("Total nodes: %d\n", array_count(root.array));
|
||||||
printf("Category: %s, air date: %s\nQuestion: %s\n", root.array[0].nodes[0].string,
|
printf("Category: %s, air date: %s\nQuestion: %s\n", root.array[0].nodes[0].string,
|
||||||
root.array[0].nodes[1].string,
|
root.array[0].nodes[1].string,
|
||||||
root.array[0].nodes[2].string);
|
root.array[0].nodes[2].string);
|
||||||
} else {
|
} else {
|
||||||
printf("Error: %s\n", error);
|
printf("Error: %s\n", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
json5_free(&root);
|
json5_free(&root);
|
||||||
free(content);
|
free(content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#define main main__
|
#define main main__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef JSON5_DEMO
|
#ifdef JSON5_DEMO
|
||||||
int main() {
|
int main() {
|
||||||
char source5[] =
|
char source5[] =
|
||||||
" // comments\n" /* json5 sample */
|
" // comments\n" /* json5 sample */
|
||||||
" unquoted: 'and you can quote me on that',\n"
|
" unquoted: 'and you can quote me on that',\n"
|
||||||
" singleQuotes: 'I can use \"double quotes\" here',\n"
|
" singleQuotes: 'I can use \"double quotes\" here',\n"
|
||||||
" lineBreaks : \"Look, Mom! \\\n"
|
" lineBreaks : \"Look, Mom! \\\n"
|
||||||
"No \\n's!\",\n"
|
"No \\n's!\",\n"
|
||||||
" hexadecimal: 0x100,\n"
|
" hexadecimal: 0x100,\n"
|
||||||
" leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n"
|
" leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n"
|
||||||
" positiveSign: +1,\n"
|
" positiveSign: +1,\n"
|
||||||
" trailingComma: 'in objects', andIn: ['arrays', ],\n"
|
" trailingComma: 'in objects', andIn: ['arrays', ],\n"
|
||||||
" \"backwardsCompatible\": \"with JSON\",\n"
|
" \"backwardsCompatible\": \"with JSON\",\n"
|
||||||
""
|
""
|
||||||
" ip = \"127.0.0.1\"\n" /* sjson sample */
|
" ip = \"127.0.0.1\"\n" /* sjson sample */
|
||||||
" port = 8888\n"
|
" port = 8888\n"
|
||||||
""
|
""
|
||||||
" /* comment //nested comment*/\n" /* tests */
|
" /* comment //nested comment*/\n" /* tests */
|
||||||
" // comment /*nested comment*/\n"
|
" // comment /*nested comment*/\n"
|
||||||
" nil: null,"
|
" nil: null,"
|
||||||
" \"+lšctžýáíé=:\": true,,,,"
|
" \"+lšctžýáíé=:\": true,,,,"
|
||||||
" huge: 2.2239333e5, "
|
" huge: 2.2239333e5, "
|
||||||
" array: [+1,2,-3,4,5], "
|
" array: [+1,2,-3,4,5], "
|
||||||
" hello: 'world /*comment in string*/ //again', "
|
" hello: 'world /*comment in string*/ //again', "
|
||||||
" abc: 42.67, def: false, "
|
" abc: 42.67, def: false, "
|
||||||
" children : { a: 1, b: 2, },"
|
" children : { a: 1, b: 2, },"
|
||||||
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],"
|
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],"
|
||||||
""
|
""
|
||||||
" multiline: `this is\n"
|
" multiline: `this is\n"
|
||||||
"a multiline string\n"
|
"a multiline string\n"
|
||||||
"yeah`"
|
"yeah`"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
json5 root = { 0 };
|
json5 root = { 0 };
|
||||||
char *error = json5_parse(&root, source5, 0);
|
char *error = json5_parse(&root, source5, 0);
|
||||||
if( error ) {
|
if( error ) {
|
||||||
printf("Error: %s\n", error);
|
printf("Error: %s\n", error);
|
||||||
} else {
|
} else {
|
||||||
json5_write(stdout, &root);
|
json5_write(stdout, &root);
|
||||||
}
|
}
|
||||||
json5_free(&root);
|
json5_free(&root);
|
||||||
}
|
}
|
||||||
#define main main__
|
#define main main__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // JSON5_C
|
#endif // JSON5_C
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,275 +1,275 @@
|
||||||
// lite editor, platform details
|
// lite editor, platform details
|
||||||
// - rlyeh, public domain
|
// - rlyeh, public domain
|
||||||
|
|
||||||
#define LT_DATAPATH "/lite"
|
#define LT_DATAPATH "/lite"
|
||||||
|
|
||||||
#define lt_assert(x) ASSERT(x)
|
#define lt_assert(x) ASSERT(x)
|
||||||
|
|
||||||
#define lt_realpath(p, q) file_pathabs(p)
|
#define lt_realpath(p, q) file_pathabs(p)
|
||||||
#define lt_realpath_free(p)
|
#define lt_realpath_free(p)
|
||||||
|
|
||||||
#define lt_malloc(n) MALLOC(n)
|
#define lt_malloc(n) MALLOC(n)
|
||||||
#define lt_calloc(n,m) CALLOC(n,m)
|
#define lt_calloc(n,m) CALLOC(n,m)
|
||||||
#define lt_free(p) FREE(p)
|
#define lt_free(p) FREE(p)
|
||||||
#define lt_memcpy(d,s,c) memcpy(d,s,c)
|
#define lt_memcpy(d,s,c) memcpy(d,s,c)
|
||||||
#define lt_memset(p,ch,c) memset(p,ch,c)
|
#define lt_memset(p,ch,c) memset(p,ch,c)
|
||||||
|
|
||||||
#define lt_time_ms() time_ms()
|
#define lt_time_ms() time_ms()
|
||||||
#define lt_sleep_ms(ms) sleep_ms(ms)
|
#define lt_sleep_ms(ms) sleep_ms(ms)
|
||||||
|
|
||||||
#define lt_getclipboard(w) window_clipboard()
|
#define lt_getclipboard(w) window_clipboard()
|
||||||
#define lt_setclipboard(w,s) window_setclipboard(s)
|
#define lt_setclipboard(w,s) window_setclipboard(s)
|
||||||
|
|
||||||
#define lt_window() window_handle()
|
#define lt_window() window_handle()
|
||||||
#define lt_setwindowmode(m) window_fullscreen(m == 2), (m < 2 && (window_maximize(m),1)) // 0:normal,1:maximized,2:fullscreen
|
#define lt_setwindowmode(m) window_fullscreen(m == 2), (m < 2 && (window_maximize(m),1)) // 0:normal,1:maximized,2:fullscreen
|
||||||
#define lt_setwindowtitle(t) //window_title(t)
|
#define lt_setwindowtitle(t) //window_title(t)
|
||||||
#define lt_haswindowfocus() window_has_focus()
|
#define lt_haswindowfocus() window_has_focus()
|
||||||
#define lt_setcursor(shape) window_cursor_shape(lt_events & (1<<31) ? CURSOR_SW_AUTO : shape+1) // 0:arrow,1:ibeam,2:sizeh,3:sizev,4:hand
|
#define lt_setcursor(shape) window_cursor_shape(lt_events & (1<<31) ? CURSOR_SW_AUTO : shape+1) // 0:arrow,1:ibeam,2:sizeh,3:sizev,4:hand
|
||||||
|
|
||||||
#define lt_prompt(msg,title) ifndef(win32, 0, (MessageBoxA(0, msg, title, MB_YESNO | MB_ICONWARNING) == IDYES))
|
#define lt_prompt(msg,title) ifndef(win32, 0, (MessageBoxA(0, msg, title, MB_YESNO | MB_ICONWARNING) == IDYES))
|
||||||
|
|
||||||
unsigned lt_events = ~0u;
|
unsigned lt_events = ~0u;
|
||||||
int lt_mx = 0, lt_my = 0, lt_wx = 0, lt_wy = 0, lt_ww = 0, lt_wh = 0;
|
int lt_mx = 0, lt_my = 0, lt_wx = 0, lt_wy = 0, lt_ww = 0, lt_wh = 0;
|
||||||
|
|
||||||
typedef struct lt_surface {
|
typedef struct lt_surface {
|
||||||
unsigned w, h;
|
unsigned w, h;
|
||||||
void *pixels;
|
void *pixels;
|
||||||
texture_t t;
|
texture_t t;
|
||||||
} lt_surface;
|
} lt_surface;
|
||||||
|
|
||||||
typedef struct lt_rect {
|
typedef struct lt_rect {
|
||||||
int x, y, width, height;
|
int x, y, width, height;
|
||||||
} lt_rect;
|
} lt_rect;
|
||||||
|
|
||||||
lt_surface *lt_getsurface(void *window) {
|
lt_surface *lt_getsurface(void *window) {
|
||||||
static lt_surface s = {0};
|
static lt_surface s = {0};
|
||||||
return &s;
|
return &s;
|
||||||
}
|
}
|
||||||
void lt_updatesurfacerects(lt_surface *s, lt_rect* rects, unsigned count) {
|
void lt_updatesurfacerects(lt_surface *s, lt_rect* rects, unsigned count) {
|
||||||
if(0)
|
if(0)
|
||||||
for( int i = 0; i < count; ++i ) {
|
for( int i = 0; i < count; ++i ) {
|
||||||
memset( (unsigned*)s->pixels + (rects[i].x + rects[i].y * s->w), 0xFF, rects[i].width*4 );
|
memset( (unsigned*)s->pixels + (rects[i].x + rects[i].y * s->w), 0xFF, rects[i].width*4 );
|
||||||
memset( (unsigned*)s->pixels + (rects[i].x + (rects[i].y + (rects[i].height-1)) * s->w), 0xFF, rects[i].width*4 );
|
memset( (unsigned*)s->pixels + (rects[i].x + (rects[i].y + (rects[i].height-1)) * s->w), 0xFF, rects[i].width*4 );
|
||||||
for( int y = 1; y < (rects[i].height-1); ++y ) {
|
for( int y = 1; y < (rects[i].height-1); ++y ) {
|
||||||
((unsigned*)s->pixels)[ rects[i].x + y * s->w ] =
|
((unsigned*)s->pixels)[ rects[i].x + y * s->w ] =
|
||||||
((unsigned*)s->pixels)[ rects[i].x + (rects[i].width-1) + y * s->w ] = 0xFFFFFFFF;
|
((unsigned*)s->pixels)[ rects[i].x + (rects[i].width-1) + y * s->w ] = 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update contents
|
// update contents
|
||||||
texture_update(&s->t, s->w, s->h, 4, s->pixels, TEXTURE_LINEAR|TEXTURE_BGRA);
|
texture_update(&s->t, s->w, s->h, 4, s->pixels, TEXTURE_LINEAR|TEXTURE_BGRA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ren_set_clip_rect(struct lt_rect rect);
|
void ren_set_clip_rect(struct lt_rect rect);
|
||||||
void rencache_invalidate(void);
|
void rencache_invalidate(void);
|
||||||
int lt_resizesurface(lt_surface *s, int ww, int wh) {
|
int lt_resizesurface(lt_surface *s, int ww, int wh) {
|
||||||
s->w = ww, s->h = wh;
|
s->w = ww, s->h = wh;
|
||||||
if( s->t.id == 0 || s->w != s->t.w || s->h != s->t.h ) {
|
if( s->t.id == 0 || s->w != s->t.w || s->h != s->t.h ) {
|
||||||
// invalidate tiles
|
// invalidate tiles
|
||||||
ren_set_clip_rect( (lt_rect) { 0, 0, s->w, s->h } );
|
ren_set_clip_rect( (lt_rect) { 0, 0, s->w, s->h } );
|
||||||
rencache_invalidate();
|
rencache_invalidate();
|
||||||
|
|
||||||
// texture clear
|
// texture clear
|
||||||
if( !s->t.id ) s->t = texture_create(1, 1, 4, " ", TEXTURE_LINEAR|TEXTURE_RGBA|TEXTURE_BYTE );
|
if( !s->t.id ) s->t = texture_create(1, 1, 4, " ", TEXTURE_LINEAR|TEXTURE_RGBA|TEXTURE_BYTE );
|
||||||
s->pixels = REALLOC(s->pixels, s->w * s->h * 4);
|
s->pixels = REALLOC(s->pixels, s->w * s->h * 4);
|
||||||
memset(s->pixels, 0, s->w * s->h * 4);
|
memset(s->pixels, 0, s->w * s->h * 4);
|
||||||
|
|
||||||
// texture update
|
// texture update
|
||||||
lt_updatesurfacerects(s,0,0);
|
lt_updatesurfacerects(s,0,0);
|
||||||
return 1; // resized
|
return 1; // resized
|
||||||
}
|
}
|
||||||
return 0; // unchanged
|
return 0; // unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
void *lt_load_file(const char *filename, int *size) {
|
void *lt_load_file(const char *filename, int *size) {
|
||||||
int datalen; char *data = file_load(filename, &datalen);
|
int datalen; char *data = file_load(filename, &datalen);
|
||||||
if( !data || !datalen ) {
|
if( !data || !datalen ) {
|
||||||
filename = (const char *)file_normalize(filename);
|
filename = (const char *)file_normalize(filename);
|
||||||
if( strbegi(filename, app_path()) ) filename += strlen(app_path());
|
if( strbegi(filename, app_path()) ) filename += strlen(app_path());
|
||||||
data = vfs_load(filename, &datalen);
|
data = vfs_load(filename, &datalen);
|
||||||
}
|
}
|
||||||
if (size) *size = 0;
|
if (size) *size = 0;
|
||||||
if (!data) { return NULL; }
|
if (!data) { return NULL; }
|
||||||
if (size) *size = datalen;
|
if (size) *size = datalen;
|
||||||
// return permanent buffers here, as file_load() and vfs_load() do return temporaries
|
// return permanent buffers here, as file_load() and vfs_load() do return temporaries
|
||||||
data = memcpy(MALLOC(datalen+1), data, datalen);
|
data = memcpy(MALLOC(datalen+1), data, datalen);
|
||||||
data[datalen] = 0;
|
data[datalen] = 0;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* lt_button_name(int button) {
|
const char* lt_button_name(int button) {
|
||||||
if(button == GLFW_MOUSE_BUTTON_LEFT) return "left";
|
if(button == GLFW_MOUSE_BUTTON_LEFT) return "left";
|
||||||
if(button == GLFW_MOUSE_BUTTON_RIGHT) return "right";
|
if(button == GLFW_MOUSE_BUTTON_RIGHT) return "right";
|
||||||
if(button == GLFW_MOUSE_BUTTON_MIDDLE) return "middle";
|
if(button == GLFW_MOUSE_BUTTON_MIDDLE) return "middle";
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
char* lt_key_name(char *dst, int key, int vk, int mods) {
|
char* lt_key_name(char *dst, int key, int vk, int mods) {
|
||||||
// @todo: "altgr" -> left ctrl + right alt
|
// @todo: "altgr" -> left ctrl + right alt
|
||||||
|
|
||||||
if( key == GLFW_KEY_UP ) return "up";
|
if( key == GLFW_KEY_UP ) return "up";
|
||||||
if( key == GLFW_KEY_DOWN ) return "down";
|
if( key == GLFW_KEY_DOWN ) return "down";
|
||||||
if( key == GLFW_KEY_LEFT ) return "left";
|
if( key == GLFW_KEY_LEFT ) return "left";
|
||||||
if( key == GLFW_KEY_RIGHT ) return "right";
|
if( key == GLFW_KEY_RIGHT ) return "right";
|
||||||
if( key == GLFW_KEY_LEFT_ALT ) return "left alt";
|
if( key == GLFW_KEY_LEFT_ALT ) return "left alt";
|
||||||
if( key == GLFW_KEY_RIGHT_ALT ) return "right alt";
|
if( key == GLFW_KEY_RIGHT_ALT ) return "right alt";
|
||||||
if( key == GLFW_KEY_LEFT_SHIFT ) return "left shift";
|
if( key == GLFW_KEY_LEFT_SHIFT ) return "left shift";
|
||||||
if( key == GLFW_KEY_RIGHT_SHIFT ) return "right shift";
|
if( key == GLFW_KEY_RIGHT_SHIFT ) return "right shift";
|
||||||
if( key == GLFW_KEY_LEFT_CONTROL ) return "left ctrl";
|
if( key == GLFW_KEY_LEFT_CONTROL ) return "left ctrl";
|
||||||
if( key == GLFW_KEY_RIGHT_CONTROL ) return "right ctrl";
|
if( key == GLFW_KEY_RIGHT_CONTROL ) return "right ctrl";
|
||||||
if( key == GLFW_KEY_LEFT_SUPER ) return "left windows";
|
if( key == GLFW_KEY_LEFT_SUPER ) return "left windows";
|
||||||
if( key == GLFW_KEY_RIGHT_SUPER ) return "left windows";
|
if( key == GLFW_KEY_RIGHT_SUPER ) return "left windows";
|
||||||
if( key == GLFW_KEY_MENU ) return "menu";
|
if( key == GLFW_KEY_MENU ) return "menu";
|
||||||
|
|
||||||
if( key == GLFW_KEY_ESCAPE ) return "escape";
|
if( key == GLFW_KEY_ESCAPE ) return "escape";
|
||||||
if( key == GLFW_KEY_BACKSPACE ) return "backspace";
|
if( key == GLFW_KEY_BACKSPACE ) return "backspace";
|
||||||
if( key == GLFW_KEY_ENTER ) return "return";
|
if( key == GLFW_KEY_ENTER ) return "return";
|
||||||
if( key == GLFW_KEY_KP_ENTER ) return "keypad enter";
|
if( key == GLFW_KEY_KP_ENTER ) return "keypad enter";
|
||||||
if( key == GLFW_KEY_TAB ) return "tab";
|
if( key == GLFW_KEY_TAB ) return "tab";
|
||||||
if( key == GLFW_KEY_CAPS_LOCK ) return "capslock";
|
if( key == GLFW_KEY_CAPS_LOCK ) return "capslock";
|
||||||
|
|
||||||
if( key == GLFW_KEY_HOME ) return "home";
|
if( key == GLFW_KEY_HOME ) return "home";
|
||||||
if( key == GLFW_KEY_END ) return "end";
|
if( key == GLFW_KEY_END ) return "end";
|
||||||
if( key == GLFW_KEY_INSERT ) return "insert";
|
if( key == GLFW_KEY_INSERT ) return "insert";
|
||||||
if( key == GLFW_KEY_DELETE ) return "delete";
|
if( key == GLFW_KEY_DELETE ) return "delete";
|
||||||
if( key == GLFW_KEY_PAGE_UP ) return "pageup";
|
if( key == GLFW_KEY_PAGE_UP ) return "pageup";
|
||||||
if( key == GLFW_KEY_PAGE_DOWN ) return "pagedown";
|
if( key == GLFW_KEY_PAGE_DOWN ) return "pagedown";
|
||||||
|
|
||||||
if( key == GLFW_KEY_F1 ) return "f1";
|
if( key == GLFW_KEY_F1 ) return "f1";
|
||||||
if( key == GLFW_KEY_F2 ) return "f2";
|
if( key == GLFW_KEY_F2 ) return "f2";
|
||||||
if( key == GLFW_KEY_F3 ) return "f3";
|
if( key == GLFW_KEY_F3 ) return "f3";
|
||||||
if( key == GLFW_KEY_F4 ) return "f4";
|
if( key == GLFW_KEY_F4 ) return "f4";
|
||||||
if( key == GLFW_KEY_F5 ) return "f5";
|
if( key == GLFW_KEY_F5 ) return "f5";
|
||||||
if( key == GLFW_KEY_F6 ) return "f6";
|
if( key == GLFW_KEY_F6 ) return "f6";
|
||||||
if( key == GLFW_KEY_F7 ) return "f7";
|
if( key == GLFW_KEY_F7 ) return "f7";
|
||||||
if( key == GLFW_KEY_F8 ) return "f8";
|
if( key == GLFW_KEY_F8 ) return "f8";
|
||||||
if( key == GLFW_KEY_F9 ) return "f9";
|
if( key == GLFW_KEY_F9 ) return "f9";
|
||||||
if( key == GLFW_KEY_F10 ) return "f10";
|
if( key == GLFW_KEY_F10 ) return "f10";
|
||||||
if( key == GLFW_KEY_F11 ) return "f11";
|
if( key == GLFW_KEY_F11 ) return "f11";
|
||||||
if( key == GLFW_KEY_F12 ) return "f12";
|
if( key == GLFW_KEY_F12 ) return "f12";
|
||||||
|
|
||||||
const char *name = glfwGetKeyName(key, vk);
|
const char *name = glfwGetKeyName(key, vk);
|
||||||
strcpy(dst, name ? name : "");
|
strcpy(dst, name ? name : "");
|
||||||
char *p = dst;
|
char *p = dst;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
*p = tolower(*p);
|
*p = tolower(*p);
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lt_globpath(struct lua_State*L, const char *path) {
|
void lt_globpath(struct lua_State*L, const char *path) {
|
||||||
unsigned j = 0;
|
unsigned j = 0;
|
||||||
|
|
||||||
if(!strend(path, "/")) path = (const char *)va("%s/", path);
|
if(!strend(path, "/")) path = (const char *)va("%s/", path);
|
||||||
for( dir *d = dir_open(path, ""); d; dir_close(d), d = 0 ) {
|
for( dir *d = dir_open(path, ""); d; dir_close(d), d = 0 ) {
|
||||||
for( unsigned i = 0, end = dir_count(d); i < end; ++i ) {
|
for( unsigned i = 0, end = dir_count(d); i < end; ++i ) {
|
||||||
char *name = dir_name(d,i);
|
char *name = dir_name(d,i);
|
||||||
char *last = name + strlen(name) - 1;
|
char *last = name + strlen(name) - 1;
|
||||||
if( *last == '/' ) *last = '\0';
|
if( *last == '/' ) *last = '\0';
|
||||||
name = file_name(name);
|
name = file_name(name);
|
||||||
lua_pushstring(L, name);
|
lua_pushstring(L, name);
|
||||||
lua_rawseti(L, -2, ++j);
|
lua_rawseti(L, -2, ++j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for( const char *section = strstri(path, LT_DATAPATH); section && section[sizeof(LT_DATAPATH)-1] == '/'; section = 0) {
|
for( const char *section = strstri(path, LT_DATAPATH); section && section[sizeof(LT_DATAPATH)-1] == '/'; section = 0) {
|
||||||
array(char*) list = vfs_list("**");
|
array(char*) list = vfs_list("**");
|
||||||
for( unsigned i = 0, end = array_count(list); i < end; ++i ) {
|
for( unsigned i = 0, end = array_count(list); i < end; ++i ) {
|
||||||
char *name = list[i];
|
char *name = list[i];
|
||||||
if( !strstri(name, section+1) ) continue;
|
if( !strstri(name, section+1) ) continue;
|
||||||
lua_pushstring(L, file_name(name));
|
lua_pushstring(L, file_name(name));
|
||||||
lua_rawseti(L, -2, ++j);
|
lua_rawseti(L, -2, ++j);
|
||||||
}
|
}
|
||||||
if( array_count(list) ) return;
|
if( array_count(list) ) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int lt_emit_event(lua_State *L, const char *event_name, const char *event_fmt, ...) {
|
int lt_emit_event(lua_State *L, const char *event_name, const char *event_fmt, ...) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
lua_pushstring(L, event_name);
|
lua_pushstring(L, event_name);
|
||||||
if( event_fmt ) {
|
if( event_fmt ) {
|
||||||
va_list va;
|
va_list va;
|
||||||
va_start(va, event_fmt);
|
va_start(va, event_fmt);
|
||||||
for( ; event_fmt[count]; ++count ) {
|
for( ; event_fmt[count]; ++count ) {
|
||||||
/**/ if( event_fmt[count] == 'd' ) { int d = va_arg(va, int); lua_pushnumber(L, d); }
|
/**/ if( event_fmt[count] == 'd' ) { int d = va_arg(va, int); lua_pushnumber(L, d); }
|
||||||
else if( event_fmt[count] == 'f' ) { double f = va_arg(va, double); lua_pushnumber(L, f); }
|
else if( event_fmt[count] == 'f' ) { double f = va_arg(va, double); lua_pushnumber(L, f); }
|
||||||
else if( event_fmt[count] == 's' ) { const char *s = va_arg(va, const char *); lua_pushstring(L, s); }
|
else if( event_fmt[count] == 's' ) { const char *s = va_arg(va, const char *); lua_pushstring(L, s); }
|
||||||
}
|
}
|
||||||
va_end(va);
|
va_end(va);
|
||||||
}
|
}
|
||||||
return 1+count;
|
return 1+count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int printi(int i) {
|
int printi(int i) {
|
||||||
// printf("clicks: %d\n", i);
|
// printf("clicks: %d\n", i);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* codepoint_to_utf8_(unsigned c);
|
static const char* codepoint_to_utf8_(unsigned c);
|
||||||
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
|
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
char buf[16];
|
char buf[16];
|
||||||
static int prevx = 0, prevy = 0;
|
static int prevx = 0, prevy = 0;
|
||||||
|
|
||||||
static unsigned clicks_time = 0, clicks = 0;
|
static unsigned clicks_time = 0, clicks = 0;
|
||||||
if( (lt_time_ms() - clicks_time) > 400 ) clicks = 0;
|
if( (lt_time_ms() - clicks_time) > 400 ) clicks = 0;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
for( GLEQevent e; gleqNextEvent(&e); gleqFreeEvent(&e) )
|
for( GLEQevent e; gleqNextEvent(&e); gleqFreeEvent(&e) )
|
||||||
if( lt_events & e.type )
|
if( lt_events & e.type )
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
default:
|
default:
|
||||||
break; case GLEQ_WINDOW_CLOSED: // it used to be ok. depends on window_swap() flow
|
break; case GLEQ_WINDOW_CLOSED: // it used to be ok. depends on window_swap() flow
|
||||||
rc += lt_emit_event(L, "quit", NULL);
|
rc += lt_emit_event(L, "quit", NULL);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
break; case GLEQ_WINDOW_MOVED:
|
break; case GLEQ_WINDOW_MOVED:
|
||||||
lt_wx = e.pos.x;
|
lt_wx = e.pos.x;
|
||||||
lt_wy = e.pos.y;
|
lt_wy = e.pos.y;
|
||||||
|
|
||||||
break; case GLEQ_WINDOW_RESIZED:
|
break; case GLEQ_WINDOW_RESIZED:
|
||||||
rc += lt_emit_event(L, "resized", "dd", lt_ww = e.size.width, lt_wh = e.size.height);
|
rc += lt_emit_event(L, "resized", "dd", lt_ww = e.size.width, lt_wh = e.size.height);
|
||||||
lt_resizesurface(lt_getsurface(lt_window()), lt_ww, lt_wh);
|
lt_resizesurface(lt_getsurface(lt_window()), lt_ww, lt_wh);
|
||||||
|
|
||||||
break; case GLEQ_WINDOW_REFRESH:
|
break; case GLEQ_WINDOW_REFRESH:
|
||||||
rc += lt_emit_event(L, "exposed", NULL);
|
rc += lt_emit_event(L, "exposed", NULL);
|
||||||
rencache_invalidate();
|
rencache_invalidate();
|
||||||
|
|
||||||
break; case GLEQ_FILE_DROPPED:
|
break; case GLEQ_FILE_DROPPED:
|
||||||
rc += lt_emit_event(L, "filedropped", "sdd", e.file.paths[0], lt_mx, lt_my);
|
rc += lt_emit_event(L, "filedropped", "sdd", e.file.paths[0], lt_mx, lt_my);
|
||||||
|
|
||||||
break; case GLEQ_KEY_PRESSED:
|
break; case GLEQ_KEY_PRESSED:
|
||||||
case GLEQ_KEY_REPEATED:
|
case GLEQ_KEY_REPEATED:
|
||||||
rc += lt_emit_event(L, "keypressed", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
|
rc += lt_emit_event(L, "keypressed", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
|
||||||
goto bottom;
|
goto bottom;
|
||||||
|
|
||||||
break; case GLEQ_KEY_RELEASED:
|
break; case GLEQ_KEY_RELEASED:
|
||||||
rc += lt_emit_event(L, "keyreleased", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
|
rc += lt_emit_event(L, "keyreleased", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
|
||||||
goto bottom;
|
goto bottom;
|
||||||
|
|
||||||
break; case GLEQ_CODEPOINT_INPUT:
|
break; case GLEQ_CODEPOINT_INPUT:
|
||||||
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
|
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
|
||||||
|
|
||||||
break; case GLEQ_BUTTON_PRESSED:
|
break; case GLEQ_BUTTON_PRESSED:
|
||||||
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
|
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
|
||||||
|
|
||||||
break; case GLEQ_BUTTON_RELEASED:
|
break; case GLEQ_BUTTON_RELEASED:
|
||||||
clicks += e.mouse.button == GLFW_MOUSE_BUTTON_1;
|
clicks += e.mouse.button == GLFW_MOUSE_BUTTON_1;
|
||||||
clicks_time = lt_time_ms();
|
clicks_time = lt_time_ms();
|
||||||
rc += lt_emit_event(L, "mousereleased", "sdd", lt_button_name(e.mouse.button), lt_mx, lt_my);
|
rc += lt_emit_event(L, "mousereleased", "sdd", lt_button_name(e.mouse.button), lt_mx, lt_my);
|
||||||
|
|
||||||
break; case GLEQ_CURSOR_MOVED:
|
break; case GLEQ_CURSOR_MOVED:
|
||||||
lt_mx = e.pos.x - lt_wx, lt_my = e.pos.y - lt_wy;
|
lt_mx = e.pos.x - lt_wx, lt_my = e.pos.y - lt_wy;
|
||||||
rc += lt_emit_event(L, "mousemoved", "dddd", lt_mx, lt_my, lt_mx - prevx, lt_my - prevy);
|
rc += lt_emit_event(L, "mousemoved", "dddd", lt_mx, lt_my, lt_mx - prevx, lt_my - prevy);
|
||||||
prevx = lt_mx, prevy = lt_my;
|
prevx = lt_mx, prevy = lt_my;
|
||||||
|
|
||||||
break; case GLEQ_SCROLLED:
|
break; case GLEQ_SCROLLED:
|
||||||
rc += lt_emit_event(L, "mousewheel", "f", e.scroll.y);
|
rc += lt_emit_event(L, "mousewheel", "f", e.scroll.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom:;
|
bottom:;
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,424 +1,424 @@
|
||||||
/*
|
/*
|
||||||
* GLEQ - A basic event queue for GLFW 3
|
* GLEQ - A basic event queue for GLFW 3
|
||||||
* Copyright © Camilla Löwy <elmindreda@glfw.org>
|
* Copyright © Camilla Löwy <elmindreda@glfw.org>
|
||||||
*
|
*
|
||||||
* This software is provided 'as-is', without any express or implied
|
* This software is provided 'as-is', without any express or implied
|
||||||
* warranty. In no event will the authors be held liable for any damages
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
* arising from the use of this software.
|
* arising from the use of this software.
|
||||||
*
|
*
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
* including commercial applications, and to alter it and redistribute it
|
* including commercial applications, and to alter it and redistribute it
|
||||||
* freely, subject to the following restrictions:
|
* freely, subject to the following restrictions:
|
||||||
*
|
*
|
||||||
* 1. The origin of this software must not be misrepresented; you must not
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
* claim that you wrote the original software. If you use this software
|
* claim that you wrote the original software. If you use this software
|
||||||
* in a product, an acknowledgment in the product documentation would
|
* in a product, an acknowledgment in the product documentation would
|
||||||
* be appreciated but is not required.
|
* be appreciated but is not required.
|
||||||
*
|
*
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not
|
* 2. Altered source versions must be plainly marked as such, and must not
|
||||||
* be misrepresented as being the original software.
|
* be misrepresented as being the original software.
|
||||||
*
|
*
|
||||||
* 3. This notice may not be removed or altered from any source
|
* 3. This notice may not be removed or altered from any source
|
||||||
* distribution.
|
* distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GLEQ_HEADER_FILE
|
#ifndef GLEQ_HEADER_FILE
|
||||||
#define GLEQ_HEADER_FILE
|
#define GLEQ_HEADER_FILE
|
||||||
|
|
||||||
// #include <GLFW/glfw3.h>
|
// #include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#ifdef GLEQ_STATIC
|
#ifdef GLEQ_STATIC
|
||||||
#define GLEQDEF static
|
#define GLEQDEF static
|
||||||
#else
|
#else
|
||||||
#define GLEQDEF extern
|
#define GLEQDEF extern
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
GLEQ_NONE = 0,
|
GLEQ_NONE = 0,
|
||||||
GLEQ_WINDOW_MOVED = 1<<1,
|
GLEQ_WINDOW_MOVED = 1<<1,
|
||||||
GLEQ_WINDOW_RESIZED = 1<<2,
|
GLEQ_WINDOW_RESIZED = 1<<2,
|
||||||
GLEQ_WINDOW_CLOSED = 1<<3,
|
GLEQ_WINDOW_CLOSED = 1<<3,
|
||||||
GLEQ_WINDOW_REFRESH = 1<<4,
|
GLEQ_WINDOW_REFRESH = 1<<4,
|
||||||
GLEQ_WINDOW_FOCUSED = 1<<5,
|
GLEQ_WINDOW_FOCUSED = 1<<5,
|
||||||
GLEQ_WINDOW_DEFOCUSED = 1<<6,
|
GLEQ_WINDOW_DEFOCUSED = 1<<6,
|
||||||
GLEQ_WINDOW_ICONIFIED = 1<<7,
|
GLEQ_WINDOW_ICONIFIED = 1<<7,
|
||||||
GLEQ_WINDOW_UNICONIFIED = 1<<8,
|
GLEQ_WINDOW_UNICONIFIED = 1<<8,
|
||||||
GLEQ_FRAMEBUFFER_RESIZED = 1<<9,
|
GLEQ_FRAMEBUFFER_RESIZED = 1<<9,
|
||||||
GLEQ_BUTTON_PRESSED = 1<<10,
|
GLEQ_BUTTON_PRESSED = 1<<10,
|
||||||
GLEQ_BUTTON_RELEASED = 1<<11,
|
GLEQ_BUTTON_RELEASED = 1<<11,
|
||||||
GLEQ_CURSOR_MOVED = 1<<12,
|
GLEQ_CURSOR_MOVED = 1<<12,
|
||||||
GLEQ_CURSOR_ENTERED = 1<<13,
|
GLEQ_CURSOR_ENTERED = 1<<13,
|
||||||
GLEQ_CURSOR_LEFT = 1<<14,
|
GLEQ_CURSOR_LEFT = 1<<14,
|
||||||
GLEQ_SCROLLED = 1<<15,
|
GLEQ_SCROLLED = 1<<15,
|
||||||
GLEQ_KEY_PRESSED = 1<<16,
|
GLEQ_KEY_PRESSED = 1<<16,
|
||||||
GLEQ_KEY_REPEATED = 1<<17,
|
GLEQ_KEY_REPEATED = 1<<17,
|
||||||
GLEQ_KEY_RELEASED = 1<<18,
|
GLEQ_KEY_RELEASED = 1<<18,
|
||||||
GLEQ_CODEPOINT_INPUT = 1<<19,
|
GLEQ_CODEPOINT_INPUT = 1<<19,
|
||||||
GLEQ_MONITOR_CONNECTED = 1<<20,
|
GLEQ_MONITOR_CONNECTED = 1<<20,
|
||||||
GLEQ_MONITOR_DISCONNECTED = 1<<21,
|
GLEQ_MONITOR_DISCONNECTED = 1<<21,
|
||||||
#if GLFW_VERSION_MINOR >= 1
|
#if GLFW_VERSION_MINOR >= 1
|
||||||
GLEQ_FILE_DROPPED = 1<<22,
|
GLEQ_FILE_DROPPED = 1<<22,
|
||||||
#endif
|
#endif
|
||||||
#if GLFW_VERSION_MINOR >= 2
|
#if GLFW_VERSION_MINOR >= 2
|
||||||
GLEQ_JOYSTICK_CONNECTED = 1<<23,
|
GLEQ_JOYSTICK_CONNECTED = 1<<23,
|
||||||
GLEQ_JOYSTICK_DISCONNECTED = 1<<24,
|
GLEQ_JOYSTICK_DISCONNECTED = 1<<24,
|
||||||
#endif
|
#endif
|
||||||
#if GLFW_VERSION_MINOR >= 3
|
#if GLFW_VERSION_MINOR >= 3
|
||||||
GLEQ_WINDOW_MAXIMIZED = 1<<25,
|
GLEQ_WINDOW_MAXIMIZED = 1<<25,
|
||||||
GLEQ_WINDOW_UNMAXIMIZED = 1<<26,
|
GLEQ_WINDOW_UNMAXIMIZED = 1<<26,
|
||||||
GLEQ_WINDOW_SCALE_CHANGED = 1<<27,
|
GLEQ_WINDOW_SCALE_CHANGED = 1<<27,
|
||||||
#endif
|
#endif
|
||||||
} GLEQtype;
|
} GLEQtype;
|
||||||
|
|
||||||
typedef struct GLEQevent
|
typedef struct GLEQevent
|
||||||
{
|
{
|
||||||
unsigned/*GLEQtype*/ type;
|
unsigned/*GLEQtype*/ type;
|
||||||
union {
|
union {
|
||||||
GLFWwindow* window;
|
GLFWwindow* window;
|
||||||
GLFWmonitor* monitor;
|
GLFWmonitor* monitor;
|
||||||
int joystick;
|
int joystick;
|
||||||
};
|
};
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
} pos;
|
} pos;
|
||||||
struct {
|
struct {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
} size;
|
} size;
|
||||||
struct {
|
struct {
|
||||||
double x;
|
double x;
|
||||||
double y;
|
double y;
|
||||||
} scroll;
|
} scroll;
|
||||||
struct {
|
struct {
|
||||||
int key;
|
int key;
|
||||||
int scancode;
|
int scancode;
|
||||||
int mods;
|
int mods;
|
||||||
} keyboard;
|
} keyboard;
|
||||||
struct {
|
struct {
|
||||||
int button;
|
int button;
|
||||||
int mods;
|
int mods;
|
||||||
} mouse;
|
} mouse;
|
||||||
unsigned int codepoint;
|
unsigned int codepoint;
|
||||||
#if GLFW_VERSION_MINOR >= 1
|
#if GLFW_VERSION_MINOR >= 1
|
||||||
struct {
|
struct {
|
||||||
char** paths;
|
char** paths;
|
||||||
int count;
|
int count;
|
||||||
} file;
|
} file;
|
||||||
#endif
|
#endif
|
||||||
#if GLFW_VERSION_MINOR >= 3
|
#if GLFW_VERSION_MINOR >= 3
|
||||||
struct {
|
struct {
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
} scale;
|
} scale;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
} GLEQevent;
|
} GLEQevent;
|
||||||
|
|
||||||
GLEQDEF void gleqInit(void);
|
GLEQDEF void gleqInit(void);
|
||||||
GLEQDEF void gleqTrackWindow(GLFWwindow* window);
|
GLEQDEF void gleqTrackWindow(GLFWwindow* window);
|
||||||
|
|
||||||
GLEQDEF int gleqNextEvent(GLEQevent* event);
|
GLEQDEF int gleqNextEvent(GLEQevent* event);
|
||||||
GLEQDEF void gleqFreeEvent(GLEQevent* event);
|
GLEQDEF void gleqFreeEvent(GLEQevent* event);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef GLEQ_IMPLEMENTATION
|
#ifdef GLEQ_IMPLEMENTATION
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#ifndef GLEQ_CAPACITY
|
#ifndef GLEQ_CAPACITY
|
||||||
#define GLEQ_CAPACITY 1024
|
#define GLEQ_CAPACITY 1024
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct
|
static struct
|
||||||
{
|
{
|
||||||
GLEQevent events[GLEQ_CAPACITY];
|
GLEQevent events[GLEQ_CAPACITY];
|
||||||
size_t head;
|
size_t head;
|
||||||
size_t tail;
|
size_t tail;
|
||||||
} gleq_queue = { {0}, 0, 0 };
|
} gleq_queue = { {0}, 0, 0 };
|
||||||
|
|
||||||
static char* gleq_strdup(const char* string)
|
static char* gleq_strdup(const char* string)
|
||||||
{
|
{
|
||||||
const size_t size = strlen(string) + 1;
|
const size_t size = strlen(string) + 1;
|
||||||
char* result = (char*) malloc(size);
|
char* result = (char*) malloc(size);
|
||||||
memcpy(result, string, size);
|
memcpy(result, string, size);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLEQevent* gleq_new_event(void)
|
static GLEQevent* gleq_new_event(void)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_queue.events + gleq_queue.head;
|
GLEQevent* event = gleq_queue.events + gleq_queue.head;
|
||||||
gleq_queue.head = (gleq_queue.head + 1) % GLEQ_CAPACITY;
|
gleq_queue.head = (gleq_queue.head + 1) % GLEQ_CAPACITY;
|
||||||
assert(gleq_queue.head != gleq_queue.tail);
|
assert(gleq_queue.head != gleq_queue.tail);
|
||||||
memset(event, 0, sizeof(GLEQevent));
|
memset(event, 0, sizeof(GLEQevent));
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_window_pos_callback(GLFWwindow* window, int x, int y)
|
static void gleq_window_pos_callback(GLFWwindow* window, int x, int y)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_WINDOW_MOVED;
|
event->type = GLEQ_WINDOW_MOVED;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->pos.x = x;
|
event->pos.x = x;
|
||||||
event->pos.y = y;
|
event->pos.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_window_size_callback(GLFWwindow* window, int width, int height)
|
static void gleq_window_size_callback(GLFWwindow* window, int width, int height)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_WINDOW_RESIZED;
|
event->type = GLEQ_WINDOW_RESIZED;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->size.width = width;
|
event->size.width = width;
|
||||||
event->size.height = height;
|
event->size.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_window_close_callback(GLFWwindow* window)
|
static void gleq_window_close_callback(GLFWwindow* window)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_WINDOW_CLOSED;
|
event->type = GLEQ_WINDOW_CLOSED;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_window_refresh_callback(GLFWwindow* window)
|
static void gleq_window_refresh_callback(GLFWwindow* window)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_WINDOW_REFRESH;
|
event->type = GLEQ_WINDOW_REFRESH;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_window_focus_callback(GLFWwindow* window, int focused)
|
static void gleq_window_focus_callback(GLFWwindow* window, int focused)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->window = window;
|
event->window = window;
|
||||||
|
|
||||||
if (focused)
|
if (focused)
|
||||||
event->type = GLEQ_WINDOW_FOCUSED;
|
event->type = GLEQ_WINDOW_FOCUSED;
|
||||||
else
|
else
|
||||||
event->type = GLEQ_WINDOW_DEFOCUSED;
|
event->type = GLEQ_WINDOW_DEFOCUSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_window_iconify_callback(GLFWwindow* window, int iconified)
|
static void gleq_window_iconify_callback(GLFWwindow* window, int iconified)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->window = window;
|
event->window = window;
|
||||||
|
|
||||||
if (iconified)
|
if (iconified)
|
||||||
event->type = GLEQ_WINDOW_ICONIFIED;
|
event->type = GLEQ_WINDOW_ICONIFIED;
|
||||||
else
|
else
|
||||||
event->type = GLEQ_WINDOW_UNICONIFIED;
|
event->type = GLEQ_WINDOW_UNICONIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
static void gleq_framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_FRAMEBUFFER_RESIZED;
|
event->type = GLEQ_FRAMEBUFFER_RESIZED;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->size.width = width;
|
event->size.width = width;
|
||||||
event->size.height = height;
|
event->size.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
|
static void gleq_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->mouse.button = button;
|
event->mouse.button = button;
|
||||||
event->mouse.mods = mods;
|
event->mouse.mods = mods;
|
||||||
|
|
||||||
if (action == GLFW_PRESS)
|
if (action == GLFW_PRESS)
|
||||||
event->type = GLEQ_BUTTON_PRESSED;
|
event->type = GLEQ_BUTTON_PRESSED;
|
||||||
else if (action == GLFW_RELEASE)
|
else if (action == GLFW_RELEASE)
|
||||||
event->type = GLEQ_BUTTON_RELEASED;
|
event->type = GLEQ_BUTTON_RELEASED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_cursor_pos_callback(GLFWwindow* window, double x, double y)
|
static void gleq_cursor_pos_callback(GLFWwindow* window, double x, double y)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_CURSOR_MOVED;
|
event->type = GLEQ_CURSOR_MOVED;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->pos.x = (int) x;
|
event->pos.x = (int) x;
|
||||||
event->pos.y = (int) y;
|
event->pos.y = (int) y;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_cursor_enter_callback(GLFWwindow* window, int entered)
|
static void gleq_cursor_enter_callback(GLFWwindow* window, int entered)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->window = window;
|
event->window = window;
|
||||||
|
|
||||||
if (entered)
|
if (entered)
|
||||||
event->type = GLEQ_CURSOR_ENTERED;
|
event->type = GLEQ_CURSOR_ENTERED;
|
||||||
else
|
else
|
||||||
event->type = GLEQ_CURSOR_LEFT;
|
event->type = GLEQ_CURSOR_LEFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_scroll_callback(GLFWwindow* window, double x, double y)
|
static void gleq_scroll_callback(GLFWwindow* window, double x, double y)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_SCROLLED;
|
event->type = GLEQ_SCROLLED;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->scroll.x = x;
|
event->scroll.x = x;
|
||||||
event->scroll.y = y;
|
event->scroll.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
static void gleq_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->keyboard.key = key;
|
event->keyboard.key = key;
|
||||||
event->keyboard.scancode = scancode;
|
event->keyboard.scancode = scancode;
|
||||||
event->keyboard.mods = mods;
|
event->keyboard.mods = mods;
|
||||||
|
|
||||||
if (action == GLFW_PRESS)
|
if (action == GLFW_PRESS)
|
||||||
event->type = GLEQ_KEY_PRESSED;
|
event->type = GLEQ_KEY_PRESSED;
|
||||||
else if (action == GLFW_RELEASE)
|
else if (action == GLFW_RELEASE)
|
||||||
event->type = GLEQ_KEY_RELEASED;
|
event->type = GLEQ_KEY_RELEASED;
|
||||||
else if (action == GLFW_REPEAT)
|
else if (action == GLFW_REPEAT)
|
||||||
event->type = GLEQ_KEY_REPEATED;
|
event->type = GLEQ_KEY_REPEATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void (*gleq_char_callback_prev)(GLFWwindow* window, unsigned int codepoint) = 0;
|
static void (*gleq_char_callback_prev)(GLFWwindow* window, unsigned int codepoint) = 0;
|
||||||
static void gleq_char_callback(GLFWwindow* window, unsigned int codepoint)
|
static void gleq_char_callback(GLFWwindow* window, unsigned int codepoint)
|
||||||
{
|
{
|
||||||
if( gleq_char_callback_prev )
|
if( gleq_char_callback_prev )
|
||||||
gleq_char_callback_prev(window, codepoint);
|
gleq_char_callback_prev(window, codepoint);
|
||||||
|
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_CODEPOINT_INPUT;
|
event->type = GLEQ_CODEPOINT_INPUT;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->codepoint = codepoint;
|
event->codepoint = codepoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_monitor_callback(GLFWmonitor* monitor, int action)
|
static void gleq_monitor_callback(GLFWmonitor* monitor, int action)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->monitor = monitor;
|
event->monitor = monitor;
|
||||||
|
|
||||||
if (action == GLFW_CONNECTED)
|
if (action == GLFW_CONNECTED)
|
||||||
event->type = GLEQ_MONITOR_CONNECTED;
|
event->type = GLEQ_MONITOR_CONNECTED;
|
||||||
else if (action == GLFW_DISCONNECTED)
|
else if (action == GLFW_DISCONNECTED)
|
||||||
event->type = GLEQ_MONITOR_DISCONNECTED;
|
event->type = GLEQ_MONITOR_DISCONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if GLFW_VERSION_MINOR >= 1
|
#if GLFW_VERSION_MINOR >= 1
|
||||||
static void gleq_file_drop_callback(GLFWwindow* window, int count, const char** paths)
|
static void gleq_file_drop_callback(GLFWwindow* window, int count, const char** paths)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->type = GLEQ_FILE_DROPPED;
|
event->type = GLEQ_FILE_DROPPED;
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->file.paths = (char**) malloc(count * sizeof(char*));
|
event->file.paths = (char**) malloc(count * sizeof(char*));
|
||||||
event->file.count = count;
|
event->file.count = count;
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
event->file.paths[count] = gleq_strdup(paths[count]);
|
event->file.paths[count] = gleq_strdup(paths[count]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if GLFW_VERSION_MINOR >= 2
|
#if GLFW_VERSION_MINOR >= 2
|
||||||
static void gleq_joystick_callback(int jid, int action)
|
static void gleq_joystick_callback(int jid, int action)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->joystick = jid;
|
event->joystick = jid;
|
||||||
|
|
||||||
if (action == GLFW_CONNECTED)
|
if (action == GLFW_CONNECTED)
|
||||||
event->type = GLEQ_JOYSTICK_CONNECTED;
|
event->type = GLEQ_JOYSTICK_CONNECTED;
|
||||||
else if (action == GLFW_DISCONNECTED)
|
else if (action == GLFW_DISCONNECTED)
|
||||||
event->type = GLEQ_JOYSTICK_DISCONNECTED;
|
event->type = GLEQ_JOYSTICK_DISCONNECTED;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if GLFW_VERSION_MINOR >= 3
|
#if GLFW_VERSION_MINOR >= 3
|
||||||
static void gleq_window_maximize_callback(GLFWwindow* window, int maximized)
|
static void gleq_window_maximize_callback(GLFWwindow* window, int maximized)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->window = window;
|
event->window = window;
|
||||||
|
|
||||||
if (maximized)
|
if (maximized)
|
||||||
event->type = GLEQ_WINDOW_MAXIMIZED;
|
event->type = GLEQ_WINDOW_MAXIMIZED;
|
||||||
else
|
else
|
||||||
event->type = GLEQ_WINDOW_UNMAXIMIZED;
|
event->type = GLEQ_WINDOW_UNMAXIMIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gleq_window_content_scale_callback(GLFWwindow* window, float xscale, float yscale)
|
static void gleq_window_content_scale_callback(GLFWwindow* window, float xscale, float yscale)
|
||||||
{
|
{
|
||||||
GLEQevent* event = gleq_new_event();
|
GLEQevent* event = gleq_new_event();
|
||||||
event->window = window;
|
event->window = window;
|
||||||
event->type = GLEQ_WINDOW_SCALE_CHANGED;
|
event->type = GLEQ_WINDOW_SCALE_CHANGED;
|
||||||
event->scale.x = xscale;
|
event->scale.x = xscale;
|
||||||
event->scale.y = yscale;
|
event->scale.y = yscale;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GLEQDEF void gleqInit(void)
|
GLEQDEF void gleqInit(void)
|
||||||
{
|
{
|
||||||
glfwSetMonitorCallback(gleq_monitor_callback);
|
glfwSetMonitorCallback(gleq_monitor_callback);
|
||||||
#if GLFW_VERSION_MINOR >= 2
|
#if GLFW_VERSION_MINOR >= 2
|
||||||
glfwSetJoystickCallback(gleq_joystick_callback);
|
glfwSetJoystickCallback(gleq_joystick_callback);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
GLEQDEF void gleqTrackWindow(GLFWwindow* window)
|
GLEQDEF void gleqTrackWindow(GLFWwindow* window)
|
||||||
{
|
{
|
||||||
glfwSetWindowPosCallback(window, gleq_window_pos_callback);
|
glfwSetWindowPosCallback(window, gleq_window_pos_callback);
|
||||||
glfwSetWindowSizeCallback(window, gleq_window_size_callback);
|
glfwSetWindowSizeCallback(window, gleq_window_size_callback);
|
||||||
glfwSetWindowCloseCallback(window, gleq_window_close_callback);
|
glfwSetWindowCloseCallback(window, gleq_window_close_callback);
|
||||||
glfwSetWindowRefreshCallback(window, gleq_window_refresh_callback);
|
glfwSetWindowRefreshCallback(window, gleq_window_refresh_callback);
|
||||||
glfwSetWindowFocusCallback(window, gleq_window_focus_callback);
|
glfwSetWindowFocusCallback(window, gleq_window_focus_callback);
|
||||||
glfwSetWindowIconifyCallback(window, gleq_window_iconify_callback);
|
glfwSetWindowIconifyCallback(window, gleq_window_iconify_callback);
|
||||||
glfwSetFramebufferSizeCallback(window, gleq_framebuffer_size_callback);
|
glfwSetFramebufferSizeCallback(window, gleq_framebuffer_size_callback);
|
||||||
glfwSetMouseButtonCallback(window, gleq_mouse_button_callback);
|
glfwSetMouseButtonCallback(window, gleq_mouse_button_callback);
|
||||||
glfwSetCursorPosCallback(window, gleq_cursor_pos_callback);
|
glfwSetCursorPosCallback(window, gleq_cursor_pos_callback);
|
||||||
glfwSetCursorEnterCallback(window, gleq_cursor_enter_callback);
|
glfwSetCursorEnterCallback(window, gleq_cursor_enter_callback);
|
||||||
glfwSetScrollCallback(window, gleq_scroll_callback);
|
glfwSetScrollCallback(window, gleq_scroll_callback);
|
||||||
glfwSetKeyCallback(window, gleq_key_callback);
|
glfwSetKeyCallback(window, gleq_key_callback);
|
||||||
gleq_char_callback_prev = //< @r-lyeh
|
gleq_char_callback_prev = //< @r-lyeh
|
||||||
glfwSetCharCallback(window, gleq_char_callback);
|
glfwSetCharCallback(window, gleq_char_callback);
|
||||||
#if GLFW_VERSION_MINOR >= 1
|
#if GLFW_VERSION_MINOR >= 1
|
||||||
glfwSetDropCallback(window, gleq_file_drop_callback);
|
glfwSetDropCallback(window, gleq_file_drop_callback);
|
||||||
#endif
|
#endif
|
||||||
#if GLFW_VERSION_MINOR >= 3
|
#if GLFW_VERSION_MINOR >= 3
|
||||||
glfwSetWindowMaximizeCallback(window, gleq_window_maximize_callback);
|
glfwSetWindowMaximizeCallback(window, gleq_window_maximize_callback);
|
||||||
glfwSetWindowContentScaleCallback(window, gleq_window_content_scale_callback);
|
glfwSetWindowContentScaleCallback(window, gleq_window_content_scale_callback);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
GLEQDEF int gleqNextEvent(GLEQevent* event)
|
GLEQDEF int gleqNextEvent(GLEQevent* event)
|
||||||
{
|
{
|
||||||
memset(event, 0, sizeof(GLEQevent));
|
memset(event, 0, sizeof(GLEQevent));
|
||||||
|
|
||||||
if (gleq_queue.head != gleq_queue.tail)
|
if (gleq_queue.head != gleq_queue.tail)
|
||||||
{
|
{
|
||||||
*event = gleq_queue.events[gleq_queue.tail];
|
*event = gleq_queue.events[gleq_queue.tail];
|
||||||
gleq_queue.tail = (gleq_queue.tail + 1) % GLEQ_CAPACITY;
|
gleq_queue.tail = (gleq_queue.tail + 1) % GLEQ_CAPACITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return event->type != GLEQ_NONE;
|
return event->type != GLEQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLEQDEF void gleqFreeEvent(GLEQevent* event)
|
GLEQDEF void gleqFreeEvent(GLEQevent* event)
|
||||||
{
|
{
|
||||||
#if GLFW_VERSION_MINOR >= 1
|
#if GLFW_VERSION_MINOR >= 1
|
||||||
if (event->type == GLEQ_FILE_DROPPED)
|
if (event->type == GLEQ_FILE_DROPPED)
|
||||||
{
|
{
|
||||||
while (event->file.count--)
|
while (event->file.count--)
|
||||||
free(event->file.paths[event->file.count]);
|
free(event->file.paths[event->file.count]);
|
||||||
|
|
||||||
free(event->file.paths);
|
free(event->file.paths);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memset(event, 0, sizeof(GLEQevent));
|
memset(event, 0, sizeof(GLEQevent));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* GLEQ_IMPLEMENTATION */
|
#endif /* GLEQ_IMPLEMENTATION */
|
||||||
|
|
||||||
#endif /* GLEQ_HEADER_FILE */
|
#endif /* GLEQ_HEADER_FILE */
|
||||||
|
|
58480
engine/split/3rd_lua.h
58480
engine/split/3rd_lua.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
185074
engine/split/3rd_miniaudio.h
185074
engine/split/3rd_miniaudio.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,483 +1,483 @@
|
||||||
// file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain)
|
// file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain)
|
||||||
// - rlyeh, public domain
|
// - rlyeh, public domain
|
||||||
//
|
//
|
||||||
// changelog:
|
// changelog:
|
||||||
// - ported to V4K api
|
// - ported to V4K api
|
||||||
// - namespaced symbols
|
// - namespaced symbols
|
||||||
// - diverse win32 fixes
|
// - diverse win32 fixes
|
||||||
// - adaptive cols/rows
|
// - adaptive cols/rows
|
||||||
// - removed nk_begin()/nk_end() pairs
|
// - removed nk_begin()/nk_end() pairs
|
||||||
// - dangling nk_group_begin/end() pairs
|
// - dangling nk_group_begin/end() pairs
|
||||||
// - simplified file<->media_group concept
|
// - simplified file<->media_group concept
|
||||||
// - minor cosmetics
|
// - minor cosmetics
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <direct.h> // _getcwd()
|
#include <direct.h> // _getcwd()
|
||||||
#else
|
#else
|
||||||
#include <unistd.h> // getcwd()
|
#include <unistd.h> // getcwd()
|
||||||
#include <pwd.h> // getpwuid()
|
#include <pwd.h> // getpwuid()
|
||||||
#define _popen popen
|
#define _popen popen
|
||||||
#define _pclose pclose
|
#define _pclose pclose
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char** old_file_list(const char *cwd, const char *masks) {
|
const char** old_file_list(const char *cwd, const char *masks) {
|
||||||
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
|
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
|
||||||
|
|
||||||
static __thread array(char*) list = 0;
|
static __thread array(char*) list = 0;
|
||||||
const char *arg0 = cwd; // app_path();
|
const char *arg0 = cwd; // app_path();
|
||||||
int larg0 = strlen(arg0);
|
int larg0 = strlen(arg0);
|
||||||
|
|
||||||
for( int i = 0; i < array_count(list); ++i ) {
|
for( int i = 0; i < array_count(list); ++i ) {
|
||||||
FREE(list[i]);
|
FREE(list[i]);
|
||||||
}
|
}
|
||||||
array_resize(list, 0);//array_free(list);
|
array_resize(list, 0);//array_free(list);
|
||||||
|
|
||||||
for each_substring(masks,";",it) {
|
for each_substring(masks,";",it) {
|
||||||
int recurse = !!strstr(it, "**");
|
int recurse = !!strstr(it, "**");
|
||||||
#if is(win32)
|
#if is(win32)
|
||||||
char *glob = va("dir %s/b/o:n \"%s\\%s\" 2> NUL", recurse ? "/s":"", cwd, it);
|
char *glob = va("dir %s/b/o:n \"%s\\%s\" 2> NUL", recurse ? "/s":"", cwd, it);
|
||||||
#else // linux, osx
|
#else // linux, osx
|
||||||
char *glob = va("find %s %s -name \"%s\" | sort", cwd, !recurse ? "-maxdepth 1":"-type f", it);
|
char *glob = va("find %s %s -name \"%s\" | sort", cwd, !recurse ? "-maxdepth 1":"-type f", it);
|
||||||
#endif
|
#endif
|
||||||
for( FILE *in = _popen(glob, "r"); in; _pclose(in), in = 0) {
|
for( FILE *in = _popen(glob, "r"); in; _pclose(in), in = 0) {
|
||||||
char buf[1024], *line = buf;
|
char buf[1024], *line = buf;
|
||||||
while( fgets(buf, sizeof(buf), in) ) {
|
while( fgets(buf, sizeof(buf), in) ) {
|
||||||
// clean up
|
// clean up
|
||||||
if( strstr(line, arg0) ) line = buf + larg0;
|
if( strstr(line, arg0) ) line = buf + larg0;
|
||||||
if( !memcmp(line, "./", 2) ) line += 2;
|
if( !memcmp(line, "./", 2) ) line += 2;
|
||||||
int len = strlen(line); while( len > 0 && line[len-1] < 32 ) line[--len] = 0;
|
int len = strlen(line); while( len > 0 && line[len-1] < 32 ) line[--len] = 0;
|
||||||
if( line[0] == '\0' ) continue;
|
if( line[0] == '\0' ) continue;
|
||||||
// do not insert system folders/files
|
// do not insert system folders/files
|
||||||
for(int i = 0; i < len; ++i ) if(line[i] == '\\') line[i] = '/';
|
for(int i = 0; i < len; ++i ) if(line[i] == '\\') line[i] = '/';
|
||||||
if( line[0] == '.' ) if( !strcmp(line,".git") || !strcmp(line,".vs") || !strcmp(line,".") || !strcmp(line,"..") ) continue;
|
if( line[0] == '.' ) if( !strcmp(line,".git") || !strcmp(line,".vs") || !strcmp(line,".") || !strcmp(line,"..") ) continue;
|
||||||
if( strstr(line, "/.") ) continue;
|
if( strstr(line, "/.") ) continue;
|
||||||
// insert copy
|
// insert copy
|
||||||
#if is(win32)
|
#if is(win32)
|
||||||
char *copy = STRDUP(line); // full path already provided
|
char *copy = STRDUP(line); // full path already provided
|
||||||
#else
|
#else
|
||||||
// while(line[0] == '/') ++line;
|
// while(line[0] == '/') ++line;
|
||||||
char *copy = STRDUP(va("%s%s", cwd, line)); // need to prepend path
|
char *copy = STRDUP(va("%s%s", cwd, line)); // need to prepend path
|
||||||
#endif
|
#endif
|
||||||
array_push(list, copy);
|
array_push(list, copy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
array_push(list, 0); // terminator
|
array_push(list, 0); // terminator
|
||||||
return (const char**)list;
|
return (const char**)list;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
#define BROWSER_PRINTF(...) do {} while(0)
|
#define BROWSER_PRINTF(...) do {} while(0)
|
||||||
#else
|
#else
|
||||||
#define BROWSER_PRINTF printf
|
#define BROWSER_PRINTF printf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum browser_groups {
|
enum browser_groups {
|
||||||
BROWSER_FOLDER,
|
BROWSER_FOLDER,
|
||||||
BROWSER_HOME,
|
BROWSER_HOME,
|
||||||
BROWSER_DESKTOP,
|
BROWSER_DESKTOP,
|
||||||
BROWSER_COMPUTER,
|
BROWSER_COMPUTER,
|
||||||
BROWSER_PROJECT,
|
BROWSER_PROJECT,
|
||||||
BROWSER_MAXFOLDERS,
|
BROWSER_MAXFOLDERS,
|
||||||
|
|
||||||
BROWSER_MAXTYPES = 64,
|
BROWSER_MAXTYPES = 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct browser_media_group {
|
struct browser_media_group {
|
||||||
unsigned icon;
|
unsigned icon;
|
||||||
const char *extensions;
|
const char *extensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct browser_media {
|
struct browser_media {
|
||||||
int font;
|
int font;
|
||||||
int icon_sheet;
|
int icon_sheet;
|
||||||
struct nk_image custom_folders[BROWSER_MAXFOLDERS];
|
struct nk_image custom_folders[BROWSER_MAXFOLDERS];
|
||||||
struct nk_image custom_files[BROWSER_MAXTYPES];
|
struct nk_image custom_files[BROWSER_MAXTYPES];
|
||||||
struct browser_media_group group[BROWSER_MAXTYPES];
|
struct browser_media_group group[BROWSER_MAXTYPES];
|
||||||
} media = {0};
|
} media = {0};
|
||||||
|
|
||||||
void browser_config_dir(struct nk_image icon, unsigned counter) {
|
void browser_config_dir(struct nk_image icon, unsigned counter) {
|
||||||
if( counter < BROWSER_MAXFOLDERS ) {
|
if( counter < BROWSER_MAXFOLDERS ) {
|
||||||
media.custom_folders[ counter ] = icon;
|
media.custom_folders[ counter ] = icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void browser_config_type(struct nk_image icon, const char *extensions) {
|
void browser_config_type(struct nk_image icon, const char *extensions) {
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
if( counter < BROWSER_MAXTYPES ) {
|
if( counter < BROWSER_MAXTYPES ) {
|
||||||
media.custom_files[ counter ] = icon;
|
media.custom_files[ counter ] = icon;
|
||||||
media.group[ counter ].icon = counter;
|
media.group[ counter ].icon = counter;
|
||||||
media.group[ counter ].extensions = extensions;
|
media.group[ counter ].extensions = extensions;
|
||||||
++counter;
|
++counter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BROWSER_MAX_PATH 512
|
#define BROWSER_MAX_PATH 512
|
||||||
struct browser {
|
struct browser {
|
||||||
/* path */
|
/* path */
|
||||||
char file[BROWSER_MAX_PATH]; // selection
|
char file[BROWSER_MAX_PATH]; // selection
|
||||||
char directory[BROWSER_MAX_PATH]; // current cwd while browsing
|
char directory[BROWSER_MAX_PATH]; // current cwd while browsing
|
||||||
|
|
||||||
char home[BROWSER_MAX_PATH];
|
char home[BROWSER_MAX_PATH];
|
||||||
char desktop[BROWSER_MAX_PATH];
|
char desktop[BROWSER_MAX_PATH];
|
||||||
char computer[BROWSER_MAX_PATH];
|
char computer[BROWSER_MAX_PATH];
|
||||||
char project[BROWSER_MAX_PATH]; // cwd when first invoked
|
char project[BROWSER_MAX_PATH]; // cwd when first invoked
|
||||||
|
|
||||||
/* directory content */
|
/* directory content */
|
||||||
array(char*) files;
|
array(char*) files;
|
||||||
array(char*) directories;
|
array(char*) directories;
|
||||||
size_t file_count;
|
size_t file_count;
|
||||||
size_t dir_count;
|
size_t dir_count;
|
||||||
|
|
||||||
/* filtered directory content */
|
/* filtered directory content */
|
||||||
array(char*) ffiles;
|
array(char*) ffiles;
|
||||||
array(char*) fdirectories;
|
array(char*) fdirectories;
|
||||||
|
|
||||||
/* view mode */
|
/* view mode */
|
||||||
bool listing;
|
bool listing;
|
||||||
float zooming;
|
float zooming;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct nk_image* media_icon_for_file(const char *file) {
|
static struct nk_image* media_icon_for_file(const char *file) {
|
||||||
/* extract extension .xxx from file */
|
/* extract extension .xxx from file */
|
||||||
char *ext = strrchr(file, '.');
|
char *ext = strrchr(file, '.');
|
||||||
if( ext && strlen(ext) < 16 ) {
|
if( ext && strlen(ext) < 16 ) {
|
||||||
char ext_dot[16+1];
|
char ext_dot[16+1];
|
||||||
snprintf(ext_dot, 16, "%s.", ext);
|
snprintf(ext_dot, 16, "%s.", ext);
|
||||||
/* check for all file definition of all groups for fitting extension. skip first group (default) */
|
/* check for all file definition of all groups for fitting extension. skip first group (default) */
|
||||||
for (int i = 1; i < BROWSER_MAXTYPES && media.group[i].extensions; ++i) {
|
for (int i = 1; i < BROWSER_MAXTYPES && media.group[i].extensions; ++i) {
|
||||||
if( strstri(media.group[i].extensions, ext_dot) ) {
|
if( strstri(media.group[i].extensions, ext_dot) ) {
|
||||||
return &media.custom_files[ media.group[i].icon ];
|
return &media.custom_files[ media.group[i].icon ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// return first (default) group
|
// return first (default) group
|
||||||
return &media.custom_files[0];
|
return &media.custom_files[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void browser_reload_directory_content(struct browser *browser, const char *path) {
|
static void browser_reload_directory_content(struct browser *browser, const char *path) {
|
||||||
if(path[0] == '\0') path = va("./");
|
if(path[0] == '\0') path = va("./");
|
||||||
if(!strend(path, "/")) path = va("%s/", path);
|
if(!strend(path, "/")) path = va("%s/", path);
|
||||||
|
|
||||||
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
|
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
|
||||||
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
|
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
|
||||||
|
|
||||||
array_resize(browser->files, 0);
|
array_resize(browser->files, 0);
|
||||||
array_resize(browser->directories, 0);
|
array_resize(browser->directories, 0);
|
||||||
|
|
||||||
BROWSER_PRINTF("searching at %s\n", path);
|
BROWSER_PRINTF("searching at %s\n", path);
|
||||||
|
|
||||||
const char** list = old_file_list(path, "*");
|
const char** list = old_file_list(path, "*");
|
||||||
for( int i = 0; list[i]; ++i ) {
|
for( int i = 0; list[i]; ++i ) {
|
||||||
|
|
||||||
char *absolute = file_pathabs(ifndef(win32, list[i], va("%s/%s", path, list[i]))); // ../dir/./file.ext -> c:/prj/dir/file.ext
|
char *absolute = file_pathabs(ifndef(win32, list[i], va("%s/%s", path, list[i]))); // ../dir/./file.ext -> c:/prj/dir/file.ext
|
||||||
BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) );
|
BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) );
|
||||||
|
|
||||||
if( file_directory(absolute) ) {
|
if( file_directory(absolute) ) {
|
||||||
// remove last '/' if present. ok to overwrite absolute var, file_*() API returns writeable strings.
|
// remove last '/' if present. ok to overwrite absolute var, file_*() API returns writeable strings.
|
||||||
char *dir = absolute; if( dir[ strlen(dir) - 1 ] == '/' ) dir[ strlen(dir) - 1 ] = '\0';
|
char *dir = absolute; if( dir[ strlen(dir) - 1 ] == '/' ) dir[ strlen(dir) - 1 ] = '\0';
|
||||||
|
|
||||||
dir = file_name(dir); // /home/rlyeh/prj/v4k/art -> art
|
dir = file_name(dir); // /home/rlyeh/prj/v4k/art -> art
|
||||||
BROWSER_PRINTF("%s\n", dir);
|
BROWSER_PRINTF("%s\n", dir);
|
||||||
|
|
||||||
if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
|
if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
|
||||||
array_push(browser->directories, STRDUP(dir));
|
array_push(browser->directories, STRDUP(dir));
|
||||||
} else {
|
} else {
|
||||||
const char *fname = file_name(absolute);
|
const char *fname = file_name(absolute);
|
||||||
|
|
||||||
BROWSER_PRINTF("%s\n", fname);
|
BROWSER_PRINTF("%s\n", fname);
|
||||||
|
|
||||||
if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
|
if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
|
||||||
array_push(browser->files, STRDUP(fname));
|
array_push(browser->files, STRDUP(fname));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
browser->file_count = array_count(browser->files);
|
browser->file_count = array_count(browser->files);
|
||||||
browser->dir_count = array_count(browser->directories);
|
browser->dir_count = array_count(browser->directories);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void browser_chdir_and_reload_directory_content(struct browser *browser, const char *path) {
|
static void browser_chdir_and_reload_directory_content(struct browser *browser, const char *path) {
|
||||||
if( path != browser->directory ) strncpy(browser->directory, path, BROWSER_MAX_PATH);
|
if( path != browser->directory ) strncpy(browser->directory, path, BROWSER_MAX_PATH);
|
||||||
browser_reload_directory_content(browser, path);
|
browser_reload_directory_content(browser, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void browser_init(struct browser *browser) {
|
static void browser_init(struct browser *browser) {
|
||||||
memset(browser, 0, sizeof(*browser));
|
memset(browser, 0, sizeof(*browser));
|
||||||
{
|
{
|
||||||
/* load files and sub-directory list */
|
/* load files and sub-directory list */
|
||||||
const char *home = getenv("HOME");
|
const char *home = getenv("HOME");
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (!home) home = getenv("USERPROFILE");
|
if (!home) home = getenv("USERPROFILE");
|
||||||
#else
|
#else
|
||||||
if (!home) home = getpwuid(getuid())->pw_dir;
|
if (!home) home = getpwuid(getuid())->pw_dir;
|
||||||
#endif
|
#endif
|
||||||
snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home);
|
snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home);
|
||||||
snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home);
|
snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home);
|
||||||
snprintf(browser->computer, BROWSER_MAX_PATH, "%s", ifdef(win32, va("%.*s", 3, getenv("windir")), "/"));
|
snprintf(browser->computer, BROWSER_MAX_PATH, "%s", ifdef(win32, va("%.*s", 3, getenv("windir")), "/"));
|
||||||
{
|
{
|
||||||
ifdef(win32, _getcwd, getcwd)(browser->project, sizeof(browser->project) - 1); // -1 == room for '/'
|
ifdef(win32, _getcwd, getcwd)(browser->project, sizeof(browser->project) - 1); // -1 == room for '/'
|
||||||
strcat(browser->project, "/");
|
strcat(browser->project, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
BROWSER_PRINTF("%s\n", browser->home);
|
BROWSER_PRINTF("%s\n", browser->home);
|
||||||
BROWSER_PRINTF("%s\n", browser->desktop);
|
BROWSER_PRINTF("%s\n", browser->desktop);
|
||||||
BROWSER_PRINTF("%s\n", browser->computer);
|
BROWSER_PRINTF("%s\n", browser->computer);
|
||||||
BROWSER_PRINTF("%s\n", browser->project);
|
BROWSER_PRINTF("%s\n", browser->project);
|
||||||
|
|
||||||
browser_chdir_and_reload_directory_content(browser, browser->project);
|
browser_chdir_and_reload_directory_content(browser, browser->project);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void browser_free(struct browser *browser) {
|
static void browser_free(struct browser *browser) {
|
||||||
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
|
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
|
||||||
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
|
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
|
||||||
array_free(browser->files);
|
array_free(browser->files);
|
||||||
array_free(browser->directories);
|
array_free(browser->directories);
|
||||||
array_free(browser->ffiles);
|
array_free(browser->ffiles);
|
||||||
array_free(browser->fdirectories);
|
array_free(browser->fdirectories);
|
||||||
|
|
||||||
memset(browser, 0, sizeof(*browser));
|
memset(browser, 0, sizeof(*browser));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) {
|
static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) {
|
||||||
int clicked = 0;
|
int clicked = 0;
|
||||||
|
|
||||||
static float ratio[] = {0.25f, NK_UNDEFINED};
|
static float ratio[] = {0.25f, NK_UNDEFINED};
|
||||||
|
|
||||||
float spacing_x = ctx->style.window.spacing.x;
|
float spacing_x = ctx->style.window.spacing.x;
|
||||||
|
|
||||||
/* output path directory selector in the menubar */
|
/* output path directory selector in the menubar */
|
||||||
ctx->style.window.spacing.x = 0;
|
ctx->style.window.spacing.x = 0;
|
||||||
if( windowed ) nk_menubar_begin(ctx);
|
if( windowed ) nk_menubar_begin(ctx);
|
||||||
{
|
{
|
||||||
char *d = browser->directory;
|
char *d = browser->directory;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
char *begin = d;
|
char *begin = d;
|
||||||
#else
|
#else
|
||||||
char *begin = d + 1;
|
char *begin = d + 1;
|
||||||
#endif
|
#endif
|
||||||
nk_layout_row_template_begin(ctx, 25);
|
nk_layout_row_template_begin(ctx, 25);
|
||||||
nk_layout_row_template_push_variable(ctx, 40);
|
nk_layout_row_template_push_variable(ctx, 40);
|
||||||
nk_layout_row_template_push_variable(ctx, 40);
|
nk_layout_row_template_push_variable(ctx, 40);
|
||||||
nk_layout_row_template_push_variable(ctx, 40);
|
nk_layout_row_template_push_variable(ctx, 40);
|
||||||
nk_layout_row_template_end(ctx);
|
nk_layout_row_template_end(ctx);
|
||||||
|
|
||||||
if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) {
|
if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) {
|
||||||
browser->listing ^= 1;
|
browser->listing ^= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (*d++) {
|
while (*d++) {
|
||||||
if (*d == '/') {
|
if (*d == '/') {
|
||||||
*d = '\0';
|
*d = '\0';
|
||||||
if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) {
|
if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) {
|
||||||
*d++ = '/'; *d = '\0';
|
*d++ = '/'; *d = '\0';
|
||||||
browser_chdir_and_reload_directory_content(browser, browser->directory);
|
browser_chdir_and_reload_directory_content(browser, browser->directory);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*d = '/';
|
*d = '/';
|
||||||
begin = d + 1;
|
begin = d + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( windowed ) nk_menubar_end(ctx);
|
if( windowed ) nk_menubar_end(ctx);
|
||||||
ctx->style.window.spacing.x = spacing_x;
|
ctx->style.window.spacing.x = spacing_x;
|
||||||
|
|
||||||
if(nk_window_has_focus(ctx)) {
|
if(nk_window_has_focus(ctx)) {
|
||||||
browser->zooming = clampf( browser->zooming + (input(KEY_LCTRL) || input(KEY_RCTRL)) * input_diff(MOUSE_W) * 0.1, 1, 3);
|
browser->zooming = clampf( browser->zooming + (input(KEY_LCTRL) || input(KEY_RCTRL)) * input_diff(MOUSE_W) * 0.1, 1, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons
|
bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons
|
||||||
size_t cols = total_space.w / (100 * browser->zooming);
|
size_t cols = total_space.w / (100 * browser->zooming);
|
||||||
int icon_height = (67 * browser->zooming) * (tiny ? 0.33 : 1.); // icon height (96) + button padding (??). originally: 135
|
int icon_height = (67 * browser->zooming) * (tiny ? 0.33 : 1.); // icon height (96) + button padding (??). originally: 135
|
||||||
/**/ if( tiny ) cols = (int)cols+1.5, cols /= 2, compact = total_space.w < 500; // cols <= 2;
|
/**/ if( tiny ) cols = (int)cols+1.5, cols /= 2, compact = total_space.w < 500; // cols <= 2;
|
||||||
else cols = (int)cols+1, compact = total_space.w < 500; // cols <= 5;
|
else cols = (int)cols+1, compact = total_space.w < 500; // cols <= 5;
|
||||||
if( cols < 1 ) cols=1;
|
if( cols < 1 ) cols=1;
|
||||||
|
|
||||||
/* window layout */
|
/* window layout */
|
||||||
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio);
|
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio);
|
||||||
if( !compact )
|
if( !compact )
|
||||||
if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) {
|
if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) {
|
||||||
nk_layout_row_dynamic(ctx, 40, 1);
|
nk_layout_row_dynamic(ctx, 40, 1);
|
||||||
|
|
||||||
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT))
|
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT))
|
||||||
browser_chdir_and_reload_directory_content(browser, browser->home);
|
browser_chdir_and_reload_directory_content(browser, browser->home);
|
||||||
|
|
||||||
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT))
|
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT))
|
||||||
browser_chdir_and_reload_directory_content(browser, browser->desktop);
|
browser_chdir_and_reload_directory_content(browser, browser->desktop);
|
||||||
|
|
||||||
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT))
|
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT))
|
||||||
browser_chdir_and_reload_directory_content(browser, browser->computer);
|
browser_chdir_and_reload_directory_content(browser, browser->computer);
|
||||||
|
|
||||||
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT))
|
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT))
|
||||||
browser_chdir_and_reload_directory_content(browser, browser->project);
|
browser_chdir_and_reload_directory_content(browser, browser->project);
|
||||||
|
|
||||||
nk_group_end(ctx);
|
nk_group_end(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* output directory content window */
|
/* output directory content window */
|
||||||
if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) {
|
if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) {
|
||||||
|
|
||||||
array(char*) *directories = &browser->directories;
|
array(char*) *directories = &browser->directories;
|
||||||
array(char*) *files = &browser->files;
|
array(char*) *files = &browser->files;
|
||||||
|
|
||||||
if( ui_filter && ui_filter[0] ) {
|
if( ui_filter && ui_filter[0] ) {
|
||||||
array_resize(browser->fdirectories, 0);
|
array_resize(browser->fdirectories, 0);
|
||||||
array_resize(browser->ffiles, 0);
|
array_resize(browser->ffiles, 0);
|
||||||
|
|
||||||
for each_array(browser->directories,char*,k)
|
for each_array(browser->directories,char*,k)
|
||||||
if( strstri(k, ui_filter) )
|
if( strstri(k, ui_filter) )
|
||||||
array_push(browser->fdirectories, k);
|
array_push(browser->fdirectories, k);
|
||||||
|
|
||||||
for each_array(browser->files,char*,k)
|
for each_array(browser->files,char*,k)
|
||||||
if( strstri(k, ui_filter) )
|
if( strstri(k, ui_filter) )
|
||||||
array_push(browser->ffiles, k);
|
array_push(browser->ffiles, k);
|
||||||
|
|
||||||
directories = &browser->fdirectories;
|
directories = &browser->fdirectories;
|
||||||
files = &browser->ffiles;
|
files = &browser->ffiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dir_count = array_count(*directories);
|
int dir_count = array_count(*directories);
|
||||||
int file_count = array_count(*files);
|
int file_count = array_count(*files);
|
||||||
|
|
||||||
int index = -1;
|
int index = -1;
|
||||||
size_t i = 0, j = 0, k = 0;
|
size_t i = 0, j = 0, k = 0;
|
||||||
size_t rows = 0;
|
size_t rows = 0;
|
||||||
size_t count = dir_count + file_count;
|
size_t count = dir_count + file_count;
|
||||||
|
|
||||||
rows = count / cols;
|
rows = count / cols;
|
||||||
for (i = 0; i <= rows; i += 1) {
|
for (i = 0; i <= rows; i += 1) {
|
||||||
if(!tiny)
|
if(!tiny)
|
||||||
{size_t n = j + cols;
|
{size_t n = j + cols;
|
||||||
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
|
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
|
||||||
for (; j < count && j < n; ++j) {
|
for (; j < count && j < n; ++j) {
|
||||||
size_t t = j-dir_count;
|
size_t t = j-dir_count;
|
||||||
|
|
||||||
/* draw one row of icons */
|
/* draw one row of icons */
|
||||||
if (j < dir_count) {
|
if (j < dir_count) {
|
||||||
/* draw and execute directory buttons */
|
/* draw and execute directory buttons */
|
||||||
if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER]))
|
if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER]))
|
||||||
index = (int)j;
|
index = (int)j;
|
||||||
} else {
|
} else {
|
||||||
/* draw and execute files buttons */
|
/* draw and execute files buttons */
|
||||||
struct nk_image *icon;
|
struct nk_image *icon;
|
||||||
size_t fileIndex = ((size_t)j - dir_count);
|
size_t fileIndex = ((size_t)j - dir_count);
|
||||||
icon = media_icon_for_file((*files)[fileIndex]);
|
icon = media_icon_for_file((*files)[fileIndex]);
|
||||||
if (nk_button_image(ctx, *icon)) {
|
if (nk_button_image(ctx, *icon)) {
|
||||||
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
|
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
|
||||||
clicked = 1;
|
clicked = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
if(!tiny)
|
if(!tiny)
|
||||||
{size_t n = k + cols;
|
{size_t n = k + cols;
|
||||||
nk_layout_row_dynamic(ctx, 20, (int)cols);
|
nk_layout_row_dynamic(ctx, 20, (int)cols);
|
||||||
for (; k < count && k < n; k++) {
|
for (; k < count && k < n; k++) {
|
||||||
size_t t = k-dir_count;
|
size_t t = k-dir_count;
|
||||||
|
|
||||||
/* draw one row of labels */
|
/* draw one row of labels */
|
||||||
if (k < dir_count) {
|
if (k < dir_count) {
|
||||||
nk_label(ctx, (*directories)[k], NK_TEXT_CENTERED);
|
nk_label(ctx, (*directories)[k], NK_TEXT_CENTERED);
|
||||||
} else {
|
} else {
|
||||||
nk_label(ctx, (*files)[t], NK_TEXT_CENTERED);
|
nk_label(ctx, (*files)[t], NK_TEXT_CENTERED);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
if(tiny)
|
if(tiny)
|
||||||
{size_t n = j + cols;
|
{size_t n = j + cols;
|
||||||
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
|
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
|
||||||
for (; j < count && j < n; ++j) {
|
for (; j < count && j < n; ++j) {
|
||||||
size_t t = j-dir_count;
|
size_t t = j-dir_count;
|
||||||
|
|
||||||
/* draw one row of icons */
|
/* draw one row of icons */
|
||||||
if (j < dir_count) {
|
if (j < dir_count) {
|
||||||
/* draw and execute directory buttons */
|
/* draw and execute directory buttons */
|
||||||
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], (*directories)[j], NK_TEXT_RIGHT))
|
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], (*directories)[j], NK_TEXT_RIGHT))
|
||||||
index = (int)j;
|
index = (int)j;
|
||||||
} else {
|
} else {
|
||||||
/* draw and execute files buttons */
|
/* draw and execute files buttons */
|
||||||
struct nk_image *icon;
|
struct nk_image *icon;
|
||||||
size_t fileIndex = ((size_t)j - dir_count);
|
size_t fileIndex = ((size_t)j - dir_count);
|
||||||
icon = media_icon_for_file((*files)[fileIndex]);
|
icon = media_icon_for_file((*files)[fileIndex]);
|
||||||
if (nk_button_image_label(ctx, *icon, (*files)[t],NK_TEXT_RIGHT)) {
|
if (nk_button_image_label(ctx, *icon, (*files)[t],NK_TEXT_RIGHT)) {
|
||||||
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
|
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
|
||||||
clicked = 1;
|
clicked = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop
|
bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop
|
||||||
bool has_popups = ui_popups(); // @fixme: move out of loop
|
bool has_popups = ui_popups(); // @fixme: move out of loop
|
||||||
if( !has_popups && has_focus ) {
|
if( !has_popups && has_focus ) {
|
||||||
struct nk_rect bounds = nk_widget_bounds(ctx);
|
struct nk_rect bounds = nk_widget_bounds(ctx);
|
||||||
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) {
|
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) {
|
||||||
|
|
||||||
char *name = j < dir_count ? (*directories)[j] : (*files)[j-dir_count];
|
char *name = j < dir_count ? (*directories)[j] : (*files)[j-dir_count];
|
||||||
|
|
||||||
char fullpath[PATH_MAX];
|
char fullpath[PATH_MAX];
|
||||||
snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name);
|
snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name);
|
||||||
|
|
||||||
struct stat t = {0};
|
struct stat t = {0};
|
||||||
if( stat( fullpath, &t ) != -1 ) {
|
if( stat( fullpath, &t ) != -1 ) {
|
||||||
char tooltip[256];
|
char tooltip[256];
|
||||||
snprintf(tooltip, 256,
|
snprintf(tooltip, 256,
|
||||||
"Path: %s\n"
|
"Path: %s\n"
|
||||||
"Type: %lld\n" // file type and mode
|
"Type: %lld\n" // file type and mode
|
||||||
"Size: %lld\n" // file size
|
"Size: %lld\n" // file size
|
||||||
"Owner: %lld\n" // user ID of file owner
|
"Owner: %lld\n" // user ID of file owner
|
||||||
"Modified: %s (%lld)", // last modification date
|
"Modified: %s (%lld)", // last modification date
|
||||||
name, (int64_t)t.st_mode, (int64_t)t.st_size, (int64_t)t.st_uid, ctime(&t.st_mtime), (int64_t)t.st_mtime
|
name, (int64_t)t.st_mode, (int64_t)t.st_size, (int64_t)t.st_uid, ctime(&t.st_mtime), (int64_t)t.st_mtime
|
||||||
);
|
);
|
||||||
nk_tooltip(ctx, tooltip);
|
nk_tooltip(ctx, tooltip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]);
|
BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]);
|
||||||
size_t n = strlen(browser->directory);
|
size_t n = strlen(browser->directory);
|
||||||
snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]);
|
snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]);
|
||||||
BROWSER_PRINTF("%s\n", browser->directory);
|
BROWSER_PRINTF("%s\n", browser->directory);
|
||||||
browser_chdir_and_reload_directory_content(browser, browser->directory);
|
browser_chdir_and_reload_directory_content(browser, browser->directory);
|
||||||
}
|
}
|
||||||
nk_group_end(ctx);
|
nk_group_end(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return clicked;
|
return clicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nk_image icon_load(const char *filename) {
|
static struct nk_image icon_load(const char *filename) {
|
||||||
texture_t t = texture(filename, 0);
|
texture_t t = texture(filename, 0);
|
||||||
return nk_image_id((int)t.id);
|
return nk_image_id((int)t.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nk_image icon_load_rect(unsigned id, unsigned w, unsigned h, unsigned wcell, unsigned hcell, unsigned col, unsigned row) {
|
static struct nk_image icon_load_rect(unsigned id, unsigned w, unsigned h, unsigned wcell, unsigned hcell, unsigned col, unsigned row) {
|
||||||
return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell });
|
return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* demo:
|
/* demo:
|
||||||
|
|
||||||
struct browser browser = {0};
|
struct browser browser = {0};
|
||||||
browser_init(&browser);
|
browser_init(&browser);
|
||||||
browser_config_dir(nk_image, BROWSER_HOME);
|
browser_config_dir(nk_image, BROWSER_HOME);
|
||||||
browser_config_dir(nk_image, BROWSER_PROJECT);
|
browser_config_dir(nk_image, BROWSER_PROJECT);
|
||||||
// [...]
|
// [...]
|
||||||
browser_config_type(nk_image, ".ext1.ext2.ext3.");
|
browser_config_type(nk_image, ".ext1.ext2.ext3.");
|
||||||
browser_config_type(nk_image, ".ext1.ext2.ext3.");
|
browser_config_type(nk_image, ".ext1.ext2.ext3.");
|
||||||
browser_config_type(nk_image, ".ext1.ext2.ext3.");
|
browser_config_type(nk_image, ".ext1.ext2.ext3.");
|
||||||
// [...]
|
// [...]
|
||||||
|
|
||||||
[...]
|
[...]
|
||||||
|
|
||||||
if( nk_begin(ctx, "window", ...) ) {
|
if( nk_begin(ctx, "window", ...) ) {
|
||||||
struct nk_rect total_space = nk_window_get_content_region(ctx);
|
struct nk_rect total_space = nk_window_get_content_region(ctx);
|
||||||
if( browser_run(ctx, &browser, 0, total_space) ) {
|
if( browser_run(ctx, &browser, 0, total_space) ) {
|
||||||
puts( browser->directory );
|
puts( browser->directory );
|
||||||
puts( browser->file );
|
puts( browser->file );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nk_end();
|
nk_end();
|
||||||
*/
|
*/
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,409 +1,409 @@
|
||||||
/* Progressive Mesh type Polygon Reduction Algorithm
|
/* Progressive Mesh type Polygon Reduction Algorithm
|
||||||
*
|
*
|
||||||
* 1998: Original version by Stan Melax (c) 1998
|
* 1998: Original version by Stan Melax (c) 1998
|
||||||
* Permission to use any of this code wherever you want is granted..
|
* Permission to use any of this code wherever you want is granted..
|
||||||
* Although, please do acknowledge authorship if appropriate.
|
* Although, please do acknowledge authorship if appropriate.
|
||||||
*
|
*
|
||||||
* 2014: Code style upgraded to be more consistent with graphics/gamedev conventions. Relicensed as MIT/PD.
|
* 2014: Code style upgraded to be more consistent with graphics/gamedev conventions. Relicensed as MIT/PD.
|
||||||
* Stan Melax: "Yes, this code can be licensed with the same license as the original. That should be fine."
|
* Stan Melax: "Yes, this code can be licensed with the same license as the original. That should be fine."
|
||||||
*
|
*
|
||||||
* 2020: C version by Cloud Wu (c) 2020. Licensed as MIT/PD.
|
* 2020: C version by Cloud Wu (c) 2020. Licensed as MIT/PD.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline void array_find_and_remove(array(int) arr, int v) {
|
static inline void array_find_and_remove(array(int) arr, int v) {
|
||||||
for( int i = 0, end = array_count(arr); i < end; i++ )
|
for( int i = 0, end = array_count(arr); i < end; i++ )
|
||||||
if( arr[i] == v ) { array_erase_fast(arr, i); --end; break; }
|
if( arr[i] == v ) { array_erase_fast(arr, i); --end; break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
struct triangle_n {
|
struct triangle_n {
|
||||||
int vertex[3]; // the 3 points (id) that make this tri
|
int vertex[3]; // the 3 points (id) that make this tri
|
||||||
vec3 normal; // unit vector othogonal to this face
|
vec3 normal; // unit vector othogonal to this face
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vertex {
|
struct vertex {
|
||||||
vec3 position; // location of point in euclidean space
|
vec3 position; // location of point in euclidean space
|
||||||
array(int) neighbor; // adjacent vertices
|
array(int) neighbor; // adjacent vertices
|
||||||
array(int) face; // adjacent triangles
|
array(int) face; // adjacent triangles
|
||||||
int id; // place of vertex in original Array
|
int id; // place of vertex in original Array
|
||||||
int collapse; // candidate vertex (id) for collapse
|
int collapse; // candidate vertex (id) for collapse
|
||||||
float objdist; // cached cost of collapsing edge
|
float objdist; // cached cost of collapsing edge
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mesh {
|
struct mesh {
|
||||||
struct vertex *v;
|
struct vertex *v;
|
||||||
struct triangle_n *t;
|
struct triangle_n *t;
|
||||||
int n_face;
|
int n_face;
|
||||||
int n_vertex;
|
int n_vertex;
|
||||||
};
|
};
|
||||||
|
|
||||||
// array
|
// array
|
||||||
|
|
||||||
static inline struct vertex *Vertex(struct mesh *M, int id) { return M->v + id; }
|
static inline struct vertex *Vertex(struct mesh *M, int id) { return M->v + id; }
|
||||||
static inline struct triangle_n *Triangle(struct mesh *M, int id) { return M->t + id; }
|
static inline struct triangle_n *Triangle(struct mesh *M, int id) { return M->t + id; }
|
||||||
static inline struct triangle_n *Face(struct mesh *M, struct vertex *v, int idx) { return M->t + v->face[idx]; }
|
static inline struct triangle_n *Face(struct mesh *M, struct vertex *v, int idx) { return M->t + v->face[idx]; }
|
||||||
|
|
||||||
static void AddVertex(struct mesh *M, const float *v) {
|
static void AddVertex(struct mesh *M, const float *v) {
|
||||||
int id = M->n_vertex++;
|
int id = M->n_vertex++;
|
||||||
struct vertex * tmp = Vertex(M, id);
|
struct vertex * tmp = Vertex(M, id);
|
||||||
tmp->position = ptr3(v);
|
tmp->position = ptr3(v);
|
||||||
tmp->neighbor = NULL;
|
tmp->neighbor = NULL;
|
||||||
tmp->face = NULL;
|
tmp->face = NULL;
|
||||||
tmp->id = id;
|
tmp->id = id;
|
||||||
tmp->collapse = -1;
|
tmp->collapse = -1;
|
||||||
tmp->objdist = 0;
|
tmp->objdist = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RemoveVertex(struct mesh *M, int id) {
|
static void RemoveVertex(struct mesh *M, int id) {
|
||||||
struct vertex * v = Vertex(M, id);
|
struct vertex * v = Vertex(M, id);
|
||||||
ASSERT(v->id == id);
|
ASSERT(v->id == id);
|
||||||
ASSERT(array_count(v->face) == 0);
|
ASSERT(array_count(v->face) == 0);
|
||||||
for (int i=0;i<array_count(v->face);i++) {
|
for (int i=0;i<array_count(v->face);i++) {
|
||||||
struct vertex * nv = Vertex(M, v->face[i]);
|
struct vertex * nv = Vertex(M, v->face[i]);
|
||||||
array_find_and_remove(nv->neighbor, id);
|
array_find_and_remove(nv->neighbor, id);
|
||||||
}
|
}
|
||||||
v->id = -1; // invalid vertex id
|
v->id = -1; // invalid vertex id
|
||||||
array_free(v->neighbor);
|
array_free(v->neighbor);
|
||||||
array_free(v->face);
|
array_free(v->face);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ComputeNormal(struct mesh *M, struct triangle_n *t) {
|
static void ComputeNormal(struct mesh *M, struct triangle_n *t) {
|
||||||
struct vertex * v0 = Vertex(M, t->vertex[0]);
|
struct vertex * v0 = Vertex(M, t->vertex[0]);
|
||||||
struct vertex * v1 = Vertex(M, t->vertex[1]);
|
struct vertex * v1 = Vertex(M, t->vertex[1]);
|
||||||
struct vertex * v2 = Vertex(M, t->vertex[2]);
|
struct vertex * v2 = Vertex(M, t->vertex[2]);
|
||||||
vec3 a = sub3(v1->position, v0->position);
|
vec3 a = sub3(v1->position, v0->position);
|
||||||
vec3 b = sub3(v2->position, v1->position);
|
vec3 b = sub3(v2->position, v1->position);
|
||||||
t->normal = norm3(cross3(a,b));
|
t->normal = norm3(cross3(a,b));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AddNeighbor(struct mesh *M, int vid, int id) {
|
static void AddNeighbor(struct mesh *M, int vid, int id) {
|
||||||
struct vertex *v = Vertex(M, vid);
|
struct vertex *v = Vertex(M, vid);
|
||||||
for (int i=0;i<array_count(v->neighbor);i++) {
|
for (int i=0;i<array_count(v->neighbor);i++) {
|
||||||
if (v->neighbor[i] == id)
|
if (v->neighbor[i] == id)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
array_push(v->neighbor, id);
|
array_push(v->neighbor, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AddTriangle(struct mesh *M, const int v[3]) {
|
static void AddTriangle(struct mesh *M, const int v[3]) {
|
||||||
if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2])
|
if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2])
|
||||||
return;
|
return;
|
||||||
ASSERT(v[0] < M->n_vertex);
|
ASSERT(v[0] < M->n_vertex);
|
||||||
ASSERT(v[1] < M->n_vertex);
|
ASSERT(v[1] < M->n_vertex);
|
||||||
ASSERT(v[2] < M->n_vertex);
|
ASSERT(v[2] < M->n_vertex);
|
||||||
int id = M->n_face++;
|
int id = M->n_face++;
|
||||||
struct triangle_n * tmp = Triangle(M, id);
|
struct triangle_n * tmp = Triangle(M, id);
|
||||||
tmp->vertex[0] = v[0];
|
tmp->vertex[0] = v[0];
|
||||||
tmp->vertex[1] = v[1];
|
tmp->vertex[1] = v[1];
|
||||||
tmp->vertex[2] = v[2];
|
tmp->vertex[2] = v[2];
|
||||||
ComputeNormal(M, tmp);
|
ComputeNormal(M, tmp);
|
||||||
|
|
||||||
for(int i=0;i<3;i++) {
|
for(int i=0;i<3;i++) {
|
||||||
struct vertex *obj = Vertex(M, v[i]);
|
struct vertex *obj = Vertex(M, v[i]);
|
||||||
array_push(obj->face, id);
|
array_push(obj->face, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
AddNeighbor(M, v[0], v[1]);
|
AddNeighbor(M, v[0], v[1]);
|
||||||
AddNeighbor(M, v[0], v[2]);
|
AddNeighbor(M, v[0], v[2]);
|
||||||
AddNeighbor(M, v[1], v[0]);
|
AddNeighbor(M, v[1], v[0]);
|
||||||
AddNeighbor(M, v[1], v[2]);
|
AddNeighbor(M, v[1], v[2]);
|
||||||
AddNeighbor(M, v[2], v[0]);
|
AddNeighbor(M, v[2], v[0]);
|
||||||
AddNeighbor(M, v[2], v[1]);
|
AddNeighbor(M, v[2], v[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int HasVertex(struct triangle_n * t, int vid) {
|
static int HasVertex(struct triangle_n * t, int vid) {
|
||||||
return (t->vertex[0] == vid || t->vertex[1] == vid || t->vertex[2] == vid);
|
return (t->vertex[0] == vid || t->vertex[1] == vid || t->vertex[2] == vid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RemoveIfNonNeighbor_(struct mesh *M, struct vertex *v, int id) {
|
static void RemoveIfNonNeighbor_(struct mesh *M, struct vertex *v, int id) {
|
||||||
for (int i=0;i<array_count(v->neighbor);i++) {
|
for (int i=0;i<array_count(v->neighbor);i++) {
|
||||||
if (v->neighbor[i] == id) {
|
if (v->neighbor[i] == id) {
|
||||||
for (int j=0;j<array_count(v->face);j++) {
|
for (int j=0;j<array_count(v->face);j++) {
|
||||||
if (HasVertex(Face(M, v, j), id))
|
if (HasVertex(Face(M, v, j), id))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// remove from neighbors
|
// remove from neighbors
|
||||||
array_erase_fast(v->neighbor, i);
|
array_erase_fast(v->neighbor, i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) {
|
static void RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) {
|
||||||
if (v0 == NULL || v1 == NULL)
|
if (v0 == NULL || v1 == NULL)
|
||||||
return;
|
return;
|
||||||
RemoveIfNonNeighbor_(M, v0, v1->id);
|
RemoveIfNonNeighbor_(M, v0, v1->id);
|
||||||
RemoveIfNonNeighbor_(M, v1, v0->id);
|
RemoveIfNonNeighbor_(M, v1, v0->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RemoveTriangle(struct mesh *M, int id) {
|
static void RemoveTriangle(struct mesh *M, int id) {
|
||||||
struct triangle_n * face = Triangle(M, id);
|
struct triangle_n * face = Triangle(M, id);
|
||||||
struct vertex * v[3];
|
struct vertex * v[3];
|
||||||
for (int i=0;i<3;i++) {
|
for (int i=0;i<3;i++) {
|
||||||
v[i] = Vertex(M, face->vertex[i]);
|
v[i] = Vertex(M, face->vertex[i]);
|
||||||
if (v[i]->id < 0)
|
if (v[i]->id < 0)
|
||||||
v[i] = NULL;
|
v[i] = NULL;
|
||||||
else {
|
else {
|
||||||
array_find_and_remove(v[i]->face, id);
|
array_find_and_remove(v[i]->face, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RemoveIfNonNeighbor(M, v[0], v[1]);
|
RemoveIfNonNeighbor(M, v[0], v[1]);
|
||||||
RemoveIfNonNeighbor(M, v[1], v[2]);
|
RemoveIfNonNeighbor(M, v[1], v[2]);
|
||||||
RemoveIfNonNeighbor(M, v[2], v[0]);
|
RemoveIfNonNeighbor(M, v[2], v[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) {
|
static void ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) {
|
||||||
struct triangle_n * face = Triangle(M, faceid);
|
struct triangle_n * face = Triangle(M, faceid);
|
||||||
ASSERT(oldid >=0 && newid >= 0);
|
ASSERT(oldid >=0 && newid >= 0);
|
||||||
ASSERT(HasVertex(face, oldid));
|
ASSERT(HasVertex(face, oldid));
|
||||||
ASSERT(!HasVertex(face, newid));
|
ASSERT(!HasVertex(face, newid));
|
||||||
if(oldid==face->vertex[0]){
|
if(oldid==face->vertex[0]){
|
||||||
face->vertex[0]=newid;
|
face->vertex[0]=newid;
|
||||||
} else if(oldid==face->vertex[1]){
|
} else if(oldid==face->vertex[1]){
|
||||||
face->vertex[1]=newid;
|
face->vertex[1]=newid;
|
||||||
} else {
|
} else {
|
||||||
face->vertex[2]=newid;
|
face->vertex[2]=newid;
|
||||||
}
|
}
|
||||||
struct vertex *vold = Vertex(M, oldid);
|
struct vertex *vold = Vertex(M, oldid);
|
||||||
struct vertex *vnew = Vertex(M, newid);
|
struct vertex *vnew = Vertex(M, newid);
|
||||||
|
|
||||||
array_find_and_remove(vold->face, faceid);
|
array_find_and_remove(vold->face, faceid);
|
||||||
array_push(vnew->face, faceid);
|
array_push(vnew->face, faceid);
|
||||||
|
|
||||||
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[0]));
|
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[0]));
|
||||||
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[1]));
|
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[1]));
|
||||||
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[2]));
|
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[2]));
|
||||||
|
|
||||||
AddNeighbor(M, face->vertex[0], face->vertex[1]);
|
AddNeighbor(M, face->vertex[0], face->vertex[1]);
|
||||||
AddNeighbor(M, face->vertex[0], face->vertex[2]);
|
AddNeighbor(M, face->vertex[0], face->vertex[2]);
|
||||||
AddNeighbor(M, face->vertex[1], face->vertex[0]);
|
AddNeighbor(M, face->vertex[1], face->vertex[0]);
|
||||||
AddNeighbor(M, face->vertex[1], face->vertex[2]);
|
AddNeighbor(M, face->vertex[1], face->vertex[2]);
|
||||||
AddNeighbor(M, face->vertex[2], face->vertex[0]);
|
AddNeighbor(M, face->vertex[2], face->vertex[0]);
|
||||||
AddNeighbor(M, face->vertex[2], face->vertex[1]);
|
AddNeighbor(M, face->vertex[2], face->vertex[1]);
|
||||||
|
|
||||||
ComputeNormal(M, face);
|
ComputeNormal(M, face);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MeshInit(struct mesh *M, int vert_n, int tri_n) {
|
static void MeshInit(struct mesh *M, int vert_n, int tri_n) {
|
||||||
M->n_face = 0;
|
M->n_face = 0;
|
||||||
M->n_vertex = 0;
|
M->n_vertex = 0;
|
||||||
M->v = (struct vertex *)MALLOC(vert_n * sizeof(struct vertex));
|
M->v = (struct vertex *)MALLOC(vert_n * sizeof(struct vertex));
|
||||||
M->t = (struct triangle_n *)MALLOC(tri_n * sizeof(struct triangle));
|
M->t = (struct triangle_n *)MALLOC(tri_n * sizeof(struct triangle));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MeshFree(struct mesh *M) {
|
static void MeshFree(struct mesh *M) {
|
||||||
FREE(M->v);
|
FREE(M->v);
|
||||||
FREE(M->t);
|
FREE(M->t);
|
||||||
}
|
}
|
||||||
|
|
||||||
static float ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) {
|
static float ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) {
|
||||||
// if we collapse edge uv by moving u to v then how
|
// if we collapse edge uv by moving u to v then how
|
||||||
// much different will the model change, i.e. how much "error".
|
// much different will the model change, i.e. how much "error".
|
||||||
// Texture, vertex normal, and border vertex code was removed
|
// Texture, vertex normal, and border vertex code was removed
|
||||||
// to keep this demo as simple as possible.
|
// to keep this demo as simple as possible.
|
||||||
// The method of determining cost was designed in order
|
// The method of determining cost was designed in order
|
||||||
// to exploit small and coplanar regions for
|
// to exploit small and coplanar regions for
|
||||||
// effective polygon reduction.
|
// effective polygon reduction.
|
||||||
// Is is possible to add some checks here to see if "folds"
|
// Is is possible to add some checks here to see if "folds"
|
||||||
// would be generated. i.e. normal of a remaining face gets
|
// would be generated. i.e. normal of a remaining face gets
|
||||||
// flipped. I never seemed to run into this problem and
|
// flipped. I never seemed to run into this problem and
|
||||||
// therefore never added code to detect this case.
|
// therefore never added code to detect this case.
|
||||||
struct vertex *v = Vertex(M, vid);
|
struct vertex *v = Vertex(M, vid);
|
||||||
vec3 tmp = sub3(v->position, u->position);
|
vec3 tmp = sub3(v->position, u->position);
|
||||||
float edgelength = len3(tmp);
|
float edgelength = len3(tmp);
|
||||||
float curvature=0;
|
float curvature=0;
|
||||||
|
|
||||||
// find the "sides" triangles that are on the edge uv
|
// find the "sides" triangles that are on the edge uv
|
||||||
array(int) sides = 0;
|
array(int) sides = 0;
|
||||||
for (int i = 0; i<array_count(u->face); i++) {
|
for (int i = 0; i<array_count(u->face); i++) {
|
||||||
if (HasVertex(Face(M, u, i), vid)) {
|
if (HasVertex(Face(M, u, i), vid)) {
|
||||||
array_push(sides, u->face[i]);
|
array_push(sides, u->face[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// use the triangle facing most away from the sides
|
// use the triangle facing most away from the sides
|
||||||
// to determine our curvature term
|
// to determine our curvature term
|
||||||
for (int i = 0; i<array_count(u->face); i++) {
|
for (int i = 0; i<array_count(u->face); i++) {
|
||||||
float mincurv=1; // curve for face i and closer side to it
|
float mincurv=1; // curve for face i and closer side to it
|
||||||
for (int j = 0; j<array_count(sides); j++) {
|
for (int j = 0; j<array_count(sides); j++) {
|
||||||
float dotprod = dot3(Triangle(M, u->face[i])->normal,
|
float dotprod = dot3(Triangle(M, u->face[i])->normal,
|
||||||
Triangle(M, sides[j])->normal); // use dot product of face normals.
|
Triangle(M, sides[j])->normal); // use dot product of face normals.
|
||||||
float t = (1-dotprod)/2.0f;
|
float t = (1-dotprod)/2.0f;
|
||||||
if (t < mincurv) {
|
if (t < mincurv) {
|
||||||
mincurv = t;
|
mincurv = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mincurv > curvature)
|
if (mincurv > curvature)
|
||||||
curvature = mincurv;
|
curvature = mincurv;
|
||||||
}
|
}
|
||||||
array_free(sides);
|
array_free(sides);
|
||||||
// the more coplanar the lower the curvature term
|
// the more coplanar the lower the curvature term
|
||||||
return edgelength * curvature;
|
return edgelength * curvature;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) {
|
static void ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) {
|
||||||
// compute the edge collapse cost for all edges that start
|
// compute the edge collapse cost for all edges that start
|
||||||
// from vertex v. Since we are only interested in reducing
|
// from vertex v. Since we are only interested in reducing
|
||||||
// the object by selecting the min cost edge at each step, we
|
// the object by selecting the min cost edge at each step, we
|
||||||
// only cache the cost of the least cost edge at this vertex
|
// only cache the cost of the least cost edge at this vertex
|
||||||
// (in member variable collapse) as well as the value of the
|
// (in member variable collapse) as well as the value of the
|
||||||
// cost (in member variable objdist).
|
// cost (in member variable objdist).
|
||||||
if (array_count(v->neighbor) == 0) {
|
if (array_count(v->neighbor) == 0) {
|
||||||
// v doesn't have neighbors so it costs nothing to collapse
|
// v doesn't have neighbors so it costs nothing to collapse
|
||||||
v->collapse=-1;
|
v->collapse=-1;
|
||||||
v->objdist=-0.01f;
|
v->objdist=-0.01f;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
v->objdist = 1000000;
|
v->objdist = 1000000;
|
||||||
v->collapse=-1;
|
v->collapse=-1;
|
||||||
// search all neighboring edges for "least cost" edge
|
// search all neighboring edges for "least cost" edge
|
||||||
for (int i = 0; i<array_count(v->neighbor); i++) {
|
for (int i = 0; i<array_count(v->neighbor); i++) {
|
||||||
float dist = ComputeEdgeCollapseCost(M, v, v->neighbor[i]);
|
float dist = ComputeEdgeCollapseCost(M, v, v->neighbor[i]);
|
||||||
if(dist<v->objdist) {
|
if(dist<v->objdist) {
|
||||||
v->collapse=v->neighbor[i]; // candidate for edge collapse
|
v->collapse=v->neighbor[i]; // candidate for edge collapse
|
||||||
v->objdist=dist; // cost of the collapse
|
v->objdist=dist; // cost of the collapse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ComputeAllEdgeCollapseCosts(struct mesh *M) {
|
static void ComputeAllEdgeCollapseCosts(struct mesh *M) {
|
||||||
// For all the edges, compute the difference it would make
|
// For all the edges, compute the difference it would make
|
||||||
// to the model if it was collapsed. The least of these
|
// to the model if it was collapsed. The least of these
|
||||||
// per vertex is cached in each vertex object.
|
// per vertex is cached in each vertex object.
|
||||||
for (int i = 0; i<M->n_vertex; i++) {
|
for (int i = 0; i<M->n_vertex; i++) {
|
||||||
ComputeEdgeCostAtVertex(M, Vertex(M, i));
|
ComputeEdgeCostAtVertex(M, Vertex(M, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Collapse(struct mesh *M, int uid, int vid) {
|
static void Collapse(struct mesh *M, int uid, int vid) {
|
||||||
// Collapse the edge uv by moving vertex u onto v
|
// Collapse the edge uv by moving vertex u onto v
|
||||||
// Actually remove tris on uv, then update tris that
|
// Actually remove tris on uv, then update tris that
|
||||||
// have u to have v, and then remove u.
|
// have u to have v, and then remove u.
|
||||||
struct vertex *u = Vertex(M, uid);
|
struct vertex *u = Vertex(M, uid);
|
||||||
if(vid < 0) {
|
if(vid < 0) {
|
||||||
// u is a vertex all by itself so just delete it
|
// u is a vertex all by itself so just delete it
|
||||||
RemoveVertex(M, uid);
|
RemoveVertex(M, uid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
array(int) tmp = 0;
|
array(int) tmp = 0;
|
||||||
|
|
||||||
// make tmp a Array of all the neighbors of u
|
// make tmp a Array of all the neighbors of u
|
||||||
for (int i = 0; i<array_count(u->neighbor); i++) {
|
for (int i = 0; i<array_count(u->neighbor); i++) {
|
||||||
array_push(tmp, u->neighbor[i]);
|
array_push(tmp, u->neighbor[i]);
|
||||||
}
|
}
|
||||||
// delete triangles on edge uv:
|
// delete triangles on edge uv:
|
||||||
for( int i = array_count(u->face); i--; ) {
|
for( int i = array_count(u->face); i--; ) {
|
||||||
if (HasVertex(Face(M, u, i), vid)) {
|
if (HasVertex(Face(M, u, i), vid)) {
|
||||||
RemoveTriangle(M, u->face[i]);
|
RemoveTriangle(M, u->face[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// update remaining triangles to have v instead of u
|
// update remaining triangles to have v instead of u
|
||||||
for( int i = array_count(u->face); i--; ) {
|
for( int i = array_count(u->face); i--; ) {
|
||||||
ReplaceVertex(M, u->face[i], uid, vid);
|
ReplaceVertex(M, u->face[i], uid, vid);
|
||||||
}
|
}
|
||||||
RemoveVertex(M, uid);
|
RemoveVertex(M, uid);
|
||||||
// recompute the edge collapse costs for neighboring vertices
|
// recompute the edge collapse costs for neighboring vertices
|
||||||
for (int i = 0; i<array_count(tmp); i++) {
|
for (int i = 0; i<array_count(tmp); i++) {
|
||||||
ComputeEdgeCostAtVertex(M, Vertex(M, tmp[i]));
|
ComputeEdgeCostAtVertex(M, Vertex(M, tmp[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
array_free(tmp);
|
array_free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct vertex *MinimumCostEdge(struct mesh *M) {
|
static struct vertex *MinimumCostEdge(struct mesh *M) {
|
||||||
// Find the edge that when collapsed will affect model the least.
|
// Find the edge that when collapsed will affect model the least.
|
||||||
// This function actually returns a Vertex, the second vertex
|
// This function actually returns a Vertex, the second vertex
|
||||||
// of the edge (collapse candidate) is stored in the vertex data.
|
// of the edge (collapse candidate) is stored in the vertex data.
|
||||||
// Serious optimization opportunity here: this function currently
|
// Serious optimization opportunity here: this function currently
|
||||||
// does a sequential search through an unsorted Array :-(
|
// does a sequential search through an unsorted Array :-(
|
||||||
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
|
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
|
||||||
struct vertex *mn = NULL;
|
struct vertex *mn = NULL;
|
||||||
for (int i = 0; i<M->n_vertex; i++) {
|
for (int i = 0; i<M->n_vertex; i++) {
|
||||||
struct vertex *v = Vertex(M, i);
|
struct vertex *v = Vertex(M, i);
|
||||||
if (v->id >=0) {
|
if (v->id >=0) {
|
||||||
if (mn == NULL || v->objdist < mn->objdist) {
|
if (mn == NULL || v->objdist < mn->objdist) {
|
||||||
mn = v;
|
mn = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mn;
|
return mn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The function ProgressiveMesh() takes a model in an "indexed face
|
* The function ProgressiveMesh() takes a model in an "indexed face
|
||||||
* set" sort of way. i.e. Array of vertices and Array of triangles.
|
* set" sort of way. i.e. Array of vertices and Array of triangles.
|
||||||
* The function then does the polygon reduction algorithm
|
* The function then does the polygon reduction algorithm
|
||||||
* internally and reduces the model all the way down to 0
|
* internally and reduces the model all the way down to 0
|
||||||
* vertices and then returns the order in which the
|
* vertices and then returns the order in which the
|
||||||
* vertices are collapsed and to which neighbor each vertex
|
* vertices are collapsed and to which neighbor each vertex
|
||||||
* is collapsed to. More specifically the returned "permutation"
|
* is collapsed to. More specifically the returned "permutation"
|
||||||
* indicates how to reorder your vertices so you can render
|
* indicates how to reorder your vertices so you can render
|
||||||
* an object by using the first n vertices (for the n
|
* an object by using the first n vertices (for the n
|
||||||
* vertex version). After permuting your vertices, the
|
* vertex version). After permuting your vertices, the
|
||||||
* map Array indicates to which vertex each vertex is collapsed to.
|
* map Array indicates to which vertex each vertex is collapsed to.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
API void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation) {
|
API void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation) {
|
||||||
struct mesh M;
|
struct mesh M;
|
||||||
MeshInit(&M, vert_n, tri_n);
|
MeshInit(&M, vert_n, tri_n);
|
||||||
|
|
||||||
// put input data into our data structures M
|
// put input data into our data structures M
|
||||||
const char * tmp = (const char *)v;
|
const char * tmp = (const char *)v;
|
||||||
for (int i=0;i<vert_n;i++, tmp += vert_stride ) {
|
for (int i=0;i<vert_n;i++, tmp += vert_stride ) {
|
||||||
AddVertex(&M, (const float *)tmp);
|
AddVertex(&M, (const float *)tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0;i<tri_n;i++) {
|
for (int i=0;i<tri_n;i++) {
|
||||||
AddTriangle(&M, &tri[i*3]);
|
AddTriangle(&M, &tri[i*3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputeAllEdgeCollapseCosts(&M); // cache all edge collapse costs
|
ComputeAllEdgeCollapseCosts(&M); // cache all edge collapse costs
|
||||||
|
|
||||||
for (int i = vert_n-1; i>=0; i--) {
|
for (int i = vert_n-1; i>=0; i--) {
|
||||||
// get the next vertex to collapse
|
// get the next vertex to collapse
|
||||||
struct vertex *mn = MinimumCostEdge(&M);
|
struct vertex *mn = MinimumCostEdge(&M);
|
||||||
// keep track of this vertex, i.e. the collapse ordering
|
// keep track of this vertex, i.e. the collapse ordering
|
||||||
permutation[mn->id] = i;
|
permutation[mn->id] = i;
|
||||||
// keep track of vertex to which we collapse to
|
// keep track of vertex to which we collapse to
|
||||||
map[i] = mn->collapse;
|
map[i] = mn->collapse;
|
||||||
// Collapse this edge
|
// Collapse this edge
|
||||||
Collapse(&M, mn->id, mn->collapse);
|
Collapse(&M, mn->id, mn->collapse);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reorder the map Array based on the collapse ordering
|
// reorder the map Array based on the collapse ordering
|
||||||
for (int i = 0; i<vert_n; i++) {
|
for (int i = 0; i<vert_n; i++) {
|
||||||
map[i] = (map[i]==-1)?0:permutation[map[i]];
|
map[i] = (map[i]==-1)?0:permutation[map[i]];
|
||||||
}
|
}
|
||||||
// The caller of this function should reorder their vertices
|
// The caller of this function should reorder their vertices
|
||||||
// according to the returned "permutation".
|
// according to the returned "permutation".
|
||||||
|
|
||||||
MeshFree(&M);
|
MeshFree(&M);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The MIT License (MIT)
|
* The MIT License (MIT)
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014 Stan Melax
|
* Copyright (c) 2014 Stan Melax
|
||||||
* Copyright (c) 2020 Cloud Wu
|
* Copyright (c) 2020 Cloud Wu
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* in the Software without restriction, including without limitation the rights
|
* in the Software without restriction, including without limitation the rights
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
* copies or substantial portions of the Software.
|
* copies or substantial portions of the Software.
|
||||||
*
|
*
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,373 +1,373 @@
|
||||||
/* rpmalloc.h - Memory allocator - Public Domain - 2016 Mattias Jansson
|
/* rpmalloc.h - Memory allocator - Public Domain - 2016 Mattias Jansson
|
||||||
*
|
*
|
||||||
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
|
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
|
||||||
* The latest source code is always available at
|
* The latest source code is always available at
|
||||||
*
|
*
|
||||||
* https://github.com/mjansson/rpmalloc
|
* https://github.com/mjansson/rpmalloc
|
||||||
*
|
*
|
||||||
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
|
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__clang__) || defined(__GNUC__)
|
#if defined(__clang__) || defined(__GNUC__)
|
||||||
# define RPMALLOC_EXPORT __attribute__((visibility("default")))
|
# define RPMALLOC_EXPORT __attribute__((visibility("default")))
|
||||||
# define RPMALLOC_ALLOCATOR
|
# define RPMALLOC_ALLOCATOR
|
||||||
# if (defined(__clang_major__) && (__clang_major__ < 4)) || (defined(__GNUC__) && defined(ENABLE_PRELOAD) && ENABLE_PRELOAD)
|
# if (defined(__clang_major__) && (__clang_major__ < 4)) || (defined(__GNUC__) && defined(ENABLE_PRELOAD) && ENABLE_PRELOAD)
|
||||||
# define RPMALLOC_ATTRIB_MALLOC
|
# define RPMALLOC_ATTRIB_MALLOC
|
||||||
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
|
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
|
||||||
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
|
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
|
||||||
# else
|
# else
|
||||||
# define RPMALLOC_ATTRIB_MALLOC __attribute__((__malloc__))
|
# define RPMALLOC_ATTRIB_MALLOC __attribute__((__malloc__))
|
||||||
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size) __attribute__((alloc_size(size)))
|
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size) __attribute__((alloc_size(size)))
|
||||||
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size) __attribute__((alloc_size(count, size)))
|
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size) __attribute__((alloc_size(count, size)))
|
||||||
# endif
|
# endif
|
||||||
# define RPMALLOC_CDECL
|
# define RPMALLOC_CDECL
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
# define RPMALLOC_EXPORT
|
# define RPMALLOC_EXPORT
|
||||||
# define RPMALLOC_ALLOCATOR __declspec(allocator) __declspec(restrict)
|
# define RPMALLOC_ALLOCATOR __declspec(allocator) __declspec(restrict)
|
||||||
# define RPMALLOC_ATTRIB_MALLOC
|
# define RPMALLOC_ATTRIB_MALLOC
|
||||||
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
|
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
|
||||||
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
|
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
|
||||||
# define RPMALLOC_CDECL __cdecl
|
# define RPMALLOC_CDECL __cdecl
|
||||||
#else
|
#else
|
||||||
# define RPMALLOC_EXPORT
|
# define RPMALLOC_EXPORT
|
||||||
# define RPMALLOC_ALLOCATOR
|
# define RPMALLOC_ALLOCATOR
|
||||||
# define RPMALLOC_ATTRIB_MALLOC
|
# define RPMALLOC_ATTRIB_MALLOC
|
||||||
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
|
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
|
||||||
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
|
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
|
||||||
# define RPMALLOC_CDECL
|
# define RPMALLOC_CDECL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//! Define RPMALLOC_CONFIGURABLE to enable configuring sizes. Will introduce
|
//! Define RPMALLOC_CONFIGURABLE to enable configuring sizes. Will introduce
|
||||||
// a very small overhead due to some size calculations not being compile time constants
|
// a very small overhead due to some size calculations not being compile time constants
|
||||||
#ifndef RPMALLOC_CONFIGURABLE
|
#ifndef RPMALLOC_CONFIGURABLE
|
||||||
#define RPMALLOC_CONFIGURABLE 0
|
#define RPMALLOC_CONFIGURABLE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//! Define RPMALLOC_FIRST_CLASS_HEAPS to enable heap based API (rpmalloc_heap_* functions).
|
//! Define RPMALLOC_FIRST_CLASS_HEAPS to enable heap based API (rpmalloc_heap_* functions).
|
||||||
// Will introduce a very small overhead to track fully allocated spans in heaps
|
// Will introduce a very small overhead to track fully allocated spans in heaps
|
||||||
#ifndef RPMALLOC_FIRST_CLASS_HEAPS
|
#ifndef RPMALLOC_FIRST_CLASS_HEAPS
|
||||||
#define RPMALLOC_FIRST_CLASS_HEAPS 0
|
#define RPMALLOC_FIRST_CLASS_HEAPS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//! Flag to rpaligned_realloc to not preserve content in reallocation
|
//! Flag to rpaligned_realloc to not preserve content in reallocation
|
||||||
#define RPMALLOC_NO_PRESERVE 1
|
#define RPMALLOC_NO_PRESERVE 1
|
||||||
//! Flag to rpaligned_realloc to fail and return null pointer if grow cannot be done in-place,
|
//! Flag to rpaligned_realloc to fail and return null pointer if grow cannot be done in-place,
|
||||||
// in which case the original pointer is still valid (just like a call to realloc which failes to allocate
|
// in which case the original pointer is still valid (just like a call to realloc which failes to allocate
|
||||||
// a new block).
|
// a new block).
|
||||||
#define RPMALLOC_GROW_OR_FAIL 2
|
#define RPMALLOC_GROW_OR_FAIL 2
|
||||||
|
|
||||||
typedef struct rpmalloc_global_statistics_t {
|
typedef struct rpmalloc_global_statistics_t {
|
||||||
//! Current amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
|
//! Current amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
|
||||||
size_t mapped;
|
size_t mapped;
|
||||||
//! Peak amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
|
//! Peak amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
|
||||||
size_t mapped_peak;
|
size_t mapped_peak;
|
||||||
//! Current amount of memory in global caches for small and medium sizes (<32KiB)
|
//! Current amount of memory in global caches for small and medium sizes (<32KiB)
|
||||||
size_t cached;
|
size_t cached;
|
||||||
//! Current amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
|
//! Current amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
|
||||||
size_t huge_alloc;
|
size_t huge_alloc;
|
||||||
//! Peak amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
|
//! Peak amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
|
||||||
size_t huge_alloc_peak;
|
size_t huge_alloc_peak;
|
||||||
//! Total amount of memory mapped since initialization (only if ENABLE_STATISTICS=1)
|
//! Total amount of memory mapped since initialization (only if ENABLE_STATISTICS=1)
|
||||||
size_t mapped_total;
|
size_t mapped_total;
|
||||||
//! Total amount of memory unmapped since initialization (only if ENABLE_STATISTICS=1)
|
//! Total amount of memory unmapped since initialization (only if ENABLE_STATISTICS=1)
|
||||||
size_t unmapped_total;
|
size_t unmapped_total;
|
||||||
} rpmalloc_global_statistics_t;
|
} rpmalloc_global_statistics_t;
|
||||||
|
|
||||||
typedef struct rpmalloc_thread_statistics_t {
|
typedef struct rpmalloc_thread_statistics_t {
|
||||||
//! Current number of bytes available in thread size class caches for small and medium sizes (<32KiB)
|
//! Current number of bytes available in thread size class caches for small and medium sizes (<32KiB)
|
||||||
size_t sizecache;
|
size_t sizecache;
|
||||||
//! Current number of bytes available in thread span caches for small and medium sizes (<32KiB)
|
//! Current number of bytes available in thread span caches for small and medium sizes (<32KiB)
|
||||||
size_t spancache;
|
size_t spancache;
|
||||||
//! Total number of bytes transitioned from thread cache to global cache (only if ENABLE_STATISTICS=1)
|
//! Total number of bytes transitioned from thread cache to global cache (only if ENABLE_STATISTICS=1)
|
||||||
size_t thread_to_global;
|
size_t thread_to_global;
|
||||||
//! Total number of bytes transitioned from global cache to thread cache (only if ENABLE_STATISTICS=1)
|
//! Total number of bytes transitioned from global cache to thread cache (only if ENABLE_STATISTICS=1)
|
||||||
size_t global_to_thread;
|
size_t global_to_thread;
|
||||||
//! Per span count statistics (only if ENABLE_STATISTICS=1)
|
//! Per span count statistics (only if ENABLE_STATISTICS=1)
|
||||||
struct {
|
struct {
|
||||||
//! Currently used number of spans
|
//! Currently used number of spans
|
||||||
size_t current;
|
size_t current;
|
||||||
//! High water mark of spans used
|
//! High water mark of spans used
|
||||||
size_t peak;
|
size_t peak;
|
||||||
//! Number of spans transitioned to global cache
|
//! Number of spans transitioned to global cache
|
||||||
size_t to_global;
|
size_t to_global;
|
||||||
//! Number of spans transitioned from global cache
|
//! Number of spans transitioned from global cache
|
||||||
size_t from_global;
|
size_t from_global;
|
||||||
//! Number of spans transitioned to thread cache
|
//! Number of spans transitioned to thread cache
|
||||||
size_t to_cache;
|
size_t to_cache;
|
||||||
//! Number of spans transitioned from thread cache
|
//! Number of spans transitioned from thread cache
|
||||||
size_t from_cache;
|
size_t from_cache;
|
||||||
//! Number of spans transitioned to reserved state
|
//! Number of spans transitioned to reserved state
|
||||||
size_t to_reserved;
|
size_t to_reserved;
|
||||||
//! Number of spans transitioned from reserved state
|
//! Number of spans transitioned from reserved state
|
||||||
size_t from_reserved;
|
size_t from_reserved;
|
||||||
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
|
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
|
||||||
size_t map_calls;
|
size_t map_calls;
|
||||||
} span_use[64];
|
} span_use[64];
|
||||||
//! Per size class statistics (only if ENABLE_STATISTICS=1)
|
//! Per size class statistics (only if ENABLE_STATISTICS=1)
|
||||||
struct {
|
struct {
|
||||||
//! Current number of allocations
|
//! Current number of allocations
|
||||||
size_t alloc_current;
|
size_t alloc_current;
|
||||||
//! Peak number of allocations
|
//! Peak number of allocations
|
||||||
size_t alloc_peak;
|
size_t alloc_peak;
|
||||||
//! Total number of allocations
|
//! Total number of allocations
|
||||||
size_t alloc_total;
|
size_t alloc_total;
|
||||||
//! Total number of frees
|
//! Total number of frees
|
||||||
size_t free_total;
|
size_t free_total;
|
||||||
//! Number of spans transitioned to cache
|
//! Number of spans transitioned to cache
|
||||||
size_t spans_to_cache;
|
size_t spans_to_cache;
|
||||||
//! Number of spans transitioned from cache
|
//! Number of spans transitioned from cache
|
||||||
size_t spans_from_cache;
|
size_t spans_from_cache;
|
||||||
//! Number of spans transitioned from reserved state
|
//! Number of spans transitioned from reserved state
|
||||||
size_t spans_from_reserved;
|
size_t spans_from_reserved;
|
||||||
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
|
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
|
||||||
size_t map_calls;
|
size_t map_calls;
|
||||||
} size_use[128];
|
} size_use[128];
|
||||||
} rpmalloc_thread_statistics_t;
|
} rpmalloc_thread_statistics_t;
|
||||||
|
|
||||||
typedef struct rpmalloc_config_t {
|
typedef struct rpmalloc_config_t {
|
||||||
//! Map memory pages for the given number of bytes. The returned address MUST be
|
//! Map memory pages for the given number of bytes. The returned address MUST be
|
||||||
// aligned to the rpmalloc span size, which will always be a power of two.
|
// aligned to the rpmalloc span size, which will always be a power of two.
|
||||||
// Optionally the function can store an alignment offset in the offset variable
|
// Optionally the function can store an alignment offset in the offset variable
|
||||||
// in case it performs alignment and the returned pointer is offset from the
|
// in case it performs alignment and the returned pointer is offset from the
|
||||||
// actual start of the memory region due to this alignment. The alignment offset
|
// actual start of the memory region due to this alignment. The alignment offset
|
||||||
// will be passed to the memory unmap function. The alignment offset MUST NOT be
|
// will be passed to the memory unmap function. The alignment offset MUST NOT be
|
||||||
// larger than 65535 (storable in an uint16_t), if it is you must use natural
|
// larger than 65535 (storable in an uint16_t), if it is you must use natural
|
||||||
// alignment to shift it into 16 bits. If you set a memory_map function, you
|
// alignment to shift it into 16 bits. If you set a memory_map function, you
|
||||||
// must also set a memory_unmap function or else the default implementation will
|
// must also set a memory_unmap function or else the default implementation will
|
||||||
// be used for both. This function must be thread safe, it can be called by
|
// be used for both. This function must be thread safe, it can be called by
|
||||||
// multiple threads simultaneously.
|
// multiple threads simultaneously.
|
||||||
void* (*memory_map)(size_t size, size_t* offset);
|
void* (*memory_map)(size_t size, size_t* offset);
|
||||||
//! Unmap the memory pages starting at address and spanning the given number of bytes.
|
//! Unmap the memory pages starting at address and spanning the given number of bytes.
|
||||||
// If release is set to non-zero, the unmap is for an entire span range as returned by
|
// If release is set to non-zero, the unmap is for an entire span range as returned by
|
||||||
// a previous call to memory_map and that the entire range should be released. The
|
// a previous call to memory_map and that the entire range should be released. The
|
||||||
// release argument holds the size of the entire span range. If release is set to 0,
|
// release argument holds the size of the entire span range. If release is set to 0,
|
||||||
// the unmap is a partial decommit of a subset of the mapped memory range.
|
// the unmap is a partial decommit of a subset of the mapped memory range.
|
||||||
// If you set a memory_unmap function, you must also set a memory_map function or
|
// If you set a memory_unmap function, you must also set a memory_map function or
|
||||||
// else the default implementation will be used for both. This function must be thread
|
// else the default implementation will be used for both. This function must be thread
|
||||||
// safe, it can be called by multiple threads simultaneously.
|
// safe, it can be called by multiple threads simultaneously.
|
||||||
void (*memory_unmap)(void* address, size_t size, size_t offset, size_t release);
|
void (*memory_unmap)(void* address, size_t size, size_t offset, size_t release);
|
||||||
//! Called when an assert fails, if asserts are enabled. Will use the standard assert()
|
//! Called when an assert fails, if asserts are enabled. Will use the standard assert()
|
||||||
// if this is not set.
|
// if this is not set.
|
||||||
void (*error_callback)(const char* message);
|
void (*error_callback)(const char* message);
|
||||||
//! Called when a call to map memory pages fails (out of memory). If this callback is
|
//! Called when a call to map memory pages fails (out of memory). If this callback is
|
||||||
// not set or returns zero the library will return a null pointer in the allocation
|
// not set or returns zero the library will return a null pointer in the allocation
|
||||||
// call. If this callback returns non-zero the map call will be retried. The argument
|
// call. If this callback returns non-zero the map call will be retried. The argument
|
||||||
// passed is the number of bytes that was requested in the map call. Only used if
|
// passed is the number of bytes that was requested in the map call. Only used if
|
||||||
// the default system memory map function is used (memory_map callback is not set).
|
// the default system memory map function is used (memory_map callback is not set).
|
||||||
int (*map_fail_callback)(size_t size);
|
int (*map_fail_callback)(size_t size);
|
||||||
//! Size of memory pages. The page size MUST be a power of two. All memory mapping
|
//! Size of memory pages. The page size MUST be a power of two. All memory mapping
|
||||||
// requests to memory_map will be made with size set to a multiple of the page size.
|
// requests to memory_map will be made with size set to a multiple of the page size.
|
||||||
// Used if RPMALLOC_CONFIGURABLE is defined to 1, otherwise system page size is used.
|
// Used if RPMALLOC_CONFIGURABLE is defined to 1, otherwise system page size is used.
|
||||||
size_t page_size;
|
size_t page_size;
|
||||||
//! Size of a span of memory blocks. MUST be a power of two, and in [4096,262144]
|
//! Size of a span of memory blocks. MUST be a power of two, and in [4096,262144]
|
||||||
// range (unless 0 - set to 0 to use the default span size). Used if RPMALLOC_CONFIGURABLE
|
// range (unless 0 - set to 0 to use the default span size). Used if RPMALLOC_CONFIGURABLE
|
||||||
// is defined to 1.
|
// is defined to 1.
|
||||||
size_t span_size;
|
size_t span_size;
|
||||||
//! Number of spans to map at each request to map new virtual memory blocks. This can
|
//! Number of spans to map at each request to map new virtual memory blocks. This can
|
||||||
// be used to minimize the system call overhead at the cost of virtual memory address
|
// be used to minimize the system call overhead at the cost of virtual memory address
|
||||||
// space. The extra mapped pages will not be written until actually used, so physical
|
// space. The extra mapped pages will not be written until actually used, so physical
|
||||||
// committed memory should not be affected in the default implementation. Will be
|
// committed memory should not be affected in the default implementation. Will be
|
||||||
// aligned to a multiple of spans that match memory page size in case of huge pages.
|
// aligned to a multiple of spans that match memory page size in case of huge pages.
|
||||||
size_t span_map_count;
|
size_t span_map_count;
|
||||||
//! Enable use of large/huge pages. If this flag is set to non-zero and page size is
|
//! Enable use of large/huge pages. If this flag is set to non-zero and page size is
|
||||||
// zero, the allocator will try to enable huge pages and auto detect the configuration.
|
// zero, the allocator will try to enable huge pages and auto detect the configuration.
|
||||||
// If this is set to non-zero and page_size is also non-zero, the allocator will
|
// If this is set to non-zero and page_size is also non-zero, the allocator will
|
||||||
// assume huge pages have been configured and enabled prior to initializing the
|
// assume huge pages have been configured and enabled prior to initializing the
|
||||||
// allocator.
|
// allocator.
|
||||||
// For Windows, see https://docs.microsoft.com/en-us/windows/desktop/memory/large-page-support
|
// For Windows, see https://docs.microsoft.com/en-us/windows/desktop/memory/large-page-support
|
||||||
// For Linux, see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
|
// For Linux, see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
|
||||||
int enable_huge_pages;
|
int enable_huge_pages;
|
||||||
//! Respectively allocated pages and huge allocated pages names for systems
|
//! Respectively allocated pages and huge allocated pages names for systems
|
||||||
// supporting it to be able to distinguish among anonymous regions.
|
// supporting it to be able to distinguish among anonymous regions.
|
||||||
const char *page_name;
|
const char *page_name;
|
||||||
const char *huge_page_name;
|
const char *huge_page_name;
|
||||||
} rpmalloc_config_t;
|
} rpmalloc_config_t;
|
||||||
|
|
||||||
//! Initialize allocator with default configuration
|
//! Initialize allocator with default configuration
|
||||||
RPMALLOC_EXPORT int
|
RPMALLOC_EXPORT int
|
||||||
rpmalloc_initialize(void);
|
rpmalloc_initialize(void);
|
||||||
|
|
||||||
//! Initialize allocator with given configuration
|
//! Initialize allocator with given configuration
|
||||||
RPMALLOC_EXPORT int
|
RPMALLOC_EXPORT int
|
||||||
rpmalloc_initialize_config(const rpmalloc_config_t* config);
|
rpmalloc_initialize_config(const rpmalloc_config_t* config);
|
||||||
|
|
||||||
//! Get allocator configuration
|
//! Get allocator configuration
|
||||||
RPMALLOC_EXPORT const rpmalloc_config_t*
|
RPMALLOC_EXPORT const rpmalloc_config_t*
|
||||||
rpmalloc_config(void);
|
rpmalloc_config(void);
|
||||||
|
|
||||||
//! Finalize allocator
|
//! Finalize allocator
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_finalize(void);
|
rpmalloc_finalize(void);
|
||||||
|
|
||||||
//! Initialize allocator for calling thread
|
//! Initialize allocator for calling thread
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_thread_initialize(void);
|
rpmalloc_thread_initialize(void);
|
||||||
|
|
||||||
//! Finalize allocator for calling thread
|
//! Finalize allocator for calling thread
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_thread_finalize(int release_caches);
|
rpmalloc_thread_finalize(int release_caches);
|
||||||
|
|
||||||
//! Perform deferred deallocations pending for the calling thread heap
|
//! Perform deferred deallocations pending for the calling thread heap
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_thread_collect(void);
|
rpmalloc_thread_collect(void);
|
||||||
|
|
||||||
//! Query if allocator is initialized for calling thread
|
//! Query if allocator is initialized for calling thread
|
||||||
RPMALLOC_EXPORT int
|
RPMALLOC_EXPORT int
|
||||||
rpmalloc_is_thread_initialized(void);
|
rpmalloc_is_thread_initialized(void);
|
||||||
|
|
||||||
//! Get per-thread statistics
|
//! Get per-thread statistics
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_thread_statistics(rpmalloc_thread_statistics_t* stats);
|
rpmalloc_thread_statistics(rpmalloc_thread_statistics_t* stats);
|
||||||
|
|
||||||
//! Get global statistics
|
//! Get global statistics
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_global_statistics(rpmalloc_global_statistics_t* stats);
|
rpmalloc_global_statistics(rpmalloc_global_statistics_t* stats);
|
||||||
|
|
||||||
//! Dump all statistics in human readable format to file (should be a FILE*)
|
//! Dump all statistics in human readable format to file (should be a FILE*)
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_dump_statistics(void* file);
|
rpmalloc_dump_statistics(void* file);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size
|
//! Allocate a memory block of at least the given size
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpmalloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1);
|
rpmalloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1);
|
||||||
|
|
||||||
//! Free the given memory block
|
//! Free the given memory block
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpfree(void* ptr);
|
rpfree(void* ptr);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size and zero initialize it
|
//! Allocate a memory block of at least the given size and zero initialize it
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpcalloc(size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(1, 2);
|
rpcalloc(size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(1, 2);
|
||||||
|
|
||||||
//! Reallocate the given block to at least the given size
|
//! Reallocate the given block to at least the given size
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rprealloc(void* ptr, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
|
rprealloc(void* ptr, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
|
||||||
|
|
||||||
//! Reallocate the given block to at least the given size and alignment,
|
//! Reallocate the given block to at least the given size and alignment,
|
||||||
// with optional control flags (see RPMALLOC_NO_PRESERVE).
|
// with optional control flags (see RPMALLOC_NO_PRESERVE).
|
||||||
// Alignment must be a power of two and a multiple of sizeof(void*),
|
// Alignment must be a power of two and a multiple of sizeof(void*),
|
||||||
// and should ideally be less than memory page size. A caveat of rpmalloc
|
// and should ideally be less than memory page size. A caveat of rpmalloc
|
||||||
// internals is that this must also be strictly less than the span size (default 64KiB)
|
// internals is that this must also be strictly less than the span size (default 64KiB)
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpaligned_realloc(void* ptr, size_t alignment, size_t size, size_t oldsize, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
|
rpaligned_realloc(void* ptr, size_t alignment, size_t size, size_t oldsize, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size and alignment.
|
//! Allocate a memory block of at least the given size and alignment.
|
||||||
// Alignment must be a power of two and a multiple of sizeof(void*),
|
// Alignment must be a power of two and a multiple of sizeof(void*),
|
||||||
// and should ideally be less than memory page size. A caveat of rpmalloc
|
// and should ideally be less than memory page size. A caveat of rpmalloc
|
||||||
// internals is that this must also be strictly less than the span size (default 64KiB)
|
// internals is that this must also be strictly less than the span size (default 64KiB)
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpaligned_alloc(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
|
rpaligned_alloc(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size and alignment, and zero initialize it.
|
//! Allocate a memory block of at least the given size and alignment, and zero initialize it.
|
||||||
// Alignment must be a power of two and a multiple of sizeof(void*),
|
// Alignment must be a power of two and a multiple of sizeof(void*),
|
||||||
// and should ideally be less than memory page size. A caveat of rpmalloc
|
// and should ideally be less than memory page size. A caveat of rpmalloc
|
||||||
// internals is that this must also be strictly less than the span size (default 64KiB)
|
// internals is that this must also be strictly less than the span size (default 64KiB)
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpaligned_calloc(size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
|
rpaligned_calloc(size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size and alignment.
|
//! Allocate a memory block of at least the given size and alignment.
|
||||||
// Alignment must be a power of two and a multiple of sizeof(void*),
|
// Alignment must be a power of two and a multiple of sizeof(void*),
|
||||||
// and should ideally be less than memory page size. A caveat of rpmalloc
|
// and should ideally be less than memory page size. A caveat of rpmalloc
|
||||||
// internals is that this must also be strictly less than the span size (default 64KiB)
|
// internals is that this must also be strictly less than the span size (default 64KiB)
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpmemalign(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
|
rpmemalign(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size and alignment.
|
//! Allocate a memory block of at least the given size and alignment.
|
||||||
// Alignment must be a power of two and a multiple of sizeof(void*),
|
// Alignment must be a power of two and a multiple of sizeof(void*),
|
||||||
// and should ideally be less than memory page size. A caveat of rpmalloc
|
// and should ideally be less than memory page size. A caveat of rpmalloc
|
||||||
// internals is that this must also be strictly less than the span size (default 64KiB)
|
// internals is that this must also be strictly less than the span size (default 64KiB)
|
||||||
RPMALLOC_EXPORT int
|
RPMALLOC_EXPORT int
|
||||||
rpposix_memalign(void** memptr, size_t alignment, size_t size);
|
rpposix_memalign(void** memptr, size_t alignment, size_t size);
|
||||||
|
|
||||||
//! Query the usable size of the given memory block (from given pointer to the end of block)
|
//! Query the usable size of the given memory block (from given pointer to the end of block)
|
||||||
RPMALLOC_EXPORT size_t
|
RPMALLOC_EXPORT size_t
|
||||||
rpmalloc_usable_size(void* ptr);
|
rpmalloc_usable_size(void* ptr);
|
||||||
|
|
||||||
//! Dummy empty function for forcing linker symbol inclusion
|
//! Dummy empty function for forcing linker symbol inclusion
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_linker_reference(void);
|
rpmalloc_linker_reference(void);
|
||||||
|
|
||||||
#if RPMALLOC_FIRST_CLASS_HEAPS
|
#if RPMALLOC_FIRST_CLASS_HEAPS
|
||||||
|
|
||||||
//! Heap type
|
//! Heap type
|
||||||
typedef struct heap_t rpmalloc_heap_t;
|
typedef struct heap_t rpmalloc_heap_t;
|
||||||
|
|
||||||
//! Acquire a new heap. Will reuse existing released heaps or allocate memory for a new heap
|
//! Acquire a new heap. Will reuse existing released heaps or allocate memory for a new heap
|
||||||
// if none available. Heap API is implemented with the strict assumption that only one single
|
// if none available. Heap API is implemented with the strict assumption that only one single
|
||||||
// thread will call heap functions for a given heap at any given time, no functions are thread safe.
|
// thread will call heap functions for a given heap at any given time, no functions are thread safe.
|
||||||
RPMALLOC_EXPORT rpmalloc_heap_t*
|
RPMALLOC_EXPORT rpmalloc_heap_t*
|
||||||
rpmalloc_heap_acquire(void);
|
rpmalloc_heap_acquire(void);
|
||||||
|
|
||||||
//! Release a heap (does NOT free the memory allocated by the heap, use rpmalloc_heap_free_all before destroying the heap).
|
//! Release a heap (does NOT free the memory allocated by the heap, use rpmalloc_heap_free_all before destroying the heap).
|
||||||
// Releasing a heap will enable it to be reused by other threads. Safe to pass a null pointer.
|
// Releasing a heap will enable it to be reused by other threads. Safe to pass a null pointer.
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_heap_release(rpmalloc_heap_t* heap);
|
rpmalloc_heap_release(rpmalloc_heap_t* heap);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size using the given heap.
|
//! Allocate a memory block of at least the given size using the given heap.
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpmalloc_heap_alloc(rpmalloc_heap_t* heap, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
|
rpmalloc_heap_alloc(rpmalloc_heap_t* heap, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size using the given heap. The returned
|
//! Allocate a memory block of at least the given size using the given heap. The returned
|
||||||
// block will have the requested alignment. Alignment must be a power of two and a multiple of sizeof(void*),
|
// block will have the requested alignment. Alignment must be a power of two and a multiple of sizeof(void*),
|
||||||
// and should ideally be less than memory page size. A caveat of rpmalloc
|
// and should ideally be less than memory page size. A caveat of rpmalloc
|
||||||
// internals is that this must also be strictly less than the span size (default 64KiB).
|
// internals is that this must also be strictly less than the span size (default 64KiB).
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpmalloc_heap_aligned_alloc(rpmalloc_heap_t* heap, size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
|
rpmalloc_heap_aligned_alloc(rpmalloc_heap_t* heap, size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size using the given heap and zero initialize it.
|
//! Allocate a memory block of at least the given size using the given heap and zero initialize it.
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpmalloc_heap_calloc(rpmalloc_heap_t* heap, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
|
rpmalloc_heap_calloc(rpmalloc_heap_t* heap, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
|
||||||
|
|
||||||
//! Allocate a memory block of at least the given size using the given heap and zero initialize it. The returned
|
//! Allocate a memory block of at least the given size using the given heap and zero initialize it. The returned
|
||||||
// block will have the requested alignment. Alignment must either be zero, or a power of two and a multiple of sizeof(void*),
|
// block will have the requested alignment. Alignment must either be zero, or a power of two and a multiple of sizeof(void*),
|
||||||
// and should ideally be less than memory page size. A caveat of rpmalloc
|
// and should ideally be less than memory page size. A caveat of rpmalloc
|
||||||
// internals is that this must also be strictly less than the span size (default 64KiB).
|
// internals is that this must also be strictly less than the span size (default 64KiB).
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpmalloc_heap_aligned_calloc(rpmalloc_heap_t* heap, size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
|
rpmalloc_heap_aligned_calloc(rpmalloc_heap_t* heap, size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
|
||||||
|
|
||||||
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
|
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
|
||||||
// by the same heap given to this function.
|
// by the same heap given to this function.
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpmalloc_heap_realloc(rpmalloc_heap_t* heap, void* ptr, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
|
rpmalloc_heap_realloc(rpmalloc_heap_t* heap, void* ptr, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
|
||||||
|
|
||||||
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
|
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
|
||||||
// by the same heap given to this function. The returned block will have the requested alignment.
|
// by the same heap given to this function. The returned block will have the requested alignment.
|
||||||
// Alignment must be either zero, or a power of two and a multiple of sizeof(void*), and should ideally be
|
// Alignment must be either zero, or a power of two and a multiple of sizeof(void*), and should ideally be
|
||||||
// less than memory page size. A caveat of rpmalloc internals is that this must also be strictly less than
|
// less than memory page size. A caveat of rpmalloc internals is that this must also be strictly less than
|
||||||
// the span size (default 64KiB).
|
// the span size (default 64KiB).
|
||||||
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
|
||||||
rpmalloc_heap_aligned_realloc(rpmalloc_heap_t* heap, void* ptr, size_t alignment, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(4);
|
rpmalloc_heap_aligned_realloc(rpmalloc_heap_t* heap, void* ptr, size_t alignment, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(4);
|
||||||
|
|
||||||
//! Free the given memory block from the given heap. The memory block MUST be allocated
|
//! Free the given memory block from the given heap. The memory block MUST be allocated
|
||||||
// by the same heap given to this function.
|
// by the same heap given to this function.
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_heap_free(rpmalloc_heap_t* heap, void* ptr);
|
rpmalloc_heap_free(rpmalloc_heap_t* heap, void* ptr);
|
||||||
|
|
||||||
//! Free all memory allocated by the heap
|
//! Free all memory allocated by the heap
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_heap_free_all(rpmalloc_heap_t* heap);
|
rpmalloc_heap_free_all(rpmalloc_heap_t* heap);
|
||||||
|
|
||||||
//! Set the given heap as the current heap for the calling thread. A heap MUST only be current heap
|
//! Set the given heap as the current heap for the calling thread. A heap MUST only be current heap
|
||||||
// for a single thread, a heap can never be shared between multiple threads. The previous
|
// for a single thread, a heap can never be shared between multiple threads. The previous
|
||||||
// current heap for the calling thread is released to be reused by other threads.
|
// current heap for the calling thread is released to be reused by other threads.
|
||||||
RPMALLOC_EXPORT void
|
RPMALLOC_EXPORT void
|
||||||
rpmalloc_heap_thread_set_current(rpmalloc_heap_t* heap);
|
rpmalloc_heap_thread_set_current(rpmalloc_heap_t* heap);
|
||||||
|
|
||||||
//! Returns which heap the given pointer is allocated on
|
//! Returns which heap the given pointer is allocated on
|
||||||
RPMALLOC_EXPORT rpmalloc_heap_t*
|
RPMALLOC_EXPORT rpmalloc_heap_t*
|
||||||
rpmalloc_get_heap_for_ptr(void* ptr);
|
rpmalloc_get_heap_for_ptr(void* ptr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,481 +1,481 @@
|
||||||
/** 1D, 2D, 3D and 4D float Perlin Simplex noise */
|
/** 1D, 2D, 3D and 4D float Perlin Simplex noise */
|
||||||
/** Original code, stefan gustavson (PD). */
|
/** Original code, stefan gustavson (PD). */
|
||||||
|
|
||||||
#ifdef SIMPLEX_C
|
#ifdef SIMPLEX_C
|
||||||
|
|
||||||
/* SimplexNoise1234, Simplex noise with true analytic
|
/* SimplexNoise1234, Simplex noise with true analytic
|
||||||
* derivative in 1D to 4D.
|
* derivative in 1D to 4D.
|
||||||
*
|
*
|
||||||
* Author: Stefan Gustavson, 2003-2005
|
* Author: Stefan Gustavson, 2003-2005
|
||||||
* Contact: stefan.gustavson@liu.se
|
* Contact: stefan.gustavson@liu.se
|
||||||
*
|
*
|
||||||
* This code was GPL licensed until February 2011.
|
* This code was GPL licensed until February 2011.
|
||||||
* As the original author of this code, I hereby
|
* As the original author of this code, I hereby
|
||||||
* release it into the public domain.
|
* release it into the public domain.
|
||||||
* Please feel free to use it for whatever you want.
|
* Please feel free to use it for whatever you want.
|
||||||
* Credit is appreciated where appropriate, and I also
|
* Credit is appreciated where appropriate, and I also
|
||||||
* appreciate being told where this code finds any use,
|
* appreciate being told where this code finds any use,
|
||||||
* but you may do as you like.
|
* but you may do as you like.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This implementation is "Simplex Noise" as presented by
|
* This implementation is "Simplex Noise" as presented by
|
||||||
* Ken Perlin at a relatively obscure and not often cited course
|
* Ken Perlin at a relatively obscure and not often cited course
|
||||||
* session "Real-Time Shading" at Siggraph 2001 (before real
|
* session "Real-Time Shading" at Siggraph 2001 (before real
|
||||||
* time shading actually took off), under the title "hardware noise".
|
* time shading actually took off), under the title "hardware noise".
|
||||||
* The 3D function is numerically equivalent to his Java reference
|
* The 3D function is numerically equivalent to his Java reference
|
||||||
* code available in the PDF course notes, although I re-implemented
|
* code available in the PDF course notes, although I re-implemented
|
||||||
* it from scratch to get more readable code. The 1D, 2D and 4D cases
|
* it from scratch to get more readable code. The 1D, 2D and 4D cases
|
||||||
* were implemented from scratch by me from Ken Perlin's text.
|
* were implemented from scratch by me from Ken Perlin's text.
|
||||||
*
|
*
|
||||||
* This file has no dependencies on any other file, not even its own
|
* This file has no dependencies on any other file, not even its own
|
||||||
* header file. The header file is made for use by external code only.
|
* header file. The header file is made for use by external code only.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// We don't really need to include this, but play nice and do it anyway.
|
// We don't really need to include this, but play nice and do it anyway.
|
||||||
//#include "noise.c"
|
//#include "noise.c"
|
||||||
|
|
||||||
#define FASTFLOOR(x) ( ((int)(x)<=(x)) ? ((int)x) : (((int)x)-1) )
|
#define FASTFLOOR(x) ( ((int)(x)<=(x)) ? ((int)x) : (((int)x)-1) )
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// Static data
|
// Static data
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Permutation table. This is just a random jumble of all numbers 0-255,
|
* Permutation table. This is just a random jumble of all numbers 0-255,
|
||||||
* repeated twice to avoid wrapping the index at 255 for each lookup.
|
* repeated twice to avoid wrapping the index at 255 for each lookup.
|
||||||
* This needs to be exactly the same for all instances on all platforms,
|
* This needs to be exactly the same for all instances on all platforms,
|
||||||
* so it's easiest to just keep it as static explicit data.
|
* so it's easiest to just keep it as static explicit data.
|
||||||
* This also removes the need for any initialisation of this class.
|
* This also removes the need for any initialisation of this class.
|
||||||
*
|
*
|
||||||
* Note that making this an int[] instead of a char[] might make the
|
* Note that making this an int[] instead of a char[] might make the
|
||||||
* code run faster on platforms with a high penalty for unaligned single
|
* code run faster on platforms with a high penalty for unaligned single
|
||||||
* byte addressing. Intel x86 is generally single-byte-friendly, but
|
* byte addressing. Intel x86 is generally single-byte-friendly, but
|
||||||
* some other CPUs are faster with 4-aligned reads.
|
* some other CPUs are faster with 4-aligned reads.
|
||||||
* However, a char[] is smaller, which avoids cache trashing, and that
|
* However, a char[] is smaller, which avoids cache trashing, and that
|
||||||
* is probably the most important aspect on most architectures.
|
* is probably the most important aspect on most architectures.
|
||||||
* This array is accessed a *lot* by the noise functions.
|
* This array is accessed a *lot* by the noise functions.
|
||||||
* A vector-valued noise over 3D accesses it 96 times, and a
|
* A vector-valued noise over 3D accesses it 96 times, and a
|
||||||
* float-valued 4D noise 64 times. We want this to fit in the cache!
|
* float-valued 4D noise 64 times. We want this to fit in the cache!
|
||||||
*/
|
*/
|
||||||
unsigned char perm[512] = {151,160,137,91,90,15,
|
unsigned char perm[512] = {151,160,137,91,90,15,
|
||||||
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
||||||
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
||||||
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
||||||
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
||||||
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
||||||
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
||||||
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
||||||
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
||||||
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
||||||
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
||||||
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
|
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
|
||||||
151,160,137,91,90,15,
|
151,160,137,91,90,15,
|
||||||
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
||||||
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
||||||
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
||||||
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
||||||
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
||||||
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
||||||
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
||||||
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
||||||
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
||||||
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
||||||
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper functions to compute gradients-dot-residualvectors (1D to 4D)
|
* Helper functions to compute gradients-dot-residualvectors (1D to 4D)
|
||||||
* Note that these generate gradients of more than unit length. To make
|
* Note that these generate gradients of more than unit length. To make
|
||||||
* a close match with the value range of classic Perlin noise, the final
|
* a close match with the value range of classic Perlin noise, the final
|
||||||
* noise values need to be rescaled to fit nicely within [-1,1].
|
* noise values need to be rescaled to fit nicely within [-1,1].
|
||||||
* (The simplex noise functions as such also have different scaling.)
|
* (The simplex noise functions as such also have different scaling.)
|
||||||
* Note also that these noise functions are the most practical and useful
|
* Note also that these noise functions are the most practical and useful
|
||||||
* signed version of Perlin noise. To return values according to the
|
* signed version of Perlin noise. To return values according to the
|
||||||
* RenderMan specification from the SL noise() and pnoise() functions,
|
* RenderMan specification from the SL noise() and pnoise() functions,
|
||||||
* the noise values need to be scaled and offset to [0,1], like this:
|
* the noise values need to be scaled and offset to [0,1], like this:
|
||||||
* float SLnoise = (noise(x,y,z) + 1.0) * 0.5;
|
* float SLnoise = (noise(x,y,z) + 1.0) * 0.5;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
float grad1( int hash, float x ) {
|
float grad1( int hash, float x ) {
|
||||||
int h = hash & 15;
|
int h = hash & 15;
|
||||||
float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
|
float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
|
||||||
if (h&8) grad = -grad; // Set a random sign for the gradient
|
if (h&8) grad = -grad; // Set a random sign for the gradient
|
||||||
return ( grad * x ); // Multiply the gradient with the distance
|
return ( grad * x ); // Multiply the gradient with the distance
|
||||||
}
|
}
|
||||||
|
|
||||||
float grad2( int hash, float x, float y ) {
|
float grad2( int hash, float x, float y ) {
|
||||||
int h = hash & 7; // Convert low 3 bits of hash code
|
int h = hash & 7; // Convert low 3 bits of hash code
|
||||||
float u = h<4 ? x : y; // into 8 simple gradient directions,
|
float u = h<4 ? x : y; // into 8 simple gradient directions,
|
||||||
float v = h<4 ? y : x; // and compute the dot product with (x,y).
|
float v = h<4 ? y : x; // and compute the dot product with (x,y).
|
||||||
return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v);
|
return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v);
|
||||||
}
|
}
|
||||||
|
|
||||||
float grad3( int hash, float x, float y , float z ) {
|
float grad3( int hash, float x, float y , float z ) {
|
||||||
int h = hash & 15; // Convert low 4 bits of hash code into 12 simple
|
int h = hash & 15; // Convert low 4 bits of hash code into 12 simple
|
||||||
float u = h<8 ? x : y; // gradient directions, and compute dot product.
|
float u = h<8 ? x : y; // gradient directions, and compute dot product.
|
||||||
float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15
|
float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15
|
||||||
return ((h&1)? -u : u) + ((h&2)? -v : v);
|
return ((h&1)? -u : u) + ((h&2)? -v : v);
|
||||||
}
|
}
|
||||||
|
|
||||||
float grad4( int hash, float x, float y, float z, float t ) {
|
float grad4( int hash, float x, float y, float z, float t ) {
|
||||||
int h = hash & 31; // Convert low 5 bits of hash code into 32 simple
|
int h = hash & 31; // Convert low 5 bits of hash code into 32 simple
|
||||||
float u = h<24 ? x : y; // gradient directions, and compute dot product.
|
float u = h<24 ? x : y; // gradient directions, and compute dot product.
|
||||||
float v = h<16 ? y : z;
|
float v = h<16 ? y : z;
|
||||||
float w = h<8 ? z : t;
|
float w = h<8 ? z : t;
|
||||||
return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w);
|
return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A lookup table to traverse the simplex around a given point in 4D.
|
// A lookup table to traverse the simplex around a given point in 4D.
|
||||||
// Details can be found where this table is used, in the 4D noise method.
|
// Details can be found where this table is used, in the 4D noise method.
|
||||||
/* TODO: This should not be required, backport it from Bill's GLSL code! */
|
/* TODO: This should not be required, backport it from Bill's GLSL code! */
|
||||||
static unsigned char simplex[64][4] = {
|
static unsigned char simplex[64][4] = {
|
||||||
{0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0},
|
{0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0},
|
||||||
{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0},
|
{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0},
|
||||||
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
|
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
|
||||||
{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},
|
{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},
|
||||||
{1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},
|
{1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},
|
||||||
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
|
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
|
||||||
{2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0},
|
{2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0},
|
||||||
{2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}};
|
{2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}};
|
||||||
|
|
||||||
// 1D simplex noise
|
// 1D simplex noise
|
||||||
float snoise1(float x) {
|
float snoise1(float x) {
|
||||||
|
|
||||||
int i0 = FASTFLOOR(x);
|
int i0 = FASTFLOOR(x);
|
||||||
int i1 = i0 + 1;
|
int i1 = i0 + 1;
|
||||||
float x0 = x - i0;
|
float x0 = x - i0;
|
||||||
float x1 = x0 - 1.0f;
|
float x1 = x0 - 1.0f;
|
||||||
|
|
||||||
float n0, n1;
|
float n0, n1;
|
||||||
|
|
||||||
float t0 = 1.0f - x0*x0;
|
float t0 = 1.0f - x0*x0;
|
||||||
// if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case
|
// if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case
|
||||||
t0 *= t0;
|
t0 *= t0;
|
||||||
n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0);
|
n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0);
|
||||||
|
|
||||||
float t1 = 1.0f - x1*x1;
|
float t1 = 1.0f - x1*x1;
|
||||||
// if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case
|
// if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case
|
||||||
t1 *= t1;
|
t1 *= t1;
|
||||||
n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1);
|
n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1);
|
||||||
// The maximum value of this noise is 8*(3/4)^4 = 2.53125
|
// The maximum value of this noise is 8*(3/4)^4 = 2.53125
|
||||||
// A factor of 0.395 would scale to fit exactly within [-1,1], but
|
// A factor of 0.395 would scale to fit exactly within [-1,1], but
|
||||||
// we want to match PRMan's 1D noise, so we scale it down some more.
|
// we want to match PRMan's 1D noise, so we scale it down some more.
|
||||||
return 0.25f * (n0 + n1);
|
return 0.25f * (n0 + n1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2D simplex noise
|
// 2D simplex noise
|
||||||
float snoise2(float x, float y) {
|
float snoise2(float x, float y) {
|
||||||
|
|
||||||
#define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0)
|
#define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0)
|
||||||
#define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0
|
#define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0
|
||||||
|
|
||||||
float n0, n1, n2; // Noise contributions from the three corners
|
float n0, n1, n2; // Noise contributions from the three corners
|
||||||
|
|
||||||
// Skew the input space to determine which simplex cell we're in
|
// Skew the input space to determine which simplex cell we're in
|
||||||
float s = (x+y)*F2; // Hairy factor for 2D
|
float s = (x+y)*F2; // Hairy factor for 2D
|
||||||
float xs = x + s;
|
float xs = x + s;
|
||||||
float ys = y + s;
|
float ys = y + s;
|
||||||
int i = FASTFLOOR(xs);
|
int i = FASTFLOOR(xs);
|
||||||
int j = FASTFLOOR(ys);
|
int j = FASTFLOOR(ys);
|
||||||
|
|
||||||
float t = (float)(i+j)*G2;
|
float t = (float)(i+j)*G2;
|
||||||
float X0 = i-t; // Unskew the cell origin back to (x,y) space
|
float X0 = i-t; // Unskew the cell origin back to (x,y) space
|
||||||
float Y0 = j-t;
|
float Y0 = j-t;
|
||||||
float x0 = x-X0; // The x,y distances from the cell origin
|
float x0 = x-X0; // The x,y distances from the cell origin
|
||||||
float y0 = y-Y0;
|
float y0 = y-Y0;
|
||||||
|
|
||||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||||
// Determine which simplex we are in.
|
// Determine which simplex we are in.
|
||||||
int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
|
int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
|
||||||
if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
|
if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
|
||||||
else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
|
else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
|
||||||
|
|
||||||
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
|
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
|
||||||
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
||||||
// c = (3-sqrt(3))/6
|
// c = (3-sqrt(3))/6
|
||||||
|
|
||||||
float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
|
float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
|
||||||
float y1 = y0 - j1 + G2;
|
float y1 = y0 - j1 + G2;
|
||||||
float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords
|
float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords
|
||||||
float y2 = y0 - 1.0f + 2.0f * G2;
|
float y2 = y0 - 1.0f + 2.0f * G2;
|
||||||
|
|
||||||
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
|
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
|
||||||
int ii = i & 0xff;
|
int ii = i & 0xff;
|
||||||
int jj = j & 0xff;
|
int jj = j & 0xff;
|
||||||
|
|
||||||
// Calculate the contribution from the three corners
|
// Calculate the contribution from the three corners
|
||||||
float t0 = 0.5f - x0*x0-y0*y0;
|
float t0 = 0.5f - x0*x0-y0*y0;
|
||||||
if(t0 < 0.0f) n0 = 0.0f;
|
if(t0 < 0.0f) n0 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t0 *= t0;
|
t0 *= t0;
|
||||||
n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0);
|
n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t1 = 0.5f - x1*x1-y1*y1;
|
float t1 = 0.5f - x1*x1-y1*y1;
|
||||||
if(t1 < 0.0f) n1 = 0.0f;
|
if(t1 < 0.0f) n1 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t1 *= t1;
|
t1 *= t1;
|
||||||
n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1);
|
n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t2 = 0.5f - x2*x2-y2*y2;
|
float t2 = 0.5f - x2*x2-y2*y2;
|
||||||
if(t2 < 0.0f) n2 = 0.0f;
|
if(t2 < 0.0f) n2 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t2 *= t2;
|
t2 *= t2;
|
||||||
n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2);
|
n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add contributions from each corner to get the final noise value.
|
// Add contributions from each corner to get the final noise value.
|
||||||
// The result is scaled to return values in the interval [-1,1].
|
// The result is scaled to return values in the interval [-1,1].
|
||||||
return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary!
|
return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary!
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3D simplex noise
|
// 3D simplex noise
|
||||||
float snoise3(float x, float y, float z) {
|
float snoise3(float x, float y, float z) {
|
||||||
|
|
||||||
// Simple skewing factors for the 3D case
|
// Simple skewing factors for the 3D case
|
||||||
#define F3 0.333333333
|
#define F3 0.333333333
|
||||||
#define G3 0.166666667
|
#define G3 0.166666667
|
||||||
|
|
||||||
float n0, n1, n2, n3; // Noise contributions from the four corners
|
float n0, n1, n2, n3; // Noise contributions from the four corners
|
||||||
|
|
||||||
// Skew the input space to determine which simplex cell we're in
|
// Skew the input space to determine which simplex cell we're in
|
||||||
float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D
|
float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D
|
||||||
float xs = x+s;
|
float xs = x+s;
|
||||||
float ys = y+s;
|
float ys = y+s;
|
||||||
float zs = z+s;
|
float zs = z+s;
|
||||||
int i = FASTFLOOR(xs);
|
int i = FASTFLOOR(xs);
|
||||||
int j = FASTFLOOR(ys);
|
int j = FASTFLOOR(ys);
|
||||||
int k = FASTFLOOR(zs);
|
int k = FASTFLOOR(zs);
|
||||||
|
|
||||||
float t = (float)(i+j+k)*G3;
|
float t = (float)(i+j+k)*G3;
|
||||||
float X0 = i-t; // Unskew the cell origin back to (x,y,z) space
|
float X0 = i-t; // Unskew the cell origin back to (x,y,z) space
|
||||||
float Y0 = j-t;
|
float Y0 = j-t;
|
||||||
float Z0 = k-t;
|
float Z0 = k-t;
|
||||||
float x0 = x-X0; // The x,y,z distances from the cell origin
|
float x0 = x-X0; // The x,y,z distances from the cell origin
|
||||||
float y0 = y-Y0;
|
float y0 = y-Y0;
|
||||||
float z0 = z-Z0;
|
float z0 = z-Z0;
|
||||||
|
|
||||||
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
||||||
// Determine which simplex we are in.
|
// Determine which simplex we are in.
|
||||||
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
|
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
|
||||||
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
|
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
|
||||||
|
|
||||||
/* This code would benefit from a backport from the GLSL version! */
|
/* This code would benefit from a backport from the GLSL version! */
|
||||||
if(x0>=y0) {
|
if(x0>=y0) {
|
||||||
if(y0>=z0)
|
if(y0>=z0)
|
||||||
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
|
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
|
||||||
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
|
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
|
||||||
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
|
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
|
||||||
}
|
}
|
||||||
else { // x0<y0
|
else { // x0<y0
|
||||||
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
|
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
|
||||||
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
|
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
|
||||||
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
|
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
|
||||||
}
|
}
|
||||||
|
|
||||||
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
||||||
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
||||||
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
||||||
// c = 1/6.
|
// c = 1/6.
|
||||||
|
|
||||||
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
|
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
|
||||||
float y1 = y0 - j1 + G3;
|
float y1 = y0 - j1 + G3;
|
||||||
float z1 = z0 - k1 + G3;
|
float z1 = z0 - k1 + G3;
|
||||||
float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords
|
float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords
|
||||||
float y2 = y0 - j2 + 2.0f*G3;
|
float y2 = y0 - j2 + 2.0f*G3;
|
||||||
float z2 = z0 - k2 + 2.0f*G3;
|
float z2 = z0 - k2 + 2.0f*G3;
|
||||||
float x3 = x0 - 1.0f + 3.0f*G3; // Offsets for last corner in (x,y,z) coords
|
float x3 = x0 - 1.0f + 3.0f*G3; // Offsets for last corner in (x,y,z) coords
|
||||||
float y3 = y0 - 1.0f + 3.0f*G3;
|
float y3 = y0 - 1.0f + 3.0f*G3;
|
||||||
float z3 = z0 - 1.0f + 3.0f*G3;
|
float z3 = z0 - 1.0f + 3.0f*G3;
|
||||||
|
|
||||||
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
|
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
|
||||||
int ii = i & 0xff;
|
int ii = i & 0xff;
|
||||||
int jj = j & 0xff;
|
int jj = j & 0xff;
|
||||||
int kk = k & 0xff;
|
int kk = k & 0xff;
|
||||||
|
|
||||||
// Calculate the contribution from the four corners
|
// Calculate the contribution from the four corners
|
||||||
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0;
|
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0;
|
||||||
if(t0 < 0.0f) n0 = 0.0f;
|
if(t0 < 0.0f) n0 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t0 *= t0;
|
t0 *= t0;
|
||||||
n0 = t0 * t0 * grad3(perm[ii+perm[jj+perm[kk]]], x0, y0, z0);
|
n0 = t0 * t0 * grad3(perm[ii+perm[jj+perm[kk]]], x0, y0, z0);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1;
|
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1;
|
||||||
if(t1 < 0.0f) n1 = 0.0f;
|
if(t1 < 0.0f) n1 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t1 *= t1;
|
t1 *= t1;
|
||||||
n1 = t1 * t1 * grad3(perm[ii+i1+perm[jj+j1+perm[kk+k1]]], x1, y1, z1);
|
n1 = t1 * t1 * grad3(perm[ii+i1+perm[jj+j1+perm[kk+k1]]], x1, y1, z1);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2;
|
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2;
|
||||||
if(t2 < 0.0f) n2 = 0.0f;
|
if(t2 < 0.0f) n2 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t2 *= t2;
|
t2 *= t2;
|
||||||
n2 = t2 * t2 * grad3(perm[ii+i2+perm[jj+j2+perm[kk+k2]]], x2, y2, z2);
|
n2 = t2 * t2 * grad3(perm[ii+i2+perm[jj+j2+perm[kk+k2]]], x2, y2, z2);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3;
|
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3;
|
||||||
if(t3<0.0f) n3 = 0.0f;
|
if(t3<0.0f) n3 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t3 *= t3;
|
t3 *= t3;
|
||||||
n3 = t3 * t3 * grad3(perm[ii+1+perm[jj+1+perm[kk+1]]], x3, y3, z3);
|
n3 = t3 * t3 * grad3(perm[ii+1+perm[jj+1+perm[kk+1]]], x3, y3, z3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add contributions from each corner to get the final noise value.
|
// Add contributions from each corner to get the final noise value.
|
||||||
// The result is scaled to stay just inside [-1,1]
|
// The result is scaled to stay just inside [-1,1]
|
||||||
return 72.0f * (n0 + n1 + n2 + n3);
|
return 72.0f * (n0 + n1 + n2 + n3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 4D simplex noise
|
// 4D simplex noise
|
||||||
float snoise4(float x, float y, float z, float w) {
|
float snoise4(float x, float y, float z, float w) {
|
||||||
|
|
||||||
// The skewing and unskewing factors are hairy again for the 4D case
|
// The skewing and unskewing factors are hairy again for the 4D case
|
||||||
#define F4 0.309016994 // F4 = (Math.sqrt(5.0)-1.0)/4.0
|
#define F4 0.309016994 // F4 = (Math.sqrt(5.0)-1.0)/4.0
|
||||||
#define G4 0.138196601 // G4 = (5.0-Math.sqrt(5.0))/20.0
|
#define G4 0.138196601 // G4 = (5.0-Math.sqrt(5.0))/20.0
|
||||||
|
|
||||||
float n0, n1, n2, n3, n4; // Noise contributions from the five corners
|
float n0, n1, n2, n3, n4; // Noise contributions from the five corners
|
||||||
|
|
||||||
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
|
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
|
||||||
float s = (x + y + z + w) * F4; // Factor for 4D skewing
|
float s = (x + y + z + w) * F4; // Factor for 4D skewing
|
||||||
float xs = x + s;
|
float xs = x + s;
|
||||||
float ys = y + s;
|
float ys = y + s;
|
||||||
float zs = z + s;
|
float zs = z + s;
|
||||||
float ws = w + s;
|
float ws = w + s;
|
||||||
int i = FASTFLOOR(xs);
|
int i = FASTFLOOR(xs);
|
||||||
int j = FASTFLOOR(ys);
|
int j = FASTFLOOR(ys);
|
||||||
int k = FASTFLOOR(zs);
|
int k = FASTFLOOR(zs);
|
||||||
int l = FASTFLOOR(ws);
|
int l = FASTFLOOR(ws);
|
||||||
|
|
||||||
float t = (i + j + k + l) * G4; // Factor for 4D unskewing
|
float t = (i + j + k + l) * G4; // Factor for 4D unskewing
|
||||||
float X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
|
float X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
|
||||||
float Y0 = j - t;
|
float Y0 = j - t;
|
||||||
float Z0 = k - t;
|
float Z0 = k - t;
|
||||||
float W0 = l - t;
|
float W0 = l - t;
|
||||||
|
|
||||||
float x0 = x - X0; // The x,y,z,w distances from the cell origin
|
float x0 = x - X0; // The x,y,z,w distances from the cell origin
|
||||||
float y0 = y - Y0;
|
float y0 = y - Y0;
|
||||||
float z0 = z - Z0;
|
float z0 = z - Z0;
|
||||||
float w0 = w - W0;
|
float w0 = w - W0;
|
||||||
|
|
||||||
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
|
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
|
||||||
// To find out which of the 24 possible simplices we're in, we need to
|
// To find out which of the 24 possible simplices we're in, we need to
|
||||||
// determine the magnitude ordering of x0, y0, z0 and w0.
|
// determine the magnitude ordering of x0, y0, z0 and w0.
|
||||||
// The method below is a good way of finding the ordering of x,y,z,w and
|
// The method below is a good way of finding the ordering of x,y,z,w and
|
||||||
// then find the correct traversal order for the simplex we?re in.
|
// then find the correct traversal order for the simplex we?re in.
|
||||||
// First, six pair-wise comparisons are performed between each possible pair
|
// First, six pair-wise comparisons are performed between each possible pair
|
||||||
// of the four coordinates, and the results are used to add up binary bits
|
// of the four coordinates, and the results are used to add up binary bits
|
||||||
// for an integer index.
|
// for an integer index.
|
||||||
int c1 = (x0 > y0) ? 32 : 0;
|
int c1 = (x0 > y0) ? 32 : 0;
|
||||||
int c2 = (x0 > z0) ? 16 : 0;
|
int c2 = (x0 > z0) ? 16 : 0;
|
||||||
int c3 = (y0 > z0) ? 8 : 0;
|
int c3 = (y0 > z0) ? 8 : 0;
|
||||||
int c4 = (x0 > w0) ? 4 : 0;
|
int c4 = (x0 > w0) ? 4 : 0;
|
||||||
int c5 = (y0 > w0) ? 2 : 0;
|
int c5 = (y0 > w0) ? 2 : 0;
|
||||||
int c6 = (z0 > w0) ? 1 : 0;
|
int c6 = (z0 > w0) ? 1 : 0;
|
||||||
int c = c1 + c2 + c3 + c4 + c5 + c6;
|
int c = c1 + c2 + c3 + c4 + c5 + c6;
|
||||||
|
|
||||||
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
|
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
|
||||||
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
|
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
|
||||||
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
|
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
|
||||||
|
|
||||||
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
|
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
|
||||||
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
|
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
|
||||||
// impossible. Only the 24 indices which have non-zero entries make any sense.
|
// impossible. Only the 24 indices which have non-zero entries make any sense.
|
||||||
// We use a thresholding to set the coordinates in turn from the largest magnitude.
|
// We use a thresholding to set the coordinates in turn from the largest magnitude.
|
||||||
// The number 3 in the "simplex" array is at the position of the largest coordinate.
|
// The number 3 in the "simplex" array is at the position of the largest coordinate.
|
||||||
i1 = simplex[c][0]>=3 ? 1 : 0;
|
i1 = simplex[c][0]>=3 ? 1 : 0;
|
||||||
j1 = simplex[c][1]>=3 ? 1 : 0;
|
j1 = simplex[c][1]>=3 ? 1 : 0;
|
||||||
k1 = simplex[c][2]>=3 ? 1 : 0;
|
k1 = simplex[c][2]>=3 ? 1 : 0;
|
||||||
l1 = simplex[c][3]>=3 ? 1 : 0;
|
l1 = simplex[c][3]>=3 ? 1 : 0;
|
||||||
// The number 2 in the "simplex" array is at the second largest coordinate.
|
// The number 2 in the "simplex" array is at the second largest coordinate.
|
||||||
i2 = simplex[c][0]>=2 ? 1 : 0;
|
i2 = simplex[c][0]>=2 ? 1 : 0;
|
||||||
j2 = simplex[c][1]>=2 ? 1 : 0;
|
j2 = simplex[c][1]>=2 ? 1 : 0;
|
||||||
k2 = simplex[c][2]>=2 ? 1 : 0;
|
k2 = simplex[c][2]>=2 ? 1 : 0;
|
||||||
l2 = simplex[c][3]>=2 ? 1 : 0;
|
l2 = simplex[c][3]>=2 ? 1 : 0;
|
||||||
// The number 1 in the "simplex" array is at the second smallest coordinate.
|
// The number 1 in the "simplex" array is at the second smallest coordinate.
|
||||||
i3 = simplex[c][0]>=1 ? 1 : 0;
|
i3 = simplex[c][0]>=1 ? 1 : 0;
|
||||||
j3 = simplex[c][1]>=1 ? 1 : 0;
|
j3 = simplex[c][1]>=1 ? 1 : 0;
|
||||||
k3 = simplex[c][2]>=1 ? 1 : 0;
|
k3 = simplex[c][2]>=1 ? 1 : 0;
|
||||||
l3 = simplex[c][3]>=1 ? 1 : 0;
|
l3 = simplex[c][3]>=1 ? 1 : 0;
|
||||||
// The fifth corner has all coordinate offsets = 1, so no need to look that up.
|
// The fifth corner has all coordinate offsets = 1, so no need to look that up.
|
||||||
|
|
||||||
float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
|
float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
|
||||||
float y1 = y0 - j1 + G4;
|
float y1 = y0 - j1 + G4;
|
||||||
float z1 = z0 - k1 + G4;
|
float z1 = z0 - k1 + G4;
|
||||||
float w1 = w0 - l1 + G4;
|
float w1 = w0 - l1 + G4;
|
||||||
float x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords
|
float x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords
|
||||||
float y2 = y0 - j2 + 2.0f*G4;
|
float y2 = y0 - j2 + 2.0f*G4;
|
||||||
float z2 = z0 - k2 + 2.0f*G4;
|
float z2 = z0 - k2 + 2.0f*G4;
|
||||||
float w2 = w0 - l2 + 2.0f*G4;
|
float w2 = w0 - l2 + 2.0f*G4;
|
||||||
float x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords
|
float x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords
|
||||||
float y3 = y0 - j3 + 3.0f*G4;
|
float y3 = y0 - j3 + 3.0f*G4;
|
||||||
float z3 = z0 - k3 + 3.0f*G4;
|
float z3 = z0 - k3 + 3.0f*G4;
|
||||||
float w3 = w0 - l3 + 3.0f*G4;
|
float w3 = w0 - l3 + 3.0f*G4;
|
||||||
float x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords
|
float x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords
|
||||||
float y4 = y0 - 1.0f + 4.0f*G4;
|
float y4 = y0 - 1.0f + 4.0f*G4;
|
||||||
float z4 = z0 - 1.0f + 4.0f*G4;
|
float z4 = z0 - 1.0f + 4.0f*G4;
|
||||||
float w4 = w0 - 1.0f + 4.0f*G4;
|
float w4 = w0 - 1.0f + 4.0f*G4;
|
||||||
|
|
||||||
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
|
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
|
||||||
int ii = i & 0xff;
|
int ii = i & 0xff;
|
||||||
int jj = j & 0xff;
|
int jj = j & 0xff;
|
||||||
int kk = k & 0xff;
|
int kk = k & 0xff;
|
||||||
int ll = l & 0xff;
|
int ll = l & 0xff;
|
||||||
|
|
||||||
// Calculate the contribution from the five corners
|
// Calculate the contribution from the five corners
|
||||||
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0 - w0*w0;
|
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0 - w0*w0;
|
||||||
if(t0 < 0.0f) n0 = 0.0f;
|
if(t0 < 0.0f) n0 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t0 *= t0;
|
t0 *= t0;
|
||||||
n0 = t0 * t0 * grad4(perm[ii+perm[jj+perm[kk+perm[ll]]]], x0, y0, z0, w0);
|
n0 = t0 * t0 * grad4(perm[ii+perm[jj+perm[kk+perm[ll]]]], x0, y0, z0, w0);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1 - w1*w1;
|
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1 - w1*w1;
|
||||||
if(t1 < 0.0f) n1 = 0.0f;
|
if(t1 < 0.0f) n1 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t1 *= t1;
|
t1 *= t1;
|
||||||
n1 = t1 * t1 * grad4(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], x1, y1, z1, w1);
|
n1 = t1 * t1 * grad4(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], x1, y1, z1, w1);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2 - w2*w2;
|
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2 - w2*w2;
|
||||||
if(t2 < 0.0f) n2 = 0.0f;
|
if(t2 < 0.0f) n2 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t2 *= t2;
|
t2 *= t2;
|
||||||
n2 = t2 * t2 * grad4(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], x2, y2, z2, w2);
|
n2 = t2 * t2 * grad4(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], x2, y2, z2, w2);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3 - w3*w3;
|
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3 - w3*w3;
|
||||||
if(t3 < 0.0f) n3 = 0.0f;
|
if(t3 < 0.0f) n3 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t3 *= t3;
|
t3 *= t3;
|
||||||
n3 = t3 * t3 * grad4(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], x3, y3, z3, w3);
|
n3 = t3 * t3 * grad4(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], x3, y3, z3, w3);
|
||||||
}
|
}
|
||||||
|
|
||||||
float t4 = 0.5f - x4*x4 - y4*y4 - z4*z4 - w4*w4;
|
float t4 = 0.5f - x4*x4 - y4*y4 - z4*z4 - w4*w4;
|
||||||
if(t4 < 0.0f) n4 = 0.0f;
|
if(t4 < 0.0f) n4 = 0.0f;
|
||||||
else {
|
else {
|
||||||
t4 *= t4;
|
t4 *= t4;
|
||||||
n4 = t4 * t4 * grad4(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], x4, y4, z4, w4);
|
n4 = t4 * t4 * grad4(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], x4, y4, z4, w4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sum up and scale the result to cover the range [-1,1]
|
// Sum up and scale the result to cover the range [-1,1]
|
||||||
return 62.0f * (n0 + n1 + n2 + n3 + n4);
|
return 62.0f * (n0 + n1 + n2 + n3 + n4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef F2
|
#undef F2
|
||||||
#undef G2
|
#undef G2
|
||||||
|
|
||||||
#undef F3
|
#undef F3
|
||||||
#undef G3
|
#undef G3
|
||||||
|
|
||||||
#undef F4
|
#undef F4
|
||||||
#undef G4
|
#undef G4
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,290 +1,290 @@
|
||||||
// https://github.com/BareRose/swrap/blob/master/swrap.h
|
// https://github.com/BareRose/swrap/blob/master/swrap.h
|
||||||
|
|
||||||
/*
|
/*
|
||||||
swrap - Portable, protocol-agnostic TCP and UDP socket wrapper, primarily designed for client-server models in applications such as games
|
swrap - Portable, protocol-agnostic TCP and UDP socket wrapper, primarily designed for client-server models in applications such as games
|
||||||
|
|
||||||
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring
|
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring
|
||||||
rights to this software to the public domain worldwide. This software is distributed without any warranty.
|
rights to this software to the public domain worldwide. This software is distributed without any warranty.
|
||||||
You should have received a copy of the CC0 Public Domain Dedication along with this software.
|
You should have received a copy of the CC0 Public Domain Dedication along with this software.
|
||||||
If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
swrap supports the following three configurations:
|
swrap supports the following three configurations:
|
||||||
#define SWRAP_EXTERN
|
#define SWRAP_EXTERN
|
||||||
Default, should be used when using swrap in multiple compilation units within the same project.
|
Default, should be used when using swrap in multiple compilation units within the same project.
|
||||||
#define SWRAP_IMPLEMENTATION
|
#define SWRAP_IMPLEMENTATION
|
||||||
Must be defined in exactly one source file within a project for swrap to be found by the linker.
|
Must be defined in exactly one source file within a project for swrap to be found by the linker.
|
||||||
#define SWRAP_STATIC
|
#define SWRAP_STATIC
|
||||||
Defines all swrap functions as static, useful if swrap is only used in a single compilation unit.
|
Defines all swrap functions as static, useful if swrap is only used in a single compilation unit.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//include only once
|
//include only once
|
||||||
#ifndef SWRAP_H
|
#ifndef SWRAP_H
|
||||||
#define SWRAP_H
|
#define SWRAP_H
|
||||||
|
|
||||||
//process configuration
|
//process configuration
|
||||||
#ifdef SWRAP_STATIC
|
#ifdef SWRAP_STATIC
|
||||||
#define SWRAP_IMPLEMENTATION
|
#define SWRAP_IMPLEMENTATION
|
||||||
#define SWDEF static
|
#define SWDEF static
|
||||||
#else //SWRAP_EXTERN
|
#else //SWRAP_EXTERN
|
||||||
#define SWDEF extern
|
#define SWDEF extern
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//constants
|
//constants
|
||||||
#define SWRAP_TCP 0
|
#define SWRAP_TCP 0
|
||||||
#define SWRAP_UDP 1
|
#define SWRAP_UDP 1
|
||||||
#define SWRAP_BIND 0
|
#define SWRAP_BIND 0
|
||||||
#define SWRAP_CONNECT 1
|
#define SWRAP_CONNECT 1
|
||||||
#define SWRAP_DEFAULT 0x00
|
#define SWRAP_DEFAULT 0x00
|
||||||
#define SWRAP_NOBLOCK 0x01
|
#define SWRAP_NOBLOCK 0x01
|
||||||
#define SWRAP_NODELAY 0x02
|
#define SWRAP_NODELAY 0x02
|
||||||
|
|
||||||
//structs
|
//structs
|
||||||
struct swrap_addr {
|
struct swrap_addr {
|
||||||
char data[128]; //enough space to hold any kind of address
|
char data[128]; //enough space to hold any kind of address
|
||||||
};
|
};
|
||||||
|
|
||||||
//function declarations
|
//function declarations
|
||||||
SWDEF int swrapInit();
|
SWDEF int swrapInit();
|
||||||
SWDEF int swrapSocket(int, int, char, const char*, const char*);
|
SWDEF int swrapSocket(int, int, char, const char*, const char*);
|
||||||
SWDEF void swrapClose(int);
|
SWDEF void swrapClose(int);
|
||||||
SWDEF void swrapTerminate();
|
SWDEF void swrapTerminate();
|
||||||
SWDEF int swrapListen(int, int);
|
SWDEF int swrapListen(int, int);
|
||||||
SWDEF int swrapAccept(int, struct swrap_addr*);
|
SWDEF int swrapAccept(int, struct swrap_addr*);
|
||||||
SWDEF int swrapAddress(int, struct swrap_addr*);
|
SWDEF int swrapAddress(int, struct swrap_addr*);
|
||||||
SWDEF int swrapAddressInfo(struct swrap_addr*, char*, size_t, char*, size_t);
|
SWDEF int swrapAddressInfo(struct swrap_addr*, char*, size_t, char*, size_t);
|
||||||
SWDEF int swrapSend(int, const char*, size_t);
|
SWDEF int swrapSend(int, const char*, size_t);
|
||||||
SWDEF int swrapReceive(int, char*, size_t);
|
SWDEF int swrapReceive(int, char*, size_t);
|
||||||
SWDEF int swrapSendTo(int, struct swrap_addr*, const char*, size_t);
|
SWDEF int swrapSendTo(int, struct swrap_addr*, const char*, size_t);
|
||||||
SWDEF int swrapReceiveFrom(int, struct swrap_addr*, char*, size_t);
|
SWDEF int swrapReceiveFrom(int, struct swrap_addr*, char*, size_t);
|
||||||
SWDEF int swrapSelect(int, double);
|
SWDEF int swrapSelect(int, double);
|
||||||
SWDEF int swrapMultiSelect(int*, size_t, double);
|
SWDEF int swrapMultiSelect(int*, size_t, double);
|
||||||
|
|
||||||
//implementation section
|
//implementation section
|
||||||
#ifdef SWRAP_IMPLEMENTATION
|
#ifdef SWRAP_IMPLEMENTATION
|
||||||
|
|
||||||
//includes
|
//includes
|
||||||
#ifdef _WIN32 //windows
|
#ifdef _WIN32 //windows
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#else //unix
|
#else //unix
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#endif
|
#endif
|
||||||
#include <stddef.h> //NULL
|
#include <stddef.h> //NULL
|
||||||
#include <limits.h> //INT_MAX on emscripten //< @r-lyeh: added
|
#include <limits.h> //INT_MAX on emscripten //< @r-lyeh: added
|
||||||
|
|
||||||
//general functions
|
//general functions
|
||||||
SWDEF int swrapInit () {
|
SWDEF int swrapInit () {
|
||||||
//initializes socket functionality, returns 0 on success
|
//initializes socket functionality, returns 0 on success
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WSADATA WsaData;
|
WSADATA WsaData;
|
||||||
return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR);
|
return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR);
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
SWDEF int swrapSocket (int prot, int mode, char flags, const char* host, const char* serv) {
|
SWDEF int swrapSocket (int prot, int mode, char flags, const char* host, const char* serv) {
|
||||||
//protocol-agnostically creates a new socket configured according to the given parameters
|
//protocol-agnostically creates a new socket configured according to the given parameters
|
||||||
//sockets have to be created and bound/connected all at once to allow for protocol-agnosticity
|
//sockets have to be created and bound/connected all at once to allow for protocol-agnosticity
|
||||||
//int: Protocol of the socket, either SWRAP_TCP or SWRAP_UDP for TCP or UDP respectively
|
//int: Protocol of the socket, either SWRAP_TCP or SWRAP_UDP for TCP or UDP respectively
|
||||||
// SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept
|
// SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept
|
||||||
// SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent
|
// SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent
|
||||||
//int: Mode of the socket
|
//int: Mode of the socket
|
||||||
// SWRAP_BIND: Bind to given address (or all interfaces if NULL) and port, e.g. for a server
|
// SWRAP_BIND: Bind to given address (or all interfaces if NULL) and port, e.g. for a server
|
||||||
// SWRAP_CONNECT: Connect to given address (localhost if NULL) and port, e.g. for a client
|
// SWRAP_CONNECT: Connect to given address (localhost if NULL) and port, e.g. for a client
|
||||||
//char: Configuration flags, either SWRAP_DEFAULT or a bitwise combination of flags
|
//char: Configuration flags, either SWRAP_DEFAULT or a bitwise combination of flags
|
||||||
// SWRAP_NOBLOCK: Sets the socket to be non-blocking, default is blocking
|
// SWRAP_NOBLOCK: Sets the socket to be non-blocking, default is blocking
|
||||||
// SWRAP_NODELAY: Disables Nagle's for TCP sockets, default is enabled
|
// SWRAP_NODELAY: Disables Nagle's for TCP sockets, default is enabled
|
||||||
//char*: Host/address as a string, can be IPv4, IPv6, etc...
|
//char*: Host/address as a string, can be IPv4, IPv6, etc...
|
||||||
//char*: Service/port as a string, e.g. "1728" or "http"
|
//char*: Service/port as a string, e.g. "1728" or "http"
|
||||||
//returns socket handle, or -1 on failure
|
//returns socket handle, or -1 on failure
|
||||||
struct addrinfo* result, hint = {
|
struct addrinfo* result, hint = {
|
||||||
(mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags
|
(mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags
|
||||||
AF_UNSPEC, //ai_family
|
AF_UNSPEC, //ai_family
|
||||||
(prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype
|
(prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype
|
||||||
0, 0, NULL, NULL, NULL};
|
0, 0, NULL, NULL, NULL};
|
||||||
//get address info
|
//get address info
|
||||||
if (getaddrinfo(host, serv, &hint, &result)) return -1;
|
if (getaddrinfo(host, serv, &hint, &result)) return -1;
|
||||||
//create socket
|
//create socket
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||||
if (wsck == INVALID_SOCKET) return -1;
|
if (wsck == INVALID_SOCKET) return -1;
|
||||||
//reject socket handle outside int range
|
//reject socket handle outside int range
|
||||||
if (wsck > INT_MAX) {
|
if (wsck > INT_MAX) {
|
||||||
closesocket(wsck);
|
closesocket(wsck);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//convert to int
|
//convert to int
|
||||||
int sock = wsck;
|
int sock = wsck;
|
||||||
#else
|
#else
|
||||||
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||||
if (sock == -1) return -1;
|
if (sock == -1) return -1;
|
||||||
#endif
|
#endif
|
||||||
//make sure IPV6_ONLY is disabled
|
//make sure IPV6_ONLY is disabled
|
||||||
if (result->ai_family == AF_INET6) {
|
if (result->ai_family == AF_INET6) {
|
||||||
int no = 0;
|
int no = 0;
|
||||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
|
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
|
||||||
}
|
}
|
||||||
//set TCP_NODELAY if applicable
|
//set TCP_NODELAY if applicable
|
||||||
if (prot == SWRAP_TCP) {
|
if (prot == SWRAP_TCP) {
|
||||||
int nodelay = (flags&SWRAP_NODELAY);
|
int nodelay = (flags&SWRAP_NODELAY);
|
||||||
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay));
|
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay));
|
||||||
}
|
}
|
||||||
//bind if applicable
|
//bind if applicable
|
||||||
if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) {
|
if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) {
|
||||||
swrapClose(sock);
|
swrapClose(sock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//set non-blocking if needed
|
//set non-blocking if needed
|
||||||
if (flags&SWRAP_NOBLOCK) {
|
if (flags&SWRAP_NOBLOCK) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
DWORD no_block = 1;
|
DWORD no_block = 1;
|
||||||
if (ioctlsocket(sock, FIONBIO, &no_block)) {
|
if (ioctlsocket(sock, FIONBIO, &no_block)) {
|
||||||
swrapClose(sock);
|
swrapClose(sock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) {
|
if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) {
|
||||||
swrapClose(sock);
|
swrapClose(sock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
//connect if applicable (return only relevant if blocking)
|
//connect if applicable (return only relevant if blocking)
|
||||||
if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) {
|
if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) {
|
||||||
swrapClose(sock);
|
swrapClose(sock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//free address info
|
//free address info
|
||||||
freeaddrinfo(result);
|
freeaddrinfo(result);
|
||||||
//return socket handle
|
//return socket handle
|
||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
SWDEF void swrapClose (int sock) {
|
SWDEF void swrapClose (int sock) {
|
||||||
//closes the given socket
|
//closes the given socket
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
#else
|
#else
|
||||||
close(sock);
|
close(sock);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
SWDEF void swrapTerminate () {
|
SWDEF void swrapTerminate () {
|
||||||
//terminates socket functionality
|
//terminates socket functionality
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//connection functions
|
//connection functions
|
||||||
SWDEF int swrapListen (int sock, int blog) {
|
SWDEF int swrapListen (int sock, int blog) {
|
||||||
//configures the given socket (must be SWRAP_TCP + SWRAP_BIND) to listen for new connections with given maximum backlog
|
//configures the given socket (must be SWRAP_TCP + SWRAP_BIND) to listen for new connections with given maximum backlog
|
||||||
//returns 0 on success, non-zero on failure
|
//returns 0 on success, non-zero on failure
|
||||||
return listen(sock, blog);
|
return listen(sock, blog);
|
||||||
}
|
}
|
||||||
SWDEF int swrapAccept (int sock, struct swrap_addr* addr) {
|
SWDEF int swrapAccept (int sock, struct swrap_addr* addr) {
|
||||||
//uses the given socket (must be swrapListen) to accept a new incoming connection, optionally returning its address
|
//uses the given socket (must be swrapListen) to accept a new incoming connection, optionally returning its address
|
||||||
//returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections)
|
//returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int addr_size = sizeof(struct swrap_addr);
|
int addr_size = sizeof(struct swrap_addr);
|
||||||
SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
|
SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
|
||||||
if (wsck == INVALID_SOCKET) return -1;
|
if (wsck == INVALID_SOCKET) return -1;
|
||||||
//reject socket handle outside int range
|
//reject socket handle outside int range
|
||||||
if (wsck > INT_MAX) {
|
if (wsck > INT_MAX) {
|
||||||
closesocket(wsck);
|
closesocket(wsck);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//return new socket
|
//return new socket
|
||||||
return wsck;
|
return wsck;
|
||||||
#else
|
#else
|
||||||
socklen_t addr_size = sizeof(struct swrap_addr);
|
socklen_t addr_size = sizeof(struct swrap_addr);
|
||||||
return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
|
return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//address functions
|
//address functions
|
||||||
SWDEF int swrapAddress (int sock, struct swrap_addr* addr) {
|
SWDEF int swrapAddress (int sock, struct swrap_addr* addr) {
|
||||||
//writes the address the given socket is bound to into given address pointer, useful when automatically assigning port
|
//writes the address the given socket is bound to into given address pointer, useful when automatically assigning port
|
||||||
//returns 0 on success, non-zero on failure
|
//returns 0 on success, non-zero on failure
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int addr_size = sizeof(struct swrap_addr);
|
int addr_size = sizeof(struct swrap_addr);
|
||||||
#else
|
#else
|
||||||
socklen_t addr_size = sizeof(struct swrap_addr);
|
socklen_t addr_size = sizeof(struct swrap_addr);
|
||||||
#endif
|
#endif
|
||||||
return getsockname(sock, (struct sockaddr*)addr, &addr_size);
|
return getsockname(sock, (struct sockaddr*)addr, &addr_size);
|
||||||
}
|
}
|
||||||
SWDEF int swrapAddressInfo (struct swrap_addr* addr, char* host, size_t host_size, char* serv, size_t serv_size) {
|
SWDEF int swrapAddressInfo (struct swrap_addr* addr, char* host, size_t host_size, char* serv, size_t serv_size) {
|
||||||
//writes the host/address and service/port of given address into given buffers (pointer + size), either buffer may be NULL
|
//writes the host/address and service/port of given address into given buffers (pointer + size), either buffer may be NULL
|
||||||
//returns 0 on success, non-zero on failure
|
//returns 0 on success, non-zero on failure
|
||||||
return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0);
|
return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//send/receive functions
|
//send/receive functions
|
||||||
SWDEF int swrapSend (int sock, const char* data, size_t data_size) {
|
SWDEF int swrapSend (int sock, const char* data, size_t data_size) {
|
||||||
//uses the given socket (either SWRAP_CONNECT or returned by swrapAccept) to send given data (pointer + size)
|
//uses the given socket (either SWRAP_CONNECT or returned by swrapAccept) to send given data (pointer + size)
|
||||||
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
|
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
|
||||||
//returns how much data was actually sent (may be less than data size), or -1 on failure
|
//returns how much data was actually sent (may be less than data size), or -1 on failure
|
||||||
return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
|
return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
|
||||||
}
|
}
|
||||||
SWDEF int swrapReceive (int sock, char* data, size_t data_size) {
|
SWDEF int swrapReceive (int sock, char* data, size_t data_size) {
|
||||||
//receives data using given socket (either SWRAP_CONNECT or returned by swrapAccept) into given buffer (pointer + size)
|
//receives data using given socket (either SWRAP_CONNECT or returned by swrapAccept) into given buffer (pointer + size)
|
||||||
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
|
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
|
||||||
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
|
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
|
||||||
return recv(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
|
return recv(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
|
||||||
}
|
}
|
||||||
SWDEF int swrapSendTo (int sock, struct swrap_addr* addr, const char* data, size_t data_size) {
|
SWDEF int swrapSendTo (int sock, struct swrap_addr* addr, const char* data, size_t data_size) {
|
||||||
//uses the given socket to send given data (pointer + size) to the given swrap_addr (e.g. from swrapReceiveFrom)
|
//uses the given socket to send given data (pointer + size) to the given swrap_addr (e.g. from swrapReceiveFrom)
|
||||||
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
|
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
|
||||||
//returns how much data was actually sent (may be less than data size), or -1 on failure
|
//returns how much data was actually sent (may be less than data size), or -1 on failure
|
||||||
return sendto(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, sizeof(struct swrap_addr));
|
return sendto(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, sizeof(struct swrap_addr));
|
||||||
}
|
}
|
||||||
SWDEF int swrapReceiveFrom (int sock, struct swrap_addr* addr, char* data, size_t data_size) {
|
SWDEF int swrapReceiveFrom (int sock, struct swrap_addr* addr, char* data, size_t data_size) {
|
||||||
//receives data using given socket into given buffer (pointer + size), optionally returning sender's address
|
//receives data using given socket into given buffer (pointer + size), optionally returning sender's address
|
||||||
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
|
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
|
||||||
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
|
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int addr_size = sizeof(struct swrap_addr);
|
int addr_size = sizeof(struct swrap_addr);
|
||||||
#else
|
#else
|
||||||
socklen_t addr_size = sizeof(struct swrap_addr);
|
socklen_t addr_size = sizeof(struct swrap_addr);
|
||||||
#endif
|
#endif
|
||||||
return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size);
|
return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
//select functions
|
//select functions
|
||||||
SWDEF int swrapSelect (int sock, double timeout) {
|
SWDEF int swrapSelect (int sock, double timeout) {
|
||||||
//waits either until given socket has new data to receive or given time (in seconds) has passed
|
//waits either until given socket has new data to receive or given time (in seconds) has passed
|
||||||
//if given socket is -1 an empty select will be performed, which is just a sub-second sleep
|
//if given socket is -1 an empty select will be performed, which is just a sub-second sleep
|
||||||
//returns 1 if new data is available, 0 if timeout was reached, and -1 on error
|
//returns 1 if new data is available, 0 if timeout was reached, and -1 on error
|
||||||
fd_set set; struct timeval time;
|
fd_set set; struct timeval time;
|
||||||
//fd set
|
//fd set
|
||||||
FD_ZERO(&set);
|
FD_ZERO(&set);
|
||||||
if (sock > -1) FD_SET(sock, &set);
|
if (sock > -1) FD_SET(sock, &set);
|
||||||
//timeout
|
//timeout
|
||||||
time.tv_sec = timeout;
|
time.tv_sec = timeout;
|
||||||
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
|
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
|
||||||
//return
|
//return
|
||||||
return select(sock+1, &set, NULL, NULL, &time);
|
return select(sock+1, &set, NULL, NULL, &time);
|
||||||
}
|
}
|
||||||
SWDEF int swrapMultiSelect (int* socks, size_t socks_size, double timeout) {
|
SWDEF int swrapMultiSelect (int* socks, size_t socks_size, double timeout) {
|
||||||
//waits either until a socket in given list has new data to receive or given time (in seconds) has passed
|
//waits either until a socket in given list has new data to receive or given time (in seconds) has passed
|
||||||
//if the given list length is 0 an empty select will be performed, which is just a sub-second sleep
|
//if the given list length is 0 an empty select will be performed, which is just a sub-second sleep
|
||||||
//returns 1 or more if new data is available, 0 if timeout was reached, and -1 on error
|
//returns 1 or more if new data is available, 0 if timeout was reached, and -1 on error
|
||||||
fd_set set; struct timeval time; int sock_max = -1;
|
fd_set set; struct timeval time; int sock_max = -1;
|
||||||
//fd set
|
//fd set
|
||||||
FD_ZERO(&set);
|
FD_ZERO(&set);
|
||||||
for (size_t i = 0; i < socks_size; i++) {
|
for (size_t i = 0; i < socks_size; i++) {
|
||||||
if (socks[i] > sock_max) sock_max = socks[i];
|
if (socks[i] > sock_max) sock_max = socks[i];
|
||||||
if (socks[i] > -1) FD_SET(socks[i], &set);
|
if (socks[i] > -1) FD_SET(socks[i], &set);
|
||||||
}
|
}
|
||||||
//timeout
|
//timeout
|
||||||
time.tv_sec = timeout;
|
time.tv_sec = timeout;
|
||||||
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
|
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
|
||||||
//return
|
//return
|
||||||
return select(sock_max+1, &set, NULL, NULL, &time);
|
return select(sock_max+1, &set, NULL, NULL, &time);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //SWRAP_IMPLEMENTATION
|
#endif //SWRAP_IMPLEMENTATION
|
||||||
#endif //SWRAP_H
|
#endif //SWRAP_H
|
||||||
|
|
16116
engine/split/3rd_tfd.h
16116
engine/split/3rd_tfd.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,82 +1,82 @@
|
||||||
// AI framework
|
// AI framework
|
||||||
// - rlyeh, public domain.
|
// - rlyeh, public domain.
|
||||||
//
|
//
|
||||||
// [src] original A-star code by @mmozeiko (PD) - https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275
|
// [src] original A-star code by @mmozeiko (PD) - https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275
|
||||||
// [src] original swarm/boids code by @Cultrarius (UNLICENSE) - https://github.com/Cultrarius/Swarmz
|
// [src] original swarm/boids code by @Cultrarius (UNLICENSE) - https://github.com/Cultrarius/Swarmz
|
||||||
|
|
||||||
// pathfinding -----------------------------------------------------------------
|
// pathfinding -----------------------------------------------------------------
|
||||||
|
|
||||||
API int pathfind_astar(int width, int height, const unsigned* map, vec2i src, vec2i dst, vec2i* path, size_t maxpath);
|
API int pathfind_astar(int width, int height, const unsigned* map, vec2i src, vec2i dst, vec2i* path, size_t maxpath);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Behavior trees: decision planning and decision making.
|
// Behavior trees: decision planning and decision making.
|
||||||
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
|
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
|
||||||
|
|
||||||
typedef int (*bt_func)();
|
typedef int (*bt_func)();
|
||||||
|
|
||||||
typedef struct bt_t {
|
typedef struct bt_t {
|
||||||
uint64_t type;
|
uint64_t type;
|
||||||
int (*action)();
|
int (*action)();
|
||||||
union {
|
union {
|
||||||
int argi;
|
int argi;
|
||||||
float argf;
|
float argf;
|
||||||
};
|
};
|
||||||
array(struct bt_t) children;
|
array(struct bt_t) children;
|
||||||
} bt_t;
|
} bt_t;
|
||||||
|
|
||||||
API bt_t bt(const char *ini_file, unsigned flags);
|
API bt_t bt(const char *ini_file, unsigned flags);
|
||||||
API int bt_run(bt_t *b);
|
API int bt_run(bt_t *b);
|
||||||
API void bt_addfun(const char *name, int(*func)());
|
API void bt_addfun(const char *name, int(*func)());
|
||||||
API bt_func bt_findfun(const char *name);
|
API bt_func bt_findfun(const char *name);
|
||||||
API char *bt_funcname(bt_func fn);
|
API char *bt_funcname(bt_func fn);
|
||||||
|
|
||||||
API int ui_bt(bt_t *b);
|
API int ui_bt(bt_t *b);
|
||||||
|
|
||||||
// boids/swarm -----------------------------------------------------------------
|
// boids/swarm -----------------------------------------------------------------
|
||||||
|
|
||||||
typedef enum SWARM_DISTANCE {
|
typedef enum SWARM_DISTANCE {
|
||||||
SWARM_DISTANCE_LINEAR,
|
SWARM_DISTANCE_LINEAR,
|
||||||
SWARM_DISTANCE_INVERSE_LINEAR,
|
SWARM_DISTANCE_INVERSE_LINEAR,
|
||||||
SWARM_DISTANCE_QUADRATIC,
|
SWARM_DISTANCE_QUADRATIC,
|
||||||
SWARM_DISTANCE_INVERSE_QUADRATIC
|
SWARM_DISTANCE_INVERSE_QUADRATIC
|
||||||
} SWARM_DISTANCE;
|
} SWARM_DISTANCE;
|
||||||
|
|
||||||
#define boid(...) C_CAST(boid_t, __VA_ARGS__)
|
#define boid(...) C_CAST(boid_t, __VA_ARGS__)
|
||||||
|
|
||||||
typedef struct boid_t {
|
typedef struct boid_t {
|
||||||
vec3 position;
|
vec3 position;
|
||||||
vec3 velocity;
|
vec3 velocity;
|
||||||
vec3 acceleration;
|
vec3 acceleration;
|
||||||
vec3 prev_position;
|
vec3 prev_position;
|
||||||
} boid_t;
|
} boid_t;
|
||||||
|
|
||||||
typedef struct swarm_t {
|
typedef struct swarm_t {
|
||||||
array(boid_t) boids;
|
array(boid_t) boids;
|
||||||
|
|
||||||
float perception_radius; // determines the vision radius of each boid. Only boids within this distance influence each other.
|
float perception_radius; // determines the vision radius of each boid. Only boids within this distance influence each other.
|
||||||
|
|
||||||
float separation_weight; // how much boids repel each other
|
float separation_weight; // how much boids repel each other
|
||||||
SWARM_DISTANCE separation_type;
|
SWARM_DISTANCE separation_type;
|
||||||
|
|
||||||
float alignment_weight; // how much boids want go in the same direction
|
float alignment_weight; // how much boids want go in the same direction
|
||||||
float cohesion_weight; // how much boids want to be in the center of the swarm
|
float cohesion_weight; // how much boids want to be in the center of the swarm
|
||||||
|
|
||||||
float steering_weight;
|
float steering_weight;
|
||||||
array(vec3) steering_targets;
|
array(vec3) steering_targets;
|
||||||
SWARM_DISTANCE steering_target_type;
|
SWARM_DISTANCE steering_target_type;
|
||||||
|
|
||||||
float blindspot_angledeg;
|
float blindspot_angledeg;
|
||||||
float max_acceleration; // how fast each boid can change its direction
|
float max_acceleration; // how fast each boid can change its direction
|
||||||
float max_velocity; // how fast each boid can move
|
float max_velocity; // how fast each boid can move
|
||||||
|
|
||||||
// private:
|
// private:
|
||||||
map(vec3*, array(boid_t*)) voxel_cache_;
|
map(vec3*, array(boid_t*)) voxel_cache_;
|
||||||
float blindspot_angledeg_compare_value_;
|
float blindspot_angledeg_compare_value_;
|
||||||
} swarm_t;
|
} swarm_t;
|
||||||
|
|
||||||
API swarm_t swarm();
|
API swarm_t swarm();
|
||||||
API void swarm_update(swarm_t *self, float delta); // acc,vel,pos
|
API void swarm_update(swarm_t *self, float delta); // acc,vel,pos
|
||||||
API void swarm_update_acceleration_only(swarm_t *self); // acc
|
API void swarm_update_acceleration_only(swarm_t *self); // acc
|
||||||
API void swarm_update_acceleration_and_velocity_only(swarm_t *self, float delta); // acc,vel
|
API void swarm_update_acceleration_and_velocity_only(swarm_t *self, float delta); // acc,vel
|
||||||
|
|
||||||
API int ui_swarm(swarm_t *self);
|
API int ui_swarm(swarm_t *self);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue