main
Dominik Madarász 2024-03-26 17:15:02 +01:00
parent b14c31b4e0
commit b841064174
196 changed files with 1133249 additions and 1136958 deletions

50
.gitignore vendored
View File

@ -1,25 +1,25 @@
default
.art*.zip
__pycache__
.vs
_deploy
tools/steamcmd
!tools/steamcmd/steamcmd.exe
_builder_output
emsdk
demos/html5/*.data
demos/html5/*.js
demos/html5/*.html
demos/ports/*/*.exe
demos/ports/doom/.doomrc
*.sublime-workspace
engine/v4k.html
*.exe.manifest
*.log
v4k.osx
libv4k.*
*.dSYM
.DS_store
*.raddbg
plugins
tools/assimp-vc*-mt.lib
default
.art*.zip
__pycache__
.vs
_deploy
tools/steamcmd
!tools/steamcmd/steamcmd.exe
_builder_output
emsdk
demos/html5/*.data
demos/html5/*.js
demos/html5/*.html
demos/ports/*/*.exe
demos/ports/doom/.doomrc
*.sublime-workspace
engine/v4k.html
*.exe.manifest
*.log
v4k.osx
libv4k.*
*.dSYM
.DS_store
*.raddbg
plugins
tools/assimp-vc*-mt.lib

2048
MAKE.bat

File diff suppressed because it is too large Load Diff

0
MAKE.sh 100755 → 100644
View File

File diff suppressed because it is too large Load Diff

View File

@ -1,134 +1,134 @@
// material demo
// - rlyeh, public domain
//
// @todo: object_print(obj, "");
#include "v4k.h"
const char *skyboxes[][2] = { // reflection, env, metadata
{"hdr/Tokyo_BigSight_1k.hdr","hdr/Tokyo_BigSight_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/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/Factory_Catwalk_1k.hdr","hdr/Factory_Catwalk_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/mesto.hdr","hdr/mesto_Env.hdr"},
};
int main() {
// create the window
window_create( 0.75f, WINDOW_MSAA8 );
window_color( GRAY );
// create camera
camera_t cam = camera();
// fx: load all post fx files in all subdirs.
fx_load("fx**.fs");
// load video, RGB texture, no audio
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_NO_AUDIO | VIDEO_LOOP ); video_seek(v, 30);
// load texture
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_SRGB);
texture_t t2 = texture("matcaps/material3", TEXTURE_SRGB);
// load model
model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS);
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("Scutum_low.fbx", 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);
// spawn object1 (diffuse)
object_t* obj1 = scene_spawn();
object_model(obj1, m1);
object_diffuse(obj1, t1);
object_scale(obj1, vec3(3,3,3));
object_move(obj1, vec3(-10+5*0,0,-10));
object_pivot(obj1, vec3(0,90,0));
// spawn object2 (matcap)
object_t* obj2 = scene_spawn();
object_model(obj2, m2);
object_diffuse(obj2, t2);
object_scale(obj2, vec3(3,3,3));
object_move(obj2, vec3(-10+5*2,0,-10));
object_pivot(obj2, vec3(0,90,0));
// spawn object3 (video)
object_t* obj3 = scene_spawn();
object_model(obj3, m1);
object_diffuse(obj3, video_textures(v)[0]);
object_scale(obj3, vec3(3,3,3));
object_move(obj3, vec3(-10+5*1,0,-10));
object_pivot(obj3, vec3(0,90,0));
// spawn object4 (pbr)
object_t* obj4 = scene_spawn();
object_model(obj4, m3);
object_scale(obj4, vec3(3,3,3));
object_move(obj4, vec3(-10+6*3,0,-10));
object_pivot(obj4, vec3(0,0,90));
// spawn object5 (pbr)
// object_t* obj5 = scene_spawn();
// object_model(obj5, m4);
// object_scale(obj5, vec3(3,3,3));
// object_move(obj5, vec3(-10+6*3,0,-10));
// object_pivot(obj5, vec3(0,0,90));
// create point light
scene_spawn_light(); // sun
light_t* l = scene_spawn_light();
light_type(l, LIGHT_POINT);
l->diffuse = vec3(1,0,0);
// load skybox
scene_get_active()->skybox = skybox_pbr(skyboxes[0][0], skyboxes[0][0], skyboxes[0][1]);
while(window_swap() && !input(KEY_ESC)) {
// draw environment
ddraw_grid(0);
// update video
video_decode( v );
// update light position
light_teleport(l, cam.position);
// draw scene
fx_begin();
scene_render(SCENE_FOREGROUND|SCENE_BACKGROUND|SCENE_UPDATE_SH_COEF);
fx_end();
// fps camera
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);
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);
camera_moveby(&cam, wasdec);
camera_fps(&cam, mouselook.x,mouselook.y);
window_cursor( !active );
if (ui_panel("FXs", 0)) {
ui_fxs();
ui_panel_end();
}
if( ui_panel( "Viewer", 0 ) ) {
for( int i = 0; i < countof(skyboxes); i++ ) {
const char *filename = skyboxes[i][0];
// bool selected = !strcmp(g_skybox.reflection->filename, file_name(filename));
bool selected = false;
if( ui_bool( filename, &selected ) ) {
scene_get_active()->skybox = skybox_pbr(skyboxes[i][0], skyboxes[i][0], skyboxes[i][1]);
}
}
ui_panel_end();
}
}
}
// material demo
// - rlyeh, public domain
//
// @todo: object_print(obj, "");
#include "v4k.h"
const char *skyboxes[][2] = { // reflection, env, metadata
{"hdr/Tokyo_BigSight_1k.hdr","hdr/Tokyo_BigSight_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/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/Factory_Catwalk_1k.hdr","hdr/Factory_Catwalk_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/mesto.hdr","hdr/mesto_Env.hdr"},
};
int main() {
// create the window
window_create( 0.75f, WINDOW_MSAA8 );
window_color( GRAY );
// create camera
camera_t cam = camera();
// fx: load all post fx files in all subdirs.
fx_load("fx**.fs");
// load video, RGB texture, no audio
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_NO_AUDIO | VIDEO_LOOP ); video_seek(v, 30);
// load texture
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_SRGB);
texture_t t2 = texture("matcaps/material3", TEXTURE_SRGB);
// load model
model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS);
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("Scutum_low.fbx", 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);
// spawn object1 (diffuse)
object_t* obj1 = scene_spawn();
object_model(obj1, m1);
object_diffuse(obj1, t1);
object_scale(obj1, vec3(3,3,3));
object_move(obj1, vec3(-10+5*0,0,-10));
object_pivot(obj1, vec3(0,90,0));
// spawn object2 (matcap)
object_t* obj2 = scene_spawn();
object_model(obj2, m2);
object_diffuse(obj2, t2);
object_scale(obj2, vec3(3,3,3));
object_move(obj2, vec3(-10+5*2,0,-10));
object_pivot(obj2, vec3(0,90,0));
// spawn object3 (video)
object_t* obj3 = scene_spawn();
object_model(obj3, m1);
object_diffuse(obj3, video_textures(v)[0]);
object_scale(obj3, vec3(3,3,3));
object_move(obj3, vec3(-10+5*1,0,-10));
object_pivot(obj3, vec3(0,90,0));
// spawn object4 (pbr)
object_t* obj4 = scene_spawn();
object_model(obj4, m3);
object_scale(obj4, vec3(3,3,3));
object_move(obj4, vec3(-10+6*3,0,-10));
object_pivot(obj4, vec3(0,0,90));
// spawn object5 (pbr)
// object_t* obj5 = scene_spawn();
// object_model(obj5, m4);
// object_scale(obj5, vec3(3,3,3));
// object_move(obj5, vec3(-10+6*3,0,-10));
// object_pivot(obj5, vec3(0,0,90));
// create point light
scene_spawn_light(); // sun
light_t* l = scene_spawn_light();
light_type(l, LIGHT_POINT);
l->diffuse = vec3(1,0,0);
// load skybox
scene_get_active()->skybox = skybox_pbr(skyboxes[0][0], skyboxes[0][0], skyboxes[0][1]);
while(window_swap() && !input(KEY_ESC)) {
// draw environment
ddraw_grid(0);
// update video
video_decode( v );
// update light position
light_teleport(l, cam.position);
// draw scene
fx_begin();
scene_render(SCENE_FOREGROUND|SCENE_BACKGROUND|SCENE_UPDATE_SH_COEF);
fx_end();
// fps camera
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);
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);
camera_moveby(&cam, wasdec);
camera_fps(&cam, mouselook.x,mouselook.y);
window_cursor( !active );
if (ui_panel("FXs", 0)) {
ui_fxs();
ui_panel_end();
}
if( ui_panel( "Viewer", 0 ) ) {
for( int i = 0; i < countof(skyboxes); i++ ) {
const char *filename = skyboxes[i][0];
// bool selected = !strcmp(g_skybox.reflection->filename, file_name(filename));
bool selected = false;
if( ui_bool( filename, &selected ) ) {
scene_get_active()->skybox = skybox_pbr(skyboxes[i][0], skyboxes[i][0], skyboxes[i][1]);
}
}
ui_panel_end();
}
}
}

View File

@ -1,79 +1,79 @@
#include "v4k.h"
model_t litm;
void bakedrawmodel(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata) {
shader_bind(lm->shader);
model_render(*m, proj, view, m->pivot, lm->shader);
shader_float("u_litboost", 4.0);
model_render(litm, proj, view, litm.pivot, lm->shader);
}
void progressupdate(float progress) {
static double lastUpdateTime = 0.0;
double time = time_ss();
if (time - lastUpdateTime > 1.0) {
lastUpdateTime = time;
PRINTF("progress: %.02f%%", progress*100);
}
// window_swap();
}
int main()
{
window_create(0.5, 0);
window_title(__FILE__);
camera_t cam = camera();
skybox_t sky = skybox(0, 0); skybox_mie_calc_sh(&sky, 2.0f);
model_t mdl = model(option("--model","gazebo.obj"), 0);
litm = model("cube.obj", 0);
{
mat44 lp; scaling44(lp, 0.3, 0.3, 0.3); translate44(lp, 8,4,0);
copy44(litm.pivot, lp);
}
rotate44(mdl.pivot, -90, 1, 0, 0);
scale44(mdl.pivot, 4,4,4);
shader_bind(mdl.program);
shader_vec3v("u_coefficients_sh", 9, sky.cubemap.sh);
// shader_bool("u_texmod", 0);
unsigned char emissive[] = { 255, 180, 0, 255 };
texture_t emission = texture_create(1,1,4,emissive,TEXTURE_LINEAR);
model_set_texture(litm, emission);
lightmap_t baker = lightmap(64, 0.01, 100, vec3(0,0,0), 2, 0.01, 0.0);
lightmap_setup(&baker, 512, 512);
array_push(baker.models, &mdl);
bool do_bake=0;
int b=1;
while (window_swap() && !input(KEY_ESC)) {
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);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( !active );
skybox_render(&sky, cam.proj, cam.view);
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
model_render(litm, cam.proj, cam.view, litm.pivot, 0);
if( ui_panel("Lightmapper", PANEL_OPEN) ) {
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_int("Bounces", &b);
if( ui_button(va("Bake %d light bounce(s)", b)) ) {
do_bake=1;
}
ui_panel_end();
}
if (do_bake) {
do_bake=0;
lightmap_bake(&baker, b, bakedrawmodel, progressupdate, 0);
}
}
}
#include "v4k.h"
model_t litm;
void bakedrawmodel(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata) {
shader_bind(lm->shader);
model_render(*m, proj, view, m->pivot, lm->shader);
shader_float("u_litboost", 4.0);
model_render(litm, proj, view, litm.pivot, lm->shader);
}
void progressupdate(float progress) {
static double lastUpdateTime = 0.0;
double time = time_ss();
if (time - lastUpdateTime > 1.0) {
lastUpdateTime = time;
PRINTF("progress: %.02f%%", progress*100);
}
// window_swap();
}
int main()
{
window_create(0.5, 0);
window_title(__FILE__);
camera_t cam = camera();
skybox_t sky = skybox(0, 0); skybox_mie_calc_sh(&sky, 2.0f);
model_t mdl = model(option("--model","gazebo.obj"), 0);
litm = model("cube.obj", 0);
{
mat44 lp; scaling44(lp, 0.3, 0.3, 0.3); translate44(lp, 8,4,0);
copy44(litm.pivot, lp);
}
rotate44(mdl.pivot, -90, 1, 0, 0);
scale44(mdl.pivot, 4,4,4);
shader_bind(mdl.program);
shader_vec3v("u_coefficients_sh", 9, sky.cubemap.sh);
// shader_bool("u_texmod", 0);
unsigned char emissive[] = { 255, 180, 0, 255 };
texture_t emission = texture_create(1,1,4,emissive,TEXTURE_LINEAR);
model_set_texture(litm, emission);
lightmap_t baker = lightmap(64, 0.01, 100, vec3(0,0,0), 2, 0.01, 0.0);
lightmap_setup(&baker, 512, 512);
array_push(baker.models, &mdl);
bool do_bake=0;
int b=1;
while (window_swap() && !input(KEY_ESC)) {
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);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( !active );
skybox_render(&sky, cam.proj, cam.view);
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
model_render(litm, cam.proj, cam.view, litm.pivot, 0);
if( ui_panel("Lightmapper", PANEL_OPEN) ) {
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_int("Bounces", &b);
if( ui_button(va("Bake %d light bounce(s)", b)) ) {
do_bake=1;
}
ui_panel_end();
}
if (do_bake) {
do_bake=0;
lightmap_bake(&baker, b, bakedrawmodel, progressupdate, 0);
}
}
}

View File

@ -1,14 +1,14 @@
frame: 0-50 AlienArmature|Alien_Clapping
frame: 51-106 AlienArmature|Alien_Death
frame: 107-207 AlienArmature|Alien_Idle
frame: 208-308 AlienArmature|Alien_IdleHold
frame: 309-334 AlienArmature|Alien_Jump
frame: 335-357 AlienArmature|Alien_Punch
frame: 358-379 AlienArmature|Alien_Run
frame: 380-401 AlienArmature|Alien_RunHold
frame: 402-432 AlienArmature|Alien_RunningJump
frame: 433-459 AlienArmature|Alien_Sitting
frame: 460-475 AlienArmature|Alien_Standing
frame: 476-508 AlienArmature|Alien_Swimming
frame: 509-534 AlienArmature|Alien_SwordSlash
frame: 535-560 AlienArmature|Alien_Walk
frame: 0-50 AlienArmature|Alien_Clapping
frame: 51-106 AlienArmature|Alien_Death
frame: 107-207 AlienArmature|Alien_Idle
frame: 208-308 AlienArmature|Alien_IdleHold
frame: 309-334 AlienArmature|Alien_Jump
frame: 335-357 AlienArmature|Alien_Punch
frame: 358-379 AlienArmature|Alien_Run
frame: 380-401 AlienArmature|Alien_RunHold
frame: 402-432 AlienArmature|Alien_RunningJump
frame: 433-459 AlienArmature|Alien_Sitting
frame: 460-475 AlienArmature|Alien_Standing
frame: 476-508 AlienArmature|Alien_Swimming
frame: 509-534 AlienArmature|Alien_SwordSlash
frame: 535-560 AlienArmature|Alien_Walk

View File

@ -1,14 +1,14 @@
frame: 0-50 AlienArmature|Alien_Clapping
frame: 51-106 AlienArmature|Alien_Death
frame: 107-207 AlienArmature|Alien_Idle
frame: 208-308 AlienArmature|Alien_IdleHold
frame: 309-334 AlienArmature|Alien_Jump
frame: 335-357 AlienArmature|Alien_Punch
frame: 358-379 AlienArmature|Alien_Run
frame: 380-401 AlienArmature|Alien_RunHold
frame: 402-432 AlienArmature|Alien_RunningJump
frame: 433-459 AlienArmature|Alien_Sitting
frame: 460-475 AlienArmature|Alien_Standing
frame: 476-508 AlienArmature|Alien_Swimming
frame: 509-534 AlienArmature|Alien_SwordSlash
frame: 535-560 AlienArmature|Alien_Walk
frame: 0-50 AlienArmature|Alien_Clapping
frame: 51-106 AlienArmature|Alien_Death
frame: 107-207 AlienArmature|Alien_Idle
frame: 208-308 AlienArmature|Alien_IdleHold
frame: 309-334 AlienArmature|Alien_Jump
frame: 335-357 AlienArmature|Alien_Punch
frame: 358-379 AlienArmature|Alien_Run
frame: 380-401 AlienArmature|Alien_RunHold
frame: 402-432 AlienArmature|Alien_RunningJump
frame: 433-459 AlienArmature|Alien_Sitting
frame: 460-475 AlienArmature|Alien_Standing
frame: 476-508 AlienArmature|Alien_Swimming
frame: 509-534 AlienArmature|Alien_SwordSlash
frame: 535-560 AlienArmature|Alien_Walk

View File

@ -1 +1 @@
frame: 0-506 Take 001
frame: 0-506 Take 001

View File

@ -1,40 +1,40 @@
frame: 0-100 Idle
frame: 101-125 Dance
frame: 126-169 Death
frame: 170-215 Hello
frame: 216-228 HitRecieve_1
frame: 229-241 HitRecieve_2
frame: 242-268 Jump
frame: 269-310 No
frame: 311-353 Pickup
frame: 354-371 Punch
frame: 372-397 Run
frame: 398-423 Run_Holding
frame: 424-449 Run_Tall
frame: 450-465 Shoot
frame: 466-484 SwordSlash
frame: 485-510 Walk
frame: 511-536 Walk_Holding
frame: 537-562 Walk_Tall
frame: 563-604 Yes
frame: 605-623 Kick
frame: 624-648 RobotArmature|Dance
frame: 649-692 RobotArmature|Death
frame: 693-738 RobotArmature|Hello
frame: 739-751 RobotArmature|HitRecieve_1
frame: 752-764 RobotArmature|HitRecieve_2
frame: 765-865 RobotArmature|Idle
frame: 866-892 RobotArmature|Jump
frame: 893-911 RobotArmature|Kick
frame: 912-953 RobotArmature|No
frame: 954-996 RobotArmature|Pickup
frame: 997-1014 RobotArmature|Punch
frame: 1015-1040 RobotArmature|Run
frame: 1041-1066 RobotArmature|Run_Holding
frame: 1067-1092 RobotArmature|Run_Tall
frame: 1093-1108 RobotArmature|Shoot
frame: 1109-1127 RobotArmature|SwordSlash
frame: 1128-1153 RobotArmature|Walk
frame: 1154-1179 RobotArmature|Walk_Holding
frame: 1180-1205 RobotArmature|Walk_Tall
frame: 1206-1247 RobotArmature|Yes
frame: 0-100 Idle
frame: 101-125 Dance
frame: 126-169 Death
frame: 170-215 Hello
frame: 216-228 HitRecieve_1
frame: 229-241 HitRecieve_2
frame: 242-268 Jump
frame: 269-310 No
frame: 311-353 Pickup
frame: 354-371 Punch
frame: 372-397 Run
frame: 398-423 Run_Holding
frame: 424-449 Run_Tall
frame: 450-465 Shoot
frame: 466-484 SwordSlash
frame: 485-510 Walk
frame: 511-536 Walk_Holding
frame: 537-562 Walk_Tall
frame: 563-604 Yes
frame: 605-623 Kick
frame: 624-648 RobotArmature|Dance
frame: 649-692 RobotArmature|Death
frame: 693-738 RobotArmature|Hello
frame: 739-751 RobotArmature|HitRecieve_1
frame: 752-764 RobotArmature|HitRecieve_2
frame: 765-865 RobotArmature|Idle
frame: 866-892 RobotArmature|Jump
frame: 893-911 RobotArmature|Kick
frame: 912-953 RobotArmature|No
frame: 954-996 RobotArmature|Pickup
frame: 997-1014 RobotArmature|Punch
frame: 1015-1040 RobotArmature|Run
frame: 1041-1066 RobotArmature|Run_Holding
frame: 1067-1092 RobotArmature|Run_Tall
frame: 1093-1108 RobotArmature|Shoot
frame: 1109-1127 RobotArmature|SwordSlash
frame: 1128-1153 RobotArmature|Walk
frame: 1154-1179 RobotArmature|Walk_Holding
frame: 1180-1205 RobotArmature|Walk_Tall
frame: 1206-1247 RobotArmature|Yes

View File

@ -1,36 +1,36 @@
frame: 0-24 Dance
frame: 25-68 Death
frame: 69-114 Hello
frame: 115-127 HitRecieve_1
frame: 128-140 HitRecieve_2
frame: 141-241 Idle
frame: 242-268 Jump
frame: 269-287 Kick
frame: 288-329 No
frame: 330-372 Pickup
frame: 373-390 Punch
frame: 391-416 Run
frame: 417-442 Run_Tall
frame: 443-458 Shoot
frame: 459-477 SwordSlash
frame: 478-503 Walk
frame: 504-529 Walk_Tall
frame: 530-572 Yes
frame: 573-597 RobotArmature|Dance
frame: 598-641 RobotArmature|Death
frame: 642-687 RobotArmature|Hello
frame: 688-700 RobotArmature|HitRecieve_1
frame: 701-713 RobotArmature|HitRecieve_2
frame: 714-814 RobotArmature|Idle
frame: 815-841 RobotArmature|Jump
frame: 842-860 RobotArmature|Kick
frame: 861-902 RobotArmature|No
frame: 903-945 RobotArmature|Pickup
frame: 946-963 RobotArmature|Punch
frame: 964-989 RobotArmature|Run
frame: 990-1015 RobotArmature|Run_Tall
frame: 1016-1031 RobotArmature|Shoot
frame: 1032-1050 RobotArmature|SwordSlash
frame: 1051-1076 RobotArmature|Walk
frame: 1077-1102 RobotArmature|Walk_Tall
frame: 1103-1145 RobotArmature|Yes
frame: 0-24 Dance
frame: 25-68 Death
frame: 69-114 Hello
frame: 115-127 HitRecieve_1
frame: 128-140 HitRecieve_2
frame: 141-241 Idle
frame: 242-268 Jump
frame: 269-287 Kick
frame: 288-329 No
frame: 330-372 Pickup
frame: 373-390 Punch
frame: 391-416 Run
frame: 417-442 Run_Tall
frame: 443-458 Shoot
frame: 459-477 SwordSlash
frame: 478-503 Walk
frame: 504-529 Walk_Tall
frame: 530-572 Yes
frame: 573-597 RobotArmature|Dance
frame: 598-641 RobotArmature|Death
frame: 642-687 RobotArmature|Hello
frame: 688-700 RobotArmature|HitRecieve_1
frame: 701-713 RobotArmature|HitRecieve_2
frame: 714-814 RobotArmature|Idle
frame: 815-841 RobotArmature|Jump
frame: 842-860 RobotArmature|Kick
frame: 861-902 RobotArmature|No
frame: 903-945 RobotArmature|Pickup
frame: 946-963 RobotArmature|Punch
frame: 964-989 RobotArmature|Run
frame: 990-1015 RobotArmature|Run_Tall
frame: 1016-1031 RobotArmature|Shoot
frame: 1032-1050 RobotArmature|SwordSlash
frame: 1051-1076 RobotArmature|Walk
frame: 1077-1102 RobotArmature|Walk_Tall
frame: 1103-1145 RobotArmature|Yes

View File

@ -1,36 +1,36 @@
frame: 0-100 Idle
frame: 101-125 Dance
frame: 126-169 Death
frame: 170-215 Hello
frame: 216-228 HitRecieve_1
frame: 229-241 HitRecieve_2
frame: 242-268 Jump
frame: 269-311 No
frame: 312-354 Pickup
frame: 355-372 Punch
frame: 373-398 Run
frame: 399-424 Run_Holding
frame: 425-440 Shoot
frame: 441-459 SwordSlash
frame: 460-485 Walk
frame: 486-528 Yes
frame: 529-547 Kick
frame: 548-573 Walk_Holding
frame: 574-598 RobotArmature|Dance
frame: 599-642 RobotArmature|Death
frame: 643-688 RobotArmature|Hello
frame: 689-701 RobotArmature|HitRecieve_1
frame: 702-714 RobotArmature|HitRecieve_2
frame: 715-815 RobotArmature|Idle
frame: 816-842 RobotArmature|Jump
frame: 843-861 RobotArmature|Kick
frame: 862-904 RobotArmature|No
frame: 905-947 RobotArmature|Pickup
frame: 948-965 RobotArmature|Punch
frame: 966-991 RobotArmature|Run
frame: 992-1017 RobotArmature|Run_Holding
frame: 1018-1033 RobotArmature|Shoot
frame: 1034-1052 RobotArmature|SwordSlash
frame: 1053-1078 RobotArmature|Walk
frame: 1079-1104 RobotArmature|Walk_Holding
frame: 1105-1147 RobotArmature|Yes
frame: 0-100 Idle
frame: 101-125 Dance
frame: 126-169 Death
frame: 170-215 Hello
frame: 216-228 HitRecieve_1
frame: 229-241 HitRecieve_2
frame: 242-268 Jump
frame: 269-311 No
frame: 312-354 Pickup
frame: 355-372 Punch
frame: 373-398 Run
frame: 399-424 Run_Holding
frame: 425-440 Shoot
frame: 441-459 SwordSlash
frame: 460-485 Walk
frame: 486-528 Yes
frame: 529-547 Kick
frame: 548-573 Walk_Holding
frame: 574-598 RobotArmature|Dance
frame: 599-642 RobotArmature|Death
frame: 643-688 RobotArmature|Hello
frame: 689-701 RobotArmature|HitRecieve_1
frame: 702-714 RobotArmature|HitRecieve_2
frame: 715-815 RobotArmature|Idle
frame: 816-842 RobotArmature|Jump
frame: 843-861 RobotArmature|Kick
frame: 862-904 RobotArmature|No
frame: 905-947 RobotArmature|Pickup
frame: 948-965 RobotArmature|Punch
frame: 966-991 RobotArmature|Run
frame: 992-1017 RobotArmature|Run_Holding
frame: 1018-1033 RobotArmature|Shoot
frame: 1034-1052 RobotArmature|SwordSlash
frame: 1053-1078 RobotArmature|Walk
frame: 1079-1104 RobotArmature|Walk_Holding
frame: 1105-1147 RobotArmature|Yes

View File

@ -1,36 +1,36 @@
frame: 0-100 Idle
frame: 101-125 Dance
frame: 126-169 Death
frame: 170-215 Hello
frame: 216-228 HitRecieve_1
frame: 229-241 HitRecieve_2
frame: 242-268 Jump
frame: 269-311 No
frame: 312-354 Pickup
frame: 355-372 Punch
frame: 373-398 Run
frame: 399-424 Run_Holding
frame: 425-440 Shoot
frame: 441-459 SwordSlash
frame: 460-485 Walk
frame: 486-528 Yes
frame: 529-547 Kick
frame: 548-573 Walk_Holding
frame: 574-598 RobotArmature|Dance
frame: 599-642 RobotArmature|Death
frame: 643-688 RobotArmature|Hello
frame: 689-701 RobotArmature|HitRecieve_1
frame: 702-714 RobotArmature|HitRecieve_2
frame: 715-815 RobotArmature|Idle
frame: 816-842 RobotArmature|Jump
frame: 843-861 RobotArmature|Kick
frame: 862-904 RobotArmature|No
frame: 905-947 RobotArmature|Pickup
frame: 948-965 RobotArmature|Punch
frame: 966-991 RobotArmature|Run
frame: 992-1017 RobotArmature|Run_Holding
frame: 1018-1033 RobotArmature|Shoot
frame: 1034-1052 RobotArmature|SwordSlash
frame: 1053-1078 RobotArmature|Walk
frame: 1079-1104 RobotArmature|Walk_Holding
frame: 1105-1147 RobotArmature|Yes
frame: 0-100 Idle
frame: 101-125 Dance
frame: 126-169 Death
frame: 170-215 Hello
frame: 216-228 HitRecieve_1
frame: 229-241 HitRecieve_2
frame: 242-268 Jump
frame: 269-311 No
frame: 312-354 Pickup
frame: 355-372 Punch
frame: 373-398 Run
frame: 399-424 Run_Holding
frame: 425-440 Shoot
frame: 441-459 SwordSlash
frame: 460-485 Walk
frame: 486-528 Yes
frame: 529-547 Kick
frame: 548-573 Walk_Holding
frame: 574-598 RobotArmature|Dance
frame: 599-642 RobotArmature|Death
frame: 643-688 RobotArmature|Hello
frame: 689-701 RobotArmature|HitRecieve_1
frame: 702-714 RobotArmature|HitRecieve_2
frame: 715-815 RobotArmature|Idle
frame: 816-842 RobotArmature|Jump
frame: 843-861 RobotArmature|Kick
frame: 862-904 RobotArmature|No
frame: 905-947 RobotArmature|Pickup
frame: 948-965 RobotArmature|Punch
frame: 966-991 RobotArmature|Run
frame: 992-1017 RobotArmature|Run_Holding
frame: 1018-1033 RobotArmature|Shoot
frame: 1034-1052 RobotArmature|SwordSlash
frame: 1053-1078 RobotArmature|Walk
frame: 1079-1104 RobotArmature|Walk_Holding
frame: 1105-1147 RobotArmature|Yes

View File

@ -1 +1 @@
frame: 0-0 Idle
frame: 0-0 Idle

View File

@ -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;">&#x26F6;</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>

View File

@ -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

View File

@ -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",
}

View File

@ -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

View File

@ -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

View File

@ -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" }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,
})

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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" }
}

View File

@ -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

View File

@ -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",
}

View File

@ -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",
}

View File

@ -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,
})

View File

@ -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

View File

@ -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
})

View File

@ -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

View File

@ -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",
}

View File

@ -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"}

View File

@ -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"
}

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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,
})

View File

@ -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,
})

View File

