├── .clang-format ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── TODO.md ├── aliases.sh ├── demos ├── bubbles.c ├── bubbles.glsl ├── clipping.c ├── clipping.glsl ├── dragpts.c ├── dragpts.glsl ├── hmap.c ├── hmap.glsl ├── klein.c ├── klein.glsl ├── marina.c ├── marina.glsl ├── orbits.c ├── orbits.glsl ├── shapes.c ├── shapes.glsl ├── sierpinski.c ├── sierpinski.glsl ├── simple.c ├── simple.glsl ├── terrainpts.c ├── terrainpts.glsl ├── trefoil.c ├── trefoil.glsl ├── trillium.c ├── trillium.glsl ├── zooming.c ├── zooming.glsl ├── ztex.c └── ztex.glsl ├── emsetup.py ├── extern ├── GL │ └── glcorearb.h ├── khash.h ├── kvec.h ├── lodepng.c ├── lodepng.h ├── lz4.cpp ├── lz4.h ├── sds.c ├── sdsalloc.h ├── tiny_obj_loader.h ├── whereami.c └── whereami.h ├── include ├── dmath.h ├── pa.h ├── parg.h ├── parwin.h ├── sds.h └── vmath.h ├── src ├── aar.c ├── asset.c ├── bindings.cpp ├── buffer.c ├── draw.c ├── framebuffer.c ├── internal.h ├── mesh.c ├── objloader.cpp ├── pargl.h ├── shader.c ├── state.c ├── texture.c ├── token.c ├── uniform.c ├── varray.c ├── window.c └── zcam.c ├── uncrustify.cfg └── web ├── css ├── main.css └── normalize.min.css ├── img ├── emscripten.png └── hud.png ├── index.html ├── js ├── parg.js ├── suffix.js └── vendor │ ├── jquery-1.11.2.min.js │ ├── modernizr-2.8.3.min.js │ └── snap.svg-min.js ├── multi.html └── parg /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: false 6 | AlignEscapedNewlinesLeft: true 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | BinPackParameters: true 12 | BinPackArguments: true 13 | PenaltyBreakBeforeFirstCallParameter: 1000 14 | 15 | AllowShortBlocksOnASingleLine: true 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortIfStatementsOnASingleLine: false 18 | AllowShortLoopsOnASingleLine: false 19 | AllowShortFunctionsOnASingleLine: true 20 | AlwaysBreakAfterDefinitionReturnType: false 21 | AlwaysBreakTemplateDeclarations: true 22 | AlwaysBreakBeforeMultilineStrings: true 23 | BreakBeforeBinaryOperators: None 24 | BreakBeforeTernaryOperators: true 25 | BreakConstructorInitializersBeforeComma: false 26 | ColumnLimit: 80 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 28 | ConstructorInitializerIndentWidth: 4 29 | DerivePointerAlignment: false 30 | PointerAlignment: Left 31 | ExperimentalAutoDetectBinPacking: false 32 | IndentCaseLabels: true 33 | IndentWrappedFunctionNames: false 34 | IndentFunctionDeclarationAfterType: false 35 | MaxEmptyLinesToKeep: 1 36 | KeepEmptyLinesAtTheStartOfBlocks: false 37 | NamespaceIndentation: None 38 | PenaltyBreakComment: 300 39 | PenaltyBreakString: 1000 40 | PenaltyBreakFirstLessLess: 120 41 | PenaltyExcessCharacter: 1000000 42 | PenaltyReturnTypeOnItsOwnLine: 200 43 | SpacesBeforeTrailingComments: 2 44 | Cpp11BracedListStyle: true 45 | Standard: Cpp11 46 | IndentWidth: 4 47 | TabWidth: 8 48 | UseTab: Never 49 | BreakBeforeBraces: Linux 50 | SpacesInParentheses: false 51 | SpacesInSquareBrackets: false 52 | SpacesInAngles: false 53 | SpaceInEmptyParentheses: false 54 | SpacesInCStyleCastParentheses: false 55 | SpaceAfterCStyleCast: true 56 | SpacesInContainerLiterals: true 57 | SpaceBeforeAssignmentOperators: true 58 | ContinuationIndentWidth: 4 59 | CommentPragmas: '^ IWYU pragma:' 60 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 61 | SpaceBeforeParens: ControlStatements 62 | DisableFormat: false 63 | ... 64 | 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.log 3 | build 4 | *.dblite 5 | .DS_Store 6 | docs/_build 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/par"] 2 | path = extern/par 3 | url = https://github.com/prideout/par 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9 3 | script: 4 | - brew install curl cmake 5 | - brew install uncrustify glfw3 emscripten 6 | - python emsetup.py 7 | - cmake -DHAVE_FUNTIMENS=0 -DEMSCRIPTEN=ON . -Bbuild 8 | - cmake --build build 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.8) 2 | project(parg) 3 | 4 | option(EMSCRIPTEN "enable JavaScript output via emcc") 5 | 6 | set(DEMOS 7 | dragpts 8 | bubbles 9 | shapes 10 | clipping 11 | klein 12 | marina 13 | trefoil 14 | sierpinski 15 | simple 16 | zooming 17 | trillium 18 | terrainpts 19 | hmap 20 | ztex 21 | orbits) 22 | 23 | find_package(glfw3 REQUIRED) 24 | find_package(curl REQUIRED) 25 | find_package(OpenGL REQUIRED) 26 | 27 | file(GLOB COREC src/*.c) 28 | file(GLOB VENDORC extern/*.c) 29 | file(GLOB SRCFILES ${COREC} ${VENDORC}) 30 | file(GLOB JSEXCLUSIONS src/window.c src/easycurl.c src/filecache.c) 31 | file(GLOB JSCPP src/bindings.cpp src/objloader.cpp) 32 | 33 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 34 | set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "-gnu99") 35 | find_package (X11 REQUIRED) 36 | add_definitions( 37 | -O3 38 | -Wall 39 | -I../include 40 | -I../extern) 41 | set(PLATFORM_LIBS 42 | ${X11_LIBRARIES} 43 | Xinerama 44 | pthread 45 | dl 46 | Xrandr 47 | Xinerama 48 | Xcursor 49 | Xi 50 | Xxf86vm 51 | GL 52 | m) 53 | endif() 54 | 55 | add_library(parg STATIC ${SRCFILES} src/objloader.cpp) 56 | 57 | target_link_libraries(parg glfw curl) 58 | 59 | target_include_directories(parg PUBLIC 60 | include 61 | extern) 62 | 63 | target_compile_definitions( 64 | parg PUBLIC 65 | GL_SILENCE_DEPRECATION) 66 | 67 | target_compile_options( 68 | parg PUBLIC 69 | -Wno-deprecated-declarations) 70 | 71 | set(EMCCARGS 72 | -c 73 | -std=c99 74 | -O3 75 | -Wall 76 | -I../include 77 | -I../extern) 78 | set(EMCXXARGS 79 | -O3 80 | -std=c++11 81 | -Wall 82 | -I../include 83 | -I../extern) 84 | set(EMLINKARGS 85 | -O3 --memory-init-file 0 --bind 86 | --post-js ../web/js/suffix.js 87 | -s 'ALLOW_MEMORY_GROWTH=1' 88 | -s 'MODULARIZE=1' 89 | -s 'EXPORT_NAME="CreateParg"' 90 | -s 'NO_FILESYSTEM=1' 91 | -s 'PRECISE_F32=1') 92 | 93 | set(JSSRC ${SRCFILES}) 94 | list(REMOVE_ITEM JSSRC ${JSEXCLUSIONS}) 95 | foreach(SRCFILE ${JSSRC}) 96 | get_filename_component(BASENAME ${SRCFILE} NAME_WE) 97 | set(JSOBJECT ${BASENAME}.js.o) 98 | add_custom_command( 99 | OUTPUT ${JSOBJECT} 100 | COMMAND emcc -o ${JSOBJECT} ${EMCCARGS} ${SRCFILE} 101 | DEPENDS ${SRCFILE} 102 | ) 103 | list(APPEND JSOBJECTS ${JSOBJECT}) 104 | endforeach() 105 | foreach(SRCFILE ${JSCPP}) 106 | get_filename_component(BASENAME ${SRCFILE} NAME_WE) 107 | set(JSOBJECT ${BASENAME}.js.o) 108 | add_custom_command( 109 | OUTPUT ${JSOBJECT} 110 | COMMAND emcc -o ${JSOBJECT} ${EMCXXARGS} ${SRCFILE} 111 | DEPENDS ${SRCFILE} 112 | ) 113 | list(APPEND JSOBJECTS ${JSOBJECT}) 114 | endforeach() 115 | 116 | foreach(DEMONAME ${DEMOS}) 117 | if(EMSCRIPTEN) 118 | add_executable( 119 | ${DEMONAME} 120 | demos/${DEMONAME}.c 121 | ${DEMONAME}.glsl 122 | ${DEMONAME}.js) 123 | else() 124 | add_executable( 125 | ${DEMONAME} 126 | demos/${DEMONAME}.c 127 | ${DEMONAME}.glsl) 128 | endif() 129 | add_custom_command( 130 | OUTPUT ${DEMONAME}.glsl 131 | COMMAND cp ../demos/${DEMONAME}.glsl ${DEMONAME}.glsl 132 | DEPENDS demos/${DEMONAME}.glsl) 133 | target_link_libraries( 134 | ${DEMONAME} 135 | parg 136 | ${OPENGL_LIBRARIES} 137 | ${PLATFORM_LIBS}) 138 | add_custom_command( 139 | OUTPUT ${DEMONAME}.js.o 140 | COMMAND emcc -o ${DEMONAME}.js.o ${EMCCARGS} ../demos/${DEMONAME}.c 141 | DEPENDS demos/${DEMONAME}.c) 142 | add_custom_command( 143 | OUTPUT ${DEMONAME}.js 144 | COMMAND emcc -o ${DEMONAME}.js ${EMLINKARGS} ${JSOBJECTS} ${DEMONAME}.js.o 145 | DEPENDS ${DEMONAME}.js.o ${JSOBJECTS} ${POSTJS}) 146 | endforeach() 147 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Philip Rideout 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parg 2 | 3 | [![Build Status](https://travis-ci.org/prideout/parg.svg?branch=master)](https://travis-ci.org/prideout/parg) 4 | 5 | This is a C99 library with some basic stuff for bootstrapping a graphics engine. Currently it is tested against OpenGL 2.1 on OS X, and WebGL 1.0 via Emscripten. 6 | 7 | The [par](https://github.com/prideout/par) library is a required submodule, so be sure to do this after cloning the repo: 8 | 9 | ```bash 10 | $ git submodule update --init 11 | ``` 12 | 13 | The [API](https://github.com/prideout/parg/blob/master/include/parg.h) is divided into modules, each of which has a single C file: 14 | 15 | - **token** string-to-uint32 hashing, and a lookup table for uint32-to-string. 16 | - **asset** unified way of loading buffers, shaders, and textures. 17 | - **buffer** an untyped blob of memory that can live on the CPU or GPU. 18 | - **mesh** triangle meshes and utilities for procedural geometry. 19 | - **texture** thin wrapper around OpenGL texture objects. 20 | - **uniform** thin wrapper around OpenGL shader uniforms. 21 | - **state** thin wrapper around miscellaneous portions of the OpenGL state machine. 22 | - **varray** an association of buffers with vertex attributes. 23 | - **draw** thin wrapper around OpenGL draw calls. 24 | - **zcam** simple map-style camera with basic zoom & pan controls. 25 | 26 | ## How to Build for macOS 27 | 28 | ```bash 29 | brew update 30 | brew install cmake uncrustify glfw clang-format pkg-config 31 | mkdir build ; cd build ; cmake .. ; make -j 32 | ``` 33 | 34 | You can then run a demo like this: 35 | ```bash 36 | ./trefoil 37 | ``` 38 | 39 | 40 | ## How to Build for WebGL on macOS 41 | 42 | ```bash 43 | brew update 44 | brew install cmake uncrustify glfw clang-format pkg-config emscripten 45 | python emsetup.py 46 | source aliases.sh 47 | initjs && build 48 | ``` 49 | 50 | You can then run a demo like this: 51 | ```bash 52 | build/trefoil 53 | [Push Esc to quit] 54 | cd web 55 | python -m SimpleHTTPServer 56 | [Open localhost:8000 in a web browser] 57 | ``` 58 | 59 | 60 | I'm currently using: 61 | - glfw 3.3 62 | - emscripten 1.36.5 63 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - offload `parg_buffer` to be external in emscripten builds 2 | - remove ALLOW_MEMORY_GROWTH 3 | - demos/picking.c 4 | - add rong & tan 5 | - parg_data_type should be a normal enum (0 1 2 3...) 6 | - the mapping to GL types should be internal 7 | - demos/gamma.c 8 | - see the notes at the top 9 | - demos/turntable 10 | - add klein to mesh.c 11 | - see img/wallpaper.png 12 | - gles3 branch: core profile + webgl2 (USE_WEBGL2 exists in emscripten) 13 | - fluid 14 | - http://codepen.io/tmrDevelops/pen/jbbeMo 15 | - lighting with sdf's 16 | - https://t.co/sFfcR0hWDo 17 | -------------------------------------------------------------------------------- /aliases.sh: -------------------------------------------------------------------------------- 1 | alias init="cmake . -Bbuild" 2 | alias initdbg="cmake -DCMAKE_BUILD_TYPE=Debug . -Bbuild" 3 | alias initjs="cmake -DEMSCRIPTEN=ON . -Bbuild" 4 | alias build="cmake --build build" 5 | alias clean="rm -rf build" 6 | alias format=" 7 | clang-format -i src/*.c demos/*.c include/parg.h; 8 | uncrustify -l C -c uncrustify.cfg --no-backup demos/*.c src/*.c include/parg.h" 9 | -------------------------------------------------------------------------------- /demos/bubbles.c: -------------------------------------------------------------------------------- 1 | #define PAR_BUBBLES_IMPLEMENTATION 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define TOKEN_TABLE(F) \ 11 | F(P_SIMPLE, "p_simple") \ 12 | F(A_POSITION, "a_position") \ 13 | F(A_CENTER, "a_center") \ 14 | F(U_MVP, "u_mvp") \ 15 | F(U_EYEPOS, "u_eyepos") \ 16 | F(U_EYEPOS_LOWPART, "u_eyepos_lowpart") \ 17 | F(U_SEL, "u_sel") 18 | 19 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 20 | 21 | #define ASSET_TABLE(F) F(SHADER_SIMPLE, "bubbles.glsl") 22 | ASSET_TABLE(PARG_TOKEN_DECLARE); 23 | 24 | const float FOVY = 32 * PARG_TWOPI / 180; 25 | const float WORLDWIDTH = 3; 26 | const double NEAR_DURATION = 0.5; 27 | const double FAR_DURATION = 3.0; 28 | 29 | struct { 30 | int nnodes; 31 | parg_mesh* disk; 32 | par_bubbles_t* bubbles; 33 | par_bubbles_t* culled; 34 | parg_buffer* centers; 35 | int hover; 36 | int potentially_clicking; 37 | double current_time; 38 | parg_zcam_animation camera_animation; 39 | float bbwidth; 40 | int* tree; 41 | int leaf; 42 | } app = {0}; 43 | 44 | void cleanup() 45 | { 46 | par_bubbles_free_result(app.bubbles); 47 | par_bubbles_free_result(app.culled); 48 | free(app.tree); 49 | } 50 | 51 | void generate(int nnodes) 52 | { 53 | app.nnodes = nnodes; 54 | 55 | // First, generate a random tree. Square the random parent pointers to make 56 | // the graph distribution a bit more interesting, and to make it easier for 57 | // humans to find deep portions of the tree. 58 | printf("Generating tree with %d nodes...\n", nnodes); 59 | app.tree = malloc(sizeof(int) * nnodes); 60 | app.tree[0] = 0; 61 | for (int i = 1; i < app.nnodes; i++) { 62 | float a = (float) rand() / RAND_MAX; 63 | float b = (float) rand() / RAND_MAX; 64 | app.tree[i] = i * a * b; 65 | } 66 | 67 | // Perform circle packing. 68 | puts("Packing circles..."); 69 | app.bubbles = par_bubbles_hpack_circle(app.tree, nnodes, 1.0); 70 | app.hover = -1; 71 | 72 | int maxdepth; 73 | par_bubbles_get_maxdepth(app.bubbles, &maxdepth, &app.leaf); 74 | printf("Node %d has depth %d\n", app.leaf, maxdepth); 75 | parg_zcam_touch(); 76 | } 77 | 78 | void init(float winwidth, float winheight, float pixratio) 79 | { 80 | parg_state_clearcolor((Vector4){0.5, 0.6, 0.7, 1.0}); 81 | parg_state_depthtest(0); 82 | parg_state_cullfaces(1); 83 | parg_state_blending(1); 84 | parg_shader_load_from_asset(SHADER_SIMPLE); 85 | parg_zcam_init(WORLDWIDTH, WORLDWIDTH, FOVY); 86 | generate(2e4); 87 | 88 | // Create template shape. 89 | float normal[3] = {0, 0, 1}; 90 | float center[3] = {0, 0, 1}; 91 | par_shapes_mesh* template = par_shapes_create_disk(1.0, 64, center, normal); 92 | template->points[2] = 0; 93 | 94 | // Create the vertex buffer for the template shape. 95 | app.disk = parg_mesh_from_shape(template); 96 | par_shapes_free_mesh(template); 97 | 98 | // Create the vertex buffer with instance-varying data. We re-populate it 99 | // on every frame, growing it if necessary. The starting size doesn't 100 | // matter much. 101 | app.centers = parg_buffer_alloc(512 * 4 * sizeof(float), PARG_GPU_ARRAY); 102 | } 103 | 104 | void draw() 105 | { 106 | Matrix4 mvp; 107 | Point3 eyepos, eyepos_lowpart; 108 | parg_zcam_highprec(&mvp, &eyepos_lowpart, &eyepos); 109 | parg_draw_clear(); 110 | parg_shader_bind(P_SIMPLE); 111 | 112 | // For the best possible precision, we bake the pan offset into the 113 | // geometry, so there's no need to transform X and Y in the shader. 114 | Point3 eyez = {0, 0, eyepos.z}; 115 | parg_uniform_point(U_EYEPOS, &eyez); 116 | parg_uniform_point(U_EYEPOS_LOWPART, &eyepos_lowpart); 117 | 118 | parg_uniform_matrix4f(U_MVP, &mvp); 119 | parg_uniform1f(U_SEL, app.hover); 120 | parg_varray_bind(parg_mesh_index(app.disk)); 121 | parg_varray_enable( 122 | parg_mesh_coord(app.disk), A_POSITION, 3, PARG_FLOAT, 0, 0); 123 | parg_varray_instances(A_CENTER, 1); 124 | parg_varray_enable(app.centers, A_CENTER, 4, PARG_FLOAT, 0, 0); 125 | double aabb[4]; 126 | parg_zcam_get_viewportd(aabb); 127 | double minradius = 4.0 * (aabb[2] - aabb[0]) / app.bbwidth; 128 | app.culled = par_bubbles_cull(app.bubbles, aabb, minradius, app.culled); 129 | int nbytes = app.culled->count * 4 * sizeof(float); 130 | float* fdisk = parg_buffer_lock_grow(app.centers, nbytes); 131 | double const* ddisk = app.culled->xyr; 132 | 133 | // This bakes the pan offset into the geometry because it allows 134 | // us to add a double-precision number with a double-precision number. 135 | for (int i = 0; i < app.culled->count; i++, fdisk += 4, ddisk += 3) { 136 | fdisk[0] = ddisk[0] - eyepos.x; 137 | fdisk[1] = ddisk[1] - eyepos.y; 138 | fdisk[2] = ddisk[2]; 139 | fdisk[3] = app.culled->ids[i]; 140 | } 141 | parg_buffer_unlock(app.centers); 142 | parg_draw_instanced_triangles_u16( 143 | 0, parg_mesh_ntriangles(app.disk), app.culled->count); 144 | } 145 | 146 | int tick(float winwidth, float winheight, float pixratio, float seconds) 147 | { 148 | app.current_time = seconds; 149 | app.bbwidth = winwidth * pixratio; 150 | parg_zcam_animation anim = app.camera_animation; 151 | if (anim.start_time > 0) { 152 | double duration = anim.final_time - anim.start_time; 153 | double t = (app.current_time - anim.start_time) / duration; 154 | t = PARG_CLAMP(t, 0, 1); 155 | parg_zcam_blend(anim.start_view, anim.final_view, anim.blend_view, t); 156 | parg_zcam_frame_position(anim.blend_view); 157 | if (t == 1.0) { 158 | app.camera_animation.start_time = 0; 159 | } 160 | } 161 | parg_zcam_tick(winwidth / winheight, seconds); 162 | return parg_zcam_has_moved(); 163 | } 164 | 165 | void dispose() 166 | { 167 | parg_shader_free(P_SIMPLE); 168 | parg_mesh_free(app.disk); 169 | parg_buffer_free(app.centers); 170 | cleanup(); 171 | } 172 | 173 | static void zoom_to_node(int i, float duration) 174 | { 175 | parg_aar view = parg_zcam_get_rectangle(); 176 | double const* xyr = app.bubbles->xyr + i * 3; 177 | app.camera_animation.start_time = app.current_time; 178 | app.camera_animation.final_time = app.current_time + duration; 179 | app.camera_animation.start_view[0] = parg_aar_centerx(view); 180 | app.camera_animation.start_view[1] = parg_aar_centery(view); 181 | app.camera_animation.start_view[2] = parg_aar_width(view); 182 | app.camera_animation.final_view[0] = xyr[0]; 183 | app.camera_animation.final_view[1] = xyr[1]; 184 | app.camera_animation.final_view[2] = xyr[2] * 2.25; 185 | } 186 | 187 | void message(const char* msg) 188 | { 189 | if (!strcmp(msg, "20K")) { 190 | generate(2e4); 191 | } else if (!strcmp(msg, "200K")) { 192 | generate(2e5); 193 | } else if (!strcmp(msg, "2M")) { 194 | generate(2e6); 195 | } else if (!strcmp(msg, "L")) { 196 | zoom_to_node(app.leaf, FAR_DURATION); 197 | } else if (!strcmp(msg, "H")) { 198 | zoom_to_node(0, FAR_DURATION); 199 | } 200 | } 201 | 202 | void input(parg_event evt, float x, float y, float z) 203 | { 204 | DPoint3 p = parg_zcam_to_world(x, y); 205 | int key = (char) x; 206 | switch (evt) { 207 | case PARG_EVENT_KEYPRESS: 208 | if (key == '1') { 209 | message("20K"); 210 | } else if (key == '2') { 211 | message("200K"); 212 | } else if (key == '3') { 213 | message("2M"); 214 | } else if (key == 'L') { 215 | message("L"); 216 | } else if (key == 'H') { 217 | message("H"); 218 | } 219 | break; 220 | case PARG_EVENT_DOWN: 221 | app.potentially_clicking = 1; 222 | parg_zcam_grab_begin(x, y); 223 | break; 224 | case PARG_EVENT_UP: 225 | parg_zcam_grab_update(x, y, z); 226 | parg_zcam_grab_end(); 227 | if (app.potentially_clicking == 1) { 228 | int i = par_bubbles_pick(app.bubbles, p.x, p.y); 229 | if (i > -1) { 230 | zoom_to_node(i, NEAR_DURATION); 231 | } 232 | } 233 | app.potentially_clicking = 0; 234 | break; 235 | case PARG_EVENT_MOVE: { 236 | app.potentially_clicking = 0; 237 | int picked = par_bubbles_pick(app.bubbles, p.x, p.y); 238 | if (picked != app.hover) { 239 | parg_zcam_touch(); 240 | app.hover = picked; 241 | } 242 | parg_zcam_grab_update(x, y, z); 243 | break; 244 | } 245 | default: 246 | break; 247 | } 248 | } 249 | 250 | int main(int argc, char* argv[]) 251 | { 252 | puts("Press 1,2,3 to regenerate 20K, 200K or 2M nodes."); 253 | puts("Press L to zoom to one of the deepest leaf nodes."); 254 | puts("Press H to return to the home view."); 255 | srand(1); 256 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 257 | ASSET_TABLE(PARG_ASSET_TABLE); 258 | parg_window_setargs(argc, argv); 259 | parg_window_oninit(init); 260 | parg_window_ontick(tick); 261 | parg_window_ondraw(draw); 262 | parg_window_onexit(dispose); 263 | parg_window_oninput(input); 264 | parg_window_onmessage(message); 265 | return parg_window_exec(400, 400, 1, 1); 266 | } 267 | -------------------------------------------------------------------------------- /demos/bubbles.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | 4 | uniform mat4 u_mvp; 5 | uniform float u_sel; 6 | uniform vec3 u_eyepos; 7 | uniform vec3 u_eyepos_lowpart; 8 | varying float v_rim; 9 | varying vec3 v_fill; 10 | varying float v_alpha; 11 | const float STROKEW = 0.98; 12 | const vec3 FILLC = vec3(1); 13 | const vec3 STROKEC = vec3(0); 14 | const vec3 SELC = vec3(1, 1, 0); 15 | 16 | -- vertex 17 | 18 | attribute vec3 a_position; 19 | attribute vec4 a_center; 20 | 21 | void main() 22 | { 23 | vec3 cen = a_center.xyz; 24 | vec3 pos = vec3(a_position.xy * cen.z + cen.xy, 0.0); 25 | bool selected = a_center.w == u_sel; 26 | v_fill = selected ? SELC : FILLC; 27 | v_alpha = selected ? 0.4 : 0.2; 28 | v_rim = a_position.z; 29 | 30 | #ifdef SINGLE_PRECISION 31 | pos -= u_eyepos; 32 | #else 33 | vec3 poslow = vec3(0); 34 | vec3 t1 = poslow - u_eyepos_lowpart; 35 | vec3 e = t1 - poslow; 36 | vec3 t2 = ((-u_eyepos_lowpart - e) + (poslow - (t1 - e))) + pos - u_eyepos; 37 | vec3 high_delta = t1 + t2; 38 | vec3 low_delta = t2 - (high_delta - t1); 39 | pos = high_delta + low_delta; 40 | #endif 41 | 42 | gl_Position = u_mvp * vec4(pos, 1.0); 43 | } 44 | 45 | -- fragment 46 | 47 | void main() 48 | { 49 | float fw = fwidth(v_rim); 50 | float e = smoothstep(STROKEW - fw, STROKEW + fw, v_rim); 51 | vec3 v = mix(v_fill, STROKEC, e); 52 | float a = mix(v_alpha, 1.0, e); 53 | gl_FragColor = vec4(v, a); 54 | } 55 | -------------------------------------------------------------------------------- /demos/clipping.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define TOKEN_TABLE(F) \ 5 | F(P_SIMPLE, "p_simple") \ 6 | F(A_POSITION, "a_position") \ 7 | F(A_NORMAL, "a_normal") \ 8 | F(U_MVP, "u_mvp") \ 9 | F(U_IMV, "u_imv") \ 10 | F(U_CLIPZ, "u_clipz") \ 11 | F(SHADER_SIMPLE, "clipping.glsl") 12 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 13 | 14 | Matrix4 projection; 15 | Matrix4 model; 16 | Matrix4 view; 17 | parg_mesh* torus; 18 | float clipz = 0; 19 | 20 | void init(float winwidth, float winheight, float pixratio) 21 | { 22 | const Vector4 bgcolor = {0.5, 0.6, 0.7, 1.0}; 23 | parg_state_clearcolor(bgcolor); 24 | parg_state_depthtest(1); 25 | parg_state_cullfaces(0); 26 | parg_shader_load_from_asset(SHADER_SIMPLE); 27 | const float h = 5.0f; 28 | const float w = h * winwidth / winheight; 29 | const float znear = 65; 30 | const float zfar = 90; 31 | projection = M4MakeFrustum(-w, w, -h, h, znear, zfar); 32 | Point3 eye = {0, -75, 25}; 33 | Point3 target = {0, 0, 0}; 34 | Vector3 up = {0, 1, 0}; 35 | view = M4MakeLookAt(eye, target, up); 36 | model = M4MakeIdentity(); 37 | torus = parg_mesh_torus(400, 100, 8, 2); 38 | } 39 | 40 | void draw() 41 | { 42 | Matrix4 modelview = M4Mul(view, model); 43 | Matrix3 invmodelview = M4GetUpper3x3(modelview); 44 | Matrix4 mvp = M4Mul(projection, modelview); 45 | parg_draw_clear(); 46 | parg_shader_bind(P_SIMPLE); 47 | parg_uniform1f(U_CLIPZ, clipz); 48 | parg_uniform_matrix4f(U_MVP, &mvp); 49 | parg_uniform_matrix3f(U_IMV, &invmodelview); 50 | parg_varray_enable(parg_mesh_coord(torus), A_POSITION, 3, PARG_FLOAT, 0, 0); 51 | parg_varray_enable(parg_mesh_norml(torus), A_NORMAL, 3, PARG_FLOAT, 0, 0); 52 | parg_varray_bind(parg_mesh_index(torus)); 53 | parg_draw_triangles_u16(0, parg_mesh_ntriangles(torus)); 54 | } 55 | 56 | int tick(float winwidth, float winheight, float pixratio, float seconds) 57 | { 58 | clipz = 0.4 + 0.05 * sin(seconds * 3); 59 | return 1; 60 | } 61 | 62 | void dispose() 63 | { 64 | parg_shader_free(P_SIMPLE); 65 | parg_mesh_free(torus); 66 | } 67 | 68 | int main(int argc, char* argv[]) 69 | { 70 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 71 | parg_asset_preload(SHADER_SIMPLE); 72 | parg_window_setargs(argc, argv); 73 | parg_window_oninit(init); 74 | parg_window_ontick(tick); 75 | parg_window_ondraw(draw); 76 | parg_window_onexit(dispose); 77 | return parg_window_exec(185 * 5, 100 * 5, 1, 1); 78 | } 79 | -------------------------------------------------------------------------------- /demos/clipping.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | 4 | uniform mat4 u_mvp; 5 | uniform mat3 u_imv; 6 | uniform float u_clipz; 7 | varying vec3 v_normal; 8 | 9 | -- vertex 10 | 11 | attribute vec4 a_position; 12 | attribute vec3 a_normal; 13 | 14 | void main() 15 | { 16 | gl_Position = u_mvp * a_position; 17 | v_normal = u_imv * a_normal; 18 | } 19 | 20 | -- fragment 21 | 22 | vec3 LightPosition = vec3(0.25, 0.25, 1.0); 23 | vec3 AmbientMaterial = vec3(0.04, 0.04, 0.04); 24 | vec3 SpecularMaterial = vec3(0.5, 0.5, 0.5); 25 | vec3 FrontMaterial = vec3(0.75, 0.75, 0.5); 26 | vec3 BackMaterial = vec3(0.5, 0.5, 0.75); 27 | float Shininess = 50.0; 28 | 29 | void main() 30 | { 31 | vec3 N = normalize(v_normal); 32 | 33 | if (gl_FrontFacing) { 34 | N = -N; 35 | } 36 | 37 | vec3 L = normalize(LightPosition); 38 | vec3 Eye = vec3(0, 0, 1); 39 | vec3 H = normalize(L + Eye); 40 | 41 | float df = max(0.0, dot(N, L)); 42 | float sf = max(0.0, dot(N, H)); 43 | sf = pow(sf, Shininess); 44 | 45 | vec3 color = gl_FrontFacing ? FrontMaterial : BackMaterial; 46 | vec3 lighting = AmbientMaterial + df * color; 47 | if (gl_FrontFacing) { 48 | lighting += sf * SpecularMaterial; 49 | } 50 | 51 | if (gl_FragCoord.z < u_clipz) { 52 | discard; 53 | } 54 | 55 | gl_FragColor = vec4(lighting, 1.0); 56 | } 57 | -------------------------------------------------------------------------------- /demos/dragpts.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | // @program p_points, vpoints, fpoints 4 | 5 | uniform mat4 u_mvp; 6 | uniform float u_pointsize; 7 | uniform vec3 u_color; 8 | uniform float u_alpha; 9 | varying float v_rim; 10 | varying vec3 v_fill; 11 | const float STROKEW = 0.99; 12 | const vec3 STROKEC = vec3(0); 13 | 14 | -- vertex 15 | 16 | attribute vec4 a_position; 17 | 18 | void main() 19 | { 20 | vec4 p = a_position; 21 | v_rim = 1.0 - 2.0 * fract(a_position.z); 22 | v_fill = u_color; 23 | p.z = 0.0; 24 | gl_Position = u_mvp * p; 25 | } 26 | 27 | -- fragment 28 | 29 | void main() 30 | { 31 | float fw = fwidth(v_rim); 32 | float e = smoothstep(STROKEW - fw, STROKEW + fw, v_rim); 33 | vec3 v = mix(v_fill, STROKEC, e); 34 | float a = mix(u_alpha, 1.0, e); 35 | gl_FragColor = vec4(v, a); 36 | } 37 | 38 | -- vpoints 39 | 40 | attribute vec4 a_position; 41 | 42 | void main() 43 | { 44 | vec4 p = a_position; 45 | p.z = 0.0; 46 | gl_Position = u_mvp * p; 47 | gl_PointSize = u_pointsize; 48 | } 49 | 50 | -- fpoints 51 | 52 | void main() 53 | { 54 | gl_FragColor = vec4(0, 0, 0, 1); 55 | } 56 | -------------------------------------------------------------------------------- /demos/hmap.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_color, vertex, color 3 | // @program p_gray, vertex, gray 4 | // @program p_graymesh, vertex, mesh 5 | // @program p_colormesh, vertexcolor, colormesh 6 | 7 | uniform mat4 u_mvp; 8 | uniform vec4 u_color; 9 | uniform float u_zscale; 10 | varying vec2 v_texcoord; 11 | 12 | -- vertex 13 | 14 | attribute vec4 a_position; 15 | varying float v_brightness; 16 | attribute vec2 a_texcoord; 17 | 18 | void main() 19 | { 20 | vec4 p = a_position; 21 | p.z *= u_zscale; 22 | gl_Position = u_mvp * p; 23 | v_brightness = p.z * 2.5; 24 | v_texcoord = a_texcoord; 25 | } 26 | 27 | -- vertexcolor 28 | 29 | attribute vec4 a_position; 30 | varying float v_brightness; 31 | 32 | void main() 33 | { 34 | vec4 p = a_position; 35 | p.z *= 0.25; 36 | gl_Position = u_mvp * p; 37 | v_brightness = a_position.z; 38 | v_texcoord = a_position.xy; 39 | } 40 | 41 | -- color 42 | 43 | uniform sampler2D img; 44 | 45 | void main() 46 | { 47 | vec4 texel = texture2D(img, v_texcoord); 48 | vec3 back = vec3(0.937, 0.937, 0.93); // 0.0, 0.2, 0.8); 49 | gl_FragColor.rgb = mix(back, texel.rgb, texel.a); 50 | gl_FragColor.a = 1.0; 51 | } 52 | 53 | -- gray 54 | 55 | uniform sampler2D img; 56 | 57 | void main() 58 | { 59 | vec4 texel = texture2D(img, v_texcoord); 60 | float L = 0.75 * (texel.a + 1.0); 61 | gl_FragColor.rgb = vec3(L * L); 62 | gl_FragColor.a = 1.0; 63 | } 64 | 65 | -- mesh 66 | 67 | varying float v_brightness; 68 | 69 | void main() 70 | { 71 | vec3 base = u_color.rgb; 72 | gl_FragColor.rgb = mix(base, vec3(1), v_brightness); 73 | gl_FragColor.a = u_color.a; 74 | } 75 | 76 | -- colormesh 77 | 78 | uniform sampler2D img; 79 | varying float v_brightness; 80 | 81 | void main() 82 | { 83 | vec4 texel = texture2D(img, v_texcoord); 84 | gl_FragColor.rgb = texel.rgb * v_brightness; // mix(texel.rgb, vec3(1), v_brightness); 85 | gl_FragColor.a = 1.0; 86 | } 87 | -------------------------------------------------------------------------------- /demos/klein.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TOKEN_TABLE(F) \ 6 | F(P_TEXTURE, "p_texture") \ 7 | F(P_CYLINDER, "p_cylinder") \ 8 | F(P_PODIUM, "p_podium") \ 9 | F(P_LOGO, "p_logo") \ 10 | F(M_KLEIN, "klein.obj") \ 11 | F(T_KLEIN, "klein.png") \ 12 | F(T_ABSTRACT, "Abstract.png") \ 13 | F(T_LOGO, "3Dlabs.png") \ 14 | F(T_RUST, "Rust.png") \ 15 | F(A_POSITION, "a_position") \ 16 | F(A_TEXCOORD, "a_texcoord") \ 17 | F(U_MVP, "u_mvp") \ 18 | F(U_RUST, "u_rust") \ 19 | F(U_REFLECTION, "u_reflection") \ 20 | F(U_RESOLUTION, "u_resolution") \ 21 | F(S_SIMPLE, "klein.glsl") 22 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 23 | 24 | Matrix4 projection; 25 | Matrix4 model; 26 | Matrix4 view; 27 | parg_mesh* kleingeo; 28 | parg_mesh* cylinder; 29 | parg_mesh* backdrop; 30 | parg_mesh* billboard; 31 | parg_texture* kleintex; 32 | parg_texture* abstract; 33 | parg_texture* logo; 34 | parg_texture* rust; 35 | parg_framebuffer* reflection; 36 | float resolution; 37 | 38 | #define NCHARTS 9 39 | static const int CHARTS[NCHARTS] = {143, 130, 32, 61, 117, 504, 805, 585, 25}; 40 | static const int PODIUM_CHART = 2; 41 | 42 | void init(float winwidth, float winheight, float pixratio) 43 | { 44 | resolution = pixratio * winwidth; 45 | par_shapes_mesh* shape; 46 | shape = par_shapes_create_cylinder(30, 3); 47 | float axis[3] = {1, 0, 0}; 48 | par_shapes_rotate(shape, PARG_PI * 0.5, axis); 49 | cylinder = parg_mesh_from_shape(shape); 50 | par_shapes_free_mesh(shape); 51 | 52 | shape = par_shapes_create_plane(3, 3); 53 | par_shapes_scale(shape, 4, 4, 1); 54 | par_shapes_translate(shape, -2, -2, -1); 55 | backdrop = parg_mesh_from_shape(shape); 56 | par_shapes_free_mesh(shape); 57 | 58 | kleingeo = parg_mesh_from_asset(M_KLEIN); 59 | parg_mesh_send_to_gpu(kleingeo); 60 | 61 | kleintex = parg_texture_from_asset_linear(T_KLEIN); 62 | abstract = parg_texture_from_asset(T_ABSTRACT); 63 | logo = parg_texture_from_asset(T_LOGO); 64 | rust = parg_texture_from_asset(T_RUST); 65 | billboard = parg_mesh_rectangle(1, 1); 66 | reflection = parg_framebuffer_create_empty( 67 | 512, 512, PARG_FBO_LINEAR | PARG_FBO_ALPHA | PARG_FBO_DEPTH); 68 | 69 | const Vector4 transparent = {0, 0, 0, 0}; 70 | parg_state_clearcolor(transparent); 71 | 72 | parg_state_depthtest(1); 73 | parg_state_cullfaces(1); 74 | parg_shader_load_from_asset(S_SIMPLE); 75 | const float h = 1.0f; 76 | const float w = h * winwidth / winheight; 77 | const float znear = 4; 78 | const float zfar = 20; 79 | projection = M4MakeFrustum(-w, w, -h, h, znear, zfar); 80 | Point3 eye = {0, 1.8, 5}; 81 | Point3 target = {0, 0.7, 0}; 82 | Vector3 up = {0, 1, 0}; 83 | view = M4MakeLookAt(eye, target, up); 84 | model = M4MakeIdentity(); 85 | } 86 | 87 | void draw() 88 | { 89 | Matrix4 modelview = view; 90 | Matrix4 mvp = M4Mul(projection, view); 91 | 92 | // Draw the background. 93 | parg_draw_clear(); 94 | parg_shader_bind(P_TEXTURE); 95 | parg_texture_bind(abstract, 0); 96 | parg_uniform_matrix4f(U_MVP, &mvp); 97 | parg_varray_enable( 98 | parg_mesh_coord(backdrop), A_POSITION, 3, PARG_FLOAT, 0, 0); 99 | parg_varray_enable(parg_mesh_uv(backdrop), A_TEXCOORD, 2, PARG_FLOAT, 0, 0); 100 | parg_varray_bind(parg_mesh_index(backdrop)); 101 | parg_draw_triangles_u16(0, parg_mesh_ntriangles(backdrop)); 102 | parg_varray_disable(A_TEXCOORD); 103 | 104 | // Prep for the scene. 105 | modelview = M4Mul(view, model); 106 | mvp = M4Mul(projection, modelview); 107 | parg_texture_bind(kleintex, 0); 108 | parg_uniform_matrix4f(U_MVP, &mvp); 109 | parg_varray_enable( 110 | parg_mesh_coord(kleingeo), A_POSITION, 3, PARG_FLOAT, 0, 0); 111 | parg_varray_enable(parg_mesh_uv(kleingeo), A_TEXCOORD, 2, PARG_FLOAT, 0, 0); 112 | parg_varray_bind(parg_mesh_index(kleingeo)); 113 | 114 | // Draw each chart of the Klein bottle, skipping the podium disk. 115 | int start = 0; 116 | for (int i = 0; i < NCHARTS; i++) { 117 | if (i != PODIUM_CHART) { 118 | parg_draw_triangles_u16(start, CHARTS[i]); 119 | } 120 | start += CHARTS[i]; 121 | } 122 | 123 | // Draw it again for the reflection. 124 | parg_framebuffer_pushfbo(reflection, 0); 125 | parg_draw_clear(); 126 | Matrix4 invert = M4MakeScale((Vector3){1, -1, 1}); 127 | parg_state_cullfaces(0); 128 | Matrix4 flipped = M4Mul(model, invert); 129 | mvp = M4Mul(projection, M4Mul(view, flipped)); 130 | parg_uniform_matrix4f(U_MVP, &mvp); 131 | start = 0; 132 | for (int i = 0; i < NCHARTS; i++) { 133 | if (i != PODIUM_CHART) { 134 | parg_draw_triangles_u16(start, CHARTS[i]); 135 | } 136 | start += CHARTS[i]; 137 | } 138 | mvp = M4Mul(projection, modelview); 139 | parg_state_cullfaces(1); 140 | parg_framebuffer_popfbo(); 141 | 142 | // Draw the podium. 143 | parg_shader_bind(P_PODIUM); 144 | parg_texture_bind(rust, 1); 145 | parg_framebuffer_bindtex(reflection, 2); 146 | parg_uniform1i(U_RUST, 1); 147 | parg_uniform1i(U_REFLECTION, 2); 148 | parg_uniform2f(U_RESOLUTION, resolution, resolution); 149 | parg_uniform_matrix4f(U_MVP, &mvp); 150 | start = 0; 151 | for (int i = 0; i < NCHARTS; i++) { 152 | if (i == PODIUM_CHART) { 153 | parg_draw_triangles_u16(start, CHARTS[i]); 154 | } 155 | start += CHARTS[i]; 156 | } 157 | parg_varray_disable(A_TEXCOORD); 158 | 159 | // Draw the walls of the cylinder. 160 | #if CYLINDER 161 | parg_shader_bind(P_CYLINDER); 162 | parg_uniform_matrix4f(U_MVP, &mvp); 163 | parg_varray_enable( 164 | parg_mesh_coord(cylinder), A_POSITION, 3, PARG_FLOAT, 0, 0); 165 | parg_varray_bind(parg_mesh_index(cylinder)); 166 | parg_draw_triangles_u16(0, parg_mesh_ntriangles(cylinder)); 167 | #endif 168 | 169 | // Draw the logo billboard. 170 | parg_state_blending(1); 171 | parg_state_depthtest(0); 172 | parg_shader_bind(P_LOGO); 173 | parg_uniform_matrix4f(U_MVP, &mvp); 174 | parg_texture_bind(logo, 0); 175 | parg_varray_enable( 176 | parg_mesh_coord(billboard), A_POSITION, 2, PARG_FLOAT, 0, 0); 177 | parg_varray_enable( 178 | parg_mesh_coord(billboard), A_TEXCOORD, 2, PARG_FLOAT, 0, 0); 179 | parg_draw_one_quad(); 180 | parg_varray_disable(A_TEXCOORD); 181 | parg_state_blending(0); 182 | parg_state_depthtest(1); 183 | } 184 | 185 | int tick(float winwidth, float winheight, float pixratio, float seconds) 186 | { 187 | resolution = pixratio * winwidth; 188 | const float RADIANS_PER_SECOND = 3.14; 189 | float theta = seconds * RADIANS_PER_SECOND; 190 | model = M4MakeRotationY(theta); 191 | return 1; 192 | } 193 | 194 | void dispose() 195 | { 196 | parg_shader_free(P_TEXTURE); 197 | parg_mesh_free(kleingeo); 198 | parg_mesh_free(cylinder); 199 | parg_mesh_free(backdrop); 200 | parg_mesh_free(billboard); 201 | parg_framebuffer_free(reflection); 202 | parg_texture_free(kleintex); 203 | parg_texture_free(abstract); 204 | parg_texture_free(logo); 205 | parg_texture_free(rust); 206 | } 207 | 208 | int main(int argc, char* argv[]) 209 | { 210 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 211 | parg_asset_preload(S_SIMPLE); 212 | parg_asset_preload(M_KLEIN); 213 | parg_asset_preload(T_KLEIN); 214 | parg_asset_preload(T_ABSTRACT); 215 | parg_asset_preload(T_LOGO); 216 | parg_asset_preload(T_RUST); 217 | parg_window_setargs(argc, argv); 218 | parg_window_oninit(init); 219 | parg_window_ontick(tick); 220 | parg_window_ondraw(draw); 221 | parg_window_onexit(dispose); 222 | return parg_window_exec(250, 250, 1, 1); 223 | } 224 | -------------------------------------------------------------------------------- /demos/klein.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_texture, klein.vertex, texture 3 | // @program p_cylinder, klein.vertex, cylinder.fragment 4 | // @program p_podium, klein.vertex, podium.fragment 5 | // @program p_logo, logo.vertex, texture 6 | 7 | uniform mat4 u_mvp; 8 | uniform sampler2D u_ao; 9 | uniform sampler2D u_rust; 10 | uniform sampler2D u_reflection; 11 | uniform vec2 u_resolution; 12 | varying vec2 v_texcoord; 13 | varying vec3 v_position; 14 | 15 | -- klein.vertex 16 | 17 | attribute vec4 a_position; 18 | attribute vec2 a_texcoord; 19 | 20 | void main() 21 | { 22 | gl_Position = u_mvp * a_position; 23 | v_texcoord = a_texcoord; 24 | v_position = a_position.xyz; 25 | } 26 | 27 | -- logo.vertex 28 | 29 | attribute vec4 a_position; 30 | attribute vec2 a_texcoord; 31 | 32 | void main() 33 | { 34 | gl_Position = a_position; 35 | gl_Position.xy *= 0.4; 36 | gl_Position.xy += 0.7; 37 | v_texcoord = a_texcoord + 0.5; 38 | } 39 | 40 | -- texture 41 | 42 | void main() 43 | { 44 | gl_FragColor = texture2D(u_ao, v_texcoord); 45 | } 46 | 47 | -- cylinder.fragment 48 | 49 | void main() 50 | { 51 | gl_FragColor = vec4(0.56, 0.31, 0.17, 1.0); 52 | } 53 | 54 | -- podium.fragment 55 | 56 | void main() 57 | { 58 | gl_FragColor = texture2D(u_ao, v_texcoord); 59 | gl_FragColor *= texture2D(u_rust, v_position.xz); 60 | vec4 reflection = texture2D(u_reflection, gl_FragCoord.xy / u_resolution); 61 | reflection.a *= gl_FragCoord.z; 62 | gl_FragColor.rgb = mix(gl_FragColor.rgb, reflection.rgb, reflection.a); 63 | } 64 | -------------------------------------------------------------------------------- /demos/marina.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define TOKEN_TABLE(F) \ 9 | F(P_TEXTURED, "p_textured") \ 10 | F(P_HIGHP, "p_highp") \ 11 | F(A_POSITION, "a_position") \ 12 | F(A_TEXCOORD, "a_texcoord") \ 13 | F(U_MVP, "u_mvp") \ 14 | F(U_EYEPOS, "u_eyepos") \ 15 | F(U_EYEPOS_LOWPART, "u_eyepos_lowpart") 16 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 17 | 18 | #define ASSET_TABLE(F) \ 19 | F(SHADER_MARINA, "marina.glsl") \ 20 | F(TEXTURE_DOGGIES, "doggies.png") \ 21 | F(TEXTURE_ORIGIN, "origin_z02.png") 22 | ASSET_TABLE(PARG_TOKEN_DECLARE); 23 | 24 | #define ASSET_LIST(F) \ 25 | F("marina_z05.png") \ 26 | F("marina_z10.png") \ 27 | F("marina_z15.png") \ 28 | F("marina_z20.png") 29 | 30 | #define NUM_LEVELS 4 31 | const float gray = 0.8; 32 | const float fovy = 16 * PARG_TWOPI / 180; 33 | const float photo_lon = -122.3245; 34 | const float photo_lat = 37.8743; 35 | const int levels[NUM_LEVELS] = {5, 10, 15, 20}; 36 | int mode_highp = 1; 37 | parg_texture* marina_textures[NUM_LEVELS]; 38 | parg_texture* origin_texture; 39 | parg_texture* doggies_texture; 40 | parg_buffer* lines_buffer; 41 | DVector3 photo_position = {0}; 42 | parg_mesh* tile_mesh; 43 | parg_mesh* photo_mesh; 44 | float tscale; 45 | 46 | void init(float winwidth, float winheight, float pixratio) 47 | { 48 | parg_state_clearcolor((Vector4){gray, gray, gray, 1}); 49 | parg_shader_load_from_asset(SHADER_MARINA); 50 | origin_texture = parg_texture_from_asset(TEXTURE_ORIGIN); 51 | int imgwidth, imgheight; 52 | parg_texture_info(origin_texture, &imgwidth, &imgheight); 53 | assert(imgwidth == imgheight); 54 | tscale = 1280 / 256; 55 | parg_zcam_init(1, 1, fovy); 56 | parg_zcam_grab_update(0.5, 0.5, 20); 57 | tile_mesh = parg_mesh_rectangle(1, 1); 58 | photo_position.x = photo_lon / 360; 59 | float latrad = photo_lat * PARG_PI / 180; 60 | float mercN = log(tan((PARG_PI / 4) + (latrad / 2))); 61 | photo_position.y = mercN / (2 * PARG_PI); 62 | for (int i = 0; i < NUM_LEVELS; i++) { 63 | sds name = sdsnew("marina_z"); 64 | name = sdscatprintf(name, "%02d.png", levels[i]); 65 | parg_token id = parg_token_from_string(name); 66 | marina_textures[i] = parg_texture_from_asset(id); 67 | sdsfree(name); 68 | } 69 | doggies_texture = parg_texture_from_asset(TEXTURE_DOGGIES); 70 | parg_texture_info(doggies_texture, &imgwidth, &imgheight); 71 | photo_mesh = parg_mesh_rectangle(1, (float) imgheight / imgwidth); 72 | int nverts = 4; 73 | 74 | // Populate a single-precision buffer of vec2's for the lines. 75 | int vstride = sizeof(float) * 2; 76 | lines_buffer = parg_buffer_alloc(nverts * vstride, PARG_GPU_ARRAY); 77 | float* plines = parg_buffer_lock(lines_buffer, PARG_WRITE); 78 | *plines++ = photo_position.x; 79 | *plines++ = -1; 80 | *plines++ = photo_position.x; 81 | *plines++ = 1; 82 | *plines++ = -1; 83 | *plines++ = photo_position.y; 84 | *plines++ = 1; 85 | *plines++ = photo_position.y; 86 | parg_buffer_unlock(lines_buffer); 87 | } 88 | 89 | void draw() 90 | { 91 | double scale; 92 | DMatrix4 view, projection, model; 93 | Matrix4 mvp; 94 | parg_zcam_dmatrices(&projection, &view); 95 | parg_draw_clear(); 96 | 97 | // First, draw the map "tiles" (these aren't really slippy map tiles) 98 | parg_shader_bind(P_TEXTURED); 99 | parg_varray_enable( 100 | parg_mesh_coord(tile_mesh), A_POSITION, 2, PARG_FLOAT, 0, 0); 101 | parg_varray_enable( 102 | parg_mesh_uv(tile_mesh), A_TEXCOORD, 2, PARG_FLOAT, 0, 0); 103 | scale = tscale / pow(2, 2); 104 | model = DM4MakeScale((DVector3){scale, scale, scale}); 105 | mvp = M4MakeFromDM4(DM4Mul(projection, DM4Mul(view, model))); 106 | parg_texture_bind(origin_texture, 0); 107 | parg_uniform_matrix4f(U_MVP, &mvp); 108 | parg_draw_one_quad(); 109 | for (int i = 0; i < NUM_LEVELS; i++) { 110 | scale = tscale / pow(2, levels[i]); 111 | model = DM4Mul(DM4MakeTranslation(photo_position), 112 | DM4MakeScale((DVector3){scale, scale, scale})); 113 | mvp = M4MakeFromDM4(DM4Mul(projection, DM4Mul(view, model))); 114 | parg_texture_bind(marina_textures[i], 0); 115 | parg_uniform_matrix4f(U_MVP, &mvp); 116 | parg_draw_one_quad(); 117 | } 118 | 119 | // Recompute the MVP, pretending that the camera is at the origin. 120 | Point3 eyepos, eyepos_lowpart; 121 | parg_zcam_highprec(&mvp, &eyepos_lowpart, &eyepos); 122 | if (!mode_highp) { 123 | eyepos_lowpart = (Point3){0}; 124 | } 125 | 126 | // Draw the crosshair lines. 127 | parg_shader_bind(P_HIGHP); 128 | parg_uniform_matrix4f(U_MVP, &mvp); 129 | parg_uniform_point(U_EYEPOS, &eyepos); 130 | parg_uniform_point(U_EYEPOS_LOWPART, &eyepos_lowpart); 131 | parg_varray_enable(lines_buffer, A_POSITION, 2, PARG_FLOAT, 0, 0); 132 | parg_draw_lines(2); 133 | 134 | // Draw the photo. 135 | parg_shader_bind(P_TEXTURED); 136 | parg_varray_enable( 137 | parg_mesh_coord(photo_mesh), A_POSITION, 2, PARG_FLOAT, 0, 0); 138 | scale = tscale / pow(2, 25); 139 | model = DM4Mul(DM4MakeTranslation(photo_position), 140 | DM4MakeScale((DVector3){scale, scale, scale})); 141 | mvp = M4MakeFromDM4(DM4Mul(projection, DM4Mul(view, model))); 142 | parg_texture_bind(doggies_texture, 0); 143 | parg_uniform_matrix4f(U_MVP, &mvp); 144 | parg_draw_one_quad(); 145 | } 146 | 147 | int tick(float winwidth, float winheight, float pixratio, float seconds) 148 | { 149 | parg_zcam_tick(winwidth / winheight, seconds); 150 | return parg_zcam_has_moved(); 151 | } 152 | 153 | void dispose() 154 | { 155 | parg_buffer_free(lines_buffer); 156 | parg_mesh_free(tile_mesh); 157 | parg_mesh_free(photo_mesh); 158 | parg_texture_free(origin_texture); 159 | parg_texture_free(doggies_texture); 160 | for (int i = 0; i < NUM_LEVELS; i++) { 161 | parg_texture_free(marina_textures[i]); 162 | } 163 | } 164 | 165 | void input(parg_event evt, float x, float y, float z) 166 | { 167 | switch (evt) { 168 | case PARG_EVENT_DOWN: 169 | parg_zcam_grab_begin(x, y); 170 | break; 171 | case PARG_EVENT_UP: 172 | parg_zcam_grab_update(x, y, z); 173 | parg_zcam_grab_end(); 174 | break; 175 | case PARG_EVENT_MOVE: 176 | parg_zcam_grab_update(x, y, z); 177 | break; 178 | default: 179 | break; 180 | } 181 | } 182 | 183 | void message(const char* msg) 184 | { 185 | if (!strcmp(msg, "high")) { 186 | mode_highp = 1; 187 | } else if (!strcmp(msg, "low")) { 188 | mode_highp = 0; 189 | } 190 | } 191 | 192 | int main(int argc, char* argv[]) 193 | { 194 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 195 | ASSET_TABLE(PARG_ASSET_TABLE); 196 | ASSET_LIST(PARG_ASSET_LIST); 197 | parg_window_setargs(argc, argv); 198 | parg_window_oninit(init); 199 | parg_window_ontick(tick); 200 | parg_window_ondraw(draw); 201 | parg_window_onexit(dispose); 202 | parg_window_oninput(input); 203 | parg_window_onmessage(message); 204 | return parg_window_exec(400, 300, 1, 1); 205 | } 206 | -------------------------------------------------------------------------------- /demos/marina.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_textured, vertex, fragment 3 | // @program p_highp, vertex_highp, solid 4 | 5 | uniform mat4 u_mvp; 6 | uniform vec3 u_eyepos; 7 | uniform vec3 u_eyepos_lowpart; 8 | varying vec2 v_texcoord; 9 | 10 | -- vertex 11 | 12 | attribute vec4 a_position; 13 | attribute vec2 a_texcoord; 14 | 15 | void main() 16 | { 17 | gl_Position = u_mvp * a_position; 18 | v_texcoord = a_texcoord; 19 | } 20 | 21 | -- vertex_highp 22 | 23 | attribute vec3 a_position_lowpart; 24 | attribute vec3 a_position; 25 | attribute vec2 a_texcoord; 26 | 27 | void main() 28 | { 29 | vec3 t1 = a_position_lowpart - u_eyepos_lowpart; 30 | vec3 e = t1 - a_position_lowpart; 31 | vec3 t2 = ((-u_eyepos_lowpart - e) + (a_position_lowpart - (t1 - e))) + a_position - u_eyepos; 32 | vec3 high_delta = t1 + t2; 33 | vec3 low_delta = t2 - (high_delta - t1); 34 | vec3 p = high_delta + low_delta; 35 | gl_Position = u_mvp * vec4(p, 1.0); 36 | v_texcoord = a_texcoord; 37 | } 38 | 39 | -- fragment 40 | 41 | uniform sampler2D img; 42 | 43 | void main() 44 | { 45 | gl_FragColor = texture2D(img, v_texcoord); 46 | } 47 | 48 | -- solid 49 | 50 | void main() 51 | { 52 | gl_FragColor = vec4(0.2, 0.7, 0.8, 1.0); 53 | } 54 | -------------------------------------------------------------------------------- /demos/orbits.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static const int WINSIZE = 256; 8 | static const int NASTEROIDS = 3; 9 | 10 | #define TOKEN_TABLE(F) \ 11 | F(P_BACKGROUND, "p_background") \ 12 | F(P_ASTEROIDS, "p_asteroids") \ 13 | F(P_PARTICLES, "p_particles") \ 14 | F(P_PHYSICS, "p_physics") \ 15 | F(A_POSITION, "a_position") \ 16 | F(A_TEXCOORD, "a_texcoord") \ 17 | F(A_INDEX, "a_index") \ 18 | F(U_NPOINTS, "u_npoints") \ 19 | F(U_POSITIONS, "u_positions") \ 20 | F(U_PROPERTIES, "u_properties") \ 21 | F(U_DELTASQR, "u_deltasqr") \ 22 | F(U_BUFSIZE, "u_bufsize") \ 23 | F(U_TIME, "u_time") 24 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 25 | 26 | #define ASSET_TABLE(F) \ 27 | F(T_BACKGROUND, "blurrycolors.png") \ 28 | F(T_ASTEROIDS, "asteroids.png") \ 29 | F(S_ORBITS, "orbits.glsl") 30 | ASSET_TABLE(PARG_TOKEN_DECLARE); 31 | 32 | struct { 33 | parg_texture* bkgdtex; 34 | parg_texture* asteroidtex; 35 | parg_mesh* quadmesh; 36 | parg_buffer* asteroid_positions; 37 | parg_buffer* particle_indices; 38 | parg_framebuffer* particle_positionsa; 39 | parg_framebuffer* particle_positionsb; 40 | parg_framebuffer* particle_properties; 41 | float current_time; 42 | int request_physics; 43 | int playing; 44 | float* asteroids; 45 | int bufsize; 46 | int nparticles; 47 | } app; 48 | 49 | static void dispose_particles() 50 | { 51 | parg_buffer_free(app.particle_indices); 52 | parg_framebuffer_free(app.particle_positionsa); 53 | parg_framebuffer_free(app.particle_positionsb); 54 | parg_framebuffer_free(app.particle_properties); 55 | } 56 | 57 | static void create_particles() 58 | { 59 | app.nparticles = app.bufsize * app.bufsize; 60 | 61 | // Initialize a trivial "1 2 3" vertex buffer since OpenGL ES / WebGL 62 | // do not allow zero-vertex rendering, nor access to gl_Vertex. 63 | int nbytes = sizeof(float) * app.nparticles; 64 | float *inds = malloc(nbytes), *pinds = inds; 65 | for (int i = 0; i < app.nparticles; i++) { 66 | *pinds++ = i; 67 | } 68 | app.particle_indices = parg_buffer_create(inds, nbytes, PARG_GPU_ARRAY); 69 | free(inds); 70 | 71 | // Initialize particle positions. 72 | nbytes = sizeof(float) * 4 * app.nparticles; 73 | float *src = malloc(nbytes), *psrc = src; 74 | float y = 1; 75 | float d = 2.0 / (app.nparticles - 1); 76 | float vx = 0.0025; 77 | for (int i = 0; i < app.nparticles; i++) { 78 | float x = -1.0 + i * d; 79 | *psrc++ = x; 80 | *psrc++ = y; 81 | *psrc++ = x + vx; 82 | *psrc++ = y; 83 | } 84 | int res = app.bufsize; 85 | app.particle_positionsa = parg_framebuffer_create( 86 | res, res, src, nbytes, PARG_FBO_FLOAT | PARG_FBO_ALPHA); 87 | app.particle_positionsb = parg_framebuffer_create_empty( 88 | res, res, PARG_FBO_FLOAT | PARG_FBO_ALPHA); 89 | 90 | // Initialize particle properties. 91 | psrc = src; 92 | float birth = 0; 93 | for (int i = 0; i < app.nparticles; i++) { 94 | int gravindex = i % NASTEROIDS; 95 | *psrc++ = app.asteroids[gravindex * 3 + 0]; 96 | *psrc++ = app.asteroids[gravindex * 3 + 1]; 97 | *psrc++ = app.asteroids[gravindex * 3 + 2]; 98 | *psrc++ = birth; 99 | } 100 | app.particle_properties = parg_framebuffer_create( 101 | res, res, src, nbytes, PARG_FBO_FLOAT | PARG_FBO_ALPHA); 102 | free(src); 103 | } 104 | 105 | static void message(const char* msg) 106 | { 107 | if (!strcmp(msg, "play")) { 108 | app.playing = 1; 109 | } else if (!strcmp(msg, "pause")) { 110 | app.playing = 0; 111 | } else { 112 | app.bufsize = atoi(msg); 113 | printf("Switching to %dx%d (%d)\n", app.bufsize, app.bufsize, 114 | app.nparticles); 115 | } 116 | } 117 | 118 | static void draw() 119 | { 120 | const float TIMESTEP = 0.01666; 121 | 122 | if (app.nparticles != app.bufsize * app.bufsize) { 123 | dispose_particles(); 124 | create_particles(); 125 | } 126 | 127 | parg_buffer* pos = parg_mesh_coord(app.quadmesh); 128 | parg_buffer* uvs = parg_mesh_uv(app.quadmesh); 129 | 130 | parg_shader_bind(P_BACKGROUND); 131 | parg_varray_enable(pos, A_POSITION, 2, PARG_FLOAT, 0, 0); 132 | parg_varray_enable(uvs, A_TEXCOORD, 2, PARG_FLOAT, 0, 0); 133 | parg_texture_bind(app.bkgdtex, 0); 134 | parg_draw_one_quad(); 135 | 136 | parg_shader_bind(P_PHYSICS); 137 | parg_uniform1i(U_POSITIONS, 0); 138 | parg_uniform1i(U_PROPERTIES, 1); 139 | parg_uniform1f(U_DELTASQR, TIMESTEP * TIMESTEP); 140 | parg_framebuffer_bindtex(app.particle_positionsa, 0); 141 | parg_framebuffer_bindtex(app.particle_properties, 1); 142 | parg_framebuffer_pushfbo(app.particle_positionsb, 0); 143 | parg_draw_one_quad(); 144 | parg_framebuffer_popfbo(); 145 | parg_framebuffer_swap(app.particle_positionsa, app.particle_positionsb); 146 | parg_varray_disable(A_TEXCOORD); 147 | 148 | parg_shader_bind(P_ASTEROIDS); 149 | parg_state_blending(1); 150 | parg_uniform1f(U_TIME, app.current_time); 151 | parg_uniform1f(U_NPOINTS, NASTEROIDS); 152 | parg_varray_enable(app.asteroid_positions, A_POSITION, 3, PARG_FLOAT, 0, 0); 153 | parg_texture_bind(app.asteroidtex, 0); 154 | parg_draw_points(NASTEROIDS); 155 | 156 | parg_shader_bind(P_PARTICLES); 157 | parg_state_blending(2); 158 | parg_uniform1f(U_TIME, app.current_time); 159 | parg_uniform1f(U_NPOINTS, app.nparticles); 160 | parg_varray_enable(app.particle_indices, A_POSITION, 1, PARG_FLOAT, 0, 0); 161 | parg_framebuffer_bindtex(app.particle_positionsa, 0); 162 | parg_uniform1i(U_POSITIONS, 0); 163 | parg_uniform1f(U_BUFSIZE, app.bufsize); 164 | parg_draw_points(app.nparticles); 165 | parg_state_blending(0); 166 | } 167 | 168 | static void init(float winwidth, float winheight, float pixratio) 169 | { 170 | parg_shader_load_from_asset(S_ORBITS); 171 | app.quadmesh = parg_mesh_rectangle(2, 2); 172 | app.bkgdtex = parg_texture_from_asset(T_BACKGROUND); 173 | app.asteroidtex = parg_texture_from_asset(T_ASTEROIDS); 174 | 175 | // Initialize gravitational sources (asteroids). 176 | int nbytes = sizeof(float) * 3 * NASTEROIDS; 177 | float *asteroids = app.asteroids = malloc(nbytes), *psrc = asteroids; 178 | const float dtheta = PARG_TWOPI / NASTEROIDS; 179 | const float radius = 0.5; 180 | for (float i = 0, theta = 0; i < NASTEROIDS; i++, theta += dtheta) { 181 | *psrc++ = radius * cos(theta); 182 | *psrc++ = radius * sin(theta); 183 | *psrc++ = i; 184 | } 185 | app.asteroid_positions = 186 | parg_buffer_create(asteroids, nbytes, PARG_GPU_ARRAY); 187 | 188 | create_particles(); 189 | draw(); 190 | } 191 | 192 | static void dispose() 193 | { 194 | parg_mesh_free(app.quadmesh); 195 | parg_texture_free(app.bkgdtex); 196 | parg_texture_free(app.asteroidtex); 197 | parg_buffer_free(app.asteroid_positions); 198 | dispose_particles(); 199 | free(app.asteroids); 200 | } 201 | 202 | static void input(parg_event evt, float x, float y, float z) 203 | { 204 | char key = (char) x; 205 | switch (evt) { 206 | case PARG_EVENT_KEYPRESS: 207 | if (key == ' ') { 208 | switch (app.bufsize) { 209 | case 256: 210 | message("512"); 211 | break; 212 | case 512: 213 | message("1024"); 214 | break; 215 | case 1024: 216 | message("256"); 217 | break; 218 | } 219 | } 220 | break; 221 | default: 222 | break; 223 | } 224 | } 225 | 226 | static int tick(float winwidth, float winheight, float pixratio, float seconds) 227 | { 228 | app.current_time = seconds; 229 | return app.playing; 230 | } 231 | 232 | int main(int argc, char* argv[]) 233 | { 234 | app.bufsize = 512; 235 | app.playing = 1; 236 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 237 | ASSET_TABLE(PARG_ASSET_TABLE); 238 | parg_window_setargs(argc, argv); 239 | parg_window_oninit(init); 240 | parg_window_ontick(tick); 241 | parg_window_ondraw(draw); 242 | parg_window_onexit(dispose); 243 | parg_window_oninput(input); 244 | parg_window_onmessage(message); 245 | return parg_window_exec(WINSIZE, WINSIZE, 1, 0); 246 | } 247 | -------------------------------------------------------------------------------- /demos/orbits.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_background, background.vs, background.fs 3 | // @program p_physics, background.vs, physics.fs 4 | // @program p_asteroids, asteroids.vs, asteroids.fs 5 | // @program p_particles, particles.vs, particles.fs 6 | 7 | varying vec2 v_texcoord; 8 | varying float v_index; 9 | 10 | uniform float u_time; 11 | uniform float u_npoints; 12 | uniform float u_deltasqr; 13 | uniform float u_bufsize; 14 | uniform sampler2D u_image; 15 | uniform sampler2D u_positions; 16 | uniform sampler2D u_properties; 17 | 18 | const vec2 ATLAS_SIZE = vec2(256, 128); 19 | const float SPRITE_SIZE = 32.0; 20 | const float NSPRITES = 19.0; 21 | const float NCOLS = 5.0; 22 | const float ROTSPEED = 40.0; 23 | const float BRIGHTEN = 1.75; 24 | const float DARKEN = 1.0; 25 | 26 | // http://www.iquilezles.org/www/articles/palettes/palettes.htm 27 | vec3 select_color(float t) 28 | { 29 | t /= u_npoints; 30 | const vec3 a = vec3(0.5, 0.5, 0.5); 31 | const vec3 b = vec3(0.5, 0.5, 0.5); 32 | const vec3 c = vec3(2.0, 1.0, 0.0); 33 | const vec3 d = vec3(0.50, 0.20, 0.25); 34 | return a + b * cos(6.28 * (c * t + d)); 35 | } 36 | 37 | -- background.vs 38 | 39 | attribute vec4 a_position; 40 | attribute vec2 a_texcoord; 41 | 42 | void main() 43 | { 44 | gl_Position = a_position; 45 | v_texcoord = a_texcoord; 46 | } 47 | 48 | -- background.fs 49 | 50 | void main() 51 | { 52 | gl_FragColor = texture2D(u_image, v_texcoord); 53 | gl_FragColor.rgb *= DARKEN; 54 | } 55 | 56 | -- physics.fs 57 | 58 | void main() 59 | { 60 | vec4 postexel = texture2D(u_positions, v_texcoord); 61 | vec4 proptexel = texture2D(u_properties, v_texcoord); 62 | vec2 asteroid_position = proptexel.rg; 63 | vec2 current_position = postexel.rg; 64 | vec2 previous_position = postexel.ba; 65 | 66 | for (int i = 0; i < 5; i++) { 67 | float d = distance(asteroid_position, current_position); 68 | d = max(d, 0.2); // stability! 69 | vec2 force_direction = (asteroid_position - current_position) / d; 70 | float Gm = 0.1; 71 | vec2 acceleration = Gm * force_direction / (d * d); 72 | vec2 velocity = current_position - previous_position; 73 | vec2 new_position = current_position + velocity + acceleration * u_deltasqr; 74 | previous_position = current_position; 75 | current_position = new_position; 76 | } 77 | 78 | gl_FragColor.rg = current_position; 79 | gl_FragColor.ba = previous_position; 80 | } 81 | 82 | -- asteroids.vs 83 | 84 | attribute vec4 a_position; 85 | 86 | void main() 87 | { 88 | gl_Position = a_position; 89 | v_index = a_position.z; 90 | gl_Position.z = 0.0; 91 | gl_PointSize = 50.0; 92 | } 93 | 94 | -- asteroids.fs 95 | 96 | void main() 97 | { 98 | float time = v_index * 2.0 + u_time * ROTSPEED; 99 | float sprite_index = floor(mod(time, NSPRITES)); 100 | vec2 sprite = vec2(mod(sprite_index, NCOLS), floor(sprite_index / NCOLS)); 101 | vec2 uv = sprite * SPRITE_SIZE + gl_PointCoord * SPRITE_SIZE; 102 | uv.y = ATLAS_SIZE.y - 1.0 - uv.y; 103 | gl_FragColor = texture2D(u_image, uv / ATLAS_SIZE); 104 | gl_FragColor.rgb *= select_color(v_index) * BRIGHTEN; 105 | } 106 | 107 | -- particles.vs 108 | 109 | attribute float a_position; 110 | 111 | void main() 112 | { 113 | float u = mod(a_position, u_bufsize); 114 | float v = floor(a_position / u_bufsize); 115 | vec4 texel = texture2D(u_positions, vec2(u, v) / u_bufsize); 116 | gl_Position = vec4(texel.xy, 0, 1); 117 | gl_PointSize = 8.0; 118 | } 119 | 120 | -- particles.fs 121 | 122 | void main() 123 | { 124 | vec2 pc = 2.0 * (gl_PointCoord - 0.5); 125 | float r = dot(pc, pc); 126 | gl_FragColor = vec4(1); 127 | r = max(1.0 - r, 0.0); 128 | gl_FragColor = vec4(r * 0.005); 129 | } 130 | -------------------------------------------------------------------------------- /demos/shapes.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | // @program p_texture, vertex, texture 4 | 5 | uniform mat4 u_mvp; 6 | uniform mat3 u_imv; 7 | uniform sampler2D u_img; 8 | varying vec2 v_texcoord; 9 | varying vec3 v_normal; 10 | varying vec3 v_position; 11 | 12 | -- vertex 13 | 14 | attribute vec4 a_position; 15 | attribute vec2 a_texcoord; 16 | attribute vec3 a_normal; 17 | 18 | void main() 19 | { 20 | gl_Position = u_mvp * a_position; 21 | v_normal = u_imv * a_normal; 22 | v_texcoord = a_texcoord; 23 | } 24 | 25 | -- fragment 26 | 27 | #define GAMMA vec3(2.2) 28 | 29 | vec3 LightPosition = vec3(0.5, 0.25, 1.0); 30 | vec3 AmbientMaterial = pow(vec3(0.04, 0.04, 0.04), GAMMA); 31 | vec3 SpecularMaterial = pow(vec3(0.5, 0.5, 0.5), GAMMA); 32 | vec3 FrontMaterial = pow(vec3(0.75, 0.5, 0.25), GAMMA); 33 | float Shininess = 50.0; 34 | 35 | void main() 36 | { 37 | vec3 N = normalize(v_normal); 38 | vec3 L = normalize(LightPosition); 39 | vec3 Eye = vec3(0, 0, 1); 40 | vec3 H = normalize(L + Eye); 41 | float df = max(0.0, dot(N, L)); 42 | float sf = max(0.0, dot(N, H)); 43 | sf = pow(sf, Shininess); 44 | vec3 lighting = AmbientMaterial + df * FrontMaterial; 45 | lighting += sf * SpecularMaterial; 46 | lighting = pow(lighting, 1.0 / GAMMA); 47 | gl_FragColor = vec4(lighting, 1.0); 48 | } 49 | 50 | -- texture 51 | 52 | void main() 53 | { 54 | gl_FragColor = texture2D(u_img, v_texcoord); 55 | } 56 | -------------------------------------------------------------------------------- /demos/sierpinski.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define TOKEN_TABLE(F) \ 7 | F(P_SIMPLE, "p_simple") \ 8 | F(A_POSITION, "a_position") \ 9 | F(U_MVP, "u_mvp") \ 10 | F(SHADER_SIMPLE, "sierpinski.glsl") 11 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 12 | 13 | parg_mesh* trimesh; 14 | const float gray = 0.8; 15 | const float fovy = 16 * PARG_TWOPI / 180; 16 | const float worldwidth = 6000; 17 | 18 | void init(float winwidth, float winheight, float pixratio) 19 | { 20 | parg_state_clearcolor((Vector4){gray, gray, gray, 1}); 21 | parg_state_depthtest(0); 22 | parg_state_cullfaces(0); 23 | parg_shader_load_from_asset(SHADER_SIMPLE); 24 | float worldheight = worldwidth * sqrt(0.75); 25 | parg_zcam_init(worldwidth, worldheight, fovy); 26 | trimesh = parg_mesh_sierpinski(worldwidth, 10); 27 | printf("%d triangles\n", parg_mesh_ntriangles(trimesh)); 28 | } 29 | 30 | void draw() 31 | { 32 | Matrix4 view; 33 | Matrix4 projection; 34 | parg_zcam_matrices(&projection, &view); 35 | Matrix4 model = M4MakeIdentity(); 36 | Matrix4 modelview = M4Mul(view, model); 37 | Matrix4 mvp = M4Mul(projection, modelview); 38 | parg_draw_clear(); 39 | parg_shader_bind(P_SIMPLE); 40 | parg_uniform_matrix4f(U_MVP, &mvp); 41 | parg_varray_enable( 42 | parg_mesh_coord(trimesh), A_POSITION, 2, PARG_FLOAT, 0, 0); 43 | parg_draw_triangles(0, parg_mesh_ntriangles(trimesh)); 44 | } 45 | 46 | int tick(float winwidth, float winheight, float pixratio, float seconds) 47 | { 48 | parg_zcam_tick(winwidth / winheight, seconds); 49 | return 1; 50 | } 51 | 52 | void dispose() 53 | { 54 | parg_shader_free(P_SIMPLE); 55 | parg_mesh_free(trimesh); 56 | } 57 | 58 | void input(parg_event evt, float x, float y, float z) 59 | { 60 | switch (evt) { 61 | case PARG_EVENT_DOWN: 62 | parg_zcam_grab_begin(x, y); 63 | break; 64 | case PARG_EVENT_UP: 65 | parg_zcam_grab_update(x, y, z); 66 | parg_zcam_grab_end(); 67 | break; 68 | case PARG_EVENT_MOVE: 69 | parg_zcam_grab_update(x, y, z); 70 | break; 71 | default: 72 | break; 73 | } 74 | } 75 | 76 | int main(int argc, char* argv[]) 77 | { 78 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 79 | parg_asset_preload(SHADER_SIMPLE); 80 | parg_window_setargs(argc, argv); 81 | parg_window_oninit(init); 82 | parg_window_ontick(tick); 83 | parg_window_ondraw(draw); 84 | parg_window_onexit(dispose); 85 | parg_window_oninput(input); 86 | return parg_window_exec(185 * 5, 100 * 5, 1, 1); 87 | } 88 | -------------------------------------------------------------------------------- /demos/sierpinski.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | 4 | uniform mat4 u_mvp; 5 | 6 | -- vertex 7 | 8 | attribute vec4 a_position; 9 | 10 | void main() 11 | { 12 | gl_Position = u_mvp * a_position; 13 | } 14 | 15 | -- fragment 16 | 17 | void main() 18 | { 19 | gl_FragColor = vec4(0.2, 0.2, 0.2, 1.0); 20 | } 21 | -------------------------------------------------------------------------------- /demos/simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define TOKEN_TABLE(F) \ 5 | F(P_SIMPLE, "p_simple") \ 6 | F(A_POSITION, "a_position") \ 7 | F(U_MVP, "u_mvp") \ 8 | F(U_COLOR, "u_color") \ 9 | F(SHADER_SIMPLE, "simple.glsl") 10 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 11 | 12 | Matrix4 projection; 13 | Matrix4 model; 14 | Matrix4 view; 15 | parg_buffer* tricoords; 16 | 17 | void init(float winwidth, float winheight, float pixratio) 18 | { 19 | const Vector4 bgcolor = V4ScalarDiv((Vector4){78, 61, 66, 255}, 255); 20 | parg_state_clearcolor(bgcolor); 21 | parg_state_cullfaces(1); 22 | parg_shader_load_from_asset(SHADER_SIMPLE); 23 | const float h = 5.0f; 24 | const float w = h * winwidth / winheight; 25 | const float znear = 65; 26 | const float zfar = 90; 27 | projection = M4MakeFrustum(-w, w, -h, h, znear, zfar); 28 | Point3 eye = {0, 0, 75}; 29 | Point3 target = {0, 0, 0}; 30 | Vector3 up = {0, 1, 0}; 31 | view = M4MakeLookAt(eye, target, up); 32 | model = M4MakeIdentity(); 33 | tricoords = parg_buffer_alloc(sizeof(Point3) * 3, PARG_GPU_ARRAY); 34 | Point3* pdata = (Point3*) parg_buffer_lock(tricoords, PARG_WRITE); 35 | *pdata++ = (Point3){1, 1, 0}; 36 | *pdata++ = (Point3){-1, 1, 0}; 37 | *pdata++ = (Point3){0, -1, 0}; 38 | parg_buffer_unlock(tricoords); 39 | } 40 | 41 | void draw() 42 | { 43 | const Vector4 fgcolor = V4ScalarDiv((Vector4){198, 226, 233, 255}, 255); 44 | Matrix4 mvp = M4Mul(projection, M4Mul(view, model)); 45 | parg_draw_clear(); 46 | parg_shader_bind(P_SIMPLE); 47 | parg_uniform4f(U_COLOR, &fgcolor); 48 | parg_uniform_matrix4f(U_MVP, &mvp); 49 | parg_varray_enable(tricoords, A_POSITION, 3, PARG_FLOAT, 0, 0); 50 | parg_draw_triangles(0, 1); 51 | } 52 | 53 | int tick(float winwidth, float winheight, float pixratio, float seconds) 54 | { 55 | const float RADIANS_PER_SECOND = 3.14; 56 | float theta = seconds * RADIANS_PER_SECOND; 57 | model = M4MakeRotationZ(theta); 58 | return 1; 59 | } 60 | 61 | void dispose() 62 | { 63 | parg_shader_free(P_SIMPLE); 64 | parg_buffer_free(tricoords); 65 | } 66 | 67 | int main(int argc, char* argv[]) 68 | { 69 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 70 | parg_asset_preload(SHADER_SIMPLE); 71 | parg_window_setargs(argc, argv); 72 | parg_window_oninit(init); 73 | parg_window_ontick(tick); 74 | parg_window_ondraw(draw); 75 | parg_window_onexit(dispose); 76 | return parg_window_exec(185 * 5, 100 * 5, 1, 1); 77 | } 78 | -------------------------------------------------------------------------------- /demos/simple.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | 4 | uniform mat4 u_mvp; 5 | uniform vec4 u_color; 6 | varying vec4 v_color; 7 | 8 | -- vertex 9 | 10 | attribute vec4 a_position; 11 | 12 | void main() 13 | { 14 | gl_Position = u_mvp * a_position; 15 | v_color = u_color; 16 | } 17 | 18 | -- fragment 19 | 20 | void main() 21 | { 22 | gl_FragColor = v_color; 23 | } 24 | -------------------------------------------------------------------------------- /demos/terrainpts.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define PAR_BLUENOISE_IMPLEMENTATION 9 | #include 10 | 11 | #define DO_BAKE 0 12 | 13 | #define TOKEN_TABLE(F) \ 14 | F(P_SIMPLE, "p_simple") \ 15 | F(P_TEXTURED, "p_textured") \ 16 | F(A_POSITION, "a_position") \ 17 | F(A_TEXCOORD, "a_texcoord") \ 18 | F(U_MVP, "u_mvp") \ 19 | F(U_EYEPOS, "u_eyepos") \ 20 | F(U_MAGNIFICATION, "u_magnification") \ 21 | F(U_DENSITY, "u_density") \ 22 | F(U_POINTSIZE, "u_pointsize") 23 | 24 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 25 | 26 | #if DO_BAKE 27 | #define ASSET_TABLE(F) \ 28 | F(SHADER_SIMPLE, "terrainpts.glsl") \ 29 | F(TEXTURE_TERRAIN, "terrainpts.png") \ 30 | F(BUFFER_BLUENOISE, "bluenoise.bin") 31 | #else 32 | #define ASSET_TABLE(F) \ 33 | F(SHADER_SIMPLE, "terrainpts.glsl") \ 34 | F(TEXTURE_TERRAIN, "terrainpts.png") \ 35 | F(BUFFER_TERRAIN, "terrainpts.bin") 36 | #endif 37 | 38 | ASSET_TABLE(PARG_TOKEN_DECLARE); 39 | 40 | parg_buffer* ptsvbo; 41 | parg_texture* terraintex; 42 | parg_mesh* backquad; 43 | float pointscale = 1; 44 | const float fovy = 16 * PARG_TWOPI / 180; 45 | const float worldwidth = 1; 46 | const int maxpts = 1400000; 47 | const unsigned int ocean_color = 0xFFB2B283; 48 | 49 | #define clamp(x, min, max) ((x < min) ? min : ((x > max) ? max : x)) 50 | #define sqr(a) (a * a) 51 | 52 | void init(float winwidth, float winheight, float pixratio) 53 | { 54 | backquad = parg_mesh_rectangle(1, 0.5); 55 | 56 | #if DO_BAKE 57 | 58 | par_bluenoise_context* ctx; 59 | parg_buffer* buffer; 60 | void* buffer_data; 61 | 62 | printf("Reading tiles...\n"); 63 | buffer = parg_buffer_slurp_asset(BUFFER_BLUENOISE, &buffer_data); 64 | assert(buffer_data); 65 | ctx = par_bluenoise_from_buffer(buffer_data, parg_buffer_length(buffer), 0); 66 | parg_buffer_free(buffer); 67 | 68 | printf("Pushing density function...\n"); 69 | buffer = parg_buffer_slurp_asset(TEXTURE_TERRAIN, &buffer_data); 70 | assert(buffer_data); 71 | par_bluenoise_density_from_color( 72 | ctx, buffer_data + 12, 4096, 2048, 4, ocean_color, 0); 73 | 74 | printf("Generating point sequence...\n"); 75 | float* cpupts = par_bluenoise_generate_exact(ctx, maxpts, 3); 76 | ptsvbo = parg_buffer_alloc(maxpts * 12, PARG_GPU_ARRAY); 77 | float* gpupts = parg_buffer_lock(ptsvbo, PARG_WRITE); 78 | memcpy(gpupts, cpupts, parg_buffer_length(ptsvbo)); 79 | parg_buffer_unlock(ptsvbo); 80 | 81 | parg_buffer* filevbo = 82 | parg_buffer_alloc(maxpts * sizeof(float) * 3, PARG_CPU); 83 | float* filepts = parg_buffer_lock(filevbo, PARG_WRITE); 84 | memcpy(filepts, cpupts, parg_buffer_length(filevbo)); 85 | parg_buffer_unlock(filevbo); 86 | parg_buffer_to_file(filevbo, "terrainpts.bin"); 87 | parg_buffer_free(filevbo); 88 | par_bluenoise_free(ctx); 89 | 90 | #else 91 | 92 | parg_buffer* filevbo = parg_buffer_from_asset(BUFFER_TERRAIN); 93 | ptsvbo = parg_buffer_dup(filevbo, PARG_GPU_ARRAY); 94 | parg_buffer_free(filevbo); 95 | 96 | #endif 97 | 98 | terraintex = parg_texture_from_asset(TEXTURE_TERRAIN); 99 | printf("%d points.\n", maxpts); 100 | parg_state_clearcolor((Vector4){0.51, 0.7, 0.7, 1.0}); 101 | parg_state_depthtest(0); 102 | parg_state_cullfaces(0); 103 | parg_state_blending(1); 104 | parg_shader_load_from_asset(SHADER_SIMPLE); 105 | float worldheight = worldwidth * sqrt(0.75); 106 | parg_zcam_init(worldwidth, worldheight, fovy); 107 | parg_zcam_grab_update(0.5, 0.5, 30.0); 108 | } 109 | 110 | void draw() 111 | { 112 | Matrix4 view; 113 | Matrix4 projection; 114 | parg_zcam_matrices(&projection, &view); 115 | Matrix4 model = M4MakeIdentity(); 116 | 117 | Point3 eyepos; 118 | parg_zcam_highprec(0, 0, &eyepos); 119 | 120 | Matrix4 modelview = M4Mul(view, model); 121 | Matrix4 mvp = M4Mul(projection, modelview); 122 | parg_draw_clear(); 123 | parg_texture_bind(terraintex, 0); 124 | 125 | parg_shader_bind(P_TEXTURED); 126 | parg_varray_enable( 127 | parg_mesh_coord(backquad), A_POSITION, 2, PARG_FLOAT, 0, 0); 128 | parg_varray_enable(parg_mesh_uv(backquad), A_TEXCOORD, 2, PARG_FLOAT, 0, 0); 129 | parg_uniform1f(U_MAGNIFICATION, parg_zcam_get_magnification()); 130 | parg_uniform_matrix4f(U_MVP, &mvp); 131 | parg_draw_one_quad(); 132 | 133 | parg_shader_bind(P_SIMPLE); 134 | parg_uniform_matrix4f(U_MVP, &mvp); 135 | parg_uniform_point(U_EYEPOS, &eyepos); 136 | parg_uniform1f(U_MAGNIFICATION, parg_zcam_get_magnification()); 137 | parg_uniform1f(U_DENSITY, 0.1f); 138 | parg_uniform1f(U_POINTSIZE, 20.0f * pointscale); 139 | parg_varray_enable(ptsvbo, A_POSITION, 3, PARG_FLOAT, 0, 0); 140 | parg_draw_points(maxpts); 141 | } 142 | 143 | int tick(float winwidth, float winheight, float pixratio, float seconds) 144 | { 145 | pointscale = pixratio; 146 | parg_zcam_tick(winwidth / winheight, seconds); 147 | return parg_zcam_has_moved(); 148 | } 149 | 150 | void dispose() 151 | { 152 | parg_shader_free(P_SIMPLE); 153 | parg_shader_free(P_TEXTURED); 154 | parg_buffer_free(ptsvbo); 155 | parg_texture_free(terraintex); 156 | parg_mesh_free(backquad); 157 | } 158 | 159 | void input(parg_event evt, float x, float y, float z) 160 | { 161 | switch (evt) { 162 | case PARG_EVENT_DOWN: 163 | parg_zcam_grab_begin(x, y); 164 | break; 165 | case PARG_EVENT_UP: 166 | parg_zcam_grab_update(x, y, z); 167 | parg_zcam_grab_end(); 168 | break; 169 | case PARG_EVENT_MOVE: 170 | parg_zcam_grab_update(x, y, z); 171 | break; 172 | default: 173 | break; 174 | } 175 | } 176 | 177 | int main(int argc, char* argv[]) 178 | { 179 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 180 | ASSET_TABLE(PARG_ASSET_TABLE); 181 | parg_window_setargs(argc, argv); 182 | parg_window_oninit(init); 183 | parg_window_ontick(tick); 184 | parg_window_ondraw(draw); 185 | parg_window_onexit(dispose); 186 | parg_window_oninput(input); 187 | return parg_window_exec(700, 350, 1, 1); 188 | } 189 | -------------------------------------------------------------------------------- /demos/terrainpts.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | // @program p_textured, vtexture, ftexture 4 | 5 | uniform mat4 u_mvp; 6 | uniform vec3 u_eyepos; 7 | uniform float u_pointsize; 8 | uniform float u_magnification; 9 | uniform float u_density; 10 | varying float v_pointsize; 11 | varying float v_alpha; 12 | varying vec2 v_texcoord; 13 | 14 | -- vertex 15 | 16 | attribute vec3 a_position; 17 | 18 | const vec4 OUTSIDE_FRUSTUM = vec4(2, 2, 2, 1); 19 | const float FADE_DURATION = 0.25; 20 | 21 | void main() 22 | { 23 | vec4 p = vec4(a_position.xy, 0.0, 1.0); 24 | gl_Position = u_mvp * p; 25 | gl_PointSize = u_pointsize; 26 | float rank = a_position.z * u_density; 27 | float mag2 = u_magnification * u_magnification; 28 | float culled = rank - mag2; 29 | 30 | v_alpha = smoothstep(0.0, - (FADE_DURATION * mag2), culled); 31 | 32 | v_texcoord = a_position.xy; 33 | v_texcoord.y *= 2.0 ; 34 | v_texcoord += vec2(0.5, 0.5); 35 | 36 | gl_PointSize *= v_alpha; 37 | v_pointsize = gl_PointSize; 38 | 39 | if (v_alpha == 0.0) { 40 | gl_Position = OUTSIDE_FRUSTUM; 41 | } 42 | } 43 | 44 | -- fragment 45 | 46 | uniform sampler2D img; 47 | 48 | void main() 49 | { 50 | vec2 pc = 2.0 * (gl_PointCoord - 0.5); 51 | float r = dot(pc, pc); 52 | gl_FragColor.rgb = 0.3 + texture2D(img, v_texcoord).rgb; 53 | gl_FragColor.rgb *= smoothstep(1.0, 0.0, r); 54 | gl_FragColor.a = smoothstep(1.0, 0.9, r); 55 | } 56 | 57 | -- vtexture 58 | 59 | attribute vec4 a_position; 60 | attribute vec2 a_texcoord; 61 | 62 | void main() 63 | { 64 | gl_Position = u_mvp * a_position; 65 | v_texcoord = a_texcoord; 66 | } 67 | 68 | -- ftexture 69 | 70 | uniform sampler2D img; 71 | 72 | float sample(vec2 uv) 73 | { 74 | vec4 c = texture2D(img, uv); 75 | return c.r + c.g + c.b; 76 | } 77 | 78 | float computeSobelFilter(vec2 uv) 79 | { 80 | float h = 0.5 / 2048.0; 81 | float t00 = sample(uv + h * vec2(-1, -1)); 82 | float t10 = sample(uv + h * vec2( 0, -1)); 83 | float t20 = sample(uv + h * vec2( 1, -1)); 84 | float t01 = sample(uv + h * vec2(-1, 0)); 85 | float t21 = sample(uv + h * vec2( 1, 0)); 86 | float t02 = sample(uv + h * vec2(-1, 1)); 87 | float t12 = sample(uv + h * vec2( 0, 1)); 88 | float t22 = sample(uv + h * vec2( 1, 1)); 89 | vec2 grad; 90 | grad.x = t00 + 2.0 * t01 + t02 - t20 - 2.0 * t21 - t22; 91 | grad.y = t00 + 2.0 * t10 + t20 - t02 - 2.0 * t12 - t22; 92 | return step(0.01, length(grad)); 93 | } 94 | 95 | void main() 96 | { 97 | gl_FragColor = texture2D(img, v_texcoord); 98 | gl_FragColor.rgb *= 1.0 - 99 | 0.5 * 100 | smoothstep(12.0, 8.0, u_magnification) * 101 | computeSobelFilter(v_texcoord); 102 | } 103 | -------------------------------------------------------------------------------- /demos/trefoil.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define TOKEN_TABLE(F) \ 5 | F(P_SIMPLE, "p_simple") \ 6 | F(A_POSITION, "a_position") \ 7 | F(A_NORMAL, "a_normal") \ 8 | F(U_MVP, "u_mvp") \ 9 | F(U_IMV, "u_imv") \ 10 | F(SHADER_SIMPLE, "trefoil.glsl") 11 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 12 | 13 | Matrix4 projection; 14 | Matrix4 model; 15 | Matrix4 view; 16 | parg_mesh* knot; 17 | 18 | void init(float winwidth, float winheight, float pixratio) 19 | { 20 | const Vector4 bgcolor = V4ScalarDiv((Vector4){78, 61, 66, 255}, 255); 21 | parg_state_clearcolor(bgcolor); 22 | parg_state_depthtest(1); 23 | parg_state_cullfaces(1); 24 | parg_shader_load_from_asset(SHADER_SIMPLE); 25 | const float fovy = 16 * PARG_TWOPI / 180; 26 | const float aspect = (float) winwidth / winheight; 27 | const float znear = 0.1; 28 | const float zfar = 300; 29 | projection = M4MakePerspective(fovy, aspect, znear, zfar); 30 | Point3 eye = {0, 0, 4}; 31 | Point3 target = {0, 0, 0}; 32 | Vector3 up = {0, 1, 0}; 33 | view = M4MakeLookAt(eye, target, up); 34 | model = M4MakeIdentity(); 35 | knot = parg_mesh_knot(400, 100, 8, 2); 36 | } 37 | 38 | void draw() 39 | { 40 | Matrix4 modelview = M4Mul(view, model); 41 | Matrix3 invmodelview = M4GetUpper3x3(modelview); 42 | Matrix4 mvp = M4Mul(projection, modelview); 43 | parg_draw_clear(); 44 | parg_shader_bind(P_SIMPLE); 45 | parg_uniform_matrix4f(U_MVP, &mvp); 46 | parg_uniform_matrix3f(U_IMV, &invmodelview); 47 | parg_varray_enable(parg_mesh_coord(knot), A_POSITION, 3, PARG_FLOAT, 0, 0); 48 | parg_varray_enable(parg_mesh_norml(knot), A_NORMAL, 3, PARG_FLOAT, 0, 0); 49 | parg_varray_bind(parg_mesh_index(knot)); 50 | parg_draw_triangles_u16(0, parg_mesh_ntriangles(knot)); 51 | } 52 | 53 | int tick(float winwidth, float winheight, float pixratio, float seconds) 54 | { 55 | const float RADIANS_PER_SECOND = 1.57; 56 | float theta = seconds * RADIANS_PER_SECOND; 57 | model = M4MakeRotationY(theta); 58 | return 1; 59 | } 60 | 61 | void dispose() 62 | { 63 | parg_shader_free(P_SIMPLE); 64 | parg_mesh_free(knot); 65 | } 66 | 67 | int main(int argc, char* argv[]) 68 | { 69 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 70 | parg_asset_preload(SHADER_SIMPLE); 71 | parg_window_setargs(argc, argv); 72 | parg_window_oninit(init); 73 | parg_window_ontick(tick); 74 | parg_window_ondraw(draw); 75 | parg_window_onexit(dispose); 76 | return parg_window_exec(185 * 5, 100 * 5, 1, 1); 77 | } 78 | -------------------------------------------------------------------------------- /demos/trefoil.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | 4 | uniform mat4 u_mvp; 5 | uniform mat3 u_imv; 6 | varying vec3 v_normal; 7 | 8 | -- vertex 9 | 10 | attribute vec4 a_position; 11 | attribute vec3 a_normal; 12 | 13 | void main() 14 | { 15 | gl_Position = u_mvp * a_position; 16 | v_normal = u_imv * a_normal; 17 | } 18 | 19 | -- fragment 20 | 21 | vec3 LightPosition = vec3(0.25, 0.25, 1.0); 22 | vec3 AmbientMaterial = vec3(0.04, 0.04, 0.04); 23 | vec3 SpecularMaterial = vec3(0.5, 0.5, 0.5); 24 | vec3 FrontMaterial = vec3(0.25, 0.5, 0.75); 25 | float Shininess = 50.0; 26 | 27 | void main() 28 | { 29 | vec3 N = normalize(v_normal); 30 | vec3 L = normalize(LightPosition); 31 | vec3 Eye = vec3(0, 0, 1); 32 | vec3 H = normalize(L + Eye); 33 | float df = max(0.0, dot(N, L)); 34 | float sf = max(0.0, dot(N, H)); 35 | sf = pow(sf, Shininess); 36 | vec3 lighting = AmbientMaterial + df * FrontMaterial; 37 | lighting += sf * SpecularMaterial; 38 | gl_FragColor = vec4(lighting, 1.0); 39 | } 40 | -------------------------------------------------------------------------------- /demos/trillium.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define PAR_BLUENOISE_IMPLEMENTATION 8 | #include 9 | 10 | #define TOKEN_TABLE(F) \ 11 | F(P_SIMPLE, "p_simple") \ 12 | F(A_POSITION, "a_position") \ 13 | F(U_MVP, "u_mvp") \ 14 | F(U_DENSITY, "u_density") \ 15 | F(U_POINTSIZE, "u_pointsize") 16 | 17 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 18 | 19 | #define ASSET_TABLE(F) \ 20 | F(SHADER_SIMPLE, "trillium.glsl") \ 21 | F(TEXTURE_TRILLIUM, "trillium.png") \ 22 | F(BUFFER_BLUENOISE, "bluenoise.trimmed.bin") 23 | ASSET_TABLE(PARG_TOKEN_DECLARE); 24 | 25 | parg_buffer* ptsvbo; 26 | par_bluenoise_context* ctx; 27 | float pointscale = 1; 28 | const float gray = 0.8; 29 | const float fovy = 16 * PARG_TWOPI / 180; 30 | const float worldwidth = 1; 31 | const int maxpts = 100000; 32 | 33 | #define clamp(x, min, max) ((x < min) ? min : ((x > max) ? max : x)) 34 | #define sqr(a) (a * a) 35 | 36 | void init(float winwidth, float winheight, float pixratio) 37 | { 38 | parg_buffer* buffer; 39 | void* buffer_data; 40 | 41 | buffer = parg_buffer_slurp_asset(BUFFER_BLUENOISE, &buffer_data); 42 | ctx = par_bluenoise_from_buffer( 43 | buffer_data, parg_buffer_length(buffer), maxpts); 44 | parg_buffer_free(buffer); 45 | 46 | buffer = parg_buffer_slurp_asset(TEXTURE_TRILLIUM, &buffer_data); 47 | par_bluenoise_density_from_gray(ctx, buffer_data + 12, 3500, 3500, 4); 48 | parg_buffer_free(buffer); 49 | 50 | ptsvbo = parg_buffer_alloc(maxpts * sizeof(float) * 3, PARG_GPU_ARRAY); 51 | parg_state_clearcolor((Vector4){gray, gray, gray, 1}); 52 | parg_state_depthtest(0); 53 | parg_state_cullfaces(0); 54 | parg_shader_load_from_asset(SHADER_SIMPLE); 55 | float worldheight = worldwidth; 56 | parg_zcam_init(worldwidth, worldheight, fovy); 57 | } 58 | 59 | void draw() 60 | { 61 | float lbrt[4]; 62 | parg_zcam_get_viewport(lbrt); 63 | float left = lbrt[0]; 64 | float bottom = lbrt[1]; 65 | float right = lbrt[2]; 66 | float top = lbrt[3]; 67 | par_bluenoise_set_viewport(ctx, left, bottom, right, top); 68 | 69 | int npts; 70 | float* cpupts = par_bluenoise_generate(ctx, 40000, &npts); 71 | float* gpupts = parg_buffer_lock(ptsvbo, PARG_WRITE); 72 | memcpy(gpupts, cpupts, npts * 3 * sizeof(float)); 73 | parg_buffer_unlock(ptsvbo); 74 | 75 | Matrix4 view; 76 | Matrix4 projection; 77 | parg_zcam_matrices(&projection, &view); 78 | Matrix4 model = M4MakeIdentity(); 79 | Matrix4 modelview = M4Mul(view, model); 80 | Matrix4 mvp = M4Mul(projection, modelview); 81 | parg_draw_clear(); 82 | parg_shader_bind(P_SIMPLE); 83 | parg_uniform_matrix4f(U_MVP, &mvp); 84 | parg_uniform1f(U_POINTSIZE, 2.5f * pointscale); 85 | parg_varray_enable(ptsvbo, A_POSITION, 3, PARG_FLOAT, 0, 0); 86 | parg_draw_points(npts); 87 | } 88 | 89 | int tick(float winwidth, float winheight, float pixratio, float seconds) 90 | { 91 | pointscale = pixratio; 92 | parg_zcam_tick(winwidth / winheight, seconds); 93 | return parg_zcam_has_moved(); 94 | } 95 | 96 | void dispose() 97 | { 98 | parg_shader_free(P_SIMPLE); 99 | parg_buffer_free(ptsvbo); 100 | } 101 | 102 | void input(parg_event evt, float x, float y, float z) 103 | { 104 | switch (evt) { 105 | case PARG_EVENT_DOWN: 106 | parg_zcam_grab_begin(x, y); 107 | break; 108 | case PARG_EVENT_UP: 109 | parg_zcam_grab_update(x, y, z); 110 | parg_zcam_grab_end(); 111 | break; 112 | case PARG_EVENT_MOVE: 113 | parg_zcam_grab_update(x, y, z); 114 | break; 115 | default: 116 | break; 117 | } 118 | } 119 | 120 | int main(int argc, char* argv[]) 121 | { 122 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 123 | ASSET_TABLE(PARG_ASSET_TABLE); 124 | parg_window_setargs(argc, argv); 125 | parg_window_oninit(init); 126 | parg_window_ontick(tick); 127 | parg_window_ondraw(draw); 128 | parg_window_onexit(dispose); 129 | parg_window_oninput(input); 130 | return parg_window_exec(700, 350, 1, 1); 131 | } 132 | -------------------------------------------------------------------------------- /demos/trillium.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | 4 | uniform mat4 u_mvp; 5 | uniform float u_magnification; 6 | uniform float u_pointsize; 7 | varying float v_alpha; 8 | 9 | -- vertex 10 | 11 | attribute vec3 a_position; 12 | 13 | void main() 14 | { 15 | vec4 p = vec4(a_position.xy, 0.0, 1.0); 16 | gl_Position = u_mvp * p; 17 | v_alpha = 1.0; 18 | gl_PointSize = u_pointsize * v_alpha; 19 | } 20 | 21 | -- fragment 22 | 23 | void main() 24 | { 25 | float L = 0.8 - v_alpha * 0.6; 26 | gl_FragColor = vec4(L, L, L, 1.0); 27 | } 28 | -------------------------------------------------------------------------------- /demos/zooming.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TOKEN_TABLE(F) \ 6 | F(P_SIMPLE, "p_simple") \ 7 | F(A_POSITION, "a_position") \ 8 | F(A_TEXCOORD, "a_texcoord") \ 9 | F(U_MVP, "u_mvp") 10 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 11 | 12 | #define ASSET_TABLE(F) \ 13 | F(SHADER_ZOOMING, "zooming.glsl") \ 14 | F(TEXTURE_PALMS, "arecaceae.png") 15 | ASSET_TABLE(PARG_TOKEN_DECLARE); 16 | 17 | parg_mesh* rectmesh; 18 | parg_texture* palmstexture; 19 | 20 | const float gray = 0.8; 21 | const float fovy = 16 * PARG_TWOPI / 180; 22 | const float worldwidth = 6000; 23 | 24 | void init(float winwidth, float winheight, float pixratio) 25 | { 26 | parg_state_clearcolor((Vector4){gray, gray, gray, 1}); 27 | parg_state_depthtest(0); 28 | parg_state_cullfaces(0); 29 | parg_shader_load_from_asset(SHADER_ZOOMING); 30 | palmstexture = parg_texture_from_asset(TEXTURE_PALMS); 31 | int imgwidth, imgheight; 32 | parg_texture_info(palmstexture, &imgwidth, &imgheight); 33 | float worldheight = worldwidth * imgheight / imgwidth; 34 | parg_zcam_init(worldwidth, worldheight, fovy); 35 | rectmesh = parg_mesh_rectangle(worldwidth, worldheight); 36 | } 37 | 38 | void draw() 39 | { 40 | Matrix4 view; 41 | Matrix4 projection; 42 | parg_zcam_matrices(&projection, &view); 43 | Matrix4 model = M4MakeIdentity(); 44 | Matrix4 modelview = M4Mul(view, model); 45 | Matrix4 mvp = M4Mul(projection, modelview); 46 | parg_draw_clear(); 47 | parg_shader_bind(P_SIMPLE); 48 | parg_texture_bind(palmstexture, 0); 49 | parg_uniform_matrix4f(U_MVP, &mvp); 50 | parg_varray_enable( 51 | parg_mesh_coord(rectmesh), A_POSITION, 2, PARG_FLOAT, 0, 0); 52 | parg_varray_enable(parg_mesh_uv(rectmesh), A_TEXCOORD, 2, PARG_FLOAT, 0, 0); 53 | parg_draw_one_quad(); 54 | } 55 | 56 | int tick(float winwidth, float winheight, float pixratio, float seconds) 57 | { 58 | parg_zcam_tick(winwidth / winheight, seconds); 59 | return parg_zcam_has_moved(); 60 | } 61 | 62 | void dispose() 63 | { 64 | parg_shader_free(P_SIMPLE); 65 | parg_mesh_free(rectmesh); 66 | parg_texture_free(palmstexture); 67 | } 68 | 69 | void input(parg_event evt, float x, float y, float z) 70 | { 71 | switch (evt) { 72 | case PARG_EVENT_DOWN: 73 | parg_zcam_grab_begin(x, y); 74 | break; 75 | case PARG_EVENT_UP: 76 | parg_zcam_grab_update(x, y, z); 77 | parg_zcam_grab_end(); 78 | break; 79 | case PARG_EVENT_MOVE: 80 | parg_zcam_grab_update(x, y, z); 81 | break; 82 | default: 83 | break; 84 | } 85 | } 86 | 87 | int main(int argc, char* argv[]) 88 | { 89 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 90 | ASSET_TABLE(PARG_ASSET_TABLE); 91 | parg_window_setargs(argc, argv); 92 | parg_window_oninit(init); 93 | parg_window_ontick(tick); 94 | parg_window_ondraw(draw); 95 | parg_window_onexit(dispose); 96 | parg_window_oninput(input); 97 | return parg_window_exec(185 * 5, 100 * 5, 1, 1); 98 | } 99 | -------------------------------------------------------------------------------- /demos/zooming.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_simple, vertex, fragment 3 | 4 | uniform mat4 u_mvp; 5 | varying vec2 v_texcoord; 6 | 7 | -- vertex 8 | 9 | attribute vec4 a_position; 10 | attribute vec2 a_texcoord; 11 | 12 | void main() 13 | { 14 | gl_Position = u_mvp * a_position; 15 | v_texcoord = a_texcoord; 16 | } 17 | 18 | -- fragment 19 | 20 | uniform sampler2D img; 21 | 22 | const float brightness = 0.9; 23 | 24 | void main() 25 | { 26 | float luma = texture2D(img, v_texcoord).x; 27 | float fwid = fwidth(luma); 28 | luma = brightness * smoothstep(0.5 - fwid, 0.5 + fwid, luma); 29 | gl_FragColor = vec4(vec3(luma), 1); 30 | } 31 | -------------------------------------------------------------------------------- /demos/ztex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define PAR_MSQUARES_IMPLEMENTATION 8 | #include 9 | 10 | #define TOKEN_TABLE(F) \ 11 | F(P_LANDMASS, "p_landmass") \ 12 | F(P_LANDMASS_FRAGCOORD, "p_landmass_fragcoord") \ 13 | F(P_OCEAN, "p_ocean") \ 14 | F(P_SOLID, "p_solid") \ 15 | F(A_POSITION, "a_position") \ 16 | F(U_MVP, "u_mvp") \ 17 | F(U_COLOR, "u_color") \ 18 | F(U_SHOWGRID, "u_showgrid") \ 19 | F(U_SLIPPYBOX, "u_slippybox") \ 20 | F(U_SLIPPYFRACT, "u_slippyfract") 21 | TOKEN_TABLE(PARG_TOKEN_DECLARE); 22 | 23 | #define ASSET_TABLE(F) \ 24 | F(SHADER_DEFAULT, "ztex.glsl") \ 25 | F(TEXTURE_PAPER, "paper.png") \ 26 | F(TEXTURE_EUROPE, "europe.png") \ 27 | F(TEXTURE_OCEAN, "water.png") 28 | ASSET_TABLE(PARG_TOKEN_DECLARE); 29 | 30 | const Vector3 TARGETPOS = {0.35287, 0.005156, 0.000005}; 31 | const float DEMO_DURATION = 6; 32 | const double STARTZ = 1.2; 33 | const float fovy = 16 * PARG_TWOPI / 180; 34 | int mode_highp = 1; 35 | float current_time; 36 | float mode_demo_start = 0; 37 | int mode_demo_direction = 1; 38 | int showgrid = 0; 39 | parg_mesh* landmass_mesh; 40 | parg_mesh* ocean_mesh; 41 | parg_texture* ocean_texture; 42 | parg_texture* paper_texture; 43 | Vector2 fbsize; 44 | 45 | void init(float winwidth, float winheight, float pixratio) 46 | { 47 | printf( 48 | "Spacebar to toggle texture modes.\n" 49 | "D to toggle auto-zooming demo mode.\n" 50 | "G to toggle the slippy map grid.\n"); 51 | parg_state_clearcolor((Vector4){0.43, 0.61, 0.8, 1}); 52 | parg_state_cullfaces(1); 53 | parg_state_depthtest(0); 54 | parg_shader_load_from_asset(SHADER_DEFAULT); 55 | parg_zcam_init(1, 1, fovy); 56 | parg_zcam_set_position(0, 0, STARTZ); 57 | ocean_texture = parg_texture_from_asset(TEXTURE_OCEAN); 58 | paper_texture = parg_texture_from_asset(TEXTURE_PAPER); 59 | 60 | // Decode the europe image. 61 | int* rawdata; 62 | parg_buffer* colorbuf = 63 | parg_buffer_slurp_asset(TEXTURE_EUROPE, (void*) &rawdata); 64 | int width = *rawdata++; 65 | int height = *rawdata++; 66 | int ncomps = *rawdata++; 67 | parg_texture_fliprows(rawdata, width * ncomps, height); 68 | 69 | // Sample the ocean color from one corner of the image. 70 | int ocean_color = rawdata[0]; 71 | 72 | // Perform marching squares and generate a mesh. 73 | par_msquares_meshlist* mlist = 74 | par_msquares_color((parg_byte*) rawdata, width, height, 16, ocean_color, 75 | 4, PAR_MSQUARES_SWIZZLE | PAR_MSQUARES_DUAL | PAR_MSQUARES_HEIGHTS | 76 | PAR_MSQUARES_SIMPLIFY); 77 | par_msquares_mesh const* mesh; 78 | mesh = par_msquares_get_mesh(mlist, 0); 79 | landmass_mesh = parg_mesh_create( 80 | mesh->points, mesh->npoints, mesh->triangles, mesh->ntriangles); 81 | mesh = par_msquares_get_mesh(mlist, 1); 82 | ocean_mesh = parg_mesh_create( 83 | mesh->points, mesh->npoints, mesh->triangles, mesh->ntriangles); 84 | parg_buffer_unlock(colorbuf); 85 | par_msquares_free(mlist); 86 | } 87 | 88 | void draw() 89 | { 90 | DMatrix4 view, projection; 91 | parg_zcam_dmatrices(&projection, &view); 92 | DMatrix4 model = DM4MakeTranslation((DVector3){-0.5, -0.5, -1}); 93 | Matrix4 mvp = M4MakeFromDM4(DM4Mul(projection, DM4Mul(view, model))); 94 | const Vector4 BLACK = {0, 0, 0, 1}; 95 | 96 | Vector2 mapsize = {1, 1}; 97 | parg_aar rect = parg_zcam_get_rectangle(); 98 | parg_tilerange tiles; 99 | float slippyfract = parg_aar_to_tilerange(rect, mapsize, &tiles); 100 | parg_aar slippyaar = parg_aar_from_tilename(tiles.mintile, mapsize); 101 | Vector4* slippybox = (Vector4*) &slippyaar; 102 | slippybox->z = 1.0 / (slippybox->z - slippybox->x); 103 | slippybox->w = 1.0 / (slippybox->w - slippybox->y); 104 | 105 | parg_draw_clear(); 106 | parg_shader_bind(P_OCEAN); 107 | parg_uniform_matrix4f(U_MVP, &mvp); 108 | parg_uniform1i(U_SHOWGRID, showgrid); 109 | parg_uniform4f(U_SLIPPYBOX, slippybox); 110 | parg_uniform1f(U_SLIPPYFRACT, slippyfract); 111 | parg_texture_bind(ocean_texture, 0); 112 | parg_varray_bind(parg_mesh_index(ocean_mesh)); 113 | parg_varray_enable( 114 | parg_mesh_coord(ocean_mesh), A_POSITION, 3, PARG_FLOAT, 0, 0); 115 | parg_draw_triangles_u16(0, parg_mesh_ntriangles(ocean_mesh)); 116 | parg_shader_bind(P_SOLID); 117 | parg_uniform_matrix4f(U_MVP, &mvp); 118 | parg_uniform4f(U_COLOR, &BLACK); 119 | parg_varray_bind(parg_mesh_index(landmass_mesh)); 120 | parg_varray_enable( 121 | parg_mesh_coord(landmass_mesh), A_POSITION, 3, PARG_FLOAT, 0, 0); 122 | parg_draw_wireframe_triangles_u16(0, parg_mesh_ntriangles(landmass_mesh)); 123 | 124 | if (mode_highp) { 125 | float x = parg_aar_width(rect) / fbsize.x; 126 | float y = parg_aar_height(rect) / fbsize.y; 127 | slippybox->z *= x; 128 | slippybox->w *= y; 129 | slippybox->x = (slippybox->x - rect.left) / x; 130 | slippybox->y = (slippybox->y - rect.bottom) / y; 131 | } 132 | 133 | parg_shader_bind(mode_highp ? P_LANDMASS_FRAGCOORD : P_LANDMASS); 134 | parg_uniform_matrix4f(U_MVP, &mvp); 135 | parg_uniform1i(U_SHOWGRID, showgrid); 136 | parg_uniform4f(U_SLIPPYBOX, slippybox); 137 | parg_uniform1f(U_SLIPPYFRACT, slippyfract); 138 | parg_texture_bind(paper_texture, 0); 139 | parg_draw_triangles_u16(0, parg_mesh_ntriangles(landmass_mesh)); 140 | } 141 | 142 | int tick(float winwidth, float winheight, float pixratio, float seconds) 143 | { 144 | current_time = seconds; 145 | fbsize.x = winwidth * pixratio; 146 | fbsize.y = winheight * pixratio; 147 | parg_zcam_tick(winwidth / winheight, seconds); 148 | if (mode_demo_start) { 149 | double t = (current_time - mode_demo_start) / DEMO_DURATION; 150 | if (t >= 1) { 151 | mode_demo_start = 0; 152 | } 153 | float scrolldelta = mode_demo_direction ? -3 : 3; 154 | parg_zcam_grab_update(TARGETPOS.x, TARGETPOS.y, scrolldelta); 155 | } 156 | return parg_zcam_has_moved(); 157 | } 158 | 159 | void dispose() 160 | { 161 | parg_mesh_free(landmass_mesh); 162 | parg_mesh_free(ocean_mesh); 163 | parg_texture_free(ocean_texture); 164 | parg_texture_free(paper_texture); 165 | } 166 | 167 | void input(parg_event evt, float x, float y, float z) 168 | { 169 | char key = (char) x; 170 | switch (evt) { 171 | case PARG_EVENT_KEYPRESS: 172 | if (key == ' ') { 173 | mode_highp = !mode_highp; 174 | printf("Precision %s.\n", mode_highp ? "on" : "off"); 175 | parg_zcam_touch(); 176 | } else if (key == 'G') { 177 | showgrid = 1 - showgrid; 178 | parg_zcam_touch(); 179 | } else if (key == 'D') { 180 | if (!mode_demo_start) { 181 | mode_demo_start = current_time; 182 | mode_demo_direction = 1 - mode_demo_direction; 183 | } 184 | } 185 | break; 186 | case PARG_EVENT_DOWN: 187 | parg_zcam_grab_begin(x, y); 188 | break; 189 | case PARG_EVENT_UP: 190 | parg_zcam_grab_update(x, y, z); 191 | parg_zcam_grab_end(); 192 | break; 193 | case PARG_EVENT_MOVE: 194 | parg_zcam_grab_update(x, y, z); 195 | break; 196 | default: 197 | break; 198 | } 199 | } 200 | 201 | void message(const char* msg) 202 | { 203 | if (!strcmp(msg, "precision")) { 204 | mode_highp = 1 - mode_highp; 205 | parg_zcam_touch(); 206 | } else if (!strcmp(msg, "grid")) { 207 | showgrid = 1 - showgrid; 208 | parg_zcam_touch(); 209 | } else if (!strcmp(msg, "demo")) { 210 | if (!mode_demo_start) { 211 | mode_demo_start = current_time; 212 | mode_demo_direction = 1 - mode_demo_direction; 213 | } 214 | } 215 | } 216 | 217 | int main(int argc, char* argv[]) 218 | { 219 | TOKEN_TABLE(PARG_TOKEN_DEFINE); 220 | ASSET_TABLE(PARG_ASSET_TABLE); 221 | parg_window_setargs(argc, argv); 222 | parg_window_oninit(init); 223 | parg_window_ontick(tick); 224 | parg_window_ondraw(draw); 225 | parg_window_onexit(dispose); 226 | parg_window_oninput(input); 227 | parg_window_onmessage(message); 228 | return parg_window_exec(400, 300, 1, 1); 229 | } 230 | -------------------------------------------------------------------------------- /demos/ztex.glsl: -------------------------------------------------------------------------------- 1 | 2 | // @program p_landmass, vertex, landmass 3 | // @program p_landmass_fragcoord, vertex, landmass_fragcoord 4 | // @program p_ocean, vertex, ocean 5 | // @program p_solid, vertex, solid 6 | 7 | uniform mat4 u_mvp; 8 | uniform vec4 u_color; 9 | uniform vec4 u_slippybox; 10 | uniform float u_slippyfract; 11 | uniform bool u_showgrid; 12 | uniform sampler2D u_texture; 13 | varying vec2 v_texcoord; 14 | 15 | const float LANDMASS_TEXTURE_FREQUENCY = 4.0; 16 | const float OCEAN_TEXTURE_FREQUENCY = 16.0; 17 | const vec4 LANDMASS_COLOR = vec4(0.5, 0.7, 0.6, 1.0); 18 | 19 | vec4 sample(vec2 uv) 20 | { 21 | vec4 texel = texture2D(u_texture, uv); 22 | if (u_showgrid) { 23 | uv = mod(uv, vec2(1)); 24 | vec2 del = abs(vec2(0.5) - uv); 25 | vec2 m = 0.5 * smoothstep(0.5, 0.45, del); 26 | texel *= m.x * m.y + 0.5; 27 | } 28 | return texel; 29 | } 30 | 31 | -- vertex 32 | 33 | attribute vec4 a_position; 34 | 35 | void main() 36 | { 37 | gl_Position = u_mvp * a_position; 38 | v_texcoord = a_position.xy; 39 | } 40 | 41 | -- ocean 42 | 43 | void main() 44 | { 45 | vec2 tex_offset = v_texcoord - u_slippybox.xy; 46 | vec2 uv = tex_offset * u_slippybox.zw; 47 | gl_FragColor = sample(uv * OCEAN_TEXTURE_FREQUENCY); 48 | } 49 | 50 | -- solid 51 | 52 | void main() 53 | { 54 | gl_FragColor = u_color; 55 | } 56 | 57 | -- landmass 58 | 59 | void main() 60 | { 61 | vec2 tex_offset = v_texcoord - u_slippybox.xy; 62 | vec2 uv = tex_offset * u_slippybox.zw; 63 | vec4 texel0 = sample(uv * LANDMASS_TEXTURE_FREQUENCY); 64 | vec4 texel1 = sample(uv * LANDMASS_TEXTURE_FREQUENCY * 2.0); 65 | vec4 mixed = mix(texel0, texel1, u_slippyfract); 66 | gl_FragColor = LANDMASS_COLOR * mixed; 67 | } 68 | 69 | -- landmass_fragcoord 70 | 71 | void main() 72 | { 73 | vec2 tex_offset = gl_FragCoord.xy - u_slippybox.xy; 74 | vec2 uv = tex_offset * u_slippybox.zw; 75 | vec4 texel0 = sample(uv * LANDMASS_TEXTURE_FREQUENCY); 76 | vec4 texel1 = sample(uv * LANDMASS_TEXTURE_FREQUENCY * 2.0); 77 | vec4 mixed = mix(texel0, texel1, u_slippyfract); 78 | gl_FragColor = LANDMASS_COLOR * mixed; 79 | } 80 | -------------------------------------------------------------------------------- /emsetup.py: -------------------------------------------------------------------------------- 1 | import fileinput, os 2 | 3 | PREFIX = 'LLVM_ROOT = ' 4 | 5 | os.system('emcc') 6 | inifile = os.path.expanduser('~/.emscripten') 7 | for line in fileinput.input(inifile, inplace=True): 8 | if line.startswith(PREFIX): 9 | print PREFIX + "'/usr/local/opt/emscripten/libexec/llvm/bin'" 10 | else: 11 | print line, 12 | -------------------------------------------------------------------------------- /extern/kvec.h: -------------------------------------------------------------------------------- 1 | /* The MIT License 2 | 3 | Copyright (c) 2008, by Attractive Chaos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | /* 27 | An example: 28 | 29 | #include "kvec.h" 30 | int main() { 31 | kvec_t(int) array; 32 | kv_init(array); 33 | kv_push(int, array, 10); // append 34 | kv_a(int, array, 20) = 5; // dynamic 35 | kv_A(array, 20) = 4; // static 36 | kv_destroy(array); 37 | return 0; 38 | } 39 | */ 40 | 41 | /* 42 | 2008-09-22 (0.1.0): 43 | 44 | * The initial version. 45 | 46 | */ 47 | 48 | #ifndef AC_KVEC_H 49 | #define AC_KVEC_H 50 | 51 | #include 52 | 53 | #define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) 54 | 55 | #define kvec_t(type) struct { size_t n, m; type *a; } 56 | #define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) 57 | #define kv_destroy(v) free((v).a) 58 | #define kv_A(v, i) ((v).a[(i)]) 59 | #define kv_pop(v) ((v).a[--(v).n]) 60 | #define kv_size(v) ((v).n) 61 | #define kv_max(v) ((v).m) 62 | 63 | #define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m)) 64 | 65 | #define kv_copy(type, v1, v0) do { \ 66 | if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \ 67 | (v1).n = (v0).n; \ 68 | memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ 69 | } while (0) \ 70 | 71 | #define kv_push(type, v, x) do { \ 72 | if ((v).n == (v).m) { \ 73 | (v).m = (v).m? (v).m<<1 : 2; \ 74 | (v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \ 75 | } \ 76 | (v).a[(v).n++] = (x); \ 77 | } while (0) 78 | 79 | #define kv_pushp(type, v) (((v).n == (v).m)? \ 80 | ((v).m = ((v).m? (v).m<<1 : 2), \ 81 | (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ 82 | : 0), ((v).a + ((v).n++)) 83 | 84 | #define kv_a(type, v, i) (((v).m <= (size_t)(i)? \ 85 | ((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \ 86 | (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ 87 | : (v).n <= (size_t)(i)? (v).n = (i) + 1 \ 88 | : 0), (v).a[(i)]) 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /extern/sdsalloc.h: -------------------------------------------------------------------------------- 1 | /* SDSLib 2.0 -- A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo 4 | * Copyright (c) 2015, Oran Agra 5 | * Copyright (c) 2015, Redis Labs, Inc 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | /* SDS allocator selection. 34 | * 35 | * This file is used in order to change the SDS allocator at compile time. 36 | * Just define the following defines to what you want to use. Also add 37 | * the include of your alternate allocator if needed (not needed in order 38 | * to use the default libc allocator). */ 39 | 40 | #define s_malloc malloc 41 | #define s_realloc realloc 42 | #define s_free free 43 | -------------------------------------------------------------------------------- /extern/whereami.h: -------------------------------------------------------------------------------- 1 | // (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) 2 | 3 | #ifndef WHEREAMI_H 4 | #define WHEREAMI_H 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #ifndef WAI_FUNCSPEC 11 | #define WAI_FUNCSPEC 12 | #endif 13 | #ifndef WAI_PREFIX 14 | #define WAI_PREFIX(function) wai_##function 15 | #endif 16 | 17 | /** 18 | * Returns the path to the current executable. 19 | * 20 | * Usage: 21 | * - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to 22 | * retrieve the length of the path 23 | * - allocate the destination buffer with `path = (char*)malloc(length + 1);` 24 | * - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the 25 | * path 26 | * - add a terminal NUL character with `path[length] = '\0';` 27 | * 28 | * @param out destination buffer, optional 29 | * @param capacity destination buffer capacity 30 | * @param dirname_length optional recipient for the length of the dirname part 31 | * of the path. 32 | * 33 | * @return the length of the executable path on success (without a terminal NUL 34 | * character), otherwise `-1` 35 | */ 36 | WAI_FUNCSPEC 37 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length); 38 | 39 | /** 40 | * Returns the path to the current module 41 | * 42 | * Usage: 43 | * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve 44 | * the length of the path 45 | * - allocate the destination buffer with `path = (char*)malloc(length + 1);` 46 | * - call `wai_getModulePath(path, length, NULL)` again to retrieve the path 47 | * - add a terminal NUL character with `path[length] = '\0';` 48 | * 49 | * @param out destination buffer, optional 50 | * @param capacity destination buffer capacity 51 | * @param dirname_length optional recipient for the length of the dirname part 52 | * of the path. 53 | * 54 | * @return the length of the module path on success (without a terminal NUL 55 | * character), otherwise `-1` 56 | */ 57 | WAI_FUNCSPEC 58 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length); 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif // #ifndef WHEREAMI_H 65 | -------------------------------------------------------------------------------- /include/pa.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | // PLIABLE ARRAY (also known as a stretchy buffer) 11 | 12 | #define pa_free(a) ((a) ? free(pa___raw(a)), 0 : 0) 13 | #define pa_push(a, v) (pa___maybegrow(a, 1), (a)[pa___n(a)++] = (v)) 14 | #define pa_count(a) ((a) ? pa___n(a) : 0) 15 | #define pa_add(a, n) (pa___maybegrow(a, n), pa___n(a) += (n)) 16 | #define pa_last(a) ((a)[pa___n(a) - 1]) 17 | #define pa_end(a) (a + pa_count(a)) 18 | #define pa_clear(arr) if (arr) pa___n(arr) = 0 19 | #define pa___raw(a) ((int*) (a) -2) 20 | #define pa___m(a) pa___raw(a)[0] 21 | #define pa___n(a) pa___raw(a)[1] 22 | #define pa___needgrow(a, n) ((a) == 0 || pa___n(a) + (n) >= pa___m(a)) 23 | #define pa___maybegrow(a, n) (pa___needgrow(a, (n)) ? pa___grow(a, n) : 0) 24 | #define pa___grow(a, n) ((a) = pa___growf((a), (n), sizeof(*(a)))) 25 | 26 | static void* pa___growf(void* arr, int increment, int itemsize) 27 | { 28 | int dbl_cur = arr ? 2 * pa___m(arr) : 0; 29 | int min_needed = pa_count(arr) + increment; 30 | int m = dbl_cur > min_needed ? dbl_cur : min_needed; 31 | int* p = 32 | (int*) realloc(arr ? pa___raw(arr) : 0, itemsize * m + sizeof(int) * 2); 33 | if (p) { 34 | if (!arr) { 35 | p[1] = 0; 36 | } 37 | p[0] = m; 38 | return p + 2; 39 | } else { 40 | return (void*) (2 * sizeof(int)); 41 | } 42 | } 43 | 44 | #define pa_count2(a) (pa_count(a) / 2) 45 | #define pa_count3(a) (pa_count(a) / 3) 46 | 47 | #define pa_push2(a, x, y) \ 48 | pa_push(a, x); \ 49 | pa_push(a, y); \ 50 | 51 | #define pa_push3(a, x, y, z) \ 52 | pa_push(a, x); \ 53 | pa_push(a, y); \ 54 | pa_push(a, z) 55 | 56 | #define pa_remove(a, i) \ 57 | memmove(a + i, a + i + 1, sizeof(*(a)) * (pa_count(a) - i - 1)); \ 58 | pa___n(a) -= 1 59 | 60 | #define pa_remove3(a, i) \ 61 | memmove(a + i * 3, a + i * 3 + 3, \ 62 | sizeof(*(a)) * (pa_count(a) - i * 3 - 3)); \ 63 | pa___n(a) -= 3 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /include/parg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | // MACROS 12 | 13 | #define PARG_CLAMP(x, min, max) ((x < min) ? min : ((x > max) ? max : x)) 14 | #define PARG_SQR(a) (a * a) 15 | #define PARG_MIN(a, b) ((a < b) ? a : b) 16 | #define PARG_MAX(a, b) ((a > b) ? a : b) 17 | #define PARG_SWAP(T, A, B) \ 18 | { \ 19 | T tmp = B; \ 20 | B = A; \ 21 | A = tmp; \ 22 | } 23 | 24 | // ENUMS & CONSTANTS 25 | 26 | #define PARG_PI 3.14159265359 27 | #define PARG_TWOPI 6.28318530718 28 | #define PARG_BYTE 0x1400 29 | #define PARG_UBYTE 0x1401 30 | #define PARG_SHORT 0x1402 31 | #define PARG_USHORT 0x1403 32 | #define PARG_INT 0x1404 33 | #define PARG_UINT 0x1405 34 | #define PARG_FLOAT 0x1406 35 | #define PARG_DOUBLE 0x140A 36 | 37 | #define PARG_FBO_FLOAT (1 << 0) 38 | #define PARG_FBO_ALPHA (1 << 1) 39 | #define PARG_FBO_HALF (1 << 2) 40 | #define PARG_FBO_LINEAR (1 << 3) 41 | #define PARG_FBO_DEPTH (1 << 3) 42 | 43 | typedef unsigned int parg_data_type; 44 | typedef unsigned char parg_byte; 45 | 46 | typedef enum { 47 | PARG_CPU, 48 | PARG_CPU_LZ4, 49 | PARG_GPU_ARRAY, 50 | PARG_GPU_ELEMENTS 51 | } parg_buffer_type; 52 | 53 | typedef enum { PARG_READ, PARG_WRITE, PARG_MODIFY } parg_buffer_mode; 54 | 55 | // TOKENS 56 | 57 | typedef uint32_t parg_token; 58 | #define PARG_TOKEN_DECLARE(NAME, VAL) static parg_token NAME; 59 | #define PARG_TOKEN_DEFINE(NAME, VAL) NAME = parg_token_from_string(VAL); 60 | const char* parg_token_to_string(parg_token); 61 | parg_token parg_token_from_string(const char*); 62 | 63 | // ASSETS 64 | 65 | #define PARG_ASSET_TABLE(NAME, VAL) \ 66 | PARG_TOKEN_DEFINE(NAME, VAL); \ 67 | parg_asset_preload(NAME); 68 | #define PARG_ASSET_LIST(VAL) parg_asset_preload(parg_token_from_string(VAL)); 69 | void parg_asset_preload(parg_token id); 70 | 71 | // BUFFERS 72 | 73 | typedef struct parg_buffer_s parg_buffer; 74 | parg_buffer* parg_buffer_create( 75 | void* src, int nbytes, parg_buffer_type memtype); 76 | parg_buffer* parg_buffer_alloc(int nbytes, parg_buffer_type); 77 | parg_buffer* parg_buffer_dup(parg_buffer*, parg_buffer_type); 78 | void parg_buffer_free(parg_buffer*); 79 | int parg_buffer_length(parg_buffer*); 80 | void* parg_buffer_lock(parg_buffer*, parg_buffer_mode); 81 | void* parg_buffer_lock_grow(parg_buffer*, int nbytes); 82 | void parg_buffer_unlock(parg_buffer*); 83 | void parg_buffer_gpu_bind(parg_buffer*); 84 | int parg_buffer_gpu_check(parg_buffer*); 85 | parg_buffer* parg_buffer_from_asset(parg_token id); 86 | parg_buffer* parg_buffer_slurp_asset(parg_token id, void** ptr); 87 | void parg_buffer_to_file(parg_buffer*, const char* filepath); 88 | parg_buffer* parg_buffer_to_gpu(parg_buffer* buf, parg_buffer_type memtype); 89 | parg_buffer* parg_buffer_from_file(const char* filepath); 90 | 91 | // AXIS-ALIGNED RECTANGLE 92 | 93 | typedef struct { 94 | float left, bottom, right, top; 95 | } parg_aar; 96 | 97 | typedef struct { 98 | int x, y, z; 99 | } parg_tilename; 100 | 101 | typedef struct { 102 | parg_tilename mintile; 103 | parg_tilename maxtile; 104 | } parg_tilerange; 105 | 106 | float parg_aar_to_tilerange(parg_aar, Vector2 mapsize, parg_tilerange* range); 107 | parg_aar parg_aar_from_tilename(parg_tilename tile, Vector2 mapsize); 108 | parg_aar parg_aar_from_tilerange(parg_tilerange range, Vector2 mapsize); 109 | parg_aar parg_aar_encompass(parg_aar a, parg_aar b); 110 | parg_aar parg_aar_stretch_to_square(parg_aar rect); 111 | float parg_aar_height(parg_aar rect); 112 | float parg_aar_width(parg_aar rect); 113 | float parg_aar_centerx(parg_aar rect); 114 | float parg_aar_centery(parg_aar rect); 115 | 116 | // MESHES 117 | 118 | typedef struct parg_mesh_s parg_mesh; 119 | struct par_shapes_mesh_s; 120 | 121 | parg_mesh* parg_mesh_create(float* pts, int npts, uint16_t* tris, int ntris); 122 | parg_mesh* parg_mesh_from_shape(struct par_shapes_mesh_s const* src); 123 | parg_mesh* parg_mesh_from_asset(parg_token id); 124 | parg_mesh* parg_mesh_from_file(const char* filepath); 125 | parg_mesh* parg_mesh_knot(int cols, int rows, float major, float minor); 126 | parg_mesh* parg_mesh_torus(int cols, int rows, float major, float minor); 127 | parg_mesh* parg_mesh_rectangle(float width, float height); 128 | parg_mesh* parg_mesh_aar(parg_aar rect); 129 | parg_mesh* parg_mesh_sierpinski(float width, int depth); 130 | void parg_mesh_free(parg_mesh* m); 131 | parg_buffer* parg_mesh_coord(parg_mesh* m); 132 | parg_buffer* parg_mesh_uv(parg_mesh* m); 133 | parg_buffer* parg_mesh_norml(parg_mesh* m); 134 | parg_buffer* parg_mesh_index(parg_mesh* m); 135 | int parg_mesh_ntriangles(parg_mesh* m); 136 | void parg_mesh_compute_normals(parg_mesh* m); 137 | void parg_mesh_send_to_gpu(parg_mesh* m); 138 | 139 | // SHADERS 140 | 141 | void parg_shader_load_from_buffer(parg_buffer*); 142 | void parg_shader_load_from_asset(parg_token id); 143 | void parg_shader_bind(parg_token); 144 | void parg_shader_free(parg_token); 145 | 146 | // TEXTURES 147 | 148 | typedef struct parg_texture_s parg_texture; 149 | parg_texture* parg_texture_from_buffer(parg_buffer* rgba); 150 | parg_texture* parg_texture_from_asset(parg_token id); 151 | parg_texture* parg_texture_from_asset_linear(parg_token id); 152 | parg_texture* parg_texture_from_fp32( 153 | parg_buffer* buf, int width, int height, int ncomps, int bytoffset); 154 | parg_texture* parg_texture_from_u8( 155 | parg_buffer* buf, int width, int height, int ncomps, int byteoffset); 156 | void parg_texture_bind(parg_texture*, int stage); 157 | void parg_texture_info(parg_texture*, int* width, int* height); 158 | void parg_texture_free(parg_texture*); 159 | void parg_texture_fliprows(void* data, int rowsize, int nrows); 160 | 161 | // UNIFORMS 162 | 163 | void parg_uniform1i(parg_token tok, int val); 164 | void parg_uniform1f(parg_token tok, float val); 165 | void parg_uniform2f(parg_token tok, float x, float y); 166 | void parg_uniform3f(parg_token, const Vector3* val); 167 | void parg_uniform4f(parg_token, const Vector4* val); 168 | void parg_uniform_point(parg_token, const Point3* val); 169 | void parg_uniform_matrix4f(parg_token, const Matrix4* val); 170 | void parg_uniform_matrix3f(parg_token, const Matrix3* val); 171 | 172 | // GL STATE MACHINE 173 | 174 | void parg_state_clearcolor(Vector4 color); 175 | void parg_state_cullfaces(int enabled); 176 | void parg_state_depthtest(int enabled); 177 | void parg_state_blending(int enabled); 178 | 179 | // VERTEX ARRAYS 180 | 181 | void parg_varray_disable(parg_token attr); 182 | void parg_varray_bind(parg_buffer*); 183 | void parg_varray_enable(parg_buffer*, parg_token attr, int ncomps, 184 | parg_data_type type, int stride, int offset); 185 | void parg_varray_instances(parg_token attr, int divisor); 186 | 187 | // DRAW CALLS 188 | 189 | void parg_draw_clear(); 190 | void parg_draw_one_quad(); 191 | void parg_draw_triangles(int start, int ntriangles); 192 | void parg_draw_triangles_u16(int start, int ntriangles); 193 | void parg_draw_wireframe_triangles_u16(int start, int ntriangles); 194 | void parg_draw_instanced_triangles_u16(int start, int ntris, int ninstances); 195 | void parg_draw_lines(int nsegments); 196 | void parg_draw_points(int npoints); 197 | 198 | // MAP CAMERA 199 | 200 | void parg_zcam_init(float world_width, float world_height, float fovy); 201 | void parg_zcam_tick(float window_aspect, float seconds); 202 | DPoint3 parg_zcam_to_world(float winx, float winy); 203 | float parg_zcam_get_magnification(); 204 | void parg_zcam_get_viewport(float* lbrt); 205 | void parg_zcam_get_viewportd(double* lbrt); 206 | parg_aar parg_zcam_get_rectangle(); 207 | void parg_zcam_grab_begin(float winx, float winy); 208 | void parg_zcam_grab_update(float winx, float winy, float scrolldelta); 209 | void parg_zcam_grab_end(); 210 | Point3 parg_zcam_matrices(Matrix4* proj, Matrix4* view); 211 | DPoint3 parg_zcam_dmatrices(DMatrix4* proj, DMatrix4* view); 212 | void parg_zcam_highprec(Matrix4* vp, Point3* eyepos_lo, Point3* eyepos_hi); 213 | int parg_zcam_has_moved(); 214 | void parg_zcam_touch(); 215 | void parg_zcam_set_position(double x, double y, double z); 216 | void parg_zcam_frame_position(double const* xyw); 217 | void parg_zcam_blend( 218 | double const* cameraA, double const* cameraB, double* result, double t); 219 | 220 | typedef struct { 221 | double start_view[3]; 222 | double final_view[3]; 223 | double blend_view[3]; 224 | double start_time; 225 | double final_time; 226 | } parg_zcam_animation; 227 | 228 | // OFFSCREEN FRAMEBUFFER 229 | 230 | typedef struct parg_framebuffer_s parg_framebuffer; 231 | parg_framebuffer* parg_framebuffer_create_empty( 232 | int width, int height, int flags); 233 | parg_framebuffer* parg_framebuffer_create( 234 | int width, int height, void* src, int nbytes, int flags); 235 | void parg_framebuffer_bindtex(parg_framebuffer*, int stage); 236 | void parg_framebuffer_bindfbo(parg_framebuffer*, int mrt_index); 237 | void parg_framebuffer_pushfbo(parg_framebuffer*, int mrt_index); 238 | void parg_framebuffer_popfbo(); 239 | void parg_framebuffer_free(parg_framebuffer*); 240 | void parg_framebuffer_swap(parg_framebuffer*, parg_framebuffer*); 241 | 242 | #ifdef __cplusplus 243 | } 244 | #endif 245 | -------------------------------------------------------------------------------- /include/parwin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef enum { 4 | PARG_EVENT_DOWN, 5 | PARG_EVENT_UP, 6 | PARG_EVENT_MOVE, 7 | PARG_EVENT_KEYPRESS 8 | } parg_event; 9 | 10 | typedef void (*parg_window_fn_init)(float, float, float); 11 | typedef int (*parg_window_fn_tick)(float, float, float, float); 12 | typedef void (*parg_window_fn_input)(parg_event, float, float, float); 13 | typedef void (*parg_window_fn_draw)(); 14 | typedef void (*parg_window_fn_exit)(void); 15 | typedef void (*parg_window_fn_message)(const char*); 16 | 17 | void parg_window_setargs(int argc, char *argv[]); 18 | void parg_window_oninit(parg_window_fn_init); 19 | void parg_window_ontick(parg_window_fn_tick); 20 | void parg_window_ondraw(parg_window_fn_draw); 21 | void parg_window_onexit(parg_window_fn_exit); 22 | void parg_window_oninput(parg_window_fn_input); 23 | void parg_window_onmessage(parg_window_fn_message); 24 | int parg_window_exec(float winwidth, float winheight, int vsync, int aa); 25 | 26 | #ifdef EMSCRIPTEN 27 | void parg_window_send(const char* msg, double* values, int nvalues); 28 | #else 29 | static void parg_window_send(const char* msg, double* values, int nvalues) {} 30 | #endif 31 | -------------------------------------------------------------------------------- /include/sds.h: -------------------------------------------------------------------------------- 1 | /* SDSLib 2.0 -- A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo 4 | * Copyright (c) 2015, Oran Agra 5 | * Copyright (c) 2015, Redis Labs, Inc 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __SDS_H 34 | #define __SDS_H 35 | 36 | #define SDS_MAX_PREALLOC (1024 * 1024) 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | typedef char* sds; 43 | 44 | /* Note: sdshdr5 is never used, we just access the flags byte directly. 45 | * However is here to document the layout of type 5 SDS strings. */ 46 | struct __attribute__((__packed__)) sdshdr5 { 47 | unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ 48 | char buf[]; 49 | }; 50 | struct __attribute__((__packed__)) sdshdr8 { 51 | uint8_t len; /* used */ 52 | uint8_t alloc; /* excluding the header and null terminator */ 53 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 54 | char buf[]; 55 | }; 56 | struct __attribute__((__packed__)) sdshdr16 { 57 | uint16_t len; /* used */ 58 | uint16_t alloc; /* excluding the header and null terminator */ 59 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 60 | char buf[]; 61 | }; 62 | struct __attribute__((__packed__)) sdshdr32 { 63 | uint32_t len; /* used */ 64 | uint32_t alloc; /* excluding the header and null terminator */ 65 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 66 | char buf[]; 67 | }; 68 | struct __attribute__((__packed__)) sdshdr64 { 69 | uint64_t len; /* used */ 70 | uint64_t alloc; /* excluding the header and null terminator */ 71 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 72 | char buf[]; 73 | }; 74 | 75 | #define SDS_TYPE_5 0 76 | #define SDS_TYPE_8 1 77 | #define SDS_TYPE_16 2 78 | #define SDS_TYPE_32 3 79 | #define SDS_TYPE_64 4 80 | #define SDS_TYPE_MASK 7 81 | #define SDS_TYPE_BITS 3 82 | #define SDS_HDR_VAR(T, s) \ 83 | struct sdshdr##T * sh = (struct sdshdr##T *) ((s) - (sizeof(struct sdshdr##T))); 84 | #define SDS_HDR(T, s) ((struct sdshdr##T *)((s) - (sizeof(struct sdshdr##T)))) 85 | #define SDS_TYPE_5_LEN(f) ((f) >> SDS_TYPE_BITS) 86 | 87 | static inline size_t sdslen(const sds s) 88 | { 89 | unsigned char flags = s[-1]; 90 | switch (flags & SDS_TYPE_MASK) { 91 | case SDS_TYPE_5: 92 | return SDS_TYPE_5_LEN(flags); 93 | case SDS_TYPE_8: 94 | return SDS_HDR(8, s)->len; 95 | case SDS_TYPE_16: 96 | return SDS_HDR(16, s)->len; 97 | case SDS_TYPE_32: 98 | return SDS_HDR(32, s)->len; 99 | case SDS_TYPE_64: 100 | return SDS_HDR(64, s)->len; 101 | } 102 | return 0; 103 | } 104 | 105 | static inline size_t sdsavail(const sds s) 106 | { 107 | unsigned char flags = s[-1]; 108 | switch (flags & SDS_TYPE_MASK) { 109 | case SDS_TYPE_5: { 110 | return 0; 111 | } 112 | case SDS_TYPE_8: { 113 | SDS_HDR_VAR(8, s); 114 | return sh->alloc - sh->len; 115 | } 116 | case SDS_TYPE_16: { 117 | SDS_HDR_VAR(16, s); 118 | return sh->alloc - sh->len; 119 | } 120 | case SDS_TYPE_32: { 121 | SDS_HDR_VAR(32, s); 122 | return sh->alloc - sh->len; 123 | } 124 | case SDS_TYPE_64: { 125 | SDS_HDR_VAR(64, s); 126 | return sh->alloc - sh->len; 127 | } 128 | } 129 | return 0; 130 | } 131 | 132 | static inline void sdssetlen(sds s, size_t newlen) 133 | { 134 | unsigned char flags = s[-1]; 135 | switch (flags & SDS_TYPE_MASK) { 136 | case SDS_TYPE_5: { 137 | unsigned char* fp = ((unsigned char*) s) - 1; 138 | *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); 139 | } break; 140 | case SDS_TYPE_8: 141 | SDS_HDR(8, s)->len = newlen; 142 | break; 143 | case SDS_TYPE_16: 144 | SDS_HDR(16, s)->len = newlen; 145 | break; 146 | case SDS_TYPE_32: 147 | SDS_HDR(32, s)->len = newlen; 148 | break; 149 | case SDS_TYPE_64: 150 | SDS_HDR(64, s)->len = newlen; 151 | break; 152 | } 153 | } 154 | 155 | static inline void sdsinclen(sds s, size_t inc) 156 | { 157 | unsigned char flags = s[-1]; 158 | switch (flags & SDS_TYPE_MASK) { 159 | case SDS_TYPE_5: { 160 | unsigned char* fp = ((unsigned char*) s) - 1; 161 | unsigned char newlen = SDS_TYPE_5_LEN(flags) + inc; 162 | *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); 163 | } break; 164 | case SDS_TYPE_8: 165 | SDS_HDR(8, s)->len += inc; 166 | break; 167 | case SDS_TYPE_16: 168 | SDS_HDR(16, s)->len += inc; 169 | break; 170 | case SDS_TYPE_32: 171 | SDS_HDR(32, s)->len += inc; 172 | break; 173 | case SDS_TYPE_64: 174 | SDS_HDR(64, s)->len += inc; 175 | break; 176 | } 177 | } 178 | 179 | /* sdsalloc() = sdsavail() + sdslen() */ 180 | static inline size_t sdsalloc(const sds s) 181 | { 182 | unsigned char flags = s[-1]; 183 | switch (flags & SDS_TYPE_MASK) { 184 | case SDS_TYPE_5: 185 | return SDS_TYPE_5_LEN(flags); 186 | case SDS_TYPE_8: 187 | return SDS_HDR(8, s)->alloc; 188 | case SDS_TYPE_16: 189 | return SDS_HDR(16, s)->alloc; 190 | case SDS_TYPE_32: 191 | return SDS_HDR(32, s)->alloc; 192 | case SDS_TYPE_64: 193 | return SDS_HDR(64, s)->alloc; 194 | } 195 | return 0; 196 | } 197 | 198 | static inline void sdssetalloc(sds s, size_t newlen) 199 | { 200 | unsigned char flags = s[-1]; 201 | switch (flags & SDS_TYPE_MASK) { 202 | case SDS_TYPE_5: 203 | /* Nothing to do, this type has no total allocation info. */ 204 | break; 205 | case SDS_TYPE_8: 206 | SDS_HDR(8, s)->alloc = newlen; 207 | break; 208 | case SDS_TYPE_16: 209 | SDS_HDR(16, s)->alloc = newlen; 210 | break; 211 | case SDS_TYPE_32: 212 | SDS_HDR(32, s)->alloc = newlen; 213 | break; 214 | case SDS_TYPE_64: 215 | SDS_HDR(64, s)->alloc = newlen; 216 | break; 217 | } 218 | } 219 | 220 | sds sdsnewlen(const void* init, size_t initlen); 221 | sds sdsnew(const char* init); 222 | sds sdsempty(void); 223 | sds sdsdup(const sds s); 224 | void sdsfree(sds s); 225 | sds sdsgrowzero(sds s, size_t len); 226 | sds sdscatlen(sds s, const void* t, size_t len); 227 | sds sdscat(sds s, const char* t); 228 | sds sdscatsds(sds s, const sds t); 229 | sds sdscpylen(sds s, const char* t, size_t len); 230 | sds sdscpy(sds s, const char* t); 231 | 232 | sds sdscatvprintf(sds s, const char* fmt, va_list ap); 233 | #ifdef __GNUC__ 234 | sds sdscatprintf(sds s, const char* fmt, ...) 235 | __attribute__((format(printf, 2, 3))); 236 | #else 237 | sds sdscatprintf(sds s, const char* fmt, ...); 238 | #endif 239 | 240 | sds sdscatfmt(sds s, char const* fmt, ...); 241 | sds sdstrim(sds s, const char* cset); 242 | void sdsrange(sds s, int start, int end); 243 | void sdsupdatelen(sds s); 244 | void sdsclear(sds s); 245 | int sdscmp(const sds s1, const sds s2); 246 | sds* sdssplitlen( 247 | const char* s, int len, const char* sep, int seplen, int* count); 248 | void sdsfreesplitres(sds* tokens, int count); 249 | void sdstolower(sds s); 250 | void sdstoupper(sds s); 251 | sds sdsfromlonglong(long long value); 252 | sds sdscatrepr(sds s, const char* p, size_t len); 253 | sds* sdssplitargs(const char* line, int* argc); 254 | sds sdsmapchars(sds s, const char* from, const char* to, size_t setlen); 255 | sds sdsjoin(char** argv, int argc, char* sep); 256 | sds sdsjoinsds(sds* argv, int argc, const char* sep, size_t seplen); 257 | 258 | /* Low level functions exposed to the user API */ 259 | sds sdsMakeRoomFor(sds s, size_t addlen); 260 | void sdsIncrLen(sds s, int incr); 261 | sds sdsRemoveFreeSpace(sds s); 262 | size_t sdsAllocSize(sds s); 263 | void* sdsAllocPtr(sds s); 264 | 265 | #ifdef REDIS_TEST 266 | int sdsTest(int argc, char* argv[]); 267 | #endif 268 | 269 | #endif 270 | -------------------------------------------------------------------------------- /src/aar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | float parg_aar_to_tilerange(parg_aar rect, Vector2 size, parg_tilerange* range) 5 | { 6 | float e = PARG_MAX(size.x, size.y); 7 | Vector2 mapsquare = {e, e}; 8 | Vector2 mapmin = {e * -.5, e * -.5}; 9 | parg_aar sqrrect = parg_aar_stretch_to_square(rect); 10 | float mag = size.y / parg_aar_height(sqrrect); 11 | float z = log(mag) / log(2.0f); 12 | parg_tilename tilename = {0, 0, z}; 13 | mag = 1 << (int) z; 14 | float tilesize = mapsquare.x / mag; 15 | range->mintile = tilename; 16 | range->maxtile = tilename; 17 | range->mintile.x = (rect.left - mapmin.x) / tilesize; 18 | range->mintile.y = (rect.bottom - mapmin.y) / tilesize; 19 | range->maxtile.x = (rect.right - mapmin.x) / tilesize; 20 | range->maxtile.y = (rect.top - mapmin.y) / tilesize; 21 | return fmodf(z, 1.0); 22 | } 23 | 24 | parg_aar parg_aar_from_tilename(parg_tilename tile, Vector2 mapsize) 25 | { 26 | float e = PARG_MAX(mapsize.x, mapsize.y); 27 | Vector2 mapsquare = {e, e}; 28 | Vector2 mapmin = {e * -.5, e * -.5}; 29 | float mag = 1 << (int) tile.z; 30 | float tilesize = mapsquare.x / mag; 31 | parg_aar rect; 32 | rect.left = mapmin.x + tilesize * tile.x; 33 | rect.bottom = mapmin.y + tilesize * tile.y; 34 | rect.right = mapmin.x + tilesize * (tile.x + 1); 35 | rect.top = mapmin.y + tilesize * (tile.y + 1); 36 | return rect; 37 | } 38 | 39 | parg_aar parg_aar_from_tilerange(parg_tilerange range, Vector2 mapsize) 40 | { 41 | parg_aar a = parg_aar_from_tilename(range.mintile, mapsize); 42 | parg_aar b = parg_aar_from_tilename(range.maxtile, mapsize); 43 | return parg_aar_encompass(a, b); 44 | } 45 | 46 | parg_aar parg_aar_encompass(parg_aar a, parg_aar b) 47 | { 48 | a.left = PARG_MIN(a.left, b.left); 49 | a.bottom = PARG_MIN(a.bottom, b.bottom); 50 | a.right = PARG_MAX(a.right, b.right); 51 | a.top = PARG_MAX(a.top, b.top); 52 | return a; 53 | } 54 | 55 | parg_aar parg_aar_stretch_to_square(parg_aar rect) 56 | { 57 | float x = 0.5 * (rect.right + rect.left); 58 | float y = 0.5 * (rect.top + rect.bottom); 59 | float w = parg_aar_width(rect); 60 | float h = parg_aar_height(rect); 61 | if (w > h) { 62 | h = w / 2; 63 | rect.bottom = y - h; 64 | rect.top = y + h; 65 | } else { 66 | w = h / 2; 67 | rect.left = x - w; 68 | rect.right = x + w; 69 | } 70 | return rect; 71 | } 72 | 73 | float parg_aar_height(parg_aar rect) { return rect.top - rect.bottom; } 74 | 75 | float parg_aar_width(parg_aar rect) { return rect.right - rect.left; } 76 | 77 | float parg_aar_centerx(parg_aar rect) { return 0.5 * (rect.left + rect.right); } 78 | 79 | float parg_aar_centery(parg_aar rect) { return 0.5 * (rect.top + rect.bottom); } 80 | -------------------------------------------------------------------------------- /src/asset.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "internal.h" 3 | #include "kvec.h" 4 | #include "khash.h" 5 | #include "lodepng.h" 6 | 7 | // Mapping from asset ids (which are tokens) to buffer pointers. 8 | KHASH_MAP_INIT_INT(assmap, parg_buffer*) 9 | 10 | static khash_t(assmap)* _asset_registry = 0; 11 | 12 | static sds _exedir = 0; 13 | static sds _baseurl = 0; 14 | 15 | #ifdef EMSCRIPTEN 16 | void parg_asset_onload(const char* name, parg_buffer* buf) 17 | { 18 | parg_token id = parg_token_from_string(name); 19 | parg_assert(buf, "Unable to load asset"); 20 | if (!_asset_registry) { 21 | _asset_registry = kh_init(assmap); 22 | } 23 | int ret; 24 | int iter = kh_put(assmap, _asset_registry, id, &ret); 25 | kh_value(_asset_registry, iter) = buf; 26 | } 27 | 28 | #else 29 | 30 | static sds _pngsuffix = 0; 31 | 32 | void parg_asset_preload(parg_token id) 33 | { 34 | if (!_pngsuffix) { 35 | _pngsuffix = sdsnew(".png"); 36 | } 37 | sds filename = parg_token_to_sds(id); 38 | parg_buffer* buf = parg_buffer_from_path(filename); 39 | parg_assert(buf, "Unable to load asset"); 40 | if (sdslen(filename) > 4) { 41 | sds suffix = sdsdup(filename); 42 | sdsrange(suffix, -4, -1); 43 | if (!sdscmp(suffix, _pngsuffix)) { 44 | unsigned char* decoded; 45 | unsigned dims[3] = {0, 0, 4}; 46 | unsigned char* filedata = parg_buffer_lock(buf, PARG_READ); 47 | unsigned err = lodepng_decode_memory(&decoded, &dims[0], &dims[1], 48 | filedata, parg_buffer_length(buf), LCT_RGBA, 8); 49 | parg_assert(err == 0, "PNG decoding error"); 50 | parg_buffer_free(buf); 51 | int nbytes = dims[0] * dims[1] * dims[2]; 52 | buf = parg_buffer_alloc(nbytes + 12, PARG_CPU); 53 | int* ptr = parg_buffer_lock(buf, PARG_WRITE); 54 | *ptr++ = dims[0]; 55 | *ptr++ = dims[1]; 56 | *ptr++ = dims[2]; 57 | memcpy(ptr, decoded, nbytes); 58 | free(decoded); 59 | parg_buffer_unlock(buf); 60 | } 61 | sdsfree(suffix); 62 | } 63 | if (!_asset_registry) { 64 | _asset_registry = kh_init(assmap); 65 | } 66 | int ret; 67 | int iter = kh_put(assmap, _asset_registry, id, &ret); 68 | kh_value(_asset_registry, iter) = buf; 69 | } 70 | 71 | #endif 72 | 73 | parg_buffer* parg_asset_to_buffer(parg_token id) 74 | { 75 | parg_assert(_asset_registry, "Uninitialized asset registry"); 76 | khiter_t iter = kh_get(assmap, _asset_registry, id); 77 | parg_assert(iter != kh_end(_asset_registry), "Unknown token"); 78 | return kh_value(_asset_registry, iter); 79 | } 80 | 81 | sds parg_asset_baseurl() 82 | { 83 | if (!_baseurl) { 84 | _baseurl = sdsnew("http://github.prideout.net/assets/"); 85 | } 86 | return _baseurl; 87 | } 88 | 89 | #if EMSCRIPTEN 90 | 91 | void parg_asset_set_baseurl(const char* url) { _baseurl = sdsnew(url); } 92 | 93 | sds parg_asset_whereami() 94 | { 95 | if (!_exedir) { 96 | _exedir = sdsnew("web/"); 97 | } 98 | return _exedir; 99 | } 100 | 101 | int parg_asset_fileexists(sds fullpath) { return 1; } 102 | 103 | int parg_asset_download(const char* filename, sds targetpath) { return 0; } 104 | 105 | #else 106 | 107 | #include "whereami.h" 108 | #include 109 | #include 110 | #include 111 | 112 | #define PAR_EASYCURL_IMPLEMENTATION 113 | #include 114 | 115 | sds parg_asset_whereami() 116 | { 117 | if (!_exedir) { 118 | int length = wai_getExecutablePath(0, 0, 0); 119 | _exedir = sdsnewlen("", length); 120 | int dirlen; 121 | wai_getExecutablePath(_exedir, length, &dirlen); 122 | sdsrange(_exedir, 0, dirlen); 123 | } 124 | return _exedir; 125 | } 126 | 127 | int parg_asset_fileexists(sds fullpath) { return access(fullpath, F_OK) != -1; } 128 | 129 | int parg_asset_download(const char* filename, sds targetpath) 130 | { 131 | sds baseurl = parg_asset_baseurl(); 132 | sds fullurl = sdscat(sdsdup(baseurl), filename); 133 | printf("Downloading %s...\n", fullurl); 134 | return par_easycurl_to_file(fullurl, targetpath); 135 | return 0; 136 | } 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /src/bindings.cpp: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | #include 3 | #include 4 | #include "pargl.h" 5 | void parg_asset_set_baseurl(const char* url); 6 | void parg_asset_onload(const char* name, parg_buffer* buf); 7 | } 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | static int null_tick(float, float, float, float) { return 0; } 16 | static void null_draw() {} 17 | static void null_dispose() { } 18 | static void null_init(float, float, float) {} 19 | static void null_input(parg_event, float, float, float) {} 20 | static void null_message(const char*) {} 21 | static int _argc = 0; 22 | static char** _argv = 0; 23 | static float _pixscale = 1.0f; 24 | static int _winwidth = 0; 25 | static int _winheight = 0; 26 | static parg_window_fn_init _init = null_init; 27 | static parg_window_fn_tick _tick = null_tick; 28 | static parg_window_fn_draw _draw = null_draw; 29 | static parg_window_fn_exit _dispose = null_dispose; 30 | static parg_window_fn_input _input = null_input; 31 | static parg_window_fn_message _message = null_message; 32 | static parg_buffer* g_buffer; 33 | 34 | static emscripten::val* get_glext() 35 | { 36 | static emscripten::val* glext = 0; 37 | if (glext) { 38 | return glext; 39 | } 40 | auto parg = emscripten::val::module_property("parg"); 41 | emscripten::val GLctx = parg["GLctx"]; 42 | assert(GLctx.as() && "Can't find GLctx"); 43 | emscripten::val v = GLctx.call("getExtension", 44 | std::string("ANGLE_instanced_arrays")); 45 | assert(v.as() && "Instancing extension not available."); 46 | return glext = new emscripten::val(v); 47 | } 48 | 49 | void pargVertexAttribDivisor(GLuint a, GLuint b) 50 | { 51 | get_glext()->call("vertexAttribDivisorANGLE", a, b); 52 | } 53 | 54 | void pargDrawElementsInstanced(GLenum a, GLsizei b, GLenum c, const void * d, 55 | GLsizei e) 56 | { 57 | get_glext()->call( 58 | "drawElementsInstancedANGLE", a, b, c, (int) d, (int) e); 59 | } 60 | 61 | void parg_window_setargs(int argc, char* argv[]) 62 | { 63 | _argc = argc; 64 | _argv = argv; 65 | } 66 | 67 | void parg_window_oninit(parg_window_fn_init fn) { _init = fn; } 68 | 69 | void parg_window_ontick(parg_window_fn_tick fn) { _tick = fn; } 70 | 71 | void parg_window_ondraw(parg_window_fn_draw fn) { _draw = fn; } 72 | 73 | void parg_window_onexit(parg_window_fn_exit fn) { _dispose = fn; } 74 | 75 | void parg_window_oninput(parg_window_fn_input fn) { _input = fn; } 76 | 77 | void parg_window_onmessage(parg_window_fn_message fn) { _message = fn; } 78 | 79 | int parg_window_exec(float winwidth, float winheight, int vsync, int aa) 80 | { 81 | _winwidth = winwidth; 82 | _winheight = winheight; 83 | EM_ASM_ARGS({ 84 | Module.parg_window_dims = []; 85 | Module.parg_window_dims[0] = $0; 86 | Module.parg_window_dims[1] = $1; 87 | }, winwidth, winheight); 88 | return 0; 89 | } 90 | 91 | static void message(emscripten::val msgval) 92 | { 93 | std::string msg = msgval.as(); 94 | _message(msg.c_str()); 95 | } 96 | 97 | static void init(emscripten::val args) 98 | { 99 | message(args); 100 | _init(_winwidth, _winheight, _pixscale); 101 | } 102 | 103 | static void draw() 104 | { 105 | _draw(); 106 | #if 0 107 | GLenum err = glGetError(); 108 | if (err != GL_NO_ERROR) { 109 | puts("OpenGL Error\n"); 110 | } 111 | #endif 112 | } 113 | 114 | static int tick(float seconds, float pixscale) 115 | { 116 | _pixscale = pixscale; 117 | return _tick(_winwidth, _winheight, _pixscale, seconds); 118 | } 119 | 120 | static void input(int evt, float x, float y, float z) 121 | { 122 | _input((parg_event) evt, x, y, z); 123 | } 124 | 125 | static int alloc(std::string id, int nbytes) 126 | { 127 | using namespace emscripten; 128 | printf("Allocating %d bytes for %s\n", nbytes, id.c_str()); 129 | auto parg = val::module_property("parg"); 130 | g_buffer = parg_buffer_alloc(nbytes, PARG_CPU); 131 | return (int) parg_buffer_lock(g_buffer, PARG_WRITE); 132 | } 133 | 134 | static void commit(std::string id) 135 | { 136 | parg_buffer_unlock(g_buffer); 137 | parg_asset_onload(id.c_str(), g_buffer); 138 | } 139 | 140 | void parg_asset_preload(parg_token id) 141 | { 142 | using namespace emscripten; 143 | auto parg = val::module_property("parg"); 144 | std::string name(parg_token_to_string(id)); 145 | parg.call("asset_preload", name); 146 | } 147 | 148 | void parg_window_send(const char* msg, double* values, int nvalues) 149 | { 150 | using namespace emscripten; 151 | auto parg = val::module_property("parg"); 152 | uint32_t ptr = ((uint32_t) values) / sizeof(double); 153 | parg.call("onpod", std::string(msg), ptr, nvalues); 154 | } 155 | 156 | EMSCRIPTEN_BINDINGS(par) 157 | { 158 | using namespace emscripten; 159 | struct Window {}; 160 | class_("Window") 161 | .class_function("init", &init) 162 | .class_function("draw", &draw) 163 | .class_function("tick", &tick) 164 | .class_function("input", &input) 165 | .class_function("message", &message); 166 | struct Asset {}; 167 | class_("Asset") 168 | .class_function("alloc", &alloc) 169 | .class_function("commit", &commit); 170 | parg_asset_set_baseurl("parg/"); 171 | } 172 | -------------------------------------------------------------------------------- /src/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "internal.h" 3 | #include "pargl.h" 4 | #include 5 | #include 6 | 7 | struct parg_buffer_s { 8 | char* data; 9 | int nbytes; 10 | parg_buffer_type memtype; 11 | GLuint gpuhandle; 12 | char* gpumapped; 13 | }; 14 | 15 | parg_buffer* parg_buffer_create(void* src, int nbytes, parg_buffer_type memtype) 16 | { 17 | parg_buffer* retval = malloc(sizeof(struct parg_buffer_s)); 18 | retval->nbytes = nbytes; 19 | retval->memtype = memtype; 20 | retval->gpuhandle = 0; 21 | retval->gpumapped = 0; 22 | if (parg_buffer_gpu_check(retval)) { 23 | glGenBuffers(1, &retval->gpuhandle); 24 | GLenum target = memtype == PARG_GPU_ARRAY ? GL_ARRAY_BUFFER 25 | : GL_ELEMENT_ARRAY_BUFFER; 26 | glBindBuffer(target, retval->gpuhandle); 27 | glBufferData(target, nbytes, src, GL_STATIC_DRAW); 28 | } else { 29 | retval->data = malloc(nbytes); 30 | memcpy(retval->data, src, nbytes); 31 | } 32 | return retval; 33 | } 34 | 35 | int parg_buffer_gpu_check(parg_buffer* buf) 36 | { 37 | return buf->memtype == PARG_GPU_ARRAY || buf->memtype == PARG_GPU_ELEMENTS; 38 | } 39 | 40 | GLuint parg_buffer_gpu_handle(parg_buffer* buf) { return buf->gpuhandle; } 41 | 42 | parg_buffer* parg_buffer_alloc(int nbytes, parg_buffer_type memtype) 43 | { 44 | parg_buffer* retval = malloc(sizeof(struct parg_buffer_s)); 45 | retval->data = (memtype == PARG_CPU) ? malloc(nbytes) : 0; 46 | retval->nbytes = nbytes; 47 | retval->memtype = memtype; 48 | retval->gpuhandle = 0; 49 | retval->gpumapped = 0; 50 | if (parg_buffer_gpu_check(retval)) { 51 | glGenBuffers(1, &retval->gpuhandle); 52 | } 53 | return retval; 54 | } 55 | 56 | parg_buffer* parg_buffer_dup(parg_buffer* srcbuf, parg_buffer_type memtype) 57 | { 58 | int nbytes = parg_buffer_length(srcbuf); 59 | parg_buffer* dstbuf = parg_buffer_alloc(nbytes, memtype); 60 | void* src = parg_buffer_lock(srcbuf, PARG_READ); 61 | void* dst = parg_buffer_lock(dstbuf, PARG_WRITE); 62 | memcpy(dst, src, nbytes); 63 | parg_buffer_unlock(dstbuf); 64 | parg_buffer_unlock(srcbuf); 65 | return dstbuf; 66 | } 67 | 68 | void parg_buffer_free(parg_buffer* buf) 69 | { 70 | if (!buf) { 71 | return; 72 | } 73 | if (parg_buffer_gpu_check(buf)) { 74 | glDeleteBuffers(1, &buf->gpuhandle); 75 | } else { 76 | free(buf->data); 77 | } 78 | free(buf); 79 | } 80 | 81 | int parg_buffer_length(parg_buffer* buf) 82 | { 83 | parg_assert(buf, "Null buffer"); 84 | return buf->nbytes; 85 | } 86 | 87 | void* parg_buffer_lock(parg_buffer* buf, parg_buffer_mode access) 88 | { 89 | if (access == PARG_WRITE && parg_buffer_gpu_check(buf)) { 90 | buf->gpumapped = malloc(buf->nbytes); 91 | return buf->gpumapped; 92 | } 93 | return buf->data; 94 | } 95 | 96 | void* parg_buffer_lock_grow(parg_buffer* buf, int nbytes) 97 | { 98 | buf->nbytes = nbytes; 99 | if (parg_buffer_gpu_check(buf)) { 100 | buf->gpumapped = malloc(nbytes); 101 | return buf->gpumapped; 102 | } 103 | return buf->data = realloc(buf->data, nbytes); 104 | } 105 | 106 | void parg_buffer_unlock(parg_buffer* buf) 107 | { 108 | if (buf->gpumapped) { 109 | GLenum target = buf->memtype == PARG_GPU_ARRAY 110 | ? GL_ARRAY_BUFFER 111 | : GL_ELEMENT_ARRAY_BUFFER; 112 | glBindBuffer(target, buf->gpuhandle); 113 | glBufferData(target, buf->nbytes, buf->gpumapped, GL_STATIC_DRAW); 114 | free(buf->gpumapped); 115 | buf->gpumapped = 0; 116 | } 117 | } 118 | 119 | parg_buffer* parg_buffer_from_file(const char* filepath) 120 | { 121 | FILE* f = fopen(filepath, "rb"); 122 | parg_verify(f, "Unable to open file", filepath); 123 | fseek(f, 0, SEEK_END); 124 | long fsize = ftell(f); 125 | fseek(f, 0, SEEK_SET); 126 | parg_buffer* retval = parg_buffer_alloc(fsize + 1, PARG_CPU); 127 | char* contents = parg_buffer_lock(retval, PARG_WRITE); 128 | fread(contents, fsize, 1, f); 129 | fclose(f); 130 | contents[fsize] = 0; 131 | parg_buffer_unlock(retval); 132 | return retval; 133 | } 134 | 135 | void parg_buffer_to_file(parg_buffer* buf, const char* filepath) 136 | { 137 | FILE* f = fopen(filepath, "wb"); 138 | parg_verify(f, "Unable to open file", filepath); 139 | char* contents = parg_buffer_lock(buf, PARG_READ); 140 | fwrite(contents, 1, parg_buffer_length(buf), f); 141 | fclose(f); 142 | parg_buffer_unlock(buf); 143 | } 144 | 145 | parg_buffer* parg_buffer_from_asset(parg_token id) 146 | { 147 | return parg_asset_to_buffer(id); 148 | } 149 | 150 | parg_buffer* parg_buffer_slurp_asset(parg_token id, void** ptr) 151 | { 152 | parg_buffer* buf = parg_asset_to_buffer(id); 153 | *ptr = parg_buffer_lock(buf, PARG_READ); 154 | return buf; 155 | } 156 | 157 | parg_buffer* parg_buffer_from_path(const char* filename) 158 | { 159 | #if EMSCRIPTEN 160 | sds baseurl = parg_asset_baseurl(); 161 | sds fullurl = sdscat(sdsdup(baseurl), filename); 162 | parg_buffer* retval = 0; 163 | printf("TODO: download %s here\n", fullurl); 164 | sdsfree(fullurl); 165 | #else 166 | sds execdir = parg_asset_whereami(); 167 | sds fullpath = sdscat(sdsdup(execdir), filename); 168 | if (!parg_asset_fileexists(fullpath)) { 169 | parg_asset_download(filename, fullpath); 170 | } 171 | parg_buffer* retval = parg_buffer_from_file(fullpath); 172 | sdsfree(fullpath); 173 | #endif 174 | return retval; 175 | } 176 | 177 | void parg_buffer_gpu_bind(parg_buffer* buf) 178 | { 179 | parg_assert(parg_buffer_gpu_check(buf), "GPU buffer required"); 180 | GLenum target = buf->memtype == PARG_GPU_ARRAY ? GL_ARRAY_BUFFER 181 | : GL_ELEMENT_ARRAY_BUFFER; 182 | glBindBuffer(target, parg_buffer_gpu_handle(buf)); 183 | } 184 | -------------------------------------------------------------------------------- /src/draw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pargl.h" 3 | 4 | void parg_draw_clear() 5 | { 6 | int planes = GL_COLOR_BUFFER_BIT; 7 | if (_parg_depthtest) { 8 | planes |= GL_DEPTH_BUFFER_BIT; 9 | } 10 | glClear(planes); 11 | } 12 | 13 | void parg_draw_one_quad() { glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } 14 | 15 | void parg_draw_triangles(int start, int ntriangles) 16 | { 17 | glDrawArrays(GL_TRIANGLES, start * 3, ntriangles * 3); 18 | } 19 | 20 | void parg_draw_triangles_u16(int start, int ntriangles) 21 | { 22 | long offset = start * 3 * sizeof(uint16_t); 23 | const GLvoid* ptr = (const GLvoid*) offset; 24 | glDrawElements(GL_TRIANGLES, ntriangles * 3, GL_UNSIGNED_SHORT, ptr); 25 | } 26 | 27 | void parg_draw_instanced_triangles_u16( 28 | int start, int ntriangles, int ninstances) 29 | { 30 | long offset = start * 3 * sizeof(uint16_t); 31 | const GLvoid* ptr = (const GLvoid*) offset; 32 | pargDrawElementsInstanced( 33 | GL_TRIANGLES, ntriangles * 3, GL_UNSIGNED_SHORT, ptr, ninstances); 34 | } 35 | 36 | void parg_draw_wireframe_triangles_u16(int start, int ntriangles) 37 | { 38 | #ifndef EMSCRIPTEN 39 | glLineWidth(2); 40 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 41 | glPolygonOffset(0.0001, -0.0001); 42 | glEnable(GL_POLYGON_OFFSET_LINE); 43 | long offset = start * 3 * sizeof(uint16_t); 44 | const GLvoid* ptr = (const GLvoid*) offset; 45 | glDrawElements(GL_TRIANGLES, ntriangles * 3, GL_UNSIGNED_SHORT, ptr); 46 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 47 | glDisable(GL_POLYGON_OFFSET_LINE); 48 | #endif 49 | } 50 | 51 | void parg_draw_lines(int nsegments) 52 | { 53 | glLineWidth(2); 54 | glDrawArrays(GL_LINES, 0, nsegments * 2); 55 | } 56 | 57 | void parg_draw_points(int npoints) 58 | { 59 | #if defined(GL_PROGRAM_POINT_SIZE) 60 | glEnable(GL_PROGRAM_POINT_SIZE); 61 | #elif defined(GL_VERTEX_PROGRAM_POINT_SIZE) 62 | glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); 63 | #endif 64 | glDrawArrays(GL_POINTS, 0, npoints); 65 | } 66 | -------------------------------------------------------------------------------- /src/framebuffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "internal.h" 5 | #include "pargl.h" 6 | 7 | #if EMSCRIPTEN 8 | #define HALF_FLOAT GL_HALF_FLOAT_OES 9 | #elif defined(__APPLE__) && defined(__MACH__) 10 | #define HALF_FLOAT GL_HALF_FLOAT 11 | #endif 12 | 13 | struct parg_framebuffer_s { 14 | int width; 15 | int height; 16 | GLuint tex; 17 | GLuint fbo; 18 | GLuint depth; 19 | }; 20 | 21 | static GLint pushed_fbo = 0; 22 | static GLint pushed_viewport[4]; 23 | 24 | parg_framebuffer* parg_framebuffer_create_empty( 25 | int width, int height, int flags) 26 | { 27 | GLuint tex, fbo, depth = 0; 28 | GLenum filter = (flags & PARG_FBO_LINEAR) ? GL_LINEAR : GL_NEAREST; 29 | GLenum format = (flags & PARG_FBO_ALPHA) ? GL_RGBA : GL_RGB; 30 | GLenum type = GL_UNSIGNED_BYTE; 31 | if (flags & PARG_FBO_FLOAT) { 32 | type = GL_FLOAT; 33 | } else if (flags & PARG_FBO_HALF) { 34 | type = HALF_FLOAT; 35 | } 36 | 37 | glGenTextures(1, &tex); 38 | glBindTexture(GL_TEXTURE_2D, tex); 39 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); 40 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); 41 | glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, 0); 42 | 43 | GLint previous; 44 | glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previous); 45 | 46 | glGenFramebuffers(1, &fbo); 47 | glBindFramebuffer(GL_FRAMEBUFFER, fbo); 48 | glFramebufferTexture2D( 49 | GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); 50 | 51 | if (flags & PARG_FBO_DEPTH) { 52 | glGenRenderbuffers(1, &depth); 53 | glBindRenderbuffer(GL_RENDERBUFFER, depth); 54 | glRenderbufferStorage( 55 | GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); 56 | glFramebufferRenderbuffer( 57 | GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); 58 | } 59 | 60 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 61 | if (status != GL_FRAMEBUFFER_COMPLETE) { 62 | printf("Failed to create FBO.\n"); 63 | } 64 | 65 | glBindFramebuffer(GL_FRAMEBUFFER, previous); 66 | 67 | parg_framebuffer* framebuffer = malloc(sizeof(struct parg_framebuffer_s)); 68 | framebuffer->width = width; 69 | framebuffer->height = height; 70 | framebuffer->tex = tex; 71 | framebuffer->fbo = fbo; 72 | framebuffer->depth = depth; 73 | return framebuffer; 74 | } 75 | 76 | parg_framebuffer* parg_framebuffer_create( 77 | int width, int height, void* src, int nbytes, int flags) 78 | { 79 | GLenum format = (flags & PARG_FBO_ALPHA) ? GL_RGBA : GL_RGB; 80 | GLenum type = GL_UNSIGNED_BYTE; 81 | if (flags & PARG_FBO_FLOAT) { 82 | type = GL_FLOAT; 83 | } else if (flags & PARG_FBO_HALF) { 84 | type = HALF_FLOAT; 85 | } 86 | parg_framebuffer* fbo = parg_framebuffer_create_empty(width, height, flags); 87 | glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, src); 88 | return fbo; 89 | } 90 | 91 | void parg_framebuffer_free(parg_framebuffer* framebuffer) 92 | { 93 | glDeleteTextures(1, &framebuffer->tex); 94 | glDeleteFramebuffers(1, &framebuffer->fbo); 95 | free(framebuffer); 96 | } 97 | 98 | void parg_framebuffer_bindtex(parg_framebuffer* fbo, int stage) 99 | { 100 | glActiveTexture(GL_TEXTURE0 + stage); 101 | glBindTexture(GL_TEXTURE_2D, fbo->tex); 102 | } 103 | 104 | void parg_framebuffer_bindfbo(parg_framebuffer* fbo, int mrt_index) 105 | { 106 | // MRT is not supported. 107 | glBindFramebuffer(GL_FRAMEBUFFER, fbo->fbo); 108 | } 109 | 110 | void parg_framebuffer_swap(parg_framebuffer* a, parg_framebuffer* b) 111 | { 112 | assert(a->width == b->width && a->height == b->height); 113 | PARG_SWAP(GLuint, a->tex, b->tex); 114 | PARG_SWAP(GLuint, a->fbo, b->fbo); 115 | PARG_SWAP(GLuint, a->depth, b->depth); 116 | } 117 | 118 | void parg_framebuffer_pushfbo(parg_framebuffer* fbo, int mrt_index) 119 | { 120 | glGetIntegerv(GL_FRAMEBUFFER_BINDING, &pushed_fbo); 121 | glGetIntegerv(GL_VIEWPORT, pushed_viewport); 122 | glViewport(0, 0, fbo->width, fbo->height); 123 | parg_framebuffer_bindfbo(fbo, mrt_index); 124 | } 125 | 126 | void parg_framebuffer_popfbo() 127 | { 128 | glBindFramebuffer(GL_FRAMEBUFFER, pushed_fbo); 129 | glViewport(pushed_viewport[0], pushed_viewport[1], pushed_viewport[2], 130 | pushed_viewport[3]); 131 | } 132 | -------------------------------------------------------------------------------- /src/internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | struct parg_mesh_s { 12 | parg_buffer* coords; 13 | parg_buffer* uvs; 14 | parg_buffer* normals; 15 | parg_buffer* indices; 16 | int ntriangles; 17 | }; 18 | 19 | void parg_load_obj(parg_mesh* mesh, parg_buffer* buffer); 20 | sds parg_token_to_sds(parg_token token); 21 | parg_buffer* parg_buffer_from_path(const char* filepath); 22 | sds parg_asset_whereami(); 23 | void parg_asset_set_baseurl(const char* url); 24 | sds parg_asset_baseurl(); 25 | int parg_asset_fileexists(sds fullpath); 26 | int parg_asset_download(const char* filename, sds targetpath); 27 | parg_buffer* parg_asset_to_buffer(parg_token id); 28 | 29 | // This takes two human-readable strings: the key and the metadata. The key 30 | // should not be generated by sprintf because it is used as a grouping key in 31 | // systems like Sentry. The metadata, on the other hand, can be unique. 32 | 33 | #define parg_assert(expr, exception_key) \ 34 | if (!(expr)) { \ 35 | printf("%s\n", exception_key); \ 36 | } \ 37 | assert(expr); 38 | 39 | #define parg_verify(expr, exception_key, exception_metadata) \ 40 | if (!(expr)) { \ 41 | printf("%s: %s\n", exception_key, (char*) exception_metadata); \ 42 | } \ 43 | assert(expr); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | -------------------------------------------------------------------------------- /src/objloader.cpp: -------------------------------------------------------------------------------- 1 | #include "parg.h" 2 | #include "internal.h" 3 | 4 | #define TINYOBJLOADER_IMPLEMENTATION 5 | #include "tiny_obj_loader.h" 6 | 7 | using namespace std; 8 | 9 | class StubReader : public tinyobj::MaterialReader { 10 | public: 11 | StubReader() {} 12 | bool operator()( 13 | const string &matId, 14 | vector &materials, 15 | map &matMap, 16 | string &err) { 17 | return true; 18 | } 19 | }; 20 | 21 | void parg_load_obj(parg_mesh* dst, parg_buffer* buffer) 22 | { 23 | StubReader stubreader; 24 | vector shapes; 25 | vector materials; 26 | string err; 27 | const char* bview = (const char*) parg_buffer_lock(buffer, PARG_READ); 28 | stringstream ss(bview); 29 | tinyobj::LoadObj(shapes, materials, err, ss, stubreader); 30 | parg_assert(shapes.size() == 1, "OBJ file must have 1 shape."); 31 | const tinyobj::mesh_t& src = shapes[0].mesh; 32 | 33 | // Positions 34 | dst->coords = parg_buffer_alloc(4 * src.positions.size(), PARG_CPU); 35 | float* pcoords = (float*) parg_buffer_lock(dst->coords, PARG_WRITE); 36 | memcpy(pcoords, src.positions.data(), 4 * src.positions.size()); 37 | parg_buffer_unlock(dst->coords); 38 | 39 | // Normals 40 | if (src.normals.size() > 0) { 41 | dst->normals = parg_buffer_alloc(4 * src.normals.size(), PARG_CPU); 42 | float* pnorms = (float*) parg_buffer_lock(dst->normals, PARG_WRITE); 43 | memcpy(pnorms, src.normals.data(), 4 * src.normals.size()); 44 | parg_buffer_unlock(dst->normals); 45 | } 46 | 47 | // Texture coordinates 48 | if (src.texcoords.size() > 0) { 49 | dst->uvs = parg_buffer_alloc(4 * src.texcoords.size(), PARG_CPU); 50 | float* puvs = (float*) parg_buffer_lock(dst->uvs, PARG_WRITE); 51 | memcpy(puvs, src.texcoords.data(), 4 * src.texcoords.size()); 52 | parg_buffer_unlock(dst->uvs); 53 | } 54 | 55 | // Triangles 56 | dst->indices = parg_buffer_alloc(2 * src.indices.size(), PARG_CPU); 57 | uint16_t* ptris = (uint16_t*) parg_buffer_lock(dst->indices, PARG_WRITE); 58 | for (size_t i = 0; i < src.indices.size(); i++) { 59 | *ptris++ = src.indices[i]; 60 | } 61 | parg_buffer_unlock(dst->indices); 62 | 63 | // Finalize 64 | dst->ntriangles = (int) src.indices.size() / 3; 65 | parg_buffer_unlock(buffer); 66 | } 67 | -------------------------------------------------------------------------------- /src/pargl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if EMSCRIPTEN 4 | #include 5 | #include 6 | #include 7 | #define PARG_HALF_FLOAT GL_HALF_FLOAT_OES 8 | #define PARGL_STRING const GLchar* * 9 | 10 | void pargVertexAttribDivisor(GLuint index, GLuint divisor); 11 | 12 | void pargDrawElementsInstanced(GLenum mode, 13 | GLsizei count, 14 | GLenum type, 15 | const void * indices, 16 | GLsizei primcount); 17 | 18 | #else 19 | 20 | #define PARGL_STRING const GLchar* const * 21 | #if defined(__APPLE_CC__) 22 | #include 23 | #else 24 | #include 25 | #endif 26 | 27 | #define pargVertexAttribDivisor(a, b) \ 28 | glVertexAttribDivisor(a, b) 29 | 30 | #define pargDrawElementsInstanced(a, b, c, d, e) \ 31 | glDrawElementsInstanced(a, b, c, d, e) 32 | 33 | #endif 34 | 35 | void glGenerateMipmap(GLenum target); 36 | 37 | GLuint parg_buffer_gpu_handle(parg_buffer*); 38 | GLuint parg_shader_attrib_get(parg_token); 39 | GLint parg_shader_uniform_get(parg_token); 40 | 41 | extern int _parg_depthtest; 42 | -------------------------------------------------------------------------------- /src/shader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "internal.h" 3 | #include "pargl.h" 4 | #include 5 | #include "kvec.h" 6 | #include "khash.h" 7 | 8 | // Mapping from tokens to sds strings. 9 | KHASH_MAP_INIT_INT(smap, sds) 10 | 11 | // Mapping from tokens to OpenGL handles. 12 | KHASH_MAP_INIT_INT(glmap, GLuint) 13 | 14 | // Mapping from tokens to integer slots. 15 | KHASH_MAP_INIT_INT(imap, int) 16 | 17 | static khash_t(smap)* _vshader_registry = 0; 18 | static khash_t(smap)* _fshader_registry = 0; 19 | static khash_t(glmap)* _program_registry = 0; 20 | static khash_t(imap)* _attr_registry = 0; 21 | static khash_t(imap)* _unif_registry = 0; 22 | static GLuint _current_program = 0; 23 | static parg_token _current_program_token = 0; 24 | 25 | #define MAX_SHADER_SPEW 1024 26 | #define MAX_UNIFORM_LEN 128 27 | #define kv_last(vec) kv_A(vec, kv_size(vec) - 1) 28 | #define chunk_body kv_last(chunk_bodies) 29 | 30 | typedef kvec_t (sds) sdsvec; 31 | 32 | static int kv_find(sdsvec keys, sds key) 33 | { 34 | for (int p = 0; p < kv_size(keys); p++) { 35 | if (0 == strcmp(kv_A(keys, p), key)) { 36 | return p; 37 | } 38 | } 39 | return -1; 40 | } 41 | 42 | void parg_shader_load_from_buffer(parg_buffer* buf) 43 | { 44 | const sds ATTRIBUTE = sdsnew("attribute "); 45 | const sds PROGRAM = sdsnew("@program "); 46 | const sds PREFIX = sdsnew("_prefix"); 47 | #if defined(__APPLE__) && defined(__MACH__) 48 | const sds OSX_PREFIX = sdsnew("#version 120\n"); 49 | #endif 50 | 51 | if (!_vshader_registry) { 52 | _vshader_registry = kh_init(smap); 53 | _fshader_registry = kh_init(smap); 54 | _attr_registry = kh_init(imap); 55 | _unif_registry = kh_init(imap); 56 | } 57 | 58 | sdsvec program_args; 59 | kv_init(program_args); 60 | sdsvec chunk_bodies; 61 | kv_init(chunk_bodies); 62 | sdsvec chunk_names; 63 | kv_init(chunk_names); 64 | 65 | // Split the buffer into a list of codelines. 66 | int len = parg_buffer_length(buf); 67 | int nlines; 68 | char* contents = parg_buffer_lock(buf, PARG_READ); 69 | sds* lines = sdssplitlen(contents, len, "\n", 1, &nlines); 70 | parg_buffer_unlock(buf); 71 | 72 | // Group codelines into chunks and stash all @program lines. 73 | kv_push(sds, chunk_names, sdsdup(PREFIX)); 74 | kv_push(sds, chunk_bodies, sdsempty()); 75 | for (int j = 0; j < nlines; j++) { 76 | sds line = lines[j]; 77 | if (sdslen(line) > 2 && line[0] == '-' && line[1] == '-') { 78 | sds chunkname = sdsdup(line); 79 | sdsrange(chunkname, 2, -1); 80 | chunkname = sdstrim(chunkname, " \t"); 81 | kv_push(sds, chunk_names, chunkname); 82 | kv_push(sds, chunk_bodies, sdsempty()); 83 | chunk_body = sdscatprintf(chunk_body, "#line %d\n", j + 1); 84 | continue; 85 | } 86 | chunk_body = sdscatsds(chunk_body, line); 87 | chunk_body = sdscat(chunk_body, "\n"); 88 | char* program = strstr(line, PROGRAM); 89 | if (program) { 90 | kv_push(sds, program_args, sdsnew(program + sdslen(PROGRAM))); 91 | } 92 | char* attr = strstr(line, ATTRIBUTE); 93 | if (attr == line) { 94 | int nwords; 95 | sds aline = sdsnew(attr + sdslen(ATTRIBUTE)); 96 | aline = sdstrim(aline, "; \t"); 97 | sds* words = sdssplitlen(aline, sdslen(aline), " \t", 1, &nwords); 98 | sds attr = sdsdup(words[nwords - 1]); 99 | sdsfreesplitres(words, nwords); 100 | sdsfree(aline); 101 | parg_token tok = parg_token_from_string(attr); 102 | khiter_t iter = kh_get(imap, _attr_registry, tok); 103 | if (iter == kh_end(_attr_registry)) { 104 | int newslot = kh_size(_attr_registry); 105 | int ret; 106 | iter = kh_put(imap, _attr_registry, tok, &ret); 107 | kh_value(_attr_registry, iter) = newslot; 108 | } 109 | sdsfree(attr); 110 | } 111 | } 112 | sdsfreesplitres(lines, nlines); 113 | sds prefix_body = kv_A(chunk_bodies, 0); 114 | 115 | // Go back through the @program lines and populate the registry. 116 | for (int p = 0; p < kv_size(program_args); p++) { 117 | sds argstring = kv_A(program_args, p); 118 | 119 | // Extract the three command arguments. 120 | int nargs = sdslen(argstring); 121 | sds* args = sdssplitlen(argstring, nargs, ",", 1, &nargs); 122 | for (int a = 0; a < nargs; a++) { 123 | sdstrim(args[a], " \t"); 124 | } 125 | parg_assert(nargs == 3, "@program should have 3 args"); 126 | 127 | // Build the vshader and fshader strings. 128 | int vshader_index = kv_find(chunk_names, args[1]); 129 | int fshader_index = kv_find(chunk_names, args[2]); 130 | parg_verify(vshader_index > 0, "No such vshader", args[1]); 131 | parg_verify(fshader_index > 0, "No such fshader", args[2]); 132 | sds vshader_body = kv_A(chunk_bodies, vshader_index); 133 | sds fshader_body = kv_A(chunk_bodies, fshader_index); 134 | vshader_body = sdscat(sdsdup(prefix_body), vshader_body); 135 | 136 | #if EMSCRIPTEN 137 | sds fshader_prefix = 138 | sdscatsds(sdsnew("#extension GL_OES_standard_derivatives : enable\n" 139 | "precision highp float;\n"), 140 | prefix_body); 141 | fshader_body = sdscat(fshader_prefix, fshader_body); 142 | #else 143 | fshader_body = sdscat(sdsdup(prefix_body), fshader_body); 144 | #if defined(__APPLE__) && defined(__MACH__) 145 | sds tmp = fshader_body; 146 | fshader_body = sdscatsds(sdsdup(OSX_PREFIX), fshader_body); 147 | sdsfree(tmp); 148 | #endif 149 | #endif 150 | parg_token program_name = parg_token_from_string(args[0]); 151 | sdsfreesplitres(args, nargs); 152 | sdsfree(argstring); 153 | 154 | // Insert the vshader and fshader strings into the registries. 155 | int ret; 156 | khiter_t iter; 157 | iter = kh_put(smap, _vshader_registry, program_name, &ret); 158 | kh_value(_vshader_registry, iter) = vshader_body; 159 | iter = kh_put(smap, _fshader_registry, program_name, &ret); 160 | kh_value(_fshader_registry, iter) = fshader_body; 161 | } 162 | 163 | kv_destroy(program_args); 164 | kv_destroy(chunk_bodies); 165 | kv_destroy(chunk_names); 166 | 167 | sdsfree(ATTRIBUTE); 168 | sdsfree(PROGRAM); 169 | sdsfree(PREFIX); 170 | } 171 | 172 | void parg_shader_load_from_asset(parg_token id) 173 | { 174 | parg_buffer* buf = parg_buffer_from_asset(id); 175 | parg_shader_load_from_buffer(buf); 176 | parg_buffer_free(buf); 177 | } 178 | 179 | GLuint parg_shader_attrib(parg_token tok) { return 0; } 180 | 181 | static GLuint compile_program(parg_token tok) 182 | { 183 | khiter_t iter; 184 | 185 | iter = kh_get(smap, _vshader_registry, tok); 186 | parg_verify(iter != kh_end(_vshader_registry), "No vshader", 187 | parg_token_to_string(tok)); 188 | sds vshader_body = kh_value(_vshader_registry, iter); 189 | PARGL_STRING vshader_ptr = (PARGL_STRING) &vshader_body; 190 | 191 | iter = kh_get(smap, _fshader_registry, tok); 192 | parg_verify(iter != kh_end(_fshader_registry), "No fshader", 193 | parg_token_to_string(tok)); 194 | sds fshader_body = kh_value(_fshader_registry, iter); 195 | PARGL_STRING fshader_ptr = (PARGL_STRING) &fshader_body; 196 | 197 | GLchar spew[MAX_SHADER_SPEW]; 198 | GLint compile_success = 0; 199 | 200 | GLuint vs_handle = glCreateShader(GL_VERTEX_SHADER); 201 | glShaderSource(vs_handle, 1, vshader_ptr, 0); 202 | glCompileShader(vs_handle); 203 | glGetShaderiv(vs_handle, GL_COMPILE_STATUS, &compile_success); 204 | glGetShaderInfoLog(vs_handle, MAX_SHADER_SPEW, 0, spew); 205 | parg_verify(compile_success, parg_token_to_string(tok), spew); 206 | 207 | GLuint fs_handle = glCreateShader(GL_FRAGMENT_SHADER); 208 | glShaderSource(fs_handle, 1, fshader_ptr, 0); 209 | glCompileShader(fs_handle); 210 | glGetShaderiv(fs_handle, GL_COMPILE_STATUS, &compile_success); 211 | glGetShaderInfoLog(fs_handle, MAX_SHADER_SPEW, 0, spew); 212 | parg_verify(compile_success, parg_token_to_string(tok), spew); 213 | 214 | GLuint program_handle = glCreateProgram(); 215 | glAttachShader(program_handle, vs_handle); 216 | glAttachShader(program_handle, fs_handle); 217 | 218 | for (iter = kh_begin(_attr_registry); iter != kh_end(_attr_registry); 219 | ++iter) { 220 | if (!kh_exist(_attr_registry, iter)) { 221 | continue; 222 | } 223 | int slot = kh_value(_attr_registry, iter); 224 | parg_token tok = kh_key(_attr_registry, iter); 225 | const char* name = parg_token_to_string(tok); 226 | glBindAttribLocation(program_handle, slot, name); 227 | } 228 | 229 | GLint link_success; 230 | glLinkProgram(program_handle); 231 | glGetProgramiv(program_handle, GL_LINK_STATUS, &link_success); 232 | glGetProgramInfoLog(program_handle, MAX_SHADER_SPEW, 0, spew); 233 | parg_verify(link_success, parg_token_to_string(tok), spew); 234 | 235 | return program_handle; 236 | } 237 | 238 | static void gather_uniforms(parg_token ptoken, GLuint phandle) 239 | { 240 | int nuniforms; 241 | glGetProgramiv(phandle, GL_ACTIVE_UNIFORMS, &nuniforms); 242 | char uname[MAX_UNIFORM_LEN]; 243 | while (nuniforms--) { 244 | GLint size; 245 | GLenum type; 246 | int ret; 247 | glGetActiveUniform( 248 | phandle, nuniforms, MAX_UNIFORM_LEN, 0, &size, &type, uname); 249 | GLint loc = glGetUniformLocation(phandle, uname); 250 | parg_token utoken = parg_token_from_string(uname); 251 | parg_token combined_token = ptoken ^ utoken; 252 | khiter_t iter = kh_put(imap, _unif_registry, combined_token, &ret); 253 | kh_value(_unif_registry, iter) = loc; 254 | } 255 | } 256 | 257 | GLuint parg_shader_attrib_get(parg_token tok) 258 | { 259 | khiter_t iter = kh_get(imap, _attr_registry, tok); 260 | parg_verify(iter != kh_end(_attr_registry), "Unknown attribute", 261 | parg_token_to_string(tok)); 262 | return kh_value(_attr_registry, iter); 263 | } 264 | 265 | GLint parg_shader_uniform_get(parg_token utoken) 266 | { 267 | parg_token ptoken = _current_program_token; 268 | parg_token combined_token = ptoken ^ utoken; 269 | khiter_t iter = kh_get(imap, _unif_registry, combined_token); 270 | if (iter == kh_end(_unif_registry)) { 271 | return -1; 272 | } 273 | return kh_value(_unif_registry, iter); 274 | } 275 | 276 | void parg_shader_bind(parg_token tok) 277 | { 278 | if (!_program_registry) { 279 | _program_registry = kh_init(glmap); 280 | } 281 | khiter_t iter = kh_get(glmap, _program_registry, tok); 282 | GLuint program = 0; 283 | int ret; 284 | if (iter == kh_end(_program_registry)) { 285 | program = compile_program(tok); 286 | iter = kh_put(glmap, _program_registry, tok, &ret); 287 | kh_value(_program_registry, iter) = program; 288 | gather_uniforms(tok, program); 289 | } else { 290 | program = kh_value(_program_registry, iter); 291 | } 292 | parg_verify(program, "No program", parg_token_to_string(tok)); 293 | glUseProgram(program); 294 | _current_program = program; 295 | _current_program_token = tok; 296 | } 297 | 298 | void parg_shader_free(parg_token tok) 299 | { 300 | khiter_t iter = kh_get(glmap, _program_registry, tok); 301 | if (iter != kh_end(_program_registry)) { 302 | GLuint program = kh_value(_program_registry, iter); 303 | glDeleteProgram(program); 304 | kh_del(glmap, _program_registry, iter); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/state.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pargl.h" 3 | 4 | int _parg_depthtest = 0; 5 | 6 | void parg_state_clearcolor(Vector4 color) 7 | { 8 | glClearColor(color.x, color.y, color.z, color.w); 9 | } 10 | 11 | void parg_state_cullfaces(int enabled) 12 | { 13 | (enabled ? glEnable : glDisable)(GL_CULL_FACE); 14 | } 15 | 16 | void parg_state_depthtest(int enabled) 17 | { 18 | (enabled ? glEnable : glDisable)(GL_DEPTH_TEST); 19 | _parg_depthtest = enabled; 20 | } 21 | 22 | void parg_state_blending(int enabled) 23 | { 24 | if (enabled == 1) { 25 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 26 | } else if (enabled == 2) { 27 | glBlendFunc(GL_ONE, GL_ONE); 28 | } 29 | (enabled ? glEnable : glDisable)(GL_BLEND); 30 | } 31 | -------------------------------------------------------------------------------- /src/texture.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "internal.h" 5 | #include "pargl.h" 6 | #include "lodepng.h" 7 | 8 | struct parg_texture_s { 9 | int width; 10 | int height; 11 | GLuint handle; 12 | }; 13 | 14 | parg_texture* parg_texture_from_asset(parg_token id) 15 | { 16 | parg_texture* tex = malloc(sizeof(struct parg_texture_s)); 17 | int* rawdata; 18 | parg_buffer* pngbuf = parg_buffer_slurp_asset(id, (void*) &rawdata); 19 | tex->width = *rawdata++; 20 | tex->height = *rawdata++; 21 | int ncomps = *rawdata++; 22 | assert(ncomps == 4); 23 | glGenTextures(1, &tex->handle); 24 | glBindTexture(GL_TEXTURE_2D, tex->handle); 25 | parg_texture_fliprows(rawdata, tex->width * ncomps, tex->height); 26 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->width, tex->height, 0, GL_RGBA, 27 | GL_UNSIGNED_BYTE, rawdata); 28 | parg_buffer_free(pngbuf); 29 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 30 | glTexParameteri( 31 | GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 32 | glGenerateMipmap(GL_TEXTURE_2D); 33 | return tex; 34 | } 35 | 36 | parg_texture* parg_texture_from_buffer(parg_buffer* buf) 37 | { 38 | unsigned char* decoded; 39 | unsigned dims[3] = {0, 0, 4}; 40 | unsigned char* filedata = parg_buffer_lock(buf, PARG_READ); 41 | unsigned err = lodepng_decode_memory(&decoded, &dims[0], &dims[1], filedata, 42 | parg_buffer_length(buf), LCT_RGBA, 8); 43 | parg_assert(err == 0, "PNG decoding error"); 44 | assert(dims[2] == 4); 45 | parg_texture* tex = calloc(sizeof(struct parg_texture_s), 1); 46 | tex->width = dims[0]; 47 | tex->height = dims[1]; 48 | glGenTextures(1, &tex->handle); 49 | glBindTexture(GL_TEXTURE_2D, tex->handle); 50 | parg_texture_fliprows(decoded, tex->width * 4, tex->height); 51 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->width, tex->height, 0, GL_RGBA, 52 | GL_UNSIGNED_BYTE, decoded); 53 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 54 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 55 | parg_buffer_unlock(buf); 56 | return tex; 57 | } 58 | 59 | parg_texture* parg_texture_from_asset_linear(parg_token id) 60 | { 61 | parg_texture* tex = malloc(sizeof(struct parg_texture_s)); 62 | int* rawdata; 63 | parg_buffer* pngbuf = parg_buffer_slurp_asset(id, (void*) &rawdata); 64 | tex->width = *rawdata++; 65 | tex->height = *rawdata++; 66 | int ncomps = *rawdata++; 67 | assert(ncomps == 4); 68 | glGenTextures(1, &tex->handle); 69 | glBindTexture(GL_TEXTURE_2D, tex->handle); 70 | parg_texture_fliprows(rawdata, tex->width * ncomps, tex->height); 71 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->width, tex->height, 0, GL_RGBA, 72 | GL_UNSIGNED_BYTE, rawdata); 73 | parg_buffer_free(pngbuf); 74 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 75 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 76 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 77 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 78 | return tex; 79 | } 80 | 81 | void parg_texture_bind(parg_texture* tex, int stage) 82 | { 83 | glActiveTexture(GL_TEXTURE0 + stage); 84 | glBindTexture(GL_TEXTURE_2D, tex->handle); 85 | } 86 | 87 | void parg_texture_info(parg_texture* tex, int* width, int* height) 88 | { 89 | *width = tex->width; 90 | *height = tex->height; 91 | } 92 | 93 | void parg_texture_free(parg_texture* tex) 94 | { 95 | if (tex) { 96 | glDeleteTextures(1, &tex->handle); 97 | free(tex); 98 | } 99 | } 100 | 101 | void parg_texture_fliprows(void* data, int rowsize, int nrows) 102 | { 103 | char* tmp = malloc(rowsize); 104 | char* top = data; 105 | char* bottom = top + rowsize * nrows; 106 | for (int i = 0; i < nrows / 2; i++) { 107 | bottom -= rowsize; 108 | memcpy(tmp, top, rowsize); 109 | memcpy(top, bottom, rowsize); 110 | memcpy(bottom, tmp, rowsize); 111 | top += rowsize; 112 | } 113 | free(tmp); 114 | } 115 | 116 | parg_texture* parg_texture_from_u8( 117 | parg_buffer* buf, int width, int height, int ncomps, int byteoffset) 118 | { 119 | assert(ncomps == 4); 120 | parg_texture* tex = malloc(sizeof(struct parg_texture_s)); 121 | tex->width = width; 122 | tex->height = height; 123 | char* rawdata = parg_buffer_lock(buf, PARG_READ); 124 | glGenTextures(1, &tex->handle); 125 | glBindTexture(GL_TEXTURE_2D, tex->handle); 126 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->width, tex->height, 0, GL_RGBA, 127 | GL_UNSIGNED_BYTE, rawdata + byteoffset); 128 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 129 | glTexParameteri( 130 | GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 131 | glGenerateMipmap(GL_TEXTURE_2D); 132 | parg_buffer_unlock(buf); 133 | return tex; 134 | } 135 | 136 | parg_texture* parg_texture_from_fp32( 137 | parg_buffer* buf, int width, int height, int ncomps, int byteoffset) 138 | { 139 | assert(ncomps == 1); 140 | parg_texture* tex = malloc(sizeof(struct parg_texture_s)); 141 | tex->width = width; 142 | tex->height = height; 143 | char* rawdata = parg_buffer_lock(buf, PARG_READ); 144 | glGenTextures(1, &tex->handle); 145 | glBindTexture(GL_TEXTURE_2D, tex->handle); 146 | glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex->width, tex->height, 0, 147 | GL_ALPHA, GL_FLOAT, rawdata + byteoffset); 148 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 149 | glTexParameteri( 150 | GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 151 | glGenerateMipmap(GL_TEXTURE_2D); 152 | parg_buffer_unlock(buf); 153 | return tex; 154 | } 155 | -------------------------------------------------------------------------------- /src/token.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "internal.h" 3 | #include "khash.h" 4 | 5 | // Mapping from tokens to C strings. 6 | // There's no need for a mapping from C strings to tokens because of math. 7 | KHASH_MAP_INIT_INT(parstr, sds) 8 | 9 | static khash_t(parstr)* _token_registry = 0; 10 | 11 | sds parg_token_to_sds(parg_token token) 12 | { 13 | parg_assert(_token_registry, "Uninitialized token registry"); 14 | khiter_t iter = kh_get(parstr, _token_registry, token); 15 | parg_assert(iter != kh_end(_token_registry), "Unknown token"); 16 | return kh_value(_token_registry, iter); 17 | } 18 | 19 | const char* parg_token_to_string(parg_token token) 20 | { 21 | return parg_token_to_sds(token); 22 | } 23 | 24 | parg_token parg_token_from_string(const char* cstring) 25 | { 26 | parg_token token = kh_str_hash_func(cstring); 27 | if (!_token_registry) { 28 | _token_registry = kh_init(parstr); 29 | } 30 | int ret; 31 | int iter = kh_put(parstr, _token_registry, token, &ret); 32 | kh_value(_token_registry, iter) = sdsnew(cstring); 33 | return token; 34 | } 35 | -------------------------------------------------------------------------------- /src/uniform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pargl.h" 3 | 4 | void parg_uniform1i(parg_token tok, int val) 5 | { 6 | GLint loc = parg_shader_uniform_get(tok); 7 | if (loc > -1) { 8 | glUniform1i(loc, val); 9 | } 10 | } 11 | 12 | void parg_uniform1f(parg_token tok, float val) 13 | { 14 | GLint loc = parg_shader_uniform_get(tok); 15 | if (loc > -1) { 16 | glUniform1f(loc, val); 17 | } 18 | } 19 | 20 | void parg_uniform2f(parg_token tok, float x, float y) 21 | { 22 | GLint loc = parg_shader_uniform_get(tok); 23 | if (loc > -1) { 24 | glUniform2f(loc, x, y); 25 | } 26 | } 27 | 28 | void parg_uniform3f(parg_token tok, const Vector3* val) 29 | { 30 | GLint loc = parg_shader_uniform_get(tok); 31 | if (loc > -1) { 32 | glUniform3fv(loc, 1, &val->x); 33 | } 34 | } 35 | 36 | void parg_uniform4f(parg_token tok, const Vector4* val) 37 | { 38 | GLint loc = parg_shader_uniform_get(tok); 39 | if (loc > -1) { 40 | glUniform4fv(loc, 1, &val->x); 41 | } 42 | } 43 | 44 | void parg_uniform_point(parg_token tok, const Point3* val) 45 | { 46 | GLint loc = parg_shader_uniform_get(tok); 47 | if (loc > -1) { 48 | glUniform3fv(loc, 1, &val->x); 49 | } 50 | } 51 | 52 | void parg_uniform_matrix4f(parg_token tok, const Matrix4* val) 53 | { 54 | GLint loc = parg_shader_uniform_get(tok); 55 | if (loc > -1) { 56 | glUniformMatrix4fv(loc, 1, 0, &val->col0.x); 57 | } 58 | } 59 | 60 | void parg_uniform_matrix3f(parg_token tok, const Matrix3* val) 61 | { 62 | GLint loc = parg_shader_uniform_get(tok); 63 | if (loc == -1) { 64 | return; 65 | } 66 | float packed[9]; 67 | for (int i = 0; i < 9; ++i) { 68 | packed[i] = M3GetElem(*val, i / 3, i % 3); 69 | } 70 | glUniformMatrix3fv(loc, 1, 0, &packed[0]); 71 | } 72 | -------------------------------------------------------------------------------- /src/varray.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pargl.h" 3 | #include "internal.h" 4 | 5 | void parg_varray_enable(parg_buffer* buf, parg_token attr, int ncomps, 6 | parg_data_type type, int stride, int offset) 7 | { 8 | parg_buffer_gpu_bind(buf); 9 | GLint slot = parg_shader_attrib_get(attr); 10 | glEnableVertexAttribArray(slot); 11 | long offset64 = offset; 12 | const GLvoid* ptr = (const GLvoid*) offset64; 13 | glVertexAttribPointer(slot, ncomps, type, GL_FALSE, stride, ptr); 14 | } 15 | 16 | void parg_varray_bind(parg_buffer* buf) { parg_buffer_gpu_bind(buf); } 17 | 18 | void parg_varray_disable(parg_token attr) 19 | { 20 | GLint slot = parg_shader_attrib_get(attr); 21 | glDisableVertexAttribArray(slot); 22 | } 23 | 24 | void parg_varray_instances(parg_token attr, int divisor) 25 | { 26 | GLint slot = parg_shader_attrib_get(attr); 27 | pargVertexAttribDivisor(slot, divisor); 28 | } 29 | -------------------------------------------------------------------------------- /src/window.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "lodepng.h" 9 | 10 | static int _argc = 0; 11 | static char** _argv = 0; 12 | static float _touchpt[2] = {0}; 13 | static float _pixscale = 1.0f; 14 | static int _winwidth = 0; 15 | static int _winheight = 0; 16 | static parg_window_fn_init _init = 0; 17 | static parg_window_fn_tick _tick = 0; 18 | static parg_window_fn_draw _draw = 0; 19 | static parg_window_fn_exit _dispose = 0; 20 | static parg_window_fn_input _input = 0; 21 | static parg_window_fn_message _message = 0; 22 | 23 | void parg_window_setargs(int argc, char* argv[]) 24 | { 25 | _argc = argc; 26 | _argv = argv; 27 | } 28 | 29 | void parg_window_oninit(parg_window_fn_init fn) { _init = fn; } 30 | 31 | void parg_window_ontick(parg_window_fn_tick fn) { _tick = fn; } 32 | 33 | void parg_window_ondraw(parg_window_fn_draw fn) { _draw = fn; } 34 | 35 | void parg_window_onexit(parg_window_fn_exit fn) { _dispose = fn; } 36 | 37 | void parg_window_oninput(parg_window_fn_input fn) { _input = fn; } 38 | 39 | void parg_window_onmessage(parg_window_fn_message fn) { _message = fn; } 40 | 41 | static void onerror(int error, const char* description) 42 | { 43 | fputs(description, stderr); 44 | fputs("\n", stderr); 45 | } 46 | 47 | static void onkey(GLFWwindow* window, int key, int scancode, int action, int m) 48 | { 49 | if ((key == 'Q' || key == GLFW_KEY_ESCAPE) && action == GLFW_PRESS) { 50 | glfwSetWindowShouldClose(window, GL_TRUE); 51 | } 52 | if (_input && (action == GLFW_PRESS || action == GLFW_REPEAT)) { 53 | _input(PARG_EVENT_KEYPRESS, key, 0, 0); 54 | } 55 | } 56 | 57 | // GLFW has the origin at top-left 58 | static void onmove(GLFWwindow* window, double x, double y) 59 | { 60 | if (_input) { 61 | _touchpt[0] = x / _winwidth; 62 | _touchpt[1] = (_winheight - 1 - y) / _winheight; 63 | _input(PARG_EVENT_MOVE, _touchpt[0], _touchpt[1], 0); 64 | } 65 | } 66 | 67 | static void onclick(GLFWwindow* window, int button, int action, int mods) 68 | { 69 | if (!_input) { 70 | return; 71 | } 72 | if (action == GLFW_PRESS) { 73 | _input(PARG_EVENT_DOWN, _touchpt[0], _touchpt[1], 0); 74 | } 75 | if (action == GLFW_RELEASE) { 76 | _input(PARG_EVENT_UP, _touchpt[0], _touchpt[1], 0); 77 | } 78 | } 79 | 80 | static void onscroll(GLFWwindow* window, double dx, double dy) 81 | { 82 | if (_input) { 83 | _input(PARG_EVENT_MOVE, _touchpt[0], _touchpt[1], dy); 84 | } 85 | } 86 | 87 | int parg_window_exec(float winwidth, float winheight, int vsync, int aa) 88 | { 89 | GLFWwindow* window; 90 | glfwSetErrorCallback(onerror); 91 | if (!glfwInit()) { 92 | exit(EXIT_FAILURE); 93 | } 94 | 95 | // We use Desktop OpenGL 2.1 context only because it's the closest API to 96 | // OpenGL ES 2.0 that's available on Apple machines. 97 | 98 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 99 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); 100 | glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 101 | if (aa) { 102 | glfwWindowHint(GLFW_SAMPLES, 4); 103 | } 104 | 105 | // This removes borders which looks nice sometimes. 106 | glfwWindowHint(GLFW_DECORATED, GL_FALSE); 107 | 108 | // This GLFW feature doesn't exist yet but it's on the way. 109 | #if GLFW_VERSION_MAJOR > 3 && GLFW_VERSION_MINOR > 1000 110 | glfwWindowHint(GLFW_ALPHA_MASK, GL_TRUE); 111 | #endif 112 | 113 | char* capture = 0; 114 | for (int i = 1; i < _argc - 1; i++) { 115 | if (0 == strcmp(_argv[i], "-capture")) { 116 | capture = _argv[i + 1]; 117 | glfwWindowHint(GLFW_VISIBLE, 0); 118 | } 119 | } 120 | 121 | // 1.85 is the "Letterbox" aspect ratio, popular in the film industry. 122 | // Also, the window is small enough to fit just fine on my 13" Pro. 123 | 124 | window = glfwCreateWindow(winwidth, winheight, "par", 0, 0); 125 | if (!window) { 126 | glfwTerminate(); 127 | exit(EXIT_FAILURE); 128 | } 129 | 130 | int width, height; 131 | glfwGetFramebufferSize(window, &width, &height); 132 | glfwGetWindowSize(window, &_winwidth, &_winheight); 133 | _pixscale = (float) width / _winwidth; 134 | glfwMakeContextCurrent(window); 135 | glfwSwapInterval(vsync); 136 | if (_init) { 137 | _init(_winwidth, _winheight, _pixscale); 138 | } 139 | glfwMakeContextCurrent(0); 140 | glfwSetKeyCallback(window, onkey); 141 | glfwSetCursorPosCallback(window, onmove); 142 | glfwSetMouseButtonCallback(window, onclick); 143 | glfwSetScrollCallback(window, onscroll); 144 | 145 | struct timeval tm1; 146 | gettimeofday(&tm1, NULL); 147 | 148 | while (!glfwWindowShouldClose(window)) { 149 | int width, height; 150 | 151 | // Get microseconds. 152 | struct timeval tm2; 153 | gettimeofday(&tm2, 0); 154 | unsigned long long milliseconds = 1000 * (tm2.tv_sec - tm1.tv_sec) + 155 | (tm2.tv_usec - tm1.tv_usec) / 1000; 156 | 157 | // Check if the window has been resized. 158 | glfwGetFramebufferSize(window, &width, &height); 159 | glfwGetWindowSize(window, &_winwidth, &_winheight); 160 | _pixscale = (float) width / _winwidth; 161 | int needs_draw = 1; 162 | if (_tick) { 163 | needs_draw = 164 | _tick(_winwidth, _winheight, _pixscale, milliseconds / 1000.0); 165 | } 166 | 167 | // Perform all OpenGL work. 168 | glfwMakeContextCurrent(window); 169 | if (needs_draw && _draw) { 170 | if (capture) { 171 | parg_framebuffer_create_empty( 172 | width, height, PARG_FBO_DEPTH | PARG_FBO_ALPHA); 173 | } 174 | _draw(); 175 | GLenum err = glGetError(); 176 | if (err != GL_NO_ERROR) { 177 | puts("OpenGL Error\n"); 178 | } 179 | glfwSwapBuffers(window); 180 | if (capture) { 181 | unsigned char* buffer = malloc(width * height * 4); 182 | glReadPixels( 183 | 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); 184 | parg_texture_fliprows(buffer, width * 4, height); 185 | lodepng_encode32_file(capture, buffer, width, height); 186 | free(buffer); 187 | break; 188 | } 189 | } 190 | glfwMakeContextCurrent(0); 191 | glfwPollEvents(); 192 | } 193 | 194 | // First perform OpenGL-related cleanup. 195 | glfwMakeContextCurrent(window); 196 | if (_dispose) { 197 | _dispose(); 198 | } 199 | GLenum err = glGetError(); 200 | if (err != GL_NO_ERROR) { 201 | puts("OpenGL Error"); 202 | } 203 | glfwMakeContextCurrent(0); 204 | 205 | // Perform all other cleanup. 206 | glfwDestroyWindow(window); 207 | glfwTerminate(); 208 | 209 | return 0; 210 | } 211 | -------------------------------------------------------------------------------- /src/zcam.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static DMatrix4 _projmat; 4 | static DPoint3 _camerapos; 5 | static DVector3 _worldsize; 6 | static double _maxcamz; 7 | static double _mincamz; 8 | static double _fovy; 9 | static double _winaspect = 0; 10 | static double _zplanes[2]; 11 | static DPoint3 _grabpt; 12 | static int _grabbing = 0; 13 | static int _dirty = 1; 14 | 15 | #define MIN(a, b) (a > b ? b : a) 16 | #define MAX(a, b) (a > b ? a : b) 17 | #define CLAMP(v, lo, hi) MAX(lo, MIN(hi, v)) 18 | 19 | DPoint3 parg_zcam_to_world(float winx, float winy) 20 | { 21 | DPoint3 worldspace; 22 | double vpheight = 2 * tan(_fovy / 2) * _camerapos.z; 23 | double vpwidth = vpheight * _winaspect; 24 | worldspace.y = _camerapos.y + vpheight * (winy - 0.5); 25 | worldspace.x = _camerapos.x + vpwidth * (winx - 0.5); 26 | worldspace.z = 0; 27 | return worldspace; 28 | } 29 | 30 | void parg_zcam_get_viewport(float* lbrt) 31 | { 32 | double vpheight = 2 * tan(_fovy / 2) * _camerapos.z; 33 | double vpwidth = vpheight * _winaspect; 34 | float left = _camerapos.x - vpwidth * 0.5; 35 | float bottom = _camerapos.y - vpheight * 0.5; 36 | float right = _camerapos.x + vpwidth * 0.5; 37 | float top = _camerapos.y + vpheight * 0.5; 38 | *lbrt++ = left; 39 | *lbrt++ = bottom; 40 | *lbrt++ = right; 41 | *lbrt = top; 42 | } 43 | 44 | void parg_zcam_get_viewportd(double* lbrt) 45 | { 46 | double vpheight = 2 * tan(_fovy / 2) * _camerapos.z; 47 | double vpwidth = vpheight * _winaspect; 48 | double left = _camerapos.x - vpwidth * 0.5; 49 | double bottom = _camerapos.y - vpheight * 0.5; 50 | double right = _camerapos.x + vpwidth * 0.5; 51 | double top = _camerapos.y + vpheight * 0.5; 52 | *lbrt++ = left; 53 | *lbrt++ = bottom; 54 | *lbrt++ = right; 55 | *lbrt = top; 56 | } 57 | 58 | parg_aar parg_zcam_get_rectangle() 59 | { 60 | parg_aar rect; 61 | parg_zcam_get_viewport(&rect.left); 62 | return rect; 63 | } 64 | 65 | void parg_zcam_init(float worldwidth, float worldheight, float fovy) 66 | { 67 | _maxcamz = 0.5 * worldheight / tan(fovy * 0.5); 68 | _camerapos = (DPoint3){0, 0, _maxcamz}; 69 | _mincamz = 0; 70 | _zplanes[0] = _mincamz; 71 | _zplanes[1] = _maxcamz * 1.5; 72 | _fovy = fovy; 73 | _worldsize.x = worldwidth; 74 | _worldsize.y = worldheight; 75 | } 76 | 77 | void parg_zcam_tick(float winaspect, float seconds) 78 | { 79 | if (_winaspect != winaspect) { 80 | _winaspect = winaspect; 81 | double* z = _zplanes; 82 | _projmat = DM4MakePerspective(_fovy, _winaspect, z[0], z[1]); 83 | } 84 | } 85 | 86 | float parg_zcam_get_magnification() 87 | { 88 | double vpheight = 2 * tan(_fovy / 2) * _camerapos.z; 89 | return _worldsize.y / vpheight; 90 | } 91 | 92 | void parg_zcam_grab_begin(float winx, float winy) 93 | { 94 | _grabbing = 1; 95 | _grabpt = parg_zcam_to_world(winx, winy); 96 | } 97 | 98 | void parg_zcam_grab_update(float winx, float winy, float scrolldelta) 99 | { 100 | DPoint3 prev = _camerapos; 101 | if (_grabbing) { 102 | double vpheight = 2 * tan(_fovy / 2) * _camerapos.z; 103 | double vpwidth = vpheight * _winaspect; 104 | _camerapos.y = -vpheight * (winy - 0.5) + _grabpt.y; 105 | _camerapos.x = -vpwidth * (winx - 0.5) + _grabpt.x; 106 | } else if (scrolldelta) { 107 | DPoint3 focalpt = parg_zcam_to_world(winx, winy); 108 | _camerapos.z -= scrolldelta * _camerapos.z * 0.01; 109 | _camerapos.z = CLAMP(_camerapos.z, _mincamz, _maxcamz); 110 | double vpheight = 2 * tan(_fovy / 2) * _camerapos.z; 111 | double vpwidth = vpheight * _winaspect; 112 | _camerapos.y = -vpheight * (winy - 0.5) + focalpt.y; 113 | _camerapos.x = -vpwidth * (winx - 0.5) + focalpt.x; 114 | } 115 | _dirty |= prev.x != _camerapos.x || prev.y != _camerapos.y || 116 | prev.z != _camerapos.z; 117 | } 118 | 119 | void parg_zcam_set_position(double x, double y, double z) 120 | { 121 | _camerapos.x = x; 122 | _camerapos.y = y; 123 | _camerapos.z = z; 124 | _dirty = 1; 125 | _grabbing = 0; 126 | } 127 | 128 | void parg_zcam_frame_position(double const* xyw) 129 | { 130 | double vpheight = xyw[2] / _winaspect; 131 | _camerapos.x = xyw[0]; 132 | _camerapos.y = xyw[1]; 133 | _camerapos.z = 0.5 * vpheight / tan(_fovy / 2); 134 | _dirty = 1; 135 | _grabbing = 0; 136 | } 137 | 138 | void parg_zcam_grab_end() { _grabbing = 0; } 139 | 140 | DPoint3 parg_zcam_dmatrices(DMatrix4* proj, DMatrix4* view) 141 | { 142 | *proj = _projmat; 143 | DPoint3 target = {_camerapos.x, _camerapos.y, 0}; 144 | DVector3 up = {0, 1, 0}; 145 | *view = DM4MakeLookAt(_camerapos, target, up); 146 | return _camerapos; 147 | } 148 | 149 | Point3 parg_zcam_matrices(Matrix4* proj, Matrix4* view) 150 | { 151 | *proj = M4MakeFromDM4(_projmat); 152 | DPoint3 target = {_camerapos.x, _camerapos.y, 0}; 153 | DVector3 up = {0, 1, 0}; 154 | *view = M4MakeFromDM4(DM4MakeLookAt(_camerapos, target, up)); 155 | return (Point3){_camerapos.x, _camerapos.y, _camerapos.z}; 156 | } 157 | 158 | void parg_zcam_highprec(Matrix4* vp, Point3* eyepos_lo, Point3* eyepos_hi) 159 | { 160 | DPoint3 origin = {0, 0, 0}; 161 | DPoint3 target = {0, 0, -1}; 162 | DVector3 up = {0, 1, 0}; 163 | DMatrix4 view = DM4MakeLookAt(origin, target, up); 164 | if (vp) { 165 | *vp = M4MakeFromDM4(DM4Mul(_projmat, view)); 166 | } 167 | Point3 eyepos = P3MakeFromDP3(_camerapos); 168 | DPoint3 deyepos = DP3MakeFromP3(eyepos); 169 | DVector3 difference = DP3Sub(_camerapos, deyepos); 170 | if (eyepos_lo) { 171 | *eyepos_lo = P3MakeFromV3(V3MakeFromDV3(difference)); 172 | } 173 | *eyepos_hi = eyepos; 174 | } 175 | 176 | int parg_zcam_has_moved() 177 | { 178 | int retval = _dirty; 179 | _dirty = 0; 180 | return retval; 181 | } 182 | 183 | void parg_zcam_touch() { _dirty = 1; } 184 | 185 | // Van Wijk Interpolation: consumes two 3-tuples and produces one 3-tuple. 186 | // cameraA... XY starting center point and viewport size 187 | // cameraB... XY ending center point and viewport size 188 | // result... XY computed center and viewport size 189 | // t......... if -1, returns a recommended duration 190 | void parg_zcam_blend( 191 | double const* cameraA, double const* cameraB, double* result, double t) 192 | { 193 | double rho = sqrt(2.0), rho2 = 2, rho4 = 4, ux0 = cameraA[0], 194 | uy0 = cameraA[1], w0 = cameraA[2], ux1 = cameraB[0], 195 | uy1 = cameraB[1], w1 = cameraB[2], dx = ux1 - ux0, dy = uy1 - uy0, 196 | d2 = dx * dx + dy * dy, d1 = sqrt(d2), 197 | b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1), 198 | b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1), 199 | r0 = log(sqrt(b0 * b0 + 1.0) - b0), r1 = log(sqrt(b1 * b1 + 1) - b1), 200 | dr = r1 - r0; 201 | int validdr = (dr == dr) && dr != 0; 202 | double S = (validdr ? dr : log(w1 / w0)) / rho; 203 | if (t == -1) { 204 | result[0] = fabs(S * 1000.0); 205 | return; 206 | } 207 | double s = t * S; 208 | if (validdr) { 209 | double coshr0 = cosh(r0), 210 | u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0)); 211 | result[0] = ux0 + u * dx; 212 | result[1] = uy0 + u * dy; 213 | result[2] = w0 * coshr0 / cosh(rho * s + r0); 214 | return; 215 | } 216 | result[0] = ux0 + t * dx; 217 | result[1] = uy0 + t * dy; 218 | result[2] = w0 * exp(rho * s); 219 | } 220 | -------------------------------------------------------------------------------- /uncrustify.cfg: -------------------------------------------------------------------------------- 1 | indent_columns = 4 2 | indent_with_tabs = 0 3 | indent_continue = 4 4 | nl_func_leave_one_liners = true 5 | 6 | nl_collapse_empty_body = true 7 | nl_after_return = False 8 | nl_max = 2 9 | nl_after_func_proto = 0 10 | nl_after_func_proto_group = 1 11 | nl_after_func_body = 2 12 | nl_after_func_body_one_liner = 2 13 | nl_before_block_comment = 1 14 | nl_before_cpp_comment = 1 15 | nl_after_multiline_comment = False 16 | nl_after_struct = 1 17 | nl_end_of_file = add 18 | nl_end_of_file_min = 1 19 | -------------------------------------------------------------------------------- /web/css/main.css: -------------------------------------------------------------------------------- 1 | /*! HTML5 Boilerplate v5.0 | MIT License | http://h5bp.com/ */ 2 | 3 | html { 4 | color: #222; 5 | font-size: 1em; 6 | line-height: 1.4; 7 | } 8 | 9 | html, body { 10 | -webkit-touch-callout: none; 11 | -webkit-user-select: none; 12 | -khtml-user-select: none; 13 | -moz-user-select: none; 14 | -ms-user-select: none; 15 | user-select: none; 16 | } 17 | 18 | ::-moz-selection { 19 | background: #b3d4fc; 20 | text-shadow: none; 21 | } 22 | 23 | ::selection { 24 | background: #b3d4fc; 25 | text-shadow: none; 26 | } 27 | 28 | hr { 29 | display: block; 30 | height: 1px; 31 | border: 0; 32 | border-top: 1px solid #ccc; 33 | margin: 1em 0; 34 | padding: 0; 35 | } 36 | 37 | audio, 38 | canvas, 39 | iframe, 40 | img, 41 | svg, 42 | video { 43 | vertical-align: middle; 44 | } 45 | 46 | fieldset { 47 | border: 0; 48 | margin: 0; 49 | padding: 0; 50 | } 51 | 52 | textarea { 53 | resize: vertical; 54 | } 55 | 56 | .browserupgrade { 57 | margin: 0.2em 0; 58 | background: #ccc; 59 | color: #000; 60 | padding: 0.2em 0; 61 | } 62 | 63 | 64 | /* ========================================================================== 65 | Author's custom styles 66 | ========================================================================== */ 67 | 68 | html, body, .app { 69 | height: 100%; 70 | width: 100%; 71 | padding: 0; 72 | margin: 0; 73 | font-size: 16px; 74 | font-family: 'Roboto Condensed', sans-serif; 75 | -webkit-touch-callout: none; 76 | -webkit-user-select: none; 77 | -khtml-user-select: none; 78 | -moz-user-select: none; 79 | -ms-user-select: none; 80 | user-select: none; 81 | } 82 | 83 | #menu-toggle { 84 | position:absolute; 85 | top:10px; 86 | right:10px; 87 | width:20px; 88 | height:20px; 89 | background-color: gray; 90 | background-repeat:no-repeat; 91 | background-image:url('../img/hud.png'); 92 | background-size: 160px 20px; 93 | background-position:-1px 0px; 94 | opacity: 0.5; 95 | } 96 | 97 | #fps-toggle { 98 | right:35px; 99 | background-position:-51px 0px; 100 | } 101 | 102 | #menu-toggle:hover { 103 | cursor:pointer; 104 | } 105 | 106 | #fps-toggle:hover { 107 | cursor:pointer; 108 | } 109 | 110 | .app { 111 | overflow: hidden; 112 | display: -webkit-flex; 113 | display: flex; 114 | flex-direction: column; 115 | } 116 | 117 | input[type=range][orient=vertical] 118 | { 119 | writing-mode: bt-lr; /* IE */ 120 | -webkit-appearance: slider-vertical; /* WebKit */ 121 | width: 8px; 122 | height: 175px; 123 | padding: 0 5px; 124 | } 125 | 126 | main { 127 | display: -webkit-flex; 128 | display: flex; 129 | -webkit-flex-direction: row; 130 | flex-direction: row; 131 | -webkit-flex: auto; 132 | flex: auto; 133 | overflow: hidden; 134 | } 135 | 136 | nav { 137 | width: 200px; 138 | min-width: 200px; 139 | padding: 10px; 140 | background: rgb(86, 76, 65); 141 | transition: all .2s; 142 | } 143 | 144 | nav.collapsed { 145 | width: 0; 146 | padding: 0; 147 | min-width: 0; 148 | transition: all .2s; 149 | } 150 | 151 | article { 152 | -webkit-flex: auto; 153 | flex: auto; 154 | overflow-x: hidden; 155 | overflow-y: auto; 156 | display: -webkit-flex; 157 | display: flex; 158 | position: relative; 159 | } 160 | 161 | .canvas-container { 162 | display: -webkit-flex; 163 | display: flex; 164 | -webkit-flex: 1; 165 | flex: 1; 166 | -webkit-align-items: center; 167 | align-items: center; 168 | -webkit-justify-content: center; 169 | justify-content: center; 170 | background: #303030; 171 | color: white; 172 | position: relative; 173 | } 174 | 175 | canvas,svg { 176 | position: absolute; 177 | left: 0; 178 | top: 0; 179 | } 180 | 181 | svg { 182 | pointer-events:none; 183 | } 184 | 185 | svg text { 186 | paint-order: stroke; 187 | stroke: #444; 188 | stroke-width: 0.01px; 189 | fill: #eee; 190 | font-family: 'Roboto Condensed', sans-serif; 191 | } 192 | 193 | nav a { 194 | color: #07f; 195 | text-decoration: none; 196 | } 197 | 198 | nav a:hover { 199 | cursor: pointer; 200 | } 201 | 202 | nav button { 203 | width: 100%; 204 | background-color: rgba(255,255,255,0.125); 205 | font-family: 'Roboto Condensed', sans-serif; 206 | line-height: 2.0; 207 | border: none; 208 | border-radius: 5px; 209 | color: #aaa; 210 | text-decoration: none; 211 | display: block; 212 | text-align: center; 213 | margin-top: 5px; 214 | margin-bottom: 5px; 215 | } 216 | 217 | nav button:hover { 218 | background: #0078e7; 219 | color: #eee; 220 | cursor: pointer; 221 | } 222 | 223 | nav button.selected { 224 | background: #30a8e7; 225 | color: black; 226 | cursor: pointer; 227 | } 228 | 229 | nav button:focus { 230 | outline: 0; 231 | } 232 | 233 | nav a.wip, nav button.wip, nav a.disabled, nav button.disabled { 234 | color: #aaa; 235 | background: #e6e6e6; 236 | } 237 | 238 | nav a.disabled, nav button.disabled { 239 | cursor: default; 240 | } 241 | 242 | nav a.disabled:hover, nav button.disabled:hover { 243 | } 244 | 245 | /* ========================================================================== 246 | Media Queries 247 | ========================================================================== */ 248 | 249 | @media only screen and (min-width: 35em) { 250 | 251 | } 252 | 253 | @media print, 254 | (-o-min-device-pixel-ratio: 5/4), 255 | (-webkit-min-device-pixel-ratio: 1.25), 256 | (min-resolution: 120dpi) { 257 | 258 | } 259 | 260 | /* ========================================================================== 261 | Helper classes 262 | ========================================================================== */ 263 | 264 | .hidden { 265 | display: none !important; 266 | visibility: hidden; 267 | } 268 | 269 | .visuallyhidden { 270 | border: 0; 271 | clip: rect(0 0 0 0); 272 | height: 1px; 273 | margin: -1px; 274 | overflow: hidden; 275 | padding: 0; 276 | position: absolute; 277 | width: 1px; 278 | } 279 | 280 | .visuallyhidden.focusable:active, 281 | .visuallyhidden.focusable:focus { 282 | clip: auto; 283 | height: auto; 284 | margin: 0; 285 | overflow: visible; 286 | position: static; 287 | width: auto; 288 | } 289 | 290 | .invisible { 291 | visibility: hidden; 292 | } 293 | 294 | .clearfix:before, 295 | .clearfix:after { 296 | content: " "; 297 | display: table; 298 | } 299 | 300 | .clearfix:after { 301 | clear: both; 302 | } 303 | 304 | .clearfix { 305 | *zoom: 1; 306 | } 307 | 308 | /* ========================================================================== 309 | Print styles 310 | ========================================================================== */ 311 | 312 | @media print { 313 | *, 314 | *:before, 315 | *:after { 316 | background: transparent !important; 317 | color: #000 !important; 318 | box-shadow: none !important; 319 | text-shadow: none !important; 320 | } 321 | 322 | a, 323 | a:visited { 324 | text-decoration: underline; 325 | } 326 | 327 | a[href]:after { 328 | content: " (" attr(href) ")"; 329 | } 330 | 331 | abbr[title]:after { 332 | content: " (" attr(title) ")"; 333 | } 334 | 335 | a[href^="#"]:after, 336 | a[href^="javascript:"]:after { 337 | content: ""; 338 | } 339 | 340 | pre, 341 | blockquote { 342 | border: 1px solid #999; 343 | page-break-inside: avoid; 344 | } 345 | 346 | thead { 347 | display: table-header-group; 348 | } 349 | 350 | tr, 351 | img { 352 | page-break-inside: avoid; 353 | } 354 | 355 | img { 356 | max-width: 100% !important; 357 | } 358 | 359 | p, 360 | h2, 361 | h3 { 362 | orphans: 3; 363 | widows: 3; 364 | } 365 | 366 | h2, 367 | h3 { 368 | page-break-after: avoid; 369 | } 370 | } -------------------------------------------------------------------------------- /web/css/normalize.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} -------------------------------------------------------------------------------- /web/img/emscripten.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prideout/parg/a295a79169292830fc75ba7ee60a56451aa3d523/web/img/emscripten.png -------------------------------------------------------------------------------- /web/img/hud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prideout/parg/a295a79169292830fc75ba7ee60a56451aa3d523/web/img/hud.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | parg 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 21 |
22 |
23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /web/js/parg.js: -------------------------------------------------------------------------------- 1 | 'using strict'; 2 | 3 | /* jshint esnext: true */ 4 | 5 | var PargApp = function(canvas, args, baseurl, block_interaction, attribs) { 6 | this.attribs = attribs || { 7 | alpha: true, 8 | antialias: true 9 | }; 10 | this.canvas = canvas; 11 | this.GLctx = null; 12 | this.args = args; 13 | this.baseurl = baseurl || 'parg/'; 14 | this.nrequests = 0; 15 | this.requests = []; 16 | this.linked_module = null; 17 | this.block_interaction = block_interaction; 18 | 19 | // First, execute the user-defined main() function in its entirety: 20 | this.module = CreateParg({parg: this}); 21 | 22 | // Now, make HTTP requests for all assets: 23 | this.request_assets(); 24 | 25 | // After receiving responses from all HTTP requests, parg will 26 | // automatically call the user-defined init() function. 27 | 28 | this.viewBox = [-1.5, 1.5, 3, 3]; 29 | 30 | if (window.Snap) { 31 | this.paper = window.Snap('#hud').attr({'viewBox': this.viewBox}); 32 | this.rect = this.paper.rect(-0.2, -0.2, 0.4, 0.4).attr({ 33 | fill: 'white', 34 | strokeWidth: 0, 35 | opacity: 0.01 36 | }); 37 | this.labels = {}; 38 | } 39 | }; 40 | 41 | PargApp.prototype.onpod = function(msg, pvalues, nvalues) { 42 | var pod, x, y, id, miz, maxz, el, idx; 43 | if (msg == "labels" && this.labels) { 44 | pod = this.module.HEAPF64.subarray(pvalues, pvalues + nvalues); 45 | var removals = Object.keys(this.labels).map(parseFloat); 46 | for (var i = 0; i < nvalues;) { 47 | x = pod[i++]; y = -pod[i++] - 0.2; id = pod[i++]; 48 | minz = pod[i++]; maxz = pod[i++]; 49 | el = this.labels[id]; idx = removals.indexOf(id); 50 | if (!el) { 51 | this.labels[id] = this.paper.text(x, y, '' + id).attr({ 52 | 'text-anchor':'middle', 53 | 'font-size':'0.1', 54 | }); 55 | } else { 56 | el.attr({'x': x, 'y': y}); 57 | } 58 | if (idx > -1) { 59 | removals.splice(idx, 1); 60 | } 61 | } 62 | for (id of removals) { 63 | this.labels[id].remove(); 64 | delete this.labels[id]; 65 | } 66 | } else if (msg == "viewport" && this.paper) { 67 | pod = this.module.HEAPF64.subarray(pvalues, pvalues + nvalues); 68 | var left = pod[0], bottom = pod[1], right = pod[2], top = pod[3]; 69 | this.paper.attr({'viewBox': [left, -top, right - left, top - bottom]}); 70 | this.rect.attr({ 71 | 'x': left, 'y': -top, 72 | 'width': right - left, 73 | 'height': top - bottom}); 74 | } 75 | }; 76 | 77 | PargApp.prototype.image_preload = function(id) { 78 | var url = this.baseurl + id; 79 | var img = new Image(); 80 | var onloadFunc = function() { 81 | this.onimage(id, img); 82 | }.bind(this); 83 | var errorFunc = function() { 84 | window.console.error('Unable to download ' + url); 85 | }; 86 | img.onload = onloadFunc; 87 | img.onerror = errorFunc; 88 | this.requests[this.nrequests++] = img; 89 | img.src = url; 90 | }; 91 | 92 | PargApp.prototype.asset_preload = function(id) { 93 | if (id.endsWith('.png')) { 94 | this.image_preload(id); 95 | return; 96 | } 97 | var url = this.baseurl + id; 98 | var xhr = new XMLHttpRequest(); 99 | xhr.open('GET', url, true); 100 | xhr.responseType = 'arraybuffer'; 101 | var onloadFunc = function() { 102 | if (xhr.response) { 103 | this.onasset(id, xhr.response); 104 | } 105 | }.bind(this); 106 | var errorFunc = function() { 107 | window.console.error('Unable to download ' + url); 108 | }; 109 | xhr.onload = onloadFunc; 110 | xhr.onerror = errorFunc; 111 | this.requests[this.nrequests++] = xhr; 112 | }; 113 | 114 | PargApp.prototype.request_assets = function() { 115 | for (var i = 0, len = this.requests.length; i < len; i++) { 116 | if (this.requests[i].send) { 117 | this.requests[i].send(null); 118 | } 119 | } 120 | }; 121 | 122 | PargApp.prototype.onasset = function(id, arraybuffer) { 123 | var ptr = this.module.Asset.alloc(id, arraybuffer.byteLength); 124 | var u8buffer = new Uint8Array(arraybuffer); 125 | this.module.HEAPU8.set(u8buffer, ptr); 126 | this.module.Asset.commit(id); 127 | if (--this.nrequests === 0) { 128 | this.start(); 129 | } 130 | }; 131 | 132 | PargApp.prototype.onimage = function(id, img) { 133 | var canvas = document.createElement("canvas"); 134 | var w = canvas.width = img.width; 135 | var h = canvas.height = img.height; 136 | var ctx = canvas.getContext("2d"); 137 | ctx.drawImage(img, 0, 0); 138 | var pixelview = ctx.getImageData(0, 0, w, h).data; 139 | var pixelbuf = pixelview.buffer; 140 | var annotated = new Uint8Array(pixelbuf.byteLength + 12); 141 | var metadata = new Uint32Array(annotated.buffer); 142 | metadata[0] = w; 143 | metadata[1] = h; 144 | metadata[2] = 4; 145 | annotated.subarray(12).set(pixelview); 146 | this.onasset(id, annotated); 147 | }; 148 | 149 | PargApp.prototype.start = function() { 150 | 151 | var cevents = { 152 | PAR_EVENT_DOWN: 0, 153 | PAR_EVENT_UP: 1, 154 | PAR_EVENT_MOVE: 2 155 | }; 156 | 157 | var dims = this.module.par_window_dims || this.module.parg_window_dims; 158 | var $canvas = $(this.canvas); 159 | var canvas = $canvas[0]; 160 | $('canvas,svg').css({ 161 | width: dims[0] + 'px', 162 | height: dims[1] + 'px' 163 | }); 164 | canvas.width = dims[0] * window.devicePixelRatio; 165 | canvas.height = dims[1] * window.devicePixelRatio; 166 | 167 | var contextHandle = this.module.GL.createContext(canvas, this.attribs); 168 | this.module.GL.makeContextCurrent(contextHandle); 169 | var GLctx = this.module.ctx; 170 | this.GLctx = GLctx; 171 | GLctx.clearColor(0.2, 0.4, 0.8, 1.0); 172 | GLctx.clear(GLctx.COLOR_BUFFER_BIT); 173 | GLctx.getExtension('OES_element_index_uint'); 174 | GLctx.getExtension('OES_standard_derivatives'); 175 | 176 | this.module.Window.init(this.args); 177 | 178 | var clientWidth = canvas.clientWidth; 179 | var clientHeight = canvas.clientHeight; 180 | var clientMaxY = clientHeight - 1; 181 | var app = this; 182 | 183 | var onmousecore = function(event) { 184 | if (app.block_interaction) { 185 | return; 186 | } 187 | var box = canvas.getBoundingClientRect(); 188 | var x = (event.clientX - box.left) / clientWidth; 189 | var y = (clientMaxY - event.clientY + box.top) / clientHeight; 190 | var etype = event.type; 191 | var delta; 192 | if (etype == "mousedown") { 193 | this.Window.input(cevents.PAR_EVENT_DOWN, x, y, 0); 194 | } else if (etype == "mouseup") { 195 | this.Window.input(cevents.PAR_EVENT_UP, x, y, 0); 196 | } else if (etype == "mousemove") { 197 | this.Window.input(cevents.PAR_EVENT_MOVE, x, y, 0); 198 | } else if (etype == "mousewheel") { 199 | event.preventDefault(); 200 | delta = event.wheelDelta / 10.0; 201 | this.Window.input(cevents.PAR_EVENT_MOVE, x, y, delta); 202 | } else if (etype == "DOMMouseScroll") { 203 | event.preventDefault(); 204 | delta = -event.detail * 2.0; 205 | this.Window.input(cevents.PAR_EVENT_MOVE, x, y, delta); 206 | } else if (etype == "wheel") { 207 | event.preventDefault(); 208 | delta = -event.deltaY / 2.0; 209 | this.Window.input(cevents.PAR_EVENT_MOVE, x, y, delta); 210 | } 211 | }; 212 | 213 | var onmouse = onmousecore.bind(this.module); 214 | if (this.linked_module) { 215 | var fn1 = onmouse; 216 | var fn2 = onmousecore.bind(this.linked_module); 217 | onmouse = function(event) { 218 | fn1(event); 219 | fn2(event); 220 | }; 221 | } 222 | 223 | canvas.addEventListener("mousedown", onmouse); 224 | canvas.addEventListener("mouseup", onmouse); 225 | canvas.addEventListener("mousemove", onmouse); 226 | canvas.addEventListener("mousewheel", onmouse); 227 | canvas.addEventListener("DOMMouseScroll", onmouse); 228 | canvas.addEventListener("wheel", onmouse); 229 | 230 | var raf = function() { 231 | var milliseconds = window.performance.now(); 232 | var needs_draw = this.module.Window.tick(milliseconds / 1000.0, 233 | window.devicePixelRatio); 234 | if (needs_draw) { 235 | this.module.Window.draw(); 236 | } 237 | window.requestAnimationFrame(raf); 238 | }.bind(this); 239 | 240 | window.requestAnimationFrame(raf); 241 | }; 242 | -------------------------------------------------------------------------------- /web/js/suffix.js: -------------------------------------------------------------------------------- 1 | // Expose the GL namespace so that the parg client can access it. 2 | Module["GL"] = GL; 3 | -------------------------------------------------------------------------------- /web/multi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | parg 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 18 |
19 | 21 |
22 | 23 | 24 | 25 | 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /web/parg: -------------------------------------------------------------------------------- 1 | ../build --------------------------------------------------------------------------------