diff options
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | examples/CMakeLists.txt | 2 | ||||
-rw-r--r-- | examples/freefall01.cpp | 224 | ||||
-rw-r--r-- | examples/freefall01.html | 208 | ||||
-rw-r--r-- | examples/piviz.cpp | 15 | ||||
-rw-r--r-- | examples/spacewars.cpp | 20 | ||||
-rw-r--r-- | include/pixel/pixel.hpp | 4 | ||||
-rw-r--r-- | src/sdl_subsys.cpp | 5 |
8 files changed, 354 insertions, 125 deletions
@@ -15,6 +15,7 @@ C++20 and better where the [SDL2 library](https://www.libsdl.org/) is supported, optionally SDL2 with [emscripten](https://emscripten.org/) or [SFML library](https://www.sfml-dev.org/). ## Online WebAssembly Examples +* [freefall01](https://jausoft.com/projects/gfxbox2/freefall01.html) * [piviz](https://jausoft.com/projects/gfxbox2/piviz.html) * [spacewars](https://jausoft.com/projects/gfxbox2/spacewars.html) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f80f98c..40c52ac 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,7 +4,7 @@ include_directories( # These examples use the standard separate compilation if (DEFINED EMSCRIPTEN) - set(SOURCES_IDIOMATIC_TARGETS "spacewars.cpp;piviz.cpp") + set(SOURCES_IDIOMATIC_TARGETS "spacewars.cpp;piviz.cpp;freefall01.cpp") else() file(GLOB SOURCES_IDIOMATIC_TARGETS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp") endif() diff --git a/examples/freefall01.cpp b/examples/freefall01.cpp index fbe8295..1ceac9b 100644 --- a/examples/freefall01.cpp +++ b/examples/freefall01.cpp @@ -236,64 +236,22 @@ class ball_t : public pixel::f2::disk_t { } }; -int main(int argc, char *argv[]) -{ - float rho = rho_default; - int win_width = 1920, win_height = 1080; - std::string record_bmpseq_basename; - bool enable_vsync = true; - int forced_fps = -1; - { - for(int i=1; i<argc; ++i) { - if( 0 == strcmp("-width", argv[i]) && i+1<argc) { - win_width = atoi(argv[i+1]); - ++i; - } else if( 0 == strcmp("-height", argv[i]) && i+1<argc) { - win_height = atoi(argv[i+1]); - ++i; - } else if( 0 == strcmp("-record", argv[i]) && i+1<argc) { - record_bmpseq_basename = argv[i+1]; - ++i; - } else if( 0 == strcmp("-debug_gfx", argv[i]) ) { - debug_gfx = true; - } else if( 0 == strcmp("-fps", argv[i]) && i+1<argc) { - forced_fps = atoi(argv[i+1]); - enable_vsync = false; - ++i; - } else if( 0 == strcmp("-rho", argv[i]) && i+1<argc) { - rho = atof(argv[i+1]); - ++i; - } - } - } - { - const uint64_t elapsed_ms = pixel::getElapsedMillisecond(); - pixel::log_printf(elapsed_ms, "Usage %s -width <int> -height <int> -record <bmp-files-basename> -debug_gfx -fps <int>\n", argv[0]); - pixel::log_printf(elapsed_ms, "- win size %d x %d\n", win_width, win_height); - pixel::log_printf(elapsed_ms, "- record %s\n", record_bmpseq_basename.size()==0 ? "disabled" : record_bmpseq_basename.c_str()); - pixel::log_printf(elapsed_ms, "- debug_gfx %d\n", debug_gfx); - pixel::log_printf(elapsed_ms, "- enable_vsync %d\n", enable_vsync); - pixel::log_printf(elapsed_ms, "- forced_fps %d\n", forced_fps); - pixel::log_printf(elapsed_ms, "- rho %f\n", rho); - } +static const float ball_height = 0.05f; // [m] .. diameter +static const float ball_radius = ball_height/2.0f; // [m] +static const float small_gap = ball_radius; +static const float thickness = 1.0f * ball_height; - { - const float origin_norm[] = { 0.5f, 0.5f }; - pixel::init_gfx_subsystem("freefall01", win_width, win_height, origin_norm, enable_vsync); - } - - const float ball_height = 0.05f; // [m] .. diameter - const float ball_radius = ball_height/2.0f; // [m] - const float thickness = 1.0f * ball_height; - const float small_gap = ball_radius; +static float rho = rho_default; +static int forced_fps = -1; +static std::string record_bmpseq_basename; - pixel::cart_coord.set_height(0.0f, drop_height+6.0f*thickness); +void mainloop() { - std::shared_ptr<ball_t> ball_1 = std::make_shared<ball_t>( "one", rho, -4.0f*ball_height, drop_height-ball_radius, ball_radius, + static std::shared_ptr<ball_t> ball_1 = std::make_shared<ball_t>( "one", rho, -4.0f*ball_height, drop_height-ball_radius, ball_radius, 0.0f /* [m/s] */, pixel::adeg_to_rad(90)); - std::shared_ptr<ball_t> ball_2 = std::make_shared<ball_t>( "two", rho, +2.0f*ball_height, drop_height-ball_radius, ball_radius, + static std::shared_ptr<ball_t> ball_2 = std::make_shared<ball_t>( "two", rho, +2.0f*ball_height, drop_height-ball_radius, ball_radius, 0.0f /* [m/s] */, pixel::adeg_to_rad(90)); - std::shared_ptr<ball_t> ball_3 = std::make_shared<ball_t>( "can", rho, pixel::cart_coord.min_x()+2*ball_height, pixel::cart_coord.min_y()+small_gap+thickness+ball_height, ball_radius, + static std::shared_ptr<ball_t> ball_3 = std::make_shared<ball_t>( "can", rho, pixel::cart_coord.min_x()+2*ball_height, pixel::cart_coord.min_y()+small_gap+thickness+ball_height, ball_radius, 6.8f /* [m/s] */, pixel::adeg_to_rad(64)); // 6.1f /* [m/s] */, pixel::adeg_to_rad(78)); { @@ -343,28 +301,39 @@ int main(int argc, char *argv[]) } } - pixel::texture_ref hud_text; - uint64_t frame_count_total = 0; + static pixel::texture_ref hud_text; + static uint64_t frame_count_total = 0; - uint64_t t_last = pixel::getElapsedMillisecond(); // [ms] - pixel::input_event_t event; - while( !event.pressed_and_clr( pixel::input_event_type_t::WINDOW_CLOSE_REQ ) ) { - if( pixel::handle_events(event) ) { - // std::cout << "Event " << pixel::to_string(event) << std::endl; - } - if( event.pressed_and_clr( pixel::input_event_type_t::WINDOW_RESIZED ) ) { - pixel::cart_coord.set_height(0.0f, drop_height+2.0f*thickness); - } + static uint64_t t_last = pixel::getElapsedMillisecond(); // [ms] + static pixel::input_event_t event; + + if( event.pressed_and_clr( pixel::input_event_type_t::WINDOW_CLOSE_REQ ) ) { + printf("Exit Application\n"); + #if defined(__EMSCRIPTEN__) + emscripten_cancel_main_loop(); + #else + exit(0); + #endif + } + + pixel::handle_events(event); + if( event.pressed_and_clr( pixel::input_event_type_t::WINDOW_RESIZED ) ) { + pixel::cart_coord.set_height(0.0f, drop_height+6.0f*thickness); + } + const bool animating = !event.paused(); + + // while( !event.pressed_and_clr( pixel::input_event_type_t::WINDOW_CLOSE_REQ ) ) { - // white background - pixel::clear_pixel_fb(255, 255, 255, 255); + // white background + pixel::clear_pixel_fb(255, 255, 255, 255); - const uint64_t t1 = pixel::getElapsedMillisecond(); // [ms] - const float dt = (float)( t1 - t_last ) / 1000.0f; // [s] - t_last = t1; + const uint64_t t1 = pixel::getElapsedMillisecond(); // [ms] + const float dt = (float)( t1 - t_last ) / 1000.0f; // [s] + t_last = t1; - hud_text = pixel::make_text_texture("td "+pixel::to_decstring(t1, ',', 9)+", fps "+std::to_string(pixel::get_gpu_fps())); + hud_text = pixel::make_text_texture("td "+pixel::to_decstring(t1, ',', 9)+", fps "+std::to_string(pixel::get_gpu_fps())); + if( animating ) { // move ball_1 ball_1->tick(dt); @@ -373,42 +342,97 @@ int main(int argc, char *argv[]) // move ball_3 ball_3->tick(dt); + } - pixel::set_pixel_color(0 /* r */, 0 /* g */, 0 /* b */, 255 /* a */); - { - pixel::f2::geom_list_t& list = pixel::f2::gobjects(); - if( debug_gfx ) { - for(pixel::f2::geom_ref_t g : list) { - if( g.get() != ball_1.get() && - g.get() != ball_2.get() && - g.get() != ball_3.get() ) - { - g->draw(); - } - } - } else { - for(pixel::f2::geom_ref_t g : list) { + pixel::set_pixel_color(0 /* r */, 0 /* g */, 0 /* b */, 255 /* a */); + { + pixel::f2::geom_list_t& list = pixel::f2::gobjects(); + if( debug_gfx ) { + for(pixel::f2::geom_ref_t g : list) { + if( g.get() != ball_1.get() && + g.get() != ball_2.get() && + g.get() != ball_3.get() ) + { g->draw(); } } + } else { + for(pixel::f2::geom_ref_t g : list) { + g->draw(); + } } + } - fflush(nullptr); - pixel::swap_pixel_fb(false); - if( nullptr != hud_text ) { - const int thickness_pixel = pixel::cart_coord.to_fb_dy(thickness); - const int small_gap_pixel = pixel::cart_coord.to_fb_dy(small_gap); - const int text_height = thickness_pixel - 2; - const float sy = (float)text_height / (float)hud_text->height; - hud_text->draw(small_gap_pixel*2.0f, small_gap_pixel+1, sy, sy); - } - pixel::swap_gpu_buffer(forced_fps); - if( record_bmpseq_basename.size() > 0 ) { - std::string snap_fname(128, '\0'); - const int written = std::snprintf(&snap_fname[0], snap_fname.size(), "%s-%7.7" PRIu64 ".bmp", record_bmpseq_basename.c_str(), frame_count_total); - snap_fname.resize(written); - pixel::save_snapshot(snap_fname); + fflush(nullptr); + pixel::swap_pixel_fb(false); + if( nullptr != hud_text ) { + const int thickness_pixel = pixel::cart_coord.to_fb_dy(thickness); + const int small_gap_pixel = pixel::cart_coord.to_fb_dy(small_gap); + const int text_height = thickness_pixel - 2; + const float sy = (float)text_height / (float)hud_text->height; + hud_text->draw(small_gap_pixel*2.0f, small_gap_pixel+1, sy, sy); + } + pixel::swap_gpu_buffer(forced_fps); + if( record_bmpseq_basename.size() > 0 ) { + std::string snap_fname(128, '\0'); + const int written = std::snprintf(&snap_fname[0], snap_fname.size(), "%s-%7.7" PRIu64 ".bmp", record_bmpseq_basename.c_str(), frame_count_total); + snap_fname.resize(written); + pixel::save_snapshot(snap_fname); + } + +} + +int main(int argc, char *argv[]) +{ + int win_width = 1920, win_height = 1080; + bool enable_vsync = true; + #if defined(__EMSCRIPTEN__) + win_width = 1024, win_height = 576; // 16:9 + #endif + { + for(int i=1; i<argc; ++i) { + if( 0 == strcmp("-width", argv[i]) && i+1<argc) { + win_width = atoi(argv[i+1]); + ++i; + } else if( 0 == strcmp("-height", argv[i]) && i+1<argc) { + win_height = atoi(argv[i+1]); + ++i; + } else if( 0 == strcmp("-record", argv[i]) && i+1<argc) { + record_bmpseq_basename = argv[i+1]; + ++i; + } else if( 0 == strcmp("-debug_gfx", argv[i]) ) { + debug_gfx = true; + } else if( 0 == strcmp("-fps", argv[i]) && i+1<argc) { + forced_fps = atoi(argv[i+1]); + enable_vsync = false; + ++i; + } else if( 0 == strcmp("-rho", argv[i]) && i+1<argc) { + rho = atof(argv[i+1]); + ++i; + } } } - exit(0); + { + const uint64_t elapsed_ms = pixel::getElapsedMillisecond(); + pixel::log_printf(elapsed_ms, "Usage %s -width <int> -height <int> -record <bmp-files-basename> -debug_gfx -fps <int>\n", argv[0]); + pixel::log_printf(elapsed_ms, "- win size %d x %d\n", win_width, win_height); + pixel::log_printf(elapsed_ms, "- record %s\n", record_bmpseq_basename.size()==0 ? "disabled" : record_bmpseq_basename.c_str()); + pixel::log_printf(elapsed_ms, "- debug_gfx %d\n", debug_gfx); + pixel::log_printf(elapsed_ms, "- enable_vsync %d\n", enable_vsync); + pixel::log_printf(elapsed_ms, "- forced_fps %d\n", forced_fps); + pixel::log_printf(elapsed_ms, "- rho %f\n", rho); + } + + { + const float origin_norm[] = { 0.5f, 0.5f }; + pixel::init_gfx_subsystem("freefall01", win_width, win_height, origin_norm, enable_vsync, true /* subsys primitives */); + } + + pixel::cart_coord.set_height(0.0f, drop_height+6.0f*thickness); + + #if defined(__EMSCRIPTEN__) + emscripten_set_main_loop(mainloop, 0, 1); + #else + while( true ) { mainloop(); } + #endif } diff --git a/examples/freefall01.html b/examples/freefall01.html new file mode 100644 index 0000000..96a15ec --- /dev/null +++ b/examples/freefall01.html @@ -0,0 +1,208 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Jausoft - Freefall01</title> + <link href="/style.css" rel="stylesheet" type="text/css"/> + <link href="/style-alt1.css" rel="alternate stylesheet" title="default sans-serif font" type="text/css"/> + <link href="/images/favicon.ico" rel="shortcut icon"/> + <style> + html {padding:0; margin:0; color:#000000; background: #ffffff; } + a { color:#000000; } + + table { + border-collapse: collapse; + border-spacing: 1em; + } + th, td { + text-align: left; + padding: 0.25em; + } + /* tr:nth-child(even) { + background-color: #c0c0c0; + } */ + td:nth-child(even), th:nth-child(even) { + background-color: #c0c0c0; + } + + .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } + div.emscripten { text-align: center; } + div.emscripten_border { border: 1px solid black; } + /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ + canvas.emscripten { border: 0px none; background-color: white; } + + #emscripten_logo { + display: inline-block; + margin: 0; + } + + @-webkit-keyframes rotation { + from {-webkit-transform: rotate(0deg);} + to {-webkit-transform: rotate(360deg);} + } + @-moz-keyframes rotation { + from {-moz-transform: rotate(0deg);} + to {-moz-transform: rotate(360deg);} + } + @-o-keyframes rotation { + from {-o-transform: rotate(0deg);} + to {-o-transform: rotate(360deg);} + } + @keyframes rotation { + from {transform: rotate(0deg);} + to {transform: rotate(360deg);} + } + + #status { + display: inline-block; + vertical-align: top; + margin-top: 30px; + margin-left: 20px; + font-weight: bold; + color: rgb(120, 120, 120); + } + + #progress { + height: 20px; + width: 300px; + } + + #controls { + display: inline-block; + float: right; + vertical-align: top; + margin-top: 30px; + margin-right: 20px; + } + + #output { + width: 100%; + height: 200px; + margin: 0 auto; + margin-top: 10px; + border-left: 0px; + border-right: 0px; + padding-left: 0px; + padding-right: 0px; + display: block; + background-color: white; + color: black; + font-family: 'Lucida Console', Monaco, monospace; + outline: none; + } + </style> + </head> + <body> + <a href="https://jausoft.com"><img src="/images/jaugsweklogo-hp-220x80.png" alt="Jausoft"/></a> + <span style="padding:1em; font-size:2em;"><span class="acap">F</span>reefall01</span>... a <a href="https://jausoft.com/cgit/cs_class/gfxbox2.git/about/">gfxbox2 example</a>. + + <div class="spinner" id='spinner'></div> + <div class="emscripten" id="status">Downloading...</div> + +<span id='controls'> + <span> + <input type="button" value="Fullscreen" onclick="Module.requestFullscreen(true, true)"> <!-- pointerLock, resize --> + </span> +</span> + + <div class="emscripten"> + <progress value="0" max="100" id="progress" hidden=1></progress> + </div> + + <div class="emscripten_border"> + <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas> + </div> + <textarea id="output" rows="8"></textarea> + + <div> + <table> + <tr> + <th>Demo Previous</th> + <th>Demo Next</th> + <th>Value Up</th> + <th>Value Down</th> + <th>Toggle Manual / Auto</th> + </tr> + <tr> + <td>Cursor-Left</td> + <td>Cursor-Right</td> + <td>Cursor-Up</td> + <td>Cursor-Down</td> + <td>P</td> + </tr> + </table> + </div> + + <script type='text/javascript'> + var statusElement = document.getElementById('status'); + var progressElement = document.getElementById('progress'); + var spinnerElement = document.getElementById('spinner'); + + var Module = { + preRun: [], + postRun: [], + print: (function() { + var element = document.getElementById('output'); + if (element) element.value = ''; // clear browser cache + return function(text) { + if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); + console.log(text); + if (element) { + element.value += text + "\n"; + element.scrollTop = element.scrollHeight; // focus on bottom + } + }; + })(), + canvas: (function() { + var canvas = document.getElementById('canvas'); + // As a default initial behavior, pop up an alert when webgl context is lost. To make your + // application robust, you may want to override this behavior before shipping! + // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 + canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false); + + return canvas; + })(), + setStatus: function(text) { + if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' }; + if (text === Module.setStatus.last.text) return; + var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + var now = Date.now(); + if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon + Module.setStatus.last.time = now; + Module.setStatus.last.text = text; + if (m) { + text = m[1]; + progressElement.value = parseInt(m[2])*100; + progressElement.max = parseInt(m[4])*100; + progressElement.hidden = false; + spinnerElement.hidden = false; + } else { + progressElement.value = null; + progressElement.max = null; + progressElement.hidden = true; + if (!text) spinnerElement.style.display = 'none'; + } + statusElement.innerHTML = text; + }, + totalDependencies: 0, + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); + } + }; + Module.setStatus('Downloading...'); + window.onerror = function(event) { + // TODO: do not warn on ok events like simulating an infinite loop or exitStatus + Module.setStatus('Exception thrown, see JavaScript console'); + spinnerElement.style.display = 'none'; + Module.setStatus = function(text) { + if (text) Module.printErr('[post-exception status] ' + text); + }; + }; + </script> + <script async type="text/javascript" src="freefall01.js"></script> + </body> +</html> + + diff --git a/examples/piviz.cpp b/examples/piviz.cpp index 2bf34fd..0eed4a5 100644 --- a/examples/piviz.cpp +++ b/examples/piviz.cpp @@ -30,10 +30,6 @@ #include <cmath> #include <iostream> -#if defined(__EMSCRIPTEN__) - #include <emscripten.h> -#endif - using namespace std; using namespace pixel::f2; @@ -617,9 +613,9 @@ int main(int argc, char *argv[]) { unsigned int win_width = 1920, win_height = 1000; bool use_subsys_primitives = true; -#if defined(__EMSCRIPTEN__) - win_width = 1024, win_height = 576; // 16:9 -#endif + #if defined(__EMSCRIPTEN__) + win_width = 1024, win_height = 576; // 16:9 + #endif { for(int i=1; i<argc; ++i) { if( 0 == strcmp("-width", argv[i]) && i+1<argc) { @@ -632,12 +628,11 @@ int main(int argc, char *argv[]) } } if( use_subsys_primitives ) { - forced_fps = 0; + forced_fps = -1; } { const float origin_norm[] = { 0.5f, 0.5f }; - pixel::init_gfx_subsystem("piviz", win_width, win_height, origin_norm); - pixel::init_gfx_subsystem("spacewars", win_width, win_height, origin_norm, true /* enable_vsync */, use_subsys_primitives); + pixel::init_gfx_subsystem("piviz", win_width, win_height, origin_norm, true /* enable_vsync */, use_subsys_primitives); } pixel::log_printf(0, "XX %s\n", pixel::cart_coord.toString().c_str()); diff --git a/examples/spacewars.cpp b/examples/spacewars.cpp index c52b7e8..645d516 100644 --- a/examples/spacewars.cpp +++ b/examples/spacewars.cpp @@ -30,10 +30,6 @@ #include <algorithm> #include <random> -#if defined(__EMSCRIPTEN__) - #include <emscripten.h> -#endif - constexpr static const int player_id_1 = 1; constexpr static const int player_id_2 = 2; constexpr static const float spaceship_height = 10.0f; // [m] @@ -900,14 +896,12 @@ void mainloop() { hud_text->draw(dx, 0); } pixel::swap_gpu_buffer(forced_fps); - #if !defined(__EMSCRIPTEN__) - if( record_bmpseq_basename.size() > 0 ) { - std::string snap_fname(128, '\0'); - const int written = std::snprintf(&snap_fname[0], snap_fname.size(), "%s-%7.7" PRIu64 ".bmp", record_bmpseq_basename.c_str(), frame_count_total); - snap_fname.resize(written); - pixel::save_snapshot(snap_fname); - } - #endif + if( record_bmpseq_basename.size() > 0 ) { + std::string snap_fname(128, '\0'); + const int written = std::snprintf(&snap_fname[0], snap_fname.size(), "%s-%7.7" PRIu64 ".bmp", record_bmpseq_basename.c_str(), frame_count_total); + snap_fname.resize(written); + pixel::save_snapshot(snap_fname); + } ++frame_count_total; } @@ -963,7 +957,7 @@ int main(int argc, char *argv[]) } if( !fps_set ) { if( use_subsys_primitives ) { - forced_fps = 0; + forced_fps = -1; } else { forced_fps = 30; } diff --git a/include/pixel/pixel.hpp b/include/pixel/pixel.hpp index baba0b8..c49153a 100644 --- a/include/pixel/pixel.hpp +++ b/include/pixel/pixel.hpp @@ -38,6 +38,10 @@ #include <vector> #include <iostream> +#if defined(__EMSCRIPTEN__) + #include <emscripten.h> +#endif + namespace pixel::f2 { class vec_t; // fwd typedef vec_t point_t; diff --git a/src/sdl_subsys.cpp b/src/sdl_subsys.cpp index 5d02b01..5dac67c 100644 --- a/src/sdl_subsys.cpp +++ b/src/sdl_subsys.cpp @@ -387,7 +387,10 @@ bool pixel::handle_one_event(input_event_t& event) noexcept { } } -#if !defined(__EMSCRIPTEN__) +#if defined(__EMSCRIPTEN__) + void pixel::save_snapshot(const std::string&) noexcept { + } +#else static std::atomic<int> active_threads = 0; static void store_surface(SDL_Surface *sshot, char* fname) noexcept { |