@ -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" }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,11 +1,11 @@
uniform samplerCube u_cubemap;
in vec3 v_direction;
out vec4 fragcolor;
void main() {
fragcolor = vec4(texture(u_cubemap, v_direction).rgb, 1.0);
fragcolor.rgb = pow(fragcolor.rgb, vec3(1.0/2.2));
uniform samplerCube u_cubemap;
in vec3 v_direction;
out vec4 fragcolor;
void main() {
fragcolor = vec4(texture(u_cubemap, v_direction).rgb, 1.0);
fragcolor.rgb = pow(fragcolor.rgb, vec3(1.0/2.2));
}

View File

@ -1,154 +1,154 @@
uniform vec3 uSunPos;
uniform vec3 uRayOrigin;
uniform float uSunIntensity;
uniform float uPlanetRadius;
uniform float uAtmosphereRadius;
uniform vec3 uRayleighScattering;
uniform float uMieScattering;
uniform float uRayleighScaleHeight;
uniform float uMieScaleHeight;
uniform float uMiePreferredDirection;
in vec3 v_direction;
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);
void main() {
vec3 color = atmosphere(
normalize(v_direction), // normalized ray direction
uRayOrigin, // ray origin
uSunPos, // position of the sun
uSunIntensity, // intensity of the sun
uPlanetRadius, // radius of the planet in meters
uAtmosphereRadius, // radius of the atmosphere in meters
uRayleighScattering, // Rayleigh scattering coefficient
uMieScattering, // Mie scattering coefficient
uRayleighScaleHeight, // Rayleigh scale height
uMieScaleHeight, // Mie scale height
uMiePreferredDirection // Mie preferred scattering direction
);
// Apply exposure.
color = 1.0 - exp(-1.0 * color);
fragcolor = vec4(color, 1);
}
// [src] https://github.com/wwwtyro/glsl-atmosphere by wwwtyro (Unlicensed)
// For more information, please refer to <http://unlicense.org>
#define PI 3.141592
#define iSteps 16
#define jSteps 8
vec2 rsi(vec3 r0, vec3 rd, float sr) {
// ray-sphere intersection that assumes
// the sphere is centered at the origin.
// No intersection when result.x > result.y
float a = dot(rd, rd);
float b = 2.0 * dot(rd, r0);
float c = dot(r0, r0) - (sr * sr);
float d = (b*b) - 4.0*a*c;
if (d < 0.0) return vec2(1e5,-1e5);
return vec2(
(-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) {
// Normalize the sun and view directions.
pSun = normalize(pSun);
r = normalize(r);
// Calculate the step size of the primary ray.
vec2 p = rsi(r0, r, rAtmos);
if (p.x > p.y) return vec3(0,0,0);
p.y = min(p.y, rsi(r0, r, rPlanet).x);
float iStepSize = (p.y - p.x) / float(iSteps);
// Initialize the primary ray time.
float iTime = 0.0;
// Initialize accumulators for Rayleigh and Mie scattering.
vec3 totalRlh = vec3(0,0,0);
vec3 totalMie = vec3(0,0,0);
// Initialize optical depth accumulators for the primary ray.
float iOdRlh = 0.0;
float iOdMie = 0.0;
// Calculate the Rayleigh and Mie phases.
float mu = dot(r, pSun);
float mumu = mu * mu;
float gg = g * g;
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));
// Sample the primary ray.
for (int i = 0; i < iSteps; i++) {
// Calculate the primary ray sample position.
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
// Calculate the height of the sample.
float iHeight = length(iPos) - rPlanet;
// Calculate the optical depth of the Rayleigh and Mie scattering for this step.
float odStepRlh = exp(-iHeight / shRlh) * iStepSize;
float odStepMie = exp(-iHeight / shMie) * iStepSize;
// Accumulate optical depth.
iOdRlh += odStepRlh;
iOdMie += odStepMie;
// Calculate the step size of the secondary ray.
float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps);
// Initialize the secondary ray time.
float jTime = 0.0;
// Initialize optical depth accumulators for the secondary ray.
float jOdRlh = 0.0;
float jOdMie = 0.0;
// Sample the secondary ray.
for (int j = 0; j < jSteps; j++) {
// Calculate the secondary ray sample position.
vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5);
// Calculate the height of the sample.
float jHeight = length(jPos) - rPlanet;
// Accumulate the optical depth.
jOdRlh += exp(-jHeight / shRlh) * jStepSize;
jOdMie += exp(-jHeight / shMie) * jStepSize;
// Increment the secondary ray time.
jTime += jStepSize;
}
// Calculate attenuation.
vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh)));
// Accumulate scattering.
totalRlh += odStepRlh * attn;
totalMie += odStepMie * attn;
// Increment the primary ray time.
iTime += iStepSize;
}
// Calculate and return the final color.
return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie);
uniform vec3 uSunPos;
uniform vec3 uRayOrigin;
uniform float uSunIntensity;
uniform float uPlanetRadius;
uniform float uAtmosphereRadius;
uniform vec3 uRayleighScattering;
uniform float uMieScattering;
uniform float uRayleighScaleHeight;
uniform float uMieScaleHeight;
uniform float uMiePreferredDirection;
in vec3 v_direction;
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);
void main() {
vec3 color = atmosphere(
normalize(v_direction), // normalized ray direction
uRayOrigin, // ray origin
uSunPos, // position of the sun
uSunIntensity, // intensity of the sun
uPlanetRadius, // radius of the planet in meters
uAtmosphereRadius, // radius of the atmosphere in meters
uRayleighScattering, // Rayleigh scattering coefficient
uMieScattering, // Mie scattering coefficient
uRayleighScaleHeight, // Rayleigh scale height
uMieScaleHeight, // Mie scale height
uMiePreferredDirection // Mie preferred scattering direction
);
// Apply exposure.
color = 1.0 - exp(-1.0 * color);
fragcolor = vec4(color, 1);
}
// [src] https://github.com/wwwtyro/glsl-atmosphere by wwwtyro (Unlicensed)
// For more information, please refer to <http://unlicense.org>
#define PI 3.141592
#define iSteps 16
#define jSteps 8
vec2 rsi(vec3 r0, vec3 rd, float sr) {
// ray-sphere intersection that assumes
// the sphere is centered at the origin.
// No intersection when result.x > result.y
float a = dot(rd, rd);
float b = 2.0 * dot(rd, r0);
float c = dot(r0, r0) - (sr * sr);
float d = (b*b) - 4.0*a*c;
if (d < 0.0) return vec2(1e5,-1e5);
return vec2(
(-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) {
// Normalize the sun and view directions.
pSun = normalize(pSun);
r = normalize(r);
// Calculate the step size of the primary ray.
vec2 p = rsi(r0, r, rAtmos);
if (p.x > p.y) return vec3(0,0,0);
p.y = min(p.y, rsi(r0, r, rPlanet).x);
float iStepSize = (p.y - p.x) / float(iSteps);
// Initialize the primary ray time.
float iTime = 0.0;
// Initialize accumulators for Rayleigh and Mie scattering.
vec3 totalRlh = vec3(0,0,0);
vec3 totalMie = vec3(0,0,0);
// Initialize optical depth accumulators for the primary ray.
float iOdRlh = 0.0;
float iOdMie = 0.0;
// Calculate the Rayleigh and Mie phases.
float mu = dot(r, pSun);
float mumu = mu * mu;
float gg = g * g;
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));
// Sample the primary ray.
for (int i = 0; i < iSteps; i++) {
// Calculate the primary ray sample position.
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
// Calculate the height of the sample.
float iHeight = length(iPos) - rPlanet;
// Calculate the optical depth of the Rayleigh and Mie scattering for this step.
float odStepRlh = exp(-iHeight / shRlh) * iStepSize;
float odStepMie = exp(-iHeight / shMie) * iStepSize;
// Accumulate optical depth.
iOdRlh += odStepRlh;
iOdMie += odStepMie;
// Calculate the step size of the secondary ray.
float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps);
// Initialize the secondary ray time.
float jTime = 0.0;
// Initialize optical depth accumulators for the secondary ray.
float jOdRlh = 0.0;
float jOdMie = 0.0;
// Sample the secondary ray.
for (int j = 0; j < jSteps; j++) {
// Calculate the secondary ray sample position.
vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5);
// Calculate the height of the sample.
float jHeight = length(jPos) - rPlanet;
// Accumulate the optical depth.
jOdRlh += exp(-jHeight / shRlh) * jStepSize;
jOdMie += exp(-jHeight / shMie) * jStepSize;
// Increment the secondary ray time.
jTime += jStepSize;
}
// Calculate attenuation.
vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh)));
// Accumulate scattering.
totalRlh += odStepRlh * attn;
totalMie += odStepMie * attn;
// Increment the primary ray time.
iTime += iStepSize;
}
// Calculate and return the final color.
return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie);
}

View File

@ -1,485 +1,485 @@
#version 400
// 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
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_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_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_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_SPECULAR 1.50f
#define BOOST_NOISE 2.50f
struct Light
{
vec3 direction;
vec3 color;
};
struct ColorMap
{
bool has_tex;
vec4 color;
};
uniform ColorMap map_albedo; uniform sampler2D map_albedo_tex;
uniform ColorMap map_diffuse; uniform sampler2D map_diffuse_tex;
uniform ColorMap map_specular; uniform sampler2D map_specular_tex; // not used
uniform ColorMap map_normals; uniform sampler2D map_normals_tex;
uniform ColorMap map_roughness; uniform sampler2D map_roughness_tex;
uniform ColorMap map_metallic; uniform sampler2D map_metallic_tex;
uniform ColorMap map_ao; uniform sampler2D map_ao_tex;
uniform ColorMap map_ambient; uniform sampler2D map_ambient_tex;
uniform ColorMap map_emissive; uniform sampler2D map_emissive_tex;
#define sample_colormap(ColorMap_, uv_) \
(ColorMap_.has_tex ? texture( ColorMap_##_tex, uv_ ) : ColorMap_.color)
in vec3 out_normal;
in vec3 out_tangent;
in vec3 out_binormal;
in vec2 out_texcoord;
in vec3 out_worldpos;
in vec3 out_to_camera;
uniform float skysphere_rotation;
uniform float skysphere_mip_count;
uniform float exposure;
uniform uint frame_count;
uniform float specular_shininess;
uniform vec3 camera_position;
uniform Light lights[3];
uniform sampler2D tex_skysphere;
uniform sampler2D tex_skyenv;
uniform sampler2D tex_brdf_lut;
uniform bool has_tex_skysphere;
uniform bool has_tex_skyenv;
out vec4 frag_color;
const float PI = 3.1415926536;
// MurMurHash 3 finalizer. Implementation is in public domain.
uint hash( uint h )
{
h ^= h >> 16;
h *= 0x85ebca6bU;
h ^= h >> 13;
h *= 0xc2b2ae35U;
h ^= h >> 16;
return h;
}
// 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.
float random( uvec3 h )
{
uint m = hash(h.x ^ hash( h.y ) ^ hash( h.z ));
return uintBitsToFloat( ( m & 0x007FFFFFu ) | 0x3f800000u ) - 1.;
}
float random( vec3 v )
{
return random(floatBitsToUint( v ));
}
vec3 fresnel_schlick( vec3 H, vec3 V, vec3 F0 )
{
float cosTheta = clamp( dot( H, V ), 0., 1. );
return F0 + ( vec3( 1.0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
}
// A Fresnel term that dampens rough specular reflections.
// https://seblagarde.wordpress.com/2011/08/17/hello-world/
vec3 fresnel_schlick_roughness( vec3 H, vec3 V, vec3 F0, float roughness )
{
float cosTheta = clamp( dot( H, V ), 0., 1. );
return F0 + ( max( vec3( 1.0 - roughness ), F0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
}
float distribution_ggx( vec3 N, vec3 H, float roughness )
{
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max( 0., dot( N, H ) );
float factor = NdotH * NdotH * ( a2 - 1. ) + 1.;
return a2 / ( PI * factor * factor );
}
float geometry_schlick_ggx( vec3 N, vec3 V, float k )
{
float NdotV = max( 0., dot( N, V ) );
return NdotV / (NdotV * ( 1. - k ) + k );
}
float geometry_smith( vec3 N, vec3 V, vec3 L, float roughness )
{
#if 1 // original
float r = roughness + 1.;
float k = (r * r) / 8.;
#elif 0 // vries
float a = roughness;
float k = (a * a) / 2.0;
#elif 0 // vries improved?
float a = roughness * roughness;
float k = a / 2.0;
#endif
return geometry_schlick_ggx( N, V, k ) * geometry_schlick_ggx( N, L, k );
}
vec2 sphere_to_polar( vec3 normal )
{
normal = normalize( normal );
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.
// 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.
vec2 sphere_to_polar_clamp_y( vec3 normal, float texture_height )
{
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) );
}
vec3 sample_sky( vec3 normal )
{
vec2 polar = sphere_to_polar( normal );
return texture( tex_skysphere, polar ).rgb * exposure;
}
// Takes samples around the hemisphere, converts them to radiances via weighting and
// returns a normalized sum.
vec3 sample_irradiance_slow( vec3 normal, vec3 vertex_tangent )
{
float delta = 0.10;
vec3 up = abs( normal.y ) < 0.999 ? vec3( 0., 1., 0. ) : vec3( 0., 0., 1. );
vec3 tangent_x = normalize( cross( up, normal ) );
vec3 tangent_y = cross( normal, tangent_x );
int numIrradianceSamples = 0;
vec3 irradiance = vec3(0.);
for ( float phi = 0.; phi < 2. * PI ; phi += delta )
{
for ( float theta = 0.; theta < 0.5 * PI; theta += delta )
{
vec3 tangent_space = vec3(
sin( theta ) * cos( phi ),
sin( theta ) * sin( phi ),
cos( theta ) );
vec3 world_space = tangent_space.x * tangent_x + tangent_space.y + tangent_y + tangent_space.z * normal;
vec3 color = sample_sky( world_space );
irradiance += color * cos( theta ) * sin( theta );
numIrradianceSamples++;
}
}
irradiance = PI * irradiance / float( numIrradianceSamples );
return irradiance;
}
vec3 sample_irradiance_fast( vec3 normal, vec3 vertex_tangent )
{
// Sample the irradiance map if it exists, otherwise fall back to blurred reflection map.
if ( has_tex_skyenv )
{
vec2 polar = sphere_to_polar_clamp_y( normal, 180.0 );
return textureLod( tex_skyenv, polar, 0.0 ).rgb * exposure;
}
else
{
vec2 polar = sphere_to_polar( normal );
return textureLod( tex_skysphere, polar, 0.80 * skysphere_mip_count ).rgb * exposure;
}
}
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
// vector R according to the BRDF lobe.
//
// 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")
// 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.
//
// Here we also simplify approximation #2 by using bilinear mipmaps with a magic formula instead
// of properly convolving it with a GGX lobe.
//
// For details, see Brian Karis, "Real Shading in Unreal Engine 4", 2013.
vec3 R = 2. * dot( V, N ) * N - V;
vec2 polar = sphere_to_polar( R );
// Map roughness from range [0, 1] into a mip LOD [0, skysphere_mip_count].
// The magic numbers were chosen empirically.
float mip = 0.9 * skysphere_mip_count * pow(roughness, 0.25 * BOOST_SPECULAR);
vec3 prefiltered = textureLod( tex_skysphere, polar, mip ).rgb * exposure;
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
// to avoid black pixels.
if (USE_WRAPAROUND_SPECULAR)
{
NdotV = NdotV * 0.9 + 0.1;
}
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).
// 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
vec3 specular = prefiltered * (fresnel * envBRDF.x + vec3(envBRDF.y));
return specular;
}
void main(void)
{
vec3 baseColor = vec3( 0.5, 0.5, 0.5 );
float roughness = 1.0;
float metallic = 0.0;
float ao = 1.0;
float alpha = 1.0;
vec4 baseColor_alpha;
if ( map_albedo.has_tex )
baseColor_alpha = sample_colormap( map_albedo, out_texcoord );
else
baseColor_alpha = sample_colormap( map_diffuse, out_texcoord );
baseColor = baseColor_alpha.xyz;
alpha = baseColor_alpha.w;
if( map_metallic.has_tex && map_roughness.has_tex ) {
metallic = sample_colormap( map_metallic, out_texcoord ).x;
roughness = sample_colormap( map_roughness, out_texcoord ).x;
}
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)
metallic = sample_colormap( map_roughness, out_texcoord ).b + sample_colormap( map_roughness, out_texcoord ).r;
roughness = sample_colormap( map_roughness, out_texcoord ).g;
}
if ( map_ao.has_tex )
ao = sample_colormap( map_ao, out_texcoord ).x;
else if ( map_ambient.has_tex )
ao = sample_colormap( map_ambient, out_texcoord ).x;
vec3 emissive = sample_colormap( map_emissive, out_texcoord ).rgb;
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_length = length(normalmap);
normalmap /= normalmap_length;
vec3 normal = out_normal;
if ( map_normals.has_tex )
{
// Mikkelsen's tangent space normal map decoding. See http://mikktspace.com/ for rationale.
vec3 bi = cross( out_normal, out_tangent );
vec3 nmap = normalmap.xyz;
normal = nmap.x * out_tangent + nmap.y * bi + nmap.z * out_normal;
}
normal = normalize( normal );
if( USE_MAP_DEBUGGING && !USE_AMBIENT_DEBUGGING )
{
vec3 c = vec3(1., 0., 0.);
float x = gl_FragCoord.x / resolution.x;
float y = gl_FragCoord.y / resolution.y;
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 < (5.0/7.0) ) c = vec3(ao);
if ( y < (4.0/7.0) ) c = vec3(emissive);
if ( y < (3.0/7.0) ) c = vec3(metallic);
if ( y < (2.0/7.0) ) c = vec3(roughness);
if ( y < (1.0/7.0) ) c = baseColor;
frag_color = vec4(c, 1.);
return;
}
if (USE_NORMAL_VARIATION_TO_ROUGHNESS)
{
// Try to reduce specular aliasing by increasing roughness when minified normal maps have high variation.
float variation = 1. - pow( normalmap_length, 8. );
float minification = clamp( normalmap_mip - 2., 0., 1. );
roughness = mix( roughness, 1.0, variation * minification );
}
vec3 N = normal;
vec3 V = normalize( out_to_camera );
vec3 Lo = vec3(0.);
vec3 F0 = vec3(0.04);
F0 = mix( F0, baseColor, metallic );
bool use_ibl = has_tex_skysphere;
// Add contributions from analytic lights only if we don't have a skybox.
{
int num_lights = use_ibl ? 1 : lights.length();
for ( int i = 0; i < num_lights; i++ )
{
vec3 radiance = lights[ i ].color * BOOST_LIGHTING;
vec3 L = -normalize( lights[ i ].direction );
vec3 H = normalize( V + L );
vec3 F = fresnel_schlick( H, V, F0 );
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
// Premultiplied alpha applied to the diffuse component only
kD *= alpha;
float D = distribution_ggx( N, H, roughness );
float G = geometry_smith( N, V, L, roughness );
vec3 num = D * F * G;
float denom = 4. * max( 0., dot( N, V ) ) * max( 0., dot( N, L ) );
vec3 specular = kS * (num / max( 0.001, denom ));
float NdotL = max( 0., dot( N, L ) );
Lo += ( kD * ( baseColor / PI ) + specular ) * radiance * NdotL;
}
}
vec3 ambient = sample_colormap( map_ambient, out_texcoord ).xyz;
vec3 diffuse_ambient;
vec3 specular_ambient;
if ( use_ibl )
{
// Image based lighting.
// Based on https://learnopengl.com/PBR/IBL/Diffuse-irradiance
vec3 irradiance = vec3(0.);
if ( USE_BRUTEFORCE_IRRADIANCE )
{
irradiance = sample_irradiance_slow( normal, out_tangent );
}
else
{
irradiance = sample_irradiance_fast( normal, out_tangent );
}
// Compute the Fresnel term for a perfect mirror reflection with L = R.
// In this case the halfway vector H = N.
//
// We use a modified Fresnel function that dampens specular reflections of very
// rough surfaces to avoid too bright pixels at grazing angles.
vec3 F = fresnel_schlick_roughness( N, V, F0, roughness );
vec3 kS = F;
// Subtract the amount of reflected light (specular) to get the energy left for
// absorbed (diffuse) light.
vec3 kD = vec3(1.) - kS;
// Metallic surfaces have only a specular reflection.
kD *= 1.0 - metallic;
// Premultiplied alpha applied to the diffuse component only
kD *= alpha;
// Modulate the incoming lighting with the diffuse color: some wavelengths get absorbed.
diffuse_ambient = irradiance * baseColor;
// Ambient light also has a specular part.
specular_ambient = specular_ibl( V, normal, roughness, F );
// Ambient occlusion tells us the fraction of sky light that reaches this point.
if (USE_SPECULAR_AO_ATTENUATION)
{
ambient = ao * (kD * diffuse_ambient + specular_ambient);
}
else
{
// We don't attenuate specular_ambient ambient here with AO which might cause flickering in dark cavities.
ambient = ao * (kD * diffuse_ambient) + specular_ambient;
}
}
vec3 color = (ambient + Lo) + emissive;
if ( USE_AMBIENT_DEBUGGING )
{
float y = gl_FragCoord.y / resolution.y;
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) < (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) < (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) < (2.0/7.0/2.0) ) color = vec3(roughness);
if ( (y-0.5) < (1.0/7.0/2.0) ) color = baseColor;
} else {
float x = gl_FragCoord.x / resolution.x;
if ( x < 0.33 )
color = specular_ambient;
else if( x > 0.66 )
color = diffuse_ambient;
}
}
#if 0 // original
// basic tonemap and gamma correction
color = color / ( vec3(1.) + color );
color = pow( color, vec3(1. / 2.2) );
#elif 0
// filmic tonemapper
vec3 linearColor = color;
vec3 x = max(vec3(0.0), linearColor - 0.004);
color = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06);
// gamma correction
// color = pow( color, vec3(1. / 2.2) );
#elif 1
// aces film (CC0, src: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/)
vec3 x = color;
float a = 2.51f;
float b = 0.03f;
float c = 2.43f;
float d = 0.59f;
float e = 0.14f;
color = clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0);
// gamma correction
color = pow( color, vec3(1. / 2.2) );
#endif
// dither with noise.
// float dither = random( uvec3( floatBitsToUint( gl_FragCoord.xy ), frame_count ) );
// 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
// see the background, maybe we can approximate it well enough by adding a fresnel term
frag_color = vec4( color, alpha );
}
#version 400
// 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
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_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_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_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_SPECULAR 1.50f
#define BOOST_NOISE 2.50f
struct Light
{
vec3 direction;
vec3 color;
};
struct ColorMap
{
bool has_tex;
vec4 color;
};
uniform ColorMap map_albedo; uniform sampler2D map_albedo_tex;
uniform ColorMap map_diffuse; uniform sampler2D map_diffuse_tex;
uniform ColorMap map_specular; uniform sampler2D map_specular_tex; // not used
uniform ColorMap map_normals; uniform sampler2D map_normals_tex;
uniform ColorMap map_roughness; uniform sampler2D map_roughness_tex;
uniform ColorMap map_metallic; uniform sampler2D map_metallic_tex;
uniform ColorMap map_ao; uniform sampler2D map_ao_tex;
uniform ColorMap map_ambient; uniform sampler2D map_ambient_tex;
uniform ColorMap map_emissive; uniform sampler2D map_emissive_tex;
#define sample_colormap(ColorMap_, uv_) \
(ColorMap_.has_tex ? texture( ColorMap_##_tex, uv_ ) : ColorMap_.color)
in vec3 out_normal;
in vec3 out_tangent;
in vec3 out_binormal;
in vec2 out_texcoord;
in vec3 out_worldpos;
in vec3 out_to_camera;
uniform float skysphere_rotation;
uniform float skysphere_mip_count;
uniform float exposure;
uniform uint frame_count;
uniform float specular_shininess;
uniform vec3 camera_position;
uniform Light lights[3];
uniform sampler2D tex_skysphere;
uniform sampler2D tex_skyenv;
uniform sampler2D tex_brdf_lut;
uniform bool has_tex_skysphere;
uniform bool has_tex_skyenv;
out vec4 frag_color;
const float PI = 3.1415926536;
// MurMurHash 3 finalizer. Implementation is in public domain.
uint hash( uint h )
{
h ^= h >> 16;
h *= 0x85ebca6bU;
h ^= h >> 13;
h *= 0xc2b2ae35U;
h ^= h >> 16;
return h;
}
// 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.
float random( uvec3 h )
{
uint m = hash(h.x ^ hash( h.y ) ^ hash( h.z ));
return uintBitsToFloat( ( m & 0x007FFFFFu ) | 0x3f800000u ) - 1.;
}
float random( vec3 v )
{
return random(floatBitsToUint( v ));
}
vec3 fresnel_schlick( vec3 H, vec3 V, vec3 F0 )
{
float cosTheta = clamp( dot( H, V ), 0., 1. );
return F0 + ( vec3( 1.0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
}
// A Fresnel term that dampens rough specular reflections.
// https://seblagarde.wordpress.com/2011/08/17/hello-world/
vec3 fresnel_schlick_roughness( vec3 H, vec3 V, vec3 F0, float roughness )
{
float cosTheta = clamp( dot( H, V ), 0., 1. );
return F0 + ( max( vec3( 1.0 - roughness ), F0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
}
float distribution_ggx( vec3 N, vec3 H, float roughness )
{
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max( 0., dot( N, H ) );
float factor = NdotH * NdotH * ( a2 - 1. ) + 1.;
return a2 / ( PI * factor * factor );
}
float geometry_schlick_ggx( vec3 N, vec3 V, float k )
{
float NdotV = max( 0., dot( N, V ) );
return NdotV / (NdotV * ( 1. - k ) + k );
}
float geometry_smith( vec3 N, vec3 V, vec3 L, float roughness )
{
#if 1 // original
float r = roughness + 1.;
float k = (r * r) / 8.;
#elif 0 // vries
float a = roughness;
float k = (a * a) / 2.0;
#elif 0 // vries improved?
float a = roughness * roughness;
float k = a / 2.0;
#endif
return geometry_schlick_ggx( N, V, k ) * geometry_schlick_ggx( N, L, k );
}
vec2 sphere_to_polar( vec3 normal )
{
normal = normalize( normal );
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.
// 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.
vec2 sphere_to_polar_clamp_y( vec3 normal, float texture_height )
{
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) );
}
vec3 sample_sky( vec3 normal )
{
vec2 polar = sphere_to_polar( normal );
return texture( tex_skysphere, polar ).rgb * exposure;
}
// Takes samples around the hemisphere, converts them to radiances via weighting and
// returns a normalized sum.
vec3 sample_irradiance_slow( vec3 normal, vec3 vertex_tangent )
{
float delta = 0.10;
vec3 up = abs( normal.y ) < 0.999 ? vec3( 0., 1., 0. ) : vec3( 0., 0., 1. );
vec3 tangent_x = normalize( cross( up, normal ) );
vec3 tangent_y = cross( normal, tangent_x );
int numIrradianceSamples = 0;
vec3 irradiance = vec3(0.);
for ( float phi = 0.; phi < 2. * PI ; phi += delta )
{
for ( float theta = 0.; theta < 0.5 * PI; theta += delta )
{
vec3 tangent_space = vec3(
sin( theta ) * cos( phi ),
sin( theta ) * sin( phi ),
cos( theta ) );
vec3 world_space = tangent_space.x * tangent_x + tangent_space.y + tangent_y + tangent_space.z * normal;
vec3 color = sample_sky( world_space );
irradiance += color * cos( theta ) * sin( theta );
numIrradianceSamples++;
}
}
irradiance = PI * irradiance / float( numIrradianceSamples );
return irradiance;
}
vec3 sample_irradiance_fast( vec3 normal, vec3 vertex_tangent )
{
// Sample the irradiance map if it exists, otherwise fall back to blurred reflection map.
if ( has_tex_skyenv )
{
vec2 polar = sphere_to_polar_clamp_y( normal, 180.0 );
return textureLod( tex_skyenv, polar, 0.0 ).rgb * exposure;
}
else
{
vec2 polar = sphere_to_polar( normal );
return textureLod( tex_skysphere, polar, 0.80 * skysphere_mip_count ).rgb * exposure;
}
}
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
// vector R according to the BRDF lobe.
//
// 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")
// 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.
//
// Here we also simplify approximation #2 by using bilinear mipmaps with a magic formula instead
// of properly convolving it with a GGX lobe.
//
// For details, see Brian Karis, "Real Shading in Unreal Engine 4", 2013.
vec3 R = 2. * dot( V, N ) * N - V;
vec2 polar = sphere_to_polar( R );
// Map roughness from range [0, 1] into a mip LOD [0, skysphere_mip_count].
// The magic numbers were chosen empirically.
float mip = 0.9 * skysphere_mip_count * pow(roughness, 0.25 * BOOST_SPECULAR);
vec3 prefiltered = textureLod( tex_skysphere, polar, mip ).rgb * exposure;
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
// to avoid black pixels.
if (USE_WRAPAROUND_SPECULAR)
{
NdotV = NdotV * 0.9 + 0.1;
}
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).
// 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
vec3 specular = prefiltered * (fresnel * envBRDF.x + vec3(envBRDF.y));
return specular;
}
void main(void)
{
vec3 baseColor = vec3( 0.5, 0.5, 0.5 );
float roughness = 1.0;
float metallic = 0.0;
float ao = 1.0;
float alpha = 1.0;
vec4 baseColor_alpha;
if ( map_albedo.has_tex )
baseColor_alpha = sample_colormap( map_albedo, out_texcoord );
else
baseColor_alpha = sample_colormap( map_diffuse, out_texcoord );
baseColor = baseColor_alpha.xyz;
alpha = baseColor_alpha.w;
if( map_metallic.has_tex && map_roughness.has_tex ) {
metallic = sample_colormap( map_metallic, out_texcoord ).x;
roughness = sample_colormap( map_roughness, out_texcoord ).x;
}
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)
metallic = sample_colormap( map_roughness, out_texcoord ).b + sample_colormap( map_roughness, out_texcoord ).r;
roughness = sample_colormap( map_roughness, out_texcoord ).g;
}
if ( map_ao.has_tex )
ao = sample_colormap( map_ao, out_texcoord ).x;
else if ( map_ambient.has_tex )
ao = sample_colormap( map_ambient, out_texcoord ).x;
vec3 emissive = sample_colormap( map_emissive, out_texcoord ).rgb;
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_length = length(normalmap);
normalmap /= normalmap_length;
vec3 normal = out_normal;
if ( map_normals.has_tex )
{
// Mikkelsen's tangent space normal map decoding. See http://mikktspace.com/ for rationale.
vec3 bi = cross( out_normal, out_tangent );
vec3 nmap = normalmap.xyz;
normal = nmap.x * out_tangent + nmap.y * bi + nmap.z * out_normal;
}
normal = normalize( normal );
if( USE_MAP_DEBUGGING && !USE_AMBIENT_DEBUGGING )
{
vec3 c = vec3(1., 0., 0.);
float x = gl_FragCoord.x / resolution.x;
float y = gl_FragCoord.y / resolution.y;
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 < (5.0/7.0) ) c = vec3(ao);
if ( y < (4.0/7.0) ) c = vec3(emissive);
if ( y < (3.0/7.0) ) c = vec3(metallic);
if ( y < (2.0/7.0) ) c = vec3(roughness);
if ( y < (1.0/7.0) ) c = baseColor;
frag_color = vec4(c, 1.);
return;
}
if (USE_NORMAL_VARIATION_TO_ROUGHNESS)
{
// Try to reduce specular aliasing by increasing roughness when minified normal maps have high variation.
float variation = 1. - pow( normalmap_length, 8. );
float minification = clamp( normalmap_mip - 2., 0., 1. );
roughness = mix( roughness, 1.0, variation * minification );
}
vec3 N = normal;
vec3 V = normalize( out_to_camera );
vec3 Lo = vec3(0.);
vec3 F0 = vec3(0.04);
F0 = mix( F0, baseColor, metallic );
bool use_ibl = has_tex_skysphere;
// Add contributions from analytic lights only if we don't have a skybox.
{
int num_lights = use_ibl ? 1 : lights.length();
for ( int i = 0; i < num_lights; i++ )
{
vec3 radiance = lights[ i ].color * BOOST_LIGHTING;
vec3 L = -normalize( lights[ i ].direction );
vec3 H = normalize( V + L );
vec3 F = fresnel_schlick( H, V, F0 );
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
// Premultiplied alpha applied to the diffuse component only
kD *= alpha;
float D = distribution_ggx( N, H, roughness );
float G = geometry_smith( N, V, L, roughness );
vec3 num = D * F * G;
float denom = 4. * max( 0., dot( N, V ) ) * max( 0., dot( N, L ) );
vec3 specular = kS * (num / max( 0.001, denom ));
float NdotL = max( 0., dot( N, L ) );
Lo += ( kD * ( baseColor / PI ) + specular ) * radiance * NdotL;
}
}
vec3 ambient = sample_colormap( map_ambient, out_texcoord ).xyz;
vec3 diffuse_ambient;
vec3 specular_ambient;
if ( use_ibl )
{
// Image based lighting.
// Based on https://learnopengl.com/PBR/IBL/Diffuse-irradiance
vec3 irradiance = vec3(0.);
if ( USE_BRUTEFORCE_IRRADIANCE )
{
irradiance = sample_irradiance_slow( normal, out_tangent );
}
else
{
irradiance = sample_irradiance_fast( normal, out_tangent );
}
// Compute the Fresnel term for a perfect mirror reflection with L = R.
// In this case the halfway vector H = N.
//
// We use a modified Fresnel function that dampens specular reflections of very
// rough surfaces to avoid too bright pixels at grazing angles.
vec3 F = fresnel_schlick_roughness( N, V, F0, roughness );
vec3 kS = F;
// Subtract the amount of reflected light (specular) to get the energy left for
// absorbed (diffuse) light.
vec3 kD = vec3(1.) - kS;
// Metallic surfaces have only a specular reflection.
kD *= 1.0 - metallic;
// Premultiplied alpha applied to the diffuse component only
kD *= alpha;
// Modulate the incoming lighting with the diffuse color: some wavelengths get absorbed.
diffuse_ambient = irradiance * baseColor;
// Ambient light also has a specular part.
specular_ambient = specular_ibl( V, normal, roughness, F );
// Ambient occlusion tells us the fraction of sky light that reaches this point.
if (USE_SPECULAR_AO_ATTENUATION)
{
ambient = ao * (kD * diffuse_ambient + specular_ambient);
}
else
{
// We don't attenuate specular_ambient ambient here with AO which might cause flickering in dark cavities.
ambient = ao * (kD * diffuse_ambient) + specular_ambient;
}
}
vec3 color = (ambient + Lo) + emissive;
if ( USE_AMBIENT_DEBUGGING )
{
float y = gl_FragCoord.y / resolution.y;
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) < (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) < (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) < (2.0/7.0/2.0) ) color = vec3(roughness);
if ( (y-0.5) < (1.0/7.0/2.0) ) color = baseColor;
} else {
float x = gl_FragCoord.x / resolution.x;
if ( x < 0.33 )
color = specular_ambient;
else if( x > 0.66 )
color = diffuse_ambient;
}
}
#if 0 // original
// basic tonemap and gamma correction
color = color / ( vec3(1.) + color );
color = pow( color, vec3(1. / 2.2) );
#elif 0
// filmic tonemapper
vec3 linearColor = color;
vec3 x = max(vec3(0.0), linearColor - 0.004);
color = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06);
// gamma correction
// color = pow( color, vec3(1. / 2.2) );
#elif 1
// aces film (CC0, src: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/)
vec3 x = color;
float a = 2.51f;
float b = 0.03f;
float c = 2.43f;
float d = 0.59f;
float e = 0.14f;
color = clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0);
// gamma correction
color = pow( color, vec3(1. / 2.2) );
#endif
// dither with noise.
// float dither = random( uvec3( floatBitsToUint( gl_FragCoord.xy ), frame_count ) );
// 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
// see the background, maybe we can approximate it well enough by adding a fresnel term
frag_color = vec4( color, alpha );
}

View File

@ -1,180 +1,180 @@
#ifndef MAX_BONES
#define MAX_BONES 110
#endif
uniform mat3x4 vsBoneMatrix[MAX_BONES];
uniform bool SKINNED = false;
uniform mat4 M; // RIM
uniform mat4 VP;
uniform mat4 P;
uniform int u_billboard;
#if 0
// Fetch blend channels from all attached blend deformers.
for (size_t di = 0; di < mesh->blend_deformers.count; di++) {
ufbx_blend_deformer *deformer = mesh->blend_deformers.data[di];
for (size_t ci = 0; ci < deformer->channels.count; ci++) {
ufbx_blend_channel *chan = deformer->channels.data[ci];
if (chan->keyframes.count == 0) continue;
if (num_blend_shapes < MAX_BLEND_SHAPES) {
blend_channels[num_blend_shapes] = chan;
vmesh->blend_channel_indices[num_blend_shapes] = (int32_t)chan->typed_id;
num_blend_shapes++;
}
}
}
if (num_blend_shapes > 0) {
vmesh->blend_shape_image = pack_blend_channels_to_image(mesh, blend_channels, num_blend_shapes);
vmesh->num_blend_shapes = num_blend_shapes;
}
ubo.f_num_blend_shapes = (float)mesh->num_blend_shapes;
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;
}
sg_image blend_shapes = mesh->num_blend_shapes > 0 ? mesh->blend_shape_image : view->empty_blend_shape_image;
#endif
// for blendshapes
#ifndef MAX_BLENDSHAPES
#define MAX_BLENDSHAPES 16
#endif
uniform vec4 blend_weights[MAX_BLENDSHAPES]; // @todo: implement me
uniform float f_num_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 vec2 att_texcoord;
in vec3 att_normal;
in vec4 att_tangent; // vec3 + bi sign
in mat4 att_instanced_matrix; // for instanced rendering
in vec4 att_indexes; // @fixme: gles might use ivec4 instead?
in vec4 att_weights; // @todo: downgrade from float to byte
in float att_vertexindex; // for blendshapes
in vec4 att_color;
in vec3 att_bitangent; // @todo: remove? also, ass2iqe might output this
in vec2 att_texcoord2;
out vec4 v_color;
out vec3 v_position, v_position_ws;
out vec3 v_normal, v_normal_ws;
out vec2 v_texcoord, v_texcoord2;
out vec3 v_tangent;
out vec3 v_binormal;
out vec3 v_viewpos;
out vec3 v_to_camera;
// shadow
uniform mat4 model, view, inv_view;
uniform mat4 cameraToShadowProjector;
out vec4 vneye;
out vec4 vpeye;
out vec4 sc;
void do_shadow() {
vneye = view * model * vec4(att_normal, 0.0f);
vpeye = view * model * vec4(att_position, 1.0);
sc = cameraToShadowProjector * model * vec4(att_position, 1.0f);
}
// blendshapes
vec3 evaluate_blend_shape(int vertex_index) {
ivec2 coord = ivec2(vertex_index & (2048 - 1), vertex_index >> 11);
int num_blend_shapes = int(f_num_blend_shapes);
vec3 offset = vec3(0.0);
for (int i = 0; i < num_blend_shapes; i++) {
vec4 packedw = blend_weights[i >> 2];
float weight = packedw[i & 3];
offset += weight * texelFetch(blend_shapes, ivec3(coord, i), 0).xyz;
}
return offset;
}
void main() {
vec3 objPos;
if(!SKINNED) {
objPos = att_position;
v_normal = att_normal;
} else {
mat3x4 m = vsBoneMatrix[int(att_indexes.x)] * att_weights.x;
m += vsBoneMatrix[int(att_indexes.y)] * att_weights.y;
m += vsBoneMatrix[int(att_indexes.z)] * att_weights.z;
m += vsBoneMatrix[int(att_indexes.w)] * att_weights.w;
objPos = vec4(att_position, 1.0) * m;
// blendshapes
// objPos += evaluate_blend_shape(int(att_vertexindex));
v_normal = vec4(att_normal, 0.0) * m;
//@todo: tangents
}
// vec3 tangent = att_tangent.xyz;
// 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 = normalize(v_normal);
v_position = att_position;
v_texcoord = att_texcoord;
v_texcoord2 = att_texcoord2;
v_color = att_color;
mat4 modelView = view * att_instanced_matrix;
mat4 l_model = att_instanced_matrix;
v_position_ws = (l_model * vec4( objPos, 1.0 )).xyz;
if(u_billboard > 0) {
vec3 cameraPosition = -transpose(mat3(view)) * view[3].xyz;
vec3 lookDir = normalize(cameraPosition - v_position_ws);
vec3 up = vec3(modelView[0][1], modelView[1][1], modelView[2][1]);
vec3 right = normalize(cross(up, lookDir));
up = cross(lookDir, right);
vec3 scale;
scale.x = length(vec3(l_model[0]));
scale.y = length(vec3(l_model[1]));
scale.z = length(vec3(l_model[2]));
// scale.x *= sign(l_model[0][0]);
// scale.y *= sign(l_model[1][1]);
// scale.z *= sign(l_model[2][2]);
mat4 billboardRotation = mat4(
vec4(right * scale.x, 0.0),
vec4(-up * scale.y, 0.0),
vec4(-lookDir * scale.z, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
if((u_billboard & 0x4) != 0) l_model[0] = billboardRotation[0];
if((u_billboard & 0x2) != 0) l_model[1] = billboardRotation[1];
if((u_billboard & 0x1) != 0) l_model[2] = billboardRotation[2];
modelView = view * l_model;
}
v_position_ws = (l_model * vec4( objPos, 1.0 )).xyz;
v_tangent = normalize(mat3(att_instanced_matrix) * att_tangent.xyz);
#if 0
// compute tangent T and bitangent B
vec3 Q1 = dFdx(att_position);
vec3 Q2 = dFdy(att_position);
vec2 st1 = dFdx(att_texcoord);
vec2 st2 = dFdy(att_texcoord);
vec3 T = normalize(Q1*st2.t - Q2*st1.t);
vec3 B = normalize(-Q1*st2.s + Q2*st1.s);
vec3 binormal = B;
#else
vec3 binormal = cross(att_normal, att_tangent.xyz) * att_tangent.w;
#endif
v_binormal = normalize(mat3(att_instanced_matrix) * binormal);
vec4 finalPos = modelView * vec4( objPos, 1.0 );
vec3 to_camera = normalize( -finalPos.xyz );
v_to_camera = mat3( inv_view ) * to_camera;
gl_Position = P * finalPos;
do_shadow();
#ifndef MAX_BONES
#define MAX_BONES 110
#endif
uniform mat3x4 vsBoneMatrix[MAX_BONES];
uniform bool SKINNED = false;
uniform mat4 M; // RIM
uniform mat4 VP;
uniform mat4 P;
uniform int u_billboard;
#if 0
// Fetch blend channels from all attached blend deformers.
for (size_t di = 0; di < mesh->blend_deformers.count; di++) {
ufbx_blend_deformer *deformer = mesh->blend_deformers.data[di];
for (size_t ci = 0; ci < deformer->channels.count; ci++) {
ufbx_blend_channel *chan = deformer->channels.data[ci];
if (chan->keyframes.count == 0) continue;
if (num_blend_shapes < MAX_BLEND_SHAPES) {
blend_channels[num_blend_shapes] = chan;
vmesh->blend_channel_indices[num_blend_shapes] = (int32_t)chan->typed_id;
num_blend_shapes++;
}
}
}
if (num_blend_shapes > 0) {
vmesh->blend_shape_image = pack_blend_channels_to_image(mesh, blend_channels, num_blend_shapes);
vmesh->num_blend_shapes = num_blend_shapes;
}
ubo.f_num_blend_shapes = (float)mesh->num_blend_shapes;
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;
}
sg_image blend_shapes = mesh->num_blend_shapes > 0 ? mesh->blend_shape_image : view->empty_blend_shape_image;
#endif
// for blendshapes
#ifndef MAX_BLENDSHAPES
#define MAX_BLENDSHAPES 16
#endif
uniform vec4 blend_weights[MAX_BLENDSHAPES]; // @todo: implement me
uniform float f_num_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 vec2 att_texcoord;
in vec3 att_normal;
in vec4 att_tangent; // vec3 + bi sign
in mat4 att_instanced_matrix; // for instanced rendering
in vec4 att_indexes; // @fixme: gles might use ivec4 instead?
in vec4 att_weights; // @todo: downgrade from float to byte
in float att_vertexindex; // for blendshapes
in vec4 att_color;
in vec3 att_bitangent; // @todo: remove? also, ass2iqe might output this
in vec2 att_texcoord2;
out vec4 v_color;
out vec3 v_position, v_position_ws;
out vec3 v_normal, v_normal_ws;
out vec2 v_texcoord, v_texcoord2;
out vec3 v_tangent;
out vec3 v_binormal;
out vec3 v_viewpos;
out vec3 v_to_camera;
// shadow
uniform mat4 model, view, inv_view;
uniform mat4 cameraToShadowProjector;
out vec4 vneye;
out vec4 vpeye;
out vec4 sc;
void do_shadow() {
vneye = view * model * vec4(att_normal, 0.0f);
vpeye = view * model * vec4(att_position, 1.0);
sc = cameraToShadowProjector * model * vec4(att_position, 1.0f);
}
// blendshapes
vec3 evaluate_blend_shape(int vertex_index) {
ivec2 coord = ivec2(vertex_index & (2048 - 1), vertex_index >> 11);
int num_blend_shapes = int(f_num_blend_shapes);
vec3 offset = vec3(0.0);
for (int i = 0; i < num_blend_shapes; i++) {
vec4 packedw = blend_weights[i >> 2];
float weight = packedw[i & 3];
offset += weight * texelFetch(blend_shapes, ivec3(coord, i), 0).xyz;
}
return offset;
}
void main() {
vec3 objPos;
if(!SKINNED) {
objPos = att_position;
v_normal = att_normal;
} else {
mat3x4 m = vsBoneMatrix[int(att_indexes.x)] * att_weights.x;
m += vsBoneMatrix[int(att_indexes.y)] * att_weights.y;
m += vsBoneMatrix[int(att_indexes.z)] * att_weights.z;
m += vsBoneMatrix[int(att_indexes.w)] * att_weights.w;
objPos = vec4(att_position, 1.0) * m;
// blendshapes
// objPos += evaluate_blend_shape(int(att_vertexindex));
v_normal = vec4(att_normal, 0.0) * m;
//@todo: tangents
}
// vec3 tangent = att_tangent.xyz;
// 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 = normalize(v_normal);
v_position = att_position;
v_texcoord = att_texcoord;
v_texcoord2 = att_texcoord2;
v_color = att_color;
mat4 modelView = view * att_instanced_matrix;
mat4 l_model = att_instanced_matrix;
v_position_ws = (l_model * vec4( objPos, 1.0 )).xyz;
if(u_billboard > 0) {
vec3 cameraPosition = -transpose(mat3(view)) * view[3].xyz;
vec3 lookDir = normalize(cameraPosition - v_position_ws);
vec3 up = vec3(modelView[0][1], modelView[1][1], modelView[2][1]);
vec3 right = normalize(cross(up, lookDir));
up = cross(lookDir, right);
vec3 scale;
scale.x = length(vec3(l_model[0]));
scale.y = length(vec3(l_model[1]));
scale.z = length(vec3(l_model[2]));
// scale.x *= sign(l_model[0][0]);
// scale.y *= sign(l_model[1][1]);
// scale.z *= sign(l_model[2][2]);
mat4 billboardRotation = mat4(
vec4(right * scale.x, 0.0),
vec4(-up * scale.y, 0.0),
vec4(-lookDir * scale.z, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
if((u_billboard & 0x4) != 0) l_model[0] = billboardRotation[0];
if((u_billboard & 0x2) != 0) l_model[1] = billboardRotation[1];
if((u_billboard & 0x1) != 0) l_model[2] = billboardRotation[2];
modelView = view * l_model;
}
v_position_ws = (l_model * vec4( objPos, 1.0 )).xyz;
v_tangent = normalize(mat3(att_instanced_matrix) * att_tangent.xyz);
#if 0
// compute tangent T and bitangent B
vec3 Q1 = dFdx(att_position);
vec3 Q2 = dFdy(att_position);
vec2 st1 = dFdx(att_texcoord);
vec2 st2 = dFdy(att_texcoord);
vec3 T = normalize(Q1*st2.t - Q2*st1.t);
vec3 B = normalize(-Q1*st2.s + Q2*st1.s);
vec3 binormal = B;
#else
vec3 binormal = cross(att_normal, att_tangent.xyz) * att_tangent.w;
#endif
v_binormal = normalize(mat3(att_instanced_matrix) * binormal);
vec4 finalPos = modelView * vec4( objPos, 1.0 );
vec3 to_camera = normalize( -finalPos.xyz );
v_to_camera = mat3( inv_view ) * to_camera;
gl_Position = P * finalPos;
do_shadow();
}

View File

@ -1 +1 @@
frame: 0-0 Idle
frame: 0-0 Idle

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,140 +1,140 @@
// base64 de/encoder. Based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN).
// - rlyeh, public domain
#ifndef BASE64_H
#define BASE64_H
unsigned base64_bounds(unsigned size);
char* base64_encode(const void *inp, unsigned inlen); // free() after use
char* base64_decode(const char *inp, unsigned inlen); // array_free() after use
#endif
#ifdef BASE64_C
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#define BASE64_ENCODE_OUT_SIZE(s) ((unsigned int)((((s) + 2) / 3) * 4 + 1))
#define BASE64_DECODE_OUT_SIZE(s) ((unsigned int)(((s) / 4) * 3))
unsigned base64_bounds(unsigned size) {
return BASE64_ENCODE_OUT_SIZE(size);
}
char* base64_encode(const void *inp, unsigned inlen) { // free() after use
unsigned outlen = base64_bounds(inlen);
char *out_ = MALLOC(outlen);
out_[outlen] = 0;
uint_least32_t v;
unsigned ii, io, rem;
char *out = (char *)out_;
const unsigned char *in = (const unsigned char *)inp;
const uint8_t base64enc_tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(io = 0, ii = 0, v = 0, rem = 0; ii < inlen; ii ++) {
unsigned char ch;
ch = in[ii];
v = (v << 8) | ch;
rem += 8;
while (rem >= 6) {
rem -= 6;
if (io >= outlen)
return (FREE(out_), NULL); /* truncation is failure */
out[io ++] = base64enc_tab[(v >> rem) & 63];
}
}
if (rem) {
v <<= (6 - rem);
if (io >= outlen)
return (FREE(out_), NULL); /* truncation is failure */
out[io ++] = base64enc_tab[v & 63];
}
while(io&3) {
if(io>=outlen) return (FREE(out_), NULL); /* truncation is failure */
out[io++]='=';
}
if(io>=outlen) return (FREE(out_), NULL); /* no room for null terminator */
out[io]=0;
return out_;
}
#ifdef array_resize
array(char) base64_decode(const char *inp, unsigned inlen) { // array_free() after use
#if 0
unsigned long outlen = BASE64_DECODE_OUT_SIZE(inlen);
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 ) {
array_free(out_);
return 0;
}
array_resize(out_, outlen);
out_[outlen] = 0;
return out_;
#else
unsigned outlen = BASE64_DECODE_OUT_SIZE(inlen);
array(char) out_ = 0; array_resize(out_, outlen);
// based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN)
uint_least32_t v;
unsigned ii, io, rem;
char *out = (char *)out_;
const unsigned char *in = (const unsigned char *)inp;
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, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
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 };
for (io = 0, ii = 0,v = 0, rem = 0; ii < inlen; ii ++) {
unsigned char ch;
if (isspace(in[ii]))
continue;
if ((in[ii]=='=') || (!in[ii]))
break; /* stop at = or null character*/
ch = base64dec_tab[(unsigned char)in[ii]];
if (ch == 255)
break; /* stop at a parse error */
v = (v<<6) | ch;
rem += 6;
if (rem >= 8) {
rem -= 8;
if (io >= outlen)
return (array_free(out_), NULL); /* truncation is failure */
out[io ++] = (v >> rem) & 255;
}
}
if (rem >= 8) {
rem -= 8;
if (io >= outlen)
return (array_free(out_), NULL); /* truncation is failure */
out[io ++] = (v >> rem) & 255;
}
return (array_resize(out_, io), out_);
#endif
}
#endif // array_resize
#endif // BASE64_C
// base64 de/encoder. Based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN).
// - rlyeh, public domain
#ifndef BASE64_H
#define BASE64_H
unsigned base64_bounds(unsigned size);
char* base64_encode(const void *inp, unsigned inlen); // free() after use
char* base64_decode(const char *inp, unsigned inlen); // array_free() after use
#endif
#ifdef BASE64_C
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#define BASE64_ENCODE_OUT_SIZE(s) ((unsigned int)((((s) + 2) / 3) * 4 + 1))
#define BASE64_DECODE_OUT_SIZE(s) ((unsigned int)(((s) / 4) * 3))
unsigned base64_bounds(unsigned size) {
return BASE64_ENCODE_OUT_SIZE(size);
}
char* base64_encode(const void *inp, unsigned inlen) { // free() after use
unsigned outlen = base64_bounds(inlen);
char *out_ = MALLOC(outlen);
out_[outlen] = 0;
uint_least32_t v;
unsigned ii, io, rem;
char *out = (char *)out_;
const unsigned char *in = (const unsigned char *)inp;
const uint8_t base64enc_tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(io = 0, ii = 0, v = 0, rem = 0; ii < inlen; ii ++) {
unsigned char ch;
ch = in[ii];
v = (v << 8) | ch;
rem += 8;
while (rem >= 6) {
rem -= 6;
if (io >= outlen)
return (FREE(out_), NULL); /* truncation is failure */
out[io ++] = base64enc_tab[(v >> rem) & 63];
}
}
if (rem) {
v <<= (6 - rem);
if (io >= outlen)
return (FREE(out_), NULL); /* truncation is failure */
out[io ++] = base64enc_tab[v & 63];
}
while(io&3) {
if(io>=outlen) return (FREE(out_), NULL); /* truncation is failure */
out[io++]='=';
}
if(io>=outlen) return (FREE(out_), NULL); /* no room for null terminator */
out[io]=0;
return out_;
}
#ifdef array_resize
array(char) base64_decode(const char *inp, unsigned inlen) { // array_free() after use
#if 0
unsigned long outlen = BASE64_DECODE_OUT_SIZE(inlen);
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 ) {
array_free(out_);
return 0;
}
array_resize(out_, outlen);
out_[outlen] = 0;
return out_;
#else
unsigned outlen = BASE64_DECODE_OUT_SIZE(inlen);
array(char) out_ = 0; array_resize(out_, outlen);
// based on code by Jon Mayo - November 13, 2003 (PUBLIC DOMAIN)
uint_least32_t v;
unsigned ii, io, rem;
char *out = (char *)out_;
const unsigned char *in = (const unsigned char *)inp;
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, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
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 };
for (io = 0, ii = 0,v = 0, rem = 0; ii < inlen; ii ++) {
unsigned char ch;
if (isspace(in[ii]))
continue;
if ((in[ii]=='=') || (!in[ii]))
break; /* stop at = or null character*/
ch = base64dec_tab[(unsigned char)in[ii]];
if (ch == 255)
break; /* stop at a parse error */
v = (v<<6) | ch;
rem += 6;
if (rem >= 8) {
rem -= 8;
if (io >= outlen)
return (array_free(out_), NULL); /* truncation is failure */
out[io ++] = (v >> rem) & 255;
}
}
if (rem >= 8) {
rem -= 8;
if (io >= outlen)
return (array_free(out_), NULL); /* truncation is failure */
out[io ++] = (v >> rem) & 255;
}
return (array_resize(out_, io), out_);
#endif
}
#endif // array_resize
#endif // BASE64_C

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

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

View File

@ -1,407 +1,407 @@
/* public domain Simple, Minimalistic MPEG Layer 1 decoder - http://jonolick.com
*
* Revision History:
* 1.00 (2014-26-1) Initial release.
*
* Basic usage:
* int hz, channels, outputSize;
* short *output;
* jo_read_mp1(input, inputSize, output, outputSize, hz, channels);
* // Do something with the data here
* free(output);
*
* */
#ifndef JO_INCLUDE_MP1_H
#define JO_INCLUDE_MP1_H
#include <stdbool.h>
extern bool jo_read_mp1(const void *input, int inputSize, short **output, int *outputSize, int *hz, int *channels);
#endif // JO_INCLUDE_MP1_H
#ifndef JO_MP1_HEADER_FILE_ONLY
#if defined(_MSC_VER) && _MSC_VER >= 0x1400
#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen()
#endif
#include <stdlib.h>
#include <math.h>
#include <limits.h>
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,
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.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;
// 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;
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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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)));
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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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},
{-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.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.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.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.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.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.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.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.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.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.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.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
static unsigned jo_readBits(const unsigned char *data, int *at, int num) {
unsigned r = 0;
// read partial starting bits
int sc = (8 - (*at & 7)) & 7;
sc = sc > num ? num : sc;
if(sc) {
r = (data[*at/8] >> (8 - (*at&7) - sc)) & ((1<<sc)-1);
num -= sc;
*at += sc;
}
// read full bytes
while(num>=8) {
r <<= 8;
r |= data[*at/8];
*at += 8;
num -= 8;
}
// read partial ending bits
if(num) {
r <<= num;
r |= (data[*at/8] >> (8 - num)) & ((1<<num)-1);
*at += num;
}
return r;
}
bool jo_read_mp1(const void *input, int inputSize, short **output_, int *outputSize_, int *hz_, int *channels_) {
int outputSize = 0, hz, channels;
short *output = 0;
int outputMax = 0;
const unsigned char *data = (const unsigned char *)input;
// Buffers for the lapped transform
double buf[2][2 * 512] = { 0 };
int bufOffset[2] = { 64,64 };
int at = 0; // Where in the stream are we?
while (at < inputSize * 8) {
// Sync markers are byte aligned
at = (at + 7)&-8;
while (at < inputSize * 8 - 32) {
if (data[at / 8] == 0xFF && (data[at / 8 + 1] & 0xF0) == 0xF0) {
break;
}
else {
at += 8;
}
}
if (at >= inputSize * 8 - 32) {
break;
}
unsigned header = jo_readBits(data, &at, 32);
//printf("header: %x.%x/%x: %08x\n", (at-32)/8, (at-32)&7, inputSize, header);
// sync = 0xFFF
// ID = 1
// layer = 3 (layer 1)
if ((header & 0xFFFE0000) != 0xFFFE0000) {
return false;
}
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];
if (kbps < 0) {
return false;
}
static const int hzTbl[4] = { 44100, 48000, 32000, 0 };
hz = hzTbl[(header >> 10) & 3];
if (!hz) {
return false;
}
// mode 0 = stereo
// mode 1 = joint stereo
// mode 2 = dual channel (no idea what this does TODO)
// mode 3 = mono
int mode = (header >> 6) & 3;
int modeExt = (header >> 4) & 3;
channels = mode == 3 ? 1 : 2;
const int bound = mode == 1 ? (modeExt + 1) * 4 : 32;
bool errorProtection = ((header >> 16) & 1) ^ 1; //< @r-lyeh extra parens
if (errorProtection) {
at += 16; // skip the CRC.
}
// Read bit allocations
int bitAlloc[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
bitAlloc[i][ch] = jo_readBits(data, &at, 4);
}
}
for (int i = bound; i < 32; ++i) {
bitAlloc[i][1] = bitAlloc[i][0] = jo_readBits(data, &at, 4);
}
// Read scale indexes
int scaleIdx[32][2];
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
scaleIdx[i][ch] = bitAlloc[i][ch] ? jo_readBits(data, &at, 6) : 63;
}
}
// Read & compute output samples
short pcm[12][2][32];
for (int s = 0; s < 12; ++s) {
// Read normalized, quantized band samples
int samples[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
if (bitAlloc[i][ch]) {
samples[i][ch] = jo_readBits(data, &at, bitAlloc[i][ch] + 1);
}
}
}
for (int i = bound; i < 32; ++i) {
if (bitAlloc[i][0]) {
samples[i][1] = samples[i][0] = jo_readBits(data, &at, bitAlloc[i][0] + 1);
}
}
// Compute bands: Dequantize & Denormalize
double bandTbl[2][32] = { 0 };
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
int b = bitAlloc[i][ch];
if (b++) {
int samp = samples[i][ch];
double f = ((samp >> (b - 1)) & 1) ? 0 : -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 *= s_jo_multTbl[scaleIdx[i][ch]];
bandTbl[ch][i] = f;
}
}
}
// Convert subbands to PCM
for (int ch = 0; ch < channels; ++ch) {
bufOffset[ch] = (bufOffset[ch] + 0x3C0) & 0x3ff;
double *bufOffsetPtr = buf[ch] + bufOffset[ch];
const double *f = s_jo_filterTbl[0];
for (int i = 0; i < 64; ++i) {
double sum = 0;
for (int j = 0; j < 32; ++j) {
sum += *f++ * bandTbl[ch][j];
}
bufOffsetPtr[i] = sum;
}
const double *w = s_jo_windowTbl;
for (int i = 0; i < 32; ++i) {
double sum = 0;
for (int j = 0; j < 16; ++j) {
int k = i | (j + (j + 1 & -2)) << 5;
sum += *w++ * buf[ch][(k + bufOffset[ch]) & 0x3ff];
}
int ss = (int)(sum * 0x8000);
ss = ss > SHRT_MAX ? SHRT_MAX : ss < SHRT_MIN ? SHRT_MIN : ss;
pcm[s][ch][i] = ss;
}
}
}
if (at > inputSize * 8) {
printf("file corruption?\n");
return false;
}
if (outputMax == 0) {
// estimate total number of samples (may be totally wrong, but its better than nothing)
at = (at + 7)&-8;
outputMax = inputSize / (at / 8) * 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
if (outputSize * sizeof(*output) + 384 * channels * sizeof(*output) > outputMax) {
outputMax += 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
for (int i = 0; i < 12; ++i) {
for (int j = 0; j < 32; ++j) {
for (int k = 0; k < channels; ++k) {
output[outputSize++] = pcm[i][k][j];
}
}
}
}
*outputSize_ = outputSize;
*hz_ = hz;
*channels_ = channels;
*output_ = output;
return outputSize && hz && channels && output;
}
#endif // JO_MP1_HEADER_FILE_ONLY
/* public domain Simple, Minimalistic MPEG Layer 1 decoder - http://jonolick.com
*
* Revision History:
* 1.00 (2014-26-1) Initial release.
*
* Basic usage:
* int hz, channels, outputSize;
* short *output;
* jo_read_mp1(input, inputSize, output, outputSize, hz, channels);
* // Do something with the data here
* free(output);
*
* */
#ifndef JO_INCLUDE_MP1_H
#define JO_INCLUDE_MP1_H
#include <stdbool.h>
extern bool jo_read_mp1(const void *input, int inputSize, short **output, int *outputSize, int *hz, int *channels);
#endif // JO_INCLUDE_MP1_H
#ifndef JO_MP1_HEADER_FILE_ONLY
#if defined(_MSC_VER) && _MSC_VER >= 0x1400
#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen()
#endif
#include <stdlib.h>
#include <math.h>
#include <limits.h>
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,
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.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;
// 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;
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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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)));
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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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},
{-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.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.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.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.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.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.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.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.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.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.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.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
static unsigned jo_readBits(const unsigned char *data, int *at, int num) {
unsigned r = 0;
// read partial starting bits
int sc = (8 - (*at & 7)) & 7;
sc = sc > num ? num : sc;
if(sc) {
r = (data[*at/8] >> (8 - (*at&7) - sc)) & ((1<<sc)-1);
num -= sc;
*at += sc;
}
// read full bytes
while(num>=8) {
r <<= 8;
r |= data[*at/8];
*at += 8;
num -= 8;
}
// read partial ending bits
if(num) {
r <<= num;
r |= (data[*at/8] >> (8 - num)) & ((1<<num)-1);
*at += num;
}
return r;
}
bool jo_read_mp1(const void *input, int inputSize, short **output_, int *outputSize_, int *hz_, int *channels_) {
int outputSize = 0, hz, channels;
short *output = 0;
int outputMax = 0;
const unsigned char *data = (const unsigned char *)input;
// Buffers for the lapped transform
double buf[2][2 * 512] = { 0 };
int bufOffset[2] = { 64,64 };
int at = 0; // Where in the stream are we?
while (at < inputSize * 8) {
// Sync markers are byte aligned
at = (at + 7)&-8;
while (at < inputSize * 8 - 32) {
if (data[at / 8] == 0xFF && (data[at / 8 + 1] & 0xF0) == 0xF0) {
break;
}
else {
at += 8;
}
}
if (at >= inputSize * 8 - 32) {
break;
}
unsigned header = jo_readBits(data, &at, 32);
//printf("header: %x.%x/%x: %08x\n", (at-32)/8, (at-32)&7, inputSize, header);
// sync = 0xFFF
// ID = 1
// layer = 3 (layer 1)
if ((header & 0xFFFE0000) != 0xFFFE0000) {
return false;
}
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];
if (kbps < 0) {
return false;
}
static const int hzTbl[4] = { 44100, 48000, 32000, 0 };
hz = hzTbl[(header >> 10) & 3];
if (!hz) {
return false;
}
// mode 0 = stereo
// mode 1 = joint stereo
// mode 2 = dual channel (no idea what this does TODO)
// mode 3 = mono
int mode = (header >> 6) & 3;
int modeExt = (header >> 4) & 3;
channels = mode == 3 ? 1 : 2;
const int bound = mode == 1 ? (modeExt + 1) * 4 : 32;
bool errorProtection = ((header >> 16) & 1) ^ 1; //< @r-lyeh extra parens
if (errorProtection) {
at += 16; // skip the CRC.
}
// Read bit allocations
int bitAlloc[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
bitAlloc[i][ch] = jo_readBits(data, &at, 4);
}
}
for (int i = bound; i < 32; ++i) {
bitAlloc[i][1] = bitAlloc[i][0] = jo_readBits(data, &at, 4);
}
// Read scale indexes
int scaleIdx[32][2];
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
scaleIdx[i][ch] = bitAlloc[i][ch] ? jo_readBits(data, &at, 6) : 63;
}
}
// Read & compute output samples
short pcm[12][2][32];
for (int s = 0; s < 12; ++s) {
// Read normalized, quantized band samples
int samples[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
if (bitAlloc[i][ch]) {
samples[i][ch] = jo_readBits(data, &at, bitAlloc[i][ch] + 1);
}
}
}
for (int i = bound; i < 32; ++i) {
if (bitAlloc[i][0]) {
samples[i][1] = samples[i][0] = jo_readBits(data, &at, bitAlloc[i][0] + 1);
}
}
// Compute bands: Dequantize & Denormalize
double bandTbl[2][32] = { 0 };
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
int b = bitAlloc[i][ch];
if (b++) {
int samp = samples[i][ch];
double f = ((samp >> (b - 1)) & 1) ? 0 : -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 *= s_jo_multTbl[scaleIdx[i][ch]];
bandTbl[ch][i] = f;
}
}
}
// Convert subbands to PCM
for (int ch = 0; ch < channels; ++ch) {
bufOffset[ch] = (bufOffset[ch] + 0x3C0) & 0x3ff;
double *bufOffsetPtr = buf[ch] + bufOffset[ch];
const double *f = s_jo_filterTbl[0];
for (int i = 0; i < 64; ++i) {
double sum = 0;
for (int j = 0; j < 32; ++j) {
sum += *f++ * bandTbl[ch][j];
}
bufOffsetPtr[i] = sum;
}
const double *w = s_jo_windowTbl;
for (int i = 0; i < 32; ++i) {
double sum = 0;
for (int j = 0; j < 16; ++j) {
int k = i | (j + (j + 1 & -2)) << 5;
sum += *w++ * buf[ch][(k + bufOffset[ch]) & 0x3ff];
}
int ss = (int)(sum * 0x8000);
ss = ss > SHRT_MAX ? SHRT_MAX : ss < SHRT_MIN ? SHRT_MIN : ss;
pcm[s][ch][i] = ss;
}
}
}
if (at > inputSize * 8) {
printf("file corruption?\n");
return false;
}
if (outputMax == 0) {
// estimate total number of samples (may be totally wrong, but its better than nothing)
at = (at + 7)&-8;
outputMax = inputSize / (at / 8) * 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
if (outputSize * sizeof(*output) + 384 * channels * sizeof(*output) > outputMax) {
outputMax += 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
for (int i = 0; i < 12; ++i) {
for (int j = 0; j < 32; ++j) {
for (int k = 0; k < channels; ++k) {
output[outputSize++] = pcm[i][k][j];
}
}
}
}
*outputSize_ = outputSize;
*hz_ = hz;
*channels_ = channels;
*output_ = output;
return outputSize && hz && channels && output;
}
#endif // JO_MP1_HEADER_FILE_ONLY

View File

@ -1,264 +1,264 @@
/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com
*
* Latest revisions:
* 1.02c rgbx -> bgrx channel swap && vertical image flip && userdef components (@r-lyeh)
* 1.02 (22-03-2017) Fixed AC encoding bug.
* Fixed color space bug (thx r- lyeh!)
* 1.01 (18-10-2016) warning fixes
* 1.00 (25-09-2016) initial release
*
* Basic usage:
* char *frame = new char[width*height*4]; // 4 component. bgrx format, where X is unused
* 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 1
* jo_write_mpeg(fp, frame, width, height, 60); // frame 2
* ...
* fclose(fp);
*
* Notes:
* Only supports 24, 25, 30, 50, or 60 fps
*
* 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 ...
*
* MPEG-1/2 currently has no active patents as far as I am aware.
*
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
* http://www.cs.cornell.edu/dali/api/mpegvideo-c.html
* */
#ifndef JO_INCLUDE_MPEG_H
#define JO_INCLUDE_MPEG_H
#include <stdio.h>
// 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
// then include jo_mpeg.c from it.
// Returns false on failure
extern void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps);
#endif // JO_INCLUDE_MPEG_H
#ifndef JO_MPEG_HEADER_FILE_ONLY
#ifndef JO_MPEG_COMPONENTS
#define JO_MPEG_COMPONENTS 4
#endif
#include <stdio.h>
#include <math.h>
#include <memory.h>
// 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_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] = {
{{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}},
{{10,5},{8,8},{22,11},{40,13},{40,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}},
{{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}},
};
static const float s_jo_quantTbl[64] = {
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.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.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.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,
};
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 {
FILE *fp;
int buf, cnt;
} jo_bits_t;
static void jo_writeBits(jo_bits_t *b, int value, int count) {
b->cnt += count;
b->buf |= value << (24 - b->cnt);
while(b->cnt >= 8) {
unsigned char c = (b->buf >> 16) & 255;
putc(c, b->fp);
b->buf <<= 8;
b->cnt -= 8;
}
}
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 tmp7 = *d0 - *d7;
float tmp1 = *d1 + *d6;
float tmp6 = *d1 - *d6;
float tmp2 = *d2 + *d5;
float tmp5 = *d2 - *d5;
float tmp3 = *d3 + *d4;
float tmp4 = *d3 - *d4;
// Even part
float tmp10 = tmp0 + tmp3; // phase 2
float tmp13 = tmp0 - tmp3;
float tmp11 = tmp1 + tmp2;
float tmp12 = tmp1 - tmp2;
*d0 = tmp10 + tmp11; // phase 3
*d4 = tmp10 - tmp11;
float z1 = (tmp12 + tmp13) * 0.707106781f; // c4
*d2 = tmp13 + z1; // phase 5
*d6 = tmp13 - z1;
// Odd part
tmp10 = tmp4 + tmp5; // phase 2
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
// The rotator is modified from fig 4-8 to avoid extra negations.
float z5 = (tmp10 - tmp12) * 0.382683433f; // c6
float z2 = tmp10 * 0.541196100f + z5; // c2-c6
float z4 = tmp12 * 1.306562965f + z5; // c2+c6
float z3 = tmp11 * 0.707106781f; // c4
float z11 = tmp7 + z3; // phase 5
float z13 = tmp7 - z3;
*d5 = z13 + z2; // phase 6
*d3 = z13 - z2;
*d1 = z11 + z4;
*d7 = z11 - z4;
}
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) {
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) {
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];
for(int i=0; i<64; ++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));
}
DC = Q[0] - DC;
int aDC = DC < 0 ? -DC : DC;
int size = 0;
int tempval = aDC;
while(tempval) {
size++;
tempval >>= 1;
}
jo_writeBits(bits, htdc[size][0], htdc[size][1]);
if(DC < 0) aDC ^= (1 << size) - 1;
jo_writeBits(bits, aDC, size);
int endpos = 63;
for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ }
for(int i = 1; i <= endpos;) {
int run = 0;
while (Q[i]==0 && i<endpos) {
++run;
++i;
}
int AC = Q[i++];
int aAC = AC < 0 ? -AC : AC;
int code = 0, size = 0;
if (run<32 && aAC<=40) {
code = s_jo_HTAC[run][aAC-1][0];
size = s_jo_HTAC[run][aAC-1][1];
if (AC < 0) code += 1;
}
if(!size) {
jo_writeBits(bits, 1, 6);
jo_writeBits(bits, run, 6);
if (AC < -127) {
jo_writeBits(bits, 128, 8);
} else if(AC > 127) {
jo_writeBits(bits, 0, 8);
}
code = AC&255;
size = 8;
}
jo_writeBits(bits, code, size);
}
jo_writeBits(bits, 2, 2);
return Q[0];
}
void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps) {
int lastDCY = 128, lastDCCR = 128, lastDCCB = 128;
jo_bits_t bits = {fp};
// Sequence Header
fwrite("\x00\x00\x01\xB3", 4, 1, fp);
// 12 bits for width, height
putc((width>>4)&0xFF, fp);
putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp);
putc(height & 0xFF, fp);
// aspect ratio, framerate
if(fps <= 24) putc(0x12, fp);
else if(fps <= 25) putc(0x13, fp);
else if(fps <= 30) putc(0x15, fp);
else if(fps <= 50) putc(0x16, fp);
else putc(0x18, fp); // 60fps
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\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header
fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header
jo_writeBits(&bits, 0x10, 6);
for (int vblock=0; vblock<(height+15)/16; vblock++) {
for (int hblock=0; hblock<(width+15)/16; hblock++) {
jo_writeBits(&bits, 3, 2);
float Y[256], CBx[256], CRx[256];
for (int i=0; i<256; ++i) {
int y = vblock*16+(i/16);
int x = hblock*16+(i&15);
x = x >= width ? width-1 : x;
y = y >= height ? height-1 : y;
int _4 = JO_MPEG_COMPONENTS;
// const unsigned char *c = bgrx + y*width*_4+x*_4; // original
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
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;
CRx[i] = ( 0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
}
// Downsample Cb,Cr (420 format)
float CB[64], CR[64];
for (int i=0; i<64; ++i) {
int j =(i&7)*2 + (i&56)*4;
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;
}
for (int k1=0; k1<2; ++k1) {
for (int k2=0; k2<2; ++k2) {
float block[64];
for (int i=0; i<64; i+=8) {
int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8;
memcpy(block+i, Y+j, 8*sizeof(Y[0]));
}
lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY);
}
}
lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB);
lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR);
}
}
jo_writeBits(&bits, 0, 7);
fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence
}
#endif
/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com
*
* Latest revisions:
* 1.02c rgbx -> bgrx channel swap && vertical image flip && userdef components (@r-lyeh)
* 1.02 (22-03-2017) Fixed AC encoding bug.
* Fixed color space bug (thx r- lyeh!)
* 1.01 (18-10-2016) warning fixes
* 1.00 (25-09-2016) initial release
*
* Basic usage:
* char *frame = new char[width*height*4]; // 4 component. bgrx format, where X is unused
* 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 1
* jo_write_mpeg(fp, frame, width, height, 60); // frame 2
* ...
* fclose(fp);
*
* Notes:
* Only supports 24, 25, 30, 50, or 60 fps
*
* 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 ...
*
* MPEG-1/2 currently has no active patents as far as I am aware.
*
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
* http://www.cs.cornell.edu/dali/api/mpegvideo-c.html
* */
#ifndef JO_INCLUDE_MPEG_H
#define JO_INCLUDE_MPEG_H
#include <stdio.h>
// 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
// then include jo_mpeg.c from it.
// Returns false on failure
extern void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps);
#endif // JO_INCLUDE_MPEG_H
#ifndef JO_MPEG_HEADER_FILE_ONLY
#ifndef JO_MPEG_COMPONENTS
#define JO_MPEG_COMPONENTS 4
#endif
#include <stdio.h>
#include <math.h>
#include <memory.h>
// 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_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] = {
{{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}},
{{10,5},{8,8},{22,11},{40,13},{40,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}},
{{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}},
};
static const float s_jo_quantTbl[64] = {
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.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.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.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,
};
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 {
FILE *fp;
int buf, cnt;
} jo_bits_t;
static void jo_writeBits(jo_bits_t *b, int value, int count) {
b->cnt += count;
b->buf |= value << (24 - b->cnt);
while(b->cnt >= 8) {
unsigned char c = (b->buf >> 16) & 255;
putc(c, b->fp);
b->buf <<= 8;
b->cnt -= 8;
}
}
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 tmp7 = *d0 - *d7;
float tmp1 = *d1 + *d6;
float tmp6 = *d1 - *d6;
float tmp2 = *d2 + *d5;
float tmp5 = *d2 - *d5;
float tmp3 = *d3 + *d4;
float tmp4 = *d3 - *d4;
// Even part
float tmp10 = tmp0 + tmp3; // phase 2
float tmp13 = tmp0 - tmp3;
float tmp11 = tmp1 + tmp2;
float tmp12 = tmp1 - tmp2;
*d0 = tmp10 + tmp11; // phase 3
*d4 = tmp10 - tmp11;
float z1 = (tmp12 + tmp13) * 0.707106781f; // c4
*d2 = tmp13 + z1; // phase 5
*d6 = tmp13 - z1;
// Odd part
tmp10 = tmp4 + tmp5; // phase 2
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
// The rotator is modified from fig 4-8 to avoid extra negations.
float z5 = (tmp10 - tmp12) * 0.382683433f; // c6
float z2 = tmp10 * 0.541196100f + z5; // c2-c6
float z4 = tmp12 * 1.306562965f + z5; // c2+c6
float z3 = tmp11 * 0.707106781f; // c4
float z11 = tmp7 + z3; // phase 5
float z13 = tmp7 - z3;
*d5 = z13 + z2; // phase 6
*d3 = z13 - z2;
*d1 = z11 + z4;
*d7 = z11 - z4;
}
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) {
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) {
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];
for(int i=0; i<64; ++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));
}
DC = Q[0] - DC;
int aDC = DC < 0 ? -DC : DC;
int size = 0;
int tempval = aDC;
while(tempval) {
size++;
tempval >>= 1;
}
jo_writeBits(bits, htdc[size][0], htdc[size][1]);
if(DC < 0) aDC ^= (1 << size) - 1;
jo_writeBits(bits, aDC, size);
int endpos = 63;
for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ }
for(int i = 1; i <= endpos;) {
int run = 0;
while (Q[i]==0 && i<endpos) {
++run;
++i;
}
int AC = Q[i++];
int aAC = AC < 0 ? -AC : AC;
int code = 0, size = 0;
if (run<32 && aAC<=40) {
code = s_jo_HTAC[run][aAC-1][0];
size = s_jo_HTAC[run][aAC-1][1];
if (AC < 0) code += 1;
}
if(!size) {
jo_writeBits(bits, 1, 6);
jo_writeBits(bits, run, 6);
if (AC < -127) {
jo_writeBits(bits, 128, 8);
} else if(AC > 127) {
jo_writeBits(bits, 0, 8);
}
code = AC&255;
size = 8;
}
jo_writeBits(bits, code, size);
}
jo_writeBits(bits, 2, 2);
return Q[0];
}
void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps) {
int lastDCY = 128, lastDCCR = 128, lastDCCB = 128;
jo_bits_t bits = {fp};
// Sequence Header
fwrite("\x00\x00\x01\xB3", 4, 1, fp);
// 12 bits for width, height
putc((width>>4)&0xFF, fp);
putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp);
putc(height & 0xFF, fp);
// aspect ratio, framerate
if(fps <= 24) putc(0x12, fp);
else if(fps <= 25) putc(0x13, fp);
else if(fps <= 30) putc(0x15, fp);
else if(fps <= 50) putc(0x16, fp);
else putc(0x18, fp); // 60fps
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\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header
fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header
jo_writeBits(&bits, 0x10, 6);
for (int vblock=0; vblock<(height+15)/16; vblock++) {
for (int hblock=0; hblock<(width+15)/16; hblock++) {
jo_writeBits(&bits, 3, 2);
float Y[256], CBx[256], CRx[256];
for (int i=0; i<256; ++i) {
int y = vblock*16+(i/16);
int x = hblock*16+(i&15);
x = x >= width ? width-1 : x;
y = y >= height ? height-1 : y;
int _4 = JO_MPEG_COMPONENTS;
// const unsigned char *c = bgrx + y*width*_4+x*_4; // original
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
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;
CRx[i] = ( 0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
}
// Downsample Cb,Cr (420 format)
float CB[64], CR[64];
for (int i=0; i<64; ++i) {
int j =(i&7)*2 + (i&56)*4;
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;
}
for (int k1=0; k1<2; ++k1) {
for (int k2=0; k2<2; ++k2) {
float block[64];
for (int i=0; i<64; i+=8) {
int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8;
memcpy(block+i, Y+j, 8*sizeof(Y[0]));
}
lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY);
}
}
lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB);
lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR);
}
}
jo_writeBits(&bits, 0, 7);
fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence
}
#endif

View File

@ -1,440 +1,440 @@
// JSON5 + SJSON parser module
//
// License:
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
// No warranty is implied, use at your own risk.
//
// Credits:
// r-lyeh (fork)
// Dominik Madarasz (@zaklaus) (original code)
#ifndef JSON5_H
#define JSON5_H
#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)
#endif
#include <stdint.h>
#include <stdio.h>
typedef enum json5_type {
JSON5_UNDEFINED, // 0
JSON5_NULL, // 1
JSON5_BOOL, // 2
JSON5_OBJECT, // 3
JSON5_STRING, // 4
JSON5_ARRAY, // 5
JSON5_INTEGER, // 6
JSON5_REAL, // 7
} json5_type;
typedef struct json5 {
char* name;
#ifdef NDEBUG
unsigned type : 3;
#else
json5_type type;
#endif
unsigned count : 29;
union {
struct json5* array;
struct json5* nodes;
int64_t integer;
double real;
char* string;
int boolean;
};
} json5;
char* json5_parse(json5 *root, char *source, int flags);
void json5_write(FILE *fp, const json5 *root);
void json5_free(json5 *root);
#endif // JSON5_H
// json5 ----------------------------------------------------------------------
#ifdef JSON5_C
//#pragma once
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
char *json5__trim(char *p) {
while (*p) {
/**/ if( isspace(*p) ) ++p;
else if( p[0] == '/' && p[1] == '*' ) { // skip C comment
for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {}
if( *p ) p += 2;
}
else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment
for( p += 2; *p && p[0] != '\n'; ++p) {}
if( *p ) ++p;
}
else break;
}
return p;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code);
char *json5__parse_string(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( *p == '"' || *p == '\'' || *p == '`' ) {
obj->type = JSON5_STRING;
obj->string = p + 1;
char eos_char = *p, *b = obj->string, *e = b;
while (*e) {
/**/ if( *e == '\\' && (e[1] == eos_char) ) ++e;
else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' ';
else if( *e == eos_char ) break;
++e;
}
*e = '\0';
return p = e + 1;
}
//JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_object(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( 1 /* *p == '{' */ ) { /* <-- for SJSON */
int skip = *p == '{'; /* <-- for SJSON */
obj->type = JSON5_OBJECT;
obj->nodes = 0;
obj->count = 0;
while (*p) {
json5 node = { 0 };
do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' );
if( *p == '}' ) {
++p;
break;
}
// @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)
node.name = p;
do {
++p;
} while (*p && (isalnum(*p) || *p == '_' || *p == '$' || *p == '.') ); // also || is_unicode(p)
char *e = p;
p = json5__trim(p);
*e = '\0';
}
else { //if( *p == '"' || *p == '\'' || *p == '`' ) {
char *ps = json5__parse_string(&node, p, err_code);
if( !ps ) {
return NULL;
}
p = ps;
node.name = node.string;
p = json5__trim(p);
}
// @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
p = json5__trim(p + 1);
p = json5__parse_value(&node, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( node.type != JSON5_UNDEFINED ) {
array_push(obj->nodes, node);
++obj->count;
}
if( *p == '}') { ++p; break; }
}
return p;
}
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code) {
assert(obj && p);
p = json5__trim(p);
char *is_string = json5__parse_string(obj, p, err_code);
if( is_string ) {
p = is_string;
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '{' ) {
p = json5__parse_object( obj, p, err_code );
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '[' ) {
obj->type = JSON5_ARRAY;
obj->array = 0;
obj->count = 0;
while (*p) {
json5 elem = { 0 };
do { p = json5__trim(p + 1); } while( *p == ',' );
if( *p == ']') { ++p; break; }
p = json5__parse_value(&elem, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( elem.type != JSON5_UNDEFINED ) {
array_push(obj->array, elem);
++obj->count;
}
if (*p == ']') { ++p; break; }
}
}
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 int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 };
for( int i = 0; labels[i]; ++i ) {
if( !strncmp(p, labels[i], lenghts[i] ) ) {
p += lenghts[i];
#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;
#else
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN;
#endif
else if( i >= 1 ) obj->type = JSON5_BOOL, obj->boolean = i <= 2;
else obj->type = JSON5_NULL;
break;
}
}
if( obj->type == JSON5_UNDEFINED ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
}
else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) {
char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0;
while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) {
is_hex |= (*p | 32) == 'x';
is_dbl |= *p == '.';
*buf++ = *p++;
}
obj->type = is_dbl ? JSON5_REAL : JSON5_INTEGER;
long long unsigned int llu;
long long int lli;
/**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real );
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 {
return NULL;
}
return p;
}
char *json5_parse(json5 *root, char *p, int flags) {
char *err_code = "";
*root = (json5) {0};
if( p && p[0] ) {
p = json5__trim(p);
if( *p == '[' ) { /* <-- for SJSON */
json5__parse_value(root, p, &err_code);
} else {
json5__parse_object(root, p, &err_code); /* <-- for SJSON */
}
} else {
root->type = JSON5_OBJECT;
}
return err_code[0] ? err_code : 0;
}
void json5_free(json5 *root) {
if( root->type == JSON5_ARRAY && root->array ) {
for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) {
json5_free(root->array + i);
}
array_free(root->array);
}
if( root->type == JSON5_OBJECT && root->nodes ) {
for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) {
json5_free(root->nodes + i);
}
array_free(root->nodes);
}
*root = (json5) {0}; // needed?
}
void json5_write(FILE *fp, const json5 *o) {
static __thread int indent = 0;
int tabs = 1; // 0,1,2,4,8
if( o->name ) {
fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":");
}
/**/ 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_INTEGER ) fprintf(fp, "%lld", (long long int)o->integer);
else if( o->type == JSON5_REAL ) {
/**/ 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 fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness
}
#if 0
else if( o->type == JSON5_STRING ) { // write (escaped) string
char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256];
for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i];
const char *b = o->string, *e = strpbrk(b, chars), *sep = "\"";
while( e ) {
fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] );
e = strpbrk( b = e + 1, chars);
sep = "";
}
fprintf(fp, "%s%s\"", sep, b);
}
#else
else if( o->type == JSON5_STRING ) { // write string
fprintf(fp, "\"%s\"", o->string);
}
#endif
else if( o->type == JSON5_ARRAY ) {
const char *sep = "";
fprintf(fp, "%s", tabs ? "[ " : "[");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ", " : ",";
json5_write(fp, o->array + i);
}
fprintf(fp, "%s", tabs ? " ]" : "]");
}
else if( o->type == JSON5_OBJECT ) {
const char *sep = "";
fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":"");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ",";
json5_write(fp, o->nodes + i);
}
fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, "");
} else {
char p[16] = {0};
JSON5_ASSERT; /* "json5_error_invalid_value"; */
}
}
#ifdef JSON5_BENCH
#include <time.h>
int main() {
// https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/
char *content = 0;
for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) {
fseek(fp, 0L, SEEK_END);
size_t pos = ftell(fp);
fseek(fp, 0L, SEEK_SET);
content = (char*)malloc( pos + 1 );
fread(content, 1, pos, fp);
content[pos] = 0;
}
if( content ) {
clock_t start = clock();
json5 root = {0};
char *error = json5_parse(&root, content, 0);
clock_t end = clock();
double delta = ( end - start ) / (double)CLOCKS_PER_SEC;
if( !error ) {
printf("Parsing time: %.3fms\n", delta*1000);
printf("Total nodes: %d\n", array_count(root.array));
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[2].string);
} else {
printf("Error: %s\n", error);
}
json5_free(&root);
free(content);
}
}
#define main main__
#endif
#ifdef JSON5_DEMO
int main() {
char source5[] =
" // comments\n" /* json5 sample */
" unquoted: 'and you can quote me on that',\n"
" singleQuotes: 'I can use \"double quotes\" here',\n"
" lineBreaks : \"Look, Mom! \\\n"
"No \\n's!\",\n"
" hexadecimal: 0x100,\n"
" leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n"
" positiveSign: +1,\n"
" trailingComma: 'in objects', andIn: ['arrays', ],\n"
" \"backwardsCompatible\": \"with JSON\",\n"
""
" ip = \"127.0.0.1\"\n" /* sjson sample */
" port = 8888\n"
""
" /* comment //nested comment*/\n" /* tests */
" // comment /*nested comment*/\n"
" nil: null,"
" \"+lšctžýáíé=:\": true,,,,"
" huge: 2.2239333e5, "
" array: [+1,2,-3,4,5], "
" hello: 'world /*comment in string*/ //again', "
" abc: 42.67, def: false, "
" children : { a: 1, b: 2, },"
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],"
""
" multiline: `this is\n"
"a multiline string\n"
"yeah`"
"}\n";
json5 root = { 0 };
char *error = json5_parse(&root, source5, 0);
if( error ) {
printf("Error: %s\n", error);
} else {
json5_write(stdout, &root);
}
json5_free(&root);
}
#define main main__
#endif
#endif // JSON5_C
// JSON5 + SJSON parser module
//
// License:
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
// No warranty is implied, use at your own risk.
//
// Credits:
// r-lyeh (fork)
// Dominik Madarasz (@zaklaus) (original code)
#ifndef JSON5_H
#define JSON5_H
#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)
#endif
#include <stdint.h>
#include <stdio.h>
typedef enum json5_type {
JSON5_UNDEFINED, // 0
JSON5_NULL, // 1
JSON5_BOOL, // 2
JSON5_OBJECT, // 3
JSON5_STRING, // 4
JSON5_ARRAY, // 5
JSON5_INTEGER, // 6
JSON5_REAL, // 7
} json5_type;
typedef struct json5 {
char* name;
#ifdef NDEBUG
unsigned type : 3;
#else
json5_type type;
#endif
unsigned count : 29;
union {
struct json5* array;
struct json5* nodes;
int64_t integer;
double real;
char* string;
int boolean;
};
} json5;
char* json5_parse(json5 *root, char *source, int flags);
void json5_write(FILE *fp, const json5 *root);
void json5_free(json5 *root);
#endif // JSON5_H
// json5 ----------------------------------------------------------------------
#ifdef JSON5_C
//#pragma once
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
char *json5__trim(char *p) {
while (*p) {
/**/ if( isspace(*p) ) ++p;
else if( p[0] == '/' && p[1] == '*' ) { // skip C comment
for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {}
if( *p ) p += 2;
}
else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment
for( p += 2; *p && p[0] != '\n'; ++p) {}
if( *p ) ++p;
}
else break;
}
return p;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code);
char *json5__parse_string(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( *p == '"' || *p == '\'' || *p == '`' ) {
obj->type = JSON5_STRING;
obj->string = p + 1;
char eos_char = *p, *b = obj->string, *e = b;
while (*e) {
/**/ if( *e == '\\' && (e[1] == eos_char) ) ++e;
else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' ';
else if( *e == eos_char ) break;
++e;
}
*e = '\0';
return p = e + 1;
}
//JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_object(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( 1 /* *p == '{' */ ) { /* <-- for SJSON */
int skip = *p == '{'; /* <-- for SJSON */
obj->type = JSON5_OBJECT;
obj->nodes = 0;
obj->count = 0;
while (*p) {
json5 node = { 0 };
do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' );
if( *p == '}' ) {
++p;
break;
}
// @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)
node.name = p;
do {
++p;
} while (*p && (isalnum(*p) || *p == '_' || *p == '$' || *p == '.') ); // also || is_unicode(p)
char *e = p;
p = json5__trim(p);
*e = '\0';
}
else { //if( *p == '"' || *p == '\'' || *p == '`' ) {
char *ps = json5__parse_string(&node, p, err_code);
if( !ps ) {
return NULL;
}
p = ps;
node.name = node.string;
p = json5__trim(p);
}
// @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
p = json5__trim(p + 1);
p = json5__parse_value(&node, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( node.type != JSON5_UNDEFINED ) {
array_push(obj->nodes, node);
++obj->count;
}
if( *p == '}') { ++p; break; }
}
return p;
}
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code) {
assert(obj && p);
p = json5__trim(p);
char *is_string = json5__parse_string(obj, p, err_code);
if( is_string ) {
p = is_string;
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '{' ) {
p = json5__parse_object( obj, p, err_code );
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '[' ) {
obj->type = JSON5_ARRAY;
obj->array = 0;
obj->count = 0;
while (*p) {
json5 elem = { 0 };
do { p = json5__trim(p + 1); } while( *p == ',' );
if( *p == ']') { ++p; break; }
p = json5__parse_value(&elem, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( elem.type != JSON5_UNDEFINED ) {
array_push(obj->array, elem);
++obj->count;
}
if (*p == ']') { ++p; break; }
}
}
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 int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 };
for( int i = 0; labels[i]; ++i ) {
if( !strncmp(p, labels[i], lenghts[i] ) ) {
p += lenghts[i];
#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;
#else
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN;
#endif
else if( i >= 1 ) obj->type = JSON5_BOOL, obj->boolean = i <= 2;
else obj->type = JSON5_NULL;
break;
}
}
if( obj->type == JSON5_UNDEFINED ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
}
else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) {
char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0;
while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) {
is_hex |= (*p | 32) == 'x';
is_dbl |= *p == '.';
*buf++ = *p++;
}
obj->type = is_dbl ? JSON5_REAL : JSON5_INTEGER;
long long unsigned int llu;
long long int lli;
/**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real );
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 {
return NULL;
}
return p;
}
char *json5_parse(json5 *root, char *p, int flags) {
char *err_code = "";
*root = (json5) {0};
if( p && p[0] ) {
p = json5__trim(p);
if( *p == '[' ) { /* <-- for SJSON */
json5__parse_value(root, p, &err_code);
} else {
json5__parse_object(root, p, &err_code); /* <-- for SJSON */
}
} else {
root->type = JSON5_OBJECT;
}
return err_code[0] ? err_code : 0;
}
void json5_free(json5 *root) {
if( root->type == JSON5_ARRAY && root->array ) {
for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) {
json5_free(root->array + i);
}
array_free(root->array);
}
if( root->type == JSON5_OBJECT && root->nodes ) {
for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) {
json5_free(root->nodes + i);
}
array_free(root->nodes);
}
*root = (json5) {0}; // needed?
}
void json5_write(FILE *fp, const json5 *o) {
static __thread int indent = 0;
int tabs = 1; // 0,1,2,4,8
if( o->name ) {
fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":");
}
/**/ 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_INTEGER ) fprintf(fp, "%lld", (long long int)o->integer);
else if( o->type == JSON5_REAL ) {
/**/ 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 fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness
}
#if 0
else if( o->type == JSON5_STRING ) { // write (escaped) string
char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256];
for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i];
const char *b = o->string, *e = strpbrk(b, chars), *sep = "\"";
while( e ) {
fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] );
e = strpbrk( b = e + 1, chars);
sep = "";
}
fprintf(fp, "%s%s\"", sep, b);
}
#else
else if( o->type == JSON5_STRING ) { // write string
fprintf(fp, "\"%s\"", o->string);
}
#endif
else if( o->type == JSON5_ARRAY ) {
const char *sep = "";
fprintf(fp, "%s", tabs ? "[ " : "[");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ", " : ",";
json5_write(fp, o->array + i);
}
fprintf(fp, "%s", tabs ? " ]" : "]");
}
else if( o->type == JSON5_OBJECT ) {
const char *sep = "";
fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":"");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ",";
json5_write(fp, o->nodes + i);
}
fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, "");
} else {
char p[16] = {0};
JSON5_ASSERT; /* "json5_error_invalid_value"; */
}
}
#ifdef JSON5_BENCH
#include <time.h>
int main() {
// https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/
char *content = 0;
for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) {
fseek(fp, 0L, SEEK_END);
size_t pos = ftell(fp);
fseek(fp, 0L, SEEK_SET);
content = (char*)malloc( pos + 1 );
fread(content, 1, pos, fp);
content[pos] = 0;
}
if( content ) {
clock_t start = clock();
json5 root = {0};
char *error = json5_parse(&root, content, 0);
clock_t end = clock();
double delta = ( end - start ) / (double)CLOCKS_PER_SEC;
if( !error ) {
printf("Parsing time: %.3fms\n", delta*1000);
printf("Total nodes: %d\n", array_count(root.array));
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[2].string);
} else {
printf("Error: %s\n", error);
}
json5_free(&root);
free(content);
}
}
#define main main__
#endif
#ifdef JSON5_DEMO
int main() {
char source5[] =
" // comments\n" /* json5 sample */
" unquoted: 'and you can quote me on that',\n"
" singleQuotes: 'I can use \"double quotes\" here',\n"
" lineBreaks : \"Look, Mom! \\\n"
"No \\n's!\",\n"
" hexadecimal: 0x100,\n"
" leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n"
" positiveSign: +1,\n"
" trailingComma: 'in objects', andIn: ['arrays', ],\n"
" \"backwardsCompatible\": \"with JSON\",\n"
""
" ip = \"127.0.0.1\"\n" /* sjson sample */
" port = 8888\n"
""
" /* comment //nested comment*/\n" /* tests */
" // comment /*nested comment*/\n"
" nil: null,"
" \"+lšctžýáíé=:\": true,,,,"
" huge: 2.2239333e5, "
" array: [+1,2,-3,4,5], "
" hello: 'world /*comment in string*/ //again', "
" abc: 42.67, def: false, "
" children : { a: 1, b: 2, },"
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],"
""
" multiline: `this is\n"
"a multiline string\n"
"yeah`"
"}\n";
json5 root = { 0 };
char *error = json5_parse(&root, source5, 0);
if( error ) {
printf("Error: %s\n", error);
} else {
json5_write(stdout, &root);
}
json5_free(&root);
}
#define main main__
#endif
#endif // JSON5_C

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,275 +1,275 @@
// lite editor, platform details
// - rlyeh, public domain
#define LT_DATAPATH "/lite"
#define lt_assert(x) ASSERT(x)
#define lt_realpath(p, q) file_pathabs(p)
#define lt_realpath_free(p)
#define lt_malloc(n) MALLOC(n)
#define lt_calloc(n,m) CALLOC(n,m)
#define lt_free(p) FREE(p)
#define lt_memcpy(d,s,c) memcpy(d,s,c)
#define lt_memset(p,ch,c) memset(p,ch,c)
#define lt_time_ms() time_ms()
#define lt_sleep_ms(ms) sleep_ms(ms)
#define lt_getclipboard(w) window_clipboard()
#define lt_setclipboard(w,s) window_setclipboard(s)
#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_setwindowtitle(t) //window_title(t)
#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_prompt(msg,title) ifndef(win32, 0, (MessageBoxA(0, msg, title, MB_YESNO | MB_ICONWARNING) == IDYES))
unsigned lt_events = ~0u;
int lt_mx = 0, lt_my = 0, lt_wx = 0, lt_wy = 0, lt_ww = 0, lt_wh = 0;
typedef struct lt_surface {
unsigned w, h;
void *pixels;
texture_t t;
} lt_surface;
typedef struct lt_rect {
int x, y, width, height;
} lt_rect;
lt_surface *lt_getsurface(void *window) {
static lt_surface s = {0};
return &s;
}
void lt_updatesurfacerects(lt_surface *s, lt_rect* rects, unsigned count) {
if(0)
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 + (rects[i].height-1)) * s->w), 0xFF, rects[i].width*4 );
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 + (rects[i].width-1) + y * s->w ] = 0xFFFFFFFF;
}
}
// update contents
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 rencache_invalidate(void);
int lt_resizesurface(lt_surface *s, int ww, int wh) {
s->w = ww, s->h = wh;
if( s->t.id == 0 || s->w != s->t.w || s->h != s->t.h ) {
// invalidate tiles
ren_set_clip_rect( (lt_rect) { 0, 0, s->w, s->h } );
rencache_invalidate();
// texture clear
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);
memset(s->pixels, 0, s->w * s->h * 4);
// texture update
lt_updatesurfacerects(s,0,0);
return 1; // resized
}
return 0; // unchanged
}
void *lt_load_file(const char *filename, int *size) {
int datalen; char *data = file_load(filename, &datalen);
if( !data || !datalen ) {
filename = (const char *)file_normalize(filename);
if( strbegi(filename, app_path()) ) filename += strlen(app_path());
data = vfs_load(filename, &datalen);
}
if (size) *size = 0;
if (!data) { return NULL; }
if (size) *size = datalen;
// return permanent buffers here, as file_load() and vfs_load() do return temporaries
data = memcpy(MALLOC(datalen+1), data, datalen);
data[datalen] = 0;
return data;
}
const char* lt_button_name(int button) {
if(button == GLFW_MOUSE_BUTTON_LEFT) return "left";
if(button == GLFW_MOUSE_BUTTON_RIGHT) return "right";
if(button == GLFW_MOUSE_BUTTON_MIDDLE) return "middle";
return "?";
}
char* lt_key_name(char *dst, int key, int vk, int mods) {
// @todo: "altgr" -> left ctrl + right alt
if( key == GLFW_KEY_UP ) return "up";
if( key == GLFW_KEY_DOWN ) return "down";
if( key == GLFW_KEY_LEFT ) return "left";
if( key == GLFW_KEY_RIGHT ) return "right";
if( key == GLFW_KEY_LEFT_ALT ) return "left alt";
if( key == GLFW_KEY_RIGHT_ALT ) return "right alt";
if( key == GLFW_KEY_LEFT_SHIFT ) return "left shift";
if( key == GLFW_KEY_RIGHT_SHIFT ) return "right shift";
if( key == GLFW_KEY_LEFT_CONTROL ) return "left ctrl";
if( key == GLFW_KEY_RIGHT_CONTROL ) return "right ctrl";
if( key == GLFW_KEY_LEFT_SUPER ) return "left windows";
if( key == GLFW_KEY_RIGHT_SUPER ) return "left windows";
if( key == GLFW_KEY_MENU ) return "menu";
if( key == GLFW_KEY_ESCAPE ) return "escape";
if( key == GLFW_KEY_BACKSPACE ) return "backspace";
if( key == GLFW_KEY_ENTER ) return "return";
if( key == GLFW_KEY_KP_ENTER ) return "keypad enter";
if( key == GLFW_KEY_TAB ) return "tab";
if( key == GLFW_KEY_CAPS_LOCK ) return "capslock";
if( key == GLFW_KEY_HOME ) return "home";
if( key == GLFW_KEY_END ) return "end";
if( key == GLFW_KEY_INSERT ) return "insert";
if( key == GLFW_KEY_DELETE ) return "delete";
if( key == GLFW_KEY_PAGE_UP ) return "pageup";
if( key == GLFW_KEY_PAGE_DOWN ) return "pagedown";
if( key == GLFW_KEY_F1 ) return "f1";
if( key == GLFW_KEY_F2 ) return "f2";
if( key == GLFW_KEY_F3 ) return "f3";
if( key == GLFW_KEY_F4 ) return "f4";
if( key == GLFW_KEY_F5 ) return "f5";
if( key == GLFW_KEY_F6 ) return "f6";
if( key == GLFW_KEY_F7 ) return "f7";
if( key == GLFW_KEY_F8 ) return "f8";
if( key == GLFW_KEY_F9 ) return "f9";
if( key == GLFW_KEY_F10 ) return "f10";
if( key == GLFW_KEY_F11 ) return "f11";
if( key == GLFW_KEY_F12 ) return "f12";
const char *name = glfwGetKeyName(key, vk);
strcpy(dst, name ? name : "");
char *p = dst;
while (*p) {
*p = tolower(*p);
p++;
}
return dst;
}
void lt_globpath(struct lua_State*L, const char *path) {
unsigned j = 0;
if(!strend(path, "/")) path = (const char *)va("%s/", path);
for( dir *d = dir_open(path, ""); d; dir_close(d), d = 0 ) {
for( unsigned i = 0, end = dir_count(d); i < end; ++i ) {
char *name = dir_name(d,i);
char *last = name + strlen(name) - 1;
if( *last == '/' ) *last = '\0';
name = file_name(name);
lua_pushstring(L, name);
lua_rawseti(L, -2, ++j);
}
}
for( const char *section = strstri(path, LT_DATAPATH); section && section[sizeof(LT_DATAPATH)-1] == '/'; section = 0) {
array(char*) list = vfs_list("**");
for( unsigned i = 0, end = array_count(list); i < end; ++i ) {
char *name = list[i];
if( !strstri(name, section+1) ) continue;
lua_pushstring(L, file_name(name));
lua_rawseti(L, -2, ++j);
}
if( array_count(list) ) return;
}
}
int lt_emit_event(lua_State *L, const char *event_name, const char *event_fmt, ...) {
int count = 0;
lua_pushstring(L, event_name);
if( event_fmt ) {
va_list va;
va_start(va, event_fmt);
for( ; event_fmt[count]; ++count ) {
/**/ 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] == 's' ) { const char *s = va_arg(va, const char *); lua_pushstring(L, s); }
}
va_end(va);
}
return 1+count;
}
int printi(int i) {
// printf("clicks: %d\n", i);
return i;
}
static const char* codepoint_to_utf8_(unsigned c);
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
int rc = 0;
char buf[16];
static int prevx = 0, prevy = 0;
static unsigned clicks_time = 0, clicks = 0;
if( (lt_time_ms() - clicks_time) > 400 ) clicks = 0;
//
for( GLEQevent e; gleqNextEvent(&e); gleqFreeEvent(&e) )
if( lt_events & e.type )
switch (e.type) {
default:
break; case GLEQ_WINDOW_CLOSED: // it used to be ok. depends on window_swap() flow
rc += lt_emit_event(L, "quit", NULL);
return rc;
break; case GLEQ_WINDOW_MOVED:
lt_wx = e.pos.x;
lt_wy = e.pos.y;
break; case GLEQ_WINDOW_RESIZED:
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);
break; case GLEQ_WINDOW_REFRESH:
rc += lt_emit_event(L, "exposed", NULL);
rencache_invalidate();
break; case GLEQ_FILE_DROPPED:
rc += lt_emit_event(L, "filedropped", "sdd", e.file.paths[0], lt_mx, lt_my);
break; case GLEQ_KEY_PRESSED:
case GLEQ_KEY_REPEATED:
rc += lt_emit_event(L, "keypressed", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
goto bottom;
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));
goto bottom;
break; case GLEQ_CODEPOINT_INPUT:
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
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));
break; case GLEQ_BUTTON_RELEASED:
clicks += e.mouse.button == GLFW_MOUSE_BUTTON_1;
clicks_time = lt_time_ms();
rc += lt_emit_event(L, "mousereleased", "sdd", lt_button_name(e.mouse.button), lt_mx, lt_my);
break; case GLEQ_CURSOR_MOVED:
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);
prevx = lt_mx, prevy = lt_my;
break; case GLEQ_SCROLLED:
rc += lt_emit_event(L, "mousewheel", "f", e.scroll.y);
}
bottom:;
return rc;
}
// lite editor, platform details
// - rlyeh, public domain
#define LT_DATAPATH "/lite"
#define lt_assert(x) ASSERT(x)
#define lt_realpath(p, q) file_pathabs(p)
#define lt_realpath_free(p)
#define lt_malloc(n) MALLOC(n)
#define lt_calloc(n,m) CALLOC(n,m)
#define lt_free(p) FREE(p)
#define lt_memcpy(d,s,c) memcpy(d,s,c)
#define lt_memset(p,ch,c) memset(p,ch,c)
#define lt_time_ms() time_ms()
#define lt_sleep_ms(ms) sleep_ms(ms)
#define lt_getclipboard(w) window_clipboard()
#define lt_setclipboard(w,s) window_setclipboard(s)
#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_setwindowtitle(t) //window_title(t)
#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_prompt(msg,title) ifndef(win32, 0, (MessageBoxA(0, msg, title, MB_YESNO | MB_ICONWARNING) == IDYES))
unsigned lt_events = ~0u;
int lt_mx = 0, lt_my = 0, lt_wx = 0, lt_wy = 0, lt_ww = 0, lt_wh = 0;
typedef struct lt_surface {
unsigned w, h;
void *pixels;
texture_t t;
} lt_surface;
typedef struct lt_rect {
int x, y, width, height;
} lt_rect;
lt_surface *lt_getsurface(void *window) {
static lt_surface s = {0};
return &s;
}
void lt_updatesurfacerects(lt_surface *s, lt_rect* rects, unsigned count) {
if(0)
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 + (rects[i].height-1)) * s->w), 0xFF, rects[i].width*4 );
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 + (rects[i].width-1) + y * s->w ] = 0xFFFFFFFF;
}
}
// update contents
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 rencache_invalidate(void);
int lt_resizesurface(lt_surface *s, int ww, int wh) {
s->w = ww, s->h = wh;
if( s->t.id == 0 || s->w != s->t.w || s->h != s->t.h ) {
// invalidate tiles
ren_set_clip_rect( (lt_rect) { 0, 0, s->w, s->h } );
rencache_invalidate();
// texture clear
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);
memset(s->pixels, 0, s->w * s->h * 4);
// texture update
lt_updatesurfacerects(s,0,0);
return 1; // resized
}
return 0; // unchanged
}
void *lt_load_file(const char *filename, int *size) {
int datalen; char *data = file_load(filename, &datalen);
if( !data || !datalen ) {
filename = (const char *)file_normalize(filename);
if( strbegi(filename, app_path()) ) filename += strlen(app_path());
data = vfs_load(filename, &datalen);
}
if (size) *size = 0;
if (!data) { return NULL; }
if (size) *size = datalen;
// return permanent buffers here, as file_load() and vfs_load() do return temporaries
data = memcpy(MALLOC(datalen+1), data, datalen);
data[datalen] = 0;
return data;
}
const char* lt_button_name(int button) {
if(button == GLFW_MOUSE_BUTTON_LEFT) return "left";
if(button == GLFW_MOUSE_BUTTON_RIGHT) return "right";
if(button == GLFW_MOUSE_BUTTON_MIDDLE) return "middle";
return "?";
}
char* lt_key_name(char *dst, int key, int vk, int mods) {
// @todo: "altgr" -> left ctrl + right alt
if( key == GLFW_KEY_UP ) return "up";
if( key == GLFW_KEY_DOWN ) return "down";
if( key == GLFW_KEY_LEFT ) return "left";
if( key == GLFW_KEY_RIGHT ) return "right";
if( key == GLFW_KEY_LEFT_ALT ) return "left alt";
if( key == GLFW_KEY_RIGHT_ALT ) return "right alt";
if( key == GLFW_KEY_LEFT_SHIFT ) return "left shift";
if( key == GLFW_KEY_RIGHT_SHIFT ) return "right shift";
if( key == GLFW_KEY_LEFT_CONTROL ) return "left ctrl";
if( key == GLFW_KEY_RIGHT_CONTROL ) return "right ctrl";
if( key == GLFW_KEY_LEFT_SUPER ) return "left windows";
if( key == GLFW_KEY_RIGHT_SUPER ) return "left windows";
if( key == GLFW_KEY_MENU ) return "menu";
if( key == GLFW_KEY_ESCAPE ) return "escape";
if( key == GLFW_KEY_BACKSPACE ) return "backspace";
if( key == GLFW_KEY_ENTER ) return "return";
if( key == GLFW_KEY_KP_ENTER ) return "keypad enter";
if( key == GLFW_KEY_TAB ) return "tab";
if( key == GLFW_KEY_CAPS_LOCK ) return "capslock";
if( key == GLFW_KEY_HOME ) return "home";
if( key == GLFW_KEY_END ) return "end";
if( key == GLFW_KEY_INSERT ) return "insert";
if( key == GLFW_KEY_DELETE ) return "delete";
if( key == GLFW_KEY_PAGE_UP ) return "pageup";
if( key == GLFW_KEY_PAGE_DOWN ) return "pagedown";
if( key == GLFW_KEY_F1 ) return "f1";
if( key == GLFW_KEY_F2 ) return "f2";
if( key == GLFW_KEY_F3 ) return "f3";
if( key == GLFW_KEY_F4 ) return "f4";
if( key == GLFW_KEY_F5 ) return "f5";
if( key == GLFW_KEY_F6 ) return "f6";
if( key == GLFW_KEY_F7 ) return "f7";
if( key == GLFW_KEY_F8 ) return "f8";
if( key == GLFW_KEY_F9 ) return "f9";
if( key == GLFW_KEY_F10 ) return "f10";
if( key == GLFW_KEY_F11 ) return "f11";
if( key == GLFW_KEY_F12 ) return "f12";
const char *name = glfwGetKeyName(key, vk);
strcpy(dst, name ? name : "");
char *p = dst;
while (*p) {
*p = tolower(*p);
p++;
}
return dst;
}
void lt_globpath(struct lua_State*L, const char *path) {
unsigned j = 0;
if(!strend(path, "/")) path = (const char *)va("%s/", path);
for( dir *d = dir_open(path, ""); d; dir_close(d), d = 0 ) {
for( unsigned i = 0, end = dir_count(d); i < end; ++i ) {
char *name = dir_name(d,i);
char *last = name + strlen(name) - 1;
if( *last == '/' ) *last = '\0';
name = file_name(name);
lua_pushstring(L, name);
lua_rawseti(L, -2, ++j);
}
}
for( const char *section = strstri(path, LT_DATAPATH); section && section[sizeof(LT_DATAPATH)-1] == '/'; section = 0) {
array(char*) list = vfs_list("**");
for( unsigned i = 0, end = array_count(list); i < end; ++i ) {
char *name = list[i];
if( !strstri(name, section+1) ) continue;
lua_pushstring(L, file_name(name));
lua_rawseti(L, -2, ++j);
}
if( array_count(list) ) return;
}
}
int lt_emit_event(lua_State *L, const char *event_name, const char *event_fmt, ...) {
int count = 0;
lua_pushstring(L, event_name);
if( event_fmt ) {
va_list va;
va_start(va, event_fmt);
for( ; event_fmt[count]; ++count ) {
/**/ 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] == 's' ) { const char *s = va_arg(va, const char *); lua_pushstring(L, s); }
}
va_end(va);
}
return 1+count;
}
int printi(int i) {
// printf("clicks: %d\n", i);
return i;
}
static const char* codepoint_to_utf8_(unsigned c);
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
int rc = 0;
char buf[16];
static int prevx = 0, prevy = 0;
static unsigned clicks_time = 0, clicks = 0;
if( (lt_time_ms() - clicks_time) > 400 ) clicks = 0;
//
for( GLEQevent e; gleqNextEvent(&e); gleqFreeEvent(&e) )
if( lt_events & e.type )
switch (e.type) {
default:
break; case GLEQ_WINDOW_CLOSED: // it used to be ok. depends on window_swap() flow
rc += lt_emit_event(L, "quit", NULL);
return rc;
break; case GLEQ_WINDOW_MOVED:
lt_wx = e.pos.x;
lt_wy = e.pos.y;
break; case GLEQ_WINDOW_RESIZED:
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);
break; case GLEQ_WINDOW_REFRESH:
rc += lt_emit_event(L, "exposed", NULL);
rencache_invalidate();
break; case GLEQ_FILE_DROPPED:
rc += lt_emit_event(L, "filedropped", "sdd", e.file.paths[0], lt_mx, lt_my);
break; case GLEQ_KEY_PRESSED:
case GLEQ_KEY_REPEATED:
rc += lt_emit_event(L, "keypressed", "s", lt_key_name(buf, e.keyboard.key, e.keyboard.scancode, e.keyboard.mods));
goto bottom;
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));
goto bottom;
break; case GLEQ_CODEPOINT_INPUT:
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
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));
break; case GLEQ_BUTTON_RELEASED:
clicks += e.mouse.button == GLFW_MOUSE_BUTTON_1;
clicks_time = lt_time_ms();
rc += lt_emit_event(L, "mousereleased", "sdd", lt_button_name(e.mouse.button), lt_mx, lt_my);
break; case GLEQ_CURSOR_MOVED:
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);
prevx = lt_mx, prevy = lt_my;
break; case GLEQ_SCROLLED:
rc += lt_emit_event(L, "mousewheel", "f", e.scroll.y);
}
bottom:;
return rc;
}

View File

@ -1,424 +1,424 @@
/*
* GLEQ - A basic event queue for GLFW 3
* Copyright © Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 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
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#ifndef GLEQ_HEADER_FILE
#define GLEQ_HEADER_FILE
// #include <GLFW/glfw3.h>
#ifdef GLEQ_STATIC
#define GLEQDEF static
#else
#define GLEQDEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
GLEQ_NONE = 0,
GLEQ_WINDOW_MOVED = 1<<1,
GLEQ_WINDOW_RESIZED = 1<<2,
GLEQ_WINDOW_CLOSED = 1<<3,
GLEQ_WINDOW_REFRESH = 1<<4,
GLEQ_WINDOW_FOCUSED = 1<<5,
GLEQ_WINDOW_DEFOCUSED = 1<<6,
GLEQ_WINDOW_ICONIFIED = 1<<7,
GLEQ_WINDOW_UNICONIFIED = 1<<8,
GLEQ_FRAMEBUFFER_RESIZED = 1<<9,
GLEQ_BUTTON_PRESSED = 1<<10,
GLEQ_BUTTON_RELEASED = 1<<11,
GLEQ_CURSOR_MOVED = 1<<12,
GLEQ_CURSOR_ENTERED = 1<<13,
GLEQ_CURSOR_LEFT = 1<<14,
GLEQ_SCROLLED = 1<<15,
GLEQ_KEY_PRESSED = 1<<16,
GLEQ_KEY_REPEATED = 1<<17,
GLEQ_KEY_RELEASED = 1<<18,
GLEQ_CODEPOINT_INPUT = 1<<19,
GLEQ_MONITOR_CONNECTED = 1<<20,
GLEQ_MONITOR_DISCONNECTED = 1<<21,
#if GLFW_VERSION_MINOR >= 1
GLEQ_FILE_DROPPED = 1<<22,
#endif
#if GLFW_VERSION_MINOR >= 2
GLEQ_JOYSTICK_CONNECTED = 1<<23,
GLEQ_JOYSTICK_DISCONNECTED = 1<<24,
#endif
#if GLFW_VERSION_MINOR >= 3
GLEQ_WINDOW_MAXIMIZED = 1<<25,
GLEQ_WINDOW_UNMAXIMIZED = 1<<26,
GLEQ_WINDOW_SCALE_CHANGED = 1<<27,
#endif
} GLEQtype;
typedef struct GLEQevent
{
unsigned/*GLEQtype*/ type;
union {
GLFWwindow* window;
GLFWmonitor* monitor;
int joystick;
};
union {
struct {
int x;
int y;
} pos;
struct {
int width;
int height;
} size;
struct {
double x;
double y;
} scroll;
struct {
int key;
int scancode;
int mods;
} keyboard;
struct {
int button;
int mods;
} mouse;
unsigned int codepoint;
#if GLFW_VERSION_MINOR >= 1
struct {
char** paths;
int count;
} file;
#endif
#if GLFW_VERSION_MINOR >= 3
struct {
float x;
float y;
} scale;
#endif
};
} GLEQevent;
GLEQDEF void gleqInit(void);
GLEQDEF void gleqTrackWindow(GLFWwindow* window);
GLEQDEF int gleqNextEvent(GLEQevent* event);
GLEQDEF void gleqFreeEvent(GLEQevent* event);
#ifdef __cplusplus
}
#endif
#ifdef GLEQ_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef GLEQ_CAPACITY
#define GLEQ_CAPACITY 1024
#endif
static struct
{
GLEQevent events[GLEQ_CAPACITY];
size_t head;
size_t tail;
} gleq_queue = { {0}, 0, 0 };
static char* gleq_strdup(const char* string)
{
const size_t size = strlen(string) + 1;
char* result = (char*) malloc(size);
memcpy(result, string, size);
return result;
}
static GLEQevent* gleq_new_event(void)
{
GLEQevent* event = gleq_queue.events + gleq_queue.head;
gleq_queue.head = (gleq_queue.head + 1) % GLEQ_CAPACITY;
assert(gleq_queue.head != gleq_queue.tail);
memset(event, 0, sizeof(GLEQevent));
return event;
}
static void gleq_window_pos_callback(GLFWwindow* window, int x, int y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_MOVED;
event->window = window;
event->pos.x = x;
event->pos.y = y;
}
static void gleq_window_size_callback(GLFWwindow* window, int width, int height)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_RESIZED;
event->window = window;
event->size.width = width;
event->size.height = height;
}
static void gleq_window_close_callback(GLFWwindow* window)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_CLOSED;
event->window = window;
}
static void gleq_window_refresh_callback(GLFWwindow* window)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_REFRESH;
event->window = window;
}
static void gleq_window_focus_callback(GLFWwindow* window, int focused)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (focused)
event->type = GLEQ_WINDOW_FOCUSED;
else
event->type = GLEQ_WINDOW_DEFOCUSED;
}
static void gleq_window_iconify_callback(GLFWwindow* window, int iconified)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (iconified)
event->type = GLEQ_WINDOW_ICONIFIED;
else
event->type = GLEQ_WINDOW_UNICONIFIED;
}
static void gleq_framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_FRAMEBUFFER_RESIZED;
event->window = window;
event->size.width = width;
event->size.height = height;
}
static void gleq_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->mouse.button = button;
event->mouse.mods = mods;
if (action == GLFW_PRESS)
event->type = GLEQ_BUTTON_PRESSED;
else if (action == GLFW_RELEASE)
event->type = GLEQ_BUTTON_RELEASED;
}
static void gleq_cursor_pos_callback(GLFWwindow* window, double x, double y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_CURSOR_MOVED;
event->window = window;
event->pos.x = (int) x;
event->pos.y = (int) y;
}
static void gleq_cursor_enter_callback(GLFWwindow* window, int entered)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (entered)
event->type = GLEQ_CURSOR_ENTERED;
else
event->type = GLEQ_CURSOR_LEFT;
}
static void gleq_scroll_callback(GLFWwindow* window, double x, double y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_SCROLLED;
event->window = window;
event->scroll.x = x;
event->scroll.y = y;
}
static void gleq_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->keyboard.key = key;
event->keyboard.scancode = scancode;
event->keyboard.mods = mods;
if (action == GLFW_PRESS)
event->type = GLEQ_KEY_PRESSED;
else if (action == GLFW_RELEASE)
event->type = GLEQ_KEY_RELEASED;
else if (action == GLFW_REPEAT)
event->type = GLEQ_KEY_REPEATED;
}
static void (*gleq_char_callback_prev)(GLFWwindow* window, unsigned int codepoint) = 0;
static void gleq_char_callback(GLFWwindow* window, unsigned int codepoint)
{
if( gleq_char_callback_prev )
gleq_char_callback_prev(window, codepoint);
GLEQevent* event = gleq_new_event();
event->type = GLEQ_CODEPOINT_INPUT;
event->window = window;
event->codepoint = codepoint;
}
static void gleq_monitor_callback(GLFWmonitor* monitor, int action)
{
GLEQevent* event = gleq_new_event();
event->monitor = monitor;
if (action == GLFW_CONNECTED)
event->type = GLEQ_MONITOR_CONNECTED;
else if (action == GLFW_DISCONNECTED)
event->type = GLEQ_MONITOR_DISCONNECTED;
}
#if GLFW_VERSION_MINOR >= 1
static void gleq_file_drop_callback(GLFWwindow* window, int count, const char** paths)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_FILE_DROPPED;
event->window = window;
event->file.paths = (char**) malloc(count * sizeof(char*));
event->file.count = count;
while (count--)
event->file.paths[count] = gleq_strdup(paths[count]);
}
#endif
#if GLFW_VERSION_MINOR >= 2
static void gleq_joystick_callback(int jid, int action)
{
GLEQevent* event = gleq_new_event();
event->joystick = jid;
if (action == GLFW_CONNECTED)
event->type = GLEQ_JOYSTICK_CONNECTED;
else if (action == GLFW_DISCONNECTED)
event->type = GLEQ_JOYSTICK_DISCONNECTED;
}
#endif
#if GLFW_VERSION_MINOR >= 3
static void gleq_window_maximize_callback(GLFWwindow* window, int maximized)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (maximized)
event->type = GLEQ_WINDOW_MAXIMIZED;
else
event->type = GLEQ_WINDOW_UNMAXIMIZED;
}
static void gleq_window_content_scale_callback(GLFWwindow* window, float xscale, float yscale)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->type = GLEQ_WINDOW_SCALE_CHANGED;
event->scale.x = xscale;
event->scale.y = yscale;
}
#endif
GLEQDEF void gleqInit(void)
{
glfwSetMonitorCallback(gleq_monitor_callback);
#if GLFW_VERSION_MINOR >= 2
glfwSetJoystickCallback(gleq_joystick_callback);
#endif
}
GLEQDEF void gleqTrackWindow(GLFWwindow* window)
{
glfwSetWindowPosCallback(window, gleq_window_pos_callback);
glfwSetWindowSizeCallback(window, gleq_window_size_callback);
glfwSetWindowCloseCallback(window, gleq_window_close_callback);
glfwSetWindowRefreshCallback(window, gleq_window_refresh_callback);
glfwSetWindowFocusCallback(window, gleq_window_focus_callback);
glfwSetWindowIconifyCallback(window, gleq_window_iconify_callback);
glfwSetFramebufferSizeCallback(window, gleq_framebuffer_size_callback);
glfwSetMouseButtonCallback(window, gleq_mouse_button_callback);
glfwSetCursorPosCallback(window, gleq_cursor_pos_callback);
glfwSetCursorEnterCallback(window, gleq_cursor_enter_callback);
glfwSetScrollCallback(window, gleq_scroll_callback);
glfwSetKeyCallback(window, gleq_key_callback);
gleq_char_callback_prev = //< @r-lyeh
glfwSetCharCallback(window, gleq_char_callback);
#if GLFW_VERSION_MINOR >= 1
glfwSetDropCallback(window, gleq_file_drop_callback);
#endif
#if GLFW_VERSION_MINOR >= 3
glfwSetWindowMaximizeCallback(window, gleq_window_maximize_callback);
glfwSetWindowContentScaleCallback(window, gleq_window_content_scale_callback);
#endif
}
GLEQDEF int gleqNextEvent(GLEQevent* event)
{
memset(event, 0, sizeof(GLEQevent));
if (gleq_queue.head != gleq_queue.tail)
{
*event = gleq_queue.events[gleq_queue.tail];
gleq_queue.tail = (gleq_queue.tail + 1) % GLEQ_CAPACITY;
}
return event->type != GLEQ_NONE;
}
GLEQDEF void gleqFreeEvent(GLEQevent* event)
{
#if GLFW_VERSION_MINOR >= 1
if (event->type == GLEQ_FILE_DROPPED)
{
while (event->file.count--)
free(event->file.paths[event->file.count]);
free(event->file.paths);
}
#endif
memset(event, 0, sizeof(GLEQevent));
}
#endif /* GLEQ_IMPLEMENTATION */
#endif /* GLEQ_HEADER_FILE */
/*
* GLEQ - A basic event queue for GLFW 3
* Copyright © Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 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
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#ifndef GLEQ_HEADER_FILE
#define GLEQ_HEADER_FILE
// #include <GLFW/glfw3.h>
#ifdef GLEQ_STATIC
#define GLEQDEF static
#else
#define GLEQDEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
GLEQ_NONE = 0,
GLEQ_WINDOW_MOVED = 1<<1,
GLEQ_WINDOW_RESIZED = 1<<2,
GLEQ_WINDOW_CLOSED = 1<<3,
GLEQ_WINDOW_REFRESH = 1<<4,
GLEQ_WINDOW_FOCUSED = 1<<5,
GLEQ_WINDOW_DEFOCUSED = 1<<6,
GLEQ_WINDOW_ICONIFIED = 1<<7,
GLEQ_WINDOW_UNICONIFIED = 1<<8,
GLEQ_FRAMEBUFFER_RESIZED = 1<<9,
GLEQ_BUTTON_PRESSED = 1<<10,
GLEQ_BUTTON_RELEASED = 1<<11,
GLEQ_CURSOR_MOVED = 1<<12,
GLEQ_CURSOR_ENTERED = 1<<13,
GLEQ_CURSOR_LEFT = 1<<14,
GLEQ_SCROLLED = 1<<15,
GLEQ_KEY_PRESSED = 1<<16,
GLEQ_KEY_REPEATED = 1<<17,
GLEQ_KEY_RELEASED = 1<<18,
GLEQ_CODEPOINT_INPUT = 1<<19,
GLEQ_MONITOR_CONNECTED = 1<<20,
GLEQ_MONITOR_DISCONNECTED = 1<<21,
#if GLFW_VERSION_MINOR >= 1
GLEQ_FILE_DROPPED = 1<<22,
#endif
#if GLFW_VERSION_MINOR >= 2
GLEQ_JOYSTICK_CONNECTED = 1<<23,
GLEQ_JOYSTICK_DISCONNECTED = 1<<24,
#endif
#if GLFW_VERSION_MINOR >= 3
GLEQ_WINDOW_MAXIMIZED = 1<<25,
GLEQ_WINDOW_UNMAXIMIZED = 1<<26,
GLEQ_WINDOW_SCALE_CHANGED = 1<<27,
#endif
} GLEQtype;
typedef struct GLEQevent
{
unsigned/*GLEQtype*/ type;
union {
GLFWwindow* window;
GLFWmonitor* monitor;
int joystick;
};
union {
struct {
int x;
int y;
} pos;
struct {
int width;
int height;
} size;
struct {
double x;
double y;
} scroll;
struct {
int key;
int scancode;
int mods;
} keyboard;
struct {
int button;
int mods;
} mouse;
unsigned int codepoint;
#if GLFW_VERSION_MINOR >= 1
struct {
char** paths;
int count;
} file;
#endif
#if GLFW_VERSION_MINOR >= 3
struct {
float x;
float y;
} scale;
#endif
};
} GLEQevent;
GLEQDEF void gleqInit(void);
GLEQDEF void gleqTrackWindow(GLFWwindow* window);
GLEQDEF int gleqNextEvent(GLEQevent* event);
GLEQDEF void gleqFreeEvent(GLEQevent* event);
#ifdef __cplusplus
}
#endif
#ifdef GLEQ_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef GLEQ_CAPACITY
#define GLEQ_CAPACITY 1024
#endif
static struct
{
GLEQevent events[GLEQ_CAPACITY];
size_t head;
size_t tail;
} gleq_queue = { {0}, 0, 0 };
static char* gleq_strdup(const char* string)
{
const size_t size = strlen(string) + 1;
char* result = (char*) malloc(size);
memcpy(result, string, size);
return result;
}
static GLEQevent* gleq_new_event(void)
{
GLEQevent* event = gleq_queue.events + gleq_queue.head;
gleq_queue.head = (gleq_queue.head + 1) % GLEQ_CAPACITY;
assert(gleq_queue.head != gleq_queue.tail);
memset(event, 0, sizeof(GLEQevent));
return event;
}
static void gleq_window_pos_callback(GLFWwindow* window, int x, int y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_MOVED;
event->window = window;
event->pos.x = x;
event->pos.y = y;
}
static void gleq_window_size_callback(GLFWwindow* window, int width, int height)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_RESIZED;
event->window = window;
event->size.width = width;
event->size.height = height;
}
static void gleq_window_close_callback(GLFWwindow* window)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_CLOSED;
event->window = window;
}
static void gleq_window_refresh_callback(GLFWwindow* window)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_WINDOW_REFRESH;
event->window = window;
}
static void gleq_window_focus_callback(GLFWwindow* window, int focused)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (focused)
event->type = GLEQ_WINDOW_FOCUSED;
else
event->type = GLEQ_WINDOW_DEFOCUSED;
}
static void gleq_window_iconify_callback(GLFWwindow* window, int iconified)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (iconified)
event->type = GLEQ_WINDOW_ICONIFIED;
else
event->type = GLEQ_WINDOW_UNICONIFIED;
}
static void gleq_framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_FRAMEBUFFER_RESIZED;
event->window = window;
event->size.width = width;
event->size.height = height;
}
static void gleq_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->mouse.button = button;
event->mouse.mods = mods;
if (action == GLFW_PRESS)
event->type = GLEQ_BUTTON_PRESSED;
else if (action == GLFW_RELEASE)
event->type = GLEQ_BUTTON_RELEASED;
}
static void gleq_cursor_pos_callback(GLFWwindow* window, double x, double y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_CURSOR_MOVED;
event->window = window;
event->pos.x = (int) x;
event->pos.y = (int) y;
}
static void gleq_cursor_enter_callback(GLFWwindow* window, int entered)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (entered)
event->type = GLEQ_CURSOR_ENTERED;
else
event->type = GLEQ_CURSOR_LEFT;
}
static void gleq_scroll_callback(GLFWwindow* window, double x, double y)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_SCROLLED;
event->window = window;
event->scroll.x = x;
event->scroll.y = y;
}
static void gleq_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->keyboard.key = key;
event->keyboard.scancode = scancode;
event->keyboard.mods = mods;
if (action == GLFW_PRESS)
event->type = GLEQ_KEY_PRESSED;
else if (action == GLFW_RELEASE)
event->type = GLEQ_KEY_RELEASED;
else if (action == GLFW_REPEAT)
event->type = GLEQ_KEY_REPEATED;
}
static void (*gleq_char_callback_prev)(GLFWwindow* window, unsigned int codepoint) = 0;
static void gleq_char_callback(GLFWwindow* window, unsigned int codepoint)
{
if( gleq_char_callback_prev )
gleq_char_callback_prev(window, codepoint);
GLEQevent* event = gleq_new_event();
event->type = GLEQ_CODEPOINT_INPUT;
event->window = window;
event->codepoint = codepoint;
}
static void gleq_monitor_callback(GLFWmonitor* monitor, int action)
{
GLEQevent* event = gleq_new_event();
event->monitor = monitor;
if (action == GLFW_CONNECTED)
event->type = GLEQ_MONITOR_CONNECTED;
else if (action == GLFW_DISCONNECTED)
event->type = GLEQ_MONITOR_DISCONNECTED;
}
#if GLFW_VERSION_MINOR >= 1
static void gleq_file_drop_callback(GLFWwindow* window, int count, const char** paths)
{
GLEQevent* event = gleq_new_event();
event->type = GLEQ_FILE_DROPPED;
event->window = window;
event->file.paths = (char**) malloc(count * sizeof(char*));
event->file.count = count;
while (count--)
event->file.paths[count] = gleq_strdup(paths[count]);
}
#endif
#if GLFW_VERSION_MINOR >= 2
static void gleq_joystick_callback(int jid, int action)
{
GLEQevent* event = gleq_new_event();
event->joystick = jid;
if (action == GLFW_CONNECTED)
event->type = GLEQ_JOYSTICK_CONNECTED;
else if (action == GLFW_DISCONNECTED)
event->type = GLEQ_JOYSTICK_DISCONNECTED;
}
#endif
#if GLFW_VERSION_MINOR >= 3
static void gleq_window_maximize_callback(GLFWwindow* window, int maximized)
{
GLEQevent* event = gleq_new_event();
event->window = window;
if (maximized)
event->type = GLEQ_WINDOW_MAXIMIZED;
else
event->type = GLEQ_WINDOW_UNMAXIMIZED;
}
static void gleq_window_content_scale_callback(GLFWwindow* window, float xscale, float yscale)
{
GLEQevent* event = gleq_new_event();
event->window = window;
event->type = GLEQ_WINDOW_SCALE_CHANGED;
event->scale.x = xscale;
event->scale.y = yscale;
}
#endif
GLEQDEF void gleqInit(void)
{
glfwSetMonitorCallback(gleq_monitor_callback);
#if GLFW_VERSION_MINOR >= 2
glfwSetJoystickCallback(gleq_joystick_callback);
#endif
}
GLEQDEF void gleqTrackWindow(GLFWwindow* window)
{
glfwSetWindowPosCallback(window, gleq_window_pos_callback);
glfwSetWindowSizeCallback(window, gleq_window_size_callback);
glfwSetWindowCloseCallback(window, gleq_window_close_callback);
glfwSetWindowRefreshCallback(window, gleq_window_refresh_callback);
glfwSetWindowFocusCallback(window, gleq_window_focus_callback);
glfwSetWindowIconifyCallback(window, gleq_window_iconify_callback);
glfwSetFramebufferSizeCallback(window, gleq_framebuffer_size_callback);
glfwSetMouseButtonCallback(window, gleq_mouse_button_callback);
glfwSetCursorPosCallback(window, gleq_cursor_pos_callback);
glfwSetCursorEnterCallback(window, gleq_cursor_enter_callback);
glfwSetScrollCallback(window, gleq_scroll_callback);
glfwSetKeyCallback(window, gleq_key_callback);
gleq_char_callback_prev = //< @r-lyeh
glfwSetCharCallback(window, gleq_char_callback);
#if GLFW_VERSION_MINOR >= 1
glfwSetDropCallback(window, gleq_file_drop_callback);
#endif
#if GLFW_VERSION_MINOR >= 3
glfwSetWindowMaximizeCallback(window, gleq_window_maximize_callback);
glfwSetWindowContentScaleCallback(window, gleq_window_content_scale_callback);
#endif
}
GLEQDEF int gleqNextEvent(GLEQevent* event)
{
memset(event, 0, sizeof(GLEQevent));
if (gleq_queue.head != gleq_queue.tail)
{
*event = gleq_queue.events[gleq_queue.tail];
gleq_queue.tail = (gleq_queue.tail + 1) % GLEQ_CAPACITY;
}
return event->type != GLEQ_NONE;
}
GLEQDEF void gleqFreeEvent(GLEQevent* event)
{
#if GLFW_VERSION_MINOR >= 1
if (event->type == GLEQ_FILE_DROPPED)
{
while (event->file.count--)
free(event->file.paths[event->file.count]);
free(event->file.paths);
}
#endif
memset(event, 0, sizeof(GLEQevent));
}
#endif /* GLEQ_IMPLEMENTATION */
#endif /* GLEQ_HEADER_FILE */

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

View File

@ -1,483 +1,483 @@
// file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain)
// - rlyeh, public domain
//
// changelog:
// - ported to V4K api
// - namespaced symbols
// - diverse win32 fixes
// - adaptive cols/rows
// - removed nk_begin()/nk_end() pairs
// - dangling nk_group_begin/end() pairs
// - simplified file<->media_group concept
// - minor cosmetics
#ifdef _WIN32
#include <direct.h> // _getcwd()
#else
#include <unistd.h> // getcwd()
#include <pwd.h> // getpwuid()
#define _popen popen
#define _pclose pclose
#endif
const char** old_file_list(const char *cwd, const char *masks) {
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
static __thread array(char*) list = 0;
const char *arg0 = cwd; // app_path();
int larg0 = strlen(arg0);
for( int i = 0; i < array_count(list); ++i ) {
FREE(list[i]);
}
array_resize(list, 0);//array_free(list);
for each_substring(masks,";",it) {
int recurse = !!strstr(it, "**");
#if is(win32)
char *glob = va("dir %s/b/o:n \"%s\\%s\" 2> NUL", recurse ? "/s":"", cwd, it);
#else // linux, osx
char *glob = va("find %s %s -name \"%s\" | sort", cwd, !recurse ? "-maxdepth 1":"-type f", it);
#endif
for( FILE *in = _popen(glob, "r"); in; _pclose(in), in = 0) {
char buf[1024], *line = buf;
while( fgets(buf, sizeof(buf), in) ) {
// clean up
if( strstr(line, arg0) ) line = buf + larg0;
if( !memcmp(line, "./", 2) ) line += 2;
int len = strlen(line); while( len > 0 && line[len-1] < 32 ) line[--len] = 0;
if( line[0] == '\0' ) continue;
// do not insert system folders/files
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( strstr(line, "/.") ) continue;
// insert copy
#if is(win32)
char *copy = STRDUP(line); // full path already provided
#else
// while(line[0] == '/') ++line;
char *copy = STRDUP(va("%s%s", cwd, line)); // need to prepend path
#endif
array_push(list, copy);
}
}
}
array_push(list, 0); // terminator
return (const char**)list;
}
#if 1
#define BROWSER_PRINTF(...) do {} while(0)
#else
#define BROWSER_PRINTF printf
#endif
enum browser_groups {
BROWSER_FOLDER,
BROWSER_HOME,
BROWSER_DESKTOP,
BROWSER_COMPUTER,
BROWSER_PROJECT,
BROWSER_MAXFOLDERS,
BROWSER_MAXTYPES = 64,
};
struct browser_media_group {
unsigned icon;
const char *extensions;
};
struct browser_media {
int font;
int icon_sheet;
struct nk_image custom_folders[BROWSER_MAXFOLDERS];
struct nk_image custom_files[BROWSER_MAXTYPES];
struct browser_media_group group[BROWSER_MAXTYPES];
} media = {0};
void browser_config_dir(struct nk_image icon, unsigned counter) {
if( counter < BROWSER_MAXFOLDERS ) {
media.custom_folders[ counter ] = icon;
}
}
void browser_config_type(struct nk_image icon, const char *extensions) {
static int counter = 0;
if( counter < BROWSER_MAXTYPES ) {
media.custom_files[ counter ] = icon;
media.group[ counter ].icon = counter;
media.group[ counter ].extensions = extensions;
++counter;
}
}
#define BROWSER_MAX_PATH 512
struct browser {
/* path */
char file[BROWSER_MAX_PATH]; // selection
char directory[BROWSER_MAX_PATH]; // current cwd while browsing
char home[BROWSER_MAX_PATH];
char desktop[BROWSER_MAX_PATH];
char computer[BROWSER_MAX_PATH];
char project[BROWSER_MAX_PATH]; // cwd when first invoked
/* directory content */
array(char*) files;
array(char*) directories;
size_t file_count;
size_t dir_count;
/* filtered directory content */
array(char*) ffiles;
array(char*) fdirectories;
/* view mode */
bool listing;
float zooming;
};
static struct nk_image* media_icon_for_file(const char *file) {
/* extract extension .xxx from file */
char *ext = strrchr(file, '.');
if( ext && strlen(ext) < 16 ) {
char ext_dot[16+1];
snprintf(ext_dot, 16, "%s.", ext);
/* 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) {
if( strstri(media.group[i].extensions, ext_dot) ) {
return &media.custom_files[ media.group[i].icon ];
}
}
}
// return first (default) group
return &media.custom_files[0];
}
static void browser_reload_directory_content(struct browser *browser, const char *path) {
if(path[0] == '\0') path = va("./");
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->directories); ++i) FREE(browser->directories[i]);
array_resize(browser->files, 0);
array_resize(browser->directories, 0);
BROWSER_PRINTF("searching at %s\n", path);
const char** list = old_file_list(path, "*");
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
BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) );
if( file_directory(absolute) ) {
// 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';
dir = file_name(dir); // /home/rlyeh/prj/v4k/art -> art
BROWSER_PRINTF("%s\n", dir);
if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->directories, STRDUP(dir));
} else {
const char *fname = file_name(absolute);
BROWSER_PRINTF("%s\n", fname);
if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->files, STRDUP(fname));
}
}
browser->file_count = array_count(browser->files);
browser->dir_count = array_count(browser->directories);
}
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);
browser_reload_directory_content(browser, path);
}
static void browser_init(struct browser *browser) {
memset(browser, 0, sizeof(*browser));
{
/* load files and sub-directory list */
const char *home = getenv("HOME");
#ifdef _WIN32
if (!home) home = getenv("USERPROFILE");
#else
if (!home) home = getpwuid(getuid())->pw_dir;
#endif
snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home);
snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home);
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 '/'
strcat(browser->project, "/");
}
BROWSER_PRINTF("%s\n", browser->home);
BROWSER_PRINTF("%s\n", browser->desktop);
BROWSER_PRINTF("%s\n", browser->computer);
BROWSER_PRINTF("%s\n", browser->project);
browser_chdir_and_reload_directory_content(browser, browser->project);
}
}
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->directories); ++i) FREE(browser->directories[i]);
array_free(browser->files);
array_free(browser->directories);
array_free(browser->ffiles);
array_free(browser->fdirectories);
memset(browser, 0, sizeof(*browser));
}
static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) {
int clicked = 0;
static float ratio[] = {0.25f, NK_UNDEFINED};
float spacing_x = ctx->style.window.spacing.x;
/* output path directory selector in the menubar */
ctx->style.window.spacing.x = 0;
if( windowed ) nk_menubar_begin(ctx);
{
char *d = browser->directory;
#ifdef _WIN32
char *begin = d;
#else
char *begin = d + 1;
#endif
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_end(ctx);
if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) {
browser->listing ^= 1;
}
while (*d++) {
if (*d == '/') {
*d = '\0';
if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) {
*d++ = '/'; *d = '\0';
browser_chdir_and_reload_directory_content(browser, browser->directory);
break;
}
*d = '/';
begin = d + 1;
}
}
}
if( windowed ) nk_menubar_end(ctx);
ctx->style.window.spacing.x = spacing_x;
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);
}
bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons
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
/**/ 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;
if( cols < 1 ) cols=1;
/* window layout */
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio);
if( !compact )
if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) {
nk_layout_row_dynamic(ctx, 40, 1);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->home);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->desktop);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->computer);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->project);
nk_group_end(ctx);
}
/* output directory content window */
if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) {
array(char*) *directories = &browser->directories;
array(char*) *files = &browser->files;
if( ui_filter && ui_filter[0] ) {
array_resize(browser->fdirectories, 0);
array_resize(browser->ffiles, 0);
for each_array(browser->directories,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->fdirectories, k);
for each_array(browser->files,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->ffiles, k);
directories = &browser->fdirectories;
files = &browser->ffiles;
}
int dir_count = array_count(*directories);
int file_count = array_count(*files);
int index = -1;
size_t i = 0, j = 0, k = 0;
size_t rows = 0;
size_t count = dir_count + file_count;
rows = count / cols;
for (i = 0; i <= rows; i += 1) {
if(!tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER]))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
if (nk_button_image(ctx, *icon)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
}}
if(!tiny)
{size_t n = k + cols;
nk_layout_row_dynamic(ctx, 20, (int)cols);
for (; k < count && k < n; k++) {
size_t t = k-dir_count;
/* draw one row of labels */
if (k < dir_count) {
nk_label(ctx, (*directories)[k], NK_TEXT_CENTERED);
} else {
nk_label(ctx, (*files)[t], NK_TEXT_CENTERED);
}
}}
if(tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], (*directories)[j], NK_TEXT_RIGHT))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
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]);
clicked = 1;
}
}
#if 0
bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop
bool has_popups = ui_popups(); // @fixme: move out of loop
if( !has_popups && has_focus ) {
struct nk_rect bounds = nk_widget_bounds(ctx);
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) {
char *name = j < dir_count ? (*directories)[j] : (*files)[j-dir_count];
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name);
struct stat t = {0};
if( stat( fullpath, &t ) != -1 ) {
char tooltip[256];
snprintf(tooltip, 256,
"Path: %s\n"
"Type: %lld\n" // file type and mode
"Size: %lld\n" // file size
"Owner: %lld\n" // user ID of file owner
"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
);
nk_tooltip(ctx, tooltip);
}
}
}
#endif
}}
}
if (index != -1) {
BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]);
size_t n = strlen(browser->directory);
snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]);
BROWSER_PRINTF("%s\n", browser->directory);
browser_chdir_and_reload_directory_content(browser, browser->directory);
}
nk_group_end(ctx);
}
return clicked;
}
static struct nk_image icon_load(const char *filename) {
texture_t t = texture(filename, 0);
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) {
return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell });
}
/* demo:
struct browser browser = {0};
browser_init(&browser);
browser_config_dir(nk_image, BROWSER_HOME);
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.");
// [...]
[...]
if( nk_begin(ctx, "window", ...) ) {
struct nk_rect total_space = nk_window_get_content_region(ctx);
if( browser_run(ctx, &browser, 0, total_space) ) {
puts( browser->directory );
puts( browser->file );
}
}
nk_end();
*/
// file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain)
// - rlyeh, public domain
//
// changelog:
// - ported to V4K api
// - namespaced symbols
// - diverse win32 fixes
// - adaptive cols/rows
// - removed nk_begin()/nk_end() pairs
// - dangling nk_group_begin/end() pairs
// - simplified file<->media_group concept
// - minor cosmetics
#ifdef _WIN32
#include <direct.h> // _getcwd()
#else
#include <unistd.h> // getcwd()
#include <pwd.h> // getpwuid()
#define _popen popen
#define _pclose pclose
#endif
const char** old_file_list(const char *cwd, const char *masks) {
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
static __thread array(char*) list = 0;
const char *arg0 = cwd; // app_path();
int larg0 = strlen(arg0);
for( int i = 0; i < array_count(list); ++i ) {
FREE(list[i]);
}
array_resize(list, 0);//array_free(list);
for each_substring(masks,";",it) {
int recurse = !!strstr(it, "**");
#if is(win32)
char *glob = va("dir %s/b/o:n \"%s\\%s\" 2> NUL", recurse ? "/s":"", cwd, it);
#else // linux, osx
char *glob = va("find %s %s -name \"%s\" | sort", cwd, !recurse ? "-maxdepth 1":"-type f", it);
#endif
for( FILE *in = _popen(glob, "r"); in; _pclose(in), in = 0) {
char buf[1024], *line = buf;
while( fgets(buf, sizeof(buf), in) ) {
// clean up
if( strstr(line, arg0) ) line = buf + larg0;
if( !memcmp(line, "./", 2) ) line += 2;
int len = strlen(line); while( len > 0 && line[len-1] < 32 ) line[--len] = 0;
if( line[0] == '\0' ) continue;
// do not insert system folders/files
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( strstr(line, "/.") ) continue;
// insert copy
#if is(win32)
char *copy = STRDUP(line); // full path already provided
#else
// while(line[0] == '/') ++line;
char *copy = STRDUP(va("%s%s", cwd, line)); // need to prepend path
#endif
array_push(list, copy);
}
}
}
array_push(list, 0); // terminator
return (const char**)list;
}
#if 1
#define BROWSER_PRINTF(...) do {} while(0)
#else
#define BROWSER_PRINTF printf
#endif
enum browser_groups {
BROWSER_FOLDER,
BROWSER_HOME,
BROWSER_DESKTOP,
BROWSER_COMPUTER,
BROWSER_PROJECT,
BROWSER_MAXFOLDERS,
BROWSER_MAXTYPES = 64,
};
struct browser_media_group {
unsigned icon;
const char *extensions;
};
struct browser_media {
int font;
int icon_sheet;
struct nk_image custom_folders[BROWSER_MAXFOLDERS];
struct nk_image custom_files[BROWSER_MAXTYPES];
struct browser_media_group group[BROWSER_MAXTYPES];
} media = {0};
void browser_config_dir(struct nk_image icon, unsigned counter) {
if( counter < BROWSER_MAXFOLDERS ) {
media.custom_folders[ counter ] = icon;
}
}
void browser_config_type(struct nk_image icon, const char *extensions) {
static int counter = 0;
if( counter < BROWSER_MAXTYPES ) {
media.custom_files[ counter ] = icon;
media.group[ counter ].icon = counter;
media.group[ counter ].extensions = extensions;
++counter;
}
}
#define BROWSER_MAX_PATH 512
struct browser {
/* path */
char file[BROWSER_MAX_PATH]; // selection
char directory[BROWSER_MAX_PATH]; // current cwd while browsing
char home[BROWSER_MAX_PATH];
char desktop[BROWSER_MAX_PATH];
char computer[BROWSER_MAX_PATH];
char project[BROWSER_MAX_PATH]; // cwd when first invoked
/* directory content */
array(char*) files;
array(char*) directories;
size_t file_count;
size_t dir_count;
/* filtered directory content */
array(char*) ffiles;
array(char*) fdirectories;
/* view mode */
bool listing;
float zooming;
};
static struct nk_image* media_icon_for_file(const char *file) {
/* extract extension .xxx from file */
char *ext = strrchr(file, '.');
if( ext && strlen(ext) < 16 ) {
char ext_dot[16+1];
snprintf(ext_dot, 16, "%s.", ext);
/* 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) {
if( strstri(media.group[i].extensions, ext_dot) ) {
return &media.custom_files[ media.group[i].icon ];
}
}
}
// return first (default) group
return &media.custom_files[0];
}
static void browser_reload_directory_content(struct browser *browser, const char *path) {
if(path[0] == '\0') path = va("./");
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->directories); ++i) FREE(browser->directories[i]);
array_resize(browser->files, 0);
array_resize(browser->directories, 0);
BROWSER_PRINTF("searching at %s\n", path);
const char** list = old_file_list(path, "*");
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
BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) );
if( file_directory(absolute) ) {
// 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';
dir = file_name(dir); // /home/rlyeh/prj/v4k/art -> art
BROWSER_PRINTF("%s\n", dir);
if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->directories, STRDUP(dir));
} else {
const char *fname = file_name(absolute);
BROWSER_PRINTF("%s\n", fname);
if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->files, STRDUP(fname));
}
}
browser->file_count = array_count(browser->files);
browser->dir_count = array_count(browser->directories);
}
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);
browser_reload_directory_content(browser, path);
}
static void browser_init(struct browser *browser) {
memset(browser, 0, sizeof(*browser));
{
/* load files and sub-directory list */
const char *home = getenv("HOME");
#ifdef _WIN32
if (!home) home = getenv("USERPROFILE");
#else
if (!home) home = getpwuid(getuid())->pw_dir;
#endif
snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home);
snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home);
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 '/'
strcat(browser->project, "/");
}
BROWSER_PRINTF("%s\n", browser->home);
BROWSER_PRINTF("%s\n", browser->desktop);
BROWSER_PRINTF("%s\n", browser->computer);
BROWSER_PRINTF("%s\n", browser->project);
browser_chdir_and_reload_directory_content(browser, browser->project);
}
}
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->directories); ++i) FREE(browser->directories[i]);
array_free(browser->files);
array_free(browser->directories);
array_free(browser->ffiles);
array_free(browser->fdirectories);
memset(browser, 0, sizeof(*browser));
}
static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) {
int clicked = 0;
static float ratio[] = {0.25f, NK_UNDEFINED};
float spacing_x = ctx->style.window.spacing.x;
/* output path directory selector in the menubar */
ctx->style.window.spacing.x = 0;
if( windowed ) nk_menubar_begin(ctx);
{
char *d = browser->directory;
#ifdef _WIN32
char *begin = d;
#else
char *begin = d + 1;
#endif
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_end(ctx);
if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) {
browser->listing ^= 1;
}
while (*d++) {
if (*d == '/') {
*d = '\0';
if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) {
*d++ = '/'; *d = '\0';
browser_chdir_and_reload_directory_content(browser, browser->directory);
break;
}
*d = '/';
begin = d + 1;
}
}
}
if( windowed ) nk_menubar_end(ctx);
ctx->style.window.spacing.x = spacing_x;
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);
}
bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons
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
/**/ 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;
if( cols < 1 ) cols=1;
/* window layout */
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio);
if( !compact )
if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) {
nk_layout_row_dynamic(ctx, 40, 1);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->home);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->desktop);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->computer);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->project);
nk_group_end(ctx);
}
/* output directory content window */
if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) {
array(char*) *directories = &browser->directories;
array(char*) *files = &browser->files;
if( ui_filter && ui_filter[0] ) {
array_resize(browser->fdirectories, 0);
array_resize(browser->ffiles, 0);
for each_array(browser->directories,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->fdirectories, k);
for each_array(browser->files,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->ffiles, k);
directories = &browser->fdirectories;
files = &browser->ffiles;
}
int dir_count = array_count(*directories);
int file_count = array_count(*files);
int index = -1;
size_t i = 0, j = 0, k = 0;
size_t rows = 0;
size_t count = dir_count + file_count;
rows = count / cols;
for (i = 0; i <= rows; i += 1) {
if(!tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER]))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
if (nk_button_image(ctx, *icon)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
}}
if(!tiny)
{size_t n = k + cols;
nk_layout_row_dynamic(ctx, 20, (int)cols);
for (; k < count && k < n; k++) {
size_t t = k-dir_count;
/* draw one row of labels */
if (k < dir_count) {
nk_label(ctx, (*directories)[k], NK_TEXT_CENTERED);
} else {
nk_label(ctx, (*files)[t], NK_TEXT_CENTERED);
}
}}
if(tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], (*directories)[j], NK_TEXT_RIGHT))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
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]);
clicked = 1;
}
}
#if 0
bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop
bool has_popups = ui_popups(); // @fixme: move out of loop
if( !has_popups && has_focus ) {
struct nk_rect bounds = nk_widget_bounds(ctx);
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) {
char *name = j < dir_count ? (*directories)[j] : (*files)[j-dir_count];
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name);
struct stat t = {0};
if( stat( fullpath, &t ) != -1 ) {
char tooltip[256];
snprintf(tooltip, 256,
"Path: %s\n"
"Type: %lld\n" // file type and mode
"Size: %lld\n" // file size
"Owner: %lld\n" // user ID of file owner
"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
);
nk_tooltip(ctx, tooltip);
}
}
}
#endif
}}
}
if (index != -1) {
BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]);
size_t n = strlen(browser->directory);
snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]);
BROWSER_PRINTF("%s\n", browser->directory);
browser_chdir_and_reload_directory_content(browser, browser->directory);
}
nk_group_end(ctx);
}
return clicked;
}
static struct nk_image icon_load(const char *filename) {
texture_t t = texture(filename, 0);
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) {
return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell });
}
/* demo:
struct browser browser = {0};
browser_init(&browser);
browser_config_dir(nk_image, BROWSER_HOME);
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.");
// [...]
[...]
if( nk_begin(ctx, "window", ...) ) {
struct nk_rect total_space = nk_window_get_content_region(ctx);
if( browser_run(ctx, &browser, 0, total_space) ) {
puts( browser->directory );
puts( browser->file );
}
}
nk_end();
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,409 +1,409 @@
/* Progressive Mesh type Polygon Reduction Algorithm
*
* 1998: Original version by Stan Melax (c) 1998
* Permission to use any of this code wherever you want is granted..
* Although, please do acknowledge authorship if appropriate.
*
* 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."
*
* 2020: C version by Cloud Wu (c) 2020. Licensed as MIT/PD.
*/
static inline void array_find_and_remove(array(int) arr, int v) {
for( int i = 0, end = array_count(arr); i < end; i++ )
if( arr[i] == v ) { array_erase_fast(arr, i); --end; break; }
}
#include <assert.h>
#include <math.h>
#include <stdlib.h>
struct triangle_n {
int vertex[3]; // the 3 points (id) that make this tri
vec3 normal; // unit vector othogonal to this face
};
struct vertex {
vec3 position; // location of point in euclidean space
array(int) neighbor; // adjacent vertices
array(int) face; // adjacent triangles
int id; // place of vertex in original Array
int collapse; // candidate vertex (id) for collapse
float objdist; // cached cost of collapsing edge
};
struct mesh {
struct vertex *v;
struct triangle_n *t;
int n_face;
int n_vertex;
};
// array
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 *Face(struct mesh *M, struct vertex *v, int idx) { return M->t + v->face[idx]; }
static void AddVertex(struct mesh *M, const float *v) {
int id = M->n_vertex++;
struct vertex * tmp = Vertex(M, id);
tmp->position = ptr3(v);
tmp->neighbor = NULL;
tmp->face = NULL;
tmp->id = id;
tmp->collapse = -1;
tmp->objdist = 0;
}
static void RemoveVertex(struct mesh *M, int id) {
struct vertex * v = Vertex(M, id);
ASSERT(v->id == id);
ASSERT(array_count(v->face) == 0);
for (int i=0;i<array_count(v->face);i++) {
struct vertex * nv = Vertex(M, v->face[i]);
array_find_and_remove(nv->neighbor, id);
}
v->id = -1; // invalid vertex id
array_free(v->neighbor);
array_free(v->face);
}
static void ComputeNormal(struct mesh *M, struct triangle_n *t) {
struct vertex * v0 = Vertex(M, t->vertex[0]);
struct vertex * v1 = Vertex(M, t->vertex[1]);
struct vertex * v2 = Vertex(M, t->vertex[2]);
vec3 a = sub3(v1->position, v0->position);
vec3 b = sub3(v2->position, v1->position);
t->normal = norm3(cross3(a,b));
}
static void AddNeighbor(struct mesh *M, int vid, int id) {
struct vertex *v = Vertex(M, vid);
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id)
return;
}
array_push(v->neighbor, id);
}
static void AddTriangle(struct mesh *M, const int v[3]) {
if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2])
return;
ASSERT(v[0] < M->n_vertex);
ASSERT(v[1] < M->n_vertex);
ASSERT(v[2] < M->n_vertex);
int id = M->n_face++;
struct triangle_n * tmp = Triangle(M, id);
tmp->vertex[0] = v[0];
tmp->vertex[1] = v[1];
tmp->vertex[2] = v[2];
ComputeNormal(M, tmp);
for(int i=0;i<3;i++) {
struct vertex *obj = Vertex(M, v[i]);
array_push(obj->face, id);
}
AddNeighbor(M, v[0], v[1]);
AddNeighbor(M, v[0], v[2]);
AddNeighbor(M, v[1], v[0]);
AddNeighbor(M, v[1], v[2]);
AddNeighbor(M, v[2], v[0]);
AddNeighbor(M, v[2], v[1]);
}
static int HasVertex(struct triangle_n * t, int 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) {
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id) {
for (int j=0;j<array_count(v->face);j++) {
if (HasVertex(Face(M, v, j), id))
return;
}
// remove from neighbors
array_erase_fast(v->neighbor, i);
return;
}
}
}
static void RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) {
if (v0 == NULL || v1 == NULL)
return;
RemoveIfNonNeighbor_(M, v0, v1->id);
RemoveIfNonNeighbor_(M, v1, v0->id);
}
static void RemoveTriangle(struct mesh *M, int id) {
struct triangle_n * face = Triangle(M, id);
struct vertex * v[3];
for (int i=0;i<3;i++) {
v[i] = Vertex(M, face->vertex[i]);
if (v[i]->id < 0)
v[i] = NULL;
else {
array_find_and_remove(v[i]->face, id);
}
}
RemoveIfNonNeighbor(M, v[0], v[1]);
RemoveIfNonNeighbor(M, v[1], v[2]);
RemoveIfNonNeighbor(M, v[2], v[0]);
}
static void ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) {
struct triangle_n * face = Triangle(M, faceid);
ASSERT(oldid >=0 && newid >= 0);
ASSERT(HasVertex(face, oldid));
ASSERT(!HasVertex(face, newid));
if(oldid==face->vertex[0]){
face->vertex[0]=newid;
} else if(oldid==face->vertex[1]){
face->vertex[1]=newid;
} else {
face->vertex[2]=newid;
}
struct vertex *vold = Vertex(M, oldid);
struct vertex *vnew = Vertex(M, newid);
array_find_and_remove(vold->face, faceid);
array_push(vnew->face, faceid);
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[0]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[1]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[2]));
AddNeighbor(M, face->vertex[0], face->vertex[1]);
AddNeighbor(M, face->vertex[0], face->vertex[2]);
AddNeighbor(M, face->vertex[1], face->vertex[0]);
AddNeighbor(M, face->vertex[1], face->vertex[2]);
AddNeighbor(M, face->vertex[2], face->vertex[0]);
AddNeighbor(M, face->vertex[2], face->vertex[1]);
ComputeNormal(M, face);
}
static void MeshInit(struct mesh *M, int vert_n, int tri_n) {
M->n_face = 0;
M->n_vertex = 0;
M->v = (struct vertex *)MALLOC(vert_n * sizeof(struct vertex));
M->t = (struct triangle_n *)MALLOC(tri_n * sizeof(struct triangle));
}
static void MeshFree(struct mesh *M) {
FREE(M->v);
FREE(M->t);
}
static float ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) {
// if we collapse edge uv by moving u to v then how
// much different will the model change, i.e. how much "error".
// Texture, vertex normal, and border vertex code was removed
// to keep this demo as simple as possible.
// The method of determining cost was designed in order
// to exploit small and coplanar regions for
// effective polygon reduction.
// Is is possible to add some checks here to see if "folds"
// would be generated. i.e. normal of a remaining face gets
// flipped. I never seemed to run into this problem and
// therefore never added code to detect this case.
struct vertex *v = Vertex(M, vid);
vec3 tmp = sub3(v->position, u->position);
float edgelength = len3(tmp);
float curvature=0;
// find the "sides" triangles that are on the edge uv
array(int) sides = 0;
for (int i = 0; i<array_count(u->face); i++) {
if (HasVertex(Face(M, u, i), vid)) {
array_push(sides, u->face[i]);
}
}
// use the triangle facing most away from the sides
// to determine our curvature term
for (int i = 0; i<array_count(u->face); i++) {
float mincurv=1; // curve for face i and closer side to it
for (int j = 0; j<array_count(sides); j++) {
float dotprod = dot3(Triangle(M, u->face[i])->normal,
Triangle(M, sides[j])->normal); // use dot product of face normals.
float t = (1-dotprod)/2.0f;
if (t < mincurv) {
mincurv = t;
}
}
if (mincurv > curvature)
curvature = mincurv;
}
array_free(sides);
// the more coplanar the lower the curvature term
return edgelength * curvature;
}
static void ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) {
// compute the edge collapse cost for all edges that start
// from vertex v. Since we are only interested in reducing
// the object by selecting the min cost edge at each step, we
// only cache the cost of the least cost edge at this vertex
// (in member variable collapse) as well as the value of the
// cost (in member variable objdist).
if (array_count(v->neighbor) == 0) {
// v doesn't have neighbors so it costs nothing to collapse
v->collapse=-1;
v->objdist=-0.01f;
return;
}
v->objdist = 1000000;
v->collapse=-1;
// search all neighboring edges for "least cost" edge
for (int i = 0; i<array_count(v->neighbor); i++) {
float dist = ComputeEdgeCollapseCost(M, v, v->neighbor[i]);
if(dist<v->objdist) {
v->collapse=v->neighbor[i]; // candidate for edge collapse
v->objdist=dist; // cost of the collapse
}
}
}
static void ComputeAllEdgeCollapseCosts(struct mesh *M) {
// For all the edges, compute the difference it would make
// to the model if it was collapsed. The least of these
// per vertex is cached in each vertex object.
for (int i = 0; i<M->n_vertex; i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, i));
}
}
static void Collapse(struct mesh *M, int uid, int vid) {
// Collapse the edge uv by moving vertex u onto v
// Actually remove tris on uv, then update tris that
// have u to have v, and then remove u.
struct vertex *u = Vertex(M, uid);
if(vid < 0) {
// u is a vertex all by itself so just delete it
RemoveVertex(M, uid);
return;
}
array(int) tmp = 0;
// make tmp a Array of all the neighbors of u
for (int i = 0; i<array_count(u->neighbor); i++) {
array_push(tmp, u->neighbor[i]);
}
// delete triangles on edge uv:
for( int i = array_count(u->face); i--; ) {
if (HasVertex(Face(M, u, i), vid)) {
RemoveTriangle(M, u->face[i]);
}
}
// update remaining triangles to have v instead of u
for( int i = array_count(u->face); i--; ) {
ReplaceVertex(M, u->face[i], uid, vid);
}
RemoveVertex(M, uid);
// recompute the edge collapse costs for neighboring vertices
for (int i = 0; i<array_count(tmp); i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, tmp[i]));
}
array_free(tmp);
}
static struct vertex *MinimumCostEdge(struct mesh *M) {
// Find the edge that when collapsed will affect model the least.
// This function actually returns a Vertex, the second vertex
// of the edge (collapse candidate) is stored in the vertex data.
// Serious optimization opportunity here: this function currently
// does a sequential search through an unsorted Array :-(
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
struct vertex *mn = NULL;
for (int i = 0; i<M->n_vertex; i++) {
struct vertex *v = Vertex(M, i);
if (v->id >=0) {
if (mn == NULL || v->objdist < mn->objdist) {
mn = v;
}
}
}
return mn;
}
/*
* The function ProgressiveMesh() takes a model in an "indexed face
* set" sort of way. i.e. Array of vertices and Array of triangles.
* The function then does the polygon reduction algorithm
* internally and reduces the model all the way down to 0
* vertices and then returns the order in which the
* vertices are collapsed and to which neighbor each vertex
* is collapsed to. More specifically the returned "permutation"
* indicates how to reorder your vertices so you can render
* an object by using the first n vertices (for the n
* vertex version). After permuting your vertices, the
* 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) {
struct mesh M;
MeshInit(&M, vert_n, tri_n);
// put input data into our data structures M
const char * tmp = (const char *)v;
for (int i=0;i<vert_n;i++, tmp += vert_stride ) {
AddVertex(&M, (const float *)tmp);
}
for (int i=0;i<tri_n;i++) {
AddTriangle(&M, &tri[i*3]);
}
ComputeAllEdgeCollapseCosts(&M); // cache all edge collapse costs
for (int i = vert_n-1; i>=0; i--) {
// get the next vertex to collapse
struct vertex *mn = MinimumCostEdge(&M);
// keep track of this vertex, i.e. the collapse ordering
permutation[mn->id] = i;
// keep track of vertex to which we collapse to
map[i] = mn->collapse;
// Collapse this edge
Collapse(&M, mn->id, mn->collapse);
}
// reorder the map Array based on the collapse ordering
for (int i = 0; i<vert_n; i++) {
map[i] = (map[i]==-1)?0:permutation[map[i]];
}
// The caller of this function should reorder their vertices
// according to the returned "permutation".
MeshFree(&M);
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Stan Melax
* Copyright (c) 2020 Cloud Wu
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* 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
* SOFTWARE.
*/
/* Progressive Mesh type Polygon Reduction Algorithm
*
* 1998: Original version by Stan Melax (c) 1998
* Permission to use any of this code wherever you want is granted..
* Although, please do acknowledge authorship if appropriate.
*
* 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."
*
* 2020: C version by Cloud Wu (c) 2020. Licensed as MIT/PD.
*/
static inline void array_find_and_remove(array(int) arr, int v) {
for( int i = 0, end = array_count(arr); i < end; i++ )
if( arr[i] == v ) { array_erase_fast(arr, i); --end; break; }
}
#include <assert.h>
#include <math.h>
#include <stdlib.h>
struct triangle_n {
int vertex[3]; // the 3 points (id) that make this tri
vec3 normal; // unit vector othogonal to this face
};
struct vertex {
vec3 position; // location of point in euclidean space
array(int) neighbor; // adjacent vertices
array(int) face; // adjacent triangles
int id; // place of vertex in original Array
int collapse; // candidate vertex (id) for collapse
float objdist; // cached cost of collapsing edge
};
struct mesh {
struct vertex *v;
struct triangle_n *t;
int n_face;
int n_vertex;
};
// array
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 *Face(struct mesh *M, struct vertex *v, int idx) { return M->t + v->face[idx]; }
static void AddVertex(struct mesh *M, const float *v) {
int id = M->n_vertex++;
struct vertex * tmp = Vertex(M, id);
tmp->position = ptr3(v);
tmp->neighbor = NULL;
tmp->face = NULL;
tmp->id = id;
tmp->collapse = -1;
tmp->objdist = 0;
}
static void RemoveVertex(struct mesh *M, int id) {
struct vertex * v = Vertex(M, id);
ASSERT(v->id == id);
ASSERT(array_count(v->face) == 0);
for (int i=0;i<array_count(v->face);i++) {
struct vertex * nv = Vertex(M, v->face[i]);
array_find_and_remove(nv->neighbor, id);
}
v->id = -1; // invalid vertex id
array_free(v->neighbor);
array_free(v->face);
}
static void ComputeNormal(struct mesh *M, struct triangle_n *t) {
struct vertex * v0 = Vertex(M, t->vertex[0]);
struct vertex * v1 = Vertex(M, t->vertex[1]);
struct vertex * v2 = Vertex(M, t->vertex[2]);
vec3 a = sub3(v1->position, v0->position);
vec3 b = sub3(v2->position, v1->position);
t->normal = norm3(cross3(a,b));
}
static void AddNeighbor(struct mesh *M, int vid, int id) {
struct vertex *v = Vertex(M, vid);
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id)
return;
}
array_push(v->neighbor, id);
}
static void AddTriangle(struct mesh *M, const int v[3]) {
if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2])
return;
ASSERT(v[0] < M->n_vertex);
ASSERT(v[1] < M->n_vertex);
ASSERT(v[2] < M->n_vertex);
int id = M->n_face++;
struct triangle_n * tmp = Triangle(M, id);
tmp->vertex[0] = v[0];
tmp->vertex[1] = v[1];
tmp->vertex[2] = v[2];
ComputeNormal(M, tmp);
for(int i=0;i<3;i++) {
struct vertex *obj = Vertex(M, v[i]);
array_push(obj->face, id);
}
AddNeighbor(M, v[0], v[1]);
AddNeighbor(M, v[0], v[2]);
AddNeighbor(M, v[1], v[0]);
AddNeighbor(M, v[1], v[2]);
AddNeighbor(M, v[2], v[0]);
AddNeighbor(M, v[2], v[1]);
}
static int HasVertex(struct triangle_n * t, int 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) {
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id) {
for (int j=0;j<array_count(v->face);j++) {
if (HasVertex(Face(M, v, j), id))
return;
}
// remove from neighbors
array_erase_fast(v->neighbor, i);
return;
}
}
}
static void RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) {
if (v0 == NULL || v1 == NULL)
return;
RemoveIfNonNeighbor_(M, v0, v1->id);
RemoveIfNonNeighbor_(M, v1, v0->id);
}
static void RemoveTriangle(struct mesh *M, int id) {
struct triangle_n * face = Triangle(M, id);
struct vertex * v[3];
for (int i=0;i<3;i++) {
v[i] = Vertex(M, face->vertex[i]);
if (v[i]->id < 0)
v[i] = NULL;
else {
array_find_and_remove(v[i]->face, id);
}
}
RemoveIfNonNeighbor(M, v[0], v[1]);
RemoveIfNonNeighbor(M, v[1], v[2]);
RemoveIfNonNeighbor(M, v[2], v[0]);
}
static void ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) {
struct triangle_n * face = Triangle(M, faceid);
ASSERT(oldid >=0 && newid >= 0);
ASSERT(HasVertex(face, oldid));
ASSERT(!HasVertex(face, newid));
if(oldid==face->vertex[0]){
face->vertex[0]=newid;
} else if(oldid==face->vertex[1]){
face->vertex[1]=newid;
} else {
face->vertex[2]=newid;
}
struct vertex *vold = Vertex(M, oldid);
struct vertex *vnew = Vertex(M, newid);
array_find_and_remove(vold->face, faceid);
array_push(vnew->face, faceid);
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[0]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[1]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[2]));
AddNeighbor(M, face->vertex[0], face->vertex[1]);
AddNeighbor(M, face->vertex[0], face->vertex[2]);
AddNeighbor(M, face->vertex[1], face->vertex[0]);
AddNeighbor(M, face->vertex[1], face->vertex[2]);
AddNeighbor(M, face->vertex[2], face->vertex[0]);
AddNeighbor(M, face->vertex[2], face->vertex[1]);
ComputeNormal(M, face);
}
static void MeshInit(struct mesh *M, int vert_n, int tri_n) {
M->n_face = 0;
M->n_vertex = 0;
M->v = (struct vertex *)MALLOC(vert_n * sizeof(struct vertex));
M->t = (struct triangle_n *)MALLOC(tri_n * sizeof(struct triangle));
}
static void MeshFree(struct mesh *M) {
FREE(M->v);
FREE(M->t);
}
static float ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) {
// if we collapse edge uv by moving u to v then how
// much different will the model change, i.e. how much "error".
// Texture, vertex normal, and border vertex code was removed
// to keep this demo as simple as possible.
// The method of determining cost was designed in order
// to exploit small and coplanar regions for
// effective polygon reduction.
// Is is possible to add some checks here to see if "folds"
// would be generated. i.e. normal of a remaining face gets
// flipped. I never seemed to run into this problem and
// therefore never added code to detect this case.
struct vertex *v = Vertex(M, vid);
vec3 tmp = sub3(v->position, u->position);
float edgelength = len3(tmp);
float curvature=0;
// find the "sides" triangles that are on the edge uv
array(int) sides = 0;
for (int i = 0; i<array_count(u->face); i++) {
if (HasVertex(Face(M, u, i), vid)) {
array_push(sides, u->face[i]);
}
}
// use the triangle facing most away from the sides
// to determine our curvature term
for (int i = 0; i<array_count(u->face); i++) {
float mincurv=1; // curve for face i and closer side to it
for (int j = 0; j<array_count(sides); j++) {
float dotprod = dot3(Triangle(M, u->face[i])->normal,
Triangle(M, sides[j])->normal); // use dot product of face normals.
float t = (1-dotprod)/2.0f;
if (t < mincurv) {
mincurv = t;
}
}
if (mincurv > curvature)
curvature = mincurv;
}
array_free(sides);
// the more coplanar the lower the curvature term
return edgelength * curvature;
}
static void ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) {
// compute the edge collapse cost for all edges that start
// from vertex v. Since we are only interested in reducing
// the object by selecting the min cost edge at each step, we
// only cache the cost of the least cost edge at this vertex
// (in member variable collapse) as well as the value of the
// cost (in member variable objdist).
if (array_count(v->neighbor) == 0) {
// v doesn't have neighbors so it costs nothing to collapse
v->collapse=-1;
v->objdist=-0.01f;
return;
}
v->objdist = 1000000;
v->collapse=-1;
// search all neighboring edges for "least cost" edge
for (int i = 0; i<array_count(v->neighbor); i++) {
float dist = ComputeEdgeCollapseCost(M, v, v->neighbor[i]);
if(dist<v->objdist) {
v->collapse=v->neighbor[i]; // candidate for edge collapse
v->objdist=dist; // cost of the collapse
}
}
}
static void ComputeAllEdgeCollapseCosts(struct mesh *M) {
// For all the edges, compute the difference it would make
// to the model if it was collapsed. The least of these
// per vertex is cached in each vertex object.
for (int i = 0; i<M->n_vertex; i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, i));
}
}
static void Collapse(struct mesh *M, int uid, int vid) {
// Collapse the edge uv by moving vertex u onto v
// Actually remove tris on uv, then update tris that
// have u to have v, and then remove u.
struct vertex *u = Vertex(M, uid);
if(vid < 0) {
// u is a vertex all by itself so just delete it
RemoveVertex(M, uid);
return;
}
array(int) tmp = 0;
// make tmp a Array of all the neighbors of u
for (int i = 0; i<array_count(u->neighbor); i++) {
array_push(tmp, u->neighbor[i]);
}
// delete triangles on edge uv:
for( int i = array_count(u->face); i--; ) {
if (HasVertex(Face(M, u, i), vid)) {
RemoveTriangle(M, u->face[i]);
}
}
// update remaining triangles to have v instead of u
for( int i = array_count(u->face); i--; ) {
ReplaceVertex(M, u->face[i], uid, vid);
}
RemoveVertex(M, uid);
// recompute the edge collapse costs for neighboring vertices
for (int i = 0; i<array_count(tmp); i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, tmp[i]));
}
array_free(tmp);
}
static struct vertex *MinimumCostEdge(struct mesh *M) {
// Find the edge that when collapsed will affect model the least.
// This function actually returns a Vertex, the second vertex
// of the edge (collapse candidate) is stored in the vertex data.
// Serious optimization opportunity here: this function currently
// does a sequential search through an unsorted Array :-(
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
struct vertex *mn = NULL;
for (int i = 0; i<M->n_vertex; i++) {
struct vertex *v = Vertex(M, i);
if (v->id >=0) {
if (mn == NULL || v->objdist < mn->objdist) {
mn = v;
}
}
}
return mn;
}
/*
* The function ProgressiveMesh() takes a model in an "indexed face
* set" sort of way. i.e. Array of vertices and Array of triangles.
* The function then does the polygon reduction algorithm
* internally and reduces the model all the way down to 0
* vertices and then returns the order in which the
* vertices are collapsed and to which neighbor each vertex
* is collapsed to. More specifically the returned "permutation"
* indicates how to reorder your vertices so you can render
* an object by using the first n vertices (for the n
* vertex version). After permuting your vertices, the
* 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) {
struct mesh M;
MeshInit(&M, vert_n, tri_n);
// put input data into our data structures M
const char * tmp = (const char *)v;
for (int i=0;i<vert_n;i++, tmp += vert_stride ) {
AddVertex(&M, (const float *)tmp);
}
for (int i=0;i<tri_n;i++) {
AddTriangle(&M, &tri[i*3]);
}
ComputeAllEdgeCollapseCosts(&M); // cache all edge collapse costs
for (int i = vert_n-1; i>=0; i--) {
// get the next vertex to collapse
struct vertex *mn = MinimumCostEdge(&M);
// keep track of this vertex, i.e. the collapse ordering
permutation[mn->id] = i;
// keep track of vertex to which we collapse to
map[i] = mn->collapse;
// Collapse this edge
Collapse(&M, mn->id, mn->collapse);
}
// reorder the map Array based on the collapse ordering
for (int i = 0; i<vert_n; i++) {
map[i] = (map[i]==-1)?0:permutation[map[i]];
}
// The caller of this function should reorder their vertices
// according to the returned "permutation".
MeshFree(&M);
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Stan Melax
* Copyright (c) 2020 Cloud Wu
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* 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
* SOFTWARE.
*/

File diff suppressed because it is too large Load Diff

View File

@ -1,373 +1,373 @@
/* rpmalloc.h - Memory allocator - Public Domain - 2016 Mattias Jansson
*
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
* The latest source code is always available at
*
* https://github.com/mjansson/rpmalloc
*
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
*
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__clang__) || defined(__GNUC__)
# define RPMALLOC_EXPORT __attribute__((visibility("default")))
# define RPMALLOC_ALLOCATOR
# if (defined(__clang_major__) && (__clang_major__ < 4)) || (defined(__GNUC__) && defined(ENABLE_PRELOAD) && ENABLE_PRELOAD)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
# else
# define RPMALLOC_ATTRIB_MALLOC __attribute__((__malloc__))
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size) __attribute__((alloc_size(size)))
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size) __attribute__((alloc_size(count, size)))
# endif
# define RPMALLOC_CDECL
#elif defined(_MSC_VER)
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR __declspec(allocator) __declspec(restrict)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL __cdecl
#else
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL
#endif
//! Define RPMALLOC_CONFIGURABLE to enable configuring sizes. Will introduce
// a very small overhead due to some size calculations not being compile time constants
#ifndef RPMALLOC_CONFIGURABLE
#define RPMALLOC_CONFIGURABLE 0
#endif
//! 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
#ifndef RPMALLOC_FIRST_CLASS_HEAPS
#define RPMALLOC_FIRST_CLASS_HEAPS 0
#endif
//! Flag to rpaligned_realloc to not preserve content in reallocation
#define RPMALLOC_NO_PRESERVE 1
//! 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
// a new block).
#define RPMALLOC_GROW_OR_FAIL 2
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)
size_t mapped;
//! Peak amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped_peak;
//! Current amount of memory in global caches for small and medium sizes (<32KiB)
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)
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)
size_t huge_alloc_peak;
//! Total amount of memory mapped since initialization (only if ENABLE_STATISTICS=1)
size_t mapped_total;
//! Total amount of memory unmapped since initialization (only if ENABLE_STATISTICS=1)
size_t unmapped_total;
} rpmalloc_global_statistics_t;
typedef struct rpmalloc_thread_statistics_t {
//! Current number of bytes available in thread size class caches for small and medium sizes (<32KiB)
size_t sizecache;
//! Current number of bytes available in thread span caches for small and medium sizes (<32KiB)
size_t spancache;
//! Total number of bytes transitioned from thread cache to global cache (only if ENABLE_STATISTICS=1)
size_t thread_to_global;
//! Total number of bytes transitioned from global cache to thread cache (only if ENABLE_STATISTICS=1)
size_t global_to_thread;
//! Per span count statistics (only if ENABLE_STATISTICS=1)
struct {
//! Currently used number of spans
size_t current;
//! High water mark of spans used
size_t peak;
//! Number of spans transitioned to global cache
size_t to_global;
//! Number of spans transitioned from global cache
size_t from_global;
//! Number of spans transitioned to thread cache
size_t to_cache;
//! Number of spans transitioned from thread cache
size_t from_cache;
//! Number of spans transitioned to reserved state
size_t to_reserved;
//! Number of spans transitioned from reserved state
size_t from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} span_use[64];
//! Per size class statistics (only if ENABLE_STATISTICS=1)
struct {
//! Current number of allocations
size_t alloc_current;
//! Peak number of allocations
size_t alloc_peak;
//! Total number of allocations
size_t alloc_total;
//! Total number of frees
size_t free_total;
//! Number of spans transitioned to cache
size_t spans_to_cache;
//! Number of spans transitioned from cache
size_t spans_from_cache;
//! Number of spans transitioned from reserved state
size_t spans_from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} size_use[128];
} rpmalloc_thread_statistics_t;
typedef struct rpmalloc_config_t {
//! 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.
// 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
// 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
// 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
// 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
// multiple threads simultaneously.
void* (*memory_map)(size_t size, size_t* offset);
//! 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
// 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,
// 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
// else the default implementation will be used for both. This function must be thread
// safe, it can be called by multiple threads simultaneously.
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()
// if this is not set.
void (*error_callback)(const char* message);
//! 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
// 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
// the default system memory map function is used (memory_map callback is not set).
int (*map_fail_callback)(size_t size);
//! 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.
// Used if RPMALLOC_CONFIGURABLE is defined to 1, otherwise system page size is used.
size_t page_size;
//! 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
// is defined to 1.
size_t span_size;
//! 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
// 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
// aligned to a multiple of spans that match memory page size in case of huge pages.
size_t span_map_count;
//! 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.
// 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
// allocator.
// 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
int enable_huge_pages;
//! Respectively allocated pages and huge allocated pages names for systems
// supporting it to be able to distinguish among anonymous regions.
const char *page_name;
const char *huge_page_name;
} rpmalloc_config_t;
//! Initialize allocator with default configuration
RPMALLOC_EXPORT int
rpmalloc_initialize(void);
//! Initialize allocator with given configuration
RPMALLOC_EXPORT int
rpmalloc_initialize_config(const rpmalloc_config_t* config);
//! Get allocator configuration
RPMALLOC_EXPORT const rpmalloc_config_t*
rpmalloc_config(void);
//! Finalize allocator
RPMALLOC_EXPORT void
rpmalloc_finalize(void);
//! Initialize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_initialize(void);
//! Finalize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_finalize(int release_caches);
//! Perform deferred deallocations pending for the calling thread heap
RPMALLOC_EXPORT void
rpmalloc_thread_collect(void);
//! Query if allocator is initialized for calling thread
RPMALLOC_EXPORT int
rpmalloc_is_thread_initialized(void);
//! Get per-thread statistics
RPMALLOC_EXPORT void
rpmalloc_thread_statistics(rpmalloc_thread_statistics_t* stats);
//! Get global statistics
RPMALLOC_EXPORT void
rpmalloc_global_statistics(rpmalloc_global_statistics_t* stats);
//! Dump all statistics in human readable format to file (should be a FILE*)
RPMALLOC_EXPORT void
rpmalloc_dump_statistics(void* file);
//! Allocate a memory block of at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1);
//! Free the given memory block
RPMALLOC_EXPORT void
rpfree(void* ptr);
//! Allocate a memory block of at least the given size and zero initialize it
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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,
// with optional control flags (see RPMALLOC_NO_PRESERVE).
// 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
// internals is that this must also be strictly less than the span size (default 64KiB)
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);
//! 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*),
// 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)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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.
// 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
// internals is that this must also be strictly less than the span size (default 64KiB)
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);
//! 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*),
// 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)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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.
// 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
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT int
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)
RPMALLOC_EXPORT size_t
rpmalloc_usable_size(void* ptr);
//! Dummy empty function for forcing linker symbol inclusion
RPMALLOC_EXPORT void
rpmalloc_linker_reference(void);
#if RPMALLOC_FIRST_CLASS_HEAPS
//! Heap type
typedef struct heap_t rpmalloc_heap_t;
//! 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
// thread will call heap functions for a given heap at any given time, no functions are thread safe.
RPMALLOC_EXPORT rpmalloc_heap_t*
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).
// Releasing a heap will enable it to be reused by other threads. Safe to pass a null pointer.
RPMALLOC_EXPORT void
rpmalloc_heap_release(rpmalloc_heap_t* heap);
//! Allocate a memory block of at least the given size using the given heap.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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
// 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
// internals is that this must also be strictly less than the span size (default 64KiB).
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);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it.
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);
//! 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*),
// 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).
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);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function.
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);
//! 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.
// 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
// the span size (default 64KiB).
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);
//! Free the given memory block from the given heap. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT void
rpmalloc_heap_free(rpmalloc_heap_t* heap, void* ptr);
//! Free all memory allocated by the heap
RPMALLOC_EXPORT void
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
// 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.
RPMALLOC_EXPORT void
rpmalloc_heap_thread_set_current(rpmalloc_heap_t* heap);
//! Returns which heap the given pointer is allocated on
RPMALLOC_EXPORT rpmalloc_heap_t*
rpmalloc_get_heap_for_ptr(void* ptr);
#endif
#ifdef __cplusplus
}
#endif
/* rpmalloc.h - Memory allocator - Public Domain - 2016 Mattias Jansson
*
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
* The latest source code is always available at
*
* https://github.com/mjansson/rpmalloc
*
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
*
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__clang__) || defined(__GNUC__)
# define RPMALLOC_EXPORT __attribute__((visibility("default")))
# define RPMALLOC_ALLOCATOR
# if (defined(__clang_major__) && (__clang_major__ < 4)) || (defined(__GNUC__) && defined(ENABLE_PRELOAD) && ENABLE_PRELOAD)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
# else
# define RPMALLOC_ATTRIB_MALLOC __attribute__((__malloc__))
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size) __attribute__((alloc_size(size)))
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size) __attribute__((alloc_size(count, size)))
# endif
# define RPMALLOC_CDECL
#elif defined(_MSC_VER)
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR __declspec(allocator) __declspec(restrict)
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL __cdecl
#else
# define RPMALLOC_EXPORT
# define RPMALLOC_ALLOCATOR
# define RPMALLOC_ATTRIB_MALLOC
# define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
# define RPMALLOC_ATTRIB_ALLOC_SIZE2(count,size)
# define RPMALLOC_CDECL
#endif
//! Define RPMALLOC_CONFIGURABLE to enable configuring sizes. Will introduce
// a very small overhead due to some size calculations not being compile time constants
#ifndef RPMALLOC_CONFIGURABLE
#define RPMALLOC_CONFIGURABLE 0
#endif
//! 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
#ifndef RPMALLOC_FIRST_CLASS_HEAPS
#define RPMALLOC_FIRST_CLASS_HEAPS 0
#endif
//! Flag to rpaligned_realloc to not preserve content in reallocation
#define RPMALLOC_NO_PRESERVE 1
//! 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
// a new block).
#define RPMALLOC_GROW_OR_FAIL 2
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)
size_t mapped;
//! Peak amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped_peak;
//! Current amount of memory in global caches for small and medium sizes (<32KiB)
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)
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)
size_t huge_alloc_peak;
//! Total amount of memory mapped since initialization (only if ENABLE_STATISTICS=1)
size_t mapped_total;
//! Total amount of memory unmapped since initialization (only if ENABLE_STATISTICS=1)
size_t unmapped_total;
} rpmalloc_global_statistics_t;
typedef struct rpmalloc_thread_statistics_t {
//! Current number of bytes available in thread size class caches for small and medium sizes (<32KiB)
size_t sizecache;
//! Current number of bytes available in thread span caches for small and medium sizes (<32KiB)
size_t spancache;
//! Total number of bytes transitioned from thread cache to global cache (only if ENABLE_STATISTICS=1)
size_t thread_to_global;
//! Total number of bytes transitioned from global cache to thread cache (only if ENABLE_STATISTICS=1)
size_t global_to_thread;
//! Per span count statistics (only if ENABLE_STATISTICS=1)
struct {
//! Currently used number of spans
size_t current;
//! High water mark of spans used
size_t peak;
//! Number of spans transitioned to global cache
size_t to_global;
//! Number of spans transitioned from global cache
size_t from_global;
//! Number of spans transitioned to thread cache
size_t to_cache;
//! Number of spans transitioned from thread cache
size_t from_cache;
//! Number of spans transitioned to reserved state
size_t to_reserved;
//! Number of spans transitioned from reserved state
size_t from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} span_use[64];
//! Per size class statistics (only if ENABLE_STATISTICS=1)
struct {
//! Current number of allocations
size_t alloc_current;
//! Peak number of allocations
size_t alloc_peak;
//! Total number of allocations
size_t alloc_total;
//! Total number of frees
size_t free_total;
//! Number of spans transitioned to cache
size_t spans_to_cache;
//! Number of spans transitioned from cache
size_t spans_from_cache;
//! Number of spans transitioned from reserved state
size_t spans_from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} size_use[128];
} rpmalloc_thread_statistics_t;
typedef struct rpmalloc_config_t {
//! 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.
// 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
// 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
// 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
// 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
// multiple threads simultaneously.
void* (*memory_map)(size_t size, size_t* offset);
//! 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
// 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,
// 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
// else the default implementation will be used for both. This function must be thread
// safe, it can be called by multiple threads simultaneously.
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()
// if this is not set.
void (*error_callback)(const char* message);
//! 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
// 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
// the default system memory map function is used (memory_map callback is not set).
int (*map_fail_callback)(size_t size);
//! 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.
// Used if RPMALLOC_CONFIGURABLE is defined to 1, otherwise system page size is used.
size_t page_size;
//! 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
// is defined to 1.
size_t span_size;
//! 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
// 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
// aligned to a multiple of spans that match memory page size in case of huge pages.
size_t span_map_count;
//! 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.
// 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
// allocator.
// 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
int enable_huge_pages;
//! Respectively allocated pages and huge allocated pages names for systems
// supporting it to be able to distinguish among anonymous regions.
const char *page_name;
const char *huge_page_name;
} rpmalloc_config_t;
//! Initialize allocator with default configuration
RPMALLOC_EXPORT int
rpmalloc_initialize(void);
//! Initialize allocator with given configuration
RPMALLOC_EXPORT int
rpmalloc_initialize_config(const rpmalloc_config_t* config);
//! Get allocator configuration
RPMALLOC_EXPORT const rpmalloc_config_t*
rpmalloc_config(void);
//! Finalize allocator
RPMALLOC_EXPORT void
rpmalloc_finalize(void);
//! Initialize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_initialize(void);
//! Finalize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_finalize(int release_caches);
//! Perform deferred deallocations pending for the calling thread heap
RPMALLOC_EXPORT void
rpmalloc_thread_collect(void);
//! Query if allocator is initialized for calling thread
RPMALLOC_EXPORT int
rpmalloc_is_thread_initialized(void);
//! Get per-thread statistics
RPMALLOC_EXPORT void
rpmalloc_thread_statistics(rpmalloc_thread_statistics_t* stats);
//! Get global statistics
RPMALLOC_EXPORT void
rpmalloc_global_statistics(rpmalloc_global_statistics_t* stats);
//! Dump all statistics in human readable format to file (should be a FILE*)
RPMALLOC_EXPORT void
rpmalloc_dump_statistics(void* file);
//! Allocate a memory block of at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
rpmalloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1);
//! Free the given memory block
RPMALLOC_EXPORT void
rpfree(void* ptr);
//! Allocate a memory block of at least the given size and zero initialize it
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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,
// with optional control flags (see RPMALLOC_NO_PRESERVE).
// 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
// internals is that this must also be strictly less than the span size (default 64KiB)
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);
//! 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*),
// 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)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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.
// 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
// internals is that this must also be strictly less than the span size (default 64KiB)
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);
//! 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*),
// 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)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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.
// 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
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT int
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)
RPMALLOC_EXPORT size_t
rpmalloc_usable_size(void* ptr);
//! Dummy empty function for forcing linker symbol inclusion
RPMALLOC_EXPORT void
rpmalloc_linker_reference(void);
#if RPMALLOC_FIRST_CLASS_HEAPS
//! Heap type
typedef struct heap_t rpmalloc_heap_t;
//! 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
// thread will call heap functions for a given heap at any given time, no functions are thread safe.
RPMALLOC_EXPORT rpmalloc_heap_t*
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).
// Releasing a heap will enable it to be reused by other threads. Safe to pass a null pointer.
RPMALLOC_EXPORT void
rpmalloc_heap_release(rpmalloc_heap_t* heap);
//! Allocate a memory block of at least the given size using the given heap.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void*
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
// 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
// internals is that this must also be strictly less than the span size (default 64KiB).
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);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it.
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);
//! 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*),
// 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).
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);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function.
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);
//! 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.
// 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
// the span size (default 64KiB).
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);
//! Free the given memory block from the given heap. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT void
rpmalloc_heap_free(rpmalloc_heap_t* heap, void* ptr);
//! Free all memory allocated by the heap
RPMALLOC_EXPORT void
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
// 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.
RPMALLOC_EXPORT void
rpmalloc_heap_thread_set_current(rpmalloc_heap_t* heap);
//! Returns which heap the given pointer is allocated on
RPMALLOC_EXPORT rpmalloc_heap_t*
rpmalloc_get_heap_for_ptr(void* ptr);
#endif
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,481 +1,481 @@
/** 1D, 2D, 3D and 4D float Perlin Simplex noise */
/** Original code, stefan gustavson (PD). */
#ifdef SIMPLEX_C
/* SimplexNoise1234, Simplex noise with true analytic
* derivative in 1D to 4D.
*
* Author: Stefan Gustavson, 2003-2005
* Contact: stefan.gustavson@liu.se
*
* This code was GPL licensed until February 2011.
* As the original author of this code, I hereby
* release it into the public domain.
* Please feel free to use it for whatever you want.
* Credit is appreciated where appropriate, and I also
* appreciate being told where this code finds any use,
* but you may do as you like.
*/
/*
* This implementation is "Simplex Noise" as presented by
* Ken Perlin at a relatively obscure and not often cited course
* session "Real-Time Shading" at Siggraph 2001 (before real
* time shading actually took off), under the title "hardware noise".
* The 3D function is numerically equivalent to his Java reference
* 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
* were implemented from scratch by me from Ken Perlin's text.
*
* 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.
*/
// We don't really need to include this, but play nice and do it anyway.
//#include "noise.c"
#define FASTFLOOR(x) ( ((int)(x)<=(x)) ? ((int)x) : (((int)x)-1) )
//---------------------------------------------------------------------
// Static data
/*
* 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.
* 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.
* This also removes the need for any initialisation of this class.
*
* 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
* byte addressing. Intel x86 is generally single-byte-friendly, but
* some other CPUs are faster with 4-aligned reads.
* However, a char[] is smaller, which avoids cache trashing, and that
* is probably the most important aspect on most architectures.
* This array is accessed a *lot* by the noise functions.
* 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!
*/
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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
};
//---------------------------------------------------------------------
/*
* Helper functions to compute gradients-dot-residualvectors (1D to 4D)
* 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
* noise values need to be rescaled to fit nicely within [-1,1].
* (The simplex noise functions as such also have different scaling.)
* Note also that these noise functions are the most practical and useful
* signed version of Perlin noise. To return values according to the
* RenderMan specification from the SL noise() and pnoise() functions,
* 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 grad1( int hash, float x ) {
int h = hash & 15;
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
return ( grad * x ); // Multiply the gradient with the distance
}
float grad2( int hash, float x, float y ) {
int h = hash & 7; // Convert low 3 bits of hash code
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).
return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v);
}
float grad3( int hash, float x, float y , float z ) {
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 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);
}
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
float u = h<24 ? x : y; // gradient directions, and compute dot product.
float v = h<16 ? y : z;
float w = h<8 ? z : t;
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.
// 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! */
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,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},
{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},
{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,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
float snoise1(float x) {
int i0 = FASTFLOOR(x);
int i1 = i0 + 1;
float x0 = x - i0;
float x1 = x0 - 1.0f;
float n0, n1;
float t0 = 1.0f - x0*x0;
// if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case
t0 *= t0;
n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0);
float t1 = 1.0f - x1*x1;
// if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case
t1 *= t1;
n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1);
// 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
// we want to match PRMan's 1D noise, so we scale it down some more.
return 0.25f * (n0 + n1);
}
// 2D simplex noise
float snoise2(float x, float y) {
#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
float n0, n1, n2; // Noise contributions from the three corners
// Skew the input space to determine which simplex cell we're in
float s = (x+y)*F2; // Hairy factor for 2D
float xs = x + s;
float ys = y + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
float t = (float)(i+j)*G2;
float X0 = i-t; // Unskew the cell origin back to (x,y) space
float Y0 = j-t;
float x0 = x-X0; // The x,y distances from the cell origin
float y0 = y-Y0;
// For the 2D case, the simplex shape is an equilateral triangle.
// Determine which simplex we are in.
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)
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 (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
// c = (3-sqrt(3))/6
float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
float y1 = y0 - j1 + G2;
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;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
// Calculate the contribution from the three corners
float t0 = 0.5f - x0*x0-y0*y0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0);
}
float t1 = 0.5f - x1*x1-y1*y1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1);
}
float t2 = 0.5f - x2*x2-y2*y2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary!
}
// 3D simplex noise
float snoise3(float x, float y, float z) {
// Simple skewing factors for the 3D case
#define F3 0.333333333
#define G3 0.166666667
float n0, n1, n2, n3; // Noise contributions from the four corners
// 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 xs = x+s;
float ys = y+s;
float zs = z+s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
float t = (float)(i+j+k)*G3;
float X0 = i-t; // Unskew the cell origin back to (x,y,z) space
float Y0 = j-t;
float Z0 = k-t;
float x0 = x-X0; // The x,y,z distances from the cell origin
float y0 = y-Y0;
float z0 = z-Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
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
/* This code would benefit from a backport from the GLSL version! */
if(x0>=y0) {
if(y0>=z0)
{ 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 { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
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 { 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 (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
// c = 1/6.
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
float y1 = y0 - j1 + G3;
float z1 = z0 - k1 + G3;
float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords
float y2 = y0 - j2 + 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 y3 = y0 - 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
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
// Calculate the contribution from the four corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad3(perm[ii+perm[jj+perm[kk]]], x0, y0, z0);
}
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
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;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
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;
if(t3<0.0f) n3 = 0.0f;
else {
t3 *= t3;
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.
// The result is scaled to stay just inside [-1,1]
return 72.0f * (n0 + n1 + n2 + n3);
}
// 4D simplex noise
float snoise4(float x, float y, float z, float w) {
// 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 G4 0.138196601 // G4 = (5.0-Math.sqrt(5.0))/20.0
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
float s = (x + y + z + w) * F4; // Factor for 4D skewing
float xs = x + s;
float ys = y + s;
float zs = z + s;
float ws = w + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
int l = FASTFLOOR(ws);
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 Y0 = j - t;
float Z0 = k - t;
float W0 = l - t;
float x0 = x - X0; // The x,y,z,w distances from the cell origin
float y0 = y - Y0;
float z0 = z - Z0;
float w0 = w - W0;
// 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
// 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
// then find the correct traversal order for the simplex we?re in.
// 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
// for an integer index.
int c1 = (x0 > y0) ? 32 : 0;
int c2 = (x0 > z0) ? 16 : 0;
int c3 = (y0 > z0) ? 8 : 0;
int c4 = (x0 > w0) ? 4 : 0;
int c5 = (y0 > w0) ? 2 : 0;
int c6 = (z0 > w0) ? 1 : 0;
int c = c1 + c2 + c3 + c4 + c5 + c6;
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 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.
// 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.
// 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.
i1 = simplex[c][0]>=3 ? 1 : 0;
j1 = simplex[c][1]>=3 ? 1 : 0;
k1 = simplex[c][2]>=3 ? 1 : 0;
l1 = simplex[c][3]>=3 ? 1 : 0;
// The number 2 in the "simplex" array is at the second largest coordinate.
i2 = simplex[c][0]>=2 ? 1 : 0;
j2 = simplex[c][1]>=2 ? 1 : 0;
k2 = simplex[c][2]>=2 ? 1 : 0;
l2 = simplex[c][3]>=2 ? 1 : 0;
// The number 1 in the "simplex" array is at the second smallest coordinate.
i3 = simplex[c][0]>=1 ? 1 : 0;
j3 = simplex[c][1]>=1 ? 1 : 0;
k3 = simplex[c][2]>=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.
float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
float y1 = y0 - j1 + G4;
float z1 = z0 - k1 + G4;
float w1 = w0 - l1 + G4;
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 z2 = z0 - k2 + 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 y3 = y0 - j3 + 3.0f*G4;
float z3 = z0 - k3 + 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 y4 = y0 - 1.0f + 4.0f*G4;
float z4 = z0 - 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
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
int ll = l & 0xff;
// Calculate the contribution from the five corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0 - w0*w0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
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;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
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;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
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;
if(t3 < 0.0f) n3 = 0.0f;
else {
t3 *= t3;
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;
if(t4 < 0.0f) n4 = 0.0f;
else {
t4 *= t4;
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]
return 62.0f * (n0 + n1 + n2 + n3 + n4);
}
#undef F2
#undef G2
#undef F3
#undef G3
#undef F4
#undef G4
#endif
/** 1D, 2D, 3D and 4D float Perlin Simplex noise */
/** Original code, stefan gustavson (PD). */
#ifdef SIMPLEX_C
/* SimplexNoise1234, Simplex noise with true analytic
* derivative in 1D to 4D.
*
* Author: Stefan Gustavson, 2003-2005
* Contact: stefan.gustavson@liu.se
*
* This code was GPL licensed until February 2011.
* As the original author of this code, I hereby
* release it into the public domain.
* Please feel free to use it for whatever you want.
* Credit is appreciated where appropriate, and I also
* appreciate being told where this code finds any use,
* but you may do as you like.
*/
/*
* This implementation is "Simplex Noise" as presented by
* Ken Perlin at a relatively obscure and not often cited course
* session "Real-Time Shading" at Siggraph 2001 (before real
* time shading actually took off), under the title "hardware noise".
* The 3D function is numerically equivalent to his Java reference
* 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
* were implemented from scratch by me from Ken Perlin's text.
*
* 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.
*/
// We don't really need to include this, but play nice and do it anyway.
//#include "noise.c"
#define FASTFLOOR(x) ( ((int)(x)<=(x)) ? ((int)x) : (((int)x)-1) )
//---------------------------------------------------------------------
// Static data
/*
* 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.
* 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.
* This also removes the need for any initialisation of this class.
*
* 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
* byte addressing. Intel x86 is generally single-byte-friendly, but
* some other CPUs are faster with 4-aligned reads.
* However, a char[] is smaller, which avoids cache trashing, and that
* is probably the most important aspect on most architectures.
* This array is accessed a *lot* by the noise functions.
* 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!
*/
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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
};
//---------------------------------------------------------------------
/*
* Helper functions to compute gradients-dot-residualvectors (1D to 4D)
* 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
* noise values need to be rescaled to fit nicely within [-1,1].
* (The simplex noise functions as such also have different scaling.)
* Note also that these noise functions are the most practical and useful
* signed version of Perlin noise. To return values according to the
* RenderMan specification from the SL noise() and pnoise() functions,
* 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 grad1( int hash, float x ) {
int h = hash & 15;
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
return ( grad * x ); // Multiply the gradient with the distance
}
float grad2( int hash, float x, float y ) {
int h = hash & 7; // Convert low 3 bits of hash code
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).
return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v);
}
float grad3( int hash, float x, float y , float z ) {
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 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);
}
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
float u = h<24 ? x : y; // gradient directions, and compute dot product.
float v = h<16 ? y : z;
float w = h<8 ? z : t;
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.
// 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! */
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,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},
{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},
{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,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
float snoise1(float x) {
int i0 = FASTFLOOR(x);
int i1 = i0 + 1;
float x0 = x - i0;
float x1 = x0 - 1.0f;
float n0, n1;
float t0 = 1.0f - x0*x0;
// if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case
t0 *= t0;
n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0);
float t1 = 1.0f - x1*x1;
// if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case
t1 *= t1;
n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1);
// 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
// we want to match PRMan's 1D noise, so we scale it down some more.
return 0.25f * (n0 + n1);
}
// 2D simplex noise
float snoise2(float x, float y) {
#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
float n0, n1, n2; // Noise contributions from the three corners
// Skew the input space to determine which simplex cell we're in
float s = (x+y)*F2; // Hairy factor for 2D
float xs = x + s;
float ys = y + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
float t = (float)(i+j)*G2;
float X0 = i-t; // Unskew the cell origin back to (x,y) space
float Y0 = j-t;
float x0 = x-X0; // The x,y distances from the cell origin
float y0 = y-Y0;
// For the 2D case, the simplex shape is an equilateral triangle.
// Determine which simplex we are in.
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)
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 (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
// c = (3-sqrt(3))/6
float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
float y1 = y0 - j1 + G2;
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;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
// Calculate the contribution from the three corners
float t0 = 0.5f - x0*x0-y0*y0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0);
}
float t1 = 0.5f - x1*x1-y1*y1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1);
}
float t2 = 0.5f - x2*x2-y2*y2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary!
}
// 3D simplex noise
float snoise3(float x, float y, float z) {
// Simple skewing factors for the 3D case
#define F3 0.333333333
#define G3 0.166666667
float n0, n1, n2, n3; // Noise contributions from the four corners
// 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 xs = x+s;
float ys = y+s;
float zs = z+s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
float t = (float)(i+j+k)*G3;
float X0 = i-t; // Unskew the cell origin back to (x,y,z) space
float Y0 = j-t;
float Z0 = k-t;
float x0 = x-X0; // The x,y,z distances from the cell origin
float y0 = y-Y0;
float z0 = z-Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
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
/* This code would benefit from a backport from the GLSL version! */
if(x0>=y0) {
if(y0>=z0)
{ 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 { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
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 { 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 (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
// c = 1/6.
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
float y1 = y0 - j1 + G3;
float z1 = z0 - k1 + G3;
float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords
float y2 = y0 - j2 + 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 y3 = y0 - 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
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
// Calculate the contribution from the four corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad3(perm[ii+perm[jj+perm[kk]]], x0, y0, z0);
}
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
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;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
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;
if(t3<0.0f) n3 = 0.0f;
else {
t3 *= t3;
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.
// The result is scaled to stay just inside [-1,1]
return 72.0f * (n0 + n1 + n2 + n3);
}
// 4D simplex noise
float snoise4(float x, float y, float z, float w) {
// 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 G4 0.138196601 // G4 = (5.0-Math.sqrt(5.0))/20.0
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
float s = (x + y + z + w) * F4; // Factor for 4D skewing
float xs = x + s;
float ys = y + s;
float zs = z + s;
float ws = w + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
int l = FASTFLOOR(ws);
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 Y0 = j - t;
float Z0 = k - t;
float W0 = l - t;
float x0 = x - X0; // The x,y,z,w distances from the cell origin
float y0 = y - Y0;
float z0 = z - Z0;
float w0 = w - W0;
// 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
// 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
// then find the correct traversal order for the simplex we?re in.
// 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
// for an integer index.
int c1 = (x0 > y0) ? 32 : 0;
int c2 = (x0 > z0) ? 16 : 0;
int c3 = (y0 > z0) ? 8 : 0;
int c4 = (x0 > w0) ? 4 : 0;
int c5 = (y0 > w0) ? 2 : 0;
int c6 = (z0 > w0) ? 1 : 0;
int c = c1 + c2 + c3 + c4 + c5 + c6;
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 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.
// 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.
// 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.
i1 = simplex[c][0]>=3 ? 1 : 0;
j1 = simplex[c][1]>=3 ? 1 : 0;
k1 = simplex[c][2]>=3 ? 1 : 0;
l1 = simplex[c][3]>=3 ? 1 : 0;
// The number 2 in the "simplex" array is at the second largest coordinate.
i2 = simplex[c][0]>=2 ? 1 : 0;
j2 = simplex[c][1]>=2 ? 1 : 0;
k2 = simplex[c][2]>=2 ? 1 : 0;
l2 = simplex[c][3]>=2 ? 1 : 0;
// The number 1 in the "simplex" array is at the second smallest coordinate.
i3 = simplex[c][0]>=1 ? 1 : 0;
j3 = simplex[c][1]>=1 ? 1 : 0;
k3 = simplex[c][2]>=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.
float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
float y1 = y0 - j1 + G4;
float z1 = z0 - k1 + G4;
float w1 = w0 - l1 + G4;
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 z2 = z0 - k2 + 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 y3 = y0 - j3 + 3.0f*G4;
float z3 = z0 - k3 + 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 y4 = y0 - 1.0f + 4.0f*G4;
float z4 = z0 - 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
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
int ll = l & 0xff;
// Calculate the contribution from the five corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0 - w0*w0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
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;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
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;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
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;
if(t3 < 0.0f) n3 = 0.0f;
else {
t3 *= t3;
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;
if(t4 < 0.0f) n4 = 0.0f;
else {
t4 *= t4;
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]
return 62.0f * (n0 + n1 + n2 + n3 + n4);
}
#undef F2
#undef G2
#undef F3
#undef G3
#undef F4
#undef G4
#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

View File

@ -1,290 +1,290 @@
// 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
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.
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/>.
*/
/*
swrap supports the following three configurations:
#define SWRAP_EXTERN
Default, should be used when using swrap in multiple compilation units within the same project.
#define SWRAP_IMPLEMENTATION
Must be defined in exactly one source file within a project for swrap to be found by the linker.
#define SWRAP_STATIC
Defines all swrap functions as static, useful if swrap is only used in a single compilation unit.
*/
//include only once
#ifndef SWRAP_H
#define SWRAP_H
//process configuration
#ifdef SWRAP_STATIC
#define SWRAP_IMPLEMENTATION
#define SWDEF static
#else //SWRAP_EXTERN
#define SWDEF extern
#endif
//constants
#define SWRAP_TCP 0
#define SWRAP_UDP 1
#define SWRAP_BIND 0
#define SWRAP_CONNECT 1
#define SWRAP_DEFAULT 0x00
#define SWRAP_NOBLOCK 0x01
#define SWRAP_NODELAY 0x02
//structs
struct swrap_addr {
char data[128]; //enough space to hold any kind of address
};
//function declarations
SWDEF int swrapInit();
SWDEF int swrapSocket(int, int, char, const char*, const char*);
SWDEF void swrapClose(int);
SWDEF void swrapTerminate();
SWDEF int swrapListen(int, int);
SWDEF int swrapAccept(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 swrapSend(int, const char*, size_t);
SWDEF int swrapReceive(int, 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 swrapSelect(int, double);
SWDEF int swrapMultiSelect(int*, size_t, double);
//implementation section
#ifdef SWRAP_IMPLEMENTATION
//includes
#ifdef _WIN32 //windows
#include <ws2tcpip.h>
#else //unix
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/tcp.h>
#endif
#include <stddef.h> //NULL
#include <limits.h> //INT_MAX on emscripten //< @r-lyeh: added
//general functions
SWDEF int swrapInit () {
//initializes socket functionality, returns 0 on success
#ifdef _WIN32
WSADATA WsaData;
return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR);
#else
return 0;
#endif
}
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
//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
// SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept
// SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent
//int: Mode of the socket
// 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
//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_NODELAY: Disables Nagle's for TCP sockets, default is enabled
//char*: Host/address as a string, can be IPv4, IPv6, etc...
//char*: Service/port as a string, e.g. "1728" or "http"
//returns socket handle, or -1 on failure
struct addrinfo* result, hint = {
(mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags
AF_UNSPEC, //ai_family
(prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype
0, 0, NULL, NULL, NULL};
//get address info
if (getaddrinfo(host, serv, &hint, &result)) return -1;
//create socket
#ifdef _WIN32
SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//convert to int
int sock = wsck;
#else
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == -1) return -1;
#endif
//make sure IPV6_ONLY is disabled
if (result->ai_family == AF_INET6) {
int no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
}
//set TCP_NODELAY if applicable
if (prot == SWRAP_TCP) {
int nodelay = (flags&SWRAP_NODELAY);
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay));
}
//bind if applicable
if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) {
swrapClose(sock);
return -1;
}
//set non-blocking if needed
if (flags&SWRAP_NOBLOCK) {
#ifdef _WIN32
DWORD no_block = 1;
if (ioctlsocket(sock, FIONBIO, &no_block)) {
swrapClose(sock);
return -1;
}
#else
if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) {
swrapClose(sock);
return -1;
}
#endif
}
//connect if applicable (return only relevant if blocking)
if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) {
swrapClose(sock);
return -1;
}
//free address info
freeaddrinfo(result);
//return socket handle
return sock;
}
SWDEF void swrapClose (int sock) {
//closes the given socket
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
}
SWDEF void swrapTerminate () {
//terminates socket functionality
#ifdef _WIN32
WSACleanup();
#endif
}
//connection functions
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
//returns 0 on success, non-zero on failure
return listen(sock, blog);
}
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
//returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//return new socket
return wsck;
#else
socklen_t addr_size = sizeof(struct swrap_addr);
return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
#endif
}
//address functions
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
//returns 0 on success, non-zero on failure
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
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) {
//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
return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0);
}
//send/receive functions
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)
//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
return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
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)
//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)
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) {
//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
//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));
}
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
//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)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size);
}
//select functions
SWDEF int swrapSelect (int sock, double timeout) {
//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
//returns 1 if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time;
//fd set
FD_ZERO(&set);
if (sock > -1) FD_SET(sock, &set);
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock+1, &set, NULL, NULL, &time);
}
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
//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
fd_set set; struct timeval time; int sock_max = -1;
//fd set
FD_ZERO(&set);
for (size_t i = 0; i < socks_size; i++) {
if (socks[i] > sock_max) sock_max = socks[i];
if (socks[i] > -1) FD_SET(socks[i], &set);
}
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock_max+1, &set, NULL, NULL, &time);
}
#endif //SWRAP_IMPLEMENTATION
#endif //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
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.
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/>.
*/
/*
swrap supports the following three configurations:
#define SWRAP_EXTERN
Default, should be used when using swrap in multiple compilation units within the same project.
#define SWRAP_IMPLEMENTATION
Must be defined in exactly one source file within a project for swrap to be found by the linker.
#define SWRAP_STATIC
Defines all swrap functions as static, useful if swrap is only used in a single compilation unit.
*/
//include only once
#ifndef SWRAP_H
#define SWRAP_H
//process configuration
#ifdef SWRAP_STATIC
#define SWRAP_IMPLEMENTATION
#define SWDEF static
#else //SWRAP_EXTERN
#define SWDEF extern
#endif
//constants
#define SWRAP_TCP 0
#define SWRAP_UDP 1
#define SWRAP_BIND 0
#define SWRAP_CONNECT 1
#define SWRAP_DEFAULT 0x00
#define SWRAP_NOBLOCK 0x01
#define SWRAP_NODELAY 0x02
//structs
struct swrap_addr {
char data[128]; //enough space to hold any kind of address
};
//function declarations
SWDEF int swrapInit();
SWDEF int swrapSocket(int, int, char, const char*, const char*);
SWDEF void swrapClose(int);
SWDEF void swrapTerminate();
SWDEF int swrapListen(int, int);
SWDEF int swrapAccept(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 swrapSend(int, const char*, size_t);
SWDEF int swrapReceive(int, 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 swrapSelect(int, double);
SWDEF int swrapMultiSelect(int*, size_t, double);
//implementation section
#ifdef SWRAP_IMPLEMENTATION
//includes
#ifdef _WIN32 //windows
#include <ws2tcpip.h>
#else //unix
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/tcp.h>
#endif
#include <stddef.h> //NULL
#include <limits.h> //INT_MAX on emscripten //< @r-lyeh: added
//general functions
SWDEF int swrapInit () {
//initializes socket functionality, returns 0 on success
#ifdef _WIN32
WSADATA WsaData;
return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR);
#else
return 0;
#endif
}
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
//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
// SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept
// SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent
//int: Mode of the socket
// 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
//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_NODELAY: Disables Nagle's for TCP sockets, default is enabled
//char*: Host/address as a string, can be IPv4, IPv6, etc...
//char*: Service/port as a string, e.g. "1728" or "http"
//returns socket handle, or -1 on failure
struct addrinfo* result, hint = {
(mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags
AF_UNSPEC, //ai_family
(prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype
0, 0, NULL, NULL, NULL};
//get address info
if (getaddrinfo(host, serv, &hint, &result)) return -1;
//create socket
#ifdef _WIN32
SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//convert to int
int sock = wsck;
#else
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == -1) return -1;
#endif
//make sure IPV6_ONLY is disabled
if (result->ai_family == AF_INET6) {
int no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
}
//set TCP_NODELAY if applicable
if (prot == SWRAP_TCP) {
int nodelay = (flags&SWRAP_NODELAY);
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay));
}
//bind if applicable
if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) {
swrapClose(sock);
return -1;
}
//set non-blocking if needed
if (flags&SWRAP_NOBLOCK) {
#ifdef _WIN32
DWORD no_block = 1;
if (ioctlsocket(sock, FIONBIO, &no_block)) {
swrapClose(sock);
return -1;
}
#else
if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) {
swrapClose(sock);
return -1;
}
#endif
}
//connect if applicable (return only relevant if blocking)
if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) {
swrapClose(sock);
return -1;
}
//free address info
freeaddrinfo(result);
//return socket handle
return sock;
}
SWDEF void swrapClose (int sock) {
//closes the given socket
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
}
SWDEF void swrapTerminate () {
//terminates socket functionality
#ifdef _WIN32
WSACleanup();
#endif
}
//connection functions
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
//returns 0 on success, non-zero on failure
return listen(sock, blog);
}
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
//returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//return new socket
return wsck;
#else
socklen_t addr_size = sizeof(struct swrap_addr);
return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
#endif
}
//address functions
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
//returns 0 on success, non-zero on failure
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
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) {
//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
return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0);
}
//send/receive functions
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)
//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
return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
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)
//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)
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) {
//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
//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));
}
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
//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)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size);
}
//select functions
SWDEF int swrapSelect (int sock, double timeout) {
//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
//returns 1 if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time;
//fd set
FD_ZERO(&set);
if (sock > -1) FD_SET(sock, &set);
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock+1, &set, NULL, NULL, &time);
}
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
//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
fd_set set; struct timeval time; int sock_max = -1;
//fd set
FD_ZERO(&set);
for (size_t i = 0; i < socks_size; i++) {
if (socks[i] > sock_max) sock_max = socks[i];
if (socks[i] > -1) FD_SET(socks[i], &set);
}
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock_max+1, &set, NULL, NULL, &time);
}
#endif //SWRAP_IMPLEMENTATION
#endif //SWRAP_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

View File

@ -1,82 +1,82 @@
// AI framework
// - rlyeh, public domain.
//
// [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
// pathfinding -----------------------------------------------------------------
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.
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
typedef int (*bt_func)();
typedef struct bt_t {
uint64_t type;
int (*action)();
union {
int argi;
float argf;
};
array(struct bt_t) children;
} bt_t;
API bt_t bt(const char *ini_file, unsigned flags);
API int bt_run(bt_t *b);
API void bt_addfun(const char *name, int(*func)());
API bt_func bt_findfun(const char *name);
API char *bt_funcname(bt_func fn);
API int ui_bt(bt_t *b);
// boids/swarm -----------------------------------------------------------------
typedef enum SWARM_DISTANCE {
SWARM_DISTANCE_LINEAR,
SWARM_DISTANCE_INVERSE_LINEAR,
SWARM_DISTANCE_QUADRATIC,
SWARM_DISTANCE_INVERSE_QUADRATIC
} SWARM_DISTANCE;
#define boid(...) C_CAST(boid_t, __VA_ARGS__)
typedef struct boid_t {
vec3 position;
vec3 velocity;
vec3 acceleration;
vec3 prev_position;
} boid_t;
typedef struct swarm_t {
array(boid_t) boids;
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
SWARM_DISTANCE separation_type;
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 steering_weight;
array(vec3) steering_targets;
SWARM_DISTANCE steering_target_type;
float blindspot_angledeg;
float max_acceleration; // how fast each boid can change its direction
float max_velocity; // how fast each boid can move
// private:
map(vec3*, array(boid_t*)) voxel_cache_;
float blindspot_angledeg_compare_value_;
} swarm_t;
API swarm_t swarm();
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_and_velocity_only(swarm_t *self, float delta); // acc,vel
API int ui_swarm(swarm_t *self);
// AI framework
// - rlyeh, public domain.
//
// [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
// pathfinding -----------------------------------------------------------------
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.
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
typedef int (*bt_func)();
typedef struct bt_t {
uint64_t type;
int (*action)();
union {
int argi;
float argf;
};
array(struct bt_t) children;
} bt_t;
API bt_t bt(const char *ini_file, unsigned flags);
API int bt_run(bt_t *b);
API void bt_addfun(const char *name, int(*func)());
API bt_func bt_findfun(const char *name);
API char *bt_funcname(bt_func fn);
API int ui_bt(bt_t *b);
// boids/swarm -----------------------------------------------------------------
typedef enum SWARM_DISTANCE {
SWARM_DISTANCE_LINEAR,
SWARM_DISTANCE_INVERSE_LINEAR,
SWARM_DISTANCE_QUADRATIC,
SWARM_DISTANCE_INVERSE_QUADRATIC
} SWARM_DISTANCE;
#define boid(...) C_CAST(boid_t, __VA_ARGS__)
typedef struct boid_t {
vec3 position;
vec3 velocity;
vec3 acceleration;
vec3 prev_position;
} boid_t;
typedef struct swarm_t {
array(boid_t) boids;
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
SWARM_DISTANCE separation_type;
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 steering_weight;
array(vec3) steering_targets;
SWARM_DISTANCE steering_target_type;
float blindspot_angledeg;
float max_acceleration; // how fast each boid can change its direction
float max_velocity; // how fast each boid can move
// private:
map(vec3*, array(boid_t*)) voxel_cache_;
float blindspot_angledeg_compare_value_;
} swarm_t;
API swarm_t swarm();
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_and_velocity_only(swarm_t *self, float delta); // acc,vel
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