diff --git a/MAKE.bat b/MAKE.bat index dc03717..40a1908 100644 --- a/MAKE.bat +++ b/MAKE.bat @@ -15,9 +15,9 @@ if [ "$1" = "tidy" ]; then rm 0?-* 2> /dev/null rm fwk.o 2> /dev/null rm .art*.zip 2> /dev/null - rm demos/lua/.art*.zip 2> /dev/null + rm engine/bind/.art*.zip 2> /dev/null rm demos/html5/.art*.zip 2> /dev/null - rm demos/lua/libfwk* 2> /dev/null + rm engine/bind/libfwk* 2> /dev/null rm fwk_*.* 2> /dev/null rm 3rd_*.* 2> /dev/null rm libfwk* 2> /dev/null @@ -63,7 +63,7 @@ while [ $# -ge 1 ]; do echo sh MAKE.bat [tidy] echo sh MAKE.bat [split,join] echo sh MAKE.bat [cook] - echo sh MAKE.bat [sln] + echo sh MAKE.bat [proj] exit fi if [ "$1" = "dll" ]; then @@ -93,7 +93,7 @@ while [ $# -ge 1 ]; do if [ "$1" = "tcc" ]; then export cc="tcc -D__STDC_NO_VLA__" fi - if [ "$1" = "sln" ]; then + if [ "$1" = "proj" ]; then if [ "$(uname)" != "Darwin" ]; then chmod +x tools/premake5.linux tools/premake5.linux gmake @@ -147,14 +147,14 @@ if [ "$(uname)" != "Darwin" ]; then chmod +x tools/xlsx2ini.linux chmod +x tools/premake5.linux chmod +x tools/ninja.linux - chmod +x demos/lua/luajit.linux + chmod +x engine/bind/luajit.linux echo build=$build, type=$dll, cc=$cc, args=$args # framework (as dynamic library) if [ "$dll" = "dll" ]; then echo libfwk.so && $cc -o libfwk.so engine/fwk.c -shared -fPIC -w -lX11 -lm -ldl -lpthread $flags $args - cp libfwk.so demos/lua/ + cp libfwk.so engine/bind/ export import="libfwk.so -Wl,-rpath,./" else # framework (static) @@ -167,15 +167,14 @@ if [ "$(uname)" != "Darwin" ]; then # demos echo hello && $cc -o hello hello.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $args & - # echo 00-ui && $cc -o 00-ui demos/00-ui.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & - # echo 01-sprite && $cc -o 01-sprite demos/01-sprite.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & - # echo 02-ddraw && $cc -o 02-ddraw demos/02-ddraw.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & - # echo 03-anims && $cc -o 03-anims demos/03-anims.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & - # echo 04-actor && $cc -o 04-actor demos/04-actor.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & - # echo 04-controller && $cc -o 04-controller demos/04-controller.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & - # echo 05-scene && $cc -o 05-scene demos/05-scene.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & - # echo 06-pbr && $cc -o 06-pbr demos/06-pbr.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & - # echo 07-network && $cc -o 07-network demos/07-network.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args + echo 00-ui && $cc -o 00-ui demos/00-ui.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & + echo 01-sprite && $cc -o 01-sprite demos/01-sprite.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & + echo 02-ddraw && $cc -o 02-ddraw demos/02-ddraw.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & + echo 03-anims && $cc -o 03-anims demos/03-anims.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & + echo 04-actor && $cc -o 04-actor demos/04-actor.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & + echo 05-scene && $cc -o 05-scene demos/05-scene.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & + echo 06-controller && $cc -o 06-controller demos/06-controller.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args & + echo 07-network && $cc -o 07-network demos/07-network.c -lm -ldl -lpthread -lX11 -w -Iengine/ $flags $import $args fi if [ "$(uname)" = "Darwin" ]; then @@ -202,14 +201,14 @@ if [ "$(uname)" = "Darwin" ]; then chmod +x tools/xlsx2ini.osx chmod +x tools/premake5.osx chmod +x tools/ninja.osx - chmod +x demos/lua/luajit.osx + chmod +x engine/bind/luajit.osx echo build=$build, type=$dll, cc=$cc, args=$args # framework (as dynamic library) if [ "$dll" = "dll" ]; then echo libfwk && cc -ObjC -dynamiclib -o libfwk.dylib engine/fwk.c -framework cocoa -framework iokit -framework audiotoolbox -w $flags $args - cp libfwk.dylib demos/lua + cp libfwk.dylib engine/bind export import=libfwk.dylib else # framework @@ -222,15 +221,14 @@ if [ "$(uname)" = "Darwin" ]; then # demos echo hello && cc -o hello -ObjC hello.c -w -Iengine/ $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 00-ui && cc -o 00-ui demos/00-ui.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 01-sprite && cc -o 01-sprite demos/01-sprite.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 02-ddraw && cc -o 02-ddraw demos/02-ddraw.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 03-anims && cc -o 03-anims demos/03-anims.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 04-actor && cc -o 04-actor demos/04-actor.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 04-controller && cc -o 04-controller demos/04-controller.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 05-scene && cc -o 05-scene demos/05-scene.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 06-pbr && cc -o 06-pbr demos/06-pbr.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & - # echo 07-network && cc -o 07-network demos/07-network.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox + echo 00-ui && cc -o 00-ui demos/00-ui.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & + echo 01-sprite && cc -o 01-sprite demos/01-sprite.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & + echo 02-ddraw && cc -o 02-ddraw demos/02-ddraw.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & + echo 03-anims && cc -o 03-anims demos/03-anims.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & + echo 04-actor && cc -o 04-actor demos/04-actor.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & + echo 05-scene && cc -o 05-scene demos/05-scene.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & + echo 06-controller && cc -o 06-controller demos/06-controller.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox & + echo 07-network && cc -o 07-network demos/07-network.c -w -Iengine/ $import $flags $args -framework cocoa -framework iokit -framework audiotoolbox fi exit @@ -249,17 +247,18 @@ if "%1"=="-h" goto showhelp if "%1"=="help" ( :showhelp echo %0 ; compile everything: `make dll dev` alias - echo %0 [help] ; show this screen - echo %0 [docs] ; generate tools/docs/docs.html file + echo %0 [all] ; build everything + echo %0 [bind] ; generate lua bindings echo %0 [cook] ; cook .zipfiles with tools/cook.ini cookbook + echo %0 [docs] ; generate tools/docs/docs.html file + echo %0 [help] ; show this screen + echo %0 [proj] ; generate a xcode/gmake/ninja/visual studio solution echo %0 [sync] ; sync repo to latest echo %0 [tidy] ; clean up temp files - echo %0 [bindings] ; generate demos/lua bindings echo %0 [checkmem] ; check untracked allocators in FWK echo %0 [split^|join] ; engine/fwk* ^>split^> engine/split/* or engine/split/* ^>join^> engine/fwk* echo %0 [amalgamation] ; combine engine/fwk* into a single-header file - echo %0 [sln] ; generate a xcode/gmake/ninja/visual studio solution - echo %0 [cl^|tcc^|cc^|gcc^|clang^|clang-cl] [dbg^|dev^|rel] [static^|dll] [nofwk^|nodemos^|noeditor] [vis] [-- args] + echo %0 [cl^|tcc^|cc^|gcc^|clang^|clang-cl] [dbg^|dev^|rel] [static^|dll] [nofwk^|nodemos^|editor] [vis] [-- args] echo cl \ echo tcc ^| echo cc ^| select compiler. must be accessible in PATH @@ -272,8 +271,8 @@ if "%1"=="help" ( echo static \ link fwk as static library echo dll / link fwk as dynamic library (dll^) (default^) echo nofwk \ do not compile framework - echo nodemos ^| do not compile demos - echo noeditor / do not compile editor + echo demos ^| do compile demos + echo editor / do compile editor echo vis ^> visualize invokation cmdline. echo args ^> after `--` separator is found, pass all remaining arguments to compiler as-is echo. @@ -298,10 +297,10 @@ if "%1"=="cook" ( exit /b ) rem generate bindings -if "%1"=="bindings" ( +if "%1"=="bind" ( rem luajit tools\luajit tools\luajit_make_bindings.lua > fwk.lua - move /y fwk.lua demos\lua + move /y fwk.lua engine\bind exit /b ) @@ -349,7 +348,7 @@ rem generate prior files to a github release if "%1"=="github" ( rem call make.bat dll call make.bat docs - call make.bat bindings + call make.bat bind call make.bat amalgamation call make.bat split @@ -391,7 +390,7 @@ rem tidy environment if "%1"=="tidy" ( move /y ??-*.png demos > nul 2> nul move /y ??-*.c demos > nul 2> nul - del demos\lua\fwk.dll > nul 2> nul + del engine\bind\fwk.dll > nul 2> nul del .temp*.* > nul 2> nul del *.zip > nul 2> nul del *.mem > nul 2> nul @@ -427,10 +426,11 @@ set build=dev set args=-Iengine set other= set fwk=yes -set demos=yes -set editor=yes +set hello=yes +set demos=no +set editor=no set vis=no -set sln=no +set proj=no set rc=0 :parse_args @@ -454,7 +454,10 @@ set rc=0 if "%1"=="nofwk" set "fwk=no" && goto loop if "%1"=="nodemos" set "demos=no" && goto loop + if "%1"=="demos" set "demos=yes" && set "hello=no" && goto loop if "%1"=="noeditor" set "editor=no" && goto loop + if "%1"=="editor" set "editor=yes" && set "hello=no"&& goto loop + if "%1"=="all" set "fwk=yes" && set "demos=yes" && set "editor=yes" && set "hello=yes" && goto loop if "%1"=="tcc" set "cc=%1" && goto loop if "%1"=="cl" set "cc=%1" && goto loop @@ -464,7 +467,7 @@ set rc=0 if "%1"=="clang" set "cc=%1" && goto loop if "%1"=="clang-cl" set "cc=%1" && goto loop - if "%1"=="sln" set "sln=yes" && goto loop + if "%1"=="proj" set "proj=yes" && goto loop if not "%1"=="" set "other=!other! %1" && set "editor=no" && set "demos=no" @@ -503,9 +506,10 @@ if "!cc!"=="" ( ) rem solution. @todo: lin/osx -if "!sln!"=="yes" if not "%vs%"=="" pushd tools && premake5 vs20%vs% & popd -if "!sln!"=="yes" pushd tools && premake5 ninja & popd -if "!sln!"=="yes" pushd tools && premake5 gmake & popd & exit /b +if "!proj!"=="yes" if not "%vs%"=="00" pushd tools && premake5 vs20%vs% & popd +if "!proj!"=="yes" if "%vs%"=="00" pushd tools && premake5 vs2013 & popd +if "!proj!"=="yes" pushd tools && premake5 ninja & popd +if "!proj!"=="yes" pushd tools && premake5 gmake & popd & exit /b rem --- pipeline rem cl tools/ass2iqe.c /Fetools/ass2iqe.exe /nologo /openmp /O2 /Oy /MT /DNDEBUG assimp.lib @@ -645,30 +649,33 @@ if not "!other!"=="" ( rem framework if "!fwk!"=="yes" ( -if "!vis!"=="yes" echo !cc! engine\fwk.c !export! !args! ^&^& if "!dll!"=="dll" copy /y fwk.dll demos\lua ^> nul -!echo! fwk && !cc! engine\fwk.c !export! !args! && if "!dll!"=="dll" copy /y fwk.dll demos\lua > nul || set rc=1 +if "!vis!"=="yes" echo !cc! engine\fwk.c !export! !args! ^&^& if "!dll!"=="dll" copy /y fwk.dll engine\bind ^> nul +!echo! fwk && !cc! engine\fwk.c !export! !args! && if "!dll!"=="dll" copy /y fwk.dll engine\bind > nul || set rc=1 ) rem editor if "!editor!"=="yes" ( set edit=-DCOOK_ON_DEMAND -DUI_LESSER_SPACING -DUI_ICONS_SMALL if "!vis!"=="yes" echo !cc! !o! editor.exe tools\editor\editor.c !edit! !import! !args! -rem !echo! editor && !cc! !o! editor.exe tools\editor\editor.c !edit! !import! !args! || set rc=1 -rem !echo! editor2 && !cc! !o! editor2.exe tools\editor\editor2.c !edit! !args! || set rc=1 +!echo! editor && !cc! !o! editor.exe tools\editor\editor.c !edit! !import! !args! || set rc=1 +!echo! editor2 && !cc! !o! editor2.exe tools\editor\editor2.c !edit! !args! || set rc=1 ) rem demos if "!demos!"=="yes" ( +!echo! 00-ui && !cc! !o! 00-ui.exe demos\00-ui.c !import! !args! || set rc=1 +!echo! 01-sprite && !cc! !o! 01-sprite.exe demos\01-sprite.c !import! !args! || set rc=1 +!echo! 02-ddraw && !cc! !o! 02-ddraw.exe demos\02-ddraw.c !import! !args! || set rc=1 +!echo! 03-anims && !cc! !o! 03-anims.exe demos\03-anims.c !import! !args! || set rc=1 +!echo! 04-actor && !cc! !o! 04-actor.exe demos\04-actor.c !import! !args! || set rc=1 +!echo! 05-scene && !cc! !o! 05-scene.exe demos\05-scene.c !import! !args! || set rc=1 +!echo! 06-controller && !cc! !o! 06-controller.exe demos\06-controller.c !import! !args! || set rc=1 +!echo! 07-network && !cc! !o! 07-network.exe demos\07-network.c !import! !args! || set rc=1 +) + +rem hello +if "!hello!"=="yes" ( !echo! hello && !cc! !o! hello.exe hello.c !args! || set rc=1 -rem !echo! 00-ui && !cc! !o! 00-ui.exe demos\00-ui.c !import! !args! || set rc=1 -rem !echo! 01-sprite && !cc! !o! 01-sprite.exe demos\01-sprite.c !import! !args! || set rc=1 -rem !echo! 02-ddraw && !cc! !o! 02-ddraw.exe demos\02-ddraw.c !import! !args! || set rc=1 -rem !echo! 03-anims && !cc! !o! 03-anims.exe demos\03-anims.c !import! !args! || set rc=1 -rem !echo! 04-actor && !cc! !o! 04-actor.exe demos\04-actor.c !import! !args! || set rc=1 -rem !echo! 04-controller && !cc! !o! 04-controller.exe demos\04-controller.c !import! !args! || set rc=1 -rem !echo! 05-scene && !cc! !o! 05-scene.exe demos\05-scene.c !import! !args! || set rc=1 -rem !echo! 06-pbr && !cc! !o! 06-pbr.exe demos\06-pbr.c !import! !args! || set rc=1 -rem !echo! 07-network && !cc! !o! 07-network.exe demos\07-network.c !import! !args! || set rc=1 ) rem user-defined apps diff --git a/README.md b/README.md index 6b75a62..b409a35 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -

F·W·K

+

F·W·K

-3D game framework in C.
+3D game engine/framework in C, with Luajit and Python bindings now.

@@ -8,16 +8,12 @@

## Goals -- [x] ~~C++~~. C. -- [x] ~~Fast~~. Naive. -- [x] ~~Modern~~. Simple. -- [x] ~~Full featured~~. Small. -- [x] ~~Rich build system~~. Single file. -- [x] ~~Royaltie fee~~. Free and unlicensed. +- [x] ~~Full featured~~, ~~Fast~~, ~~Modern C++~~. Small, Naive, Simple C. +- [x] ~~Rich build system~~, ~~Royaltie fee~~. Single file, Freely unlicensed. ## Features ᕦ(ᐛ)ᕤ - [x] Pipeline: configurable and integrated [asset pipeline](tools/cook.ini). -- [x] Embedded: single-file header, all dependencies included. +- [x] Embedded: [single-file header](engine/joint/fwk.h), all dependencies included. - [x] Compiler: MSVC, MINGW64, TCC, GCC, clang, clang-cl and emscripten. - [x] Linkage: Both static linkage and dynamic .dll/.so/.dylib support. - [x] Platform: Windows, Linux and OSX. Partial HTML5/Web support. @@ -52,25 +48,36 @@ - [x] Scene handling. - [x] Profiler, stats and leaks finder. - [x] [Editor (wip)](https://user-images.githubusercontent.com/35402248/174457347-f787a6a2-aac8-404c-a5da-f44310c3d432.mp4). -- [x] [Documentation (wip)](https://bit.ly/-f-w-k-). +- [x] [Documentation (wip)](https://bit.ly/fwk2023). ## Roadmap ᕕ(ᐛ)ᕗ (in order of arrival; ✱: partial support) - [ ] AI pass: actors, waypoints, pathfinding, behavior trees (h/fsm,goap), and navmesh generation. -- [ ] Render pass: reverse-Z, automatic LODs, impostors, decals. - - [ ] Materials: (colors✱, textures✱, matcaps✱, videos✱, shadertoys✱). Shadertoys as post-fx✱. - - [ ] Lighting: Hard/soft shadow mapping (VSM,CCSM). Baked lightmaps. Refl probes. Integrated PBR. - [ ] Network/VM pass: Entity/component/systems and worlds. + - [ ] Core pass: struct serialization. - [ ] Message pipeline and replication. - [ ] Digital signals, message buffering and event polling. - [ ] World streaming and level loading. - [ ] Scenegraphs and spatial partioning. BVH, PVS, occluders, frustum culling. - [ ] Server/client architecture. Hybrid P2P. - [ ] NAT traversal. Socketless API, message API and pub/sub wrappers (enet/websocket). +- [ ] Editor pass = netbased + offline rendering + virtual input. + - [ ] Basic: Gizmos✱, scene tree, property editor✱, load/save✱, undo/redo✱, copy/paste, on/off (vis,tick,ddraw,log), vcs. + - [ ] Scenenode: node singleton display, node console, node labels, node outlines✱. + - [ ] Debug: toggles on/off (billboards✱, materials, un/lit, cast shadows, wireframe, skybox✱/mie✱, fog/atmosphere, collide✱, physics). + - [ ] Level: volumes, triggers, platforms, level streaming. + - [ ] Sub-editor: timeline and data tracks, node graphs. + - [ ] Sub-editor: Procedural content, brushes, noise and CSG. + - [ ] Sub-editor: blendshapes, additive anims, head/foot/hand IKs. + - [ ] Script pass: DLL✱ (module->plugin/sys), Lua✱, Luajit✱, Teal✱ and TypeScript. +- [ ] Render pass: reverse-Z, automatic LODs, impostors, decals. + - [ ] Materials: (colors✱, textures✱, matcaps✱, videos✱, shadertoys✱). Shadertoys as post-fx✱. + - [ ] Lighting: Hard/soft shadow mapping (VSM,CCSM). Baked lightmaps. Refl probes. Integrated PBR. - [ ] Tools pass - [ ] Extend shaders + bindings. Per-platform✱, per-type✱, per-asset options. GIF, PKM. - [ ] Extend atlas (sprite/lightmaps). Fit packing (sprites). - [ ] Extend bindings and messaging: parse C headers during cooking stage. - [ ] API pass + - [ ] Extend math: quat2, bezier, catmull. - [ ] Discuss API and freeze it. - [ ] Document everything. @@ -183,17 +190,6 @@ int main() { } ``` -```C -#include "fwk.h" // Minimal HTML5 sample -void render(void *arg) { - if( !input(KEY_ESC) ) puts("hello FWK from HTML5!"); -} -int main() { - window_create(75.0, 0); // 75% size, no extra flags - window_loop(render, NULL); // game loop -} -``` - ```lua local fwk = require("fwk") -- Minimal Lua sample fwk.window_create(75.0,0) -- 75% size, no extra flags @@ -203,6 +199,12 @@ end ``` ## Quickstart +- Double-click `MAKE.bat` (Win) or `sh MAKE.bat` (Linux/OSX) to quick start. +- `MAKE.bat all` (Win) or `sh MAKE.bat all` (Linux/OSX) to build everything. +- `MAKE.bat proj` (Win) or `sh MAKE.bat proj` (Linux/OSX) to generate solutions/makefiles. +- `MAKE.bat help` (Win) or `sh MAKE.bat help` (Linux/OSX) for a bunch of options. +- `MAKE.bat hello.c` (Win) or `sh MAKE.bat hello.c` (Linux/OSX) to build a single executable. +- Alternatively, ```bat echo win/vc && cl hello.c echo win/clang-cl && clang-cl hello.c @@ -215,94 +217,108 @@ echo osx && cc -ObjC hello.c -framework cocoa -framework iokit -fr ``` ## Cook -- Most asset types need to be cooked before being used in your application. Some other assets like `.png` do not. -- Cooked assets will be written into .zipfiles close to your executable, and mounted before entering game loop. -- Cooked .zipfiles and your executable are the only required assets when releasing your game. -- Cook manually your assets by invoking supplied [`tools/cook` standalone binary](tools/). -- Cook automatically your assets by just playing your game: a runtime cook is already embedded into your binary. - - In order to achieve this, ensure the [`tools/` folder](tools/) is close to your executable. - - This folder contains all the related binaries to perform any asset conversion plus the [cookbook](tools/cook.ini) to do so. +- Assets need to be cooked before being consumed in your application. The [tools/](tools/) folder contains all the related binaries to perform any asset processing plus the [cookbook](tools/cook.ini) to do so. +- Your game will cook all your assets as long as the [`tools/`](tools/) folder is next to your executable. Alternatively, cook them all just by invoking supplied [`tools/cook` standalone binary](tools/). +- In both cases, assets will be cooked and packed into .zipfiles next to your executable, then mounted before entering game loop. These .zipfiles plus your executable are the only required files when releasing your game. ## Extra tips - Any ico/png file named after the executable name will be automatically used as app icon. - Similar to the ico/png case above, the cooked .zipfiles can be named after the main executable as well. -- Dropped files into game window will be imported & saved into [`import/`](art/engine/import) folder. -- Update the gamepad controller database by upgrading the [`gamecontrollerdb.txt`](art/engine/input) file. -- Depending on your IDE, you might need to browse to [`split/`](split/) sources when debugging FWK. +- Dropped files into game window will be imported & saved into [`import/`](engine/art/import/) folder. +- Update the gamepad controller database by upgrading the [`gamecontrollerdb.txt`](engine/art/input/) file. +- Depending on your IDE, you might need to browse to [`engine/split/`](engine/split/) sources when debugging FWK. - Cook assets on demand, as opposed to cook all existing assets on depot, by using `--cook-on-demand` flag. - Linux/OSX users can optionally install wine and use the Windows tools instead (by using `--cook-wine` flag). - Disable automatic cooking by using `--cook-jobs=0` flag (not recommended). -- Generate a project solution by dropping `split/fwk.h, fwk.c and fwk` files into it. +- Generate a project solution by dropping `engine/fwk.h, fwk.c and fwk` files into it. +- Auto-generated Luajit and Python bindings can be found in the [`engine/bind/`](engine/bind/) folder. + -## Credits (Artwork + demos) -- [Nanofactory](https://sketchfab.com/3d-models/kgirls01-d2f946f58a8040ae993cda70c97b302c), for kgirls01 3D model (CC BY-NC-ND 4.0). -- [RottingPixels](https://opengameart.org/content/2d-castle-platformer-tileset-16x16), for castle-tileset (CC0). -- [wwwtyro](https://github.com/wwwtyro/glsl-atmosphere), for nicest rayleigh/mie scattering shader around (CC0). +## Credits +**Artwork** +[Dean Evans, Raijin](https://youtu.be/RRvYkrrpMKo?t=147 "for the Map song (c)"), +[FMS_Cat](https://gist.github.com/FMS-Cat/a1ccea3ce866c34706084e3526204f4f "for nicest VHS/VCR shader around (MIT)"), +[Goblin165cm](https://sketchfab.com/3d-models/halloween-little-witch-ccc023590bfb4789af9322864e42d1ab "for witch 3D model (CC BY 4.0)"), +[Nuulbee](https://sketchfab.com/3d-models/kgirls01-d2f946f58a8040ae993cda70c97b302c "for kgirls01 3D model (CC BY-NC-ND 4.0)"), +[Quaternius](https://www.patreon.com/quaternius "for the lovely 3D robots (CC0)"), +[Rotting Pixels](https://opengameart.org/content/2d-castle-platformer-tileset-16x16 "for castle-tileset (CC0)"), +[Tom Lewandowski](https://QuestStudios.com "for his MIDI recordings (c)"), +[Rye Terrell](https://github.com/wwwtyro/glsl-atmosphere "for nicest rayleigh/mie scattering shader around (CC0)"), +**Tools** +[Aaron Barany](https://github.com/akb825/Cuttlefish "for cuttlefish (APACHE2)"), +[Arseny Kapoulkine](https://github.com/zeux/pugixml/ "for pugixml (MIT)"), +[Assimp authors](https://github.com/assimp/assimp "for assimp (BSD3)"), +[Bernhard Schelling](https://github.com/schellingb/TinySoundFont "for tml.h (Zlib) and tsf.h (MIT)"), +[Christian Collins](http://www.schristiancollins.com "for GeneralUser GS soundfont (PD)"), +[FFMPEG authors](https://www.ffmpeg.org/ "for ffmpeg (LGPL21)"), +[Imagination](https://developer.imaginationtech.com/pvrtextool/ "for pvrtextoolcli (ITL)"), +[Krzysztof Gabis](https://github.com/kgabis/ape "for split.py/join.py (MIT)"), +[Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo "for iqm.cpp (PD)"), +[Martín Lucas Golini](https://github.com/SpartanJ/eepp/commit/8552941da19380d7a629c4da80a976aec5d39e5c "for emscripten-fs.html (CC0)"), +[Mattias Gustavsson](https://github.com/mattiasgustavsson/libs "for mid.h (PD)"), +[Michael Schmoock](http://github.com/willsteel/lcpp "for lcpp (MIT)"), +[Morgan McGuire](https://casual-effects.com/markdeep/ "for markdeep (BSD2)"), +[Olivier Lapicque, Konstanty Bialkowski](https://github.com/Konstanty/libmodplug "for libmodplug (PD)"), +[Polyglot Team](https://docs.google.com/spreadsheets/d/17f0dQawb-s_Fd7DHgmVvJoEGDMH_yoSd8EYigrb0zmM/edit "for polyglot gamedev (CC0)"), +[Tildearrow](https://github.com/tildearrow/furnace/ "for Furnace (GPL2)"), +[Tomas Pettersson](http://www.drpetter.se/ "for sfxr (PD)"), +[Tor Andersson](https://github.com/ccxvii/asstools "for assiqe.c (BSD)"), +**Runtime** +[Andreas Mantler](https://github.com/ands "for their math library (PD)"), +[Barerose](https://github.com/barerose "for swrap (CC0) and math library (CC0)"), +[Camilla Löwy](https://github.com/elmindreda "for glfw3 (Zlib)"), +[Dave Rand](https://tools.ietf.org/html/rfc1978 "for ppp (PD)"), +[David Herberth](https://github.com/dav1dde/ "for glad generated code (PD)"), +[David Reid](https://github.com/mackron "for miniaudio (PD)"), +[Dominic Szablewski](https://github.com/phoboslab/pl_mpeg "for pl_mpeg (MIT)"), +[Dominik Madarász](https://github.com/zaklaus "for json5 parser (PD)"), +[Eduard Suica](https://github.com/eduardsui/tlse "for tlse (PD)"), +[Evan Wallace](https://github.com/evanw "for their math library (CC0)"), +[Gargaj+cce/Peisik](https://github.com/gargaj/foxotron "for Foxotron/PBR shaders (UNLICENSE)"), +[Guilherme Lampert](https://github.com/glampert "for their math library (PD)"), +[Guillaume Vareille](http://tinyfiledialogs.sourceforge.net "for tinyfiledialogs (ZLIB)"), +[Haruhiko Okumura](https://oku.edu.mie-u.ac.jp/~okumura/compression/ "for lzss (PD)"), +[Igor Pavlov](https://www.7-zip.org/ "for LZMA (PD)"), +[Ilya Muravyov](https://github.com/encode84 "for bcm, balz, crush, ulz, lz4x (PD)"), +[Jon Olick](https://www.jonolick.com/ "for jo_mp1 and jo_mpeg (PD)"), +[Joonas Pihlajamaa](https://github.com/jokkebk/JUnzip "for JUnzip library (PD)"), +[Juliette Focault](https://github.com/juliettef/IconFontCppHeaders/blob/main/IconsMaterialDesign.h "for the generated MD header (ZLIB)"), +[Kristoffer Grönlund](https://github.com/krig "for their math library (CC0)"), +[Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo "for IQM spec & player (PD)"), +[Lee Salzman, V.Hrytsenko, D.Madarász](https://github.com/zpl-c/enet/ "for enet (MIT)"), +[Libtomcrypt](https://github.com/libtom/libtomcrypt "for libtomcrypt (Unlicense)"), +[Lua authors](https://www.lua.org/ "for Lua language (MIT)"), +[Mattias Gustavsson](https://github.com/mattiasgustavsson/libs "for thread.h and https.h (PD)"), +[Micha Mettke](https://github.com/vurtun "for their math library (PD)"), +[Micha Mettke, Chris Willcocks, Dmitry Hrabrov](https://github.com/vurtun/nuklear "for nuklear (PD)"), +[Michael Galetzka](https://github.com/Cultrarius/Swarmz "for swarmz (UNLICENSE)"), +[Morten Vassvik](https://github.com/vassvik/mv_easy_font "for mv_easy_font (Unlicense)"), +[Mārtiņš Možeiko](https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275 "for A* pathfinding (PD)"), +[Omar Cornut, vaiorabbit](https://github.com/ocornut/imgui/pull/3627 "for tables of unicode ranges (MIT-0)"), +[Rabia Alhaffar](https://github.com/Rabios/ice_libs "for ice_batt.h (PD)"), +[Rich Geldreich](https://github.com/richgel999/miniz "for miniz (PD)"), +[Ross Williams](http://ross.net/compression/lzrw3a.html "for lzrw3a (PD)"), +[Samuli Raivio](https://github.com/bqqbarbhg/bq_websocket "for bq_websocket (PD)"), +[Sean Barrett](https://github.com/nothings "for stb_image, stb_image_write, stb_sprintf, stb_truetype and stb_vorbis (PD)"), +[Sebastian Steinhauer](https://github.com/kieselsteini "for sts_mixer (PD)"), +[Stan Melax, Cloud Wu](https://web.archive.org/web/20031204035320/http://www.melax.com/polychop/gdmag.pdf "for polychop C algorithm (PD)"), +[Stefan Gustavson](https://github.com/stegu/perlin-noise "for simplex noise (PD)"), +[Sterling Orsten](https://github.com/sgorsten "for their math library (UNLICENSE)"), +[Tor Andersson](https://github.com/ccxvii/minilibs "for xml.c (PD)"), +[Wolfgang Draxinger](https://github.com/datenwolf "for their math library (WTFPL2)"), -## Credits (Tools) -- [Aaron Barany](https://github.com/akb825/Cuttlefish), for cuttlefish (APACHE2). -- [Arseny Kapoulkine](https://github.com/zeux/pugixml/), for pugixml (MIT). -- [Assimp authors](https://github.com/assimp/assimp), for assimp (BSD3). -- [Bernhard Schelling](https://github.com/schellingb/TinySoundFont), for tml.h (Zlib) and tsf.h (MIT). -- [ffmpeg authors](https://www.ffmpeg.org/), for ffmpeg (LGPL21). -- [Imagination](https://developer.imaginationtech.com/pvrtextool/), for pvrtextoolcli (ITL). -- [Krzysztof Gabis](https://github.com/kgabis/ape), for split.py/join.py (MIT). -- [Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo), for iqm.cpp (PD). -- [Mattias Gustavsson](https://github.com/mattiasgustavsson/libs), for mid.h (PD). -- [Michael Schmoock](http://github.com/willsteel/lcpp), for lcpp (MIT). -- [Olivier Lapicque, Konstanty Bialkowski](https://github.com/Konstanty/libmodplug), for libmodplug (PD). -- [Polyglot Team](https://docs.google.com/spreadsheets/d/17f0dQawb-s_Fd7DHgmVvJoEGDMH_yoSd8EYigrb0zmM/edit), for polyglot gamedev (CC0). -- [Tildearrow](https://github.com/tildearrow/furnace/), for Furnace (GPL2). -- [Tomas Pettersson](http://www.drpetter.se/), for sfxr (PD). -- [Tor Andersson](https://github.com/ccxvii/asstools), for assiqe.c (BSD). - -## Credits (Runtime) -- [Barerose](https://github.com/barerose), for swrap (CC0). -- [Camilla Löwy](https://github.com/elmindreda), for glfw3 (Zlib). -- [Dave Rand](https://tools.ietf.org/html/rfc1978) for ppp (PD). -- [David Herberth](https://github.com/dav1dde/), for glad generated code (PD). -- [David Reid](https://github.com/mackron), for miniaudio (PD). -- [Dominic Szablewski](https://github.com/phoboslab/pl_mpeg), for pl_mpeg (MIT). -- [Dominik Madarász](https://github.com/zaklaus), for json5 parser (PD). -- [Eduard Suica](https://github.com/eduardsui/tlse), for tlse (PD). -- [Gargaj+cce/Peisik](https://github.com/gargaj/foxotron), for Foxotron/PBR shaders (UNLICENSE). -- [Guillaume Vareille](http://tinyfiledialogs.sourceforge.net), for tinyfiledialogs (ZLIB). -- [Haruhiko Okumura](https://oku.edu.mie-u.ac.jp/~okumura/compression/) for lzss (PD). -- [Igor Pavlov](https://www.7-zip.org/) for LZMA (PD). -- [Ilya Muravyov](https://github.com/encode84) for bcm, balz, crush, ulz, lz4x (PD). -- [Jon Olick](https://www.jonolick.com/), for jo_mp1 and jo_mpeg (PD). -- [Joonas Pihlajamaa](https://github.com/jokkebk/JUnzip), for JUnzip library (PD). -- [Juliette Focault](https://github.com/juliettef/IconFontCppHeaders/blob/main/IconsMaterialDesign.h), for the generated MD header (ZLIB). -- [Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo), for IQM spec & player (PD). -- [Lee Salzman, V.Hrytsenko, D.Madarász](https://github.com/zpl-c/enet/), for enet (MIT). -- [Libtomcrypt](https://github.com/libtom/libtomcrypt), for libtomcrypt (Unlicense). -- [Lua authors](https://www.lua.org/), for Lua language (MIT). -- [Mārtiņš Možeiko](https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275), for A* pathfinding (PD). -- [Mattias Gustavsson](https://github.com/mattiasgustavsson/libs), for thread.h and https.h (PD). -- [Micha Mettke, Chris Willcocks, Dmitry Hrabrov](https://github.com/vurtun/nuklear), for nuklear (PD). -- [Michael Galetzka](https://github.com/Cultrarius/Swarmz), for swarmz (UNLICENSE). -- [Omar Cornut, vaiorabbit](https://github.com/ocornut/imgui/pull/3627), for tables of unicode ranges (MIT-0). -- [Rabia Alhaffar](https://github.com/Rabios/ice_libs), for ice_batt.h (PD). -- [Rich Geldreich](https://github.com/richgel999/miniz), for miniz (PD). -- [Ross Williams](http://ross.net/compression/lzrw3a.html) for lzrw3a (PD). -- [Samuli Raivio](https://github.com/bqqbarbhg/bq_websocket), for bq_websocket (PD). -- [Sean Barrett](https://github.com/nothings), for stb_image, stb_image_write, stb_sprintf, stb_truetype and stb_vorbis (PD). -- [Sebastian Steinhauer](https://github.com/kieselsteini), for sts_mixer (PD). -- [Stan Melax, Cloud Wu](https://web.archive.org/web/20031204035320/http://www.melax.com/polychop/gdmag.pdf), for polychop C algorithm (PD). -- [Stefan Gustavson](https://github.com/stegu/perlin-noise), for simplex noise (PD). -- [Tor Andersson](https://github.com/ccxvii/minilibs), for xml.c (PD). -- [Vassvik](https://github.com/vassvik/mv_easy_font), for mv_easy_font (Unlicense). -- Special thanks to [@ands](https://github.com/ands), [@barerose](https://github.com/barerose), [@datenwolf](https://github.com/datenwolf), [@evanw](https://github.com/evanw), [@glampert](https://github.com/glampert), [@krig](https://github.com/krig), [@sgorsten](https://github.com/sgorsten) and [@vurtun](https://github.com/vurtun) for their math libraries (PD,CC0,WTFPL2,CC0,PD,CC0,Unlicense,PD). + ## Unlicense This software is released into the [public domain](https://unlicense.org/). Also dual-licensed as [0-BSD](https://opensource.org/licenses/0BSD) or [MIT (No Attribution)](https://github.com/aws/mit-0) for those countries where public domain is a concern (sigh). Any contribution to this repository is implicitly subjected to the same release conditions aforementioned. ## Links -

-Issues -Discord
+Still looking for alternatives? [amulet](https://github.com/ianmaclarty/amulet), [aroma](https://github.com/leafo/aroma/), [astera](https://github.com/tek256/astera), [blendelf](https://github.com/jesterKing/BlendELF), [bullordengine](https://github.com/MarilynDafa/Bulllord-Engine), [candle](https://github.com/EvilPudding/candle), [cave](https://github.com/kieselsteini/cave), [chickpea](https://github.com/ivansafrin/chickpea), [corange](https://github.com/orangeduck/Corange), [cute](https://github.com/RandyGaul/cute_framework), [dos-like](https://github.com/mattiasgustavsson/dos-like), [ejoy2d](https://github.com/ejoy/ejoy2d), [exengine](https://github.com/exezin/exengine), [gunslinger](https://github.com/MrFrenik/gunslinger), [hate](https://github.com/excessive/hate), [island](https://github.com/island-org/island), [juno](https://github.com/rxi/juno), [l](https://github.com/Lyatus/L), [lgf](https://github.com/Planimeter/lgf), [limbus](https://github.com/redien/limbus), [love](https://github.com/love2d/love/), [lovr](https://github.com/bjornbytes/lovr), [mini3d](https://github.com/mini3d/mini3d), [mintaro](https://github.com/mackron/mintaro), [mio](https://github.com/ccxvii/mio), [olive.c](https://github.com/tsoding/olive.c), [opensource](https://github.com/w23/OpenSource), [ouzel](https://github.com/elnormous/ouzel/), [pez](https://github.com/prideout/pez), [pixie](https://github.com/mattiasgustavsson/pixie), [punity](https://github.com/martincohen/Punity), [r96](https://github.com/badlogic/r96), [ricotech](https://github.com/dbechrd/RicoTech), [rizz](https://github.com/septag/rizz), [tigr](https://github.com/erkkah/tigr), [yourgamelib](https://github.com/duddel/yourgamelib) -Still looking for alternatives? -[amulet](https://github.com/ianmaclarty/amulet), [aroma](https://github.com/leafo/aroma/), [astera](https://github.com/tek256/astera), [blendelf](https://github.com/jesterKing/BlendELF), [bullordengine](https://github.com/MarilynDafa/Bulllord-Engine), [candle](https://github.com/EvilPudding/candle), [cave](https://github.com/kieselsteini/cave), [chickpea](https://github.com/ivansafrin/chickpea), [corange](https://github.com/orangeduck/Corange), [cute](https://github.com/RandyGaul/cute_framework), [dos-like](https://github.com/mattiasgustavsson/dos-like), [ejoy2d](https://github.com/ejoy/ejoy2d), [exengine](https://github.com/exezin/exengine), [gunslinger](https://github.com/MrFrenik/gunslinger), [hate](https://github.com/excessive/hate), [island](https://github.com/island-org/island), [juno](https://github.com/rxi/juno), [l](https://github.com/Lyatus/L), [lgf](https://github.com/Planimeter/lgf), [limbus](https://github.com/redien/limbus), [love](https://github.com/love2d/love/), [lovr](https://github.com/bjornbytes/lovr), [mini3d](https://github.com/mini3d/mini3d), [mintaro](https://github.com/mackron/mintaro), [mio](https://github.com/ccxvii/mio), [olive.c](https://github.com/tsoding/olive.c), [opensource](https://github.com/w23/OpenSource), [ouzel](https://github.com/elnormous/ouzel/), [pez](https://github.com/prideout/pez), [pixie](https://github.com/mattiasgustavsson/pixie), [punity](https://github.com/martincohen/Punity), [r96](https://github.com/badlogic/r96), [ricotech](https://github.com/dbechrd/RicoTech), [rizz](https://github.com/septag/rizz), [tigr](https://github.com/erkkah/tigr), [yourgamelib](https://github.com/duddel/yourgamelib) -

+Issues Discord diff --git a/demos/00-demo.c b/demos/00-demo.c new file mode 100644 index 0000000..6099f4b --- /dev/null +++ b/demos/00-demo.c @@ -0,0 +1,278 @@ +// framework demo +// - rlyeh, public domain + +#include "fwk.h" + +int main() { + // options + bool do_about = 0; + float do_scale = 0.10f; + bool do_debugdraw = 0; + float do_gamepad_deadzone = 0.15f; + vec2 do_gamepad_polarity = vec2(+1,+1); + vec2 do_gamepad_sensitivity = vec2(0.1f,0.1f); + vec2 do_mouse_polarity = vec2(+1,-1); + vec2 do_mouse_sensitivity = vec2(0.2f,0.2f); + bool do_billboard_x = 0, do_billboard_y = 0, do_billboard_z = 0; + + // window (80% sized, MSAA x4 flag) + window_create(80, WINDOW_MSAA4); + window_title(__FILE__); + + // load all fx files + for(const char **list = file_list("./", "fx**.fs"); *list; list++) { + fx_load(*list); + } + + // load skybox + skybox_t sky = skybox(flag("--mie") ? 0 : "cubemaps/stardust", 0); // --mie for rayleigh/mie scattering + + // load static scene + model_t sponza; + int do_sponza = flag("--sponza"); + if( do_sponza ) { + sponza = model("sponza.obj", 0); // MODEL_NO_TEXTURES); + translation44(sponza.pivot, 0,-1,0); + rotate44(sponza.pivot, -90,1,0,0); + scale44(sponza.pivot, 10,10,10); + } + + model_t shaderball; + int do_shaderball = flag("--shaderball"); + if( do_shaderball ) { + shaderball = model("shaderball.glb", 0); + translation44(shaderball.pivot, 0,0,-10); + rotate44(shaderball.pivot, -90,1,0,0); + scale44(shaderball.pivot, 0.02,0.02,0.02); + } + + // animated models loading + int model_flags = flag("--matcaps") ? MODEL_MATCAPS : 0; + model_t girl = model("kgirl/kgirls01.fbx", model_flags); + model_t alien = model("alien/alien_helmet.fbx", model_flags); rotation44(alien.pivot, -90,1,0,0); + model_t george = model("robots/george.fbx", model_flags); + model_t leela = model("robots/leela.fbx", model_flags); + model_t mike = model("robots/mike.fbx", model_flags); + model_t stan = model("robots/stan.fbx", model_flags); + model_t robots[4] = { george, leela, mike, stan }; + for( int i = 0; i < countof(robots); ++i ) { + rotation44(robots[i].pivot, -90,1,0,0); + } + + if( flag("--matcaps") ) { + // patch models to use matcaps + model_set_texture(george, texture("matcaps/3B6E10_E3F2C3_88AC2E_99CE51-256px", 0)); // green + model_set_texture(leela, texture("matcaps/39433A_65866E_86BF8B_BFF8D8-256px", 0)); + model_set_texture(mike, texture("matcaps/394641_B1A67E_75BEBE_7D7256-256px.png", 0)); + model_set_texture(stan, texture("matcaps/test_steel", 0)); + model_set_texture(girl, texture("matcaps/material3", 0)); + model_set_texture(alien, texture("matcaps/material3", 0)); + + if( flag("--shaderball") ) + model_set_texture(shaderball, texture("matcaps/normals", 0)); + } + + // camera + camera_t cam = camera(); + cam.speed = 0.2f; + + // audio (both clips & streams) + audio_t SFX1 = audio_clip( "coin.wav" ); + audio_t SFX2 = audio_clip( "pew.sfxr" ); + audio_t BGM1 = audio_stream( "waterworld-map.fur"); // wrath_of_the_djinn.xm" ); + audio_t BGM2 = audio_stream( "larry.mid" ); + audio_t BGM3 = audio_stream( "monkey1.mid" ), BGM = BGM1; + audio_play(SFX1, 0); + audio_play(BGM, 0); + + // demo loop + while (window_swap()) + { + // input + if( input_down(KEY_ESC) ) break; + if( input_down(KEY_F5) ) window_reload(); + if( input_down(KEY_W) && input_held(KEY_LCTRL) ) break; + if( input_down(KEY_F11) ) window_fullscreen( window_has_fullscreen() ^ 1 ); + if( input_down(KEY_X) ) window_screenshot(__FILE__ ".png"); + if( input_down(KEY_Z) ) window_record(__FILE__ ".mp4"); + + // vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), do_gamepad_deadzone + 1e-3); + // vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), do_gamepad_deadzone + 1e-3); + + // fps camera + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z); + camera_fps(&cam, mouse.x,mouse.y); + window_cursor( !active ); + + // apply post-fxs from here + fx_begin(); + + // queue debug drawcalls + profile("Debugdraw") { + ddraw_grid(0); + ddraw_color(YELLOW); + ddraw_text(vec3(+1,+1,-1), 0.04f, va("(%f,%f,%f)", cam.position.x,cam.position.y,cam.position.z)); + if(do_debugdraw) ddraw_demo(); // showcase many debugdraw shapes + ddraw_color(YELLOW); + ddraw_flush(); + } + + // draw skybox + profile("Skybox") { + skybox_render(&sky, cam.proj, cam.view); + } + + profile("Skeletal update") if(!window_has_pause()) { + float delta = window_delta() * 30; // 30fps anim + + // animate girl & alien + girl.curframe = model_animate(girl, girl.curframe + delta); + alien.curframe = model_animate(alien, alien.curframe + delta); + + // animate robots + for(int i = 0; i < countof(robots); ++i) { + robots[i].curframe = model_animate(robots[i], robots[i].curframe + delta); + } + } + + profile("Skeletal render") { + static vec3 p = {-10,0,-10}, r = {0,0,0}, s = {2,2,2}; + gizmo(&p, &r, &s); + mat44 M; rotationq44(M, eulerq(r)); scale44(M, s.x,s.y,s.z); relocate44(M, p.x,p.y,p.z); + + model_render(girl, cam.proj, cam.view, M, 0); + + aabb box = model_aabb(girl, M); + ddraw_color(YELLOW); + ddraw_aabb(box.min, box.max); + } + + profile("Skeletal render") { + static vec3 p = {+10,0,-10}, r = {0,-90,0}, s = {1,1,1}; + //gizmo(&p, &r, &s); + mat44 M; rotationq44(M, eulerq(r)); scale44(M, s.x,s.y,s.z); relocate44(M, p.x,p.y,p.z); + + model_render(alien, cam.proj, cam.view, M, 0); + + aabb box = model_aabb(alien, M); // @fixme: neg Y + ddraw_color(YELLOW); + //ddraw_aabb(box.min, box.max); + } + + profile("Skeletal render") for(int i = 0; i < countof(robots); ++i) { + float scale = 0.50; + mat44 M; copy44(M, robots[i].pivot); translate44(M, i*3,0,0); scale44(M, scale,scale,scale); + model_render(robots[i], cam.proj, cam.view, M, 0); + } + + if(do_sponza) profile("Sponza") { + float scale = 1.00; + mat44 M; copy44(M, sponza.pivot); translate44(M, 0,0,0); scale44(M, scale,scale,scale); + model_render(sponza, cam.proj, cam.view, M, 0); + } + + if(do_shaderball) profile("Shaderball") { + float scale = 1.00; + mat44 M; copy44(M, shaderball.pivot); translate44(M, 0,0,0); scale44(M, scale,scale,scale); + model_render(shaderball, cam.proj, cam.view, M, 0); + } + + // post-fxs end here + fx_end(); + + // font demo + do_once font_scales(FONT_FACE1, 48, 24, 18, 12, 9, 6); + font_print(va(FONT_RIGHT FONT_BOTTOM FONT_H4 "%5.2f FPS", window_fps())); + + // queue ui + if( ui_panel("App", 0)) { + if(ui_bool("Show debugdraw demo", &do_debugdraw)) {} + if(ui_separator()) {} + if(ui_slider("Gamepad deadzone", &do_gamepad_deadzone)) {} + if(ui_float2("Gamepad polarity", do_gamepad_polarity.v2)) {} + if(ui_float2("Gamepad sensitivity", do_gamepad_sensitivity.v2)) {} + if(ui_separator()) {} + if(ui_float2("Mouse polarity", do_mouse_polarity.v2)) {} + if(ui_float2("Mouse sensitivity", do_mouse_sensitivity.v2)) {} + if(ui_separator()) {} + if(ui_button("About...")) { do_about = 1; audio_play(SFX1, 0); } + if(ui_dialog("About", __FILE__ "\n" __DATE__ "\n" "Public Domain.", 0, &do_about)) {} + ui_panel_end(); + } + if( ui_panel("Camera", 0)) { + if( ui_float("Speed", &cam.speed) ) {} + if( ui_float3("Position", cam.position.v3) ) {} + ui_panel_end(); + } + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, fx_enabled(i) ^ 1); + } + ui_panel_end(); + } + if( ui_panel("Audio", 0)) { + static float bgm = 1, sfx = 1, master = 1; + if( ui_slider2("BGM", &bgm, va("%.2f", bgm))) audio_volume_stream(bgm); + if( ui_slider2("SFX", &sfx, va("%.2f", sfx))) audio_volume_clip(sfx); + if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master); + if( ui_label2_toolbar("BGM: Waterworld Map" /*Wrath of the Djinn"*/, ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = BGM1, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("BGM: Leisure Suit Larry", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = BGM2, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("BGM: Monkey Island", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = BGM3, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(SFX1, 0); + if( ui_label2_toolbar("SFX: Pew", ICON_MD_VOLUME_UP)) audio_play(SFX2, 0); + ui_panel_end(); + } + + input_demo(); // show some keyboard/mouse/gamepad UI tabs + ui_demo(1); // show all UI widgets in a tab + } + + // data tests (json5) + const char json5[] = + " /* json5 */ // comment\n" + " abc: 42.67, def: true, integer:0x100 \n" + " huge: 2.2239333e5, \n" + " hello: 'world /*comment in string*/ //again', \n" + " children : { a: 1, b: 2, c: 3 },\n" + " array: [+1,2,-3,4,5], \n" + " invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],"; + if( json_push(json5) ) { + assert( json_float("/abc") == 42.67 ); + assert( json_int("/def") == 1 ); + assert( json_int("/integer") == 0x100 ); + assert( json_float("/huge") > 2.22e5 ); + assert( strlen(json_string("/hello")) == 35 ); + assert( json_int("/children/a") == 1 ); + assert( json_int("/children.b") == 2 ); + assert( json_int("/children[c]") == 3 ); + assert( json_int("/array[%d]", 2) == -3 ); + assert( json_count("/invalids") == 8 ); + assert( isnan(json_float("/invalids[0]")) ); + assert( !json_find("/non_existing") ); + assert( PRINTF("json5 tests OK\n") ); + json_pop(); + } + + // data tests (xml) + const char *xml = vfs_read("test1.xml"); + if( xml_push(xml) ) { + puts( xml ); + puts( xml_string("/person/firstName/$") ); + puts( xml_string("/person/lastName/$") ); + puts( xml_string("/person/address/@type") ); + xml_pop(); + } + + // network test (https) + array(char) webfile = download("https://www.google.com/"); + printf("Network test: %d bytes downloaded from google.com\n", array_count(webfile)); + + // script test (lua) + script_run( "-- Bye.lua\nio.write(\"script test: Bye world!, from \", _VERSION, \"\\n\")" ); +} diff --git a/demos/00-hello.c b/demos/00-hello.c new file mode 100644 index 0000000..e2832d9 --- /dev/null +++ b/demos/00-hello.c @@ -0,0 +1,7 @@ +#include "fwk.h" // Minimal C sample +int main() { + window_create(75.0, 0); // 75% size, no extra flags + while( window_swap() && !input(KEY_ESC) ) { // game loop + puts("hello FWK from C!"); + } +} \ No newline at end of file diff --git a/demos/00-ui.c b/demos/00-ui.c new file mode 100644 index 0000000..7ad04b6 --- /dev/null +++ b/demos/00-ui.c @@ -0,0 +1,86 @@ +// hello ui: config, window, system, ui, video +// - rlyeh, public domain. +// +// Compile with: +// `make demos\00-ui.c` (windows) +// `sh MAKE.bat demos/00-ui.c` (linux, osx) + +#include "fwk.h" + +int main() { + float app_volume = 1.00f; + unsigned app_size = flag("--fullscreen") ? 100 : 75; + unsigned app_flags = flag("--msaa") ? WINDOW_MSAA4 : 0; + unsigned app_target_fps = optioni("--fps", 60); // --fps=30, --fps=45, etc. defaults to 60. + + // window api (fullscreen or 75% sized, optional MSAA flags) + window_create(app_size, app_flags); + window_title(__FILE__); + window_fps_lock(app_target_fps); + + // load video + video_t *v = video( "bjork-all-is-full-of-love.mp4", VIDEO_RGB ); + + // app loop + while( window_swap() ) { + // input controls + if( input(KEY_ESC) ) break; + + // profile api + texture_t *textures; + profile( "Video decoder" ) { + // video api: decode frame and get associated textures (audio is sent to audiomixer automatically) + textures = video_decode( v ); + // fullscreen video + // if(video_is_rgb(v)) fullscreen_quad_rgb( textures[0], 1.3f ); + // else fullscreen_quad_ycbcr( textures, 1.3f ); + } + + // create menubar on top + int choice1 = ui_menu("File;Shell;Exit"); + int choice2 = ui_menu("Help;About"); + if( choice1 == 1 ) system(ifdef(win32, "start \"\" cmd", ifdef(osx, "open sh", "xdg-open sh"))); + if( choice1 == 2 ) exit(0); + + // showcase a few ui widgets + ui_demo(0); + + // create ui panel + if( ui_panel("myPanel", PANEL_OPEN) ) { + // Print some numbers + ui_section("Stats"); + ui_label2("FPS", va("%5.2f", window_fps())); + ui_separator(); + + // add some buttons + ui_section("Buttons"); + if( ui_button("Screenshot") ) window_screenshot(__FILE__ ".png"), ui_notify(0,ICON_MD_WARNING "Screenshot"); + if( ui_button("Record Video") ) window_record(__FILE__ ".mp4"), ui_notify(0,ICON_MD_WARNING "Recoding video"); + if( ui_button("Toggle fullscreen") ) window_fullscreen( !window_has_fullscreen() ); + ui_separator(); + + // some more video controls + ui_section("Video"); + if( ui_button("Rewind") ) video_seek(v, video_position(v) - 3); + if( ui_button("Pause") ) video_pause(v, video_is_paused(v) ^ 1); + if( ui_button("Forward") ) video_seek(v, video_position(v) + 3); + if( ui_slider2("Volume", &app_volume, va("%.2f", app_volume))) audio_volume_master(app_volume); + + // end of panel. must be enclosed within same if() branch. + ui_panel_end(); + } + + // create window + static int open = 1; + if( ui_window("myWindow", &open) ) { + // present decoded texture in a widget, without any label (NULL) + ui_texture( NULL, textures[0] ); + // end of window. must be enclosed within same if() branch. + ui_window_end(); + } + } +} + +// this demo supersedes following old sources: +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-hello.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-video.c diff --git a/demos/01-sprite.c b/demos/01-sprite.c new file mode 100644 index 0000000..def3fcb --- /dev/null +++ b/demos/01-sprite.c @@ -0,0 +1,167 @@ +// sprite demo: window, audio, camera, font, tiled, render, fx, spritesheet, input, ui. @todo: finish spine +// - rlyeh, public domain. +// +// Compile with: +// `make demos\01-sprite.c` (windows) +// `sh MAKE.bat demos/01-sprite.c` (linux, osx) + +#include "fwk.h" + +void demo_kids(vec3 offs) { + // init + static texture_t kids; do_once kids = texture( "spriteSheetExample.png", TEXTURE_LINEAR ); + static vec3 pos[2] = {{490,362},{442,362}}, vel[2] = {0}; + static int row[2] = {0,3}, frame[2] = {0}; + static int inputs[2][4] = {{KEY_W,KEY_A,KEY_S,KEY_D},{KEY_UP,KEY_LEFT,KEY_DOWN,KEY_RIGHT}}; + + // move + for( int i = 0; i < countof(pos); ++i ) { + vel[i].x = input(inputs[i][3]) - input(inputs[i][1]); + vel[i].y = input(inputs[i][2]) - input(inputs[i][0]); + pos[i].x = fmod(pos[i].x + vel[i].x, window_width() + 128); + pos[i].y = fmod(pos[i].y + vel[i].y, window_height() + 128); + frame[i] += vel[i].x || vel[i].y; + } + + // render + for( int i = 0; i < countof(pos); ++i ) { + int col = frame[i] / 10, num_frame = row[i] * 4 + col % 4; // 4x4 tilesheet + float position[3] = {pos[i].x,pos[i].y,pos[i].y}, offset[2]={0,0}, scale[2]={0.5,0.5}; + float spritesheet[3]={num_frame,4,4}; + sprite_sheet(kids, + spritesheet, // num_frame in a 4x4 spritesheet + position, 0, // position(x,y,depth:sort-by-Y), angle + offset, scale, // offset(x,y), scale(x,y) + false, WHITE, false // is_additive, tint color, resolution-independent + ); + } +} + +void demo_hud() { + // draw pixel-art hud, 16x16 ui element, scaled and positioned in resolution-independant way + static texture_t inputs; do_once inputs = texture( "prompts_tilemap_34x24_16x16x1.png", TEXTURE_LINEAR ); + float spritesheet[3] = {17,34,24}, offset[2] = {0, - 2*absf(sin(window_time()*5))}; // sprite cell and animation + float scale[2] = {3, 3}, tile_w = 16 * scale[0], tile_h = 16 * scale[1]; // scaling + float position[3] = {window_width() - tile_w, window_height() - tile_h, window_height() }; // position in screen-coordinates (x,y,z-index) + sprite_sheet(inputs, spritesheet, position, 0/*deg*/, offset, scale, false, WHITE, 1); +} + +int main() { + // window (80% sized, MSAA x4 flag) + window_create(80.0, WINDOW_MSAA4); + window_title(__FILE__); + + // tiled map + tiled_t tmx = tiled(vfs_read("castle.tmx")); + // tmx.parallax = true; + + // spine model + //spine_t *spn = spine("goblins.json", "goblins.atlas", 0); + + // camera 2d + camera_t cam = camera(); + cam.position = vec3(window_width()/2, window_height()/2, 3); // at(CX,CY) zoom(x3) + camera_enable(&cam); + + // audio (both clips & streams) + audio_t clip1 = audio_clip( "coin.wav" ); + audio_t clip2 = audio_clip( "pew.sfxr" ); + audio_t stream1 = audio_stream( "larry.mid" ); + audio_t stream2 = audio_stream( "monkey1.mid" ); + audio_t BGM = stream1; + audio_play(BGM, 0); + + // font config: faces (6 max) and colors (10 max) + #define FONT_CJK FONT_FACE3 + #define FONT_YELLOW FONT_COLOR2 + #define FONT_LIME FONT_COLOR3 + font_face(FONT_CJK, "mplus-1p-medium.ttf", 48.f, FONT_JP|FONT_2048); // CJK|FONT_2048|FONT_OVERSAMPLE_Y); + font_color(FONT_YELLOW, RGB4(255,255,0,255)); + font_color(FONT_LIME, RGB4(128,255,0,255)); + + // fx: load all post fx files in all subdirs. enable a few filters by default + fx_load("fx**.fs"); + fx_enable(fx_find("fxCRT2.fs"), 1); + fx_enable(fx_find("fxGrain.fs"), 1); + fx_enable(fx_find("fxContrast.fs"), 1); + fx_enable(fx_find("fxVignette.fs"), 1); + + // demo loop + while (window_swap() && !input_down(KEY_ESC)) { + + // handle input + if( input_down(KEY_F5) ) window_reload(); + if( input_down(KEY_F11) ) window_fullscreen( !window_has_fullscreen() ); + + // camera panning (x,y) & zooming (z) + if( !ui_hover() && !ui_active() ) { + if( input(MOUSE_L) ) cam.position.x += input_diff(MOUSE_X); + if( input(MOUSE_L) ) cam.position.y += input_diff(MOUSE_Y); + cam.position.z += input_diff(MOUSE_W) * 0.1; + } + + // apply post-fxs from here + fx_begin(); + + profile("Rendering") { + vec3 center = add3(cam.position,vec3(-window_width()/1,-window_height()/2,0)); + // render tiled map + tiled_render(tmx, center); + // + demo_kids(vec3(0,0,1)); + demo_hud(); + // render spine model + // spine_animate(spn, !window_has_pause() * window_delta()); + // spine_render(spn, vec3(cam.position.x, cam.position.y, 1), true ); + // sprite_flush(); + } + + // subtitle sample + font_print( + FONT_BOTTOM FONT_CENTER + FONT_CJK FONT_H1 + FONT_YELLOW "私はガラスを食べられます。" FONT_LIME "それは私を傷つけません。\n" + ); + + // post-fxs end here + fx_end(); + + // ui + if( ui_panel("Audio", 0)) { + static float bgm = 1, sfx = 1, master = 1; + if( ui_slider2("BGM", &bgm, va("%.2f", bgm))) audio_volume_stream(bgm); + if( ui_slider2("SFX", &sfx, va("%.2f", sfx))) audio_volume_clip(sfx); + if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master); + if( ui_label2_toolbar("BGM: Leisure Suit Larry", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream1, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("BGM: Monkey Island", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream2, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(clip1, 0); + if( ui_label2_toolbar("SFX: Pew", ICON_MD_VOLUME_UP)) audio_play(clip2, 0); + ui_panel_end(); + } + if( ui_panel("Tiled", 0)) { + ui_float("Zoom in", &cam.position.z); + tiled_ui(&tmx); + ui_panel_end(); + } + /*if( ui_panel("Spine", 0)) { + spine_ui(spn); + ui_panel_end(); + }*/ + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, fx_enabled(i) ^ 1); + } + ui_panel_end(); + } + } +} + +// this demo supersedes following old sources: +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-audio.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-font.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-spine.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-sprite.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-tiled.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-tilemap.c diff --git a/demos/02-ddraw.c b/demos/02-ddraw.c new file mode 100644 index 0000000..1fd22e1 --- /dev/null +++ b/demos/02-ddraw.c @@ -0,0 +1,119 @@ +// ddraw demo: fps camera, renderdd, collide, math, ui, fx +// - rlyeh, public domain. +// +// Compile with: +// `make demos\02-ddraw.c` (windows) +// `sh MAKE.bat demos/02-ddraw.c` (linux, osx) + +#include "fwk.h" + +int main() { + bool do_colliders_demo = 1; + bool do_debugdraw_demo = 0; + + // 75% size, MSAAx2 + window_create(75.0, WINDOW_MSAA2); + window_title(__FILE__); + + // camera that points to origin + camera_t cam = camera(); + // load skybox folder (no flags) + skybox_t sky = skybox("cubemaps/stardust", 0); + // load all postfx files in all subdirs + fx_load("fx**.fs"); + + // main loop + while( window_swap() ) { + + // input handler + if (input_down(KEY_F11) ) window_fullscreen( window_has_fullscreen()^1 ); + if (input_down(KEY_ESC) ) break; + + // fps camera + profile("FPS camera") { + if( input(GAMEPAD_CONNECTED) ) { + vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 ); + vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 ); + vec2 mouse = scale2(vec2(filtered_rpad.x, filtered_rpad.y), 1.0f); + vec3 wasdec = scale3(vec3(filtered_lpad.x, input(GAMEPAD_LT) - input(GAMEPAD_RT), filtered_lpad.y), 1.0f); + camera_move(&cam, wasdec.x,wasdec.y,wasdec.z); + camera_fps(&cam, mouse.x,mouse.y); + window_cursor( true ); + } else { + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z); + camera_fps(&cam, mouse.x,mouse.y); + window_cursor( !active ); + } + } + + fx_begin(); + + // draw skybox + skybox_render(&sky, cam.proj, cam.view); + + // world + ddraw_grid(0); + + // boids + static swarm_t sw; + profile("boids") { + do_once sw = swarm(); + do_once array_push(sw.steering_targets, vec3(0,0,0)); + do_once for(int i = 0; i < 100; ++i) + array_push(sw.boids, boid(scale3(rnd3(),10), rnd3())); // pos,vel + + // move + sw.steering_targets[0] = cam.position; + swarm_update(&sw, window_delta()); + + // draw + for (int j = 0, end = array_count(sw.boids); j < end; ++j) { + vec3 dir = norm3(sub3(sw.boids[j].position, sw.boids[j].prev_position)); + ddraw_boid(sw.boids[j].position, dir); + } + } + + // showcase many debugdraw shapes + if( do_debugdraw_demo ) { + ddraw_demo(); + } + + // showcase many colliding tests + if( do_colliders_demo ) { + collide_demo(); + } + + fx_end(); + + // ui + if( ui_panel("App", 0) ) { + ui_bool("Collide demo", &do_colliders_demo); + ui_bool("DebugDraw demo", &do_debugdraw_demo); + ui_panel_end(); + } + if( ui_panel("Swarm", 0) ) { + ui_swarm(&sw); + ui_panel_end(); + } + if( ui_panel("Camera", 0)) { + if( ui_float("Speed", &cam.speed) ) {} + if( ui_float3("Position", cam.position.v3) ) {} + ui_panel_end(); + } + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, fx_enabled(i) ^ 1); + } + ui_panel_end(); + } + } +} + +// this demo supersedes following old sources: +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-collide.c diff --git a/demos/02-frustum.c b/demos/02-frustum.c new file mode 100644 index 0000000..34cc971 --- /dev/null +++ b/demos/02-frustum.c @@ -0,0 +1,65 @@ +#include "fwk.h" + +void ddraw_camera(camera_t *c) { + vec3 center = c->position; +// ddraw_prism(add3(center,vec3(2,0,0)), 0.5, 1, vec3(-1,0,0), 4); // center,radius,height,normal,segments +// ddraw_box(center, vec3(2,2,1)); // center,extents + + mat33 r; rotationq33(r, eulerq(vec3(-c->yaw,-c->pitch,0))); + ddraw_cube33(center, vec3(2,2,2), r); + + ddraw_circle(add3(center,vec3(+1,1,0)), vec3(0,0,1), 0.8); // pos,normal,radius + ddraw_circle(add3(center,vec3(-1,1,0)), vec3(0,0,1), 0.8); // pos,normal,radius + + mat44 projview; multiply44x2(projview, c->proj, c->view); + ddraw_frustum(projview); +} + +int main() { + window_create(0.75, 0); + + camera_t cam = camera(); + camera_t cam2 = camera(); + + int spin = 1; + + while( window_swap() ) { + if(input_down(KEY_SPACE)) spin^=1; + // spin 2nd camera + double t = window_time(), c = cos(t), s = sin(t); + if(spin) + camera_teleport(&cam2, vec3(c * 100, 100, s * 100)); + camera_lookat(&cam2, vec3(0,0,0)); + + // fps camera + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z); + camera_fps(&cam, mouse.x,mouse.y); + window_cursor( !active ); + + mat44 projview; multiply44x2(projview, cam2.proj, cam2.view); + frustum f = frustum_build(projview); + + ddraw_ground(0); + ddraw_camera(&cam2); + + int drawn = 0, total = 0; + for(int z = -300; z < 300; z += 5) { + for(int x = -300; x < 300; x += 5) { + vec3 min = vec3(x, 0, z); + vec3 max = add3(min, vec3(2.5,2.5,2.5)); + + if( frustum_test_aabb(f, aabb(min, max)) ) { + ddraw_aabb( min, max ); + ++drawn; + } + ++total; + } + } + + font_print(va(FONT_RIGHT "%d/%d cubes drawn", drawn, total)); + } +} diff --git a/demos/03-anims.c b/demos/03-anims.c new file mode 100644 index 0000000..5b85e52 --- /dev/null +++ b/demos/03-anims.c @@ -0,0 +1,186 @@ +// anims demo: input, mesh, anim, render, fx, ui, instancing +// - rlyeh, public domain. +// +// Compile with: +// `make demos\03-anims.c` (windows) +// `sh MAKE.bat demos/03-anims.c` (linux, osx) +// +// @todo: instanced poses, instanced render_bones +// @todo: ik, modify bone transform (bone space) + +#include "fwk.h" + +typedef struct anims_t { + int inuse; // animation number in use + float speed; // x1.00 + array(anim_t) anims; // [begin,end,flags] frames of every animation in set + array(mat44) M; // instanced transforms +} anims_t; + +anims_t animations(const char *pathfile, int flags) { + anims_t a = {0}; + char *anim_file = vfs_read(pathfile); + for each_substring(anim_file, "\r\n", anim) { + int from, to; + char anim_name[128] = {0}; + if( sscanf(anim, "%*s %d-%d %127[^\r\n]", &from, &to, anim_name) != 3) continue; + array_push(a.anims, !!strstri(anim_name, "loop") ? loop(from, to, 0, 0) : clip(from, to, 0, 0)); // [from,to,flags] + array_back(a.anims)->name = strswap(strswap(strswap(STRDUP(anim_name), "Loop", ""), "loop", ""), "()", ""); + } + array_resize(a.M, 32*32); + for(int z = 0, i = 0; z < 32; ++z) { + for(int x = 0; x < 32; ++x, ++i) { + vec3 p = vec3(-x*3,0,-z*3); + vec3 r = vec3(0,0,0); + vec3 s = vec3(2,2,2); + compose44(a.M[i], p, eulerq(r), s); + } + } + a.speed = 1.0; + return a; +} + +int main() { + bool do_showaabb = 0; + bool do_showbones = 0; + bool do_showmodel = 1; + bool do_showgizmo = 1; + + // 75% sized, MSAAx2 + window_create(75, WINDOW_MSAA2); + window_title(__FILE__); + + camera_t cam = camera(); + skybox_t sky = skybox("cubemaps/stardust", 0); + model_t mdl = model("kgirls01.fbx", 0); + anims_t a = animations("kgirl/animlist.txt", 0); + + // load all postfx files in all subdirs + fx_load("fx**.fs"); + + while( window_swap() && !input(KEY_ESC) ) { + // fps camera + if( input(GAMEPAD_CONNECTED) ) { + vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 ); + vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 ); + vec2 mouse = scale2(vec2(filtered_rpad.x, filtered_rpad.y), 1.0f); + vec3 wasdec = scale3(vec3(filtered_lpad.x, input(GAMEPAD_LT) - input(GAMEPAD_RT), filtered_lpad.y), 1.0f); + camera_move(&cam, wasdec.x,wasdec.y,wasdec.z); + camera_fps(&cam, mouse.x,mouse.y); + window_cursor( true ); + } else { + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z); + camera_fps(&cam, mouse.x,mouse.y); + window_cursor( !active ); + } + + // skeletal update + static bool is_dragging_slider = 0; + vec2i anim = vec2i( a.anims[ a.inuse ].from, a.anims[ a.inuse ].to ); + profile("Skeletal update") { + float delta = window_delta() * 30 * a.speed * !is_dragging_slider; // 30fps anim timer + if(!window_has_pause()) mdl.curframe = model_animate_clip(mdl, mdl.curframe + delta, anim.min, anim.max, a.anims[a.inuse].flags & ANIM_LOOP ); + } + + // render + fx_begin(); + + // Skybox + profile("Skybox") { + skybox_render(&sky, cam.proj, cam.view); + } + + // ground + ddraw_ground(0); + ddraw_flush(); + + // characters + static int NUM_INSTANCES = 1; + profile("Skeletal render") { + if( do_showmodel ) model_render_instanced(mdl, cam.proj, cam.view, a.M, 0, NUM_INSTANCES); + + if( do_showbones ) model_render_skeleton(mdl, a.M[0]); + + if( do_showaabb ) { + aabb box = model_aabb(mdl, a.M[0]); + ddraw_aabb(box.min, box.max); + } + + if( do_showgizmo ) { + static vec3 p = {0,0,0}, r = {0,0,0}, s = {2,2,2}; + gizmo(&p, &r, &s); + compose44(a.M[0], p, eulerq(r), s); + } + } + + fx_end(); + + if( ui_panel("Animation", 0) ) { + if( ui_bool("Show aabb", &do_showaabb) ); + if( ui_bool("Show bones", &do_showbones) ); + if( ui_bool("Show models", &do_showmodel) ); + if( ui_bool("Show gizmo", &do_showgizmo) ); + ui_separator(); + + if( ui_int("Instances", &NUM_INSTANCES)) NUM_INSTANCES = clampi(NUM_INSTANCES, 1, array_count(a.M)); + ui_separator(); + + ui_label(va("Anim %s [%d.. %.2f ..%d]", a.anims[ a.inuse ].name, anim.min, mdl.curframe, anim.max )); + + // normalize curframe into [0..1] range + is_dragging_slider = 0; + float range = (mdl.curframe - anim.min) / ((anim.max - anim.min) + !(anim.max - anim.min)); + if( ui_slider2("Frame", &range, va("%.2f/%d %02d%%", mdl.curframe - anim.min, anim.max - anim.min, (int)(range * 100.f))) ) { + mdl.curframe = range * (anim.max - anim.min) + anim.min; + is_dragging_slider = 1; + } + + ui_slider2("Speed", &a.speed, va("x%.2f", a.speed)); + ui_separator(); + + for( int i = 0; i < array_count(a.anims); ++i ) { + bool selected = a.inuse == i; + float progress = selected ? (mdl.curframe - anim.min) * 100.f / (anim.max - anim.min) : 0.f; + const char *caption = va("%s%s%s %.2f%%", selected ? "*":"", a.anims[i].name, a.anims[i].flags & ANIM_LOOP ? " (Loop)":"", progress); + int choice = ui_label2_toolbar(caption, va("%s %s %s", ICON_MD_REPLAY_CIRCLE_FILLED, a.inuse == i && a.speed <= 0 ? ICON_MD_NOT_STARTED : ICON_MD_PAUSE_CIRCLE, ICON_MD_PLAY_CIRCLE) ); + if( choice == 1 ) { // play/restart + if( mdl.curframe >= anim.max ) mdl.curframe = anim.min; // restart animation + a.speed = 1.0f; + a.inuse = i; + } + if( choice == 2 ) { // pause/advance-frame + if(a.speed <= 0) mdl.curframe = (int)mdl.curframe + 1; + a.speed = 0.0f; + a.inuse = i; + } + if( choice == 3 ) { // loop on/off + if( a.anims[i].flags & ANIM_LOOP ) + a.anims[ i ].flags &= ~ANIM_LOOP; + else + a.anims[ i ].flags |= ANIM_LOOP; + a.inuse = i; + } + } + ui_panel_end(); + } + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, b); + } + ui_panel_end(); + } + } +} + +// @todo: controller demo: root motion on/off +// @todo: controller demo: anim controller.lua + +// this demo supersedes following old sources: +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-anims.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-instanced.c diff --git a/demos/04-actor.c b/demos/04-actor.c new file mode 100644 index 0000000..3b5502e --- /dev/null +++ b/demos/04-actor.c @@ -0,0 +1,150 @@ +// actor controller demo: anims, anim blending, input, math +// - rlyeh, public domain. +// +// Compile with: +// `make demos\04-actor.c` (windows) +// `sh MAKE.bat demos/04-actor.c` (linux, osx) + +#include "fwk.h" + +int main() { + // create window (75% sized, MSAAx4) + window_create(75, WINDOW_MSAA4); + window_title(__FILE__); + + // set up our players + struct player_t { + const char *name; + model_t mdl; + anim_t idle, run; // anim clips + float keys[4], scale; // up,down,left,right + vec2 inertia; // [forward,yaw] + vec3 pivot, speed; // [pitch,yaw,roll] [turn speed,forward speed,anim speed fps] + vec3 pos, dir, pad; // [position] [facing dir] [gamepad accumulator] + bool notified; + float brain[4]; // AI + } player[3] = { + { "PLAYER-1", model("kgirls01.fbx", 0), loop(0,60,0.25,0), loop(66,85,0.25,0), // idle anim [0..60], run anim [66..85] + {KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT}, 2, {0.90,0.80}, {-100}, {3, 0.30, 30}, {0}, {1} }, + { "PLAYER-2", model("george.fbx", 0), loop(0,100,0.25,0), loop(372,396,0.25,0), // idle anim [0..100], run anim [372..396] + {KEY_I,KEY_K,KEY_J,KEY_L}, 1, {0.95,0.90}, {-90,-90}, {1.75, 0.25, 24}, {-5}, {1} }, + { "PLAYER-3", model("alien.fbx", 0), loop(110,208,0.25,0), loop(360,380,0.25,0), // idle anim [110..208], run anim [360..380] + {KEY_W,KEY_S,KEY_A,KEY_D}, 0.85, {0.85,0.75}, {-90,-90}, {3.5, 0.35, 60}, {5}, {1} } + }; + + // camera that points to origin, skybox, and a background tune + camera_t cam = camera(); + skybox_t sky = skybox("cubemaps/stardust", 0); + audio_play( audio_stream("waterworld-map.fur"), 0 ); + + // game loop + while( window_swap() ) { + // world: skybox, position markers and ground grid + skybox_render(&sky, cam.proj, cam.view); + for( int i = 0; i < countof(player); ++i ) + ddraw_position_dir(player[i].pos, player[i].dir, 1.0f); + ddraw_grid(0); + ddraw_flush(); + + // move and render players + for( int i = 0; i < countof(player); ++i ) { + struct player_t *p = &player[i]; + + // capture inputs + p->brain[0] = input(p->keys[0]); + p->brain[1] = input(p->keys[1]); + p->brain[2] = input(p->keys[2]); + p->brain[3] = input(p->keys[3]); + + // setup waypoints for PLAYER-1 + static array(vec3) points; + if( input_down(MOUSE_L) && !ui_hover() ) { + vec3 pt = editor_pick(input(MOUSE_X), input(MOUSE_Y)); + hit *h = ray_hit_plane(ray(cam.position, pt), plane(vec3(0,0,0),vec3(0,1,0))); + if(h) array_push(points, h->p); + } + // ddraw waypoints + ddraw_color(YELLOW); + for( int i = 1; i < array_count(points); ++i) ddraw_line(points[i-1],points[i]); + for( int i = 0; i < array_count(points); ++i) ddraw_circle(points[i], vec3(0,1,0), 1); // prism(points[i], 1, 0, vec3(0,1,0), 4); + ddraw_color(RED); + for( int i = 0; i < array_count(points); ++i) ddraw_point(points[i]); + ddraw_color(WHITE); + // move thru waypoints (PLAYER-1 only) + if( i == 0 && array_count(points) ) { + struct player_t *p = &player[i]; + vec3 dst = points[0]; + vec3 vector1 = norm3(vec3(p->dir.x,0,p->dir.z)); + vec3 vector2 = norm3(sub3(dst,p->pos)); + + float angle = atan2(vector2.z, vector2.x) - atan2(vector1.z, vector1.x); + angle *= 180 / C_PI; + // range [0, 360) + // if (angle < 0) { angle += 2 * 180; } + // range (-180, 180] + if (angle > 180) { angle -= 2 * 180; } + else if (angle <= -180) { angle += 2 * 180; } + + float dist = len3(sub3(p->pos, dst)); + if(dist < 1) { + // goal + array_pop_front(points); + } + else { + if( dist < 10 && abs(angle) > 10 ) { + // spin only + p->brain[ angle < 0 ? 2 : 3 ] = 1; + } + else { + // spin + p->brain[ angle < 0 ? 2 : 3 ] = 1; + // move forward + p->brain[ 0 ] = 1; + } + } + } + + // accumulate movement + float yaw = p->brain[2] - p->brain[3]; + float fwd = p->brain[0] - p->brain[1]; if(fwd<0) fwd = 0; + p->pad.x = p->pad.x * p->inertia.y + yaw * (1-p->inertia.y); + p->pad.y = p->pad.y * p->inertia.x + fwd * (1-p->inertia.x); + + // rotate yaw dir, then apply into position + p->dir = rotatey3(p->dir, p->speed.x * p->pad.x); + p->pos = add3(p->pos, scale3(p->dir, p->speed.y * p->pad.y)); + + // animate clips and blend + anim_t *primary = fwd ? &p->run : &p->idle; + anim_t *secondary = fwd ? &p->idle : &p->run; + model_animate_blends(p->mdl, primary, secondary, window_delta() * p->speed.z); + + // render model. transforms on top of initial pivot and scale + mat44 M; compose44(M, p->pos, eulerq(add3(p->pivot,vec3(atan2(p->dir.z,p->dir.x)*180/C_PI,0,0))),vec3(p->scale,p->scale,p->scale)); + model_render(p->mdl, cam.proj, cam.view, M, 0); + + // ui + if( yaw||fwd ) if( !p->notified ) p->notified = 1, ui_notify(0, va(ICON_MD_GAMEPAD " %s joined the game.", p->name)); + ddraw_text(p->pos, 0.01, va("%s: %6.3f", fwd?"run":"idle", (fwd ? p->run : p->idle).timer )); + } + + // look at the players that are moving; center of their triangle otherwise. + float A = len3(player[0].pad); if(A<0.01) A=0; + float B = len3(player[1].pad); if(B<0.01) B=0; + float C = len3(player[2].pad); if(C<0.01) C=0; + float weight = A + B + C; + if( weight ) A /= weight, B /= weight, C /= weight; else A = B = C = 0.33333; + vec3 target = add3(add3(scale3(player[0].pos,A), scale3(player[1].pos,B)), scale3(player[2].pos,C)); + // smooth target before sending to camera + static vec3 smooth; camera_lookat(&cam, smooth = mix3(target,smooth,!weight ? 0.98 : 0.95)); + + // ui + if( ui_panel("Controls", 0) ) { + ui_label2("Girl", ICON_MD_MOUSE " Set Waypoint"); + ui_label2("Girl", ICON_MD_GAMEPAD " CURSOR keys"); + ui_label2("Alien", ICON_MD_GAMEPAD " W,A,S,D keys"); + ui_label2("Robot", ICON_MD_GAMEPAD " I,J,K,L keys"); + ui_panel_end(); + } + } +} diff --git a/demos/04-lod.c b/demos/04-lod.c new file mode 100644 index 0000000..8394846 --- /dev/null +++ b/demos/04-lod.c @@ -0,0 +1,249 @@ +// LOD demo, based on Polygon Reduction Demo by Stan Melax (PD) +// - rlyeh, public domain. + +#include "fwk.h" + +aabb mesh_bounds(mesh_t *m) { + aabb b = {{1e9,1e9,1e9},{-1e9,-1e9,-1e9}}; + for( int i = 0; i < array_count(m->in_vertex3); ++i ) { + if( m->in_vertex3[i].x < b.min.x ) b.min.x = m->in_vertex3[i].x; + if( m->in_vertex3[i].x > b.max.x ) b.max.x = m->in_vertex3[i].x; + + if( m->in_vertex3[i].y < b.min.y ) b.min.y = m->in_vertex3[i].y; + if( m->in_vertex3[i].y > b.max.y ) b.max.y = m->in_vertex3[i].y; + + if( m->in_vertex3[i].z < b.min.z ) b.min.z = m->in_vertex3[i].z; + if( m->in_vertex3[i].z > b.max.z ) b.max.z = m->in_vertex3[i].z; + } + return b; +} + +vec2 world2screen(vec3 p) { + vec4 clip_pos = transform444(camera_get_active()->proj, transform444(camera_get_active()->view, vec4(p.x,p.y,p.z,1.0))); + vec4 ndc_pos = scale4(clip_pos, 1.0 / (clip_pos.w + !clip_pos.w)); // [-1..1] + vec2 screen_pos = vec2( window_width() * (ndc_pos.x + 1) / 2.0, window_height() * (1 - ndc_pos.y) / 2.0 ); + return screen_pos; +} + +float mesh_area(mesh_t *m) { + // @fixme: return 0 if mesh is out of frustum, or window is minimized + + aabb box = mesh_bounds(m); + ddraw_aabb(box.min, box.max); + + vec2 A = world2screen(vec3(box.min.x,box.min.y,box.min.z)); ddraw_text2d(A, va("A: %5.2f,%5.2f", A.x, A.y)); + vec2 B = world2screen(vec3(box.min.x,box.min.y,box.max.z)); ddraw_text2d(B, va("B: %5.2f,%5.2f", B.x, B.y)); + vec2 C = world2screen(vec3(box.min.x,box.max.y,box.min.z)); ddraw_text2d(C, va("C: %5.2f,%5.2f", C.x, C.y)); + vec2 D = world2screen(vec3(box.min.x,box.max.y,box.max.z)); ddraw_text2d(D, va("D: %5.2f,%5.2f", D.x, D.y)); + vec2 E = world2screen(vec3(box.max.x,box.min.y,box.min.z)); ddraw_text2d(E, va("E: %5.2f,%5.2f", E.x, E.y)); + vec2 F = world2screen(vec3(box.max.x,box.min.y,box.max.z)); ddraw_text2d(F, va("F: %5.2f,%5.2f", F.x, F.y)); + vec2 G = world2screen(vec3(box.max.x,box.max.y,box.min.z)); ddraw_text2d(G, va("G: %5.2f,%5.2f", G.x, G.y)); + vec2 H = world2screen(vec3(box.max.x,box.max.y,box.max.z)); ddraw_text2d(H, va("H: %5.2f,%5.2f", H.x, H.y)); + + vec2 O = min2(min2(min2(min2(min2(min2(min2(A,B),C),D),E),F),G),H); ddraw_text2d(O, "|\\"); + vec2 P = max2(max2(max2(max2(max2(max2(max2(A,B),C),D),E),F),G),H); ddraw_text2d(P, "\\|"); + + float area = (P.x-O.x) * (P.y-O.y); // len2(sub2(P,O)); + float pct = area / (float)(window_height() * window_width()); + + font_print(va(FONT_RIGHT "area: %5.2f%%", pct*100)); + return pct; +} + + +API void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation); + +// Map() +// +// Note that the use of the Map() function and the collapse_map Array isn't part of the polygon reduction +// algorithm. We set up the function here so that we could retrieve the model at any desired vertex count. +// +// When the model is rendered using a maximum of mx vertices then it is vertices 0 through mx-1 that are used. +// We are able to do this because the vertex Array gets sorted according to the collapse order. +// The Map() routine takes a vertex number 'n' and the maximum number of vertices 'mx' and returns the +// appropriate vertex in the range 0 to mx-1. When 'n' is greater than 'mx' the Map() routine follows the +// chain of edge collapses until a vertex within the limit is reached. +// An example to make this clear: assume there is a triangle with vertices 1, 3 and 12. But when +// rendering the model we limit ourselves to 10 vertices. In that case we find out how vertex 12 was +// removed by the polygon reduction algorithm. i.e. which edge was collapsed. Lets say that vertex 12 was +// collapsed to vertex number 7. This number would have been stored in the collapse_map array (i.e. +// collapse_map[12]==7). Since vertex 7 is in range (less than max of 10) we will want to render the +// triangle 1,3,7. Pretend now that we want to limit ourselves to 5 vertices. and vertex 7 was collapsed +// to vertex 3 (i.e. collapse_map[7]==3). Then triangle 1,3,12 would now be triangle 1,3,3. i.e. this +// polygon was removed by the progressive mesh polygon reduction algorithm by the time it had gotten down +// to 5 vertices. No need to draw a one dimensional polygon. :-) + +static inline int MapReduce(array(int) collapse_map, int n, int mx) { + while( n >= mx ) n = collapse_map[n]; + return n; +} + +mesh_t *ReduceModel(mesh_t *m, float lo_detail, float hi_detail, float morph) { + int max_verts_to_render = hi_detail * array_count(m->in_vertex3); + int min_verts_to_render = lo_detail * array_count(m->in_vertex3); + + if( max_verts_to_render <= 0 || min_verts_to_render <= 0 ) + return m; + + array_resize(m->out_index3, 0); + array_resize(m->out_vertex3, 0); + + ASSERT(array_count(m->lod_collapse_map)); + for( unsigned int i = 0; i < array_count(m->in_index3); i++ ) { + int p0 = MapReduce(m->lod_collapse_map, m->in_index3[i].x, max_verts_to_render); + int p1 = MapReduce(m->lod_collapse_map, m->in_index3[i].y, max_verts_to_render); + int p2 = MapReduce(m->lod_collapse_map, m->in_index3[i].z, max_verts_to_render); + // @fixme: serious optimization opportunity here, + // by sorting the triangles the following "continue" + // could have been made into a "break" statement. + if(p0==p1 || p0==p2 || p1==p2) continue; + // if we are not currenly morphing between 2 levels of detail + // (i.e. if morph=1.0) then q0,q1, and q2 may not be necessary + int q0 = MapReduce(m->lod_collapse_map, p0, min_verts_to_render); + int q1 = MapReduce(m->lod_collapse_map, p1, min_verts_to_render); + int q2 = MapReduce(m->lod_collapse_map, p2, min_verts_to_render); + vec3 v0, v1, v2; + v0 = mix3(m->in_vertex3[p0], m->in_vertex3[q0], (1-morph)); + v1 = mix3(m->in_vertex3[p1], m->in_vertex3[q1], (1-morph)); + v2 = mix3(m->in_vertex3[p2], m->in_vertex3[q2], (1-morph)); + vec3 normal = norm3(cross3(sub3(v1,v0),sub3(v2,v1))); + array_push(m->out_vertex3, v0); + array_push(m->out_vertex3, normal); + array_push(m->out_vertex3, v1); + array_push(m->out_vertex3, normal); + array_push(m->out_vertex3, v2); + array_push(m->out_vertex3, normal); + + int idx = array_count(m->out_vertex3) / 2; + array_push(m->out_index3, vec3i(idx-3,idx-2,idx-1)); + } + + return m; +} + +void DrawModel(mesh_t *m) { + static mat44 M; do_once id44(M); + static mat44 VP; multiply44x2(VP, camera_get_active()->proj, camera_get_active()->view); + + static const char *vs = + "#version 130\n" + "//" FILELINE "\n" + "uniform mat4 M,VP;\n" + "in vec3 att_position;\n" + "in vec3 att_normal;\n" + "out vec3 v_normal;\n" + "void main() {\n" + " v_normal = normalize(att_normal);\n" + " gl_Position = M * VP * vec4( att_position, 1.0 );\n" + "}\n"; + static const char *fs = + "#version 130\n" + "//" FILELINE "\n" + "in vec3 v_normal;\n" + "out vec4 fragcolor;\n" + "void main() {\n" + "fragcolor = vec4(v_normal, 1.0);\n" // diffuse + "}"; + + static unsigned program; do_once program = shader(vs, fs, "att_position,att_normal", "fragcolor"); + shader_bind(program); + shader_mat44("VP", VP); + shader_mat44("M", M); + + mesh_update(m, "p3 n3", 0,array_count(m->out_vertex3)/2,m->out_vertex3, array_count(m->out_index3)*3,m->out_index3,0); + mesh_render(m); +} + +void InitModel(mesh_t *m) { + static const double verts[]={-0.334392,+0.133007,+0.062259,-0.350189,+0.150354,-0.147769,-0.234201,+0.343811,-0.174307,-0.200259,+0.285207,+0.093749,+0.003520,+0.475208,-0.159365,+0.001856,+0.419203,+0.098582,-0.252802,+0.093666,+0.237538,-0.162901,+0.237984,+0.206905,+0.000865,+0.318141,+0.235370,-0.414624,+0.164083,-0.278254,-0.262213,+0.357334,-0.293246,+0.004628,+0.482694,-0.338626,-0.402162,+0.133528,-0.443247,-0.243781,+0.324275,-0.436763,+0.005293,+0.437592,-0.458332,-0.339884,-0.041150,-0.668211,-0.248382,+0.255825,-0.627493,+0.006261,+0.376103,-0.631506,-0.216201,-0.126776,-0.886936,-0.171075,+0.011544,-0.881386,-0.181074,+0.098223,-0.814779,-0.119891,+0.218786,-0.760153,-0.078895,+0.276780,-0.739281,+0.006801,+0.310959,-0.735661,-0.168842,+0.102387,-0.920381,-0.104072,+0.177278,-0.952530,-0.129704,+0.211848,-0.836678,-0.099875,+0.310931,-0.799381,+0.007237,+0.361687,-0.794439,-0.077913,+0.258753,-0.921640,+0.007957,+0.282241,-0.931680,-0.252222,-0.550401,-0.557810,-0.267633,-0.603419,-0.655209,-0.446838,-0.118517,-0.466159,-0.459488,-0.093017,-0.311341,-0.370645,-0.100108,-0.159454,-0.371984,-0.091991,-0.011044,-0.328945,-0.098269,+0.088659,-0.282452,-0.018862,+0.311501,-0.352403,-0.131341,+0.144902,-0.364126,-0.200299,+0.202388,-0.283965,-0.231869,+0.023668,-0.298943,-0.155218,+0.369716,-0.293787,-0.121856,+0.419097,-0.290163,-0.290797,+0.107824,-0.264165,-0.272849,+0.036347,-0.228567,-0.372573,+0.290309,-0.190431,-0.286997,+0.421917,-0.191039,-0.240973,+0.507118,-0.287272,-0.276431,-0.065444,-0.295675,-0.280818,-0.174200,-0.399537,-0.313131,-0.376167,-0.392666,-0.488581,-0.427494,-0.331669,-0.570185,-0.466054,-0.282290,-0.618140,-0.589220,-0.374238,-0.594882,-0.323298,-0.381071,-0.629723,-0.350777,-0.382112,-0.624060,-0.221577,-0.272701,-0.566522,+0.259157,-0.256702,-0.663406,+0.286079,-0.280948,-0.428359,+0.055790,-0.184974,-0.508894,+0.326265,-0.279971,-0.526918,+0.395319,-0.282599,-0.663393,+0.412411,-0.188329,-0.475093,+0.417954,-0.263384,-0.663396,+0.466604,-0.209063,-0.663393,+0.509344,-0.002044,-0.319624,+0.553078,-0.001266,-0.371260,+0.413296,-0.219753,-0.339762,-0.040921,-0.256986,-0.282511,-0.006349,-0.271706,-0.260881,+0.001764,-0.091191,-0.419184,-0.045912,-0.114944,-0.429752,-0.124739,-0.113970,-0.382987,-0.188540,-0.243012,-0.464942,-0.242850,-0.314815,-0.505402,-0.324768,+0.002774,-0.437526,-0.262766,-0.072625,-0.417748,-0.221440,-0.160112,-0.476932,-0.293450,+0.003859,-0.453425,-0.443916,-0.120363,-0.581567,-0.438689,-0.091499,-0.584191,-0.294511,-0.116469,-0.599861,-0.188308,-0.208032,-0.513640,-0.134649,-0.235749,-0.610017,-0.040939,-0.344916,-0.622487,-0.085380,-0.336401,-0.531864,-0.212298,+0.001961,-0.459550,-0.135547,-0.058296,-0.430536,-0.043440,+0.001378,-0.449511,-0.037762,-0.130135,-0.510222,+0.079144,+0.000142,-0.477549,+0.157064,-0.114284,-0.453206,+0.304397,-0.000592,-0.443558,+0.285401,-0.056215,-0.663402,+0.326073,-0.026248,-0.568010,+0.273318,-0.049261,-0.531064,+0.389854,-0.127096,-0.663398,+0.479316,-0.058384,-0.663401,+0.372891,-0.303961,+0.054199,+0.625921,-0.268594,+0.193403,+0.502766,-0.277159,+0.126123,+0.443289,-0.287605,-0.005722,+0.531844,-0.231396,-0.121289,+0.587387,-0.253475,-0.081797,+0.756541,-0.195164,-0.137969,+0.728011,-0.167673,-0.156573,+0.609388,-0.145917,-0.169029,+0.697600,-0.077776,-0.214247,+0.622586,-0.076873,-0.214971,+0.696301,-0.002341,-0.233135,+0.622859,-0.002730,-0.213526,+0.691267,-0.003136,-0.192628,+0.762731,-0.056136,-0.201222,+0.763806,-0.114589,-0.166192,+0.770723,-0.155145,-0.129632,+0.791738,-0.183611,-0.058705,+0.847012,-0.165562,+0.001980,+0.833386,-0.220084,+0.019914,+0.768935,-0.255730,+0.090306,+0.670782,-0.255594,+0.113833,+0.663389,-0.226380,+0.212655,+0.617740,-0.003367,-0.195342,+0.799680,-0.029743,-0.210508,+0.827180,-0.003818,-0.194783,+0.873636,-0.004116,-0.157907,+0.931268,-0.031280,-0.184555,+0.889476,-0.059885,-0.184448,+0.841330,-0.135333,-0.164332,+0.878200,-0.085574,-0.170948,+0.925547,-0.163833,-0.094170,+0.897114,-0.138444,-0.104250,+0.945975,-0.083497,-0.084934,+0.979607,-0.004433,-0.146642,+0.985872,-0.150715,+0.032650,+0.884111,-0.135892,-0.035520,+0.945455,-0.070612,+0.036849,+0.975733,-0.004458,-0.042526,+1.015670,-0.004249,+0.046042,+1.003240,-0.086969,+0.133224,+0.947633,-0.003873,+0.161605,+0.970499,-0.125544,+0.140012,+0.917678,-0.125651,+0.250246,+0.857602,-0.003127,+0.284070,+0.878870,-0.159174,+0.125726,+0.888878,-0.183807,+0.196970,+0.844480,-0.159890,+0.291736,+0.732480,-0.199495,+0.207230,+0.779864,-0.206182,+0.164608,+0.693257,-0.186315,+0.160689,+0.817193,-0.192827,+0.166706,+0.782271,-0.175112,+0.110008,+0.860621,-0.161022,+0.057420,+0.855111,-0.172319,+0.036155,+0.816189,-0.190318,+0.064083,+0.760605,-0.195072,+0.129179,+0.731104,-0.203126,+0.410287,+0.680536,-0.216677,+0.309274,+0.642272,-0.241515,+0.311485,+0.587832,-0.002209,+0.366663,+0.749413,-0.088230,+0.396265,+0.678635,-0.170147,+0.109517,+0.840784,-0.160521,+0.067766,+0.830650,-0.181546,+0.139805,+0.812146,-0.180495,+0.148568,+0.776087,-0.180255,+0.129125,+0.744192,-0.186298,+0.078308,+0.769352,-0.167622,+0.060539,+0.806675,-0.189876,+0.102760,+0.802582,-0.108340,+0.455446,+0.657174,-0.241585,+0.527592,+0.669296,-0.265676,+0.513366,+0.634594,-0.203073,+0.478550,+0.581526,-0.266772,+0.642330,+0.602061,-0.216961,+0.564846,+0.535435,-0.202210,+0.525495,+0.475944,-0.193888,+0.467925,+0.520606,-0.265837,+0.757267,+0.500933,-0.240306,+0.653440,+0.463215,-0.309239,+0.776868,+0.304726,-0.271009,+0.683094,+0.382018,-0.312111,+0.671099,+0.286687,-0.268791,+0.624342,+0.377231,-0.302457,+0.533996,+0.360289,-0.263656,+0.529310,+0.412564,-0.282311,+0.415167,+0.447666,-0.239201,+0.442096,+0.495604,-0.220043,+0.569026,+0.445877,-0.001263,+0.395631,+0.602029,-0.057345,+0.442535,+0.572224,-0.088927,+0.506333,+0.529106,-0.125738,+0.535076,+0.612913,-0.126251,+0.577170,+0.483159,-0.149594,+0.611520,+0.557731,-0.163188,+0.660791,+0.491080,-0.172482,+0.663387,+0.415416,-0.160464,+0.591710,+0.370659,-0.156445,+0.536396,+0.378302,-0.136496,+0.444358,+0.425226,-0.095564,+0.373768,+0.473659,-0.104146,+0.315912,+0.498104,-0.000496,+0.384194,+0.473817,-0.000183,+0.297770,+0.401486,-0.129042,+0.270145,+0.434495,+0.000100,+0.272963,+0.349138,-0.113060,+0.236984,+0.385554,+0.007260,+0.016311,-0.883396,+0.007865,+0.122104,-0.956137,-0.032842,+0.115282,-0.953252,-0.089115,+0.108449,-0.950317,-0.047440,+0.014729,-0.882756,-0.104458,+0.013137,-0.882070,-0.086439,-0.584866,-0.608343,-0.115026,-0.662605,-0.436732,-0.071683,-0.665372,-0.606385,-0.257884,-0.665381,-0.658052,-0.272542,-0.665381,-0.592063,-0.371322,-0.665382,-0.353620,-0.372362,-0.665381,-0.224420,-0.335166,-0.665380,-0.078623,-0.225999,-0.665375,-0.038981,-0.106719,-0.665374,-0.186351,-0.081749,-0.665372,-0.292554,+0.006943,-0.091505,-0.858354,+0.006117,-0.280985,-0.769967,+0.004495,-0.502360,-0.559799,-0.198638,-0.302135,-0.845816,-0.237395,-0.542544,-0.587188,-0.270001,-0.279489,-0.669861,-0.134547,-0.119852,-0.959004,-0.052088,-0.122463,-0.944549,-0.124463,-0.293508,-0.899566,-0.047616,-0.289643,-0.879292,-0.168595,-0.529132,-0.654931,-0.099793,-0.515719,-0.645873,-0.186168,-0.605282,-0.724690,-0.112970,-0.583097,-0.707469,-0.108152,-0.665375,-0.700408,-0.183019,-0.665378,-0.717630,-0.349529,-0.334459,-0.511985,-0.141182,-0.437705,-0.798194,-0.212670,-0.448725,-0.737447,-0.261111,-0.414945,-0.613835,-0.077364,-0.431480,-0.778113,+0.005174,-0.425277,-0.651592,+0.089236,-0.431732,-0.777093,+0.271006,-0.415749,-0.610577,+0.223981,-0.449384,-0.734774,+0.153275,-0.438150,-0.796391,+0.358414,-0.335529,-0.507649,+0.193434,-0.665946,-0.715325,+0.118363,-0.665717,-0.699021,+0.123515,-0.583454,-0.706020,+0.196851,-0.605860,-0.722345,+0.109788,-0.516035,-0.644590,+0.178656,-0.529656,-0.652804,+0.061157,-0.289807,-0.878626,+0.138234,-0.293905,-0.897958,+0.066933,-0.122643,-0.943820,+0.149571,-0.120281,-0.957264,+0.280989,-0.280321,-0.666487,+0.246581,-0.543275,-0.584224,+0.211720,-0.302754,-0.843303,+0.086966,-0.665627,-0.291520,+0.110634,-0.665702,-0.185021,+0.228099,-0.666061,-0.036201,+0.337743,-0.666396,-0.074503,+0.376722,-0.666513,-0.219833,+0.377265,-0.666513,-0.349036,+0.281411,-0.666217,-0.588670,+0.267564,-0.666174,-0.654834,+0.080745,-0.665602,-0.605452,+0.122016,-0.662963,-0.435280,+0.095767,-0.585141,-0.607228,+0.118944,+0.012799,-0.880702,+0.061944,+0.014564,-0.882086,+0.104725,+0.108156,-0.949130,+0.048513,+0.115159,-0.952753,+0.112696,+0.236643,+0.386937,+0.128177,+0.269757,+0.436071,+0.102643,+0.315600,+0.499370,+0.094535,+0.373481,+0.474824,+0.136270,+0.443946,+0.426895,+0.157071,+0.535923,+0.380222,+0.161350,+0.591224,+0.372630,+0.173035,+0.662865,+0.417531,+0.162808,+0.660299,+0.493077,+0.148250,+0.611070,+0.559555,+0.125719,+0.576790,+0.484702,+0.123489,+0.534699,+0.614440,+0.087621,+0.506066,+0.530188,+0.055321,+0.442365,+0.572915,+0.219936,+0.568361,+0.448571,+0.238099,+0.441375,+0.498528,+0.281711,+0.414315,+0.451121,+0.263833,+0.528513,+0.415794,+0.303284,+0.533081,+0.363998,+0.269687,+0.623528,+0.380528,+0.314255,+0.670153,+0.290524,+0.272023,+0.682273,+0.385343,+0.311480,+0.775931,+0.308527,+0.240239,+0.652714,+0.466159,+0.265619,+0.756464,+0.504187,+0.192562,+0.467341,+0.522972,+0.201605,+0.524885,+0.478417,+0.215743,+0.564193,+0.538084,+0.264969,+0.641527,+0.605317,+0.201031,+0.477940,+0.584002,+0.263086,+0.512567,+0.637832,+0.238615,+0.526867,+0.672237,+0.105309,+0.455123,+0.658482,+0.183993,+0.102195,+0.804872,+0.161563,+0.060042,+0.808692,+0.180748,+0.077754,+0.771600,+0.175168,+0.128588,+0.746368,+0.175075,+0.148030,+0.778264,+0.175658,+0.139265,+0.814333,+0.154191,+0.067291,+0.832578,+0.163818,+0.109013,+0.842830,+0.084760,+0.396004,+0.679695,+0.238888,+0.310760,+0.590775,+0.213380,+0.308625,+0.644905,+0.199666,+0.409678,+0.683003,+0.190143,+0.128597,+0.733463,+0.184833,+0.063516,+0.762902,+0.166070,+0.035644,+0.818261,+0.154361,+0.056943,+0.857042,+0.168542,+0.109489,+0.862725,+0.187387,+0.166131,+0.784599,+0.180428,+0.160135,+0.819438,+0.201823,+0.163991,+0.695756,+0.194206,+0.206635,+0.782275,+0.155438,+0.291260,+0.734412,+0.177696,+0.196424,+0.846693,+0.152305,+0.125256,+0.890786,+0.119546,+0.249876,+0.859104,+0.118369,+0.139643,+0.919173,+0.079410,+0.132973,+0.948652,+0.062419,+0.036648,+0.976547,+0.127847,-0.035919,+0.947070,+0.143624,+0.032206,+0.885913,+0.074888,-0.085173,+0.980577,+0.130184,-0.104656,+0.947620,+0.156201,-0.094653,+0.899074,+0.077366,-0.171194,+0.926545,+0.127722,-0.164729,+0.879810,+0.052670,-0.184618,+0.842019,+0.023477,-0.184638,+0.889811,+0.022626,-0.210587,+0.827500,+0.223089,+0.211976,+0.620493,+0.251444,+0.113067,+0.666494,+0.251419,+0.089540,+0.673887,+0.214360,+0.019258,+0.771595,+0.158999,+0.001490,+0.835374,+0.176696,-0.059249,+0.849218,+0.148696,-0.130091,+0.793599,+0.108290,-0.166528,+0.772088,+0.049820,-0.201382,+0.764454,+0.071341,-0.215195,+0.697209,+0.073148,-0.214475,+0.623510,+0.140502,-0.169461,+0.699354,+0.163374,-0.157073,+0.611416,+0.189466,-0.138550,+0.730366,+0.247593,-0.082554,+0.759610,+0.227468,-0.121982,+0.590197,+0.284702,-0.006586,+0.535347,+0.275741,+0.125287,+0.446676,+0.266650,+0.192594,+0.506044,+0.300086,+0.053287,+0.629620,+0.055450,-0.663935,+0.375065,+0.122854,-0.664138,+0.482323,+0.046520,-0.531571,+0.391918,+0.024824,-0.568450,+0.275106,+0.053855,-0.663931,+0.328224,+0.112829,-0.453549,+0.305788,+0.131265,-0.510617,+0.080746,+0.061174,-0.430716,-0.042710,+0.341019,-0.532887,-0.208150,+0.347705,-0.623533,-0.081139,+0.238040,-0.610732,-0.038037,+0.211764,-0.514274,-0.132078,+0.120605,-0.600219,-0.186856,+0.096985,-0.584476,-0.293357,+0.127621,-0.581941,-0.437170,+0.165902,-0.477425,-0.291453,+0.077720,-0.417975,-0.220519,+0.320892,-0.506363,-0.320874,+0.248214,-0.465684,-0.239842,+0.118764,-0.383338,-0.187114,+0.118816,-0.430106,-0.123307,+0.094131,-0.419464,-0.044777,+0.274526,-0.261706,+0.005110,+0.259842,-0.283292,-0.003185,+0.222861,-0.340431,-0.038210,+0.204445,-0.664380,+0.513353,+0.259286,-0.664547,+0.471281,+0.185402,-0.476020,+0.421718,+0.279163,-0.664604,+0.417328,+0.277157,-0.528122,+0.400208,+0.183069,-0.509812,+0.329995,+0.282599,-0.429210,+0.059242,+0.254816,-0.664541,+0.290687,+0.271436,-0.567707,+0.263966,+0.386561,-0.625221,-0.216870,+0.387086,-0.630883,-0.346073,+0.380021,-0.596021,-0.318679,+0.291269,-0.619007,-0.585707,+0.339280,-0.571198,-0.461946,+0.400045,-0.489778,-0.422640,+0.406817,-0.314349,-0.371230,+0.300588,-0.281718,-0.170549,+0.290866,-0.277304,-0.061905,+0.187735,-0.241545,+0.509437,+0.188032,-0.287569,+0.424234,+0.227520,-0.373262,+0.293102,+0.266526,-0.273650,+0.039597,+0.291592,-0.291676,+0.111386,+0.291914,-0.122741,+0.422683,+0.297574,-0.156119,+0.373368,+0.286603,-0.232731,+0.027162,+0.364663,-0.201399,+0.206850,+0.353855,-0.132408,+0.149228,+0.282208,-0.019715,+0.314960,+0.331187,-0.099266,+0.092701,+0.375463,-0.093120,-0.006467,+0.375917,-0.101236,-0.154882,+0.466635,-0.094416,-0.305669,+0.455805,-0.119881,-0.460632,+0.277465,-0.604242,-0.651871,+0.261022,-0.551176,-0.554667,+0.093627,+0.258494,-0.920589,+0.114248,+0.310608,-0.798070,+0.144232,+0.211434,-0.835001,+0.119916,+0.176940,-0.951159,+0.184061,+0.101854,-0.918220,+0.092431,+0.276521,-0.738231,+0.133504,+0.218403,-0.758602,+0.194987,+0.097655,-0.812476,+0.185542,+0.011005,-0.879202,+0.230315,-0.127450,-0.884202,+0.260471,+0.255056,-0.624378,+0.351567,-0.042194,-0.663976,+0.253742,+0.323524,-0.433716,+0.411612,+0.132299,-0.438264,+0.270513,+0.356530,-0.289984,+0.422146,+0.162819,-0.273130,+0.164724,+0.237490,+0.208912,+0.253806,+0.092900,+0.240640,+0.203608,+0.284597,+0.096223,+0.241006,+0.343093,-0.171396,+0.356076,+0.149288,-0.143443,+0.337656,+0.131992,+0.066374,}; + static const int tris[]={126,134,133,342,138,134,133,134,138,126,342,134,312,316,317,169,163,162,312,317,319,312,319,318,169,162,164,169,168,163,312,314,315,169,164,165,169,167,168,312,315,316,312,313,314,169,165,166,169,166,167,312,318,313,308,304,305,308,305,306,179,181,188,177,173,175,177,175,176,302,293,300,322,294,304,188,176,175,188,175,179,158,177,187,305,293,302,305,302,306,322,304,308,188,181,183,158,173,177,293,298,300,304,294,296,304,296,305,185,176,188,185,188,183,187,177,176,187,176,185,305,296,298,305,298,293,436,432, 28,436, 28, 23,434,278,431, 30,208,209, 30,209, 29, 19, 20, 24,208,207,211,208,211,209, 19,210,212,433,434,431,433,431,432,433,432,436,436,437,433,277,275,276,277,276,278,209,210, 25, 21, 26, 24, 21, 24, 20, 25, 26, 27, 25, 27, 29,435,439,277,439,275,277,432,431, 30,432, 30, 28,433,437,438,433,438,435,434,277,278, 24, 25,210, 24, 26, 25, 29, 27, 28, 29, 28, 30, 19, 24,210,208, 30,431,208,431,278,435,434,433,435,277,434, 25, 29,209, 27, 22, 23, 27, 23, 28, 26, 22, 27, 26, 21, 22,212,210,209,212,209,211,207,208,278,207,278,276,439,435,438, 12, 9, 10, 12, 10, 13, 2, 3, 5, 2, 5, 4, 16, 13, 14, 16, 14, 17, 22, 21, 16, 13, 10, 11, 13, 11, 14, 1, 0, 3, 1, 3, 2, 15, 12, 16, 19, 18, 15, 19, 15, 16, 19, 16, 20, 9, 1, 2, 9, 2, 10, 3, 7, 8, 3, 8, 5, 16, 17, 23, 16, 23, 22, 21, 20, 16, 10, 2, 4, 10, 4, 11, 0, 6, 7, 0, 7, 3, 12, 13, 16,451,446,445,451,445,450,442,440,439,442,439,438,442,438,441,421,420,422,412,411,426,412,426,425,408,405,407,413, 67, 68,413, 68,414,391,390,412, 80,384,386,404,406,378,390,391,377,390,377, 88,400,415,375,398,396,395,398,395,371,398,371,370,112,359,358,112,358,113,351,352,369,125,349,348,345,343,342,342,340,339,341,335,337,328,341,327,331,323,333,331,322,323,327,318,319,327,319,328,315,314,324,302,300,301,302,301,303,320,311,292,285,284,289,310,307,288,310,288,290,321,350,281,321,281,282,423,448,367,272,273,384,272,384,274,264,265,382,264,382,383,440,442,261,440,261,263,252,253,254,252,254,251,262,256,249,262,249,248,228,243,242,228, 31,243,213,215,238,213,238,237, 19,212,230,224,225,233,224,233,231,217,218, 56,217, 56, 54,217,216,239,217,239,238,217,238,215,218,217,215,218,215,214, 6,102,206,186,199,200,197,182,180,170,171,157,201,200,189,170,190,191,170,191,192,175,174,178,175,178,179,168,167,155,122,149,158,122,158,159,135,153,154,135,154,118,143,140,141,143,141,144,132,133,136,130,126,133,124,125,127,122,101,100,122,100,121,110,108,107,110,107,109, 98, 99, 97, 98, 97, 64, 98, 64, 66, 87, 55, 57, 83, 82, 79, 83, 79, 84, 78, 74, 50, 49, 71, 41, 49, 41, 37, 49, 37, 36, 58, 44, 60, 60, 59, 58, 51, 34, 33, 39, 40, 42, 39, 42, 38,243,240, 33,243, 33,229, 39, 38, 6, 44, 46, 40, 55, 56, 57, 64, 62, 65, 64, 65, 66, 41, 71, 45, 75, 50, 51, 81, 79, 82, 77, 88, 73, 93, 92, 94, 68, 47, 46, 96, 97, 99, 96, 99, 95,110,109,111,111,112,110,114,113,123,114,123,124,132,131,129,133,137,136,135,142,145,145,152,135,149,147,157,157,158,149,164,150,151,153,163,168,153,168,154,185,183,182,185,182,184,161,189,190,200,199,191,200,191,190,180,178,195,180,195,196,102,101,204,102,204,206, 43, 48,104, 43,104,103,216,217, 54,216, 54, 32,207,224,231,230,212,211,230,211,231,227,232,241,227,241,242,235,234,241,235,241,244,430,248,247,272,274,253,272,253,252,439,260,275,225,224,259,225,259,257,269,270,407,269,407,405,270,269,273,270,273,272,273,269,268,273,268,267,273,267,266,273,266,265,273,265,264,448,279,367,281,350,368,285,286,301,290,323,310,290,311,323,282,281,189,292,311,290,292,290,291,307,306,302,307,302,303,316,315,324,316,324,329,331,351,350,330,334,335,330,335,328,341,337,338,344,355,354,346,345,348,346,348,347,364,369,352,364,352,353,365,363,361,365,361,362,376,401,402,373,372,397,373,397,400,376, 92,377,381,378,387,381,387,385,386, 77, 80,390,389,412,416,417,401,403,417,415,408,429,430,419,423,418,427,428,444,427,444,446,437,436,441,450,445, 11,450, 11, 4,447,449, 5,447, 5, 8,441,438,437,425,426,451,425,451,452,417,421,415,408,407,429,399,403,400,399,400,397,394,393,416,389,411,412,386,383,385,408,387,378,408,378,406,377,391,376, 94,375,415,372,373,374,372,374,370,359,111,360,359,112,111,113,358,349,113,349,123,346,343,345,343,340,342,338,336,144,338,144,141,327,341,354,327,354,326,331,350,321,331,321,322,314,313,326,314,326,325,300,298,299,300,299,301,288,287,289,189,292,282,287,288,303,284,285,297,368,280,281,448,447,279,274,226,255,267,268,404,267,404,379,429,262,430,439,440,260,257,258,249,257,249,246,430,262,248,234,228,242,234,242,241,237,238,239,237,239,236, 15, 18,227, 15,227,229,222,223, 82,222, 82, 83,214,215,213,214,213, 81, 38,102, 6,122,159,200,122,200,201,174,171,192,174,192,194,197,193,198,190,170,161,181,179,178,181,178,180,166,156,155,163,153,152,163,152,162,120,156,149,120,149,121,152,153,135,140,143,142,135,131,132,135,132,136,130,129,128,130,128,127,100,105,119,100,119,120,106,104,107,106,107,108, 91, 95, 59, 93, 94, 68, 91, 89, 92, 76, 53, 55, 76, 55, 87, 81, 78, 79, 74, 73, 49, 69, 60, 45, 58, 62, 64, 58, 64, 61, 53, 31, 32, 32, 54, 53, 42, 43, 38, 35, 36, 0, 35, 0, 1, 34, 35, 1, 34, 1, 9, 44, 40, 41, 44, 41, 45, 33,240, 51, 63, 62, 58, 63, 58, 59, 45, 71, 70, 76, 75, 51, 76, 51, 52, 86, 85, 84, 86, 84, 87, 89, 72, 73, 89, 73, 88, 91, 92, 96, 91, 96, 95, 72, 91, 60, 72, 60, 69,104,106,105,119,105,117,119,117,118,124,127,128,117,116,129,117,129,131,118,117,131,135,140,142,146,150,152,146,152,145,149,122,121,166,165,151,166,151,156,158,172,173,161,160,189,199,198,193,199,193,191,204,201,202,178,174,194,200,159,186,109, 48, 67, 48,107,104,216, 32,236,216,236,239,223,214, 81,223, 81, 82, 33, 12, 15, 32,228,234, 32,234,236,240, 31, 52,256,255,246,256,246,249,258,263,248,258,248,249,275,260,259,275,259,276,207,276,259,270,271,429,270,429,407,413,418,366,413,366,365,368,367,279,368,279,280,303,301,286,303,286,287,283,282,292,283,292,291,320,292,189,298,296,297,298,297,299,318,327,326,318,326,313,329,330,317,336,333,320,326,354,353,334,332,333,334,333,336,342,339,139,342,139,138,345,342,126,347,357,356,369,368,351,363,356,357,363,357,361,366,367,368,366,368,369,375,373,400, 92, 90,377,409,387,408,386,385,387,386,387,388,412,394,391,396,398,399,408,406,405,415,421,419,415,419,414,425,452,448,425,448,424,444,441,443,448,452,449,448,449,447,446,444,443,446,443,445,250,247,261,250,261,428,421,422,423,421,423,419,427,410,250,417,403,401,403,402,401,420,392,412,420,412,425,420,425,424,386,411,389,383,382,381,383,381,385,378,379,404,372,371,395,372,395,397,371,372,370,361,359,360,361,360,362,368,350,351,349,347,348,356,355,344,356,344,346,344,341,340,344,340,343,338,337,336,328,335,341,324,352,351,324,351,331,320,144,336,314,325,324,322,308,309,310,309,307,287,286,289,203,280,279,203,279,205,297,295,283,297,283,284,447,205,279,274,384, 80,274, 80,226,266,267,379,266,379,380,225,257,246,225,246,245,256,254,253,256,253,255,430,247,250,226,235,244,226,244,245,232,233,244,232,244,241,230, 18, 19, 32, 31,228,219,220, 86,219, 86, 57,226,213,235,206, 7, 6,122,201,101,201,204,101,180,196,197,170,192,171,200,190,189,194,193,195,183,181,180,183,180,182,155,154,168,149,156,151,149,151,148,155,156,120,145,142,143,145,143,146,136,137,140,133,132,130,128,129,116,100,120,121,110,112,113,110,113,114, 66, 65, 63, 66, 63, 99, 66, 99, 98, 96, 46, 61, 89, 88, 90, 86, 87, 57, 80, 78, 81, 72, 69, 49, 67, 48, 47, 67, 47, 68, 56, 55, 53, 50, 49, 36, 50, 36, 35, 40, 39, 41,242,243,229,242,229,227, 6, 37, 39, 42, 47, 48, 42, 48, 43, 61, 46, 44, 45, 70, 69, 69, 70, 71, 69, 71, 49, 74, 78, 77, 83, 84, 85, 73, 74, 77, 93, 96, 92, 68, 46, 93, 95, 99, 63, 95, 63, 59,115,108,110,115,110,114,125,126,127,129,130,132,137,133,138,137,138,139,148,146,143,148,143,147,119,118,154,161,147,143,165,164,151,158,157,171,158,171,172,159,158,187,159,187,186,194,192,191,194,191,193,189,202,201,182,197,184,205, 8, 7, 48,109,107,218,219, 57,218, 57, 56,207,231,211,232,230,231,232,231,233, 53, 52, 31,388,411,386,409,430,250,262,429,254,262,254,256,442,444,428,273,264,383,273,383,384,429,271,251,429,251,254,413,365,362, 67,413,360,282,283,295,285,301,299,202,281,280,284,283,291,284,291,289,320,189,160,308,306,307,307,309,308,319,317,330,319,330,328,353,352,324,332,331,333,340,341,338,354,341,344,349,358,357,349,357,347,364,355,356,364,356,363,364,365,366,364,366,369,374,376,402,375, 92,373, 77,389,390,382,380,381,389, 77,386,393,394,412,393,412,392,401,394,416,415,400,403,411,410,427,411,427,426,422,420,424,247,248,263,247,263,261,445,443, 14,445, 14, 11,449,450, 4,449, 4, 5,443,441, 17,443, 17, 14,436, 23, 17,436, 17,441,424,448,422,448,423,422,414,419,418,414,418,413,406,404,405,399,397,395,399,395,396,420,416,392,388,410,411,386,384,383,390, 88, 77,375, 94, 92,415,414, 68,415, 68, 94,370,374,402,370,402,398,361,357,358,361,358,359,125,348,126,346,344,343,340,338,339,337,335,334,337,334,336,325,353,324,324,331,332,324,332,329,323,322,309,323,309,310,294,295,297,294,297,296,289,286,285,202,280,203,288,307,303,282,295,321, 67,360,111,418,423,367,418,367,366,272,252,251,272,251,271,272,271,270,255,253,274,265,266,380,265,380,382,442,428,261,440,263,258,440,258,260,409,250,410,255,226,245,255,245,246, 31,240,243,236,234,235,236,235,237,233,225,245,233,245,244,220,221, 85,220, 85, 86, 81,213,226, 81,226, 80, 7,206,205,186,184,198,186,198,199,204,203,205,204,205,206,195,193,196,171,174,172,173,174,175,173,172,174,155,167,166,160,161,143,160,143,144,119,154,155,148,151,150,148,150,146,140,137,139,140,139,141,127,126,130,114,124,128,114,128,115,117,105,106,117,106,116,104,105,100,104,100,103, 59, 60, 91, 97, 96, 61, 97, 61, 64, 91, 72, 89, 87, 84, 79, 87, 79, 76, 78, 80, 77, 49, 50, 74, 60, 44, 45, 61, 44, 58, 51, 50, 35, 51, 35, 34, 39, 37, 41, 33, 34, 9, 33, 9, 12, 0, 36, 37, 0, 37, 6, 40, 46, 47, 40, 47, 42, 53, 54, 56, 65, 62, 63, 72, 49, 73, 79, 78, 75, 79, 75, 76, 52, 53, 76, 92, 89, 90, 96, 93, 46,102,103,100,102,100,101,116,106,108,116,108,115,123,125,124,116,115,128,118,131,135,140,135,136,148,147,149,120,119,155,164,162,152,164,152,150,157,147,161,157,161,170,186,187,185,186,185,184,193,197,196,202,203,204,194,195,178,198,184,197, 67,111,109, 38, 43,103, 38,103,102,214,223,222,214,222,221,214,221,220,214,220,219,214,219,218,213,237,235,221,222, 83,221, 83, 85, 15,229, 33,227, 18,230,227,230,232, 52, 51,240, 75, 78, 50,408,430,409,260,258,257,260,257,259,224,207,259,268,269,405,268,405,404,413,362,360,447, 8,205,299,297,285,189,281,202,290,288,289,290,289,291,322,321,295,322,295,294,333,323,311,333,311,320,317,316,329,320,160,144,353,325,326,329,332,334,329,334,330,339,338,141,339,141,139,348,345,126,347,356,346,123,349,125,364,353,354,364,354,355,365,364,363,376,391,394,376,394,401, 92,376,374, 92,374,373,377, 90, 88,380,379,378,380,378,381,388,387,409,388,409,410,416,393,392,399,398,402,399,402,403,250,428,427,421,417,416,421,416,420,426,427,446,426,446,451,444,442,441,452,451,450,452,450,449,}; + enum { NUM_VERTS = countof(verts) / 3, NUM_TRIS = countof(tris) / 3 }; + + // Copy the geometry from the arrays of data into the arrays which we send to the reduction routine + for(int i = 0; i < NUM_VERTS; i++ ) { + const double *vp = &verts[i*3]; + array_push(m->in_vertex3, vec3(vp[0],vp[1],vp[2])); + } + for(int i = 0; i < NUM_TRIS; i++ ) { + const int *td = &tris[i*3]; + array_push(m->in_index3, vec3i(td[0],td[1],td[2])); + } + + int tri_n = array_count(m->in_index3); + int vert_n = array_count(m->in_vertex3); + int vert_stride = sizeof(float) * 3; + array(int) permutation = 0; + array_resize(m->lod_collapse_map, vert_n); + array_resize(permutation, vert_n); + ProgressiveMesh(vert_n, vert_stride, (const float *)m->in_vertex3, tri_n, (const int *)m->in_index3, m->lod_collapse_map, permutation); + // PermuteVertices { + ASSERT(array_count(permutation) == array_count(m->in_vertex3)); + // rearrange the vertex Array + array(vec3) tmp = 0; + for(int i = 0; i < array_count(m->in_vertex3); i++) array_push(tmp, m->in_vertex3[i]); + for(int i = 0; i < array_count(m->in_vertex3); i++) m->in_vertex3[permutation[i]]=tmp[i]; + // update the changes in the entries in the triangle Array + for (int i = 0; i < array_count(m->in_index3); i++) { + m->in_index3[i].x = permutation[m->in_index3[i].x]; + m->in_index3[i].y = permutation[m->in_index3[i].y]; + m->in_index3[i].z = permutation[m->in_index3[i].z]; + } + array_free(tmp); + // } PermuteVertices + array_free(permutation); +} + +int main() { + window_create(0.75, 0); + window_color(PURPLE); + + camera_t cam = camera(); + cam.speed /= 4; + cam.position = vec3(1.667,0.503,2.417); + camera_lookat(&cam, vec3(0,0,0)); + + mesh_t m = mesh(); + InitModel(&m); + + float lo_detail=0.25f; + float hi_detail=1.00f; + float morph=0.75f; + + while( window_swap() && !input(KEY_ESC) ) { + if( input(KEY_LALT) && input_down(KEY_Z) ) window_record(__FILE__ ".mp4"); + + // fps camera + if( input(GAMEPAD_CONNECTED) ) { + vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 ); + vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 ); + vec2 mouse = scale2(vec2(filtered_rpad.x, filtered_rpad.y), 1.0f); + vec3 wasdec = scale3(vec3(filtered_lpad.x, input(GAMEPAD_LT) - input(GAMEPAD_RT), filtered_lpad.y), 1.0f); + camera_move(&cam, wasdec.x,wasdec.y,wasdec.z); + camera_fps(&cam, mouse.x,mouse.y); + window_cursor( true ); + } else { + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z); + camera_fps(&cam, mouse.x,mouse.y); + window_cursor( !active ); + } + + profile("LOD generation") { + ReduceModel(&m, lo_detail, hi_detail, morph); + mesh_area(&m); + } + + DrawModel(&m); + + if( ui_panel("LODs", PANEL_OPEN) ) { + ui_label(va("Polys: %d/%d Vertices: %d/%d", array_count(m.out_index3), array_count(m.in_index3), (int)(lo_detail * array_count(m.in_vertex3)), (int)(hi_detail * array_count(m.in_vertex3)))); + if(ui_slider("Lo detail", &lo_detail)) { lo_detail = clampf(lo_detail, 0.01f, 1.0f); if(lo_detail > hi_detail) hi_detail = lo_detail; } + if(ui_slider("Hi detail", &hi_detail)) { hi_detail = clampf(hi_detail, 0.01f, 1.0f); if(hi_detail < lo_detail) lo_detail = hi_detail; } + ui_slider("Morph", &morph); + ui_panel_end(); + } + } +} diff --git a/demos/05-scene.c b/demos/05-scene.c new file mode 100644 index 0000000..fde1320 --- /dev/null +++ b/demos/05-scene.c @@ -0,0 +1,230 @@ +// scene demo +// - rlyeh, public domain + +#include "fwk.h" + +int main() { + // options + bool do_twosided = 1; + bool do_wireframe = 0; + bool do_billboard_x = 0, do_billboard_y = 0, do_billboard_z = 0; + + // window (80% sized, MSAA x4 flag) + window_create(80, WINDOW_MSAA4); + window_title(__FILE__); + + // load all postfx files in all subdirs + fx_load("fx**.fs"); + + // scene loading + #define SCENE(...) #__VA_ARGS__ + const char *my_scene = SCENE([ + { + skybox: 'cubemaps/stardust/', + }, + { + position:[-5.0,-2.0,2.0], + rotation: [90.0,0.0,180.0], + scale:0.20, + //anchor/pivot:[], + // vertex:'p3 t2', + mesh:'models/witch/witch.obj', + texture:'models/witch/witch_diffuse.tga.png', +// swapzy:true, + flipuv:false, + }, + { + position:[-5.0,-2.0,2.0], + rotation: [90.0,0.0,180.0], + scale:2.20, + //anchor/pivot:[], + // vertex:'p3 t2', + mesh:'models/witch/witch_object.obj', + texture:'models/witch/witch_object_diffuse.tga.png', +// swapzy:true, + flipuv:false, + }, + ]); + int num_spawned = scene_merge(my_scene); + object_t *obj1 = scene_index(0); + object_t *obj2 = scene_index(1); + + // manual spawn & loading + model_t m1 = model("kgirl/kgirls01.fbx", 0); //MODEL_NO_ANIMS); + texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_RGB); + object_t* obj3 = scene_spawn(); + object_model(obj3, m1); + object_diffuse(obj3, t1); + object_scale(obj3, vec3(3,3,3)); + object_move(obj3, vec3(-10,0,-10)); + object_pivot(obj3, vec3(-90+180,180,0)); + + // camera + camera_t cam = camera(); + cam.speed = 0.2f; + + // demo loop + while (window_swap()) + { + // input + if( input_down(KEY_ESC) ) break; + + // fps camera + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + window_cursor( !active ); + + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z); + camera_fps(&cam, mouse.x,mouse.y); + + // queue model scale bounces + float t = fmod(window_time(), 0.3) / 0.3; + float s = 0.01f * ease_ping_pong(t, ease_in_cubic,ease_out_cubic); + object_scale(obj1, vec3(0.20f - s,0.20f + s,0.20f - s)); + object_scale(obj2, vec3(0.20f - s,0.20f + s,0.20f - s)); + + // queue model billboard + object_billboard(obj1, (do_billboard_x << 2)|(do_billboard_y << 1)|(do_billboard_z << 0)); + object_billboard(obj2, (do_billboard_x << 2)|(do_billboard_y << 1)|(do_billboard_z << 0)); + + // queue model rotation + //object_rotate(obj3, vec3(0,1*window_time() * 20,0)); + + // flush render scene (background objects: skybox) + profile("Scene background") { + scene_render(SCENE_BACKGROUND); + } + + // queue debug drawcalls + profile("Debugdraw") { + ddraw_ground(0); + ddraw_color(YELLOW); + ddraw_text(vec3(+1,+1,-1), 0.04f, va("(%f,%f,%f)", cam.position.x,cam.position.y,cam.position.z)); + ddraw_color(YELLOW); + ddraw_flush(); + } + + // apply post-fxs from here + fx_begin(); + + // render scene (foreground objects) with post-effects + profile("Scene foreground") { + int scene_flags = 0; + scene_flags |= do_wireframe ? SCENE_WIREFRAME : 0; + scene_flags |= do_twosided ? 0 : SCENE_CULLFACE; + scene_render(SCENE_FOREGROUND | scene_flags); + } + + profile("Skeletal update") if(!window_has_pause()) { + float delta = window_delta() * 30 ; // 30fps anim + m1.curframe = model_animate(m1, m1.curframe + delta); + + ddraw_text(vec3(-10,5,-10), 0.05, va("Frame: %.1f", m1.curframe)); + } + + // post-fxs end here + fx_end(); + + // queue ui + if( ui_panel("Camera", 0)) { + if( ui_float("Speed", &cam.speed) ) {} + if( ui_float3("Position", &cam.position.x) ) {} + ui_panel_end(); + } + if( ui_panel("Scene", 0)) { + if(ui_toggle("Billboard X", &do_billboard_x)) {} + if(ui_toggle("Billboard Y", &do_billboard_y)) {} + if(ui_toggle("Billboard Z", &do_billboard_z)) {} + if(ui_separator()) {} + if(ui_bool("Wireframe", &do_wireframe)) {} + if(ui_bool("Two sided", &do_twosided)) {} + ui_panel_end(); + } + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, fx_enabled(i) ^ 1); + } + ui_panel_end(); + } + } +} + +#if 0 + +// ---------------------------------------------------------------------------- +// material demo +// - rlyeh, public domain +// +// @todo: object_print(obj, ""); + +// create camera +camera_t cam = camera(); +// load video, RGB texture, no audio +video_t *v = video( "bjork-all-is-full-of-love.mp4", VIDEO_RGB | VIDEO_NO_AUDIO ); video_seek(v, 30); +// load texture +texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_RGB); +texture_t t2 = texture("matcaps/material3", 0); +// load model +model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS); +model_t m2 = model("suzanne.obj", MODEL_NO_ANIMATIONS|MODEL_MATCAPS); + +// spawn object1 (diffuse) +object_t* obj1 = scene_spawn(); +object_model(obj1, m1); +object_diffuse(obj1, t1); +object_scale(obj1, vec3(3,3,3)); +object_move(obj1, vec3(-10+5*0,0,-10)); +object_pivot(obj1, vec3(0,90,0)); + +// spawn object2 (matcap) +object_t* obj2 = scene_spawn(); +object_model(obj2, m2); +object_diffuse(obj2, t2); +object_scale(obj2, vec3(3,3,3)); +object_move(obj2, vec3(-10+5*2,0,-10)); +object_pivot(obj2, vec3(0,90,0)); + +// spawn object2 (video) +object_t* obj3 = scene_spawn(); +object_model(obj3, m1); +object_diffuse(obj3, video_textures(v)[0]); +object_scale(obj3, vec3(3,3,3)); +object_move(obj3, vec3(-10+5*1,0,-10)); +object_pivot(obj3, vec3(0,90,0)); + +// @todo: add shadertoy material + static model_t cube; do_once cube = model("cube.obj", 0); + static shadertoy_t s; do_once s = shadertoy("shadertoys/4ttGWM.fs", 256); + model_set_texture(cube, shadertoy_render(&s, window_delta())->tx); + model_render(cube, cam.proj, cam.view, cube.pivot, 0); + +while(window_swap() && !input(KEY_ESC)) { + // draw environment + viewport_color( RGB3(22,22,32) ); + ddraw_grid(0); + ddraw_flush(); + + // update video + video_decode( v ); + + // draw scene + scene_render(SCENE_FOREGROUND); +} + +// load static scene +// model_t sponza = model("sponza.obj", MODEL_MATCAPS); +// model_set_texture(sponza, texture("matcaps/normals", 0)); +// translation44(sponza.pivot, 0,-1,0); +// rotate44(sponza.pivot, -90,1,0,0); +// scale44(sponza.pivot, 10,10,10); + // model_render(sponza, cam.proj, cam.view, sponza.pivot, 0); + +// this demo supersedes following old sources: +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-material.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-shadertoy.c + +#endif diff --git a/demos/06-controller.c b/demos/06-controller.c new file mode 100644 index 0000000..2b6586c --- /dev/null +++ b/demos/06-controller.c @@ -0,0 +1,288 @@ +// full controller demo: anims, input, collide; @todo: gamepad, input opts, easing on hits, notify on gamepad connect +// - rlyeh, public domain. +// +// Compile with: +// `make demos\04-controller.c` (windows) +// `sh MAKE.bat demos/04-controller.c` (linux, osx) + +#include "fwk.h" + +int main() { + // 75% window, MSAAx2 flag + window_create(75, WINDOW_MSAA2); + + // fx: load all post fx files in all subdirs + fx_load("fx**.fs"); + + // create a camera + camera_t cam = camera(); + camera_enable(&cam); + + // config 3d model #1 + model_t witch = model("witch/witch.obj", 0); + model_set_texture(witch, texture("witch/witch_diffuse.tga.png", 0)); + mat44 witch_pivot; vec3 witch_p = {-5,0,-5}, witch_r={-180,180,0}, witch_s={0.1,-0.1,0.1}; + + // config 3d model #2 + model_t girl = model("kgirl/kgirls01.fbx", 0); + mat44 girl_pivot; vec3 girl_p = {0,0,0}, girl_r = {270,0,0}, girl_s = {2,2,2}; + + // skybox + skybox_t sky = skybox("cubemaps/stardust", 0); + + // BGM + audio_play( audio_stream("waterworld-map.fur"), 0 ); + + // editor loop + while( window_swap() ) { + + // game camera + profile("Game.Camera") { + camera_t *cam = camera_get_active(); + + static vec3 source; + do_once source = cam->position; + + vec3 target = add3(girl_p, vec3(0,10,0)); + target = add3(target, scale3(norm3(sub3(source, target)), 10.0)); + source = mix3(source, target, 1-0.99f); + + camera_teleport(cam, source); + camera_lookat(cam, vec3(girl_p.x,0,girl_p.z)); + } + + // render begin (postfx) + fx_begin(); + + // skybox + skybox_render(&sky, cam.proj, cam.view); + + // world + ddraw_grid(0); + ddraw_flush(); + + // models + compose44(girl.pivot, girl_p, eulerq(girl_r), girl_s); + model_render(girl, cam.proj, cam.view, girl.pivot, 0); + + compose44(witch.pivot, witch_p, eulerq(witch_r), witch_s); + model_render(witch, cam.proj, cam.view, witch.pivot, 0); + + // render end (postfx) + fx_end(); + + // input controllers + + double GAME_JUMP_DOWN = input_down(KEY_Z); + double GAME_FIRE_DOWN = input_down(KEY_X); + double GAME_JUMP = input(KEY_Z); + double GAME_FIRE = input(KEY_X); + double GAME_LEFT = input(KEY_J); + double GAME_RIGHT = input(KEY_L); + double GAME_UP = input(KEY_I); + double GAME_DOWN = input(KEY_K); + double GAME_AXISX = input(KEY_L) - input(KEY_J); + double GAME_AXISY = input(KEY_I) - input(KEY_K); + + if( !input_anykey() ) { + if( input(GAMEPAD_CONNECTED) ) { + vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f /*15% deadzone*/); + GAME_JUMP_DOWN = input_down(GAMEPAD_A); + GAME_FIRE_DOWN = input_down(GAMEPAD_B) || input_down(GAMEPAD_X) || input_down(GAMEPAD_Y); + GAME_JUMP = input(GAMEPAD_A); + GAME_FIRE = input(GAMEPAD_B) || input(GAMEPAD_X) || input(GAMEPAD_Y); + GAME_AXISX = filtered_lpad.x; + GAME_AXISY = filtered_lpad.y; + } + } + + // animation controllers + + profile("Game.Animate scene") if( !window_has_pause() ) { + float delta = window_delta() * 30; // 30fps anim + + // animate girl + girl.curframe = model_animate(girl, girl.curframe + delta); + + // jump controller: jump duration=1.5s, jump height=6 units, anims (expo->circ) + float jump_delta = 1.0; + static double jump_timer = 0, jump_ss = 1.5, jump_h = 6; + if( GAME_JUMP_DOWN ) if( jump_timer == 0 ) jump_timer = time_ss(); + jump_delta = clampf(time_ss() - jump_timer, 0, jump_ss) * (1.0/jump_ss); + if( jump_delta >= 1 ) { jump_timer = 0; } + float y = ease_ping_pong( jump_delta, ease_out_expo, ease_out_circ); + girl_p.y = y * jump_h; + + // punch controller + float punch_delta = 1; + if( jump_delta >= 1 ) { + static vec3 origin; + static double punch_timer = 0, punch_ss = 0.5; + if( GAME_FIRE_DOWN ) if( punch_timer == 0 ) punch_timer = time_ss(), origin = girl_p; + punch_delta = clampf(time_ss() - punch_timer, 0, punch_ss) * (1.0/punch_ss); + if( punch_delta >= 1 ) { punch_timer = 0; } + else { + float x = ease_out_expo( punch_delta ); + vec3 fwd = rotate3q(vec3(0,0,1), eulerq(vec3(girl_r.x - 170,girl_r.y,girl_r.z))); + vec3 mix = mix3(girl_p, add3(origin,scale3(fwd,x*2)), x); + girl_p.x = mix.x, girl_p.z = mix.z; + } + } + + int modern_controller = 1; + int running = 0; + + // girl controller + + // locomotion vars + float speed = 0.2f * delta; + float yaw_boost = GAME_AXISY > 0 ? 1.0 : 1.75; + if(punch_delta < 1) yaw_boost = 0.0; // if firing... + else if(punch_delta <= 0.1) yaw_boost = 4.0; // unless initial punch chaining, extra yaw + + // old fashioned locomotion controller (boat controller) + if(!modern_controller) { + running = GAME_AXISY > 0; + + girl_r.x -= 170; + quat q = eulerq(girl_r); // += custom.pivot + vec3 rgt = rotate3q(vec3(1,0,0), q); + vec3 up = rotate3q(vec3(0,1,0), q); + vec3 fwd = rotate3q(vec3(0,0,1), q); + vec3 dir = scale3(fwd, speed * GAME_AXISY * (GAME_AXISY > 0 ? 2.0 : 0.5)); + girl_r.x += speed * 20.0 * yaw_boost * GAME_AXISX; // yaw + girl_p = add3(girl_p, dir); + girl_r.x += 170; + } + + // modern locomotion controller (mario 3d) + if(modern_controller) { + running = GAME_AXISX != 0 || GAME_AXISY != 0; + + camera_t *cam = camera_get_active(); + vec3 fwd = sub3(girl_p, cam->position); fwd.y = 0; fwd = norm3(fwd); + vec3 rgt = norm3(cross3(fwd, vec3(0,1,0))); + + // target + vec3 dir = add3( + scale3(fwd, GAME_AXISY), + scale3(rgt, GAME_AXISX) + ); dir.y = 0; dir = norm3(dir); + + // smoothing + static vec3 olddir; do_once olddir = dir; + dir = mix3(dir, olddir, 1 - (yaw_boost / 4.0) * 0.85); + olddir = dir; + + // vis + // ddraw_arrow(girl_p, add3(girl_p,scale3(dir,10))); + + // apply direction + girl_p = add3(girl_p, scale3(dir, speed * 2)); + + // apply rotation + { + girl_r.x -= 170; + quat q = eulerq(girl_r); + vec3 fwdg = rotate3q(vec3(0,0,1), q); + girl_r.x += 170; + + //float cosAngle = dot3(dir,fwdg); + //float angle = acos(cosAngle) * TO_DEG; + float angle = TO_DEG * ( atan2(fwdg.z, fwdg.x) - atan2(dir.z, dir.x)); + + if( !isnan(angle) ) { + girl_r.x -= angle; + while(girl_r.x> 180) girl_r.x-=360; + while(girl_r.x<-180) girl_r.x+=360; + } + } + } + + // anim loops + if( jump_delta < 1 ) { // jump/kick anim +#if 0 + girl.curframe = clampf(girl.curframe, 184, 202); + if( girl.curframe > 202-4 && GAME_FIRE_DOWN ) girl.curframe = 184+4; +#else + #define loopf(frame, min, max) (frame < min ? min : frame > max ? min + frame - max : frame) + if(girl.curframe >= 203) + girl.curframe = loopf(girl.curframe, 203, 220); + else + girl.curframe = clampf(girl.curframe, 184, 202); + if( girl.curframe > 202-4 && girl.curframe < 208 && GAME_FIRE_DOWN ) girl.curframe = 203; +#endif + } + else if( punch_delta < 1 ) { // punch anim + girl.curframe = clampf(girl.curframe, 90, 101); + if( girl.curframe > 101-6 && GAME_FIRE_DOWN ) girl.curframe = 101-6; + } + else if( running ) { + // loop running anim + if( girl.curframe < 65 ) girl.curframe = 65; + if( girl.curframe > 85 ) girl.curframe = 65; + } + else { // loop idle anim + if( girl.curframe > 59 ) girl.curframe = 0; + } + } + + // Game collisions + + profile("Game.collisions") { + bool punching = girl.curframe >= 90 && girl.curframe < 101; + bool air_kicking = girl.curframe >= 184 && girl.curframe < 202; + bool jump_kicking = girl.curframe >= 203 && girl.curframe < 220; + bool attacking = punching || air_kicking || jump_kicking; + + if( attacking ) { + aabb boxg = model_aabb(girl, girl_pivot); + aabb boxw = model_aabb(witch, witch_pivot); +#if 0 // halve aabb. ok + { + vec3 diag = sub3(boxg.max, boxg.min); + vec3 halve = scale3(diag, 0.25); + vec3 center = scale3(add3(boxg.min, boxg.max), 0.5); + boxg.min = sub3(center, halve); + boxg.max = add3(center, halve); + } +#endif + hit* h = aabb_hit_aabb(boxg, boxw); + if( h && GAME_FIRE ) { + vec3 dir = norm3(sub3(witch_p, girl_p)); + witch_p = add3(witch_p, mul3(dir,vec3(1,0,1))); + } + } + } + + // ui + if( ui_panel("Input", 0) ) { // @todo: showcase input binding + ui_section("Controllers"); + ui_label("Gamepad #1"); + ui_label("Keys I/J/K/L + Z/X"); + ui_panel_end(); + } + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, b); + } + ui_panel_end(); + } + } +} + +// vec2 do_gamepad_polarity = vec2(+1,+1); +// vec2 do_gamepad_sensitivity = vec2(0.1f,0.1f); +// vec2 do_mouse_polarity = vec2(+1,-1); +// vec2 do_mouse_sensitivity = vec2(0.2f,0.2f); +// float do_gamepad_deadzone = 0.15f;// +// +// if(ui_separator()) {} +// if(ui_slider("Gamepad deadzone", &do_gamepad_deadzone)) {} +// if(ui_float2("Gamepad polarity", do_gamepad_polarity.v2)) {} +// if(ui_float2("Gamepad sensitivity", do_gamepad_sensitivity.v2)) {} +// if(ui_separator()) {} +// if(ui_float2("Mouse polarity", do_mouse_polarity.v2)) {} +// if(ui_float2("Mouse sensitivity", do_mouse_sensitivity.v2)) {}// diff --git a/demos/07-network.c b/demos/07-network.c new file mode 100644 index 0000000..21cac0b --- /dev/null +++ b/demos/07-network.c @@ -0,0 +1,88 @@ +// network demo: sockets, downloads, script, @todo: ecs, vm, obj, teal +// - rlyeh, public domain. +// +// Compile with: +// `make demos\07-network.c` (windows) +// `sh MAKE.bat demos/07-network.c` (linux, osx) + +#include "fwk.h" + +volatile int client_socket = -1; +volatile int server_socket = -1; + +int server_echo(int cli) { + ui_notify("Server: client connected", va("Address %s:%s", tcp_host(cli), tcp_port(cli))); + char buf[128]; + int len = tcp_recv(cli, buf, 128); + if( len > 0 ) tcp_send(cli, buf, len); // echo + tcp_close(cli); + return 1; +} +int server_thread(void *userdata) { + server_socket = tcp_bind("0.0.0.0", "8080", 10); // port "0" for auto + for(;;) { + tcp_peek(server_socket, server_echo); + sleep_ms(100); + } +} +void client_message(const char *msg) { + client_socket = tcp_open("127.0.0.1", "8080"); + if( client_socket >= 0 ) { + tcp_send(client_socket, msg, strlen(msg)); + char buf[128]; + int rlen = tcp_recv(client_socket, buf, 128); + if(rlen > 0) ui_notify("Client", va("Received '%.*s' from server", rlen, buf)); + // tcp_close(client_socket); + } +} + + +int main() { + window_create(0.85, 0); + window_title(__FILE__); + while(window_swap()) { + static int ui_open; ui_open = 1; + if( ui_window("Network", &ui_open) ) { + + if( ui_button("Server: listen") ) { do_once thread(server_thread, NULL); } + ui_label(server_socket >= 0 ? "Listening at 8080 port" : "Not ready"); + + ui_separator(); + + if( ui_button("Client: send message") ) client_message("Hi"); + ui_label(client_socket >= 0 ? va("Connected to server. My address is %s:%s", tcp_host(client_socket), tcp_port(client_socket)) : "Not connected"); + + ui_separator(); + + if( ui_button("Download test (https)")) { + array(char) webfile = download("https://www.google.com/"); // @leak + ui_notify("Download test", va("%d bytes downloaded from google.com", array_count(webfile))); + } + + ui_separator(); + + if( ui_button("Script test")) { + script_run( "ui_notify(\"Script test\", \"Hello from \" .. _VERSION)"); + script_run( "-- Bye.lua\nio.write(\"script test: Bye world!, from \", _VERSION, \"\\n\")" ); + } + + ui_window_end(); + } + } +} + +// this demo supersedes following old sources: +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-demo.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-script.c +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-socket.c + +#if 0 // teal + script_run("local tl=require(\"tl\")\ntl.loader()"); + script_run("addsub = require(\"s2\"); print (addsub.add(10, 20))"); +s2.tl: + local function add(a: number, b: number): number + return a + b + end + local s = add(1,2) + print(s) +#endif diff --git a/demos/99-audio.c b/demos/99-audio.c new file mode 100644 index 0000000..3d0bd14 --- /dev/null +++ b/demos/99-audio.c @@ -0,0 +1,39 @@ +// audio demo +// - rlyeh, public domain + +#include "fwk.h" + +int main() { + // window (80% sized, MSAA x4 flag) + window_create(80, WINDOW_MSAA4 | WINDOW_SQUARE); + + // audio (both streams & clips) + audio_t stream1 = audio_stream( "wrath_of_the_djinn.xm" ); + audio_t stream2 = audio_stream( "larry.mid" ); + audio_t stream3 = audio_stream( "monkey1.mid" ); + audio_t stream4 = audio_stream( "waterworld-map.fur" ); + audio_t BGM = stream1; + audio_play(BGM, 0); + + audio_t SFX1 = audio_clip( "coin.wav" ); + audio_t SFX2 = audio_clip( "pew.sfxr" ); + audio_play(SFX1, 0); + + // demo loop + while (window_swap() && !input_down(KEY_ESC)) { + static int open = 1; + if( ui_window("Audio", &open)) { + static float bgm = 1, sfx = 1, master = 1; + if( ui_slider2("BGM", &bgm, va("%.2f", bgm))) audio_volume_stream(bgm); + if( ui_slider2("SFX", &sfx, va("%.2f", sfx))) audio_volume_clip(sfx); + if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master); + if( ui_label2_toolbar("BGM: Wrath of the Djinn", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream1, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("BGM: Leisure Suit Larry", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream2, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("BGM: Monkey Island", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream3, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("BGM: Waterworld Map", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream4, AUDIO_SINGLE_INSTANCE); + if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(SFX1, 0); + if( ui_label2_toolbar("SFX: Pew", ICON_MD_VOLUME_UP)) audio_play(SFX2, 0); + ui_window_end(); + } + } +} diff --git a/demos/99-cubemap.c b/demos/99-cubemap.c new file mode 100644 index 0000000..7aa8ca3 --- /dev/null +++ b/demos/99-cubemap.c @@ -0,0 +1,93 @@ +#include "fwk.h" + +int SKY_DIR = 0; +const char *SKY_DIRS[] = { + "cubemaps/bridge3/", + "cubemaps/colors/", + "cubemaps/colors2/", + "cubemaps/mountain/", + "cubemaps/room/", + "cubemaps/stardust/", + "hdr/MonValley_G_DirtRoad_1k.hdr", + "hdr/Factory_Catwalk_1k.hdr", + "hdr/Shiodome_Stairs_1k.hdr", +}; + +int OBJ_MDL = 0; +const char *OBJ_MDLS[] = { + "meshes/sphere.obj", + "meshes/suzanne.obj", + "meshes/gazebo.obj", +}; + +int main(int argc, char** argv) { + window_create(85, WINDOW_MSAA8); + + camera_t cam = camera(); + skybox_t sky = {0}; + model_t mdl = {0}; + + bool initialized = 0; + bool must_reload = 0; + + while( window_swap()) { + // reloading + if( must_reload ) { + must_reload = 0; + skybox_destroy(&sky); + model_destroy(mdl); + initialized = 0; + } + if( !initialized ) { + initialized = 1; + sky = skybox(SKY_DIRS[SKY_DIR], 0); + mdl = model(OBJ_MDLS[OBJ_MDL], 0); + rotation44(mdl.pivot, 0, 1,0,0); // @fixme: -90,1,0,0 -> should we rotate SHMs as well? compensate rotation in shader? + } + + // fps camera + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + window_cursor( !active ); + + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdec.x,wasdec.y,wasdec.z); + camera_fps(&cam, mouse.x,mouse.y); + + // render + mat44 mvp; multiply44x2(mvp, cam.proj, cam.view); + { + glClear(GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + //glDisable(GL_CULL_FACE); + + // mesh + glDepthMask(GL_TRUE); + glUseProgram(mdl.program); + glUniform3fv(glGetUniformLocation(mdl.program, "u_coefficients_sh"), 9, &sky.cubemap.sh[0].x); + glUniform1i(glGetUniformLocation(mdl.program, "u_textured"), false); + model_render(mdl, cam.proj, cam.view, mdl.pivot, 0); + + // sky + skybox_render(&sky, cam.proj, cam.view); + } + + if( ui_panel("Scene", 0)) { + if( ui_list("Skybox", SKY_DIRS, countof(SKY_DIRS), &SKY_DIR) ) { + must_reload = 1; + } + if( ui_list("Model", OBJ_MDLS, countof(OBJ_MDLS), &OBJ_MDL) ) { + must_reload = 1; + } + ui_separator(); + for (int i = 0; i < 9; i++) { + vec3 remap = scale3(add3(sky.cubemap.sh[i], vec3(1,1,1)), 127.5f); // -1..+1 -> 0..255 + ui_color3(va("SH Coefficient [%d]", i), &remap.x); + sky.cubemap.sh[i] = sub3(scale3(remap, 1/127.5f), vec3(1,1,1)); + } + ui_panel_end(); + } + } +} diff --git a/demos/99-easing.c b/demos/99-easing.c new file mode 100644 index 0000000..5597292 --- /dev/null +++ b/demos/99-easing.c @@ -0,0 +1,57 @@ +#include "fwk.h" + +struct { + float (*ease)(float); + const char *name; +} easings[] = { + {ease_linear, "ease_linear"}, + {ease_out_sine, "ease_out_sine"}, + {ease_out_quad, "ease_out_quad"}, + {ease_out_cubic, "ease_out_cubic"}, + {ease_out_quart, "ease_out_quart"}, + {ease_out_quint, "ease_out_quint"}, + {ease_out_expo, "ease_out_expo"}, + {ease_out_circ, "ease_out_circ"}, + {ease_out_back, "ease_out_back"}, + {ease_out_elastic, "ease_out_elastic"}, + {ease_out_bounce, "ease_out_bounce"}, + {ease_in_sine, "ease_in_sine"}, + {ease_in_quad, "ease_in_quad"}, + {ease_in_cubic, "ease_in_cubic"}, + {ease_in_quart, "ease_in_quart"}, + {ease_in_quint, "ease_in_quint"}, + {ease_in_expo, "ease_in_expo"}, + {ease_in_circ, "ease_in_circ"}, + {ease_in_back, "ease_in_back"}, + {ease_in_elastic, "ease_in_elastic"}, + {ease_in_bounce, "ease_in_bounce"}, + {ease_inout_sine, "ease_inout_sine"}, + {ease_inout_quad, "ease_inout_quad"}, + {ease_inout_cubic, "ease_inout_cubic"}, + {ease_inout_quart, "ease_inout_quart"}, + {ease_inout_quint, "ease_inout_quint"}, + {ease_inout_expo, "ease_inout_expo"}, + {ease_inout_circ, "ease_inout_circ"}, + {ease_inout_back, "ease_inout_back"}, + {ease_inout_elastic, "ease_inout_elastic"}, + {ease_inout_bounce, "ease_inout_bounce"}, + {ease_inout_perlin, "ease_inout_perlin"}, +}; + +int main() { + window_create(0.75, WINDOW_SQUARE); + while(window_swap()) { + static double timer = 0; timer = fmod(timer+window_delta(), 2); // loops every 2s + + static int open = 1; + if( ui_window("ease", &open) ) { + float linear_delta = timer / 2.f; // delta is [0..1] + for( int i = 0; i < countof(easings); ++i) { + float nonlinear_delta = easings[i].ease(linear_delta); + // visualize + ui_slider( easings[i].name, &nonlinear_delta ); + } + ui_window_end(); + } + } +} diff --git a/demos/99-font.c b/demos/99-font.c new file mode 100644 index 0000000..6a67148 --- /dev/null +++ b/demos/99-font.c @@ -0,0 +1,111 @@ +#include "fwk.h" + +int main() { + window_create(0.75, WINDOW_MSAA8); + + // style: our aliases + #define FONT_REGULAR FONT_FACE1 + #define FONT_ITALIC FONT_FACE2 + #define FONT_BOLD FONT_FACE3 + #define FONT_JAPANESE FONT_FACE4 + #define FONT_MONOSPACE FONT_FACE5 + + #define FONT_GRAY FONT_COLOR2 + #define FONT_ORANGE FONT_COLOR3 + #define FONT_LIME FONT_COLOR4 + #define FONT_GREEN FONT_COLOR5 + #define FONT_CYAN FONT_COLOR6 + + #define FONT_LARGEST FONT_H1 + #define FONT_LARGE FONT_H2 + #define FONT_MEDIUM FONT_H3 + #define FONT_NORMAL FONT_H4 + #define FONT_SMALL FONT_H5 + #define FONT_TINY FONT_H6 + + // style: atlas size, unicode ranges and font faces (up to 6 faces) + font_face(FONT_REGULAR, "Carlito-Regular.ttf", 48.f, FONT_EU|FONT_AR|FONT_RU|FONT_2048); + font_face(FONT_ITALIC, "Carlito-Italic.ttf", 48.f, FONT_EU|FONT_AR|FONT_RU|FONT_2048); + font_face(FONT_BOLD, "Carlito-Bold.ttf", 48.f, FONT_EU|FONT_AR|FONT_RU|FONT_2048); + font_face(FONT_JAPANESE, "mplus-1p-medium.ttf", 48.f, FONT_JP|FONT_2048); // CJK|FONT_2048|FONT_OVERSAMPLE_Y); + font_face(FONT_MONOSPACE, "Inconsolata-Regular.ttf", 24.f, FONT_EU|FONT_512); + + // style: colors (up to 10 colors) + font_color(FONT_GRAY, RGB4(100,100,100,255)); + font_color(FONT_ORANGE, RGB4(255,192,0,255)); + font_color(FONT_LIME, RGB4(192,255,0,255)); + font_color(FONT_GREEN, RGB4(0,255,192,255)); + font_color(FONT_CYAN, RGB4(0,192,255,255)); + + // prepare color highlighting for following code snippet + const char *source = + FONT_MONOSPACE FONT_LARGEST + "int main(int argc, char **argv) {\n" + " for( int i = 0; i < 10; ++i)\n" + " puts(\"hello world\");\n" + " return 0;\n" + "}\n"; + const void *colors = font_colorize(source, "void,int,char", "if,else,for,do,while,return,switch,case,break,default,"); + + // demo loop + while( window_swap() && !input(KEY_ESC) ) { + ddraw_grid(0); + + // initial spacing + font_goto(0, 50); + + // print a code snippet with syntax highlighting + font_highlight(source, colors); + + // print a few strings with markup codes + font_print( + FONT_REGULAR + FONT_LARGEST FONT_GRAY "The quick " + FONT_LARGE FONT_LIME "brown " + FONT_MEDIUM FONT_GRAY "fox " + FONT_NORMAL "jumps over " + FONT_SMALL "the lazy " + FONT_TINY "dog.\n"); + + font_print( + FONT_REGULAR FONT_LARGE FONT_CYAN + "Now is the time for all " FONT_ITALIC "good men " FONT_REGULAR "to come to the aid of " FONT_BOLD "the party.\n"); + + font_print( + FONT_ITALIC FONT_LARGE FONT_GREEN + "Ég get etið gler án þess að meiða mig!\n"); + + font_print( + FONT_BOLD FONT_LARGE FONT_ORANGE + "Эх, чужак! Общий съём цен шляп (юфть)—вдрызг!.\n"); + + font_print( + FONT_JAPANESE + "私はガラスを食べられます。それは私を傷つけません。\n"); + + font_print( "This text "); + font_print( "should display concatenated, "); + font_print( "as there are no linefeeds.\n" ); + + // i18n: pangrams.txt file, line browser + static int counter = 0; + static array(char*) lines; do_once lines = strsplit( vfs_read("pangrams.txt"), "\r\n" ); + counter += input_down(KEY_RIGHT)-input_down(KEY_LEFT); + counter += counter < 0 ? array_count(lines) : 0; + font_print( va("<< %s >>\n", lines[counter % array_count(lines)]) ); + + // this does not work yet. you cant chain alignments yet... + //font_print(FONT_TOP "Top" FONT_MIDDLE "Middle" FONT_BASELINE "Baseline" FONT_BOTTOM "Bottom\n"); + //font_print(FONT_LEFT "Left" FONT_CENTER "Center" FONT_RIGHT "Right\n"); + + // ... alignment must be the first tag in a string for now. this is a temporary hack. + font_print(FONT_LEFT "left"); + font_print(FONT_CENTER "center"); + font_print(FONT_RIGHT "right"); + + font_print(FONT_TOP FONT_CENTER "top\n"); + font_print(FONT_MIDDLE FONT_RIGHT "middle\n"); + font_print(FONT_BASELINE FONT_RIGHT "baseline\n"); + font_print(FONT_BOTTOM FONT_CENTER "bottom\n"); + } +} diff --git a/demos/99-instanced.c b/demos/99-instanced.c new file mode 100644 index 0000000..ccbcba5 --- /dev/null +++ b/demos/99-instanced.c @@ -0,0 +1,62 @@ +// instanced models demo +// - rlyeh, public domain. + +#include "fwk.h" + +int main() { + window_create(75, WINDOW_MSAA2); + window_title(__FILE__); + + camera_t cam = camera(); + skybox_t sky = skybox("cubemaps/stardust", 0); + model_t girl = model("kgirls01.fbx", 0); + + while( window_swap() ) { + if(input(KEY_F5)) window_reload(); + if(input(KEY_ESC)) break; + + // fps camera + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + window_cursor( !active ); + + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdec.x,wasdec.y,wasdec.z); + camera_fps(&cam, mouse.x,mouse.y); + + // ground rendering + ddraw_ground(0); + ddraw_flush(); + + // skeletal + profile("Skeletal update") { + float delta = window_has_pause() ? 0 : window_delta() * 30; // 30fps anim + girl.curframe = model_animate(girl, girl.curframe + delta); + } + + profile("Skeletal render") { + enum { ROW = 32, MAX_INSTANCES = ROW * ROW }; + static mat44 M[MAX_INSTANCES]; + + do_once { + int i = 0; + for(int z = 0; z < ROW; ++z) { + for(int x = 0; x < ROW; ++x, ++i) { + vec3 p = vec3(-x*3,0,-z*3); + vec3 r = vec3(0,0,0); + vec3 s = vec3(2,2,2); + rotationq44(M[i], eulerq(r)); scale44(M[i], s.x,s.y,s.z); relocate44(M[i], p.x,p.y,p.z); + } + } + } + + model_render_instanced(girl, cam.proj, cam.view, M, 0, MAX_INSTANCES); + } + + // skybox + profile("Skybox") { + skybox_render(&sky, cam.proj, cam.view); + } + } +} diff --git a/demos/99-material.c b/demos/99-material.c new file mode 100644 index 0000000..74263cb --- /dev/null +++ b/demos/99-material.c @@ -0,0 +1,68 @@ +// material demo +// - rlyeh, public domain +// +// @todo: object_print(obj, ""); + +#include "fwk.h" + +int main() { + // create the window + window_create( 0.75f, WINDOW_MSAA8 ); + + // create camera + camera_t cam = camera(); + // load video, RGB texture, no audio + video_t *v = video( "bjork-all-is-full-of-love.mp4", VIDEO_RGB | VIDEO_NO_AUDIO ); video_seek(v, 30); + // load texture + texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_RGB); + texture_t t2 = texture("matcaps/material3", 0); + // load model + model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS); + model_t m2 = model("suzanne.obj", MODEL_NO_ANIMATIONS|MODEL_MATCAPS); + + // spawn object1 (diffuse) + object_t* obj1 = scene_spawn(); + object_model(obj1, m1); + object_diffuse(obj1, t1); + object_scale(obj1, vec3(3,3,3)); + object_move(obj1, vec3(-10+5*0,0,-10)); + object_pivot(obj1, vec3(0,90,0)); + + // spawn object2 (matcap) + object_t* obj2 = scene_spawn(); + object_model(obj2, m2); + object_diffuse(obj2, t2); + object_scale(obj2, vec3(3,3,3)); + object_move(obj2, vec3(-10+5*2,0,-10)); + object_pivot(obj2, vec3(0,90,0)); + + // spawn object2 (video) + object_t* obj3 = scene_spawn(); + object_model(obj3, m1); + object_diffuse(obj3, video_textures(v)[0]); + object_scale(obj3, vec3(3,3,3)); + object_move(obj3, vec3(-10+5*1,0,-10)); + object_pivot(obj3, vec3(0,90,0)); + + while(window_swap() && !input(KEY_ESC)) { + // draw environment + viewport_color( RGB3(22,22,32) ); + ddraw_grid(0); + ddraw_flush(); + + // update video + video_decode( v ); + + // draw scene + scene_render(SCENE_FOREGROUND); + + // fps camera + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouselook = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdec.x,wasdec.y,wasdec.z); + camera_fps(&cam, mouselook.x,mouselook.y); + window_cursor( !active ); + } +} diff --git a/demos/99-pbr.c b/demos/99-pbr.c new file mode 100644 index 0000000..592b1b2 --- /dev/null +++ b/demos/99-pbr.c @@ -0,0 +1,675 @@ +// PBR model viewer. Based on Foxotron by @gargaj + cce/Peisik (UNLICENSE). +// - rlyeh, public domain. +// +// @todo: Middle mouse button to pan camera @todo + +#include "fwk.h" + +#if is(tcc) && !is(win32) // @todo: remove this & test on linux +int log2_64 (uint64_t value) { + const int tab64[64] = { + 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5}; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58]; +} +#define log2 log2_64 +#endif + +// ----------------------------------------------------------------------------- +// textures + +texture_t *LoadTextureRGBA8( const char *pathfile, unsigned flags ) { + int flags_hdr = strendi(pathfile, ".hdr") ? TEXTURE_FLOAT | TEXTURE_RGBA : 0; + texture_t t = texture(pathfile, flags | TEXTURE_LINEAR | TEXTURE_MIPMAPS | TEXTURE_REPEAT | flags_hdr); + if( t.id == texture_checker().id ) { + return NULL; + } + texture_t *tex = CALLOC(1, sizeof(texture_t)); + *tex = t; + return tex; +} + +// ----------------------------------------------------------------------------- +// models + +typedef struct Mesh { + GLuint vao, vbo, ibo; + + int vert_stride; + void *vert_stream; + int num_verts, num_tris; + + int material_idx; + bool transparent; + + vec3 aabb_min, aabb_max; +} Mesh; + +typedef struct Model { + array(Mesh) meshes; + array(pbr_material_t) materials; + unsigned shader; +} Model; + +bool ModelLoad( Model *G, const char *_path ); +void ModelDestroy( Model *G ); +void ModelRebind( Model *G, unsigned shader ); +void ModelRender( Model *G, const mat44 _worldRootMatrix ); + +void ModelDestroy( Model *G) { + for( int i = 0, end = array_count(G->materials); i < end; ++i ) { + pbr_material_destroy(&G->materials[i]); + } + array_free(G->materials); + + for( int i = 0, end = array_count(G->meshes); i < end; ++i ) { + Mesh *it = &G->meshes[i]; + glDeleteBuffers( 1, &it->ibo ); + glDeleteBuffers( 1, &it->vbo ); + glDeleteVertexArrays( 1, &it->vao ); + } + array_free(G->meshes); +} + +bool ModelLoad( Model *G, const char *_path ) { + ModelDestroy(G); + + // ------------------------------------------------------------------------- + Model g = {0}; + *G = g; + + model_t m = model(_path, 0); + + int scn_num_meshes = m.num_meshes; + int scn_num_materials = array_count(m.materials); + + // ------------------------------------------------------------------------- + + for( int i = 0; i < scn_num_materials; i++ ) { + const char *name = m.materials[i].name; + + PRINTF("Loading material %d/%d: '%s'\n", i + 1, scn_num_materials, name); + + pbr_material_t mt; + pbr_material(&mt, name); + + array_push(G->materials, mt); + } + + for( int i = 0; i < scn_num_meshes; i++ ) { + PRINTF("Loading mesh %d/%d\n", i + 1, scn_num_meshes); + + int verts = m.num_verts; + int faces = m.num_triangles; + unsigned material_index = 0; // &m.iqm->meshes[i].material; // aiGetMeshMaterialIndex(scn_mesh[i]); + + bool has_data = verts && faces; + if( !has_data ) { + continue; + } + + PRINTF("Loading mesh v%d/f%d\n", verts, faces); + + Mesh mesh = { 0 }; + + mesh.vao = m.vao; + mesh.vbo = m.vbo; + mesh.ibo = m.ibo; + + mat44 id; id44(id); + mesh.aabb_min = model_aabb(m, id).min; + mesh.aabb_max = model_aabb(m, id).max; + + // p3 n3 t3 b3 u2 + + mesh.vert_stride = m.stride; + mesh.vert_stream = m.verts; + + mesh.num_verts = verts; + mesh.num_tris = faces; + + mesh.material_idx = material_index; + + // By importing materials before meshes we can investigate whether a mesh is transparent and flag it as such. + const pbr_material_t* mtl = G->materials ? &G->materials[mesh.material_idx] : NULL; + mesh.transparent = false; + if( mtl ) { + mesh.transparent |= mtl->albedo .texture ? mtl->albedo .texture->transparent : mtl->albedo .color.a < 1.0f; + mesh.transparent |= mtl->diffuse.texture ? mtl->diffuse.texture->transparent : mtl->diffuse.color.a < 1.0f; + } + + array_push(G->meshes, mesh); + } + + #if 0 + G->mGlobalAmbient = vec4( 0.3,0.3,0.3,0.3 ); + int scn_num_lights = 0; + for( int i = 0; i < scn_num_lights; i++ ) { + PRINTF("Loading light %d/%d\n", i + 1, scn_num_lights); + + vec4 *color = aiGetLightColor(scn_light[i]); + char *type = aiGetLightType(scn_light[i]); + if( 0 == strcmp(type, "AMBIENT") ) { + memcpy( &G->mGlobalAmbient, &color->r, sizeof( float ) * 4 ); + } else { + // @todo + } + } + #endif + + return true; +} + +void ModelRender( Model *G, const mat44 _worldRootMatrix ) { + unsigned _shader = G->shader; + shader_bind( _shader ); + + shader_vec4("global_ambient", vec4(1,1,1,1)); // unused + + // loop thrice: first opaque, then transparent backface, then transparent frontface + for(int j = 0; j < 3; ++j) { + bool bTransparentPass = j > 0; + if(bTransparentPass) { + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glCullFace( j == 1 ? GL_FRONT : GL_BACK ); // glDepthMask( GL_FALSE); + } + + mat44 mat_world; copy44(mat_world, _worldRootMatrix); // @fixme mMatrices[ node.mID ] * _worldRootMatrix + shader_mat44( "mat_world", mat_world ); + + for( int i = 0, end = array_count(G->meshes); i < end; i++ ) { + const Mesh *mesh = &G->meshes[ i ]; + // Postpone rendering transparent meshes + if(mesh->transparent != bTransparentPass) + continue; + + const pbr_material_t *material = &G->materials[ mesh->material_idx ]; + shader_colormap( "map_diffuse", material->diffuse ); + shader_colormap( "map_normals", material->normals ); + shader_colormap( "map_specular", material->specular ); + shader_colormap( "map_albedo", material->albedo ); + shader_colormap( "map_roughness", material->roughness ); + shader_colormap( "map_metallic", material->metallic ); + shader_colormap( "map_ao", material->ao ); + shader_colormap( "map_ambient", material->ambient ); + shader_colormap( "map_emissive", material->emissive ); + shader_float( "specular_shininess", material->specular_shininess ); // unused, basic_specgloss.fs only + + shader_vec2( "resolution", vec2(window_width(),window_height())); + + glActiveTexture(GL_TEXTURE0); // be nice to Mesa before rendering + glBindVertexArray( mesh->vao ); + glDrawElements( GL_TRIANGLES, mesh->num_tris * 3, GL_UNSIGNED_INT, NULL ); + } + + if(bTransparentPass) { + glDisable( GL_BLEND ); + // glDepthMask( GL_TRUE ); + } + } + + //glBindVertexArray( 0 ); + //glUseProgram( 0 ); +} + +static +void G_SetupVertexArray( unsigned _shader, const char *name, int stride, int num_floats, int *offset, int opt_location ) { + int location = opt_location >= 0 ? opt_location : glGetAttribLocation( _shader, name ); + if( location >= 0 ) { + glVertexAttribPointer( location, num_floats, GL_FLOAT, GL_FALSE, stride, (GLvoid *)(uintptr_t)(*offset) ); + glEnableVertexAttribArray( location ); + } + *offset += num_floats * sizeof( GLfloat ); +} + +void ModelRebind( Model *G, unsigned _shader ) { + shader_bind(_shader); + if(_shader == G->shader) return; + G->shader = _shader; + + for( int i = 0, end = array_count(G->meshes); i < end; i++ ) { + const Mesh *mesh = &G->meshes[ i ]; + + glBindVertexArray( mesh->vao ); + glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ibo ); + + glDisableVertexAttribArray( 0 ); + glDisableVertexAttribArray( 1 ); + glDisableVertexAttribArray( 2 ); + glDisableVertexAttribArray( 3 ); + glDisableVertexAttribArray( 4 ); + glDisableVertexAttribArray( 5 ); + glDisableVertexAttribArray( 6 ); + glDisableVertexAttribArray( 7 ); + + int offset = 0, stride = mesh->vert_stride; + G_SetupVertexArray( _shader, "in_pos", stride, 3, &offset, -1/*0*/ ); + G_SetupVertexArray( _shader, "in_texcoord", stride, 2, &offset, -1/*1*/ ); + G_SetupVertexArray( _shader, "in_normal", stride, 3, &offset, -1/*2*/ ); + G_SetupVertexArray( _shader, "in_tangent", stride, 4, &offset, -1/*3*/ ); + + //glBindVertexArray( 0 ); + } +} + +// ----------------------------------------------------------------------------- +// skyboxes + +// Extracts single key value from an HDRLabs IBL file. Returns an empty string on error. +static const char *ibl_readkey( const char* pathfile, const char* key ) { + char *data = vfs_read(pathfile); + if( data ) { + const char *found = strstr(data, va("%s=", key)); + if( found ) return found + strlen(key) + 1; + found = strstr(data, va("%s =", key)); + if( found ) return found + strlen(key) + 2; + } + return ""; +} + +typedef struct Skybox { + vec3 sunColor; float sunYaw, sunPitch; // ibl settings + texture_t *reflection; // reflection map (hdr) + texture_t *env; // irradiance map (env) +} Skybox; + +Skybox g_skybox = { {1,1,1} }; + +void SkyboxDestroy( Skybox *s ) { + if( s->reflection ) texture_destroy( s->reflection ); + if( s->env ) texture_destroy( s->env ); + *s = (Skybox){0}; +} + +bool SkyboxLoad( Skybox *s, const char **slots ) { // hdr,env,ibl + SkyboxDestroy( s ); + + const char* reflectionPath = slots[0]; + const char* envPath = slots[1]; + const char* iblPath = slots[2]; + +// unsigned invalid = texture_checker().id; + + // Reflection map + if( reflectionPath ) { + if( (s->reflection = LoadTextureRGBA8( reflectionPath, TEXTURE_SRGB )) != NULL ) { + glBindTexture( GL_TEXTURE_2D, s->reflection->id ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + } + + // Irradiance map + if( envPath ) { + if( (s->env = LoadTextureRGBA8( envPath, TEXTURE_SRGB )) != NULL ) { + glBindTexture( GL_TEXTURE_2D, s->env->id ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + } + + // Sun color & direction from .ibl file + s->sunColor = vec3(1,1,1); + s->sunYaw = 0, s->sunPitch = 0; + if( iblPath ) { + vec3 sc; if( 3 == sscanf(ibl_readkey(iblPath, "SUNcolor"), "%f,%f,%f", &sc.x, &sc.y, &sc.z) ) { + s->sunColor = scale3(sc, 1/255.f); + } + vec2 uv = vec2(atof(ibl_readkey(iblPath, "SUNu")), atof(ibl_readkey(iblPath, "SUNv"))); + if( len2(uv) > 0 ) { + s->sunYaw = C_PI * (-2. * uv.x + 0.5f); + s->sunPitch = (0.5f - uv.y) * C_PI; + } + } + + return s->reflection && s->env; +} + +// ----------------------------------------------------------------------------- +// main + +const char *shader_names[] = {"Physically Based", "Basic SpecGloss" }; +const char *shaders[][2] = { // name, vs, fs + { "Shaders/pbr.vs", "Shaders/pbr.fs" }, + { "Shaders/basic_specgloss.vs", "Shaders/basic_specgloss.fs" } +}; + +const char *skyboxes[][3] = { // reflection, env, metadata + {"hdr/Tokyo_BigSight_1k.hdr","hdr/Tokyo_BigSight_Env.hdr","hdr/Tokyo_BigSight.ibl"}, + {"hdr/GCanyon_C_YumaPoint_1k.hdr","hdr/GCanyon_C_YumaPoint_Env.hdr","hdr/GCanyon_C_YumaPoint.ibl"}, + {"hdr/Factory_Catwalk_1k.hdr","hdr/Factory_Catwalk_Env.hdr","hdr/Factory_Catwalk.ibl"}, + {"hdr/MonValley_G_DirtRoad_1k.hdr","hdr/MonValley_G_DirtRoad_Env.hdr","hdr/MonValley_G_DirtRoad.ibl"}, + {"hdr/Shiodome_Stairs_1k.hdr","hdr/Shiodome_Stairs_Env.hdr","hdr/Shiodome_Stairs.ibl"}, +}; + +Model gModel; +unsigned gShader = ~0u; +unsigned gShaderConfig = ~0u; + +bool LoadShaderConfig( int slot ) { // name,vs,fs + unsigned newShader = shader( vfs_read(shaders[slot][0]), vfs_read(shaders[slot][1]), NULL, NULL ); + if( newShader == ~0u ) return false; + + shader_destroy( gShader ); + gShaderConfig = slot; + gShader = newShader; + + return true; +} + +void camera_fit(camera_t *cam) { + vec3 target = scale3( add3( gModel.meshes[0].aabb_min, gModel.meshes[0].aabb_max ), 0.5f); + float distance = len3( sub3( gModel.meshes[0].aabb_max, gModel.meshes[0].aabb_min ) ) * 0.85f; + cam->position = add3(target, scale3(norm3(sub3(cam->position,target)), distance)); + camera_lookat(cam, vec3(0,0,0)); +} + +int main( int argc, const char *argv[] ) { + window_create( 75, WINDOW_MSAA2 ); + window_title(__FILE__); + + // load all fx files in all subdirs + fx_load("fx**.fs"); + + if( !LoadShaderConfig( 0 ) ) { + return -4; + } + + brdf_lut(); + + // ------------------------------------------------------------------------- + // Mainloop + float model_yaw = 0, model_pitch = 0; + float lightYaw = 0.0f; + float lightPitch = 0.0f; + vec4 skyBackgroundColor = vec4(0.01,0.01,0.02,1); // vec4(1,0,0,1); + float skyExposure = 1.0; // plain 'exposure' instead? this is camera related + float skyBlur = 0.00; // 0.00 + float skyOpacity = 0.99; // 0.99 + bool do_wireframe = false; + bool do_xzySpace = true; // xzySpace or xyzSpace + bool do_flipY = false; + const mat44 xzyMatrix = { + 1, 0, 0, 0, + 0, 0, 1, 0, + 0,+1, 0, 0, + 0, 0, 0, 1 }; + + camera_t cam = camera(); cam.speed = 0.1; + + int firstskyboxes = 0; // 0: tokyo_bigsight + SkyboxLoad( &g_skybox, &skyboxes[firstskyboxes][0] ); + lightYaw = g_skybox.sunYaw; + lightPitch = g_skybox.sunPitch; + + unsigned skysphereShader = shader( vfs_read("Skyboxes/skysphere.vs"), vfs_read("Skyboxes/skysphere.fs"), NULL, NULL ); + Model skysphere = { 0 }; ModelLoad(&skysphere, "Skyboxes/skysphere.fbx"); ModelRebind(&skysphere, skysphereShader); + + if( ModelLoad( &gModel, argc > 1 && argv[1][0] != '-' ? argv[ 1 ] : "damagedhelmet.gltf" ) ) { + ModelRebind( &gModel, gShader ); + } + + cam.position = vec3(+1,0,+1); + camera_fit(&cam); + + static mat44 worldRootXYZ; do_once id44(worldRootXYZ); // mat44( 1.0f ); + + while( window_swap() && !input(KEY_ESC) ) { + + if( input(KEY_F5) ) window_reload(); + if( input_down( KEY_F ) ) camera_fit(&cam); + + // --------------------------------------------------------------------- + + static int fps_mode; + if(input_down(KEY_TAB)) { fps_mode ^= 1; camera_fit(&cam); } + if(fps_mode) { + // fps camera + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + window_cursor( !active ); + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z); + camera_fps(&cam, mouse.x,mouse.y); + } else { + // orbit camera + window_cursor( true ); + bool active = !ui_active() && !ui_hover() && !gizmo_active(); + vec2 inc_mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active * input(MOUSE_L)); + float inc_distance = -0.2f * active * input_diff(MOUSE_W); + camera_orbit(&cam, inc_mouse.x, inc_mouse.y, inc_distance); + // rotate model + model_yaw -= input_diff(MOUSE_X) * 0.2f * active * input(MOUSE_R); + model_pitch += input_diff(MOUSE_Y) * 0.2f * active * input(MOUSE_R); + } + + // --------------------------------------------------------------------- + + glClearColor( skyBackgroundColor.r, skyBackgroundColor.g, skyBackgroundColor.b, skyBackgroundColor.a ); + glEnable(GL_CULL_FACE); + glFrontFace(GL_CCW); + + // --------------------------------------------------------------------- + // Mesh state + + fx_begin(); + + profile("PBR Model (bindings)") { + ModelRebind( &gModel, gShader ); + + shader_mat44( "mat_projection", cam.proj ); + + //cameraPosition = scale3(cameraPosition, gCameraDistance); + shader_vec3( "camera_position", cam.position ); + + vec3 lightDirection = vec3( 0, 0, 1 ); + lightDirection = rotatex3( lightDirection, deg(lightPitch) ); + lightDirection = rotatey3( lightDirection, deg(lightYaw) ); + + vec3 fillLightDirection = vec3( 0, 0, 1 ); + fillLightDirection = rotatex3( fillLightDirection, deg(lightPitch - 0.4f) ); + fillLightDirection = rotatey3( fillLightDirection, deg(lightYaw + 0.8f) ); + + shader_vec3( "lights[0].direction", lightDirection ); + shader_vec3( "lights[0].color", g_skybox.sunColor ); + shader_vec3( "lights[1].direction", fillLightDirection ); + shader_vec3( "lights[1].color", vec3( 0.5f, 0.5f, 0.5f ) ); + shader_vec3( "lights[2].direction", neg3(fillLightDirection) ); + shader_vec3( "lights[2].color", vec3( 0.25f, 0.25f, 0.25f ) ); + + shader_float( "skysphere_rotation", lightYaw - g_skybox.sunYaw ); + + mat44 viewMatrix, inv_viewMatrix; + copy44(viewMatrix, cam.view); + invert44( inv_viewMatrix, viewMatrix); + shader_mat44( "mat_view", viewMatrix ); + shader_mat44( "mat_view_inverse", inv_viewMatrix ); + + shader_bool( "has_tex_skysphere", g_skybox.reflection != NULL ); + shader_bool( "has_tex_skyenv", g_skybox.env != NULL ); + if( g_skybox.reflection ) { + float mipCount = floor( log2( g_skybox.reflection->h ) ); + shader_texture( "tex_skysphere", *g_skybox.reflection ); + shader_float( "skysphere_mip_count", mipCount ); + } + if( g_skybox.env ) { + shader_texture( "tex_skyenv", *g_skybox.env ); + } + shader_texture( "tex_brdf_lut", brdf_lut() ); + shader_float( "exposure", skyExposure ); + shader_uint( "frame_count", (unsigned)window_frame() ); + } + + // --------------------------------------------------------------------- + // Mesh render + + mat44 M; + copy44( M, do_xzySpace ? xzyMatrix : worldRootXYZ ); + if( do_flipY ) scale44( M, 1,-1,1 ); + rotate44( M, model_yaw, 0,0,1 ); + rotate44( M, model_pitch, 1,0,0 ); + + profile("PBR Model (render)") { + ModelRender( &gModel, M ); + } + + profile("PBR Model (wireframe)") { + if( do_wireframe ) { + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + glDepthFunc( GL_LEQUAL ); + + shader_float("exposure", 100.0f ); + ModelRender( &gModel, M ); + + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + glDepthFunc( GL_LESS ); + } + } + + // --------------------------------------------------------------------- + // Skysphere render + + profile("PBR Skybox") { + ModelRebind(&skysphere, skysphereShader ); + + mat44 projview; multiply44x2(projview, cam.proj, cam.view); + shader_mat44( "mat_mvp", projview ); + + shader_bool( "has_tex_skysphere", g_skybox.reflection != NULL ); + shader_bool( "has_tex_skyenv", g_skybox.env != NULL ); + + if( g_skybox.reflection ) { + const float mipCount = floor( log2( g_skybox.reflection->h ) ); + shader_texture( "tex_skysphere", *g_skybox.reflection ); + shader_float( "skysphere_mip_count", mipCount ); + } + + if( g_skybox.env ) { + shader_texture( "tex_skyenv", *g_skybox.env ); + } + + shader_vec4( "background_color", skyBackgroundColor ); + shader_float( "skysphere_blur", skyBlur ); + shader_float( "skysphere_opacity", skyOpacity ); + shader_float( "skysphere_rotation", lightYaw - g_skybox.sunYaw ); + shader_float( "exposure", skyExposure ); + shader_uint( "frame_count", (unsigned)window_frame() ); + + glDepthFunc( GL_LEQUAL ); + ModelRender(&skysphere, worldRootXYZ ); + glDepthFunc( GL_LESS ); + } + + fx_end(); + + // --------------------------------------------------------------------- + // UI + + if( ui_panel( "Viewer", 0 ) ) { + ui_bool( "Wireframe", &do_wireframe ); + ui_separator(); + + if( ui_radio("Shader config:", shader_names, countof(shader_names), &gShaderConfig) ) { + LoadShaderConfig( gShaderConfig ); + ModelRebind(&gModel, gShader ); + } + + ui_separator(); + for( int i = 0; i < countof(skyboxes); i++ ) { + const char *filename = skyboxes[i][0]; + bool selected = !strcmp(g_skybox.reflection->filename, file_name(filename)); + if( ui_bool( filename, &selected ) ) { + SkyboxLoad( &g_skybox, &skyboxes[i][0] ); + lightYaw = g_skybox.sunYaw; + lightPitch = g_skybox.sunPitch; + } + } + + ui_separator(); + ui_float( "Sky exposure", &skyExposure); skyExposure = clampf(skyExposure, 0.1f, 4.0f ); + ui_float( "Sky blur", &skyBlur); skyBlur = clampf(skyBlur, 0.0f, 1.0f ); + ui_float( "Sky opacity", &skyOpacity); skyOpacity = clampf(skyOpacity, 0.0f, 1.0f ); + ui_color4f( "Sky background", (float *) &skyBackgroundColor.x ); + + ui_separator(); + ui_float( "SunLight Yaw", &lightYaw ); + ui_float( "SunLight Pitch", &lightPitch ); + + ui_panel_end(); + } + + if( ui_panel( "Model", 0 ) ) { + ui_label(va("Material count: %d", array_count(gModel.materials))); + ui_label(va("Mesh count: %d", array_count(gModel.meshes))); + int triCount = 0; for( int i = 0, end = array_count(gModel.meshes); i < end; ++i ) triCount += gModel.meshes[i].num_tris; + ui_label(va("Triangle count: %d", triCount)); + ui_separator(); + + bool xyzSpace = !do_xzySpace; + if( ui_bool( "XYZ space", &xyzSpace ) ) { + do_xzySpace = !do_xzySpace; + } + ui_bool( "XZY space", &do_xzySpace ); + ui_bool( "invert Y", &do_flipY ); + + ui_separator(); + for( int i = 0, end = array_count(gModel.materials); i < end; ++i ) { + pbr_material_t *it = &gModel.materials[i]; + ui_label(va("Name: %s", it->name)); + ui_float( "Specular shininess", &it->specular_shininess ); + ui_separator(); if(ui_colormap( "Albedo", &it->albedo )) colormap(&it->albedo , dialog_load(), 1); + ui_separator(); if(ui_colormap( "Ambient", &it->ambient )) colormap(&it->ambient , dialog_load(), 0); + ui_separator(); if(ui_colormap( "AO", &it->ao )) colormap(&it->ao , dialog_load(), 0); + ui_separator(); if(ui_colormap( "Diffuse", &it->diffuse )) colormap(&it->diffuse , dialog_load(), 1); + ui_separator(); if(ui_colormap( "Emissive", &it->emissive )) colormap(&it->emissive , dialog_load(), 1); + ui_separator(); if(ui_colormap( "Metallic", &it->metallic )) colormap(&it->metallic , dialog_load(), 0); + ui_separator(); if(ui_colormap( "Normal", &it->normals )) colormap(&it->normals , dialog_load(), 0); + ui_separator(); if(ui_colormap( "Roughness", &it->roughness )) colormap(&it->roughness, dialog_load(), 0); + ui_separator(); if(ui_colormap( "Specular", &it->specular )) colormap(&it->specular , dialog_load(), 0); + } + + ui_panel_end(); + } + + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, fx_enabled(i) ^ 1); + } + ui_panel_end(); + } + + if( ui_panel("Help", 0)) { + if( fps_mode ) { + ui_label("TAB: switch to Orbit camera mode"); + ui_label("WASD,QEC: move camera"); + ui_label("Drag + Mouse Button: camera freelook"); + } else { + ui_label("TAB: switch to FPS camera mode"); + ui_label("Drag + Left Mouse Button: orbit camera"); + ui_label("Drag + Right Mouse Button: rotate model"); + ui_label("Mouse wheel: camera distance"); + } + ui_label("F: center view"); + ui_panel_end(); + } + } +} diff --git a/demos/99-script.c b/demos/99-script.c new file mode 100644 index 0000000..a927a79 --- /dev/null +++ b/demos/99-script.c @@ -0,0 +1,28 @@ +#include "fwk.h" + +#define SCRIPT(...) #__VA_ARGS__ + +#if 0 // teal + script_run("local tl=require(\"tl\")\ntl.loader()"); + script_run("addsub = require(\"s2\"); print (addsub.add(10, 20))"); +s2.tl: + local function add(a: number, b: number): number + return a + b + end + local s = add(1,2) + print(s) +#endif + +int main() { + script_init(); + + script_run(SCRIPT( + window_create(75.0,0); + while window_swap() do + ddraw_grid(10); + if ui_panel("Hello from Lua!", 0) then + ui_panel_end(); + end; + end; + )); +} diff --git a/demos/99-shadertoy.c b/demos/99-shadertoy.c new file mode 100644 index 0000000..e0f5951 --- /dev/null +++ b/demos/99-shadertoy.c @@ -0,0 +1,43 @@ +// shadertoy viewer +// - rlyeh, public domain + +#include "fwk.h" + +int main() { + window_create(75, 0); // WINDOW_MSAA8); + window_title(__FILE__); + + const char **list = file_list("demos/art/shadertoys/", "**.fs"); + if(!list[0]) exit(-1); + array(char*) browser = 0; + while(*list) array_push(browser, STRDUP(file_name(*list++))); + int browser_count = array_count(browser); + + shadertoy_t sh = {0}; + + while(window_swap()) { + // selector + int next = input_down(KEY_UP) || input_down(KEY_LEFT); + int prev = input_down(KEY_DOWN) || input_down(KEY_RIGHT); + static int selector = 0; + static int reload = 1; + if( next ) if( selector > 0 ) --selector, reload = 1; + if( prev ) if( selector < browser_count - 1 ) ++selector, reload = 1; + if( reload ) { + reload = 0; + window_title(va("FWK - %s", browser[selector])); + sh = shadertoy( browser[selector], 0 ); + } + + // draw + shadertoy_render(&sh, window_delta()); + + // UI + if( ui_panel("Shadertoy", 0)) { + if( ui_list("In use", (const char**)browser, browser_count, &selector) ) { + reload = 1; + } + ui_panel_end(); + } + } +} diff --git a/demos/99-spine.c b/demos/99-spine.c new file mode 100644 index 0000000..746b194 --- /dev/null +++ b/demos/99-spine.c @@ -0,0 +1,568 @@ +// spine json loader (wip) +// - rlyeh, public domain +// +// [ref] http://es.esotericsoftware.com/spine-json-format +// +// notable misses: +// - mesh deforms +// - cubic beziers +// - shears +// - bounding boxes + +#include "fwk.h" +#define spine spine2 +#define spine_render spine_render2 +#define spine_ui spine_ui2 +#define spine_animate spine_animate2 +#define spine_skin spine_skin2 + +enum { _64 = 64 }; // max bones + +typedef struct spine_bone_t { + char *name, *parent; + struct spine_bone_t *parent_bone; + + float z; // draw order usually matches bone-id. ie, zindex == bone_id .. root(0) < chest (mid) < finger(top) + + float x, y, deg; // base + float x2, y2, deg2; // accum / temporaries during bone transform time + float x3, y3, deg3; // values from timeline + + unsigned rect_id; + unsigned atlas_id; +} spine_bone_t; + +typedef struct spine_slot_t { + char *name, *bone, *attach; +} spine_slot_t; + +typedef struct spine_rect_t { + char *name; + float x,y,w,h,sx,sy,deg; +} spine_rect_t; + +typedef struct spine_skin_t { + char *name; + array(spine_rect_t) rects; +} spine_skin_t; + +typedef struct spine_animkey_t { // offline; only during loading + float time, curve[4]; // time is mandatory, curve is optional + union { + char *name; // type: attachment (mode-1) + struct { float deg; }; // type: rotate (mode-2) + struct { float x,y; }; // type: translate (mode-3) + }; +} spine_animkey_t; + +#if 0 +typedef struct spine_pose_t { // runtime; only during playing + unsigned frame; + array(vec4) xform; // entry per bone. translation(x,y),rotation(z),attachment-id(w) +} spine_pose_t; +#endif + +typedef struct spine_anim_t { + char *name; + union { +#if 0 + struct { + unsigned frames; + array(spine_pose_t) poses; + }; +#endif + struct { + array(spine_animkey_t) attach_keys[_64]; + array(spine_animkey_t) rotate_keys[_64]; + array(spine_animkey_t) translate_keys[_64]; + }; + }; +} spine_anim_t; + +typedef struct spine_atlas_t { + char *name; + float x,y,w,h,deg; +} spine_atlas_t; + +typedef struct spine_t { + char *name; + texture_t texture; + unsigned skin; + array(spine_bone_t) bones; + array(spine_slot_t) slots; + array(spine_skin_t) skins; + array(spine_anim_t) anims; + array(spine_atlas_t) atlas; + // anim controller + unsigned inuse; + float time, maxtime; +} spine_t; + +// --- + +void spine_convert_animkeys_to_animpose(spine_anim_t *input) { + spine_anim_t copy = *input; // @todo + // @leak: attach/rot/tra keys +} + +int find_bone_id(spine_t *s, const char *bone_name) { + for( unsigned i = 0, end = array_count(s->bones); i < end; ++i ) + if( !strcmp(s->bones[i].name, bone_name)) return i; + return -1; +} +spine_bone_t *find_bone(spine_t *s, const char *bone_name) { + int bone_id = find_bone_id(s, bone_name); + return bone_id >= 0 ? &s->bones[bone_id] : NULL; +} + +void spine_skin(spine_t *p, unsigned skin) { + if( !p->texture.id ) return; + if( skin >= array_count(p->skins) ) return; + + p->skin = skin; + + char *skin_name = va("%s/", p->skins[skin].name); + int header = strlen(skin_name); + + for( int i = 0; i < array_count(p->atlas); ++i) { + if(!strbeg(p->atlas[i].name, skin_name)) continue; + + int bone_id = find_bone_id(p, p->atlas[i].name+header ); + if( bone_id < 0 ) continue; + + p->bones[bone_id].atlas_id = i; + } + + for( int i = 0; i < array_count(p->skins[p->skin].rects); ++i) { + int bone_id = find_bone_id(p, p->skins[p->skin].rects[i].name ); + if( bone_id < 0 ) continue; + + p->bones[bone_id].rect_id = i; + } +} + +spine_t spine(const char *file_json, const char *file_atlas, unsigned flags) { + spine_t z = {0}, t = z; + + char *atlas = vfs_read(file_atlas); + if(!atlas || !atlas[0]) return z; + + // goblins.png + // size: 1024, 128 + // filter: Linear, Linear + // pma: true + // dagger + // bounds: 2, 18, 26, 108 + // goblin/eyes-closed + // bounds: 2, 4, 34, 12 + spine_atlas_t *sa = 0; + const char *last_id = 0; + const char *texture_name = 0; + const char *texture_filter = 0; + const char *texture_format = 0; + const char *texture_repeat = 0; + float texture_width = 0, texture_height = 0, temp; + for each_substring(atlas, "\r\n", it) { + it += strspn(it, " \t\f\v"); + /**/ if( strbeg(it, "pma:" ) || strbeg(it, "index:") ) {} // ignored + else if( strbeg(it, "size:" ) ) sscanf(it+5, "%f,%f", &texture_width, &texture_height); + else if( strbeg(it, "rotate:" ) ) { float tmp; tmp=sa->w,sa->w=sa->h,sa->h=tmp; sa->deg = 90; } // assert(val==90) + else if( strbeg(it, "repeat:" ) ) texture_repeat = it+7; // temp string + else if( strbeg(it, "filter:" ) ) texture_filter = it+7; // temp string + else if( strbeg(it, "format:" ) ) texture_format = it+7; // temp string + else if( strbeg(it, "bounds:" ) ) { + sscanf(it+7, "%f,%f,%f,%f", &sa->x, &sa->y, &sa->w, &sa->h); + } + else if( !texture_name ) texture_name = va("%s", it); + else { + array_push(t.atlas, ((spine_atlas_t){0}) ); + sa = &t.atlas[array_count(t.atlas) - 1]; + sa->name = STRDUP(it); + } + } + for( int i = 0; i < array_count(t.atlas); ++i ) { + sa = &t.atlas[i]; + sa->x /= texture_width, sa->y /= texture_height; + sa->w /= texture_width, sa->h /= texture_height; + } + + if(!texture_name) return z; + + t.texture = texture(texture_name, 0); // @todo: add texture flags here + + json_push(vfs_read(file_json)); // @fixme: json_push_from_file() ? + + array_resize(t.bones, json_count("/bones")); + array_reserve(t.slots, json_count("/slots")); + array_resize(t.skins, json_count("/skins")); + array_resize(t.anims, json_count("/animations")); + + for( int i = 0, end = json_count("/bones"); i < end; ++i ) { + spine_bone_t v = {0}; + v.name = STRDUP(json_string("/bones[%d]/name", i)); + v.parent = STRDUP(json_string("/bones[%d]/parent", i)); + v.x = json_float("/bones[%d]/x", i); + v.y = json_float("/bones[%d]/y", i); + v.z = i; + v.deg = json_float("/bones[%d]/rotation", i); + t.bones[i] = v; + + for( int j = i-1; j > 0; --j ) { + if( strcmp(t.bones[j].name,v.parent) ) continue; + t.bones[i].parent_bone = &t.bones[j]; + break; + } + } + + for( int i = 0, end = json_count("/slots"); i < end; ++i ) { + spine_slot_t v = {0}; + v.name = STRDUP(json_string("/slots[%d]/name", i)); + v.bone = STRDUP(json_string("/slots[%d]/bone", i)); + v.attach = STRDUP(json_string("/slots[%d]/attachment", i)); + + array_push(t.slots, v); + + // slots define draw-order. so, update draw-order/zindex in bone + spine_bone_t *b = find_bone(&t, v.name); + if( b ) b->z = i; + } + + for( int i = 0, end = json_count("/skins"); i < end; ++i ) { + spine_skin_t v = {0}; + v.name = STRDUP(json_string("/skins[%d]/name", i)); + + for( int j = 0, jend = json_count("/skins[%d]/attachments",i); j < jend; ++j ) // /skins/default/ + for( int k = 0, kend = json_count("/skins[%d]/attachments[%d]",i,j); k < kend; ++k ) { // /skins/default/left hand item/ + spine_rect_t r = {0}; + r.name = STRDUP(json_key("/skins[%d]/attachments[%d][%d]",i,j,k)); // stringf("%s-%s-%s", json_key("/skins[%d]",i), json_key("/skins[%d][%d]",i,j), json_key("/skins[%d][%d][%d]",i,j,k)); + r.x = json_float("/skins[%d]/attachments[%d][%d]/x",i,j,k); + r.y = json_float("/skins[%d]/attachments[%d][%d]/y",i,j,k); + r.sx= json_float("/skins[%d]/attachments[%d][%d]/scaleX",i,j,k); r.sx += !r.sx; + r.sy= json_float("/skins[%d]/attachments[%d][%d]/scaleY",i,j,k); r.sy += !r.sy; + r.w = json_float("/skins[%d]/attachments[%d][%d]/width",i,j,k); + r.h = json_float("/skins[%d]/attachments[%d][%d]/height",i,j,k); + r.deg = json_float("/skins[%d]/attachments[%d][%d]/rotation",i,j,k); + array_push(v.rects, r); + } + + t.skins[i] = v; + } + +#if 1 + // simplify: + // merge /skins/default into existing /skins/*, then delete /skins/default + if( array_count(t.skins) > 1 ) { + for( int i = 1; i < array_count(t.skins); ++i ) { + for( int j = 0; j < array_count(t.skins[0].rects); ++j ) { + array_push(t.skins[i].rects, t.skins[0].rects[j]); + } + } + // @leak @fixme: free(t.skins[0]) + for( int i = 0; i < array_count(t.skins)-1; ++i ) { + t.skins[i] = t.skins[i+1]; + } + array_pop(t.skins); + } +#endif + + for( int i = 0, end = json_count("/animations"); i < end; ++i ) { + int id; + const char *name; + + spine_anim_t v = {0}; + v.name = STRDUP(json_key("/animations[%d]", i)); + + // slots / attachments + + for( int j = 0, jend = json_count("/animations[%d]/slots",i); j < jend; ++j ) + for( int k = 0, kend = json_count("/animations[%d]/slots[%d]",i,j); k < kend; ++k ) // ids + { + int bone_id = find_bone_id(&t, json_key("/animations[%d]/bones[%d]",i,j)); + if( bone_id < 0 ) continue; + + for( int l = 0, lend = json_count("/animations[%d]/slots[%d][%d]",i,j,k); l < lend; ++l ) { // channels (rot,tra,attach) + spine_animkey_t key = {0}; + + key.name = STRDUP(json_string("/animations[%d]/slots[%d][%d][%d]/name",i,j,k,l)); + key.time = json_float("/animations[%d]/slots[%d][%d][%d]/time",i,j,k,l); + if( json_count("/animations[%d]/slots[%d][%d][%d]/curve",i,j,k,l) == 4 ) { + key.curve[0] = json_float("/animations[%d]/slots[%d][%d][%d]/curve[0]",i,j,k,l); + key.curve[1] = json_float("/animations[%d]/slots[%d][%d][%d]/curve[1]",i,j,k,l); + key.curve[2] = json_float("/animations[%d]/slots[%d][%d][%d]/curve[2]",i,j,k,l); + key.curve[3] = json_float("/animations[%d]/slots[%d][%d][%d]/curve[3]",i,j,k,l); + } + + // @todo: convert name to id + // for(id = 0; t.bones[id].name && strcmp(t.bones[id].name,key.name); ++id) + // printf("%s vs %s\n", key.name, t.bones[id].name); + + array_push(v.attach_keys[bone_id], key); + } + } + + // bones + + for( int j = 0, jend = json_count("/animations[%d]/bones",i); j < jend; ++j ) // slots or bones + for( int k = 0, kend = json_count("/animations[%d]/bones[%d]",i,j); k < kend; ++k ) { // bone ids + int bone_id = find_bone_id(&t, json_key("/animations[%d]/bones[%d]",i,j)); + if( bone_id < 0 ) continue; + + // parse bones + for( int l = 0, lend = json_count("/animations[%d]/bones[%d][%d]",i,j,k); l < lend; ++l ) { // channels (rot,tra,attach) + const char *channel = json_key("/animations[%d]/bones[%d][%d]",i,j,k); + int track = !strcmp(channel, "rotate") ? 1 : !strcmp(channel, "translate") ? 2 : 0; + if( !track ) continue; + + spine_animkey_t key = {0}; + + key.time = json_float("/animations[%d]/bones[%d][%d][%d]/time",i,j,k,l); + if( json_count("/animations[%d]/bones[%d][%d][%d]/curve",i,j,k,l) == 4 ) { + key.curve[0] = json_float("/animations[%d]/bones[%d][%d][%d]/curve[0]",i,j,k,l); + key.curve[1] = json_float("/animations[%d]/bones[%d][%d][%d]/curve[1]",i,j,k,l); + key.curve[2] = json_float("/animations[%d]/bones[%d][%d][%d]/curve[2]",i,j,k,l); + key.curve[3] = json_float("/animations[%d]/bones[%d][%d][%d]/curve[3]",i,j,k,l); + } + + if( track == 1 ) + key.deg = json_float("/animations[%d]/bones[%d][%d][%d]/value",i,j,k,l), // "/angle" + array_push(v.rotate_keys[bone_id], key); + else + key.x = json_float("/animations[%d]/bones[%d][%d][%d]/x",i,j,k,l), + key.y = json_float("/animations[%d]/bones[%d][%d][%d]/y",i,j,k,l), + array_push(v.translate_keys[bone_id], key); + } + } + + t.anims[i] = v; + } + + json_pop(); + + spine_skin(&t, 0); + + return t; +} + +void spine_render(spine_t *p, vec3 offset, unsigned flags) { + if( !p->texture.id ) return; + if( !flags ) return; + + ddraw_push_2d(); + if( flags & 2 ) ddraw_line(vec3(0,0,0), vec3(window_width(),window_height(),0)); + if( flags & 2 ) ddraw_line(vec3(window_width(),0,0), vec3(0,window_height(),0)); + + for( int i = 1; i < array_count(p->bones); ++i ) { + spine_bone_t *self = &p->bones[i]; + + static array(spine_bone_t*) chain = 0; array_resize(chain, 0); + for( spine_bone_t *next = self; next ; next = next->parent_bone ) { + array_push(chain, next); + } + + vec3 target = {0}, prev = {0}; + for( int j = 1, end = array_count(chain); j < end; ++j ) { // traverse from root(skipped) -> `i` bone direction + int j_opposite = (end - 1) - j; + + spine_bone_t *b = chain[j_opposite]; // bone + spine_bone_t *pb = chain[j_opposite+1]; // parent bone + + prev = target; + + const float deg2rad = C_PI / 180; + b->x2 = b->x3 + pb->x2 + b->x * cos( -pb->deg2 * deg2rad ) - b->y * sin( -pb->deg2 * deg2rad ); + b->y2 = -b->y3 + pb->y2 - b->y * cos( pb->deg2 * deg2rad ) + b->x * sin( pb->deg2 * deg2rad ); + b->deg2 = -b->deg3 + pb->deg2 - b->deg; + + target = vec3(b->x2,b->y2,b->deg2); + } + + float deg = target.z, deg_prev = prev.z; + target.z = 0; prev.z = 0; + + target = add3(target, offset); + prev = add3(prev, offset); + + if( flags & 2 ) { + ddraw_point( target ); + ddraw_text( target, -0.25f, self->name ); + ddraw_line( target, prev ); // from bone to parent + } + if( flags & 1 ) { + vec4 rect = ptr4(&p->atlas[self->atlas_id].x); + float zindex = self->z; + float offsx = 0; // -(rect.w * p->texture.w); // -p->atlas[self->atlas_id].w - (self->rect_id ? p->skins[p->skin].rects[self->rect_id].w/2 : 0); + float offsy = 0; // /*-(rect.z * p->texture.h)*2*/ -p->atlas[self->atlas_id].h - (self->rect_id ? p->skins[p->skin].rects[self->rect_id].h/2 : 0); + float deg_rect = self->rect_id ? p->skins[p->skin].rects[self->rect_id].deg : 0; + float tilt = p->atlas[self->atlas_id].deg + self->deg2 - deg_rect; // + self->deg2 + deg_rect + p->atlas[self->atlas_id].deg + unsigned tint = ~0u; + sprite_rect(p->texture, rect, zindex, add3(vec3(target.x,target.y,1),vec3(offsx,offsy,0)), tilt, tint); + } + } + + ddraw_pop_2d(); + ddraw_flush(); +} + +void spine_animate(spine_t *p, float *time, float *maxtime, float delta) { + if( !p->texture.id ) return; + + if( delta > 1/120.f ) delta = 1/120.f; + if( *time >= *maxtime ) *time = 0; else *time += delta; + + // reset root // needed? + p->bones[0].x2 = 0; + p->bones[0].y2 = 0; + p->bones[0].deg2 = 0; + p->bones[0].x3 = 0; + p->bones[0].y3 = 0; + p->bones[0].deg3 = 0; + + for( int i = 0, end = array_count(p->bones); i < end; ++i) { + // @todo: attach channel + // @todo: per channel: if curve == linear || curve == stepped || array_count(curve) == 4 {...} + for each_array_ptr(p->anims[p->inuse].rotate_keys[i], spine_animkey_t, r) { + double r0 = r->time; + *maxtime = maxf( *maxtime, r0 ); + if( absf(*time - r0) < delta ) { + p->bones[i].deg3 = r->deg; + } + } + for each_array_ptr(p->anims[p->inuse].translate_keys[i], spine_animkey_t, r) { + double r0 = r->time; + *maxtime = maxf( *maxtime, r0 ); + if( absf(*time - r0) < delta ) { + p->bones[i].x3 = r->x; + p->bones[i].y3 = r->y; + } + } + } +} + +void spine_ui(spine_t *p) { + + if( ui_collapse(va("Anims: %d", array_count(p->anims)), va("%p-a", p))) { + for each_array_ptr(p->anims, spine_anim_t, q) { + if(ui_slider2("", &p->time, va("%.2f/%.0f %.2f%%", p->time, p->maxtime, p->time * 100.f))) { + spine_animate(p, &p->time, &p->maxtime, 0); + } + + int choice = ui_label2_toolbar(q->name, ICON_MD_PAUSE_CIRCLE " " ICON_MD_PLAY_CIRCLE); + if( choice == 1 ) window_pause( 0 ); // play + if( choice == 2 ) window_pause( 1 ); // pause + + for( int i = 0; i < _64; ++i ) { + ui_separator(); + ui_label(va("Bone %d: Attachment keys", i)); + for each_array_ptr(q->attach_keys[i], spine_animkey_t, r) { + ui_label(va("%.2f [%.2f %.2f %.2f %.2f] %s", r->time, r->curve[0], r->curve[1], r->curve[2], r->curve[3], r->name)); + } + ui_label(va("Bone %d: Rotate keys", i)); + for each_array_ptr(q->rotate_keys[i], spine_animkey_t, r) { + ui_label(va("%.2f [%.2f %.2f %.2f %.2f] %.2f deg", r->time, r->curve[0], r->curve[1], r->curve[2], r->curve[3], r->deg)); + } + ui_label(va("Bone %d: Translate keys", i)); + for each_array_ptr(q->translate_keys[i], spine_animkey_t, r) { + ui_label(va("%.2f [%.2f %.2f %.2f %.2f] (%.2f,%.2f)", r->time, r->curve[0], r->curve[1], r->curve[2], r->curve[3], r->x, r->y)); + } + } + } + ui_collapse_end(); + } + if( ui_collapse(va("Bones: %d", array_count(p->bones)), va("%p-b", p))) { + for each_array_ptr(p->bones, spine_bone_t, q) + if( ui_collapse(q->name, va("%p-b2", q)) ) { + ui_label2("Parent:", q->parent); + ui_label2("X:", va("%.2f", q->x)); + ui_label2("Y:", va("%.2f", q->y)); + ui_label2("Rotation:", va("%.2f", q->deg)); + ui_collapse_end(); + } + ui_collapse_end(); + } + if( ui_collapse(va("Slots: %d", array_count(p->slots)), va("%p-s", p))) { + for each_array_ptr(p->slots, spine_slot_t, q) + if( ui_collapse(q->name, va("%p-s2", q)) ) { + ui_label2("Bone:", q->bone); + ui_label2("Attachment:", q->attach); + ui_collapse_end(); + } + ui_collapse_end(); + } + if( ui_collapse(va("Skins: %d", array_count(p->skins)), va("%p-k", p))) { + for each_array_ptr(p->skins, spine_skin_t, q) + if( ui_collapse(q->name, va("%p-k2", q)) ) { + for each_array_ptr(q->rects, spine_rect_t, r) + if( ui_collapse(r->name, va("%p-k3", r)) ) { + ui_label2("X:", va("%.2f", r->x)); + ui_label2("Y:", va("%.2f", r->y)); + ui_label2("Scale X:", va("%.2f", r->sx)); + ui_label2("Scale Y:", va("%.2f", r->sy)); + ui_label2("Width:", va("%.2f", r->w)); + ui_label2("Height:", va("%.2f", r->h)); + ui_label2("Rotation:", va("%.2f", r->deg)); + ui_collapse_end(); + + + spine_bone_t *b = find_bone(p, r->name); + if( b ) { + static float tilt = 0; + if( input(KEY_LCTRL) ) tilt += 60*1/60.f; else tilt = 0; + spine_atlas_t *r = p->atlas + b->atlas_id; + sprite_flush(); + camera_get_active()->position = vec3(0,0,2); + vec4 rect = ptr4(&r->x); float zindex = 0; vec3 xy_zoom = vec3(0,0,0); unsigned tint = ~0u; + sprite_rect(p->texture, + // rect: vec4(r->x*1.0/p->texture.w,r->y*1.0/p->texture.h,(r->x+r->w)*1.0/p->texture.w,(r->y+r->h)*1.0/p->texture.h), + ptr4(&r->x), // atlas + 0, vec3(0,0,0), r->deg + tilt, tint); + sprite_flush(); + camera_get_active()->position = vec3(+window_width()/3,window_height()/2.25,2); + } + } + ui_collapse_end(); + } + ui_collapse_end(); + } + + if( ui_int("Use skin", &p->skin) ) { + p->skin = clampf(p->skin, 0, array_count(p->skins) - 1); + spine_skin(p, p->skin); + } + + if( p->texture.id ) ui_texture(0, p->texture); +} + + + +int main() { + window_create(0.75, 0); + + camera_t cam = camera(); + cam.position = vec3(0,0,1); + camera_enable(&cam); + + spine_t s = spine("goblins.json", "goblins.atlas", 0); + + while( window_swap() ) { + camera_get_active()->position.x = window_width()/2; + camera_get_active()->position.y = window_height()/2; + + static bool do_skin = 1, do_skel = 1; + spine_animate(&s, &s.time, &s.maxtime, !window_has_pause() * window_delta()); + + spine_render(&s, vec3(window_width()/2, window_height()/2, 0), do_skin ); + sprite_flush(); + glClear(GL_DEPTH_BUFFER_BIT); + spine_render(&s, vec3(window_width()/2, window_height()/2, 0), (do_skel*2)); + + if( ui_panel("Spine", 0) ) { + if(ui_button("Load")) { + s = spine("goblins.json", "goblins.atlas", 0); + } + spine_ui(&s); + if(ui_bool("Draw Skin", &do_skin)); + if(ui_bool("Draw Skeleton", &do_skel)); + ui_panel_end(); + } + } +} diff --git a/demos/99-sprite.c b/demos/99-sprite.c new file mode 100644 index 0000000..b2c7619 --- /dev/null +++ b/demos/99-sprite.c @@ -0,0 +1,229 @@ +// sprite routines +// - rlyeh, +// +// credits: original lovely demo by rxi (MIT License). +// see https://github.com/rxi/autobatch/tree/master/demo/cats + +#include "fwk.h" + +texture_t kids, catImage, shadowImage, inputs; +int NUM_SPRITES = 100, NUM_SPRITES_CHANGED = 1; + +typedef struct Cat { + int cat, flip; + double x, y; + double vx, vy; + double animSpeed; + double moveTimer; + double elapsed; +} Cat; + +void demo_cats() { + static array(Cat) cats = 0; + + // init + if( NUM_SPRITES_CHANGED ) { + NUM_SPRITES_CHANGED = 0; + + array_resize(cats, NUM_SPRITES); int i = 0; + for each_array_ptr(cats, Cat, c) { + randset(i++); + c->x = randf() * window_width(); + c->y = randf() * window_height(); + c->vx = c->vy = 0; + c->cat = randi(0, 4); + c->flip = randf() < 0.5; + c->animSpeed = 0.8 + randf() * 0.3; + c->moveTimer = 0; + c->elapsed = 0; + } + } + + // move + const float dt = 1/120.f; + const int appw = window_width(), apph = window_height(); + + enum { yscale = 1 }; + for( int i = 0; i < NUM_SPRITES; ++i ) { + Cat *c = &cats[i]; + // Add velocity to position //and wrap to screen + c->x += yscale * c->vx * dt; // % ; + c->y += yscale * c->vy * dt; // % (int)window_height(); + if( c->x < 0 ) c->x += appw; else if( c->x > appw ) c->x -= appw; + if( c->y < 0 ) c->y += apph; else if( c->y > apph ) c->y -= apph; + // Faster animation if walking + int boost = c->vx == 0 && c->vy == 0 ? 1 : 3; + // Update elapsed time + c->elapsed += dt * boost; + // Update move timer -- if we hit zero then change or zero velocity + c->moveTimer -= dt * boost; + if (c->moveTimer < 0) { + if (randf() < .2) { + c->vx = (randf() * 2 - 1) * 30 * 2; + c->vy = (randf() * 2 - 1) * 15 * 2; + c->flip = c->vx < 0; + } else { + c->vx = c->vy = 0; + } + c->moveTimer = 1 + randf() * 5; + } + } + + // render + uint32_t white = rgba(255,255,255,255); + uint32_t alpha = rgba(255,255,255,255*0.6); + for( int i = 0; i < NUM_SPRITES; ++i ) { + Cat *c = &cats[i]; + // Get current animation frame (8x4 tilesheet) + double e = c->elapsed * c->animSpeed; + double frame_num = c->cat * 8 + floor( ((int)(e * 8)) % 4 ); + frame_num = c->vx != 0 || c->vy != 0 ? frame_num + 4 : frame_num; + // Get x scale based on flip flag + int xscale = yscale * (c->flip ? -1 : 1); + // Draw + float angle = 0; //fmod(window_time()*360/5, 360); + float scale[2] = { 2*xscale, 2*yscale }; + float position[3] = { c->x,c->y,c->y }, no_offset[2] = {0,0}, spritesheet[3] = { frame_num,8,4 }; + sprite_sheet(catImage, + spritesheet, // frame_number in a 8x4 spritesheet + position, angle, // position(x,y,depth: sort by Y), angle + no_offset, scale, // offset(x,y), scale(x,y) + 0,white,0 // is_additive, tint color, resolution independant + ); + float position_neg_sort[3] = { c->x,c->y,-c->y }, offset[2] = {-1,5}, no_spritesheet[3] = {0,0,0}; + sprite_sheet(shadowImage, + no_spritesheet, // no frame_number (0x0 spritesheet) + position_neg_sort, angle, // position(x,y,depth: sort by Y), angle + offset, scale, // offset(x,y), scale(x,y) + 0,alpha,0 // is_additive, tint color, resolution independant + ); + } +} + +void demo_kids() { + static int angle; //++angle; + static int *x, *y, *v; + + // init + if( NUM_SPRITES_CHANGED ) { + NUM_SPRITES_CHANGED = 0; + + y = (int*)REALLOC(y, 0 ); + x = (int*)REALLOC(x, NUM_SPRITES * sizeof(int) ); + y = (int*)REALLOC(y, NUM_SPRITES * sizeof(int) ); + v = (int*)REALLOC(v, NUM_SPRITES * sizeof(int) ); + for( int i = 0; i < NUM_SPRITES; ++i ) { + randset(i); + x[i] = randi(0, window_width()); + y[i] = randi(0, window_height()); + v[i] = randi(1, 3); + } + } + + // config + const int appw = window_width(), apph = window_height(); + + // move & render + for( int i = 0; i < NUM_SPRITES; ++i ) { + y[i] = (y[i] + v[i]) % (apph + 128); + int col = ((x[i] / 10) % 4); // 4x4 tilesheet + int row = ((y[i] / 10) % 4); + int num_frame = col * 4 + row; + float position[3] = {x[i],y[i],y[i]}, offset[2]={0,0}, scale[2]={1,1}, spritesheet[3]={num_frame,4,4}; + sprite_sheet(kids, + spritesheet, // num_frame in a 4x4 spritesheet + position, angle, // position(x,y,depth: sort by Y), angle + offset, scale, // offset(x,y), scale(x,y) + 0, ~0u, 0 // is_additive, tint color, resolution independant + ); + } +} + +int main(int argc, char **argv) { + window_create(75.f, 0); + window_title("FWK - Sprite"); + + // options + int do_cats = 1; + NUM_SPRITES = optioni("--num_sprites,-N", NUM_SPRITES); + if(do_cats) NUM_SPRITES/=2; // cat-sprite+cat-shadow == 2 sprites + + // load sprites and sheets + kids = texture( "spriteSheetExample.png", TEXTURE_LINEAR ); + catImage = texture( "cat.png", TEXTURE_LINEAR ); // + shadowImage = texture( "cat-shadow.png", TEXTURE_LINEAR ); + inputs = texture( "prompts_tilemap_34x24_16x16x1.png", TEXTURE_LINEAR ); + + // load all post fx files + for(const char **list = file_list("./","fx**.fs"); *list; list++) { + fx_load(*list); + } + + // init camera (x,y) (z = zoom) + camera_t cam = camera(); + cam.position = vec3(window_width()/2,window_height()/2,1); + camera_enable(&cam); + + while(window_swap()) { + if( input(KEY_F5)) window_reload(); + if( input(KEY_F11)) window_fullscreen( window_has_fullscreen() ^ 1); + if( input(KEY_ESC) ) break; + + viewport_color3(vec3(0.4,0.4,0.4)); + + // camera panning (x,y) & zooming (z) + if( !ui_hover() && !ui_active() ) { + if( input(MOUSE_L) ) cam.position.x -= input_diff(MOUSE_X); + if( input(MOUSE_L) ) cam.position.y -= input_diff(MOUSE_Y); + cam.position.z += input_diff(MOUSE_W) * 0.1; // cam.p.z += 0.001f; for tests + } + + // apply post-fxs from here + fx_begin(); + + profile("Sprite batching") { + if(do_cats) demo_cats(); else demo_kids(); + } + + // flush retained renderer, so we ensure the fbos are up to date before fx_end() + profile("Sprite flushing") { + sprite_flush(); + } + + // post-fxs end here + fx_end(); + + // draw pixel-art hud, 16x16 ui element, scaled and positioned in resolution-independant way + { + vec3 old_pos = camera_get_active()->position; + + sprite_flush(); + camera_get_active()->position = vec3(window_width()/2,window_height()/2,1); + + float zindex = window_height(); // large number, on top + float spritesheet[3] = {17,34,24}, offset[2] = {0, - 2*absf(sin(window_time()*5))}; // sprite cell and animation + float scale[2] = {3, 3}, tile_w = 16 * scale[0], tile_h = 16 * scale[1]; // scaling + float position[3] = {window_width() - tile_w, window_height() - tile_h, zindex }; // position in screen-coordinates + sprite_sheet(inputs, spritesheet, position, 0/*rotation*/, offset, scale, false/*is_additive*/, WHITE/*color*/, false/*resolution_independant*/); + + sprite_flush(); + camera_get_active()->position = old_pos; + } + + if( ui_panel("Sprite", 0) ) { + const char *labels[] = {"Kids","Cats"}; + if( ui_list("Sprite type", labels, countof(labels), &do_cats) ) NUM_SPRITES_CHANGED = 1; + if( ui_int("Number of Sprites", &NUM_SPRITES) ) NUM_SPRITES_CHANGED = 1; + if( ui_clampf("Zoom", &cam.position.z, 0.1, 10)); + ui_panel_end(); + } + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, fx_enabled(i) ^ 1); + } + ui_panel_end(); + } + } +} diff --git a/demos/99-syncdemo.c b/demos/99-syncdemo.c new file mode 100644 index 0000000..fba76a6 --- /dev/null +++ b/demos/99-syncdemo.c @@ -0,0 +1,125 @@ +#define FWK_IMPLEMENTATION +#include "engine/joint/fwk.h" +#include "fwk_netsync.h" + +enum { MAX_NPCS = 5 }; +struct player_t { + uint64_t seen_until; + float x,y,z,angle; + uint32_t color; +}; +struct npc_t { + float x,y,z; + uint32_t color; +}; +struct world_t { + struct player_t player[MAX_CLIENTS]; + struct npc_t npc[MAX_NPCS]; +} world = {0}; + +char *show_notification(char *msg) { + printf("notif %s\n", msg); + ui_notify("server", msg); + return NULL; +} + +void bind_netbuffers(int64_t self_id) { + uint32_t colors[] = { ORANGE,GREEN,RED,CYAN,PURPLE,YELLOW,GRAY,PINK,AQUA }; + for (int64_t i=0; iclient rpc + if (self_id > 0) { + network_rpc("char* show_notification(char*)", show_notification); + } +} + +int main() { + // ifdef(win32, FreeConsole()); // tty_detach() + + // network setup + network_create("127.0.0.1", 0, flag("--client") ? NETWORK_CONNECT : 0); + int64_t self_id = network_get(NETWORK_RANK); + bind_netbuffers(self_id); + + // game setup + camera_t cam = camera(); + window_create( 0.35f, WINDOW_MSAA8|WINDOW_SQUARE ); + struct player_t *self = &world.player[self_id]; + + // game loop + while( window_swap() && !input(KEY_ESC) ) { + // network sync + char **event = network_sync(0); // timeout_ms:0 + while(*event) printf( "network event: %s\n", *event++ ); + + self_id = network_get(NETWORK_RANK); + if (network_get(NETWORK_LIVE) == 0) { + network_create("127.0.0.1", 0, flag("--client") ? NETWORK_CONNECT|NETWORK_NOFAIL : 0); + self_id = network_get(NETWORK_RANK); + if (self_id != -1) { + bind_netbuffers(self_id); + } + continue; + } + + /* quick hack to broadcast notif from host */ + if (self_id == 0 && input_down(KEY_F3)) { + printf("rpc %s\n", "show_notification \"hi, sailor!\""); + network_rpc_send(rand()%4, "show_notification \"hi, sailor!\""); + } + + // camera tracking + cam.position = vec3(self->x,100,self->z); + camera_lookat(&cam, vec3(self->x,0,self->z)); + + // input - move player + float iy = input(KEY_UP) - input(KEY_DOWN); + float ix = input(KEY_RIGHT) - input(KEY_LEFT); + if( iy || ix ) { + self->x += iy*window_delta()*15; + self->z += ix*window_delta()*15; + } + self->seen_until = date_epoch() + 4; + + // npc - update npc movement on server-side + if (self_id == 0) { + for (int i = 0; i < MAX_NPCS; ++i) { + struct npc_t *n = &world.npc[i]; + n->z = sinf(window_time())*4.f; + } + } + + // background - draw grid + ddraw_ground(0); + + // foreground - draw all players + for( int id = 0; id < MAX_CLIENTS; ++id ) { + struct player_t *p = &world.player[id]; + if (p->seen_until < date_epoch()) continue; /* skip inactive players */ + ddraw_color( p->color ); + ddraw_capsule(vec3(p->x,0,p->z), vec3(p->x,2,p->z), 1); + ddraw_text(vec3(p->x,4,p->z), 0.01, va("player #%d", id)); + } + for( int id = 0; id < MAX_NPCS; ++id ) { + struct npc_t *p = &world.npc[id]; + ddraw_color( p->color ); + ddraw_capsule(vec3(p->x,0,p->z), vec3(p->x,2,p->z), 1); + ddraw_text(vec3(p->x,4,p->z), 0.01, va("npc #%d", id)); + } + + // stats + window_title(va("player #%lld", self_id)); + } +} diff --git a/demos/99-video.c b/demos/99-video.c new file mode 100644 index 0000000..10d9370 --- /dev/null +++ b/demos/99-video.c @@ -0,0 +1,44 @@ +// video player +// - rlyeh, public domain + +#include "fwk.h" + +int main() { + // 75% window, msaa x2 + window_create( 75, WINDOW_MSAA2 ); + + // load video + int is_rgb = flag("--rgb") ? 1 : 0; + video_t *v = video( "bjork-all-is-full-of-love.mp4", is_rgb ? VIDEO_RGB : VIDEO_YCBCR ); + + while( window_swap() ) { + // decode video frame and get associated textures (audio is automatically sent to audiomixer) + texture_t *textures; + profile( "Video decoder" ) { + textures = video_decode( v ); + } + + // present decoded textures as a fullscreen composed quad + profile( "Video quad" ) { + if(is_rgb) fullscreen_quad_rgb( textures[0], 1.3f ); + else fullscreen_quad_ycbcr( textures, 1.3f ); + } + + // input controls + if( input(KEY_ESC) ) break; + + // ui video + if( ui_panel("Video", 0) ) { + if( ui_button("Rewind") ) video_seek(v, video_position(v) - 3); + if( ui_button("Pause") ) video_pause(v, video_is_paused(v) ^ 1); + if( ui_button("Forward") ) video_seek(v, video_position(v) + 3); + ui_panel_end(); + } + // audio + if( ui_panel("Audio", 0)) { + static float master = 1; + if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master); + ui_panel_end(); + } + } +} diff --git a/demos/MAKE.bat b/demos/MAKE.bat new file mode 100644 index 0000000..bc21afb --- /dev/null +++ b/demos/MAKE.bat @@ -0,0 +1,11 @@ +#!/bin/bash 2>nul || goto :windows + +sh ../MAKE.bat demos + +exit + +:windows + +pushd .. +call MAKE.bat demos %* +popd diff --git a/demos/art/audio/larry.mid b/demos/art/audio/larry.mid new file mode 100644 index 0000000..3b886aa Binary files /dev/null and b/demos/art/audio/larry.mid differ diff --git a/demos/art/audio/larry.txt b/demos/art/audio/larry.txt new file mode 100644 index 0000000..63987f6 --- /dev/null +++ b/demos/art/audio/larry.txt @@ -0,0 +1,38 @@ + SIERRA ON-LINE, INC. + 3-D Animated Adventure Game Soundtrack Series + =============================================== + LEISURE SUIT LARRY III: PASSIONATE PATTI- + IN PURSUIT OF THE PULSATING PECTORALS + + + "TAWNI AT THE BEACH" + Mike Dana + =============================================== + Copyright (c)1989 Sierra On-Line, Inc. + =============================================== + + GENERAL MIDI VERSION + +System Requirements: + +- MIDI Playback Software capable of reading Type 1 Standard + MIDI File format +- General MIDI sound device (Wave Table or better recommended) + + +This Standard MIDI File was recorded directly from Sierra's "Leisure Suit +Larry 3" adventure game. It has been converted from the MT-32 version for +playback on General MIDI sound cards. A Wave Table or better sound card is +highly recommended for optimal playback. + +Recorded/converted for General MIDI by Tom Lewandowski. +Address questions or comments to: + + QUEST STUDIOS + Tom Lewandowski + tom@queststudios.com + + www.QuestStudios.com + + + diff --git a/demos/art/audio/waterworld-map.fur b/demos/art/audio/waterworld-map.fur new file mode 100644 index 0000000..2bf8a7a Binary files /dev/null and b/demos/art/audio/waterworld-map.fur differ diff --git a/demos/art/fx/fxVCR.fs b/demos/art/fx/fxVCR.fs new file mode 100644 index 0000000..2c7c1cc --- /dev/null +++ b/demos/art/fx/fxVCR.fs @@ -0,0 +1,154 @@ +/** + * (c) 2021 FMS_Cat + * Original shader: https://www.shadertoy.com/view/MdffD7 + * I dumbass don't know what it says despite it's my own shader + */ +/* + * Copyright 2021 FMS_Cat + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#define half2 vec2 +#define half3 vec3 +#define half4 vec4 +#define saturate(c) clamp(c, 0.0, 1.0) + +//uniform float iTime; +uniform float iScale; +//uniform vec2 iResolution; +uniform vec4 iBackground; + +const float PI = 3.14159265f; + +const int SAMPLES = 6; + +const float COLOR_NOISE_AMP = 0.1f; +const vec3 YIQ_OFFSET = vec3( -0.1f, -0.1f, 0.0f ); +const vec3 YIQ_AMP = vec3( 1.2f, 1.1f, 1.5f ); + +bool validuv( vec2 uv ) +{ + return 0.0f < uv.x && uv.x < 1.0f && 0.0f < uv.y && uv.y < 1.0f; +} + +vec2 yflip( vec2 uv ) +{ + return vec2( uv.x, 1.0 - uv.y ); +} + +float fs( float s ) +{ + return fract( sin( s * 114.514f ) * 1919.810f ); +} + +float fs2( vec2 s ) +{ + return fs( s.x + fs( s.y ) ); +} + +mat2x2 rotate2D( float t ) +{ + return mat2x2( cos( t ), sin( t ), -sin( t ), cos( t ) ); +} + +vec3 rgb2yiq( vec3 rgb ) +{ + return mat3x3( 0.299f, 0.596f, 0.211f, 0.587f, -0.274f, -0.523f, 0.114f, -0.322f, 0.312f ) * rgb; +} + +vec3 yiq2rgb( vec3 yiq ) +{ + return mat3x3( 1.000f, 1.000f, 1.000f, 0.956f, -0.272f, -1.106f, 0.621f, -0.647f, 1.703f ) * yiq; +} + +float v2Random( vec2 v ) +{ + vec2 vf = fract( v * 256.0f ); + vec2 vi = floor( v * 256.0f ) / 256.0f; + vec2 d = vec2( 0.0f, 1.0f / 256.0f ); + + return mix( + mix( fs2( vi + d.xx ), fs2( vi + d.yx ), vf.x ), + mix( fs2( vi + d.xy ), fs2( vi + d.yy ), vf.x ), + vf.y + ); +} + +half3 vhsTex2D( vec2 uv ) { + if ( validuv( uv ) ) { + half3 yiq = half3( 0.0f, 0.0f, 0.0f ); + for ( int i = 0; i < SAMPLES; i ++ ) { + vec2 uvt = uv - vec2( float( i ), 0.0f ) / iResolution; + if ( validuv( uvt ) ) { + half4 tex = texture(iChannel0, uvt ); + yiq += ( + rgb2yiq( mix( iBackground.rgb, tex.rgb, tex.a ) ) * + vec2( float( i ), float( SAMPLES - 1 - i ) ).yxx / float( SAMPLES - 1 ) + ) / float( SAMPLES ) * 2.0f; + } + } + return yiq2rgb( yiq ); + } + return half3( 0.1f, 0.1f, 0.1f ); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) { + vec2 uv = fragCoord.xy / iResolution.xy; + + vec2 uvt = yflip( uv ); + vec3 col = vec3( 0.0f, 0.0f, 0.0f ); + + // tape wave + uvt.x += ( v2Random( vec2( uvt.y / 10.0f, iTime / 10.0f ) / 1.0f ) - 0.5f ) / iResolution.x * 1.0f; + uvt.x += ( v2Random( vec2( uvt.y, iTime * 10.0f ) ) - 0.5f ) / iResolution.x * 1.0f; + + // tape crease + float tcPhase = smoothstep( 0.9f, 0.96f, sin( uvt.y * 8.0f - ( iTime + 0.14f * v2Random( iTime * vec2( 0.67f, 0.59f ) ) ) * PI * 1.2f ) ); + float tcNoise = smoothstep( 0.3f, 1.0f, v2Random( vec2( uvt.y * 4.77f, iTime ) ) ); + float tc = tcPhase * tcNoise; + uvt.x = uvt.x - tc / iResolution.x * 8.0f; + + // switching noise + float snPhase = smoothstep( 6.0f / iResolution.y, 0.0f, uvt.y ); + uvt.y += snPhase * 0.3f; + uvt.x += snPhase * ( ( v2Random( vec2( uv.y * 100.0f, iTime * 10.0f ) ) - 0.5f ) / iResolution.x * 24.0f ); + + // fetch + half4 tex = texture(iChannel0, uv); + half3 color = vhsTex2D( yflip( uvt ) ); + color = pow( color, vec3(0.4545f) ); + + // crease noise + float cn = tcNoise * ( 0.3f + 0.7f * tcPhase ); + if ( 0.29f < cn ) { + vec2 uvtt = ( uvt + vec2( 1.0f, 0.0f ) * v2Random( vec2( uvt.y, iTime ) ) ) * vec2( 0.1f, 1.0f ); + float n0 = v2Random( uvtt ); + float n1 = v2Random( uvtt + vec2( 1.0f, 0.0f ) / iResolution.x ); + if ( n1 < n0 ) { + color = mix( color, vec3( 2.0f, 2.0f, 2.0f ), pow( n0, 10.0f ) ); + } + } + + // ac beat + color *= 1.0f + 0.1f * smoothstep( 0.4f, 0.6f, v2Random( vec2( 0.0f, 0.1f * ( uv.y + iTime * 0.2f ) ) / 10.0f ) ); + + // color noise + half2 noiseuv = uvt + vec2( fs( iTime ), fs( iTime / 0.7f ) ); + half3 noise = half3( + v2Random( noiseuv ), + v2Random( noiseuv + 0.7f ), + v2Random( noiseuv + 1.4f ) + ); + color = saturate( color ); + + // yiq + color = rgb2yiq( color ); + color += COLOR_NOISE_AMP * ( noise - 0.5f ); + color = YIQ_OFFSET + YIQ_AMP * color; + color = yiq2rgb( color ); + color = pow( color, vec3(2.2f) ); + + fragColor = half4( color, tex.a ); +} diff --git a/demos/art/models/alien/Alien.fbx b/demos/art/models/alien/Alien.fbx new file mode 100644 index 0000000..ae4e8c3 Binary files /dev/null and b/demos/art/models/alien/Alien.fbx differ diff --git a/demos/art/models/alien/Alien.license b/demos/art/models/alien/Alien.license new file mode 100644 index 0000000..8c970d5 --- /dev/null +++ b/demos/art/models/alien/Alien.license @@ -0,0 +1,12 @@ +------------------------------------------------------ +LowPoly Models by @Quaternius +Consider supporting me on Patreon, even $1 helps me a lot! + +https://www.patreon.com/quaternius +------------------------------------------------------- + +License: +CC0 1.0 Universal (CC0 1.0) +Public Domain Dedication +https://creativecommons.org/publicdomain/zero/1.0/ + diff --git a/demos/art/models/robots/George.fbx b/demos/art/models/robots/George.fbx new file mode 100644 index 0000000..d24edf9 Binary files /dev/null and b/demos/art/models/robots/George.fbx differ diff --git a/demos/art/models/robots/George_Texture.png b/demos/art/models/robots/George_Texture.png new file mode 100644 index 0000000..cecafca Binary files /dev/null and b/demos/art/models/robots/George_Texture.png differ diff --git a/demos/art/models/robots/License.txt b/demos/art/models/robots/License.txt new file mode 100644 index 0000000..8c970d5 --- /dev/null +++ b/demos/art/models/robots/License.txt @@ -0,0 +1,12 @@ +------------------------------------------------------ +LowPoly Models by @Quaternius +Consider supporting me on Patreon, even $1 helps me a lot! + +https://www.patreon.com/quaternius +------------------------------------------------------- + +License: +CC0 1.0 Universal (CC0 1.0) +Public Domain Dedication +https://creativecommons.org/publicdomain/zero/1.0/ + diff --git a/demos/art/models/witch/witch.license b/demos/art/models/witch/witch.license new file mode 100644 index 0000000..ec0442d --- /dev/null +++ b/demos/art/models/witch/witch.license @@ -0,0 +1,9 @@ +# Halloween Little Witch + +## Source + +[https://sketchfab.com/models/ccc023590bfb4789af9322864e42d1ab](https://sketchfab.com/models/ccc023590bfb4789af9322864e42d1ab) + +## License + +[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) diff --git a/demos/art/models/witch/witch.obj b/demos/art/models/witch/witch.obj new file mode 100644 index 0000000..d93df01 --- /dev/null +++ b/demos/art/models/witch/witch.obj @@ -0,0 +1,8470 @@ +v 31.381399 19.397799 49.398899 +v 31.243200 19.698000 49.888599 +v 31.264799 19.602098 49.360001 +v 31.369101 19.561798 49.138500 +v 31.373199 19.800798 49.291698 +v 31.264799 19.602098 49.360001 +v 31.243200 19.698000 49.888599 +v 31.369101 19.561798 49.138500 +v 31.206200 19.869200 47.580299 +v 31.309999 19.671900 47.631901 +v 31.237600 19.263199 47.403801 +v 31.086500 19.237398 47.228699 +v 31.359200 19.354799 48.032600 +v 31.310499 19.389099 48.257599 +v 31.226900 18.758799 47.886600 +v 31.326200 18.950100 47.827198 +v 31.067400 19.841999 47.633999 +v 30.943501 19.245199 47.272099 +v 30.887400 19.281898 47.497299 +v 30.969099 19.638899 47.739700 +v 31.157400 19.398998 48.315300 +v 31.018600 19.375198 48.141800 +v 17.397499 15.467599 23.085800 +v 16.381399 15.503399 23.502399 +v 16.361500 14.686099 23.372499 +v 16.361500 14.686099 23.372499 +v 17.361900 14.673499 22.906898 +v 17.397499 15.467599 23.085800 +v 17.237700 13.796199 22.300800 +v 16.276899 13.783499 22.808500 +v 15.665100 13.090199 21.339199 +v 15.665100 13.090199 21.339199 +v 16.791000 13.544299 21.314301 +v 17.237700 13.796199 22.300800 +v 17.148100 16.282499 22.819500 +v 17.002100 15.603199 22.807600 +v 16.381399 15.503399 23.502399 +v 16.381399 15.503399 23.502399 +v 17.397499 15.467599 23.085800 +v 17.148100 16.282499 22.819500 +v 17.361900 14.673499 22.906898 +v 16.361500 14.686099 23.372499 +v 16.276899 13.783499 22.808500 +v 16.276899 13.783499 22.808500 +v 17.237700 13.796199 22.300800 +v 17.361900 14.673499 22.906898 +v 10.847700 18.494799 23.800900 +v 10.207700 17.934799 23.823999 +v 10.849400 17.201099 24.222599 +v 10.849400 17.201099 24.222599 +v 11.421000 17.802399 24.250500 +v 10.847700 18.494799 23.800900 +v 9.511600 17.223000 23.413898 +v 9.368500 16.670198 22.501200 +v 10.061200 16.052799 22.476200 +v 10.061200 16.052799 22.476200 +v 10.226600 16.473900 23.759800 +v 9.511600 17.223000 23.413898 +v 11.600100 18.765699 23.235001 +v 10.847700 18.494799 23.800900 +v 11.421000 17.802399 24.250500 +v 11.421000 17.802399 24.250500 +v 12.226300 18.484699 23.926201 +v 11.600100 18.765699 23.235001 +v 10.207700 17.934799 23.823999 +v 9.511600 17.223000 23.413898 +v 10.226600 16.473900 23.759800 +v 10.226600 16.473900 23.759800 +v 10.849400 17.201099 24.222599 +v 10.207700 17.934799 23.823999 +v 31.310499 19.389099 48.257599 +v 31.206200 19.869200 47.580299 +v 31.067400 19.841999 47.633999 +v 31.157400 19.398998 48.315300 +v 29.976101 19.727098 48.238899 +v 29.455601 19.851898 49.070099 +v 29.768400 19.610399 49.326599 +v 30.175600 19.456499 48.615002 +v 29.549900 19.992399 50.097698 +v 29.822701 19.715698 50.075001 +v 29.959599 20.127399 50.886600 +v 30.191799 19.868000 50.799198 +v 30.847500 20.195299 51.341900 +v 30.982399 19.942598 51.202702 +v 31.800499 20.162100 51.261799 +v 31.757099 19.929899 51.142601 +v 32.509003 20.046000 50.752399 +v 32.362900 19.838099 50.655998 +v 32.659698 19.875000 49.906101 +v 32.488503 19.714899 49.983002 +v 32.239201 19.752298 49.341999 +v 32.119400 19.629200 49.514801 +v 31.554001 19.669298 49.317398 +v 31.538099 19.631399 49.524101 +v 31.373199 19.800798 49.291698 +v 31.243200 19.698000 49.888599 +v 31.018600 19.375198 48.141800 +v 30.969099 19.638899 47.739700 +v 19.552401 14.765799 45.731899 +v 20.527199 15.275700 45.292500 +v 21.635000 15.787799 47.532898 +v 20.561600 15.293599 47.971298 +v 20.117001 15.396699 48.154499 +v 19.617500 15.779299 48.376801 +v 19.301100 15.639699 47.733002 +v 19.828800 15.263399 47.597198 +v 19.379101 15.040099 46.590599 +v 19.677601 15.187599 47.278698 +v 19.151600 15.568099 47.428200 +v 18.851799 15.425599 46.772400 +v 18.523399 15.264499 46.122799 +v 19.062000 14.874899 45.923199 +v 19.227299 14.962699 46.271801 +v 18.695200 15.349099 46.459499 +v 19.446400 14.965599 46.399399 +v 19.926600 15.179899 47.399899 +v 31.237600 19.263199 47.403801 +v 31.326200 18.950100 47.827198 +v 31.226900 18.758799 47.886600 +v 31.086500 19.237398 47.228699 +v 31.359200 19.354799 48.032600 +v 31.309999 19.671900 47.631901 +v 31.206200 19.869200 47.580299 +v 31.310499 19.389099 48.257599 +v 21.878099 15.771699 47.408798 +v 20.764599 15.261199 45.179901 +v 21.058701 15.609499 44.964199 +v 22.106400 16.117298 47.231998 +v 21.635000 15.787799 47.532898 +v 20.527199 15.275700 45.292500 +v 17.873800 15.650299 46.402599 +v 18.074200 15.283998 46.351200 +v 19.226200 15.802999 48.523201 +v 19.049999 16.165798 48.667301 +v 21.981499 16.249298 47.563198 +v 19.507801 16.231098 48.671200 +v 17.862801 15.442699 46.017601 +v 20.530701 15.475399 44.762901 +v 20.700100 15.940899 48.196701 +v 19.617500 15.779299 48.376801 +v 20.579201 15.588599 48.014599 +v 18.523399 15.264499 46.122799 +v 19.428801 15.218599 45.273701 +v 19.538500 15.047299 45.685398 +v 18.621599 15.361199 46.684200 +v 18.695200 15.349099 46.459499 +v 30.943501 19.245199 47.272099 +v 31.086500 19.237398 47.228699 +v 31.226900 18.758799 47.886600 +v 31.087601 18.802099 47.924400 +v 29.725399 19.375500 48.023201 +v 29.991001 19.106998 48.411301 +v 29.471699 19.328999 49.228401 +v 29.120499 19.566299 48.944099 +v 29.566200 19.475599 50.254700 +v 29.224100 19.752600 50.277500 +v 29.973999 19.660500 51.027199 +v 29.749500 19.922798 51.135601 +v 30.860001 19.755798 51.470798 +v 30.756800 20.007399 51.604801 +v 31.810499 19.757099 51.375599 +v 31.896799 19.997599 51.545101 +v 32.517200 19.683599 50.852100 +v 32.753300 19.877399 50.871101 +v 32.667999 19.543798 50.000099 +v 32.900101 19.696499 49.882900 +v 32.247498 19.458799 49.429100 +v 32.363899 19.576399 49.226002 +v 31.562300 19.529099 49.376400 +v 31.527800 19.571899 49.196098 +v 31.381399 19.397799 49.398899 +v 31.369101 19.561798 49.138500 +v 19.101101 15.581299 47.655300 +v 19.301100 15.639699 47.733002 +v 18.851799 15.425599 46.772400 +v 19.151600 15.568099 47.428200 +v 30.887400 19.281898 47.497299 +v 30.984100 19.020699 47.911701 +v 31.157400 19.398998 48.315300 +v 31.018600 19.375198 48.141800 +v 22.273201 16.182699 32.844002 +v 21.631599 18.357498 33.751701 +v 22.282101 16.182699 33.652000 +v 22.256199 15.314099 33.584999 +v 21.606600 15.759499 35.421997 +v 21.302401 14.108599 35.503799 +v 20.970200 12.444499 35.335800 +v 21.501999 12.646099 33.830101 +v 19.991400 20.161098 34.691299 +v 20.681601 18.012798 35.807198 +v 21.761900 17.182999 34.594398 +v 20.537899 16.236599 36.507698 +v 20.193199 14.319099 36.789902 +v 19.948299 12.291100 36.397400 +v 18.081499 19.664200 37.247200 +v 19.554800 18.815899 36.673599 +v 17.756001 21.428499 35.623699 +v 17.673700 17.347300 38.146500 +v 19.213501 16.756100 37.461899 +v 17.677401 15.088900 38.316799 +v 18.802601 14.569299 37.866199 +v 16.601999 12.238899 38.491898 +v 18.560900 12.230399 37.572201 +v 21.187500 19.501699 32.029602 +v 19.324400 21.319799 32.659698 +v 16.686699 22.501499 33.346100 +v 21.571100 16.052099 30.538099 +v 20.693199 19.512299 30.489201 +v 18.862400 21.509699 30.655300 +v 16.272900 22.624800 31.066401 +v 20.098000 18.197800 23.846500 +v 20.662800 18.727900 24.466000 +v 20.290899 19.994499 21.654800 +v 21.374100 18.281599 25.487600 +v 23.065201 18.367498 25.745100 +v 23.533199 19.840399 25.606199 +v 21.482700 16.893900 25.650801 +v 23.649399 17.571899 26.734600 +v 22.294701 19.499599 24.411800 +v 22.245100 20.946798 23.472500 +v 21.137100 19.476599 22.816999 +v 20.253099 18.073799 22.061800 +v 19.640200 16.932299 21.310900 +v 20.186800 18.732899 25.054501 +v 19.546801 18.230299 24.344799 +v 18.735199 18.297899 24.818699 +v 19.578899 18.941299 25.931499 +v 20.907000 18.348200 25.981100 +v 20.980600 16.735199 26.155500 +v 20.186800 18.732899 25.054501 +v 19.546801 18.230299 24.344799 +v 19.974800 17.277498 23.779999 +v 19.443300 17.262499 24.286600 +v 18.585400 17.032700 24.524200 +v 19.443300 17.262499 24.286600 +v 20.473900 18.302999 27.112900 +v 20.907000 18.348200 25.981100 +v 20.673500 16.625898 27.262199 +v 20.980600 16.735199 26.155500 +v 23.928600 15.205699 25.655001 +v 23.101700 16.556398 25.864100 +v 21.061300 15.871299 24.934299 +v 22.833500 13.855199 23.408901 +v 22.651600 15.101398 24.763199 +v 20.332001 16.134499 24.076199 +v 21.056900 14.632799 21.287800 +v 21.379400 15.081699 23.073101 +v 20.498400 16.329100 22.355099 +v 19.640200 16.932299 21.310900 +v 20.440500 16.006498 25.367800 +v 19.833700 16.194399 24.694401 +v 19.443300 17.262499 24.286600 +v 19.974800 17.277498 23.779999 +v 18.585400 17.032700 24.524200 +v 19.443300 17.262499 24.286600 +v 19.833700 16.194399 24.694401 +v 18.922600 15.735899 24.979000 +v 20.440500 16.006498 25.367800 +v 20.441299 15.254599 26.125799 +v 20.290899 19.994499 21.654800 +v 21.807301 17.327599 23.997900 +v 20.253099 18.073799 22.061800 +v 23.065201 18.367498 25.745100 +v 23.533199 19.840399 25.606199 +v 22.294701 19.499599 24.411800 +v 22.245100 20.946798 23.472500 +v 21.137100 19.476599 22.816999 +v 19.640200 16.932299 21.310900 +v 20.498400 16.329100 22.355099 +v 21.056900 14.632799 21.287800 +v 23.928600 15.205699 25.655001 +v 23.101700 16.556398 25.864100 +v 23.649399 17.571899 26.734600 +v 22.833500 13.855199 23.408901 +v 22.651600 15.101398 24.763199 +v 21.379400 15.081699 23.073101 +v 17.329100 19.204399 25.058399 +v 18.731701 20.274099 26.908600 +v 17.202400 16.728100 24.569099 +v 20.276501 19.481098 28.826599 +v 20.751999 16.037600 29.047899 +v 18.270901 14.967899 25.202200 +v 17.202400 16.728100 24.569099 +v 20.311100 14.808699 27.160900 +v 19.660700 13.969999 27.550499 +v 20.916800 13.852499 29.659700 +v 19.496399 13.112099 27.834600 +v 15.403700 21.680899 27.429300 +v 15.912400 22.344099 29.407900 +v 18.670601 21.336599 28.981300 +v 13.875900 13.244200 24.398800 +v 14.468699 14.325600 24.933500 +v 16.351799 13.786499 24.974800 +v 16.119999 11.587599 24.904400 +v 13.603800 15.079299 25.090000 +v 13.924500 17.185799 25.032801 +v 15.736700 16.223700 24.770800 +v 14.468699 14.325600 24.933500 +v 14.468699 14.325600 24.933500 +v 15.736700 16.223700 24.770800 +v 16.351799 13.786499 24.974800 +v 14.648000 19.892899 25.578199 +v 18.522600 11.602399 26.543400 +v 18.915400 13.224298 26.555399 +v 18.892000 11.592999 27.385599 +v 17.853901 11.614799 25.636101 +v 18.087999 12.972499 25.424601 +v 16.946301 11.641399 25.094799 +v 19.496399 13.112099 27.834600 +v 19.331900 11.794899 28.244200 +v 21.365000 14.183999 30.701500 +v 21.004000 12.367699 31.557100 +v 20.396000 12.199499 30.030800 +v 20.916800 13.852499 29.659700 +v 18.094601 13.460299 25.368799 +v 22.014400 14.445899 32.353401 +v 21.365000 14.183999 30.701500 +v 22.256199 15.314099 33.584999 +v 21.501999 12.646099 33.830101 +v 22.014400 14.445899 32.353401 +v 13.447700 10.648600 24.836399 +v 12.303300 10.567200 24.954000 +v 12.827300 13.552599 24.135500 +v 13.408900 9.768199 25.291000 +v 12.208200 9.750399 25.522900 +v 14.059999 10.797699 24.821199 +v 15.383300 10.744699 24.924999 +v 18.290400 10.607100 28.358200 +v 19.357000 10.893999 30.367100 +v 17.103600 9.501599 31.484200 +v 16.204201 9.790399 28.736099 +v 14.478600 10.014299 25.072001 +v 13.603800 15.079299 25.090000 +v 14.468699 14.325600 24.933500 +v 14.534700 14.462999 24.820700 +v 13.743400 15.160699 25.026199 +v 13.875900 13.244200 24.398800 +v 13.882000 13.542500 23.953800 +v 12.915900 13.871698 24.005699 +v 12.965400 14.032399 23.854000 +v 12.420000 8.788599 28.024300 +v 13.647500 9.043299 27.499500 +v 13.848700 9.483899 28.150700 +v 12.627300 9.266899 28.548800 +v 17.781700 10.476599 26.375000 +v 17.813099 10.637099 27.164700 +v 16.641899 9.710799 26.452200 +v 16.972401 10.092699 27.170599 +v 12.242900 9.037599 26.685400 +v 13.522900 9.215399 26.235800 +v 17.445499 10.557499 25.692600 +v 16.308701 9.699299 25.837500 +v 16.649200 10.630499 25.212099 +v 15.927999 9.899799 25.380800 +v 15.104800 9.450299 25.839001 +v 14.244600 9.492799 29.139099 +v 12.829500 9.444699 29.451300 +v 19.752899 10.839099 32.219002 +v 17.715000 9.534999 32.795097 +v 14.783500 9.206599 31.606800 +v 13.198900 9.273599 31.772600 +v 19.762100 10.701399 34.044601 +v 13.906000 9.657399 35.251999 +v 13.426500 9.259499 33.079399 +v 15.191000 9.122399 32.889099 +v 15.840100 9.505799 34.890701 +v 18.093599 9.897699 34.439697 +v 15.738200 9.717499 27.463301 +v 15.446400 9.329000 26.779499 +v 12.827300 13.552599 24.135500 +v 20.106400 13.375999 27.971399 +v 20.555000 13.656300 27.655701 +v 18.919399 13.274499 25.358000 +v 20.031200 13.622299 25.801701 +v 20.566200 13.779299 26.764999 +v 20.106400 13.375999 27.971399 +v 19.660700 13.969999 27.550499 +v 19.496399 13.112099 27.834600 +v 20.555000 13.656300 27.655701 +v 18.087999 12.972499 25.424601 +v 18.094601 13.460299 25.368799 +v 18.561199 13.816299 25.563000 +v 18.919399 13.274499 25.358000 +v 20.031200 13.622299 25.801701 +v 19.628901 14.096399 26.520800 +v 20.566200 13.779299 26.764999 +v 18.087999 12.972499 25.424601 +v 13.063100 13.273199 21.860600 +v 12.745000 13.058498 21.868401 +v 12.507600 13.121599 21.708500 +v 12.646300 13.418200 21.451401 +v 12.193500 13.700199 21.477200 +v 12.057000 13.403899 21.740200 +v 12.800000 13.314098 22.959900 +v 12.419300 13.507799 22.965000 +v 12.982400 13.336299 23.462601 +v 12.807000 13.423199 23.535801 +v 12.678100 13.380499 23.078199 +v 13.112100 13.502699 22.840099 +v 12.533400 13.444799 23.620201 +v 12.399200 13.409999 23.156399 +v 12.794700 13.461800 23.653200 +v 12.641100 13.571400 23.714699 +v 13.258699 13.569499 23.429100 +v 13.023200 13.767300 23.635700 +v 12.849900 13.888499 23.695900 +v 12.344400 13.695299 21.713699 +v 12.357500 13.997599 21.132401 +v 13.019600 13.454299 21.436501 +v 14.516100 12.889500 23.026100 +v 14.669000 13.000898 23.376301 +v 14.087399 13.149199 23.326799 +v 14.602500 12.721899 22.608601 +v 13.775200 13.117599 22.859200 +v 15.139300 12.547499 22.211300 +v 15.014000 12.963699 23.508099 +v 14.389900 13.290199 23.868200 +v 15.605300 13.151899 24.224300 +v 14.651900 13.607899 24.512400 +v 16.053801 12.771199 23.310299 +v 16.616100 13.254599 24.384701 +v 15.858900 13.572599 24.711500 +v 14.989300 13.973000 24.876699 +v 12.685400 15.202799 19.348900 +v 12.541400 14.278899 21.206900 +v 11.308300 15.177399 21.577999 +v 11.380400 15.953400 19.672001 +v 10.745800 16.691599 20.214800 +v 10.061200 16.052799 22.476200 +v 10.681900 17.460400 21.042099 +v 11.443700 18.292700 21.853500 +v 11.226600 18.794199 20.837399 +v 10.634399 17.919100 20.115200 +v 10.840700 17.114498 19.467999 +v 11.620100 16.343100 18.990000 +v 12.931100 15.691199 18.686199 +v 11.938800 16.694799 18.373501 +v 11.758600 16.402300 17.463100 +v 13.318700 15.763799 17.292700 +v 13.213200 16.148499 18.118700 +v 11.032499 17.580599 18.828701 +v 10.448800 17.651798 17.702801 +v 10.791700 18.390999 19.410000 +v 9.993299 19.026899 18.421000 +v 11.257600 19.317200 20.004499 +v 10.566700 20.417999 19.292500 +v 11.435400 15.821999 16.213200 +v 13.309999 15.105599 16.137300 +v 9.891200 17.398699 16.562201 +v 9.301001 19.062599 17.196301 +v 9.973499 21.340599 17.976601 +v 9.296000 20.383699 17.619801 +v 10.953199 15.206900 14.759800 +v 13.173500 14.350899 14.627300 +v 8.933399 17.002800 15.404200 +v 8.226400 18.762098 16.066601 +v 9.017600 21.826599 16.808701 +v 8.273500 20.519199 16.648600 +v 14.100000 14.596498 19.121899 +v 13.936800 13.576899 20.988400 +v 15.151800 14.400899 19.263699 +v 15.975000 14.531199 19.683500 +v 15.665100 13.090199 21.339199 +v 16.080700 17.799898 21.313301 +v 16.173300 18.359499 20.575199 +v 14.811600 19.028700 20.765099 +v 14.798500 18.372799 21.529099 +v 16.289101 15.211800 19.009800 +v 16.790199 15.855999 19.519100 +v 16.649099 15.443899 20.562000 +v 15.416300 15.032700 18.641300 +v 17.002100 15.603199 22.807600 +v 16.902100 16.692499 21.018700 +v 15.617500 16.391600 23.081799 +v 14.580400 16.892698 23.264500 +v 14.305099 15.192799 18.557701 +v 16.942801 17.248600 20.259201 +v 14.501600 15.748399 18.071600 +v 14.919400 15.472898 17.299700 +v 15.724600 15.714099 18.164501 +v 16.536600 15.700199 17.237999 +v 16.524401 15.974899 18.505699 +v 17.615700 16.273399 17.555901 +v 17.052700 16.827799 19.059900 +v 18.279699 17.556799 18.355999 +v 17.010900 18.098999 19.639000 +v 17.857700 19.078098 18.776501 +v 16.158400 19.216599 19.841099 +v 16.668400 20.381800 18.693300 +v 14.892700 19.868299 19.923901 +v 15.034900 21.027498 18.748600 +v 15.276300 14.855000 16.167900 +v 17.231501 15.405199 16.334999 +v 18.471800 16.328598 16.681499 +v 19.253700 17.350698 17.000401 +v 19.298700 18.502800 17.400600 +v 18.617599 20.055300 17.537600 +v 17.103800 21.167198 17.321899 +v 15.108800 21.997398 17.332100 +v 15.540500 14.107800 14.759700 +v 18.103399 14.751799 15.351900 +v 19.473400 15.856699 15.862300 +v 20.288601 17.304998 16.323200 +v 20.326300 18.832399 16.532600 +v 19.397200 20.550098 16.406500 +v 17.613501 21.907799 16.043800 +v 15.224900 22.898600 15.956800 +v 15.665100 13.090199 21.339199 +v 15.975000 14.531199 19.683500 +v 16.791000 13.544299 21.314301 +v 17.397499 15.467599 23.085800 +v 17.361900 14.673499 22.906898 +v 18.328701 14.973399 22.408100 +v 17.237700 13.796199 22.300800 +v 18.194201 14.246199 20.650101 +v 18.174099 14.063999 21.767200 +v 16.649099 15.443899 20.562000 +v 17.148100 16.282499 22.819500 +v 18.483500 16.796200 21.476000 +v 17.708200 16.808399 20.765800 +v 17.307699 15.799899 20.143000 +v 17.538200 14.683099 20.163700 +v 18.995501 16.172798 21.751200 +v 17.002100 15.603199 22.807600 +v 19.111500 15.790099 20.887199 +v 19.614700 15.865799 20.276901 +v 19.118799 16.444698 20.037201 +v 18.566101 16.303398 20.632801 +v 18.744801 15.021199 20.005199 +v 19.171299 15.216999 19.638000 +v 19.580799 15.422299 19.964100 +v 19.127800 15.246299 20.563400 +v 18.318300 15.527599 19.762800 +v 18.763599 15.685499 19.437799 +v 18.854900 16.297298 19.618200 +v 18.327301 16.142799 20.026299 +v 19.673901 15.621099 19.395100 +v 19.723600 15.929499 19.843899 +v 18.636499 15.739199 19.936699 +v 18.854900 16.297298 19.618200 +v 19.148300 15.261599 21.728100 +v 18.862301 14.480499 21.226101 +v 19.276800 14.195499 20.997000 +v 19.669899 15.131499 21.635300 +v 17.254799 15.947199 19.739201 +v 17.518101 14.601299 19.670000 +v 19.429699 16.315199 21.675301 +v 18.358799 14.005599 20.256800 +v 18.780800 17.132898 21.354401 +v 17.790699 17.164200 20.533298 +v 19.530300 14.351199 20.471500 +v 19.962200 15.248199 21.129200 +v 17.605600 16.195299 19.271999 +v 17.802999 14.810099 19.188700 +v 17.790699 17.164200 20.533298 +v 18.199100 17.350698 20.094000 +v 19.823200 16.380499 21.232399 +v 18.640499 14.156300 19.747799 +v 19.190001 17.224899 20.950800 +v 18.199100 17.350698 20.094000 +v 19.243700 16.157700 19.201199 +v 19.310400 16.424999 19.643299 +v 18.957001 15.320799 20.087200 +v 18.327301 16.142799 20.026299 +v 18.594101 15.970900 19.260099 +v 18.254601 15.273199 19.458199 +v 18.576200 14.857999 19.612200 +v 19.271700 14.977299 19.506699 +v 18.599300 15.164299 18.294701 +v 18.847401 14.810599 18.390600 +v 18.724300 14.702999 18.086100 +v 18.490799 14.988699 17.987900 +v 18.847401 14.810599 18.390600 +v 18.673500 14.521299 18.718899 +v 18.532799 14.374800 18.493500 +v 18.724300 14.702999 18.086100 +v 18.283501 14.494499 18.945499 +v 18.054600 14.330799 18.949799 +v 18.035400 14.848099 18.849600 +v 17.806000 14.648999 18.852900 +v 18.035400 14.848099 18.849600 +v 18.274799 15.063999 18.554199 +v 18.090000 14.950199 18.329300 +v 17.806000 14.648999 18.852900 +v 18.461300 14.361799 17.673901 +v 18.309799 14.535199 17.594900 +v 18.281099 14.118699 18.014599 +v 18.461300 14.361799 17.673901 +v 17.940701 13.976699 18.344299 +v 18.025299 14.063000 18.219000 +v 17.377100 13.920399 18.952599 +v 17.217300 14.005599 19.003399 +v 17.410700 13.937499 18.782799 +v 17.547400 14.235600 18.342300 +v 17.797699 14.319199 18.090900 +v 17.725201 14.391799 18.882700 +v 17.776199 14.198199 18.857800 +v 17.554399 14.303699 18.550200 +v 17.539700 14.465499 18.769001 +v 17.738100 14.039199 18.430799 +v 17.729599 14.118500 18.626999 +v 17.738100 14.039199 18.430799 +v 17.729599 14.118500 18.626999 +v 18.213699 13.985600 17.264099 +v 17.963501 13.708300 17.420799 +v 18.079700 14.085199 17.268700 +v 17.605999 13.633099 17.764500 +v 17.554399 13.502699 17.816700 +v 17.491199 13.754099 17.763699 +v 17.783899 13.797499 18.134899 +v 17.779200 13.852499 17.960800 +v 17.486000 13.959099 18.128000 +v 17.461599 13.878499 18.051498 +v 17.586000 13.792599 18.158699 +v 17.659000 14.022799 17.922901 +v 17.609100 13.938599 17.868299 +v 18.025299 14.063000 18.219000 +v 17.779200 13.852499 17.960800 +v 17.722900 13.721899 18.052399 +v 17.554399 13.502699 17.816700 +v 17.605999 13.633099 17.764500 +v 17.403299 13.717899 17.882500 +v 17.455900 13.572999 17.912899 +v 18.338699 14.190498 17.473400 +v 18.233299 14.382500 17.480099 +v 18.157200 13.977999 17.822100 +v 18.338699 14.190498 17.473400 +v 18.025299 14.063000 18.219000 +v 17.733400 14.075799 17.858500 +v 17.851000 13.876799 17.881100 +v 18.177099 14.259699 17.394899 +v 18.101700 13.876999 17.697899 +v 17.963501 13.708300 17.420799 +v 18.213699 13.985600 17.264099 +v 17.720100 13.982399 17.745701 +v 17.702000 13.824699 17.558800 +v 17.742701 13.688099 17.584400 +v 17.603199 14.301100 18.915100 +v 17.217300 14.005599 19.003399 +v 17.377100 13.920399 18.952599 +v 17.490999 14.242199 18.596901 +v 17.278801 14.088799 18.723099 +v 17.180401 14.143399 18.878399 +v 17.640301 14.079299 18.664200 +v 17.776199 14.198199 18.857800 +v 17.180401 14.143399 18.878399 +v 17.539700 14.465499 18.769001 +v 17.586000 13.792599 18.158699 +v 17.455900 13.572999 17.912899 +v 17.851000 13.876799 17.881100 +v 17.742701 13.688099 17.584400 +v 20.026600 15.398699 20.646700 +v 19.585199 14.645899 20.255899 +v 18.116199 15.136999 19.272600 +v 17.957199 16.262400 19.313900 +v 18.512400 17.191799 19.858900 +v 19.952301 16.350199 20.648800 +v 18.816799 14.562199 19.701700 +v 18.512400 17.191799 19.858900 +v 19.426600 17.073999 20.437401 +v 18.847401 14.810599 18.390600 +v 18.599300 15.164299 18.294701 +v 18.594101 15.970900 19.260099 +v 18.283501 14.494499 18.945499 +v 18.673500 14.521299 18.718899 +v 18.274799 15.063999 18.554199 +v 18.035400 14.848099 18.849600 +v 18.274799 15.063999 18.554199 +v 16.649099 15.443899 20.562000 +v 17.708200 16.808399 20.765800 +v 16.531700 19.459398 18.753601 +v 16.228199 18.785000 19.396700 +v 17.293100 17.638699 19.101500 +v 17.779200 18.157099 17.946199 +v 14.718600 20.221298 17.712799 +v 16.719400 19.744099 17.571499 +v 15.874400 19.730000 16.838900 +v 14.594800 19.975800 16.838200 +v 17.284700 19.192598 16.896801 +v 17.262899 16.912100 17.444700 +v 17.451099 17.328299 16.642601 +v 17.778601 18.325100 16.909599 +v 16.526199 16.357800 17.177700 +v 16.552401 16.728399 16.366299 +v 15.495800 16.443399 17.192200 +v 15.424000 16.801498 16.565701 +v 13.649199 16.942398 17.220400 +v 13.762900 17.183899 16.625500 +v 16.961901 19.690100 15.150100 +v 15.844200 20.096498 15.242599 +v 17.155899 18.042398 14.628201 +v 17.380100 18.905600 14.890100 +v 16.310900 17.458300 14.498800 +v 15.393800 17.820200 14.380100 +v 15.248199 17.087099 16.177799 +v 14.460200 17.877600 16.057899 +v 14.842900 18.730598 14.438999 +v 14.802400 19.167799 16.450699 +v 15.108600 19.720299 14.932199 +v 14.802400 19.167799 16.450699 +v 15.108600 19.720299 14.932199 +v 15.826099 20.364698 14.614900 +v 16.739000 20.093199 14.471700 +v 16.603500 20.514500 13.718300 +v 15.798600 20.742199 13.832700 +v 16.953600 19.840399 13.606000 +v 16.761900 19.308199 13.241500 +v 16.119400 18.910898 12.933500 +v 16.117300 19.389799 12.588200 +v 15.297200 18.775398 13.565800 +v 15.282500 19.162100 13.035400 +v 16.179600 18.559999 13.479099 +v 15.017800 19.763199 13.419600 +v 15.224500 20.340799 13.818100 +v 16.246500 21.629200 12.375000 +v 15.569600 21.754499 12.492300 +v 16.487801 20.910198 12.144100 +v 16.391500 20.539799 12.030200 +v 15.916900 20.355999 11.842500 +v 15.313499 19.608799 12.687600 +v 15.202200 20.512098 11.963200 +v 14.926100 20.841799 12.242200 +v 15.224500 20.340799 13.818100 +v 15.029500 21.235800 12.367300 +v 15.029500 21.235800 12.367300 +v 14.759100 19.931099 18.923698 +v 14.623500 19.270498 19.572599 +v 16.734200 16.435499 18.454500 +v 13.434999 16.671398 18.193901 +v 15.001400 16.125898 18.056599 +v 16.085100 15.975499 18.113100 +v 14.354600 19.282499 16.378599 +v 13.931400 17.698599 15.986600 +v 14.354600 19.282499 16.378599 +v 15.056700 22.241899 10.091599 +v 14.734200 21.664700 9.833000 +v 14.792400 21.673899 10.126699 +v 15.005700 22.200600 10.222300 +v 15.592100 22.582199 10.171000 +v 16.392900 21.565300 10.737000 +v 16.178101 21.668098 10.234900 +v 16.128399 22.347099 10.210299 +v 16.168499 22.172298 11.022699 +v 16.025400 20.959700 10.583500 +v 15.959300 21.175200 10.103300 +v 15.474100 20.608099 9.901999 +v 15.160600 20.234400 9.565000 +v 15.602800 20.596298 9.679300 +v 14.680600 20.952700 9.885600 +v 14.592500 21.015799 9.656100 +v 14.602500 20.473400 9.554100 +v 15.205999 20.793200 10.591000 +v 15.008600 20.534300 10.192500 +v 14.722800 21.379200 10.783600 +v 15.953199 21.122700 9.816100 +v 15.602800 20.596298 9.679300 +v 14.592500 21.015799 9.656100 +v 14.734200 21.664700 9.833000 +v 14.800200 20.198099 9.521800 +v 15.160600 20.234400 9.565000 +v 14.800200 20.198099 9.521800 +v 14.602500 20.473400 9.554100 +v 16.211700 21.772099 9.965500 +v 15.142000 22.251999 9.944500 +v 15.602600 22.607599 10.060100 +v 16.123699 22.356199 10.065700 +v 16.123699 22.356199 10.065700 +v 15.602600 22.607599 10.060100 +v 15.592100 22.582199 10.171000 +v 16.216999 21.717499 10.112200 +v 16.211700 21.772099 9.965500 +v 16.216999 21.717499 10.112200 +v 15.056700 22.241899 10.091599 +v 15.426100 22.362400 11.186800 +v 15.602600 22.607599 10.060100 +v 15.142000 22.251999 9.944500 +v 14.794000 21.964600 11.030300 +v 14.754800 21.442999 12.594900 +v 15.702600 22.125898 12.800500 +v 15.426100 22.362400 11.186800 +v 15.455800 19.892300 11.514200 +v 14.779600 20.549700 12.037200 +v 16.787701 21.878300 12.544399 +v 15.702600 22.125898 12.800500 +v 15.841500 21.500500 14.508600 +v 17.141100 21.214699 14.226600 +v 17.125299 21.051498 12.066100 +v 17.614901 20.229198 13.469100 +v 16.513399 20.118799 11.563499 +v 16.771299 19.057199 12.752000 +v 15.575399 18.783600 12.583500 +v 14.743100 20.740499 14.043800 +v 14.713700 19.410299 13.222000 +v 15.953199 21.122700 9.816100 +v 15.841500 21.500500 14.508600 +v 15.006400 15.139499 24.690701 +v 14.039300 15.791999 24.796900 +v 12.487700 14.164899 21.239601 +v 13.858900 13.439899 21.101101 +v 14.018600 13.060999 22.232399 +v 12.523200 13.744200 22.243900 +v 14.911200 13.849400 24.468601 +v 15.257500 14.357698 24.695000 +v 16.239000 13.825899 24.110600 +v 16.490999 14.237000 24.315100 +v 15.169800 12.895399 22.747700 +v 15.303300 12.823399 21.347300 +v 16.335400 12.843399 22.136499 +v 17.126400 15.086999 23.788300 +v 17.241400 15.428199 23.343100 +v 16.440701 15.533099 23.774500 +v 17.257599 16.633598 22.464800 +v 16.504299 17.309999 22.576099 +v 15.765500 15.926099 23.870100 +v 15.093800 18.071199 22.829399 +v 14.532200 16.667599 23.926701 +v 16.089500 12.980499 23.464901 +v 14.600500 13.237799 23.817900 +v 13.981500 13.293999 23.330799 +v 12.754200 13.781599 23.232800 +v 16.882900 13.233199 22.890301 +v 16.963200 14.371899 23.614201 +v 15.816600 14.609599 24.588400 +v 17.264599 15.090899 23.739500 +v 17.414400 15.464799 23.276299 +v 16.489201 12.830599 22.001699 +v 17.065800 13.217899 22.805799 +v 17.139400 14.355598 23.572800 +v 12.428900 14.185099 20.972700 +v 13.902200 13.392799 20.824799 +v 15.339600 12.791698 21.140400 +v 17.615799 13.460299 22.335701 +v 18.191500 13.591700 22.012400 +v 18.341000 14.748499 22.840500 +v 17.679399 14.621099 23.150600 +v 18.410200 15.301199 22.826099 +v 17.859100 15.264599 23.174599 +v 17.243299 13.062499 20.807701 +v 18.065899 15.598799 22.392899 +v 12.553900 14.846299 19.788200 +v 14.077700 14.100399 19.622400 +v 15.526800 13.283400 19.778301 +v 15.508800 19.071999 11.946000 +v 15.567100 19.048199 12.294000 +v 15.465400 19.024099 12.368700 +v 15.257299 19.153898 12.007500 +v 15.306100 19.072699 12.361000 +v 15.819500 19.014599 12.243500 +v 15.782000 19.060699 11.936200 +v 16.478300 18.907398 12.446100 +v 14.588300 19.538500 12.286500 +v 14.518700 19.396399 12.594999 +v 15.714400 18.983099 12.330200 +v 16.536301 19.246599 11.908400 +v 16.550900 19.578699 11.551400 +v 15.484100 19.442699 11.756700 +v 15.640200 19.485298 11.633900 +v 15.803800 19.403999 11.687000 +v 15.218100 19.475698 11.813400 +v 15.331800 19.508799 11.720900 +v 14.607800 19.910898 11.940500 +v 16.609400 18.998899 12.366000 +v 14.590100 19.265699 12.743700 +v 16.399700 19.617699 11.450800 +v 14.755899 19.821400 11.786699 +v 14.797000 19.241299 12.757400 +v 16.272499 18.956999 12.449200 +v 22.282101 16.182699 33.652000 +v 22.256199 15.314099 33.584999 +v 22.851799 15.797199 33.166500 +v 22.804001 16.393099 33.209602 +v 22.273201 16.182699 32.844002 +v 22.882801 16.272400 32.832199 +v 22.282101 16.182699 33.652000 +v 22.804001 16.393099 33.209602 +v 23.434500 16.542898 32.990501 +v 8.988600 19.363798 35.175598 +v 9.242900 19.304600 35.936699 +v 10.778500 20.956398 35.657402 +v 8.860701 18.521500 35.933998 +v 9.789200 16.866499 37.533699 +v 10.171200 18.498400 37.434601 +v 9.383500 15.255199 37.436798 +v 7.977400 15.804599 35.312401 +v 10.425600 19.897400 36.583000 +v 12.091200 20.070700 37.323402 +v 13.252800 21.775499 35.881599 +v 11.644500 18.367399 38.080002 +v 11.211700 16.470999 38.377800 +v 10.462999 14.581900 38.101799 +v 15.601000 20.257999 37.681000 +v 16.132299 21.816999 35.906502 +v 13.672300 20.225698 37.717300 +v 15.195900 17.940498 38.579800 +v 13.306800 18.171299 38.506203 +v 14.978000 15.654399 38.962898 +v 12.819500 16.002800 38.924000 +v 13.994100 12.863298 38.948299 +v 11.922100 13.822999 38.749901 +v 11.096800 21.919199 33.812500 +v 13.661500 22.676600 33.660900 +v 11.015300 21.831200 32.201599 +v 8.772500 19.118198 32.797897 +v 13.481000 22.799198 31.608099 +v 8.777200 20.910198 25.848000 +v 8.694700 22.772900 23.705000 +v 8.739000 21.584799 26.574100 +v 8.278900 21.419100 27.802799 +v 7.196100 23.754599 28.494600 +v 6.984700 22.220299 28.588100 +v 6.466800 21.688799 29.772400 +v 7.630100 20.212898 28.099899 +v 8.068399 24.343498 25.978899 +v 7.694900 22.997599 26.993000 +v 8.135799 22.591599 25.115601 +v 8.013100 21.006399 24.225800 +v 7.779800 19.773998 23.407799 +v 9.330800 21.334000 26.973801 +v 10.216300 21.184500 27.586800 +v 10.277900 20.324299 26.313999 +v 9.414800 20.657799 26.136200 +v 8.859500 21.234699 28.111099 +v 8.144500 19.810699 28.424900 +v 9.330800 21.334000 26.973801 +v 9.414800 20.657799 26.136200 +v 9.056499 19.751099 26.122900 +v 8.453600 20.037899 25.816999 +v 9.750700 19.149399 26.086100 +v 9.056499 19.751099 26.122900 +v 8.859500 21.234699 28.111099 +v 9.569799 20.915499 29.040800 +v 8.144500 19.810699 28.424900 +v 8.712999 19.491600 29.376801 +v 6.195600 20.606998 28.853001 +v 4.841000 19.778999 29.029600 +v 7.300100 19.168400 27.367300 +v 5.574400 19.192999 27.782400 +v 4.433700 18.263599 26.661900 +v 7.747700 19.149698 26.301100 +v 6.081800 18.746899 25.777700 +v 5.574200 18.342400 24.025101 +v 7.135100 19.530899 24.717701 +v 7.779800 19.773998 23.407799 +v 8.020100 18.982300 27.563700 +v 8.392000 18.935699 26.717300 +v 8.453600 20.037899 25.816999 +v 9.056499 19.751099 26.122900 +v 9.750700 19.149399 26.086100 +v 9.045300 18.102400 26.725201 +v 8.392000 18.935699 26.717300 +v 9.056499 19.751099 26.122900 +v 8.020100 18.982300 27.563700 +v 7.934700 18.251099 28.337000 +v 8.694700 22.772900 23.705000 +v 8.013100 21.006399 24.225800 +v 7.012400 20.872398 26.613600 +v 6.984700 22.220299 28.588100 +v 7.196100 23.754599 28.494600 +v 7.694900 22.997599 26.993000 +v 8.068399 24.343498 25.978899 +v 8.135799 22.591599 25.115601 +v 7.779800 19.773998 23.407799 +v 7.135100 19.530899 24.717701 +v 5.574200 18.342400 24.025101 +v 4.841000 19.778999 29.029600 +v 6.195600 20.606998 28.853001 +v 6.466800 21.688799 29.772400 +v 4.433700 18.263599 26.661900 +v 5.574400 19.192999 27.782400 +v 6.081800 18.746899 25.777700 +v 11.829400 21.927898 28.128901 +v 11.931800 20.497599 26.012600 +v 10.788900 18.264700 25.702999 +v 10.810200 21.749199 30.500299 +v 8.968200 18.860800 31.130600 +v 10.788900 18.264700 25.702999 +v 9.325300 17.111198 26.783699 +v 8.183599 17.714298 29.305099 +v 8.485399 16.647598 29.526300 +v 8.337700 15.785699 29.807400 +v 8.063800 16.910198 31.727900 +v 13.021500 22.690100 29.980700 +v 12.024300 14.130299 24.739799 +v 9.541300 13.163799 26.067499 +v 10.337999 15.227399 26.038099 +v 12.432800 15.218699 25.327200 +v 12.432800 15.218699 25.327200 +v 11.926600 17.197100 25.448601 +v 12.432800 15.218699 25.327200 +v 10.337999 15.227399 26.038099 +v 11.926600 17.197100 25.448601 +v 8.068900 14.106998 28.391600 +v 8.029100 14.195700 29.306099 +v 8.457900 15.729899 28.404301 +v 8.672400 15.228499 27.089300 +v 8.339399 13.894400 27.318199 +v 8.935101 13.560900 26.511200 +v 8.337700 15.785699 29.807400 +v 8.028500 14.503099 30.242599 +v 8.175600 17.343699 33.028999 +v 8.063800 16.910198 31.727900 +v 7.885100 15.190699 32.182999 +v 7.956600 15.493499 33.861401 +v 8.863501 15.672099 27.000900 +v 8.175600 17.343699 33.028999 +v 8.282600 17.734900 34.770699 +v 8.860701 18.521500 35.933998 +v 8.282600 17.734900 34.770699 +v 7.977400 15.804599 35.312401 +v 11.342899 11.152899 25.208500 +v 11.135500 10.312899 25.692900 +v 10.891000 11.556999 25.381500 +v 9.793699 12.084000 25.913300 +v 8.414600 12.973299 30.104200 +v 9.924299 11.294999 29.846399 +v 9.937099 11.218800 32.752602 +v 8.393700 13.574200 32.806602 +v 10.277300 11.020899 25.814699 +v 12.736900 15.554199 25.204201 +v 12.432800 15.218699 25.327200 +v 12.140200 14.545300 24.291500 +v 12.024300 14.130299 24.739799 +v 11.571100 10.029599 28.553400 +v 11.334100 9.597599 27.908501 +v 8.138900 12.786999 28.079800 +v 8.439800 12.882899 28.821899 +v 8.781000 11.594199 27.841999 +v 8.903700 12.028999 28.597300 +v 11.103700 9.794999 26.663500 +v 8.234301 12.764399 27.321100 +v 8.855300 11.485099 27.155300 +v 8.776899 12.516600 26.603899 +v 9.113899 11.532399 26.585501 +v 9.753901 10.732399 26.785000 +v 11.564600 10.134899 29.612900 +v 9.865400 11.415699 34.182701 +v 8.546400 13.524099 34.200001 +v 13.198900 9.273599 31.772600 +v 11.789100 9.924099 32.136200 +v 9.070100 13.262599 35.930099 +v 12.102500 10.401099 35.549599 +v 11.826900 9.928199 33.481697 +v 10.241800 11.778599 35.825001 +v 9.868900 11.123798 28.500999 +v 9.720100 10.700999 27.791901 +v 7.987600 16.279598 30.114100 +v 7.632900 16.752399 29.940399 +v 8.087700 15.869699 27.273100 +v 7.454200 16.635698 28.025299 +v 7.388300 16.936600 29.094799 +v 7.987600 16.279598 30.114100 +v 8.337700 15.785699 29.807400 +v 8.485399 16.647598 29.526300 +v 7.632900 16.752399 29.940399 +v 8.672400 15.228499 27.089300 +v 8.087700 15.869699 27.273100 +v 8.692699 16.180698 27.307699 +v 8.863501 15.672099 27.000900 +v 7.454200 16.635698 28.025299 +v 7.388300 16.936600 29.094799 +v 8.233200 16.826698 28.535500 +v 8.672400 15.228499 27.089300 +v 11.650100 14.220900 22.196301 +v 11.786000 13.995199 21.655800 +v 11.648000 13.698099 21.912701 +v 11.564899 13.849998 22.148800 +v 12.120700 13.769699 23.121399 +v 12.240100 13.834099 23.638901 +v 12.257100 13.662799 23.178200 +v 12.398100 13.697499 23.632999 +v 12.149100 14.148600 23.069000 +v 12.473700 13.677099 23.729500 +v 12.347400 14.180698 23.645599 +v 12.666100 14.006800 23.720501 +v 11.751600 14.238998 21.706900 +v 11.032000 14.840099 23.709801 +v 11.564899 14.588200 23.821100 +v 11.111700 14.985699 24.077700 +v 10.742800 14.873499 23.358200 +v 11.595900 14.374599 23.281000 +v 10.178699 15.260999 23.173199 +v 11.645300 14.862700 24.409401 +v 10.895599 15.225599 24.308899 +v 10.932899 15.766899 25.140100 +v 11.909500 15.179699 25.056799 +v 10.064199 15.960299 24.433800 +v 10.740800 16.773298 25.593100 +v 12.099200 15.632299 25.468500 +v 11.341100 16.180099 25.629999 +v 13.578600 19.054699 21.822800 +v 14.798500 18.372799 21.529099 +v 14.811600 19.028700 20.765099 +v 13.433300 19.577999 21.046799 +v 12.226300 18.484699 23.926201 +v 13.669000 17.624498 23.558701 +v 12.288600 19.099699 22.016300 +v 14.580400 16.892698 23.264500 +v 12.177400 19.477699 21.161701 +v 11.443700 18.292700 21.853500 +v 11.226600 18.794199 20.837399 +v 12.271700 20.034300 20.342899 +v 11.826700 21.154299 19.362900 +v 10.566700 20.417999 19.292500 +v 11.257600 19.317200 20.004499 +v 13.564500 20.213299 20.176399 +v 13.350800 21.414099 18.915600 +v 14.892700 19.868299 19.923901 +v 15.034900 21.027498 18.748600 +v 11.300400 22.136398 17.851900 +v 9.973499 21.340599 17.976601 +v 13.002399 22.252298 17.420799 +v 15.108800 21.997398 17.332100 +v 10.583000 22.785198 16.526100 +v 9.017600 21.826599 16.808701 +v 12.675000 23.106298 16.058599 +v 15.224900 22.898600 15.956800 +v 10.061200 16.052799 22.476200 +v 9.368500 16.670198 22.501200 +v 10.681900 17.460400 21.042099 +v 10.847700 18.494799 23.800900 +v 9.979500 18.939999 23.334999 +v 10.207700 17.934799 23.823999 +v 9.511600 17.223000 23.413898 +v 9.106300 18.262400 23.090199 +v 8.697800 18.122599 22.116199 +v 11.443700 18.292700 21.853500 +v 10.997400 18.726099 20.865900 +v 11.204599 19.537600 21.603100 +v 11.600100 18.765699 23.235001 +v 8.977600 17.633598 21.374100 +v 9.919900 17.882000 20.782700 +v 10.716600 19.814999 22.242500 +v 12.226300 18.484699 23.926201 +v 9.821199 19.854698 21.568800 +v 10.142900 19.443998 20.974201 +v 9.750100 19.874100 20.497900 +v 9.482901 20.281500 21.131199 +v 8.785900 19.220400 21.396801 +v 9.221800 19.697599 21.698900 +v 8.879601 20.189999 21.195999 +v 8.573200 19.699099 21.102600 +v 9.080800 18.923100 20.829500 +v 8.800900 19.374399 20.584499 +v 9.682500 19.118000 20.610300 +v 9.250800 19.570499 20.252800 +v 8.913900 20.613600 20.625099 +v 9.334499 20.457899 20.809799 +v 8.717500 19.411098 20.654400 +v 9.250800 19.570499 20.252800 +v 9.890200 19.586899 22.723400 +v 9.675301 20.103998 22.762699 +v 8.588100 19.323898 22.692900 +v 9.027900 18.941399 22.628300 +v 8.513599 17.601599 21.068199 +v 9.691200 17.852400 20.365000 +v 10.737400 20.289299 22.072100 +v 8.183500 18.253300 22.013000 +v 11.043500 18.872099 20.419498 +v 11.329700 19.895599 21.239500 +v 9.470600 20.477200 22.282200 +v 8.362300 19.730099 22.262899 +v 8.124599 18.036699 20.741100 +v 9.275000 18.253500 19.918999 +v 10.668500 19.235100 19.898399 +v 11.043500 18.872099 20.419498 +v 10.476400 20.678799 21.613800 +v 7.859200 18.713200 21.688000 +v 10.668500 19.235100 19.898399 +v 11.033700 20.252399 20.763199 +v 9.540300 20.106798 20.270901 +v 9.129000 20.227999 20.063400 +v 8.555799 19.704599 21.088100 +v 9.682500 19.118000 20.610300 +v 8.705200 19.620899 20.031200 +v 7.990100 19.542799 20.414501 +v 7.867700 19.899799 20.811001 +v 8.318600 20.430698 20.868999 +v 7.849600 20.314299 19.548698 +v 7.578800 20.486399 19.322800 +v 7.509300 20.762798 19.577801 +v 7.751000 20.654400 19.813900 +v 7.751000 20.654400 19.813900 +v 7.509300 20.762798 19.577801 +v 7.214400 20.603800 20.015800 +v 7.477500 20.554399 20.182199 +v 6.993900 20.046200 20.324400 +v 7.252200 20.189098 20.319700 +v 7.082700 19.752699 20.048401 +v 7.350800 19.848999 20.054600 +v 7.350800 19.848999 20.054600 +v 7.082700 19.752699 20.048401 +v 7.383600 20.051498 19.542900 +v 7.614700 20.025299 19.755800 +v 7.232800 20.743999 19.138500 +v 7.158900 20.859999 19.148201 +v 7.158900 20.859999 19.148201 +v 6.802000 20.733500 19.350700 +v 6.619700 20.566198 19.593500 +v 6.501000 20.510599 19.720001 +v 6.288200 19.976299 19.613100 +v 6.429100 20.072399 19.588400 +v 6.278000 19.831200 19.493900 +v 6.543700 20.095699 19.691399 +v 6.742600 20.348398 19.524700 +v 6.620900 19.961498 19.991001 +v 6.748800 19.820398 19.993401 +v 6.716400 19.963499 19.710300 +v 6.811700 19.730398 19.778099 +v 6.587300 20.164398 19.900799 +v 6.470100 20.311499 19.810501 +v 6.470100 20.311499 19.810501 +v 6.587300 20.164398 19.900799 +v 7.267700 20.385599 18.984100 +v 7.268400 20.384298 19.123299 +v 7.027600 20.098700 19.104401 +v 6.686900 20.009199 19.269199 +v 6.711400 20.044498 19.408600 +v 6.656800 19.862400 19.278801 +v 6.511300 20.289200 19.289101 +v 6.362800 20.280800 19.356600 +v 6.451700 20.122499 19.561100 +v 6.346100 20.132999 19.457001 +v 6.465200 20.073999 19.500200 +v 6.622800 20.267199 19.401100 +v 6.621500 20.199699 19.364500 +v 6.511300 20.289200 19.289101 +v 6.619700 20.566198 19.593500 +v 6.385000 20.198700 19.305000 +v 6.686900 20.009199 19.269199 +v 6.656800 19.862400 19.278801 +v 6.559800 19.882500 19.395599 +v 6.600000 19.969299 19.481100 +v 7.243600 20.644999 19.149399 +v 7.181700 20.617498 18.998600 +v 7.181700 20.617498 18.998600 +v 6.791100 20.473499 19.113499 +v 6.619700 20.566198 19.593500 +v 6.644600 20.252399 19.280300 +v 6.727600 20.271799 19.433100 +v 7.240700 20.537300 19.108398 +v 6.834900 20.360498 19.073601 +v 7.267700 20.385599 18.984100 +v 7.027600 20.098700 19.104401 +v 6.835400 20.009199 19.272099 +v 6.879600 20.079199 19.342499 +v 6.769000 20.197300 19.372499 +v 6.630400 19.777599 19.902100 +v 6.288200 19.976299 19.613100 +v 6.278000 19.831200 19.493900 +v 6.453400 19.789598 19.434999 +v 6.548200 19.951399 19.465900 +v 6.723200 19.940100 19.630899 +v 6.596900 20.072800 19.771500 +v 6.620900 19.961498 19.991001 +v 6.453400 19.789598 19.434999 +v 6.811700 19.730398 19.778099 +v 6.346100 20.132999 19.457001 +v 6.559800 19.882500 19.395599 +v 6.644600 20.252399 19.280300 +v 6.835400 20.009199 19.272099 +v 9.322001 20.590300 21.804901 +v 8.420500 19.912199 21.919701 +v 8.147600 18.474899 20.666100 +v 9.131200 18.587700 19.963900 +v 10.304100 19.441299 19.855900 +v 10.137700 20.756699 21.179501 +v 7.964200 19.071899 21.443800 +v 10.304100 19.441299 19.855900 +v 10.580900 20.382999 20.476700 +v 7.849600 20.314299 19.548698 +v 7.751000 20.654400 19.813900 +v 8.705200 19.620899 20.031200 +v 7.477500 20.554399 20.182199 +v 7.252200 20.189098 20.319700 +v 7.350800 19.848999 20.054600 +v 7.614700 20.025299 19.755800 +v 7.614700 20.025299 19.755800 +v 11.443700 18.292700 21.853500 +v 10.997400 18.726099 20.865900 +v 11.581100 19.922298 19.909800 +v 13.106400 19.969500 19.790901 +v 13.049300 20.638699 19.064301 +v 11.190200 20.442200 18.586800 +v 13.179300 20.256399 16.327801 +v 12.753300 20.909399 17.606300 +v 11.776400 20.552099 16.764599 +v 11.005199 20.229300 17.199900 +v 10.701099 19.240000 17.120499 +v 10.685600 19.217499 18.144001 +v 11.116300 18.144899 16.815300 +v 10.865700 18.211800 17.673800 +v 11.796400 17.586699 17.457500 +v 12.137400 17.692999 16.808300 +v 13.762900 17.183899 16.625500 +v 12.653200 19.589199 14.343700 +v 11.554600 19.765299 14.554800 +v 10.927400 19.302799 14.723900 +v 10.807100 18.455999 14.839600 +v 11.362300 17.625299 14.902599 +v 12.291700 17.438299 14.663099 +v 13.016700 17.772699 14.364200 +v 13.491700 17.927099 16.141201 +v 12.428300 17.675999 16.483101 +v 13.128900 18.825199 14.254601 +v 13.794300 19.180500 16.091099 +v 13.794300 19.180500 16.091099 +v 13.128900 18.825199 14.254601 +v 12.507800 19.352798 13.641300 +v 12.415200 19.178598 12.777700 +v 11.599000 19.243000 12.854000 +v 11.589700 19.450800 13.722400 +v 11.108800 18.820599 13.158700 +v 11.083000 18.147200 13.150700 +v 11.554000 17.579399 12.565500 +v 11.531300 17.468100 13.091400 +v 12.390100 17.420099 13.666100 +v 11.505000 17.553699 13.721000 +v 12.382000 17.391600 13.025800 +v 12.835100 17.938198 12.990200 +v 12.858600 18.661900 12.979400 +v 12.367100 18.779900 11.108500 +v 11.669600 18.796000 11.138599 +v 11.266300 18.184198 11.440900 +v 11.271800 17.813000 11.587999 +v 11.656799 17.434299 11.537000 +v 12.396700 17.433100 11.487600 +v 12.374200 17.526400 12.494800 +v 12.778000 17.775299 11.471300 +v 12.858600 18.661900 12.979400 +v 12.772000 18.172699 11.317800 +v 12.772000 18.172699 11.317800 +v 10.938200 18.754900 19.274399 +v 11.857400 17.267399 18.412300 +v 10.965200 17.952999 18.790100 +v 12.869600 18.166199 9.274700 +v 12.885700 18.112999 9.409100 +v 12.877600 17.538099 9.298700 +v 12.962800 17.505798 9.012000 +v 12.515900 18.707298 9.321100 +v 11.197200 17.876799 9.856200 +v 11.586599 18.430399 9.766900 +v 11.934100 18.717098 9.285300 +v 11.607500 18.100199 9.251600 +v 11.377399 17.235298 9.877700 +v 11.622600 17.554298 9.108100 +v 11.862700 16.842400 8.922900 +v 11.768600 16.883499 8.685400 +v 12.040200 16.374899 8.596200 +v 12.722300 16.836399 9.020800 +v 12.643300 16.367599 8.665500 +v 12.855700 16.858200 8.806499 +v 12.224300 16.596699 9.262899 +v 12.099200 16.849199 9.871200 +v 12.763100 17.266199 9.979900 +v 11.642400 17.503300 8.818600 +v 12.962800 17.505798 9.012000 +v 12.855700 16.858200 8.806499 +v 11.768600 16.883499 8.685400 +v 12.358300 16.196299 8.591400 +v 12.643300 16.367599 8.665500 +v 12.358300 16.196299 8.591400 +v 12.040200 16.374899 8.596200 +v 11.648600 18.201799 8.984500 +v 11.952300 18.707399 9.137899 +v 12.526100 18.725698 9.208700 +v 12.812700 18.206299 9.118299 +v 11.952300 18.707399 9.137899 +v 12.515900 18.707298 9.321100 +v 12.526100 18.725698 9.208700 +v 11.605500 18.157700 9.127399 +v 11.648600 18.201799 8.984500 +v 11.605500 18.157700 9.127399 +v 12.869600 18.166199 9.274700 +v 12.379600 18.471298 9.798000 +v 12.526100 18.725698 9.208700 +v 12.812700 18.206299 9.118299 +v 12.889800 17.900200 9.953400 +v 12.379600 18.471298 9.798000 +v 12.368299 19.280499 11.110700 +v 13.120300 18.392799 11.341700 +v 12.825300 17.393200 11.495700 +v 11.951300 16.764898 11.560500 +v 11.227700 19.230000 11.137199 +v 11.137300 19.859798 12.866400 +v 12.496300 19.897200 12.822800 +v 12.368299 19.280499 11.110700 +v 10.660200 18.412298 11.326400 +v 10.352000 18.789598 12.953300 +v 10.982900 17.246799 11.511700 +v 10.811100 17.252699 13.116800 +v 11.878799 16.625799 13.097600 +v 13.324300 18.753099 12.897800 +v 12.950900 17.252298 13.129900 +v 11.642400 17.503300 8.818600 +v 12.496300 19.897200 12.822800 +v 13.051800 16.337399 25.131399 +v 11.242400 14.675399 22.790199 +v 11.259800 15.021899 21.672199 +v 12.254200 16.126900 25.336100 +v 11.936900 15.561899 25.077400 +v 11.377200 17.195198 25.363600 +v 10.719800 16.653999 25.100901 +v 10.513900 15.315099 23.643499 +v 9.498600 15.971498 23.225300 +v 10.064000 15.578099 22.378099 +v 12.492500 17.927599 24.676100 +v 11.839100 18.589298 24.493900 +v 10.979900 18.195599 24.784300 +v 12.535700 19.275299 23.489500 +v 13.408400 17.440899 24.438999 +v 13.734200 18.896000 23.204300 +v 10.028700 15.948099 24.502600 +v 11.464000 15.022599 24.440800 +v 11.735600 14.576399 23.773701 +v 9.461400 16.562199 23.958199 +v 10.304600 17.454300 24.585499 +v 12.234400 16.759699 25.371799 +v 11.796600 18.759899 24.469700 +v 10.897400 18.310200 24.757500 +v 9.318399 16.699598 23.901100 +v 9.365100 16.094099 23.119801 +v 10.180900 17.581900 24.569401 +v 11.103300 15.085499 21.447399 +v 9.968200 15.641699 22.204500 +v 9.013000 17.209000 23.435299 +v 9.877100 18.109200 24.140099 +v 9.451400 18.658699 23.865499 +v 8.648200 17.693298 23.139099 +v 10.589000 18.900000 24.278900 +v 10.229000 19.363899 24.022099 +v 8.844200 16.910099 22.079201 +v 11.340099 19.416498 23.781898 +v 11.159900 15.699699 20.270401 +v 9.874500 16.193899 20.931301 +v 12.123500 16.463900 12.401800 +v 12.138900 16.632799 12.726100 +v 11.979100 16.643999 12.772300 +v 11.869300 16.643398 12.705800 +v 11.854500 16.432699 12.422199 +v 11.609900 16.655100 12.703300 +v 11.001699 16.875599 12.964100 +v 11.587700 16.491400 12.437400 +v 12.892000 16.741798 12.327900 +v 13.001300 16.806099 12.650499 +v 11.724300 16.656599 12.783700 +v 10.887600 16.814699 12.339000 +v 10.852900 16.847399 11.853500 +v 11.571199 16.600300 12.027200 +v 11.730900 16.584499 11.925000 +v 11.900300 16.586199 12.037200 +v 12.050200 16.572699 11.958600 +v 12.173900 16.574200 12.044000 +v 12.861800 16.812500 11.825000 +v 10.873899 16.929798 12.851400 +v 12.942500 16.818998 12.852200 +v 10.983400 16.774399 11.743200 +v 12.671100 16.691500 11.774099 +v 12.742300 16.864599 12.890100 +v 11.208700 16.859299 12.922800 +v 9.242900 19.304600 35.936699 +v 8.755600 19.757198 35.675900 +v 8.438800 19.248499 35.695602 +v 8.860701 18.521500 35.933998 +v 8.514000 19.713299 35.356300 +v 8.988600 19.363798 35.175598 +v 8.755600 19.757198 35.675900 +v 9.242900 19.304600 35.936699 +v 8.223200 20.185999 35.666100 +v 7.977400 15.804599 35.312401 +v 8.928101 13.792099 36.625198 +v 9.383500 15.255199 37.436798 +v 9.865000 12.889299 37.267300 +v 10.462999 14.581900 38.101799 +v 7.908300 14.313999 35.368797 +v 8.589500 12.771199 35.144302 +v 10.787200 11.981699 37.833500 +v 13.300400 10.645199 38.096199 +v 13.994100 12.863298 38.948299 +v 11.922100 13.822999 38.749901 +v 12.595200 9.063799 36.145798 +v 10.121400 10.615599 36.129299 +v 9.906900 9.970299 34.094498 +v 10.809600 8.874099 33.560898 +v 16.132299 21.816999 35.906502 +v 15.601000 20.257999 37.681000 +v 15.195900 17.940498 38.579800 +v 16.976799 14.931699 38.657600 +v 16.005600 9.997399 37.621300 +v 16.601999 12.238899 38.491898 +v 15.354700 8.403399 35.665802 +v 11.895000 8.162399 33.399101 +v 13.075000 7.970099 33.269798 +v 21.501999 12.646099 33.830101 +v 20.970200 12.444499 35.335800 +v 20.481100 11.105199 34.202400 +v 19.948299 12.291100 36.397400 +v 19.469999 10.628698 35.380501 +v 19.672600 10.165099 34.366302 +v 18.444500 10.147499 36.484001 +v 18.073500 9.195699 34.405602 +v 18.560900 12.230399 37.572201 +v 16.751900 8.475899 33.875500 +v 18.222401 8.972199 31.535500 +v 18.217499 9.444699 29.965700 +v 18.588699 9.706499 29.898001 +v 18.774000 9.383799 31.777399 +v 18.033899 9.856999 28.070400 +v 19.037701 9.937499 27.939899 +v 17.628700 9.762699 27.165400 +v 18.349499 9.811199 26.893200 +v 17.138399 9.684299 26.458599 +v 17.812901 9.882999 26.372999 +v 16.547199 9.484099 25.965700 +v 19.704599 10.146299 29.805799 +v 19.916199 10.012699 31.974600 +v 19.584099 10.782000 27.801701 +v 20.399000 11.024099 29.760401 +v 18.786400 10.507999 26.607100 +v 17.451300 10.043200 25.570900 +v 20.187599 11.521799 27.519400 +v 21.133101 12.039598 29.720699 +v 19.428200 11.141499 26.531300 +v 18.418699 10.571799 25.765400 +v 20.759701 11.101799 32.072201 +v 21.690500 12.344099 31.905600 +v 14.691000 7.867699 33.532898 +v 15.467500 8.005299 31.606199 +v 16.087200 8.222399 30.750500 +v 17.171600 8.882599 29.835199 +v 13.289700 7.964099 31.257900 +v 14.306200 7.796899 31.428699 +v 12.028700 8.039699 31.402201 +v 10.860500 9.030799 31.192600 +v 9.793300 9.994899 31.689699 +v 9.134800 11.195299 32.047401 +v 13.773100 7.971199 30.657999 +v 14.714400 8.067399 29.846100 +v 13.093400 7.953899 29.769501 +v 11.843900 8.523699 30.451500 +v 10.264800 9.584699 30.993200 +v 10.877500 9.067099 30.432600 +v 9.684401 10.419899 31.342100 +v 8.891600 11.723000 34.961998 +v 7.830200 13.067900 33.681099 +v 8.778500 12.179298 33.299400 +v 7.225300 14.458099 33.949100 +v 7.366500 15.986099 33.941399 +v 8.370100 12.470699 31.962700 +v 7.390900 13.204199 32.293900 +v 7.057400 15.899199 32.512600 +v 6.782800 14.431099 32.519302 +v 7.219100 13.226398 30.898199 +v 8.126600 12.608899 30.690800 +v 6.966400 15.620899 31.030600 +v 6.702600 14.330399 30.997200 +v 7.203500 13.071499 29.687500 +v 8.041000 12.514999 29.487700 +v 6.874300 15.158199 29.690100 +v 6.757200 14.114698 29.780800 +v 7.345400 12.826599 28.899900 +v 8.112300 12.300599 28.836700 +v 6.866300 14.791399 28.847300 +v 6.969500 13.839600 28.856199 +v 7.691200 12.510499 28.451700 +v 8.308800 12.069699 28.157000 +v 6.912200 14.280699 28.190701 +v 7.060000 13.830399 27.718599 +v 7.299200 13.106499 28.072701 +v 7.974400 12.213399 27.344601 +v 18.161400 10.320899 25.953300 +v 22.024700 12.609999 31.670300 +v 22.209599 12.996599 32.675800 +v 22.516899 13.072199 31.562901 +v 22.679300 13.435598 32.161499 +v 22.945999 13.506299 31.651899 +v 23.352200 14.171399 31.831301 +v 7.489200 16.806000 34.783600 +v 7.162500 16.361898 33.812202 +v 7.091500 17.533600 34.422798 +v 6.885000 17.042000 33.845699 +v 6.745400 18.608398 34.267899 +v 6.699000 17.827999 34.038498 +v 8.098000 16.955299 34.137199 +v 7.566500 17.478699 34.122101 +v 7.566500 17.478699 34.122101 +v 8.098000 16.955299 34.137199 +v 7.207200 17.895399 34.122398 +v 7.207200 17.895399 34.122398 +v 14.899199 15.830099 42.052299 +v 15.785400 15.249799 41.997898 +v 15.647900 15.925999 42.519100 +v 14.550500 15.622999 40.771999 +v 15.418400 14.858199 40.914902 +v 14.721800 15.536699 39.341499 +v 15.342800 15.100698 39.506401 +v 14.978000 15.654399 38.962898 +v 15.877700 15.186299 38.852402 +v 18.200901 14.654599 41.602200 +v 17.723499 14.792899 42.037998 +v 17.929899 14.480999 40.782101 +v 18.796400 15.177799 42.183399 +v 18.298599 14.970599 42.096500 +v 18.960199 14.958899 41.325401 +v 17.345301 14.708099 39.225899 +v 18.461700 14.926600 38.796600 +v 18.904600 14.796999 39.694500 +v 18.065599 15.488699 42.601501 +v 16.329700 15.865199 42.688000 +v 13.994100 12.863298 38.948299 +v 14.978000 15.654399 38.962898 +v 15.885400 15.188799 38.892300 +v 16.983799 14.933999 38.695999 +v 17.924400 22.184898 45.009701 +v 16.833200 21.023199 45.952599 +v 17.843599 21.608000 48.444500 +v 19.305901 22.790798 47.339500 +v 22.052500 22.336300 46.124802 +v 23.320700 20.768398 46.052101 +v 22.157200 20.376200 43.702499 +v 21.106400 21.805099 43.642601 +v 17.097700 19.313900 47.819500 +v 17.442299 19.548300 49.066799 +v 23.739100 19.077900 46.274601 +v 22.975500 18.861898 45.064999 +v 22.706501 18.698099 44.143700 +v 16.521900 19.091499 46.648201 +v 20.844200 20.412199 53.766701 +v 22.724300 20.654400 55.110802 +v 23.195301 21.944500 54.313599 +v 21.159500 21.819099 53.024601 +v 23.894100 22.808498 53.366199 +v 21.817400 22.883699 52.182201 +v 22.635601 21.661598 51.767101 +v 24.387400 20.327299 53.311100 +v 22.889000 20.058899 51.799500 +v 24.878201 20.829699 56.070900 +v 25.323799 22.016499 55.368599 +v 25.798800 22.638699 54.501801 +v 26.067400 20.506399 54.304901 +v 27.502399 20.947699 56.716900 +v 27.693901 21.988998 56.092201 +v 27.926100 22.344200 55.234402 +v 28.005299 20.614899 54.898602 +v 29.575600 20.964600 56.810200 +v 29.750200 21.940498 56.198898 +v 29.858900 22.128899 55.486500 +v 29.770000 20.645498 55.066601 +v 31.908199 20.887999 56.393101 +v 31.838600 21.910500 55.494598 +v 31.604799 20.586800 54.747002 +v 33.875000 20.743698 55.605202 +v 33.584000 21.674799 54.650700 +v 33.128998 20.440399 53.947800 +v 35.218300 20.485498 54.194801 +v 34.757698 21.346199 53.362801 +v 34.025497 20.240700 52.856998 +v 35.811501 20.207100 52.673901 +v 35.256798 20.894499 52.147400 +v 34.449501 20.060999 51.874901 +v 35.798901 19.893200 50.972401 +v 35.215900 20.486099 50.778000 +v 34.572502 19.877499 50.858799 +v 35.171299 19.573198 49.212700 +v 34.691902 20.096498 49.196499 +v 34.225502 19.612700 49.422699 +v 34.100399 19.329699 47.852699 +v 33.791397 19.824598 48.081699 +v 33.479599 19.439098 48.501400 +v 32.588402 19.237499 47.310299 +v 32.532299 19.679899 47.580200 +v 32.395401 19.344198 47.991600 +v 31.237600 19.263199 47.403801 +v 31.309999 19.671900 47.631901 +v 31.359200 19.354799 48.032600 +v 31.086500 19.237398 47.228699 +v 30.943501 19.245199 47.272099 +v 31.067400 19.841999 47.633999 +v 31.206200 19.869200 47.580299 +v 29.725399 19.375500 48.023201 +v 29.120499 19.566299 48.944099 +v 29.455601 19.851898 49.070099 +v 29.976101 19.727098 48.238899 +v 29.224100 19.752600 50.277500 +v 29.549900 19.992399 50.097698 +v 29.749500 19.922798 51.135601 +v 29.959599 20.127399 50.886600 +v 30.756800 20.007399 51.604801 +v 30.847500 20.195299 51.341900 +v 31.896799 19.997599 51.545101 +v 31.800499 20.162100 51.261799 +v 32.753300 19.877399 50.871101 +v 32.509003 20.046000 50.752399 +v 32.900101 19.696499 49.882900 +v 32.659698 19.875000 49.906101 +v 32.363899 19.576399 49.226002 +v 32.239201 19.752298 49.341999 +v 31.527800 19.571899 49.196098 +v 31.554001 19.669298 49.317398 +v 31.369101 19.561798 49.138500 +v 31.373199 19.800798 49.291698 +v 23.525700 19.129200 46.538300 +v 23.109200 20.701799 46.293701 +v 23.565800 21.127998 48.000698 +v 24.814999 19.458799 48.421501 +v 22.022600 22.169600 46.423901 +v 22.568401 21.847599 48.036598 +v 20.740200 22.661200 46.858398 +v 21.614799 22.491299 48.846001 +v 20.425800 22.757000 49.850101 +v 21.387400 22.446199 50.195801 +v 19.432100 21.692999 50.904900 +v 18.993700 20.011599 51.554798 +v 17.717300 19.544899 49.038300 +v 18.082600 21.438900 48.477901 +v 19.552900 22.539200 47.462299 +v 22.588501 19.891499 50.858101 +v 23.727200 19.584299 49.127201 +v 22.952700 19.724798 49.920300 +v 18.082600 21.438900 48.477901 +v 19.552900 22.539200 47.462299 +v 22.022600 22.169600 46.423901 +v 23.109200 20.701799 46.293701 +v 23.525700 19.129200 46.538300 +v 22.157200 20.376200 43.702499 +v 22.706501 18.698099 44.143700 +v 21.821100 18.244799 41.630798 +v 21.458700 20.131800 41.431400 +v 21.274700 20.172199 38.547199 +v 21.839800 17.744099 38.889500 +v 21.529900 20.258299 36.411301 +v 22.320801 17.358799 36.796101 +v 22.747101 20.884600 33.024902 +v 24.460899 16.720200 33.317200 +v 24.103401 21.813499 29.221800 +v 27.777800 16.209999 30.555399 +v 17.924400 22.184898 45.009701 +v 19.741400 22.531099 44.147499 +v 18.796101 22.486500 42.025200 +v 16.905899 22.405800 42.711300 +v 17.679399 23.358398 39.099800 +v 15.304700 22.940699 40.039398 +v 16.980101 23.900799 37.229797 +v 14.275400 23.594999 38.366600 +v 16.019400 25.157000 34.757301 +v 12.902400 24.491999 36.269402 +v 14.684200 27.556599 30.928600 +v 10.723800 26.321899 33.321999 +v 15.715800 20.988400 43.371601 +v 14.072700 20.964199 40.835400 +v 12.772099 21.183199 39.549400 +v 10.292300 21.766998 38.015099 +v 7.085100 22.698599 36.585197 +v 16.833200 21.023199 45.952599 +v 15.137700 18.577999 43.983398 +v 16.521900 19.091499 46.648201 +v 13.230700 18.086399 41.598900 +v 11.746500 17.864199 40.640301 +v 8.567900 17.659300 39.884102 +v 5.016800 17.690899 40.174400 +v 21.106400 21.805099 43.642601 +v 20.386400 21.857800 41.532898 +v 19.803200 22.269600 38.576500 +v 19.642099 22.833099 36.491600 +v 19.432899 23.637100 33.710999 +v 19.487801 25.613699 29.316799 +v 4.086600 23.912399 37.130100 +v 2.678500 17.999800 41.630100 +v 8.035999 30.464699 30.798700 +v 13.856600 31.534599 27.899799 +v 20.267000 30.722500 25.799099 +v 26.328999 23.986599 26.757799 +v 30.844299 15.962599 29.224100 +v 26.487000 27.494099 25.191299 +v 33.175201 16.008099 29.537800 +v 31.743999 19.781099 27.997999 +v 0.710299 25.292698 39.406097 +v 0.165400 23.155399 42.199100 +v 3.449300 28.012600 34.737400 +v 0.078200 20.794600 44.445400 +v 30.909201 23.855499 26.287600 +v 33.797699 20.866199 27.970699 +v 35.438900 18.301399 29.734499 +v 35.847198 16.175299 30.563099 +v 0.114600 18.762600 45.177898 +v 30.969099 19.638899 47.739700 +v 30.887400 19.281898 47.497299 +v 17.873800 15.650299 46.402599 +v 19.049999 16.165798 48.667301 +v 17.815701 17.410398 49.233700 +v 16.786400 17.038399 46.681999 +v 22.106400 16.117298 47.231998 +v 21.058701 15.609499 44.964199 +v 22.119301 16.975899 44.334099 +v 23.299301 17.428699 46.685001 +v 20.579201 15.588599 48.014599 +v 20.117001 15.396699 48.154499 +v 20.561600 15.293599 47.971298 +v 18.621599 15.361199 46.684200 +v 18.733700 15.141899 46.643799 +v 18.851799 15.425599 46.772400 +v 21.635000 15.787799 47.532898 +v 19.172100 14.845798 46.485401 +v 19.379101 15.040099 46.590599 +v 19.617500 15.779299 48.376801 +v 19.446400 14.965599 46.399399 +v 19.101101 15.581299 47.655300 +v 19.197201 15.365599 47.634800 +v 19.301100 15.639699 47.733002 +v 19.629101 15.074399 47.520802 +v 19.828800 15.263399 47.597198 +v 19.926600 15.179899 47.399899 +v 19.677601 15.187599 47.278698 +v 19.151600 15.568099 47.428200 +v 18.695200 15.349099 46.459499 +v 19.227299 14.962699 46.271801 +v 19.552401 14.765799 45.731899 +v 19.538500 15.047299 45.685398 +v 20.527199 15.275700 45.292500 +v 19.062000 14.874899 45.923199 +v 18.523399 15.264499 46.122799 +v 22.975500 18.861898 45.064999 +v 23.739100 19.077900 46.274601 +v 21.163500 18.845299 53.600899 +v 23.202801 19.173698 54.853901 +v 21.814701 17.558998 53.186401 +v 23.899700 18.034698 54.272499 +v 22.637501 18.557299 52.366501 +v 22.889000 20.058899 51.799500 +v 25.331900 19.476999 55.866798 +v 25.806700 18.587999 55.276501 +v 26.067400 20.506399 54.304901 +v 27.702000 19.758499 56.533798 +v 27.502399 20.947699 56.716900 +v 27.934101 19.122398 55.857399 +v 28.005299 20.614899 54.898602 +v 29.758301 19.841299 56.616402 +v 29.575600 20.964600 56.810200 +v 29.867001 19.412800 56.016998 +v 31.846701 19.619598 55.947300 +v 31.908199 20.887999 56.393101 +v 33.592102 19.540798 55.074600 +v 33.875000 20.743698 55.605202 +v 33.128998 20.440399 53.947800 +v 34.765800 19.391800 53.753899 +v 34.025497 20.240700 52.856998 +v 35.264999 19.383799 52.457298 +v 34.449501 20.060999 51.874901 +v 35.228199 19.283098 51.045799 +v 34.706100 19.088799 49.435600 +v 33.805599 18.948099 48.296700 +v 34.100399 19.329699 47.852699 +v 32.548000 18.910700 47.781498 +v 31.326200 18.950100 47.827198 +v 31.237600 19.263199 47.403801 +v 31.157400 19.398998 48.315300 +v 31.087601 18.802099 47.924400 +v 31.226900 18.758799 47.886600 +v 31.310499 19.389099 48.257599 +v 29.991001 19.106998 48.411301 +v 30.175600 19.456499 48.615002 +v 29.768400 19.610399 49.326599 +v 29.471699 19.328999 49.228401 +v 29.822701 19.715698 50.075001 +v 29.566200 19.475599 50.254700 +v 30.191799 19.868000 50.799198 +v 29.973999 19.660500 51.027199 +v 30.982399 19.942598 51.202702 +v 30.860001 19.755798 51.470798 +v 31.757099 19.929899 51.142601 +v 31.810499 19.757099 51.375599 +v 32.362900 19.838099 50.655998 +v 32.517200 19.683599 50.852100 +v 32.488503 19.714899 49.983002 +v 32.667999 19.543798 50.000099 +v 32.119400 19.629200 49.514801 +v 32.247498 19.458799 49.429100 +v 31.538099 19.631399 49.524101 +v 31.562300 19.529099 49.376400 +v 31.381399 19.397799 49.398899 +v 31.243200 19.698000 49.888599 +v 23.525700 19.129200 46.538300 +v 24.814999 19.458799 48.421501 +v 23.554199 17.764799 48.642799 +v 23.122601 17.542000 46.922298 +v 22.544701 17.101398 48.927700 +v 21.981499 16.249298 47.563198 +v 20.700100 15.940899 48.196701 +v 21.587000 16.773600 49.913502 +v 21.372299 17.281500 51.166500 +v 20.401600 16.865398 50.950699 +v 18.993700 20.011599 51.554798 +v 19.425400 18.221100 51.568600 +v 18.058300 17.584499 49.205601 +v 19.507801 16.231098 48.671200 +v 22.588501 19.891499 50.858101 +v 23.727200 19.584299 49.127201 +v 21.399200 16.401699 42.100101 +v 21.177200 15.323699 39.398499 +v 21.433300 14.314299 37.411400 +v 22.588600 12.417398 34.387199 +v 23.852900 10.389199 31.227499 +v 17.862801 15.442699 46.017601 +v 16.753500 14.558799 44.071598 +v 18.631401 14.302599 43.421700 +v 19.428801 15.218599 45.273701 +v 15.063300 12.977399 41.755299 +v 17.402599 12.308899 40.945801 +v 13.953600 11.641799 40.417801 +v 16.666599 10.847399 39.395100 +v 12.464800 9.845599 38.750900 +v 15.576300 8.535399 37.536201 +v 10.123199 6.789099 36.684898 +v 13.942200 4.941699 34.837799 +v 15.628300 16.095999 44.223301 +v 13.946500 15.101599 41.866001 +v 12.610400 14.327599 40.744801 +v 10.081500 13.040400 39.565300 +v 6.828700 11.456599 38.604202 +v 15.137700 18.577999 43.983398 +v 20.530701 15.475399 44.762901 +v 20.253901 14.781898 42.765701 +v 19.609400 13.203799 40.106899 +v 19.467300 11.487700 38.356499 +v 19.243700 9.247799 36.041500 +v 18.956400 6.414999 32.639801 +v 2.678500 17.999800 41.630100 +v 3.833899 10.525299 39.523598 +v 7.220700 1.856199 35.783897 +v 12.819900 0.000000 33.344101 +v 19.424099 0.516800 31.067200 +v 26.038399 7.540499 29.698200 +v 30.844299 15.962599 29.224100 +v 26.080500 3.687999 29.438700 +v 31.694000 11.975698 29.439899 +v 0.094399 13.433199 43.984699 +v 0.538400 10.273699 42.126301 +v 3.008200 5.749599 38.690498 +v 0.064199 16.560799 45.246101 +v 30.760700 7.562799 29.249001 +v 33.758900 10.931000 29.806299 +v 35.437500 13.918098 30.566799 +v 35.847198 16.175299 30.563099 +v 0.114600 18.762600 45.177898 +v 31.018600 19.375198 48.141800 +v 30.984100 19.020699 47.911701 +v 19.741400 22.531099 44.147499 +v 20.687401 22.933199 46.574799 +v 20.740200 22.661200 46.858398 +vt 0.799400 0.667000 +vt 0.792700 0.666500 +vt 0.799300 0.664500 +vt 0.802500 0.665900 +vt 0.799400 0.667000 +vt 0.799300 0.664500 +vt 0.792700 0.666500 +vt 0.802500 0.665900 +vt 0.739300 0.679700 +vt 0.738400 0.677600 +vt 0.732400 0.680900 +vt 0.731400 0.683900 +vt 0.743700 0.675000 +vt 0.746700 0.676700 +vt 0.739300 0.679700 +vt 0.738400 0.677600 +vt 0.740100 0.682000 +vt 0.732200 0.686100 +vt 0.735400 0.687500 +vt 0.740700 0.684100 +vt 0.747600 0.678900 +vt 0.745800 0.682100 +vt 0.963100 0.792400 +vt 0.996500 0.796600 +vt 0.994800 0.832200 +vt 0.994800 0.832200 +vt 0.960200 0.826400 +vt 0.963100 0.792400 +vt 0.951300 0.866800 +vt 0.983500 0.875600 +vt 0.960600 0.918300 +vt 0.960600 0.918300 +vt 0.934600 0.904100 +vt 0.951300 0.866800 +vt 0.961200 0.755400 +vt 0.990200 0.753500 +vt 0.996500 0.796600 +vt 0.996500 0.796600 +vt 0.963100 0.792400 +vt 0.961200 0.755400 +vt 0.960200 0.826400 +vt 0.994800 0.832200 +vt 0.983500 0.875600 +vt 0.983500 0.875600 +vt 0.951300 0.866800 +vt 0.960200 0.826400 +vt 0.963100 0.792400 +vt 0.960200 0.826400 +vt 0.994800 0.832200 +vt 0.994800 0.832200 +vt 0.996500 0.796600 +vt 0.963100 0.792400 +vt 0.951300 0.866800 +vt 0.934600 0.904100 +vt 0.960600 0.918300 +vt 0.960600 0.918300 +vt 0.983500 0.875600 +vt 0.951300 0.866800 +vt 0.961200 0.755400 +vt 0.963100 0.792400 +vt 0.996500 0.796600 +vt 0.996500 0.796600 +vt 0.990200 0.753500 +vt 0.961200 0.755400 +vt 0.960200 0.826400 +vt 0.951300 0.866800 +vt 0.983500 0.875600 +vt 0.983500 0.875600 +vt 0.994800 0.832200 +vt 0.960200 0.826400 +vt 0.746700 0.676700 +vt 0.739300 0.679700 +vt 0.740100 0.682000 +vt 0.747600 0.678900 +vt 0.747400 0.695900 +vt 0.755200 0.704900 +vt 0.759000 0.701500 +vt 0.752000 0.693300 +vt 0.766000 0.711100 +vt 0.767100 0.706500 +vt 0.776200 0.713900 +vt 0.776600 0.709400 +vt 0.787800 0.711400 +vt 0.786900 0.707300 +vt 0.796400 0.704100 +vt 0.793800 0.701600 +vt 0.800900 0.694700 +vt 0.797900 0.693000 +vt 0.801400 0.686200 +vt 0.798700 0.684800 +vt 0.801000 0.677300 +vt 0.798500 0.676600 +vt 0.799900 0.669200 +vt 0.797600 0.669500 +vt 0.799400 0.667000 +vt 0.792700 0.666500 +vt 0.745800 0.682100 +vt 0.740700 0.684100 +vt 0.681800 0.289200 +vt 0.695500 0.290300 +vt 0.694400 0.324500 +vt 0.680300 0.321700 +vt 0.675100 0.321600 +vt 0.667700 0.323400 +vt 0.666500 0.313500 +vt 0.674900 0.313300 +vt 0.675300 0.299600 +vt 0.675100 0.308000 +vt 0.666500 0.307600 +vt 0.666700 0.299300 +vt 0.667900 0.287500 +vt 0.675800 0.289300 +vt 0.675400 0.294300 +vt 0.667000 0.293000 +vt 0.677200 0.297400 +vt 0.677200 0.311100 +vt 0.732400 0.680800 +vt 0.738400 0.677600 +vt 0.739300 0.679700 +vt 0.731400 0.683900 +vt 0.743700 0.675000 +vt 0.738400 0.677600 +vt 0.739300 0.679700 +vt 0.746700 0.676700 +vt 0.696300 0.324000 +vt 0.698300 0.290800 +vt 0.704300 0.291100 +vt 0.700500 0.325300 +vt 0.694400 0.324500 +vt 0.695500 0.290300 +vt 0.656800 0.288300 +vt 0.661800 0.288000 +vt 0.663200 0.322300 +vt 0.660100 0.324300 +vt 0.698700 0.329900 +vt 0.664300 0.329600 +vt 0.660500 0.282200 +vt 0.699800 0.284000 +vt 0.680600 0.330000 +vt 0.667700 0.323400 +vt 0.680300 0.325000 +vt 0.667900 0.287500 +vt 0.683600 0.280700 +vt 0.682300 0.286200 +vt 0.665400 0.296100 +vt 0.667000 0.293000 +vt 0.732100 0.686100 +vt 0.731400 0.683900 +vt 0.739300 0.679700 +vt 0.740100 0.682000 +vt 0.742100 0.699500 +vt 0.747400 0.695900 +vt 0.755200 0.704900 +vt 0.750300 0.708600 +vt 0.766000 0.711100 +vt 0.763900 0.715900 +vt 0.776300 0.713900 +vt 0.775700 0.718200 +vt 0.787800 0.711400 +vt 0.788700 0.714900 +vt 0.796400 0.704100 +vt 0.799300 0.706300 +vt 0.800900 0.694700 +vt 0.804500 0.694400 +vt 0.801400 0.686200 +vt 0.804900 0.686300 +vt 0.801000 0.677300 +vt 0.804100 0.677900 +vt 0.799900 0.669300 +vt 0.802200 0.668200 +vt 0.799400 0.667000 +vt 0.802500 0.665900 +vt 0.665300 0.310500 +vt 0.666500 0.313500 +vt 0.666700 0.299300 +vt 0.666500 0.307600 +vt 0.735400 0.687500 +vt 0.740700 0.684100 +vt 0.747600 0.678900 +vt 0.745800 0.682100 +vt 0.218900 0.927600 +vt 0.245700 0.894100 +vt 0.242100 0.922700 +vt 0.244000 0.957000 +vt 0.279600 0.934000 +vt 0.288500 0.959400 +vt 0.292300 0.988300 +vt 0.249400 0.992800 +vt 0.266100 0.854600 +vt 0.278000 0.897000 +vt 0.263800 0.913400 +vt 0.297600 0.921900 +vt 0.310600 0.950900 +vt 0.318300 0.984400 +vt 0.324800 0.849900 +vt 0.297800 0.874700 +vt 0.292800 0.815200 +vt 0.351800 0.881900 +vt 0.322700 0.904900 +vt 0.365700 0.918600 +vt 0.341000 0.938300 +vt 0.387500 0.957500 +vt 0.353200 0.977300 +vt 0.218100 0.872800 +vt 0.234000 0.828900 +vt 0.256300 0.779700 +vt 0.189300 0.929000 +vt 0.191700 0.867300 +vt 0.199600 0.819100 +vt 0.210900 0.768000 +vt 0.789800 0.517200 +vt 0.789200 0.548200 +vt 0.715000 0.510800 +vt 0.815500 0.579400 +vt 0.808200 0.626800 +vt 0.770700 0.637400 +vt 0.856300 0.591100 +vt 0.827500 0.653700 +vt 0.766800 0.592800 +vt 0.722800 0.589900 +vt 0.738800 0.542400 +vt 0.756200 0.482300 +vt 0.763800 0.444400 +vt 0.096200 0.867300 +vt 0.081700 0.860700 +vt 0.084500 0.843900 +vt 0.110600 0.854800 +vt 0.824600 0.564700 +vt 0.859500 0.572400 +vt 0.808500 0.544400 +vt 0.810100 0.521900 +vt 0.803200 0.494800 +vt 0.820600 0.500900 +vt 0.061500 0.845900 +vt 0.066100 0.860800 +vt 0.129800 0.878500 +vt 0.108800 0.880900 +vt 0.130200 0.909700 +vt 0.109200 0.899300 +vt 0.888800 0.668500 +vt 0.859300 0.638700 +vt 0.894900 0.584300 +vt 0.952100 0.648300 +vt 0.906300 0.632900 +vt 0.917000 0.558100 +vt 0.997400 0.593700 +vt 0.948700 0.596200 +vt 0.963200 0.550300 +vt 0.985500 0.518000 +vt 0.884800 0.566500 +vt 0.896800 0.549100 +vt 0.902400 0.519000 +vt 0.920900 0.523900 +vt 0.053000 0.917500 +vt 0.058000 0.896800 +vt 0.073500 0.906400 +vt 0.073300 0.926900 +vt 0.093100 0.908700 +vt 0.104600 0.923100 +vt 0.329500 0.798700 +vt 0.323900 0.723300 +vt 0.350100 0.768000 +vt 0.276900 0.711600 +vt 0.255000 0.731500 +vt 0.285900 0.747400 +vt 0.278500 0.779300 +vt 0.316100 0.772400 +vt 0.378500 0.766800 +vt 0.367600 0.740700 +vt 0.402000 0.718500 +vt 0.309600 0.656300 +vt 0.296100 0.684100 +vt 0.268500 0.686500 +vt 0.368800 0.665500 +vt 0.333900 0.674900 +vt 0.369100 0.703400 +vt 0.092300 0.814000 +vt 0.132300 0.829400 +vt 0.050600 0.824000 +vt 0.162700 0.862800 +vt 0.159600 0.925400 +vt 0.073400 0.945900 +vt 0.038100 0.937300 +vt 0.119000 0.935800 +vt 0.132300 0.948800 +vt 0.171200 0.964800 +vt 0.131700 0.973400 +vt 0.139200 0.762500 +vt 0.177500 0.764500 +vt 0.170500 0.818400 +vt 0.562900 0.423400 +vt 0.601900 0.411200 +vt 0.618300 0.468700 +vt 0.569700 0.499000 +vt 0.009200 0.762900 +vt 0.044300 0.763100 +vt 0.036900 0.800100 +vt 0.003500 0.784300 +vt 0.011100 0.992600 +vt 0.018200 0.956200 +vt 0.049500 0.987500 +vt 0.092900 0.762600 +vt 0.605400 0.563800 +vt 0.647500 0.556100 +vt 0.609300 0.586800 +vt 0.596000 0.534700 +vt 0.630300 0.522300 +vt 0.584000 0.516200 +vt 0.658400 0.591000 +vt 0.617200 0.609300 +vt 0.690500 0.678400 +vt 0.642300 0.707000 +vt 0.635200 0.669400 +vt 0.680300 0.649400 +vt 0.078600 0.961100 +vt 0.221800 0.958500 +vt 0.190700 0.961100 +vt 0.708700 0.758900 +vt 0.655300 0.775200 +vt 0.701500 0.725300 +vt 0.500300 0.460600 +vt 0.475600 0.441200 +vt 0.538700 0.403000 +vt 0.481300 0.480900 +vt 0.452800 0.463100 +vt 0.517200 0.466600 +vt 0.537300 0.495600 +vt 0.573500 0.607400 +vt 0.590200 0.679000 +vt 0.513700 0.677600 +vt 0.511000 0.600400 +vt 0.509000 0.492000 +vt 0.518300 0.864900 +vt 0.551400 0.865300 +vt 0.553400 0.876600 +vt 0.517500 0.872600 +vt 0.574000 0.840200 +vt 0.585200 0.843200 +vt 0.580700 0.810800 +vt 0.585300 0.811500 +vt 0.414700 0.536300 +vt 0.451900 0.539800 +vt 0.452000 0.563200 +vt 0.416000 0.560600 +vt 0.572000 0.557600 +vt 0.570900 0.575500 +vt 0.540200 0.550500 +vt 0.544600 0.569500 +vt 0.429700 0.497600 +vt 0.465900 0.507200 +vt 0.564200 0.539500 +vt 0.537100 0.535300 +vt 0.550100 0.522100 +vt 0.534000 0.521400 +vt 0.507600 0.520500 +vt 0.454600 0.593000 +vt 0.414100 0.588900 +vt 0.588900 0.717900 +vt 0.521300 0.716800 +vt 0.450400 0.661800 +vt 0.406400 0.653600 +vt 0.578700 0.767600 +vt 0.400300 0.754400 +vt 0.403200 0.691500 +vt 0.452400 0.699200 +vt 0.455900 0.758300 +vt 0.522700 0.764400 +vt 0.508100 0.565600 +vt 0.504800 0.545100 +vt 0.573700 0.809400 +vt 0.675400 0.587900 +vt 0.683000 0.575600 +vt 0.648700 0.521100 +vt 0.671500 0.534300 +vt 0.683600 0.555200 +vt 0.545500 0.222500 +vt 0.551700 0.237600 +vt 0.558400 0.225800 +vt 0.534600 0.225800 +vt 0.553900 0.302800 +vt 0.558500 0.293700 +vt 0.551400 0.283900 +vt 0.539100 0.289800 +vt 0.524400 0.266100 +vt 0.541600 0.255900 +vt 0.525400 0.242000 +vt 0.085000 0.984900 +vt 0.415000 0.037400 +vt 0.397100 0.037500 +vt 0.386900 0.029600 +vt 0.393400 0.011200 +vt 0.365400 0.004300 +vt 0.361200 0.026300 +vt 0.392400 0.092300 +vt 0.370500 0.090600 +vt 0.396200 0.117900 +vt 0.387400 0.121700 +vt 0.384200 0.097900 +vt 0.412400 0.090700 +vt 0.373900 0.125200 +vt 0.369700 0.100800 +vt 0.386800 0.127600 +vt 0.379900 0.133900 +vt 0.412900 0.122600 +vt 0.402300 0.139000 +vt 0.394600 0.147300 +vt 0.363000 0.798200 +vt 0.353200 0.777800 +vt 0.381300 0.781800 +vt 0.446000 0.811900 +vt 0.455300 0.821700 +vt 0.437200 0.830500 +vt 0.443300 0.796100 +vt 0.421400 0.819200 +vt 0.455300 0.774800 +vt 0.467500 0.819700 +vt 0.453900 0.845200 +vt 0.498000 0.837600 +vt 0.471000 0.865800 +vt 0.498900 0.797100 +vt 0.536500 0.841700 +vt 0.514200 0.860100 +vt 0.488000 0.880700 +vt 0.201700 0.304600 +vt 0.261000 0.320500 +vt 0.250900 0.358200 +vt 0.192500 0.344700 +vt 0.193200 0.375200 +vt 0.248400 0.400900 +vt 0.195800 0.406800 +vt 0.201500 0.445700 +vt 0.173300 0.434800 +vt 0.169800 0.400600 +vt 0.167000 0.371000 +vt 0.166900 0.337200 +vt 0.173800 0.296700 +vt 0.140300 0.329600 +vt 0.098600 0.324100 +vt 0.106400 0.278600 +vt 0.145900 0.289100 +vt 0.140300 0.370100 +vt 0.093800 0.371500 +vt 0.143900 0.401800 +vt 0.102000 0.412800 +vt 0.151400 0.437000 +vt 0.120200 0.459700 +vt 0.052500 0.320200 +vt 0.064000 0.267500 +vt 0.051800 0.376600 +vt 0.061500 0.421700 +vt 0.090900 0.490000 +vt 0.070000 0.460100 +vt 0.007000 0.316700 +vt 0.018700 0.255300 +vt 0.010700 0.388100 +vt 0.022700 0.434000 +vt 0.063900 0.513200 +vt 0.039900 0.477200 +vt 0.213600 0.264900 +vt 0.271000 0.282300 +vt 0.229100 0.239400 +vt 0.246100 0.214700 +vt 0.289700 0.244000 +vt 0.322600 0.159900 +vt 0.308500 0.140700 +vt 0.347100 0.124000 +vt 0.357000 0.142200 +vt 0.222500 0.204200 +vt 0.246900 0.179500 +vt 0.271700 0.194400 +vt 0.204900 0.228700 +vt 0.319300 0.228500 +vt 0.294200 0.179700 +vt 0.357400 0.206400 +vt 0.386300 0.191900 +vt 0.188000 0.258100 +vt 0.277400 0.158900 +vt 0.161600 0.251000 +vt 0.122900 0.235300 +vt 0.182500 0.215300 +vt 0.143100 0.191600 +vt 0.203300 0.188200 +vt 0.171600 0.159000 +vt 0.232100 0.159800 +vt 0.209900 0.123600 +vt 0.267200 0.135000 +vt 0.248700 0.100200 +vt 0.302700 0.116200 +vt 0.290600 0.081400 +vt 0.340300 0.103800 +vt 0.337900 0.073700 +vt 0.080800 0.216200 +vt 0.108600 0.167500 +vt 0.138400 0.133200 +vt 0.162600 0.103100 +vt 0.192200 0.082900 +vt 0.233400 0.061200 +vt 0.280300 0.047600 +vt 0.338100 0.040800 +vt 0.039600 0.196800 +vt 0.078000 0.137800 +vt 0.109200 0.105000 +vt 0.140900 0.076000 +vt 0.172000 0.052800 +vt 0.215800 0.031000 +vt 0.270000 0.013000 +vt 0.338400 0.004100 +vt 0.960600 0.918300 +vt 0.896000 0.950500 +vt 0.934600 0.904100 +vt 0.963100 0.792400 +vt 0.960200 0.826400 +vt 0.929100 0.806800 +vt 0.951300 0.866800 +vt 0.889100 0.878600 +vt 0.920100 0.846600 +vt 0.951600 0.682000 +vt 0.961200 0.755400 +vt 0.911900 0.729100 +vt 0.914500 0.686600 +vt 0.856300 0.952300 +vt 0.878300 0.913800 +vt 0.909100 0.763500 +vt 0.990200 0.753500 +vt 0.829900 0.775600 +vt 0.808200 0.782300 +vt 0.802800 0.751800 +vt 0.823900 0.741700 +vt 0.819100 0.842300 +vt 0.799800 0.832500 +vt 0.798900 0.807500 +vt 0.826300 0.806900 +vt 0.804700 0.873600 +vt 0.790600 0.857400 +vt 0.788100 0.734100 +vt 0.809200 0.722700 +vt 0.784900 0.784100 +vt 0.797700 0.780700 +vt 0.779100 0.849300 +vt 0.774700 0.872600 +vt 0.906400 0.800700 +vt 0.896900 0.840700 +vt 0.880400 0.836300 +vt 0.890300 0.797500 +vt 0.840700 0.945500 +vt 0.866300 0.909300 +vt 0.893800 0.758700 +vt 0.876700 0.875500 +vt 0.894500 0.724300 +vt 0.892800 0.677200 +vt 0.860800 0.832800 +vt 0.870200 0.793300 +vt 0.820500 0.930500 +vt 0.851000 0.901300 +vt 0.803800 0.991300 +vt 0.777100 0.966300 +vt 0.872800 0.757200 +vt 0.859300 0.869300 +vt 0.873000 0.724100 +vt 0.858200 0.679400 +vt 0.781300 0.759000 +vt 0.793500 0.756100 +vt 0.786500 0.828800 +vt 0.785200 0.895000 +vt 0.759400 0.866100 +vt 0.755800 0.839300 +vt 0.761600 0.822800 +vt 0.775300 0.804200 +vt 0.903300 0.078500 +vt 0.890900 0.070800 +vt 0.896000 0.062500 +vt 0.909700 0.066700 +vt 0.372800 0.306500 +vt 0.351200 0.305000 +vt 0.352500 0.290500 +vt 0.378400 0.291400 +vt 0.330800 0.301900 +vt 0.322700 0.292200 +vt 0.314900 0.314800 +vt 0.307000 0.302500 +vt 0.933400 0.098200 +vt 0.921300 0.083800 +vt 0.931700 0.071200 +vt 0.947900 0.091400 +vt 0.904500 0.044700 +vt 0.917900 0.045500 +vt 0.358800 0.263300 +vt 0.380200 0.264100 +vt 0.334500 0.261400 +vt 0.343100 0.261400 +vt 0.984300 0.104600 +vt 0.979900 0.109300 +vt 0.981100 0.099000 +vt 0.964400 0.067300 +vt 0.951900 0.041200 +vt 0.307300 0.289400 +vt 0.313400 0.281400 +vt 0.969500 0.082000 +vt 0.961400 0.095300 +vt 0.979900 0.070000 +vt 0.981500 0.081600 +vt 0.323200 0.261100 +vt 0.317300 0.269900 +vt 0.903400 0.022300 +vt 0.915100 0.011300 +vt 0.908300 0.024600 +vt 0.989600 0.034600 +vt 0.995100 0.038400 +vt 0.985900 0.039500 +vt 0.334400 0.246500 +vt 0.342200 0.242500 +vt 0.976200 0.054100 +vt 0.981500 0.050900 +vt 0.985500 0.054900 +vt 0.970800 0.042200 +vt 0.977000 0.041000 +vt 0.965200 0.031700 +vt 0.977400 0.035300 +vt 0.334500 0.240500 +vt 0.334600 0.223600 +vt 0.340700 0.226700 +vt 0.988000 0.044900 +vt 0.994100 0.045100 +vt 0.905200 0.033600 +vt 0.914400 0.037100 +vt 0.360400 0.250600 +vt 0.381400 0.250800 +vt 0.947500 0.024900 +vt 0.938600 0.028800 +vt 0.936000 0.018500 +vt 0.910600 0.032200 +vt 0.362000 0.242900 +vt 0.365500 0.226900 +vt 0.382100 0.235800 +vt 0.932800 0.023000 +vt 0.925300 0.016600 +vt 0.925800 0.010800 +vt 0.303700 0.283000 +vt 0.292000 0.263200 +vt 0.300600 0.262400 +vt 0.971700 0.088900 +vt 0.975600 0.100700 +vt 0.974300 0.108600 +vt 0.979800 0.087400 +vt 0.987100 0.088100 +vt 0.287200 0.270200 +vt 0.296500 0.291100 +vt 0.325900 0.242600 +vt 0.328100 0.226200 +vt 0.347700 0.242000 +vt 0.352900 0.225700 +vt 0.854000 0.792700 +vt 0.849600 0.830000 +vt 0.839300 0.893500 +vt 0.812400 0.919300 +vt 0.769100 0.940400 +vt 0.857200 0.759200 +vt 0.845800 0.864200 +vt 0.838000 0.694500 +vt 0.857400 0.729100 +vt 0.740200 0.784600 +vt 0.739100 0.769400 +vt 0.770400 0.740400 +vt 0.735900 0.815200 +vt 0.739400 0.800400 +vt 0.725900 0.844000 +vt 0.731900 0.830000 +vt 0.733900 0.755500 +vt 0.866100 0.997500 +vt 0.827200 0.995600 +vt 0.953900 0.291700 +vt 0.974800 0.288600 +vt 0.984900 0.333000 +vt 0.949200 0.345400 +vt 0.916500 0.245400 +vt 0.925400 0.300000 +vt 0.896800 0.286600 +vt 0.897000 0.247800 +vt 0.911500 0.325900 +vt 0.956100 0.385400 +vt 0.927400 0.383100 +vt 0.922400 0.352200 +vt 0.954200 0.414500 +vt 0.923800 0.416200 +vt 0.949900 0.445700 +vt 0.922800 0.449700 +vt 0.946300 0.499000 +vt 0.926200 0.499800 +vt 0.861000 0.334400 +vt 0.849700 0.305000 +vt 0.869500 0.382800 +vt 0.866600 0.357700 +vt 0.869400 0.411800 +vt 0.858600 0.433900 +vt 0.908600 0.454900 +vt 0.885900 0.484800 +vt 0.838900 0.451600 +vt 0.840500 0.506100 +vt 0.817300 0.469200 +vt 0.878200 0.261400 +vt 0.836100 0.284000 +vt 0.830400 0.311700 +vt 0.838200 0.336400 +vt 0.814300 0.340700 +vt 0.806900 0.319100 +vt 0.824600 0.358400 +vt 0.824300 0.378000 +vt 0.819200 0.398600 +vt 0.805600 0.393500 +vt 0.827100 0.425600 +vt 0.810900 0.418500 +vt 0.835700 0.402500 +vt 0.801700 0.434400 +vt 0.804800 0.299600 +vt 0.767700 0.349100 +vt 0.761900 0.330600 +vt 0.778200 0.368100 +vt 0.781900 0.375600 +vt 0.778000 0.386800 +vt 0.796700 0.413500 +vt 0.770900 0.402300 +vt 0.765500 0.411800 +vt 0.791200 0.448600 +vt 0.756800 0.417900 +vt 0.759300 0.309800 +vt 0.950900 0.241600 +vt 0.974600 0.240800 +vt 0.992900 0.379300 +vt 0.987100 0.497500 +vt 0.989400 0.449700 +vt 0.993200 0.411500 +vt 0.876000 0.247700 +vt 0.894600 0.502700 +vt 0.845100 0.513100 +vt 0.493700 0.213400 +vt 0.498200 0.230600 +vt 0.489300 0.230100 +vt 0.489100 0.215000 +vt 0.495000 0.197200 +vt 0.471100 0.313400 +vt 0.489100 0.312700 +vt 0.497800 0.329600 +vt 0.477600 0.333200 +vt 0.470200 0.291800 +vt 0.488600 0.297800 +vt 0.494600 0.279000 +vt 0.507900 0.271200 +vt 0.501500 0.283000 +vt 0.495900 0.248800 +vt 0.502600 0.246600 +vt 0.507500 0.258500 +vt 0.471300 0.264600 +vt 0.488800 0.264300 +vt 0.470600 0.237300 +vt 0.044800 0.134900 +vt 0.040900 0.152000 +vt 0.011200 0.151100 +vt 0.008600 0.132600 +vt 0.510000 0.263900 +vt 0.033100 0.165800 +vt 0.024300 0.170300 +vt 0.016600 0.165100 +vt 0.045300 0.111100 +vt 0.013500 0.109300 +vt 0.021800 0.095200 +vt 0.037400 0.096600 +vt 0.501600 0.327900 +vt 0.510000 0.341000 +vt 0.507700 0.342500 +vt 0.492800 0.313100 +vt 0.497200 0.312800 +vt 0.045800 0.115300 +vt 0.011300 0.113300 +vt 0.488600 0.352900 +vt 0.496200 0.196700 +vt 0.499500 0.211900 +vt 0.470400 0.216300 +vt 0.430600 0.207100 +vt 0.438000 0.174800 +vt 0.474400 0.194800 +vt 0.429100 0.266600 +vt 0.430100 0.236900 +vt 0.437600 0.351500 +vt 0.453500 0.376300 +vt 0.414900 0.405800 +vt 0.394700 0.374900 +vt 0.429300 0.327300 +vt 0.385200 0.340000 +vt 0.427500 0.295300 +vt 0.382500 0.297200 +vt 0.385400 0.266500 +vt 0.389300 0.198000 +vt 0.385000 0.236700 +vt 0.496800 0.296900 +vt 0.392100 0.160800 +vt 0.544200 0.901500 +vt 0.510100 0.891800 +vt 0.659200 0.785800 +vt 0.668900 0.823900 +vt 0.638900 0.834400 +vt 0.629200 0.795400 +vt 0.579200 0.878400 +vt 0.567800 0.894000 +vt 0.610100 0.905300 +vt 0.592400 0.920900 +vt 0.638900 0.862500 +vt 0.675100 0.858800 +vt 0.668900 0.881300 +vt 0.606300 0.945900 +vt 0.584400 0.964200 +vt 0.564200 0.947700 +vt 0.563200 0.995400 +vt 0.524900 0.988800 +vt 0.536500 0.939700 +vt 0.481600 0.974400 +vt 0.496800 0.928700 +vt 0.633100 0.888400 +vt 0.600300 0.861300 +vt 0.606600 0.841500 +vt 0.601800 0.806200 +vt 0.655300 0.901700 +vt 0.626400 0.925800 +vt 0.569300 0.911200 +vt 0.608900 0.948500 +vt 0.586700 0.967900 +vt 0.672900 0.884900 +vt 0.658300 0.905600 +vt 0.629200 0.929200 +vt 0.667700 0.783400 +vt 0.677300 0.825200 +vt 0.680400 0.860300 +vt 0.673500 0.918500 +vt 0.686200 0.931800 +vt 0.655000 0.959200 +vt 0.641800 0.944000 +vt 0.632600 0.980200 +vt 0.620900 0.967100 +vt 0.707300 0.901100 +vt 0.601000 0.992200 +vt 0.705700 0.772800 +vt 0.714700 0.818900 +vt 0.717700 0.858100 +vt 0.066100 0.047400 +vt 0.067400 0.064100 +vt 0.061800 0.067200 +vt 0.053900 0.047700 +vt 0.053800 0.065000 +vt 0.080600 0.062000 +vt 0.078300 0.047300 +vt 0.112900 0.070300 +vt 0.014900 0.049600 +vt 0.011300 0.065700 +vt 0.075200 0.066600 +vt 0.114500 0.039800 +vt 0.114700 0.016500 +vt 0.062200 0.028200 +vt 0.070500 0.022600 +vt 0.078700 0.027200 +vt 0.048500 0.030600 +vt 0.054200 0.025600 +vt 0.011900 0.025500 +vt 0.119300 0.064500 +vt 0.015100 0.075000 +vt 0.107100 0.011900 +vt 0.022300 0.021600 +vt 0.024700 0.077900 +vt 0.102800 0.071000 +vt 0.392900 0.655700 +vt 0.368500 0.652000 +vt 0.383900 0.633700 +vt 0.400000 0.635800 +vt 0.368300 0.619000 +vt 0.386100 0.618300 +vt 0.367100 0.595100 +vt 0.384800 0.607100 +vt 0.403400 0.615200 +vt 0.219000 0.927400 +vt 0.242400 0.922600 +vt 0.245900 0.893800 +vt 0.244200 0.957000 +vt 0.288900 0.959400 +vt 0.280000 0.933900 +vt 0.292700 0.988400 +vt 0.249700 0.992900 +vt 0.264100 0.913200 +vt 0.278400 0.896800 +vt 0.266400 0.854200 +vt 0.298100 0.921800 +vt 0.311100 0.950900 +vt 0.318900 0.984500 +vt 0.324800 0.849900 +vt 0.292800 0.815200 +vt 0.298200 0.874600 +vt 0.351800 0.881900 +vt 0.323100 0.904900 +vt 0.365700 0.918600 +vt 0.340300 0.939000 +vt 0.387500 0.957500 +vt 0.353400 0.976400 +vt 0.218200 0.872400 +vt 0.234200 0.828400 +vt 0.191700 0.866900 +vt 0.189300 0.928900 +vt 0.199700 0.818500 +vt 0.789800 0.517200 +vt 0.715000 0.510800 +vt 0.789200 0.548200 +vt 0.815500 0.579400 +vt 0.770700 0.637400 +vt 0.808200 0.626800 +vt 0.827500 0.653700 +vt 0.856300 0.591100 +vt 0.722800 0.589900 +vt 0.766800 0.592800 +vt 0.738800 0.542400 +vt 0.756200 0.482300 +vt 0.763800 0.444400 +vt 0.095800 0.866900 +vt 0.110300 0.854300 +vt 0.084000 0.843400 +vt 0.081200 0.860300 +vt 0.824600 0.564700 +vt 0.859500 0.572400 +vt 0.808500 0.544400 +vt 0.810100 0.521900 +vt 0.820600 0.500900 +vt 0.803200 0.494800 +vt 0.060900 0.845400 +vt 0.065500 0.860400 +vt 0.108500 0.880600 +vt 0.129500 0.878200 +vt 0.108800 0.899100 +vt 0.129900 0.909500 +vt 0.859300 0.638700 +vt 0.888800 0.668500 +vt 0.894900 0.584300 +vt 0.906300 0.632900 +vt 0.952100 0.648300 +vt 0.917000 0.558100 +vt 0.948700 0.596200 +vt 0.997400 0.593700 +vt 0.963200 0.550300 +vt 0.985500 0.518000 +vt 0.884800 0.566500 +vt 0.896800 0.549100 +vt 0.920900 0.523900 +vt 0.902400 0.519000 +vt 0.052400 0.917300 +vt 0.072700 0.926800 +vt 0.073000 0.906200 +vt 0.057400 0.896600 +vt 0.092700 0.908500 +vt 0.104200 0.922900 +vt 0.329500 0.798700 +vt 0.350100 0.768000 +vt 0.323900 0.723300 +vt 0.276900 0.711600 +vt 0.255000 0.731500 +vt 0.285900 0.747400 +vt 0.278500 0.779300 +vt 0.316100 0.772400 +vt 0.378500 0.766800 +vt 0.367600 0.740700 +vt 0.402000 0.718500 +vt 0.309600 0.656300 +vt 0.296100 0.684100 +vt 0.268500 0.686500 +vt 0.368800 0.665500 +vt 0.333900 0.674900 +vt 0.369100 0.703400 +vt 0.132100 0.828900 +vt 0.091800 0.813400 +vt 0.050000 0.823400 +vt 0.162600 0.862400 +vt 0.159500 0.925300 +vt 0.037400 0.937200 +vt 0.072800 0.945900 +vt 0.118700 0.935700 +vt 0.132100 0.948800 +vt 0.131400 0.973500 +vt 0.171100 0.964900 +vt 0.170400 0.817800 +vt 0.562900 0.423400 +vt 0.570400 0.498400 +vt 0.618300 0.468700 +vt 0.601900 0.411200 +vt 0.003500 0.784300 +vt 0.036200 0.799400 +vt 0.010300 0.992800 +vt 0.048900 0.987700 +vt 0.017500 0.956200 +vt 0.606300 0.563600 +vt 0.610100 0.586600 +vt 0.648500 0.555900 +vt 0.630300 0.522300 +vt 0.596900 0.534300 +vt 0.584800 0.515700 +vt 0.658400 0.591000 +vt 0.618100 0.609300 +vt 0.690500 0.678400 +vt 0.680300 0.649400 +vt 0.636200 0.669600 +vt 0.643300 0.707300 +vt 0.078100 0.961100 +vt 0.190600 0.961100 +vt 0.221900 0.958500 +vt 0.708700 0.758900 +vt 0.701500 0.725300 +vt 0.655300 0.775200 +vt 0.500700 0.459900 +vt 0.481600 0.480300 +vt 0.517700 0.465900 +vt 0.537800 0.495000 +vt 0.574300 0.607300 +vt 0.511500 0.600300 +vt 0.514200 0.677900 +vt 0.591000 0.679200 +vt 0.509400 0.491400 +vt 0.553400 0.876600 +vt 0.551400 0.865300 +vt 0.585200 0.843200 +vt 0.574000 0.840200 +vt 0.452200 0.562900 +vt 0.452100 0.539400 +vt 0.572600 0.557100 +vt 0.571600 0.575300 +vt 0.540800 0.550100 +vt 0.545100 0.569300 +vt 0.466100 0.506700 +vt 0.564900 0.539200 +vt 0.537700 0.534900 +vt 0.550700 0.521700 +vt 0.534500 0.521000 +vt 0.508000 0.520000 +vt 0.454800 0.592800 +vt 0.521800 0.717300 +vt 0.589700 0.718300 +vt 0.406400 0.653800 +vt 0.450500 0.662000 +vt 0.578700 0.767600 +vt 0.456200 0.758800 +vt 0.452600 0.699500 +vt 0.523200 0.765000 +vt 0.508600 0.565300 +vt 0.505200 0.544700 +vt 0.675400 0.587900 +vt 0.683000 0.575600 +vt 0.648700 0.521100 +vt 0.671500 0.534300 +vt 0.683600 0.555200 +vt 0.545500 0.222500 +vt 0.558400 0.225800 +vt 0.551700 0.237600 +vt 0.534600 0.225800 +vt 0.553900 0.302800 +vt 0.539100 0.289800 +vt 0.551400 0.283900 +vt 0.558500 0.293700 +vt 0.524400 0.266100 +vt 0.525400 0.242000 +vt 0.541600 0.255900 +vt 0.084500 0.985000 +vt 0.415000 0.037400 +vt 0.393400 0.011200 +vt 0.386900 0.029600 +vt 0.397100 0.037500 +vt 0.392400 0.092300 +vt 0.396200 0.117900 +vt 0.384200 0.097900 +vt 0.387400 0.121700 +vt 0.412400 0.090700 +vt 0.386800 0.127600 +vt 0.412900 0.122600 +vt 0.402300 0.139000 +vt 0.381300 0.781800 +vt 0.446000 0.811900 +vt 0.437200 0.830500 +vt 0.455300 0.821700 +vt 0.443300 0.796100 +vt 0.421400 0.819200 +vt 0.455300 0.774800 +vt 0.453900 0.845200 +vt 0.467500 0.819700 +vt 0.498000 0.837600 +vt 0.471000 0.865800 +vt 0.498900 0.797100 +vt 0.536500 0.841700 +vt 0.488000 0.880700 +vt 0.514200 0.860100 +vt 0.322500 0.159700 +vt 0.357000 0.141900 +vt 0.347100 0.123700 +vt 0.308400 0.140400 +vt 0.319200 0.228400 +vt 0.357300 0.206300 +vt 0.294100 0.179500 +vt 0.386300 0.191700 +vt 0.277300 0.158700 +vt 0.271500 0.194200 +vt 0.246700 0.179300 +vt 0.267100 0.134800 +vt 0.248500 0.100000 +vt 0.209700 0.123400 +vt 0.231900 0.159700 +vt 0.302600 0.116000 +vt 0.290500 0.081100 +vt 0.340200 0.103600 +vt 0.337800 0.073400 +vt 0.233200 0.060800 +vt 0.192000 0.082600 +vt 0.280200 0.047300 +vt 0.338000 0.040400 +vt 0.215600 0.030700 +vt 0.171700 0.052500 +vt 0.269800 0.012600 +vt 0.338300 0.003700 +vt 0.960600 0.918300 +vt 0.934600 0.904100 +vt 0.896000 0.950500 +vt 0.963100 0.792400 +vt 0.929100 0.806800 +vt 0.960200 0.826400 +vt 0.951300 0.866800 +vt 0.920100 0.846600 +vt 0.889100 0.878600 +vt 0.951600 0.682000 +vt 0.914500 0.686600 +vt 0.911900 0.729100 +vt 0.961200 0.755400 +vt 0.878300 0.913800 +vt 0.856300 0.952300 +vt 0.909100 0.763500 +vt 0.990200 0.753500 +vt 0.829900 0.775600 +vt 0.823900 0.741700 +vt 0.802800 0.751800 +vt 0.808200 0.782300 +vt 0.819100 0.842300 +vt 0.826300 0.806900 +vt 0.798900 0.807500 +vt 0.799800 0.832500 +vt 0.804700 0.873600 +vt 0.790600 0.857400 +vt 0.809200 0.722700 +vt 0.788100 0.734100 +vt 0.784900 0.784100 +vt 0.797600 0.780700 +vt 0.779100 0.849300 +vt 0.774700 0.872600 +vt 0.906400 0.800700 +vt 0.890300 0.797500 +vt 0.880400 0.836300 +vt 0.896900 0.840700 +vt 0.866300 0.909300 +vt 0.840700 0.945500 +vt 0.893800 0.758700 +vt 0.876700 0.875500 +vt 0.892800 0.677200 +vt 0.894500 0.724300 +vt 0.870200 0.793300 +vt 0.860800 0.832800 +vt 0.851000 0.901300 +vt 0.820500 0.930500 +vt 0.777100 0.966300 +vt 0.803800 0.991300 +vt 0.872800 0.757200 +vt 0.859300 0.869300 +vt 0.858200 0.679400 +vt 0.873000 0.724100 +vt 0.793500 0.756100 +vt 0.781300 0.759000 +vt 0.786500 0.828800 +vt 0.785200 0.895000 +vt 0.759300 0.866100 +vt 0.755800 0.839300 +vt 0.761600 0.822800 +vt 0.775300 0.804200 +vt 0.903300 0.078500 +vt 0.909700 0.066700 +vt 0.896000 0.062500 +vt 0.890900 0.070800 +vt 0.372800 0.306500 +vt 0.378400 0.291400 +vt 0.352500 0.290500 +vt 0.351200 0.305000 +vt 0.322700 0.292200 +vt 0.330800 0.301900 +vt 0.307000 0.302500 +vt 0.314900 0.314800 +vt 0.933400 0.098200 +vt 0.947900 0.091400 +vt 0.931700 0.071200 +vt 0.921300 0.083800 +vt 0.917900 0.045500 +vt 0.904500 0.044700 +vt 0.380200 0.264100 +vt 0.358800 0.263300 +vt 0.343100 0.261400 +vt 0.334500 0.261400 +vt 0.984300 0.104600 +vt 0.981100 0.099000 +vt 0.979900 0.109300 +vt 0.964400 0.067300 +vt 0.951900 0.041200 +vt 0.313400 0.281400 +vt 0.307300 0.289400 +vt 0.969500 0.082000 +vt 0.961400 0.095300 +vt 0.981500 0.081600 +vt 0.979900 0.070000 +vt 0.323200 0.261100 +vt 0.317300 0.269900 +vt 0.903400 0.022300 +vt 0.908300 0.024600 +vt 0.915100 0.011300 +vt 0.989600 0.034600 +vt 0.985900 0.039500 +vt 0.995100 0.038400 +vt 0.342200 0.242500 +vt 0.334400 0.246500 +vt 0.976200 0.054100 +vt 0.985500 0.054900 +vt 0.981500 0.050900 +vt 0.970800 0.042200 +vt 0.977000 0.041000 +vt 0.977400 0.035300 +vt 0.965200 0.031700 +vt 0.334500 0.240500 +vt 0.340700 0.226700 +vt 0.334600 0.223600 +vt 0.994100 0.045100 +vt 0.988000 0.044900 +vt 0.914400 0.037100 +vt 0.905200 0.033600 +vt 0.381400 0.250800 +vt 0.360400 0.250600 +vt 0.947500 0.024900 +vt 0.936000 0.018500 +vt 0.938600 0.028800 +vt 0.910600 0.032200 +vt 0.362000 0.242900 +vt 0.382100 0.235800 +vt 0.365500 0.226900 +vt 0.925800 0.010800 +vt 0.925300 0.016600 +vt 0.932800 0.023000 +vt 0.303700 0.283000 +vt 0.300600 0.262400 +vt 0.292000 0.263200 +vt 0.974300 0.108600 +vt 0.975600 0.100700 +vt 0.971700 0.088900 +vt 0.979800 0.087400 +vt 0.987100 0.088100 +vt 0.287200 0.270200 +vt 0.296500 0.291100 +vt 0.325900 0.242600 +vt 0.328100 0.226200 +vt 0.347700 0.242000 +vt 0.352900 0.225700 +vt 0.854000 0.792700 +vt 0.849600 0.830000 +vt 0.839300 0.893500 +vt 0.812400 0.919300 +vt 0.769000 0.940400 +vt 0.857200 0.759200 +vt 0.845800 0.864200 +vt 0.838000 0.694400 +vt 0.857400 0.729100 +vt 0.739100 0.769400 +vt 0.740200 0.784600 +vt 0.770300 0.740400 +vt 0.739400 0.800400 +vt 0.735900 0.815200 +vt 0.731900 0.830000 +vt 0.725900 0.844000 +vt 0.733900 0.755500 +vt 0.866100 0.997500 +vt 0.827200 0.995600 +vt 0.984900 0.333000 +vt 0.974800 0.288600 +vt 0.953900 0.291700 +vt 0.949200 0.345400 +vt 0.896800 0.286600 +vt 0.925400 0.300000 +vt 0.911500 0.325900 +vt 0.922400 0.352200 +vt 0.927400 0.383100 +vt 0.956100 0.385400 +vt 0.923800 0.416200 +vt 0.954200 0.414500 +vt 0.949900 0.445700 +vt 0.923300 0.451100 +vt 0.926300 0.499800 +vt 0.849700 0.305000 +vt 0.861000 0.334400 +vt 0.866600 0.357700 +vt 0.869500 0.382800 +vt 0.869400 0.411800 +vt 0.858600 0.433900 +vt 0.838900 0.451600 +vt 0.885900 0.484800 +vt 0.908600 0.454900 +vt 0.817300 0.469200 +vt 0.840500 0.506100 +vt 0.878200 0.261400 +vt 0.836100 0.284000 +vt 0.830400 0.311700 +vt 0.806900 0.319100 +vt 0.814300 0.340700 +vt 0.838200 0.336400 +vt 0.824600 0.358400 +vt 0.824300 0.378000 +vt 0.805600 0.393500 +vt 0.819200 0.398600 +vt 0.827100 0.425600 +vt 0.835700 0.402500 +vt 0.810900 0.418500 +vt 0.801700 0.434400 +vt 0.804800 0.299600 +vt 0.761900 0.330600 +vt 0.767700 0.349100 +vt 0.778200 0.368100 +vt 0.781900 0.375600 +vt 0.778000 0.386800 +vt 0.770900 0.402300 +vt 0.796700 0.413500 +vt 0.765500 0.411800 +vt 0.791200 0.448600 +vt 0.756800 0.417900 +vt 0.759300 0.309800 +vt 0.992900 0.379300 +vt 0.989400 0.449700 +vt 0.993200 0.411500 +vt 0.493700 0.213400 +vt 0.489100 0.215000 +vt 0.489300 0.230100 +vt 0.498200 0.230600 +vt 0.495000 0.197200 +vt 0.471100 0.313400 +vt 0.477600 0.333200 +vt 0.497800 0.329600 +vt 0.489100 0.312700 +vt 0.470200 0.291800 +vt 0.488600 0.297800 +vt 0.494600 0.279000 +vt 0.501500 0.283000 +vt 0.507900 0.271200 +vt 0.495900 0.248800 +vt 0.507500 0.258500 +vt 0.502600 0.246600 +vt 0.488800 0.264300 +vt 0.471300 0.264600 +vt 0.470600 0.237300 +vt 0.044800 0.134900 +vt 0.008600 0.132600 +vt 0.011200 0.151100 +vt 0.040900 0.152000 +vt 0.510000 0.263900 +vt 0.016600 0.165100 +vt 0.024300 0.170300 +vt 0.033100 0.165800 +vt 0.045300 0.111100 +vt 0.037400 0.096600 +vt 0.021800 0.095200 +vt 0.013500 0.109300 +vt 0.501600 0.327900 +vt 0.507700 0.342500 +vt 0.510000 0.341000 +vt 0.492800 0.313100 +vt 0.497200 0.312800 +vt 0.045800 0.115300 +vt 0.011300 0.113300 +vt 0.488600 0.352900 +vt 0.496200 0.196700 +vt 0.499500 0.211900 +vt 0.470400 0.216300 +vt 0.474400 0.194800 +vt 0.438000 0.174800 +vt 0.430600 0.207100 +vt 0.430100 0.236900 +vt 0.429100 0.266600 +vt 0.437600 0.351500 +vt 0.394700 0.374900 +vt 0.414900 0.405800 +vt 0.453500 0.376300 +vt 0.429300 0.327300 +vt 0.385200 0.340000 +vt 0.427500 0.295300 +vt 0.382500 0.297200 +vt 0.385400 0.266500 +vt 0.389300 0.198000 +vt 0.385000 0.236700 +vt 0.496800 0.296900 +vt 0.392100 0.160800 +vt 0.544200 0.901500 +vt 0.638900 0.834400 +vt 0.668900 0.823900 +vt 0.567800 0.894000 +vt 0.579200 0.878400 +vt 0.592400 0.920900 +vt 0.610100 0.905300 +vt 0.638900 0.862500 +vt 0.668900 0.881300 +vt 0.675100 0.858800 +vt 0.564200 0.947700 +vt 0.584400 0.964200 +vt 0.606300 0.945900 +vt 0.563200 0.995400 +vt 0.536500 0.939700 +vt 0.524900 0.988800 +vt 0.633100 0.888400 +vt 0.600300 0.861300 +vt 0.606600 0.841500 +vt 0.655300 0.901700 +vt 0.626400 0.925800 +vt 0.569300 0.911200 +vt 0.586700 0.967900 +vt 0.608900 0.948500 +vt 0.658300 0.905600 +vt 0.672900 0.884900 +vt 0.629200 0.929200 +vt 0.677300 0.825200 +vt 0.680400 0.860300 +vt 0.673500 0.918500 +vt 0.641800 0.944000 +vt 0.655000 0.959200 +vt 0.686200 0.931800 +vt 0.620900 0.967100 +vt 0.632600 0.980200 +vt 0.707300 0.901100 +vt 0.601000 0.992200 +vt 0.714700 0.818900 +vt 0.717700 0.858100 +vt 0.053900 0.047700 +vt 0.053800 0.065000 +vt 0.061800 0.067200 +vt 0.067400 0.064100 +vt 0.066100 0.047400 +vt 0.080600 0.062000 +vt 0.112900 0.070300 +vt 0.078300 0.047300 +vt 0.014900 0.049600 +vt 0.011300 0.065700 +vt 0.075200 0.066600 +vt 0.114500 0.039800 +vt 0.114700 0.016500 +vt 0.078700 0.027200 +vt 0.070500 0.022600 +vt 0.062200 0.028200 +vt 0.054200 0.025600 +vt 0.048500 0.030600 +vt 0.011900 0.025500 +vt 0.119300 0.064500 +vt 0.015100 0.075000 +vt 0.107100 0.011900 +vt 0.022300 0.021600 +vt 0.024700 0.077900 +vt 0.102800 0.071000 +vt 0.392900 0.655700 +vt 0.400000 0.635800 +vt 0.383900 0.633700 +vt 0.368500 0.652000 +vt 0.386100 0.618300 +vt 0.368300 0.619000 +vt 0.384800 0.607100 +vt 0.367100 0.595100 +vt 0.403400 0.615200 +vt 0.151000 0.707900 +vt 0.195000 0.679900 +vt 0.209700 0.709300 +vt 0.214900 0.664400 +vt 0.233200 0.696800 +vt 0.156700 0.680900 +vt 0.165600 0.649800 +vt 0.234700 0.648800 +vt 0.276500 0.614700 +vt 0.306700 0.650900 +vt 0.265600 0.679700 +vt 0.243000 0.579500 +vt 0.205000 0.618000 +vt 0.177400 0.587400 +vt 0.186800 0.560000 +vt 0.312600 0.791900 +vt 0.356100 0.820700 +vt 0.385800 0.854200 +vt 0.415900 0.934700 +vt 0.314200 0.576800 +vt 0.349300 0.608500 +vt 0.280200 0.542000 +vt 0.202600 0.540400 +vt 0.219500 0.524300 +vt 0.408900 0.473000 +vt 0.406700 0.511800 +vt 0.378600 0.491200 +vt 0.394300 0.536000 +vt 0.362600 0.514800 +vt 0.354500 0.492100 +vt 0.346600 0.538300 +vt 0.320900 0.501300 +vt 0.376400 0.568600 +vt 0.290200 0.500600 +vt 0.300600 0.447500 +vt 0.302300 0.415700 +vt 0.310700 0.412800 +vt 0.316900 0.448800 +vt 0.296600 0.379900 +vt 0.315700 0.375000 +vt 0.287200 0.364100 +vt 0.300700 0.357300 +vt 0.275600 0.353800 +vt 0.290100 0.347200 +vt 0.261400 0.348900 +vt 0.333700 0.407300 +vt 0.343700 0.447300 +vt 0.333100 0.367800 +vt 0.354500 0.402100 +vt 0.314100 0.348100 +vt 0.284900 0.331500 +vt 0.349500 0.358800 +vt 0.377400 0.396200 +vt 0.330700 0.343400 +vt 0.306700 0.332700 +vt 0.370100 0.444500 +vt 0.398300 0.435900 +vt 0.249200 0.511700 +vt 0.246400 0.471800 +vt 0.253600 0.451000 +vt 0.269200 0.425200 +vt 0.200600 0.490100 +vt 0.222200 0.481400 +vt 0.181300 0.510200 +vt 0.154900 0.529300 +vt 0.141700 0.558600 +vt 0.130400 0.585600 +vt 0.205500 0.474000 +vt 0.215400 0.451000 +vt 0.176400 0.473400 +vt 0.163100 0.502000 +vt 0.141400 0.539200 +vt 0.143900 0.520300 +vt 0.131000 0.561800 +vt 0.169700 0.630000 +vt 0.133500 0.646500 +vt 0.138700 0.619700 +vt 0.126900 0.675700 +vt 0.120500 0.703800 +vt 0.110800 0.616900 +vt 0.106100 0.641300 +vt 0.092400 0.693300 +vt 0.100100 0.666900 +vt 0.081000 0.632700 +vt 0.086200 0.611400 +vt 0.066300 0.678300 +vt 0.074200 0.655100 +vt 0.060900 0.622300 +vt 0.065000 0.602600 +vt 0.046000 0.660300 +vt 0.055000 0.643100 +vt 0.049200 0.612200 +vt 0.054500 0.594800 +vt 0.034400 0.647800 +vt 0.041000 0.631000 +vt 0.044700 0.600900 +vt 0.044500 0.585000 +vt 0.026800 0.634400 +vt 0.022200 0.622700 +vt 0.033200 0.611200 +vt 0.028100 0.586000 +vt 0.300300 0.337300 +vt 0.405200 0.429700 +vt 0.417100 0.447200 +vt 0.417100 0.424700 +vt 0.426600 0.434800 +vt 0.428700 0.423600 +vt 0.443600 0.424500 +vt 0.134100 0.723900 +vt 0.117300 0.710800 +vt 0.121000 0.735600 +vt 0.113200 0.722900 +vt 0.104700 0.750400 +vt 0.108900 0.736500 +vt 0.144400 0.738100 +vt 0.130400 0.741500 +vt 0.097300 0.722100 +vt 0.096400 0.707400 +vt 0.120400 0.744700 +vt 0.099300 0.733800 +vt 0.487100 0.933800 +vt 0.478200 0.918300 +vt 0.491700 0.920200 +vt 0.470300 0.945000 +vt 0.462100 0.928500 +vt 0.455400 0.951000 +vt 0.446800 0.941300 +vt 0.445600 0.957100 +vt 0.431000 0.947800 +vt 0.458200 0.909200 +vt 0.465800 0.916800 +vt 0.445900 0.914100 +vt 0.467500 0.897000 +vt 0.466600 0.905900 +vt 0.453700 0.895600 +vt 0.428500 0.924300 +vt 0.415000 0.911000 +vt 0.428600 0.896900 +vt 0.478700 0.907100 +vt 0.491500 0.908400 +vt 0.418500 0.994600 +vt 0.405300 0.887800 +vt 0.399400 0.902900 +vt 0.385800 0.913700 +vt 0.553400 0.319200 +vt 0.578800 0.310800 +vt 0.589100 0.343200 +vt 0.564300 0.351800 +vt 0.532400 0.372300 +vt 0.514600 0.389800 +vt 0.495000 0.364000 +vt 0.511100 0.346800 +vt 0.610300 0.319700 +vt 0.614000 0.335600 +vt 0.498800 0.405000 +vt 0.487500 0.390700 +vt 0.479900 0.381700 +vt 0.607500 0.304100 +vt 0.629100 0.408500 +vt 0.636500 0.434600 +vt 0.654900 0.430700 +vt 0.647500 0.403300 +vt 0.671700 0.427900 +vt 0.665300 0.400000 +vt 0.683700 0.402300 +vt 0.702000 0.429500 +vt 0.703400 0.404100 +vt 0.646000 0.460400 +vt 0.663300 0.457000 +vt 0.677100 0.454200 +vt 0.702500 0.453600 +vt 0.658400 0.489100 +vt 0.672600 0.484800 +vt 0.683500 0.481800 +vt 0.704700 0.478400 +vt 0.668500 0.510500 +vt 0.681800 0.506900 +vt 0.690200 0.505000 +vt 0.708200 0.500500 +vt 0.681200 0.534100 +vt 0.696200 0.528300 +vt 0.714200 0.523400 +vt 0.690900 0.555600 +vt 0.705700 0.549700 +vt 0.723000 0.544300 +vt 0.689000 0.579200 +vt 0.703600 0.576000 +vt 0.720300 0.573700 +vt 0.690300 0.601500 +vt 0.701700 0.597600 +vt 0.716000 0.595000 +vt 0.696600 0.620800 +vt 0.706700 0.616200 +vt 0.717500 0.614100 +vt 0.706800 0.637700 +vt 0.715800 0.634500 +vt 0.724100 0.633000 +vt 0.716200 0.654200 +vt 0.723000 0.650500 +vt 0.730100 0.648200 +vt 0.725000 0.669000 +vt 0.731200 0.664600 +vt 0.737000 0.662800 +vt 0.732400 0.680900 +vt 0.738400 0.677600 +vt 0.743700 0.675000 +vt 0.731400 0.683900 +vt 0.732200 0.686100 +vt 0.740100 0.682000 +vt 0.739300 0.679700 +vt 0.742100 0.699500 +vt 0.750300 0.708600 +vt 0.755200 0.704900 +vt 0.747400 0.695900 +vt 0.763900 0.715900 +vt 0.766000 0.711100 +vt 0.775700 0.718200 +vt 0.776200 0.713900 +vt 0.788700 0.714900 +vt 0.787800 0.711400 +vt 0.799300 0.706300 +vt 0.796400 0.704100 +vt 0.804500 0.694400 +vt 0.800900 0.694700 +vt 0.804900 0.686300 +vt 0.801400 0.686200 +vt 0.804100 0.677900 +vt 0.801000 0.677300 +vt 0.802200 0.668200 +vt 0.799900 0.669200 +vt 0.802500 0.665900 +vt 0.799400 0.667000 +vt 0.741600 0.339200 +vt 0.720900 0.334800 +vt 0.713700 0.356800 +vt 0.736600 0.366200 +vt 0.698700 0.330000 +vt 0.699100 0.354600 +vt 0.680600 0.330100 +vt 0.682900 0.360800 +vt 0.665300 0.367200 +vt 0.675500 0.376400 +vt 0.644300 0.371400 +vt 0.622700 0.374800 +vt 0.614500 0.338600 +vt 0.639200 0.333700 +vt 0.664300 0.329700 +vt 0.706200 0.393400 +vt 0.724000 0.375800 +vt 0.713700 0.384400 +vt 0.590500 0.346500 +vt 0.565400 0.356300 +vt 0.534900 0.375800 +vt 0.517600 0.392500 +vt 0.502500 0.407500 +vt 0.725200 0.300100 +vt 0.745100 0.312200 +vt 0.754700 0.286200 +vt 0.733200 0.274000 +vt 0.748500 0.243000 +vt 0.772800 0.264500 +vt 0.764900 0.220500 +vt 0.791000 0.251800 +vt 0.797400 0.184200 +vt 0.836400 0.231200 +vt 0.834800 0.147200 +vt 0.888500 0.218200 +vt 0.660400 0.282300 +vt 0.683100 0.280800 +vt 0.684900 0.256300 +vt 0.657700 0.255100 +vt 0.687500 0.212400 +vt 0.654000 0.212300 +vt 0.690300 0.181700 +vt 0.651100 0.182500 +vt 0.693400 0.138500 +vt 0.646500 0.143400 +vt 0.698600 0.074700 +vt 0.639000 0.084700 +vt 0.633000 0.260500 +vt 0.624500 0.223200 +vt 0.616400 0.199700 +vt 0.597800 0.163700 +vt 0.571100 0.121900 +vt 0.633700 0.295800 +vt 0.601900 0.270200 +vt 0.607500 0.304200 +vt 0.588900 0.239300 +vt 0.577800 0.225100 +vt 0.548600 0.201000 +vt 0.510500 0.181300 +vt 0.699800 0.284000 +vt 0.708400 0.262500 +vt 0.719000 0.223800 +vt 0.728900 0.192400 +vt 0.744200 0.151700 +vt 0.768600 0.097400 +vt 0.537400 0.094900 +vt 0.477300 0.173400 +vt 0.630300 0.011100 +vt 0.703700 0.007200 +vt 0.784500 0.020900 +vt 0.868400 0.109600 +vt 0.930000 0.215400 +vt 0.870800 0.060600 +vt 0.959400 0.216600 +vt 0.940700 0.165800 +vt 0.487900 0.073500 +vt 0.454700 0.104800 +vt 0.554700 0.034700 +vt 0.429100 0.138700 +vt 0.929200 0.110100 +vt 0.967800 0.152300 +vt 0.990600 0.190300 +vt 0.995500 0.218800 +vt 0.421400 0.165600 +vt 0.740700 0.684100 +vt 0.735400 0.687500 +vt 0.656800 0.288300 +vt 0.660100 0.324300 +vt 0.639200 0.329700 +vt 0.633700 0.295700 +vt 0.700500 0.325300 +vt 0.704300 0.291100 +vt 0.725200 0.300500 +vt 0.721600 0.332200 +vt 0.680300 0.325000 +vt 0.675100 0.321600 +vt 0.680300 0.321700 +vt 0.665400 0.296100 +vt 0.668100 0.296000 +vt 0.666700 0.299300 +vt 0.694400 0.324500 +vt 0.674100 0.296700 +vt 0.675300 0.299600 +vt 0.667700 0.323400 +vt 0.677200 0.297400 +vt 0.665300 0.310500 +vt 0.667900 0.310400 +vt 0.666500 0.313500 +vt 0.673800 0.310500 +vt 0.674900 0.313300 +vt 0.677200 0.311100 +vt 0.675100 0.308000 +vt 0.666500 0.307600 +vt 0.667000 0.293000 +vt 0.675400 0.294300 +vt 0.681800 0.289200 +vt 0.682300 0.286200 +vt 0.695500 0.290300 +vt 0.675800 0.289300 +vt 0.667900 0.287500 +vt 0.744000 0.320300 +vt 0.743100 0.336400 +vt 0.647600 0.403300 +vt 0.654900 0.430600 +vt 0.665300 0.399900 +vt 0.671900 0.428100 +vt 0.683700 0.402100 +vt 0.703200 0.404000 +vt 0.663000 0.457400 +vt 0.676600 0.454800 +vt 0.702400 0.453500 +vt 0.672400 0.484800 +vt 0.658500 0.489100 +vt 0.683500 0.481700 +vt 0.704600 0.478500 +vt 0.681700 0.506800 +vt 0.668600 0.510500 +vt 0.690200 0.504900 +vt 0.696300 0.528400 +vt 0.681100 0.534100 +vt 0.705800 0.549700 +vt 0.690900 0.555500 +vt 0.723000 0.544200 +vt 0.703600 0.576000 +vt 0.720400 0.573600 +vt 0.701700 0.597400 +vt 0.716100 0.595100 +vt 0.706800 0.616200 +vt 0.715800 0.634500 +vt 0.723000 0.650500 +vt 0.716200 0.654100 +vt 0.731300 0.664600 +vt 0.738400 0.677600 +vt 0.732400 0.680800 +vt 0.747600 0.678900 +vt 0.740100 0.682000 +vt 0.739300 0.679700 +vt 0.746700 0.676700 +vt 0.747400 0.695900 +vt 0.752000 0.693300 +vt 0.759000 0.701500 +vt 0.755200 0.704900 +vt 0.767100 0.706500 +vt 0.766000 0.711100 +vt 0.776600 0.709400 +vt 0.776300 0.713900 +vt 0.786900 0.707300 +vt 0.787800 0.711400 +vt 0.793900 0.701600 +vt 0.796400 0.704100 +vt 0.797900 0.693000 +vt 0.800900 0.694700 +vt 0.798700 0.684800 +vt 0.801400 0.686200 +vt 0.798500 0.676600 +vt 0.801000 0.677300 +vt 0.797600 0.669500 +vt 0.799900 0.669300 +vt 0.799400 0.667000 +vt 0.792700 0.666500 +vt 0.741700 0.339200 +vt 0.736600 0.366100 +vt 0.713400 0.356700 +vt 0.720900 0.334900 +vt 0.698900 0.354700 +vt 0.698700 0.329900 +vt 0.680600 0.330000 +vt 0.682900 0.360900 +vt 0.675600 0.376600 +vt 0.665400 0.367000 +vt 0.622600 0.374800 +vt 0.644300 0.371400 +vt 0.639100 0.333700 +vt 0.664300 0.329600 +vt 0.706300 0.393400 +vt 0.724100 0.375700 +vt 0.733100 0.274000 +vt 0.748600 0.243000 +vt 0.764800 0.220400 +vt 0.797400 0.184400 +vt 0.834400 0.147000 +vt 0.660500 0.282200 +vt 0.657800 0.255100 +vt 0.684800 0.256300 +vt 0.683600 0.280700 +vt 0.653800 0.212400 +vt 0.687700 0.212600 +vt 0.651200 0.182300 +vt 0.690500 0.181700 +vt 0.646500 0.143200 +vt 0.693500 0.138500 +vt 0.638900 0.084700 +vt 0.698500 0.074500 +vt 0.633000 0.260500 +vt 0.624500 0.223200 +vt 0.616400 0.199700 +vt 0.597900 0.163800 +vt 0.571000 0.121900 +vt 0.601900 0.270100 +vt 0.699800 0.284000 +vt 0.708400 0.262700 +vt 0.719100 0.223600 +vt 0.729000 0.192200 +vt 0.744100 0.151600 +vt 0.768200 0.097200 +vt 0.477300 0.173500 +vt 0.537400 0.094800 +vt 0.630200 0.011000 +vt 0.703700 0.007300 +vt 0.784800 0.020900 +vt 0.868600 0.109400 +vt 0.930000 0.215300 +vt 0.870500 0.060600 +vt 0.941000 0.165500 +vt 0.454800 0.104600 +vt 0.488000 0.073500 +vt 0.554700 0.034800 +vt 0.429200 0.138700 +vt 0.929100 0.109800 +vt 0.967700 0.152300 +vt 0.990700 0.190200 +vt 0.995600 0.218800 +vt 0.421500 0.165500 +vt 0.745800 0.682100 +vt 0.740700 0.684100 +vt 0.529200 0.333800 +vt 0.547400 0.360600 +vt 0.550200 0.364700 +vn -0.855995 -0.507397 -0.099099 +vn -0.871586 -0.487392 0.052799 +vn -0.854780 -0.507988 -0.106298 +vn -0.807602 -0.515401 -0.286601 +vn -0.879473 0.381788 -0.284191 +vn -0.882355 0.391180 -0.261587 +vn -0.888315 0.444107 -0.116902 +vn -0.839723 0.303008 -0.450612 +vn 0.733611 0.224803 -0.641310 +vn 0.739765 0.223190 -0.634770 +vn 0.722912 0.227504 -0.652411 +vn 0.720134 0.228211 -0.655231 +vn 0.924289 -0.270897 0.268897 +vn 0.932864 -0.266390 0.242491 +vn 0.879071 -0.288090 0.379787 +vn 0.842066 -0.297088 0.450182 +vn -0.877884 0.474092 0.067499 +vn -0.891915 0.355006 -0.280105 +vn -0.896814 0.353205 -0.266404 +vn -0.849863 0.499478 0.168093 +vn -0.677076 0.573880 0.460684 +vn -0.675298 0.575799 0.460899 +vn 0.370113 -0.154605 0.916032 +vn 0.370113 -0.154605 0.916032 +vn 0.370113 -0.154605 0.916032 +vn 0.409613 -0.218007 0.885828 +vn 0.409613 -0.218007 0.885828 +vn 0.409613 -0.218007 0.885828 +vn 0.197096 -0.915783 0.349993 +vn 0.197096 -0.915783 0.349993 +vn 0.197096 -0.915783 0.349993 +vn 0.374511 -0.924827 0.066602 +vn 0.374511 -0.924827 0.066602 +vn 0.374511 -0.924827 0.066602 +vn -0.746801 0.171700 -0.642501 +vn -0.746801 0.171700 -0.642501 +vn -0.746801 0.171700 -0.642501 +vn 0.361309 0.387709 0.848020 +vn 0.361309 0.387709 0.848020 +vn 0.361309 0.387709 0.848020 +vn 0.355290 -0.519086 0.777379 +vn 0.355290 -0.519086 0.777379 +vn 0.355290 -0.519086 0.777379 +vn 0.392994 -0.559692 0.729589 +vn 0.392994 -0.559692 0.729589 +vn 0.392994 -0.559692 0.729589 +vn -0.230300 0.301300 0.925300 +vn -0.230300 0.301300 0.925300 +vn -0.230300 0.301300 0.925300 +vn -0.348583 0.290086 0.891257 +vn -0.348583 0.290086 0.891257 +vn -0.348583 0.290086 0.891257 +vn -0.570493 -0.659792 0.489094 +vn -0.570493 -0.659792 0.489094 +vn -0.570493 -0.659792 0.489094 +vn -0.755618 -0.587314 0.290007 +vn -0.755618 -0.587314 0.290007 +vn -0.755618 -0.587314 0.290007 +vn 0.274300 0.673101 0.686801 +vn 0.274300 0.673101 0.686801 +vn 0.274300 0.673101 0.686801 +vn -0.354997 0.708695 0.609695 +vn -0.354997 0.708695 0.609695 +vn -0.354997 0.708695 0.609695 +vn -0.472693 -0.044799 0.880088 +vn -0.472693 -0.044799 0.880088 +vn -0.472693 -0.044799 0.880088 +vn -0.562590 -0.043599 0.825586 +vn -0.562590 -0.043599 0.825586 +vn -0.562590 -0.043599 0.825586 +vn 0.225001 0.828003 0.513602 +vn 0.060298 0.818675 0.571083 +vn 0.080201 0.821110 0.565107 +vn 0.244005 0.827416 0.505810 +vn 0.448604 0.825207 0.343203 +vn 0.583192 0.810388 0.056199 +vn 0.631799 0.775098 -0.007300 +vn 0.503410 0.805516 0.312606 +vn 0.662629 0.685530 -0.301613 +vn 0.662270 0.682769 -0.308586 +vn 0.497693 0.645091 -0.579792 +vn 0.467084 0.622778 -0.627678 +vn 0.186208 0.549123 -0.814734 +vn 0.105201 0.519604 -0.847906 +vn -0.267401 0.494201 -0.827202 +vn -0.302295 0.478891 -0.824185 +vn -0.568076 0.640573 -0.516678 +vn -0.611785 0.631485 -0.476388 +vn -0.668300 0.743700 0.016900 +vn -0.678072 0.734570 0.024999 +vn -0.329608 0.879721 0.342708 +vn -0.251993 0.856376 0.450688 +vn 0.242890 0.950662 0.192992 +vn 0.299493 0.919779 0.253594 +vn 0.526004 0.820207 0.224902 +vn 0.498386 0.829676 0.251493 +vn 0.320701 0.818303 0.477002 +vn 0.319297 0.809993 0.491896 +vn 0.170498 -0.975087 0.141898 +vn 0.455606 -0.889912 -0.021900 +vn 0.434189 -0.900777 0.009000 +vn 0.180006 -0.971535 0.154006 +vn -0.283401 -0.892104 0.351902 +vn -0.442897 -0.806094 0.392497 +vn -0.447385 -0.796474 0.406787 +vn -0.266602 -0.890908 0.367703 +vn -0.301905 -0.894215 0.330505 +vn -0.288997 -0.902690 0.318797 +vn -0.463773 -0.800853 0.378878 +vn -0.455494 -0.803689 0.382895 +vn -0.431291 -0.800483 0.416191 +vn -0.277898 -0.895594 0.347397 +vn -0.276491 -0.891971 0.357688 +vn -0.431190 -0.798182 0.420691 +vn -0.121400 -0.953199 0.276900 +vn -0.074301 -0.962907 0.259402 +vn 0.686143 -0.510532 -0.518232 +vn 0.679266 -0.514874 -0.522974 +vn 0.681506 -0.513505 -0.521405 +vn 0.686972 -0.509979 -0.517679 +vn 0.902408 0.402804 0.153001 +vn 0.809993 0.504195 0.299497 +vn 0.847634 0.469419 0.247310 +vn 0.910018 0.391208 0.137203 +vn 0.564114 -0.741718 0.362809 +vn 0.251010 -0.940236 -0.230109 +vn 0.601803 -0.715803 -0.354202 +vn 0.852213 -0.514608 0.094401 +vn 0.356678 -0.649660 0.671359 +vn -0.019799 -0.983851 -0.177891 +vn -0.768431 -0.484019 0.418617 +vn -0.460995 -0.871490 0.167298 +vn -0.181506 -0.724524 0.664922 +vn -0.367014 -0.491718 0.789629 +vn 0.641896 -0.376498 0.667996 +vn 0.179098 -0.445295 0.877289 +vn -0.527689 -0.848683 -0.035799 +vn 0.138005 -0.878634 -0.457118 +vn 0.337206 -0.507209 0.793115 +vn 0.195200 -0.690598 0.696398 +vn 0.319013 -0.519122 0.792933 +vn -0.136204 -0.985026 -0.105703 +vn -0.104599 -0.934991 -0.338897 +vn -0.150105 -0.926332 -0.345512 +vn 0.062498 -0.979365 0.192193 +vn 0.227598 -0.965491 0.126599 +vn -0.490294 -0.757191 -0.431595 +vn -0.210401 -0.811404 -0.545303 +vn -0.355392 -0.805683 -0.473890 +vn -0.705676 -0.697476 -0.124696 +vn -0.410999 -0.863098 -0.293499 +vn -0.439609 -0.848117 -0.295706 +vn -0.593488 -0.803983 0.037199 +vn -0.546101 -0.837502 -0.019100 +vn -0.586938 -0.745748 0.315220 +vn -0.590774 -0.747868 0.302787 +vn -0.435401 -0.640501 0.632601 +vn -0.461425 -0.642835 0.611433 +vn -0.090902 -0.500211 0.861119 +vn -0.152899 -0.525097 0.837195 +vn 0.237191 -0.604377 0.760571 +vn 0.241210 -0.613527 0.751933 +vn 0.525800 -0.692101 0.494500 +vn 0.555167 -0.716158 0.422975 +vn 0.557097 -0.830095 0.024200 +vn 0.549115 -0.835723 -0.006400 +vn 0.217300 -0.894502 -0.390701 +vn 0.244000 -0.903598 -0.352099 +vn 0.266006 -0.916621 -0.298407 +vn 0.231199 -0.900297 -0.368799 +vn 0.410295 -0.816991 -0.405196 +vn 0.242412 -0.815640 -0.525326 +vn 0.019300 -0.976490 0.214698 +vn 0.247313 -0.964949 0.087804 +vn 0.230094 -0.967477 0.105097 +vn 0.227902 -0.967809 0.106801 +vn -0.695701 -0.665901 -0.269400 +vn -0.799430 -0.593422 0.093604 +vn -0.720130 -0.324414 0.613326 +vn -0.714406 -0.330603 0.616705 +vn 0.865296 0.197999 -0.460498 +vn 0.879889 0.431595 0.198798 +vn 0.879117 0.244005 0.409408 +vn 0.954707 -0.246802 0.166201 +vn 0.834886 0.028400 0.549691 +vn 0.837155 -0.169091 0.520172 +vn 0.805284 -0.379293 0.455691 +vn 0.877811 -0.470206 0.091401 +vn 0.675135 0.655334 0.338718 +vn 0.750024 0.324710 0.576218 +vn 0.871998 0.261199 0.413999 +vn 0.684875 0.103496 0.721273 +vn 0.663609 -0.112902 0.739510 +vn 0.644611 -0.375906 0.665711 +vn 0.413787 0.488084 0.768475 +vn 0.616218 0.393512 0.682220 +vn 0.434587 0.788177 0.435787 +vn 0.336988 0.188893 0.922366 +vn 0.538275 0.138194 0.831362 +vn 0.406277 -0.281984 0.869151 +vn 0.497667 -0.049697 0.865943 +vn 0.259000 -0.265400 0.928699 +vn 0.529225 -0.306114 0.791337 +vn 0.852490 0.518394 -0.067299 +vn 0.573488 0.812183 0.107098 +vn 0.258198 0.954092 0.151799 +vn 0.927979 0.023499 -0.371891 +vn 0.848116 0.469809 -0.244905 +vn 0.550377 0.827465 -0.111295 +vn 0.218509 0.972542 -0.080103 +vn -0.860916 0.468508 -0.198304 +vn -0.406114 0.881030 0.242608 +vn -0.196602 0.692506 -0.694106 +vn 0.036200 0.662493 0.748192 +vn 0.882470 0.295590 0.365887 +vn 0.665826 0.683526 0.299112 +vn 0.131896 -0.336589 0.932369 +vn 0.717371 0.056798 0.694372 +vn 0.643517 0.752220 0.141604 +vn 0.360214 0.915436 -0.179507 +vn 0.499997 0.695596 -0.515897 +vn -0.147706 0.257810 -0.954839 +vn -0.425182 -0.168093 -0.889362 +vn 0.026700 0.990308 -0.136301 +vn -0.429198 0.593897 -0.680497 +vn -0.079297 0.557077 -0.826666 +vn 0.395978 0.854153 -0.337081 +vn 0.666981 0.644181 0.374389 +vn 0.902013 -0.260304 0.344405 +vn 0.026700 0.990308 -0.136301 +vn -0.429198 0.593897 -0.680497 +vn -0.950196 -0.120300 -0.287499 +vn -0.453615 -0.166906 -0.875429 +vn -0.122805 -0.086703 -0.988636 +vn -0.453615 -0.166906 -0.875429 +vn 0.913839 0.404617 0.034401 +vn 0.666981 0.644181 0.374389 +vn 0.995582 -0.047999 0.080699 +vn 0.902013 -0.260304 0.344405 +vn 0.814443 -0.497626 0.298416 +vn 0.785544 -0.158609 0.598134 +vn -0.274989 -0.826868 0.490581 +vn 0.477113 -0.817922 -0.321508 +vn 0.517101 -0.836001 0.183600 +vn -0.707274 -0.703574 -0.068897 +vn 0.028800 -0.594201 -0.803801 +vn 0.111097 -0.959576 -0.258594 +vn -0.078196 -0.335883 -0.938652 +vn -0.425182 -0.168093 -0.889362 +vn 0.415281 -0.890560 -0.185592 +vn -0.200396 -0.767685 -0.608688 +vn -0.453615 -0.166906 -0.875429 +vn -0.950196 -0.120300 -0.287499 +vn -0.122805 -0.086703 -0.988636 +vn -0.453615 -0.166906 -0.875429 +vn -0.200396 -0.767685 -0.608688 +vn 0.156703 -0.488609 -0.858316 +vn 0.415281 -0.890560 -0.185592 +vn 0.703802 -0.632002 -0.324401 +vn -0.196602 0.692506 -0.694106 +vn 0.797918 0.049701 -0.600714 +vn -0.147706 0.257810 -0.954839 +vn 0.882470 0.295590 0.365887 +vn 0.665826 0.683526 0.299112 +vn 0.643517 0.752220 0.141604 +vn 0.360214 0.915436 -0.179507 +vn 0.499997 0.695596 -0.515897 +vn -0.425182 -0.168093 -0.889362 +vn -0.078196 -0.335883 -0.938652 +vn 0.028800 -0.594201 -0.803801 +vn 0.814443 -0.497626 0.298416 +vn 0.785544 -0.158609 0.598134 +vn 0.717371 0.056798 0.694372 +vn 0.477113 -0.817922 -0.321508 +vn 0.517101 -0.836001 0.183600 +vn 0.111097 -0.959576 -0.258594 +vn 0.074898 0.509384 -0.857274 +vn 0.468000 0.747501 -0.471400 +vn -0.075098 -0.059998 -0.995370 +vn 0.850709 0.473805 -0.227602 +vn 0.949530 -0.049102 -0.309810 +vn 0.335195 -0.341995 -0.877886 +vn -0.075098 -0.059998 -0.995370 +vn 0.804756 -0.571240 -0.161411 +vn 0.959026 0.253807 -0.125903 +vn 0.884211 -0.219303 -0.412405 +vn 0.806828 -0.507818 -0.301910 +vn 0.112402 0.849418 -0.515611 +vn 0.177091 0.944150 -0.277885 +vn 0.518700 0.810300 -0.272700 +vn 0.292898 -0.274498 -0.915894 +vn 0.583893 0.041099 -0.810790 +vn 0.166598 -0.008600 -0.985987 +vn 0.201411 -0.111406 -0.973151 +vn 0.455981 0.859464 -0.231090 +vn -0.141603 0.130203 -0.981323 +vn -0.064498 0.009500 -0.997873 +vn 0.583893 0.041099 -0.810790 +vn 0.583893 0.041099 -0.810790 +vn -0.064498 0.009500 -0.997873 +vn 0.166598 -0.008600 -0.985987 +vn -0.041300 0.475203 -0.878906 +vn 0.826123 -0.319709 -0.464013 +vn 0.646083 -0.697882 -0.309092 +vn 0.822963 -0.485078 -0.295687 +vn 0.659083 -0.252594 -0.708382 +vn 0.459187 -0.244693 -0.853975 +vn 0.341008 -0.119603 -0.932421 +vn 0.806828 -0.507818 -0.301910 +vn 0.810420 -0.464811 -0.356609 +vn 0.917808 -0.203502 -0.340903 +vn 0.840133 -0.505220 -0.197308 +vn 0.809076 -0.491386 -0.322391 +vn 0.884211 -0.219303 -0.412405 +vn 0.381397 -0.064000 -0.922193 +vn 0.951458 -0.286287 -0.112995 +vn 0.917808 -0.203502 -0.340903 +vn 0.954707 -0.246802 0.166201 +vn 0.877811 -0.470206 0.091401 +vn 0.951458 -0.286287 -0.112995 +vn -0.025199 -0.319485 -0.947256 +vn -0.247598 -0.376796 -0.892592 +vn -0.276008 -0.205506 -0.938928 +vn -0.059198 -0.697973 -0.713673 +vn -0.284095 -0.711687 -0.642488 +vn 0.054101 -0.212704 -0.975618 +vn 0.129898 -0.237397 -0.962688 +vn 0.560207 -0.799910 -0.215203 +vn 0.626715 -0.754918 -0.193205 +vn 0.277594 -0.950981 -0.136297 +vn 0.240808 -0.964734 -0.106304 +vn 0.056099 -0.551494 -0.832291 +vn 0.455981 0.859464 -0.231090 +vn 0.583893 0.041099 -0.810790 +vn 0.407004 -0.165501 0.898308 +vn 0.302395 0.142398 0.942486 +vn 0.292898 -0.274498 -0.915894 +vn -0.122800 -0.987197 0.101800 +vn -0.414094 -0.467694 -0.780890 +vn -0.510489 -0.854282 -0.097998 +vn -0.170200 -0.936498 0.306599 +vn 0.223204 -0.962816 0.152203 +vn 0.231805 -0.946620 0.224005 +vn -0.135005 -0.887932 0.439716 +vn 0.688190 -0.701890 -0.183697 +vn 0.611498 -0.790098 -0.042600 +vn 0.431423 -0.902148 0.001500 +vn 0.427012 -0.894925 0.129504 +vn -0.270403 -0.920611 -0.281703 +vn 0.025600 -0.940509 -0.338803 +vn 0.592010 -0.514408 -0.620410 +vn 0.341503 -0.860209 -0.378704 +vn 0.336508 -0.354709 -0.872321 +vn 0.222509 -0.638527 -0.736731 +vn 0.116206 -0.908448 -0.401521 +vn 0.094898 -0.993579 -0.061599 +vn -0.218391 -0.972360 0.082597 +vn 0.640282 -0.761979 -0.097097 +vn 0.356498 -0.934294 0.001900 +vn 0.032701 -0.995645 -0.087304 +vn -0.233606 -0.972325 -0.003400 +vn 0.577398 -0.813396 -0.070700 +vn -0.189506 -0.956032 0.223808 +vn -0.213206 -0.970829 0.109703 +vn 0.041700 -0.997908 0.049400 +vn 0.078998 -0.981873 0.172295 +vn 0.363692 -0.927679 0.084498 +vn 0.285297 -0.938991 0.192098 +vn 0.253493 -0.964172 0.078198 +vn -0.276008 -0.205506 -0.938928 +vn 0.373896 -0.387396 0.842690 +vn 0.905359 0.045698 0.422181 +vn 0.300904 -0.428606 -0.851912 +vn 0.727619 -0.097502 -0.679017 +vn 0.971488 0.090599 -0.219097 +vn 0.373896 -0.387396 0.842690 +vn 0.959026 0.253807 -0.125903 +vn 0.806828 -0.507818 -0.301910 +vn 0.905359 0.045698 0.422181 +vn 0.459187 -0.244693 -0.853975 +vn 0.381397 -0.064000 -0.922193 +vn -0.339209 0.818422 -0.463813 +vn 0.300904 -0.428606 -0.851912 +vn 0.727619 -0.097502 -0.679017 +vn 0.047800 0.992892 -0.108999 +vn 0.971488 0.090599 -0.219097 +vn 0.459187 -0.244693 -0.853975 +vn 0.527713 -0.829120 -0.184605 +vn 0.132196 -0.971573 -0.196394 +vn -0.247195 -0.910280 -0.332093 +vn -0.060200 -0.637497 -0.768097 +vn -0.478022 -0.444221 -0.757735 +vn -0.578422 -0.743229 -0.336213 +vn 0.037901 -0.991830 0.121804 +vn -0.557121 -0.830431 -0.000400 +vn 0.281709 -0.901328 0.329010 +vn -0.246207 -0.933327 0.261308 +vn -0.308396 -0.950487 0.038299 +vn 0.536122 -0.840934 0.073503 +vn -0.370395 -0.747290 0.551693 +vn -0.569117 -0.817924 -0.084302 +vn -0.033000 -0.685101 0.727701 +vn -0.023799 -0.365382 0.930553 +vn 0.613701 -0.687002 0.389101 +vn 0.371078 -0.217187 0.902846 +vn 0.188606 -0.066002 0.979832 +vn -0.533791 -0.807787 -0.250096 +vn -0.546305 -0.738006 -0.396103 +vn -0.391988 -0.904372 -0.168695 +vn -0.312204 -0.892511 0.325504 +vn -0.251994 -0.887679 0.385391 +vn -0.297303 -0.892308 0.339703 +vn -0.371808 -0.927619 0.035801 +vn -0.390298 -0.908894 0.146899 +vn -0.365000 -0.926999 -0.086300 +vn -0.193197 -0.902685 0.384494 +vn -0.242802 -0.870306 0.428503 +vn -0.104400 -0.842802 0.528001 +vn -0.168294 -0.753472 0.635576 +vn -0.041100 -0.929311 0.367004 +vn 0.004500 -0.854888 0.518793 +vn -0.113905 -0.721929 0.682528 +vn -0.124597 -0.672882 0.729181 +vn -0.460196 -0.714194 -0.527396 +vn -0.539985 -0.736180 -0.407989 +vn -0.639196 -0.694596 -0.330098 +vn -0.665926 -0.609824 -0.429717 +vn -0.929510 -0.246403 -0.274403 +vn -0.705889 -0.461793 -0.537092 +vn -0.838186 0.062499 -0.541791 +vn 0.151396 0.986776 -0.057899 +vn -0.780046 0.530931 0.331120 +vn -0.983500 0.180800 0.006300 +vn -0.891825 -0.263508 -0.367711 +vn -0.615814 -0.538212 -0.575413 +vn -0.364611 -0.654719 -0.662119 +vn -0.587709 -0.797112 -0.138602 +vn -0.451697 -0.792694 0.409397 +vn -0.247409 -0.873333 0.419616 +vn -0.353803 -0.921007 -0.163001 +vn -0.869720 -0.492711 -0.028701 +vn -0.697411 -0.589609 0.407406 +vn -0.966718 -0.029801 0.254105 +vn -0.818487 -0.245796 0.519292 +vn -0.754789 0.332095 0.565692 +vn -0.626976 0.307288 0.715873 +vn -0.440011 -0.766819 0.467311 +vn -0.213501 -0.860904 0.461802 +vn -0.604508 -0.583408 0.542407 +vn -0.740713 -0.327106 0.586810 +vn -0.550895 0.537795 0.638194 +vn -0.750681 0.034099 0.659784 +vn -0.405412 -0.771323 0.490614 +vn -0.208603 -0.866914 0.452707 +vn -0.506086 -0.590984 0.628183 +vn -0.618191 -0.378694 0.688790 +vn -0.556500 0.509100 0.656600 +vn -0.696893 -0.020100 0.716893 +vn -0.241286 -0.740657 -0.627063 +vn -0.284588 -0.835765 -0.469581 +vn 0.132803 -0.783517 -0.607013 +vn 0.463400 -0.651199 -0.600999 +vn 0.139795 -0.812969 -0.565278 +vn 0.596312 0.605012 0.527610 +vn 0.611412 0.545111 0.573611 +vn 0.404408 0.676914 0.615013 +vn 0.466696 0.663395 0.584895 +vn 0.654082 -0.610083 -0.447187 +vn 0.926954 -0.361821 -0.099206 +vn 0.520480 0.704973 -0.481782 +vn 0.191699 -0.668698 -0.718397 +vn -0.110895 0.781968 0.613375 +vn 0.971671 0.189894 0.140696 +vn 0.447917 0.642325 0.621924 +vn 0.522382 0.615279 0.590380 +vn -0.167105 -0.632618 -0.756221 +vn 0.952225 0.155904 0.262607 +vn -0.152294 -0.952065 -0.265290 +vn -0.031100 -0.927910 0.371504 +vn 0.234891 -0.937762 -0.255790 +vn 0.254703 -0.900011 0.353704 +vn 0.647371 -0.759666 -0.061797 +vn 0.514401 -0.736602 0.439101 +vn 0.855995 -0.397797 0.330198 +vn 0.690672 -0.326487 0.645274 +vn 0.763280 0.147596 0.628983 +vn 0.623573 0.265589 0.735268 +vn 0.475282 0.522380 0.707973 +vn 0.402300 0.643299 0.651399 +vn 0.312299 0.655498 0.687598 +vn 0.255404 0.748311 0.612209 +vn 0.036499 -0.879987 0.473593 +vn 0.246186 -0.797456 0.550869 +vn 0.444793 -0.642589 0.623890 +vn 0.571098 -0.451598 0.685497 +vn 0.679734 -0.005800 0.733436 +vn 0.567992 0.496393 0.656490 +vn 0.375696 0.736591 0.562393 +vn 0.214497 0.805789 0.551992 +vn 0.016200 -0.869020 0.494511 +vn 0.186996 -0.729083 0.658385 +vn 0.342819 -0.569432 0.747142 +vn 0.523615 -0.357710 0.773221 +vn 0.660584 0.093698 0.744882 +vn 0.550539 0.559640 0.619444 +vn 0.365521 0.738843 0.566133 +vn 0.194608 0.812835 0.549024 +vn 0.139795 -0.812969 -0.565278 +vn 0.463400 -0.651199 -0.600999 +vn 0.327399 -0.847597 -0.417599 +vn 0.537789 0.118697 0.834682 +vn 0.515003 -0.394902 0.760805 +vn 0.618305 -0.258802 0.742106 +vn 0.488009 -0.803214 0.341606 +vn -0.074403 -0.997141 0.013201 +vn 0.484717 -0.838030 0.250509 +vn 0.520480 0.704973 -0.481782 +vn -0.339999 0.788898 0.511899 +vn -0.224387 0.698460 0.679561 +vn -0.766997 0.463898 0.443298 +vn -0.892397 0.326299 -0.311699 +vn -0.429597 -0.742395 -0.514097 +vn 0.305990 0.293790 0.905570 +vn -0.110895 0.781968 0.613375 +vn 0.436420 0.532925 -0.724934 +vn 0.857185 0.194197 0.476992 +vn 0.185801 0.963506 0.192701 +vn 0.666710 0.301505 -0.681610 +vn 0.628015 -0.032601 -0.777518 +vn 0.620003 -0.265801 -0.738204 +vn 0.808688 -0.541792 0.229097 +vn 0.729590 -0.023200 -0.683491 +vn 0.382202 0.154301 -0.911105 +vn 0.287299 0.126900 -0.949398 +vn -0.427416 0.838931 -0.336913 +vn 0.508467 0.433072 -0.744252 +vn 0.927011 -0.050601 -0.371604 +vn 0.953987 0.298796 0.025100 +vn -0.159903 0.535411 0.829317 +vn -0.427416 0.838931 -0.336913 +vn 0.399212 -0.249608 0.882228 +vn 0.268598 -0.794594 0.544496 +vn 0.317788 -0.852768 0.414484 +vn 0.553100 -0.302900 0.776100 +vn -0.962086 0.152398 -0.226197 +vn -0.766932 -0.577124 -0.280612 +vn 0.432701 0.304100 0.848701 +vn -0.217399 -0.975795 0.023700 +vn -0.021901 0.772339 0.634832 +vn -0.657255 0.737349 0.155989 +vn 0.686751 -0.696651 -0.207485 +vn 0.933378 -0.207695 0.292693 +vn -0.479788 0.332892 -0.811780 +vn -0.262807 -0.346910 -0.900325 +vn -0.657255 0.737349 0.155989 +vn -0.249001 0.927205 -0.279802 +vn 0.823807 0.360703 0.437304 +vn 0.236510 -0.755534 -0.610927 +vn 0.398702 0.882404 0.249801 +vn -0.249001 0.927205 -0.279802 +vn 0.312494 0.680486 -0.662787 +vn 0.377113 0.900530 -0.216407 +vn 0.220491 0.052798 0.973959 +vn 0.508467 0.433072 -0.744252 +vn -0.674088 0.692687 -0.256495 +vn -0.904203 0.073100 0.420801 +vn -0.294015 -0.694636 0.656534 +vn 0.583992 -0.786990 0.198997 +vn 0.108299 0.719596 -0.685896 +vn 0.881351 -0.204989 -0.425676 +vn 0.903440 -0.086404 -0.419918 +vn 0.142799 0.740692 -0.656493 +vn 0.881351 -0.204989 -0.425676 +vn 0.580519 -0.804026 0.128604 +vn 0.636011 -0.763914 0.109202 +vn 0.903440 -0.086404 -0.419918 +vn 0.017400 -0.567906 0.822909 +vn 0.130105 -0.450917 0.883033 +vn -0.812190 0.417095 0.407895 +vn -0.575025 0.561825 0.594726 +vn -0.812190 0.417095 0.407895 +vn -0.625992 0.736891 -0.255197 +vn -0.597705 0.761707 -0.250102 +vn -0.575025 0.561825 0.594726 +vn 0.896660 -0.177492 -0.405582 +vn 0.029500 0.616107 -0.787109 +vn 0.636209 -0.760210 0.131602 +vn 0.896660 -0.177492 -0.405582 +vn 0.383384 -0.874265 0.297788 +vn 0.505583 -0.862371 0.026499 +vn 0.066303 -0.850535 0.521722 +vn -0.650929 -0.341815 0.677830 +vn -0.034102 -0.904351 -0.425424 +vn -0.988245 0.148892 -0.034698 +vn -0.706999 0.491199 -0.508799 +vn -0.177105 0.170405 0.969328 +vn 0.299801 -0.631803 0.714804 +vn -0.870665 0.217691 -0.441082 +vn -0.581574 0.809964 0.075697 +vn -0.352897 -0.756293 0.550895 +vn -0.228304 -0.969318 0.091102 +vn -0.352897 -0.756293 0.550895 +vn -0.228304 -0.969318 0.091102 +vn 0.567317 -0.313909 -0.761323 +vn 0.083596 -0.869461 -0.486878 +vn -0.331500 0.338600 -0.880601 +vn 0.465895 -0.254697 -0.847391 +vn 0.037900 -0.941312 -0.335404 +vn -0.421322 0.258314 -0.869346 +vn 0.565066 -0.777553 0.275883 +vn 0.805006 -0.164501 -0.570004 +vn -0.975528 0.157905 0.153004 +vn -0.972109 0.178602 0.152001 +vn -0.362214 -0.602223 0.711427 +vn -0.092397 0.544782 -0.833472 +vn -0.107503 0.595114 -0.796419 +vn 0.505583 -0.862371 0.026499 +vn 0.805006 -0.164501 -0.570004 +vn 0.580486 -0.766681 0.274293 +vn 0.037900 -0.941312 -0.335404 +vn 0.465895 -0.254697 -0.847391 +vn -0.992325 0.024501 -0.121203 +vn -0.661936 -0.701938 0.262914 +vn 0.876870 -0.142295 -0.459184 +vn -0.097197 0.611982 -0.784877 +vn 0.605885 -0.763981 0.221894 +vn 0.876870 -0.142295 -0.459184 +vn 0.505583 -0.862371 0.026499 +vn -0.987757 0.125695 0.092396 +vn -0.303088 -0.769769 0.561777 +vn -0.109802 0.639613 -0.760815 +vn 0.590111 -0.762415 0.265505 +vn 0.083596 -0.869461 -0.486878 +vn 0.567317 -0.313909 -0.761323 +vn -0.993033 0.116504 0.017701 +vn -0.947703 -0.020400 -0.318501 +vn -0.526900 -0.849901 0.006700 +vn 0.008100 0.291602 0.956506 +vn -0.650929 -0.341815 0.677830 +vn 0.066303 -0.850535 0.521722 +vn -0.540399 -0.004000 -0.841399 +vn -0.663390 -0.185997 -0.724789 +vn -0.945157 0.313186 0.092696 +vn 0.234191 -0.840967 -0.487781 +vn 0.299801 -0.631803 0.714804 +vn -0.945157 0.313186 0.092696 +vn -0.581574 0.809964 0.075697 +vn -0.362214 -0.602223 0.711427 +vn -0.661936 -0.701938 0.262914 +vn -0.303088 -0.769769 0.561777 +vn -0.526900 -0.849901 0.006700 +vn 0.843174 -0.022199 -0.537183 +vn 0.687486 -0.078098 -0.721986 +vn 0.495678 0.102295 -0.862461 +vn 0.439398 0.223299 -0.870096 +vn 0.427687 0.484086 -0.763377 +vn 0.773775 0.247092 -0.583281 +vn 0.599887 -0.002000 -0.800082 +vn 0.427687 0.484086 -0.763377 +vn 0.652104 0.449903 -0.610204 +vn 0.881351 -0.204989 -0.425676 +vn 0.108299 0.719596 -0.685896 +vn -0.674088 0.692687 -0.256495 +vn 0.017400 -0.567906 0.822909 +vn 0.580519 -0.804026 0.128604 +vn -0.625992 0.736891 -0.255197 +vn -0.812190 0.417095 0.407895 +vn -0.625992 0.736891 -0.255197 +vn 0.520480 0.704973 -0.481782 +vn -0.766997 0.463898 0.443298 +vn 0.507981 0.697673 0.505181 +vn 0.438102 0.517103 0.735304 +vn 0.901476 -0.025299 0.432089 +vn 0.969421 0.090502 0.228105 +vn 0.291288 0.955660 -0.043198 +vn 0.508010 0.861316 0.007800 +vn 0.120398 0.979584 -0.160997 +vn 0.249697 0.771492 -0.585194 +vn 0.722336 0.690235 0.042502 +vn 0.786259 -0.617068 -0.031998 +vn 0.820624 -0.531916 -0.208906 +vn 0.992653 0.106195 -0.057997 +vn 0.333799 -0.893498 -0.300399 +vn 0.265107 -0.879324 -0.395611 +vn -0.169802 -0.884812 -0.433906 +vn -0.181991 -0.848759 -0.496476 +vn -0.285895 -0.887583 -0.361193 +vn -0.246692 -0.822374 -0.512684 +vn 0.677715 0.724116 0.127903 +vn -0.099600 0.947001 0.305400 +vn 0.768327 -0.460616 -0.444416 +vn 0.970788 0.144698 -0.191398 +vn 0.155805 -0.820826 -0.549517 +vn -0.495284 -0.683678 -0.535983 +vn -0.288895 -0.814085 -0.503791 +vn -0.412722 -0.035902 -0.910149 +vn -0.956305 -0.112401 -0.269901 +vn -0.338608 0.562314 -0.754419 +vn -0.807394 0.574196 0.135699 +vn -0.338608 0.562314 -0.754419 +vn -0.807394 0.574196 0.135699 +vn -0.115700 0.899604 0.421102 +vn 0.637073 0.725669 0.259889 +vn 0.664397 0.678497 0.313399 +vn -0.126101 0.843508 0.522105 +vn 0.970164 0.189293 -0.151494 +vn 0.787597 -0.336499 -0.516198 +vn 0.169702 -0.710808 -0.682608 +vn 0.202006 -0.582916 -0.787021 +vn -0.619323 -0.570022 -0.539920 +vn -0.665794 -0.526296 -0.528896 +vn 0.164497 -0.721688 -0.672389 +vn -0.996428 -0.036601 -0.076102 +vn -0.791286 0.522191 0.318094 +vn 0.598698 0.705097 0.379999 +vn -0.215209 0.761233 0.611727 +vn 0.967120 0.161203 -0.196704 +vn 0.762863 -0.296686 -0.574472 +vn 0.078703 -0.598923 -0.796930 +vn -0.623784 -0.510687 -0.591685 +vn -0.627298 -0.532799 -0.567999 +vn -0.997088 -0.075399 -0.011400 +vn -0.791286 0.522191 0.318094 +vn -0.804539 0.425020 0.414820 +vn -0.804539 0.425020 0.414820 +vn 0.336487 0.809469 0.481182 +vn 0.326111 0.627622 0.706924 +vn 0.728901 -0.681401 0.066300 +vn -0.321009 -0.886524 -0.333209 +vn -0.245904 -0.855915 -0.454908 +vn 0.219403 -0.937212 -0.271103 +vn 0.294389 0.307489 -0.904868 +vn -0.127203 -0.281406 -0.951120 +vn 0.294389 0.307489 -0.904868 +vn -0.678207 0.575006 -0.457605 +vn -0.688780 0.417788 -0.592483 +vn -0.954350 0.286985 0.082796 +vn -0.778936 0.626029 -0.036702 +vn -0.074198 0.977168 0.199093 +vn 0.925919 0.009300 -0.377608 +vn 0.971858 -0.200291 -0.123995 +vn 0.763291 0.643692 0.055199 +vn 0.631592 0.775190 0.013100 +vn 0.639823 -0.610422 -0.466917 +vn 0.864995 -0.486997 -0.120899 +vn 0.528590 -0.783286 0.327194 +vn 0.370193 -0.767885 -0.522790 +vn 0.705632 -0.478922 -0.522224 +vn -0.917709 -0.135301 0.373503 +vn -0.800107 0.205102 -0.563705 +vn -0.823951 -0.244015 -0.511431 +vn -0.405893 -0.913384 -0.031299 +vn -0.311890 -0.719276 0.620779 +vn -0.992425 -0.122003 -0.014400 +vn 0.727887 -0.217496 -0.650289 +vn 0.705632 -0.478922 -0.522224 +vn -0.800107 0.205102 -0.563705 +vn -0.688780 0.417788 -0.592483 +vn -0.363394 -0.776687 -0.514492 +vn 0.370193 -0.767885 -0.522790 +vn -0.363394 -0.776687 -0.514492 +vn -0.823951 -0.244015 -0.511431 +vn 0.531711 -0.497210 -0.685614 +vn -0.618997 -0.035400 -0.784596 +vn -0.001700 0.896377 -0.443289 +vn 0.651583 0.549286 -0.523187 +vn 0.651583 0.549286 -0.523187 +vn -0.001700 0.896377 -0.443289 +vn -0.074198 0.977168 0.199093 +vn 0.941480 -0.217395 -0.257595 +vn 0.531711 -0.497210 -0.685614 +vn 0.941480 -0.217395 -0.257595 +vn -0.678207 0.575006 -0.457605 +vn -0.189897 0.967185 0.168797 +vn -0.001700 0.896377 -0.443289 +vn -0.618997 -0.035400 -0.784596 +vn -0.861400 0.505400 0.050600 +vn -0.892086 0.422893 0.159198 +vn -0.170095 0.946975 0.272593 +vn -0.189897 0.967185 0.168797 +vn -0.316994 -0.694788 -0.645589 +vn -0.948656 -0.242314 -0.203312 +vn 0.641406 0.766407 0.034900 +vn -0.170095 0.946975 0.272593 +vn -0.176202 0.920710 0.348204 +vn 0.632066 0.759359 0.154492 +vn 0.928484 0.118098 -0.352094 +vn 0.957696 0.109100 -0.266299 +vn 0.557307 -0.513206 -0.652708 +vn 0.572626 -0.533324 -0.622628 +vn -0.258787 -0.684866 -0.681166 +vn -0.886163 0.427282 0.179292 +vn -0.926923 -0.258506 -0.272007 +vn 0.727887 -0.217496 -0.650289 +vn -0.176202 0.920710 0.348204 +vn 0.309210 0.285809 0.907029 +vn 0.405689 0.345790 0.846077 +vn -0.528118 -0.842529 -0.106004 +vn -0.433902 -0.898705 -0.063700 +vn -0.330609 -0.943426 -0.025401 +vn -0.512134 -0.851857 -0.109807 +vn -0.020200 -0.630295 0.776093 +vn 0.180496 -0.247295 0.951980 +vn 0.428419 -0.605126 0.671029 +vn 0.583495 -0.248098 0.773293 +vn -0.079002 -0.987122 0.139103 +vn -0.195798 -0.979688 0.043299 +vn 0.079902 -0.973625 0.213705 +vn -0.055900 0.472404 0.879608 +vn 0.245312 0.627531 0.738936 +vn 0.445703 0.439603 0.779806 +vn 0.542388 0.486989 0.684585 +vn 0.449519 0.487321 0.748632 +vn 0.374713 0.487016 0.788927 +vn 0.456397 0.468097 0.756695 +vn 0.456001 0.435401 0.776202 +vn 0.289905 -0.866715 0.405907 +vn -0.132897 -0.852181 0.506089 +vn -0.329895 -0.912685 0.241196 +vn -0.433687 -0.854774 0.285091 +vn 0.319105 -0.776313 0.543609 +vn 0.521204 -0.443403 0.729205 +vn 0.353501 0.137300 0.925303 +vn 0.468919 0.232709 0.852034 +vn 0.364095 0.648192 0.668791 +vn 0.212507 -0.972432 0.096003 +vn 0.442994 -0.755489 0.482693 +vn 0.429888 -0.410089 0.804378 +vn -0.530884 -0.816976 -0.225193 +vn -0.425708 -0.876516 -0.224704 +vn -0.156396 -0.979676 -0.125597 +vn 0.521494 -0.751391 0.404295 +vn 0.435591 -0.772784 0.461590 +vn 0.490413 -0.307808 0.815322 +vn 0.596894 -0.352997 0.720493 +vn 0.435312 0.509814 0.742020 +vn 0.554092 0.386695 0.737190 +vn 0.248613 -0.966749 -0.059903 +vn 0.499900 0.691400 0.521600 +vn -0.502399 -0.731299 -0.461299 +vn -0.412686 -0.761975 -0.499084 +vn -0.172796 -0.900482 -0.399092 +vn -0.221509 -0.824932 -0.520020 +vn -0.121604 -0.991437 -0.047602 +vn -0.147100 -0.978701 -0.143200 +vn -0.425687 -0.762076 -0.487885 +vn -0.302713 -0.940442 -0.154707 +vn 0.015701 -0.990040 -0.139906 +vn 0.000700 -0.845919 -0.533312 +vn 0.128400 -0.899699 -0.417199 +vn -0.577870 -0.621068 -0.529473 +vn -0.561306 -0.719707 -0.408604 +vn -0.122201 -0.985809 -0.115101 +vn 0.172695 -0.788879 -0.589785 +vn 0.187090 -0.749459 -0.635065 +vn -0.239710 -0.454219 -0.858035 +vn -0.214513 -0.540032 -0.813849 +vn -0.070803 -0.582925 -0.809435 +vn -0.394995 -0.509294 -0.764591 +vn -0.270799 -0.506199 -0.818798 +vn -0.643751 -0.594954 -0.481263 +vn 0.210010 -0.826039 -0.523025 +vn -0.437600 -0.874900 -0.207500 +vn 0.151003 -0.739313 -0.656212 +vn -0.670041 -0.632038 -0.389324 +vn -0.129401 -0.961407 0.242802 +vn -0.224794 -0.946874 0.229994 +vn 0.879117 0.244005 0.409408 +vn 0.954707 -0.246802 0.166201 +vn 0.692495 -0.720895 -0.027600 +vn 0.127407 0.848744 0.513227 +vn 0.865296 0.197999 -0.460498 +vn 0.082500 0.216900 -0.972701 +vn 0.879117 0.244005 0.409408 +vn 0.127407 0.848744 0.513227 +vn 0.856574 0.507884 -0.091297 +vn -0.786482 0.593986 -0.169196 +vn -0.497498 0.576598 0.648098 +vn -0.483376 0.757162 0.439378 +vn -0.853033 0.185907 0.487619 +vn -0.618617 0.158104 0.769621 +vn -0.516389 0.344093 0.784183 +vn -0.687190 -0.011300 0.726390 +vn -0.925433 0.056902 0.374613 +vn -0.481696 0.589096 0.648795 +vn -0.299807 0.577014 0.759718 +vn -0.160403 0.857118 0.489510 +vn -0.294513 0.335715 0.894739 +vn -0.372014 0.129605 0.919134 +vn -0.498406 -0.082501 0.863010 +vn 0.128001 0.558004 0.819907 +vn 0.133205 0.859532 0.493418 +vn -0.108798 0.571889 0.813084 +vn 0.105997 0.250593 0.962272 +vn -0.109604 0.292911 0.949837 +vn -0.056301 -0.193805 0.979423 +vn -0.154795 0.105796 0.982266 +vn -0.024800 -0.199401 0.979604 +vn -0.308499 -0.110400 0.944797 +vn -0.506992 0.844186 0.174097 +vn -0.086003 0.970531 0.225107 +vn -0.582609 0.812713 0.008000 +vn -0.887246 0.458424 -0.051403 +vn -0.132603 0.991124 0.009500 +vn 0.862502 0.055500 -0.503001 +vn 0.244003 0.586908 -0.772011 +vn 0.806564 0.590474 0.028199 +vn 0.504290 0.550289 0.665487 +vn -0.159404 0.881223 0.445012 +vn -0.489787 0.624384 0.608484 +vn -0.349996 0.312497 0.883090 +vn 0.044298 -0.315587 0.947862 +vn 0.043101 0.991420 -0.123403 +vn -0.161606 0.945134 0.283910 +vn -0.279987 0.882460 -0.377983 +vn -0.073003 0.239911 -0.968046 +vn -0.006900 -0.268396 -0.963284 +vn 0.369304 0.908209 -0.196902 +vn -0.065000 0.964595 -0.255599 +vn 0.043101 0.527716 -0.848326 +vn 0.399710 0.395310 -0.827020 +vn -0.153198 0.840690 0.519394 +vn -0.757827 0.137405 0.637823 +vn 0.369304 0.908209 -0.196902 +vn 0.399710 0.395310 -0.827020 +vn 0.021900 -0.280897 -0.959488 +vn 0.649072 -0.503479 -0.570276 +vn -0.256499 -0.054700 -0.964996 +vn 0.021900 -0.280897 -0.959488 +vn -0.153198 0.840690 0.519394 +vn -0.575718 0.761524 0.297709 +vn -0.757827 0.137405 0.637823 +vn -0.828312 0.389005 0.403206 +vn -0.533100 0.157300 0.831301 +vn -0.803911 -0.109901 0.584508 +vn 0.025600 -0.898902 0.437401 +vn -0.741206 -0.534504 0.406103 +vn -0.863807 -0.496704 -0.084401 +vn 0.260008 -0.935329 -0.239907 +vn -0.599300 -0.789399 -0.133000 +vn -0.546715 -0.456213 -0.702120 +vn -0.387301 -0.261800 -0.884002 +vn -0.006900 -0.268396 -0.963284 +vn -0.799911 -0.599408 0.029200 +vn -0.367794 -0.727587 -0.579090 +vn 0.649072 -0.503479 -0.570276 +vn 0.021900 -0.280897 -0.959488 +vn -0.256499 -0.054700 -0.964996 +vn -0.625015 -0.301307 -0.720118 +vn -0.367794 -0.727587 -0.579090 +vn 0.021900 -0.280897 -0.959488 +vn -0.799911 -0.599408 0.029200 +vn -0.972716 -0.230304 -0.028000 +vn 0.244003 0.586908 -0.772011 +vn -0.073003 0.239911 -0.968046 +vn -0.841176 0.442388 -0.310991 +vn -0.489787 0.624384 0.608484 +vn -0.159404 0.881223 0.445012 +vn -0.161606 0.945134 0.283910 +vn 0.043101 0.991420 -0.123403 +vn -0.279987 0.882460 -0.377983 +vn -0.006900 -0.268396 -0.963284 +vn -0.387301 -0.261800 -0.884002 +vn -0.546715 -0.456213 -0.702120 +vn -0.803911 -0.109901 0.584508 +vn -0.533100 0.157300 0.831301 +vn -0.349996 0.312497 0.883090 +vn -0.863807 -0.496704 -0.084401 +vn -0.741206 -0.534504 0.406103 +vn -0.599300 -0.789399 -0.133000 +vn -0.215907 0.911332 -0.350512 +vn -0.117106 0.555927 -0.822941 +vn -0.288298 -0.016800 -0.957393 +vn -0.577505 0.816007 0.024900 +vn -0.915941 0.400818 0.019901 +vn -0.288298 -0.016800 -0.957393 +vn -0.714787 -0.090298 -0.693488 +vn -0.977537 -0.144206 0.153706 +vn -0.731998 0.661298 0.163900 +vn -0.996781 -0.079498 0.010400 +vn -0.968525 0.228206 -0.099403 +vn -0.166098 0.974387 -0.151598 +vn -0.679179 0.101097 -0.726977 +vn -0.534213 0.062702 -0.843021 +vn -0.468007 0.154502 -0.870114 +vn -0.554579 0.427083 -0.714172 +vn -0.554579 0.427083 -0.714172 +vn -0.259910 0.053702 -0.964138 +vn -0.554579 0.427083 -0.714172 +vn -0.468007 0.154502 -0.870114 +vn -0.259910 0.053702 -0.964138 +vn -0.982984 0.113798 -0.144198 +vn -0.998465 -0.048698 0.026399 +vn -0.948432 -0.315811 -0.027201 +vn -0.769456 0.049697 -0.636763 +vn -0.892983 0.119298 -0.433992 +vn -0.640927 0.115705 -0.758832 +vn -0.996781 -0.079498 0.010400 +vn -0.998779 -0.039399 -0.029799 +vn -0.968603 0.248501 -0.007500 +vn -0.968525 0.228206 -0.099403 +vn -0.997192 -0.074599 -0.006500 +vn -0.993951 -0.073396 0.081696 +vn -0.647025 0.182407 -0.740329 +vn -0.968603 0.248501 -0.007500 +vn -0.959090 0.171298 0.225398 +vn -0.853033 0.185907 0.487619 +vn -0.959090 0.171298 0.225398 +vn -0.925433 0.056902 0.374613 +vn -0.428882 -0.220391 -0.876064 +vn -0.489412 -0.594914 -0.637615 +vn -0.450696 -0.085299 -0.888593 +vn -0.525925 -0.080204 -0.846741 +vn -0.892496 -0.448298 0.049800 +vn -0.660791 -0.748690 0.053099 +vn -0.701313 -0.711813 0.038501 +vn -0.914343 -0.397119 0.079204 +vn -0.560097 -0.403898 -0.723296 +vn -0.010000 -0.011400 0.999885 +vn -0.554579 0.427083 -0.714172 +vn -0.763613 -0.622810 0.170303 +vn -0.679179 0.101097 -0.726977 +vn -0.537912 -0.762216 0.360108 +vn -0.561189 -0.774885 0.290894 +vn -0.944692 -0.310697 0.104999 +vn -0.873511 -0.434305 0.219903 +vn -0.757599 -0.617299 0.212100 +vn -0.709809 -0.622308 0.330004 +vn -0.545415 -0.803723 -0.237807 +vn -0.923532 -0.151305 -0.352412 +vn -0.787508 -0.589706 -0.179102 +vn -0.721142 -0.101306 -0.685340 +vn -0.706596 -0.415897 -0.572496 +vn -0.627485 -0.730282 -0.270093 +vn -0.536764 -0.842244 0.050097 +vn -0.712770 -0.676371 0.185692 +vn -0.902826 -0.397212 0.164705 +vn -0.233606 -0.972325 -0.003400 +vn -0.494018 -0.869432 0.005800 +vn -0.848178 -0.505287 0.158996 +vn -0.442372 -0.856946 0.264483 +vn -0.458094 -0.878189 0.137598 +vn -0.685291 -0.676291 0.270197 +vn -0.589990 -0.729188 0.346694 +vn -0.611288 -0.756885 0.231196 +vn -0.209800 -0.247500 0.945900 +vn -0.600682 0.406588 0.688380 +vn -0.717222 -0.184706 -0.671920 +vn -0.872572 0.285891 -0.396087 +vn -0.844523 0.525715 0.102003 +vn -0.209800 -0.247500 0.945900 +vn -0.996781 -0.079498 0.010400 +vn -0.731998 0.661298 0.163900 +vn -0.600682 0.406588 0.688380 +vn -0.769456 0.049697 -0.636763 +vn -0.717222 -0.184706 -0.671920 +vn 0.493570 0.618862 -0.611063 +vn -0.647025 0.182407 -0.740329 +vn -0.872572 0.285891 -0.396087 +vn -0.844523 0.525715 0.102003 +vn 0.361602 0.917705 -0.164501 +vn -0.769456 0.049697 -0.636763 +vn -0.968993 0.177199 0.172199 +vn -0.790432 -0.147106 -0.594624 +vn -0.834902 -0.515601 -0.192600 +vn -0.970740 -0.230710 0.066603 +vn -0.857205 -0.390602 0.335602 +vn -0.790768 -0.180893 0.584777 +vn -0.733310 -0.665409 0.139602 +vn -0.668010 -0.650110 0.362105 +vn -0.900433 0.125105 0.416615 +vn -0.369602 -0.459003 0.807905 +vn -0.688712 0.189203 0.699912 +vn -0.029500 0.052300 0.998196 +vn -0.654289 -0.734487 -0.180097 +vn -0.459020 -0.821035 0.339415 +vn -0.456517 -0.811931 0.363814 +vn -0.447407 -0.791212 0.416906 +vn -0.536705 -0.839707 0.082701 +vn -0.481181 -0.853667 0.199292 +vn -0.560613 -0.827719 -0.024401 +vn -0.429593 -0.767387 0.475992 +vn -0.468283 -0.743773 0.476983 +vn -0.479306 -0.590808 0.649008 +vn -0.349900 -0.674300 0.650300 +vn -0.590176 -0.572676 0.568977 +vn -0.557703 -0.426802 0.711904 +vn -0.281313 -0.588427 0.758035 +vn -0.380400 -0.526101 0.760601 +vn 0.326806 0.803915 0.496909 +vn 0.466696 0.663395 0.584895 +vn 0.404408 0.676914 0.615013 +vn 0.178701 0.808504 0.560703 +vn 0.260406 0.915422 0.306907 +vn 0.562572 0.628969 0.536573 +vn -0.307117 0.880249 0.361720 +vn 0.522382 0.615279 0.590380 +vn -0.345325 0.810958 0.472334 +vn 0.151396 0.986776 -0.057899 +vn -0.780046 0.530931 0.331120 +vn -0.253892 0.687477 0.680378 +vn -0.174400 0.711500 0.680700 +vn -0.626976 0.307288 0.715873 +vn -0.754789 0.332095 0.565692 +vn 0.185993 0.734274 0.652877 +vn 0.156002 0.808313 0.567709 +vn 0.312299 0.655498 0.687598 +vn 0.255404 0.748311 0.612209 +vn -0.155095 0.831276 0.533785 +vn -0.550895 0.537795 0.638194 +vn 0.092898 0.864378 0.494187 +vn 0.214497 0.805789 0.551992 +vn -0.187991 0.825758 0.531773 +vn -0.556500 0.509100 0.656600 +vn 0.046399 0.846483 0.530390 +vn 0.194608 0.812835 0.549024 +vn -0.705889 -0.461793 -0.537092 +vn -0.734202 -0.603202 -0.311601 +vn -0.838186 0.062499 -0.541791 +vn -0.043399 0.632086 0.773682 +vn -0.298006 0.554611 0.776916 +vn -0.523299 0.273700 0.806999 +vn -0.786710 -0.094101 0.610108 +vn -0.727017 0.116203 0.676715 +vn -0.677072 -0.513578 0.527078 +vn 0.151396 0.986776 -0.057899 +vn 0.833865 -0.479280 -0.273788 +vn 0.976282 0.169497 0.134698 +vn 0.654671 0.752666 0.069997 +vn -0.300210 -0.948532 -0.100803 +vn 0.318994 -0.815985 -0.482091 +vn 0.581692 0.498193 0.642991 +vn 0.260406 0.915422 0.306907 +vn -0.150894 0.549179 -0.821969 +vn -0.315984 0.723663 -0.613569 +vn 0.821965 0.239190 -0.516878 +vn 0.421600 0.825699 0.374800 +vn -0.672806 0.548105 -0.496904 +vn -0.599813 0.696915 -0.393108 +vn -0.277797 0.570093 0.773190 +vn -0.774072 -0.200893 0.600378 +vn -0.628906 0.398304 -0.667706 +vn -0.688670 -0.417982 -0.592474 +vn -0.245104 0.535008 -0.808512 +vn 0.244509 -0.474117 -0.845830 +vn 0.180507 0.981336 0.066302 +vn 0.436118 0.890638 0.128705 +vn -0.082303 -0.975441 0.204308 +vn 0.244509 -0.474117 -0.845830 +vn 0.094195 0.344483 0.934055 +vn 0.034500 0.452499 0.891097 +vn -0.461297 0.045200 0.886094 +vn -0.419515 -0.005500 0.907732 +vn -0.385504 -0.918510 -0.087901 +vn 0.238514 -0.840650 -0.486229 +vn 0.610816 0.561914 0.557814 +vn -0.642196 -0.536397 0.547597 +vn 0.820030 -0.383514 -0.424815 +vn 0.966556 0.253715 0.037402 +vn -0.196508 0.839536 0.506522 +vn -0.732489 0.492892 0.469593 +vn -0.768601 -0.476901 -0.426401 +vn -0.151693 -0.434181 -0.887962 +vn 0.512207 -0.040401 -0.857911 +vn 0.820030 -0.383514 -0.424815 +vn 0.373485 0.903463 0.210391 +vn -0.981988 -0.076199 0.172898 +vn 0.512207 -0.040401 -0.857911 +vn 0.733631 0.585625 -0.344715 +vn 0.684718 0.342309 -0.643417 +vn 0.433187 0.407988 -0.803676 +vn -0.529406 -0.407105 0.744309 +vn -0.245104 0.535008 -0.808512 +vn 0.144605 -0.675624 -0.722926 +vn -0.353109 -0.934224 0.050401 +vn -0.572815 -0.222106 0.789020 +vn -0.301801 0.651001 0.696501 +vn 0.432589 -0.033299 -0.900976 +vn 0.572997 -0.010100 -0.819495 +vn 0.372221 0.926652 -0.052603 +vn 0.328604 0.933410 -0.144102 +vn 0.328604 0.933410 -0.144102 +vn 0.372221 0.926652 -0.052603 +vn -0.228903 0.816710 0.529706 +vn -0.223402 0.807108 0.546505 +vn -0.220696 -0.155397 0.962884 +vn -0.328996 0.070599 0.941689 +vn 0.100097 -0.974875 0.198995 +vn -0.064899 -0.993388 0.094699 +vn -0.064899 -0.993388 0.094699 +vn 0.100097 -0.974875 0.198995 +vn 0.150400 -0.714098 -0.683698 +vn 0.114595 -0.766470 -0.631975 +vn 0.735317 0.260606 -0.625614 +vn 0.009400 0.887155 -0.461376 +vn 0.009400 0.887155 -0.461376 +vn -0.557929 0.814843 -0.157308 +vn -0.554875 0.827763 -0.083196 +vn -0.667089 0.694989 0.268296 +vn -0.923778 0.362091 0.124597 +vn -0.224001 0.934602 -0.276301 +vn -0.837048 -0.347720 -0.422424 +vn -0.472606 -0.873712 -0.115202 +vn 0.181506 -0.861330 -0.474517 +vn -0.730535 -0.131306 0.670132 +vn -0.329393 -0.716786 0.614588 +vn 0.803895 0.572397 -0.161599 +vn 0.470102 -0.803103 -0.366101 +vn -0.600397 -0.584397 -0.545897 +vn -0.838392 0.062299 0.541495 +vn -0.838392 0.062299 0.541495 +vn -0.600397 -0.584397 -0.545897 +vn 0.669059 -0.209887 -0.712957 +vn 0.868202 -0.163500 0.468501 +vn 0.273808 -0.836125 -0.475314 +vn 0.646507 0.047801 -0.761409 +vn 0.897378 0.122597 0.423890 +vn 0.296303 -0.821009 -0.488005 +vn 0.083300 0.507197 -0.857795 +vn -0.787932 0.482620 -0.382416 +vn -0.115401 -0.906108 0.407003 +vn -0.921826 -0.371310 0.111203 +vn -0.049700 -0.643897 0.763496 +vn 0.894491 -0.255197 -0.367096 +vn 0.940198 0.340299 0.015000 +vn 0.083300 0.507197 -0.857795 +vn -0.554875 0.827763 -0.083196 +vn -0.649898 -0.083700 -0.755398 +vn 0.646507 0.047801 -0.761409 +vn 0.296303 -0.821009 -0.488005 +vn -0.340099 -0.918196 0.203099 +vn 0.240697 -0.362695 0.900287 +vn 0.941315 0.014800 0.337205 +vn 0.264694 0.363792 -0.893080 +vn 0.264694 0.363792 -0.893080 +vn -0.644396 0.313898 -0.697296 +vn -0.554875 0.827763 -0.083196 +vn -0.911958 -0.359184 -0.198291 +vn -0.130598 -0.704592 0.697492 +vn 0.952157 0.088596 0.292487 +vn -0.467578 -0.139594 -0.872860 +vn 0.669059 -0.209887 -0.712957 +vn 0.273808 -0.836125 -0.475314 +vn -0.276009 -0.960730 0.028601 +vn 0.224192 -0.484683 0.845470 +vn -0.123396 -0.552283 0.824474 +vn -0.407509 -0.766817 0.495911 +vn -0.923778 0.362091 0.124597 +vn -0.837048 -0.347720 -0.422424 +vn -0.029299 -0.587675 -0.808566 +vn 0.340200 0.387500 -0.856801 +vn 0.784738 0.355817 -0.507524 +vn -0.185902 0.965110 -0.184402 +vn -0.730535 -0.131306 0.670132 +vn -0.029299 -0.587675 -0.808566 +vn 0.470102 -0.803103 -0.366101 +vn -0.921826 -0.371310 0.111203 +vn -0.340099 -0.918196 0.203099 +vn -0.911958 -0.359184 -0.198291 +vn -0.276009 -0.960730 0.028601 +vn -0.508310 0.817917 -0.269505 +vn -0.620862 0.719857 -0.310381 +vn -0.724789 0.305795 -0.617390 +vn -0.472673 0.379879 -0.795155 +vn -0.248695 0.478590 -0.842082 +vn -0.292008 0.840324 -0.456713 +vn -0.773277 0.511885 -0.374189 +vn -0.248695 0.478590 -0.842082 +vn -0.117600 0.713999 -0.690199 +vn 0.432589 -0.033299 -0.900976 +vn 0.328604 0.933410 -0.144102 +vn 0.144605 -0.675624 -0.722926 +vn -0.223402 0.807108 0.546505 +vn -0.328996 0.070599 0.941689 +vn -0.064899 -0.993388 0.094699 +vn 0.114595 -0.766470 -0.631975 +vn 0.114595 -0.766470 -0.631975 +vn 0.151396 0.986776 -0.057899 +vn 0.833865 -0.479280 -0.273788 +vn -0.527103 0.690104 0.495903 +vn 0.205302 0.714807 0.668507 +vn 0.132205 0.903533 0.407615 +vn -0.647130 0.724733 0.236611 +vn 0.339511 0.815326 -0.469015 +vn 0.052903 0.993549 -0.100305 +vn -0.237999 0.937897 -0.252399 +vn -0.768399 0.634799 -0.081200 +vn -0.999191 0.008100 -0.039400 +vn -0.991408 0.086301 0.098301 +vn -0.758366 -0.648071 -0.069897 +vn -0.883572 -0.447286 -0.138696 +vn -0.461607 -0.849912 -0.254104 +vn -0.362907 -0.877217 -0.314306 +vn -0.246692 -0.822374 -0.512684 +vn 0.463186 0.800476 -0.380389 +vn -0.293010 0.897130 -0.330611 +vn -0.870646 0.454572 -0.187988 +vn -0.970650 -0.239612 -0.020601 +vn -0.557912 -0.826718 0.072602 +vn 0.100202 -0.993418 0.055501 +vn 0.787125 -0.600319 -0.141604 +vn 0.154896 -0.373089 -0.914774 +vn -0.193799 -0.973097 -0.124600 +vn 0.923339 0.221709 -0.313513 +vn 0.644791 0.252096 -0.721590 +vn 0.644791 0.252096 -0.721590 +vn 0.923339 0.221709 -0.313513 +vn 0.458400 0.837900 -0.296300 +vn 0.453424 0.859945 -0.234312 +vn -0.355610 0.905227 -0.232607 +vn -0.304091 0.902772 -0.304191 +vn -0.899121 0.391909 -0.194905 +vn -0.952335 -0.294511 -0.079503 +vn -0.482720 -0.874937 -0.038302 +vn -0.504210 -0.861618 -0.058201 +vn 0.282099 -0.959297 -0.013000 +vn -0.527112 -0.849219 0.031301 +vn 0.367299 -0.925197 -0.095400 +vn 0.919940 -0.383216 -0.082804 +vn 0.934514 0.309705 -0.175403 +vn 0.476001 0.852201 -0.217200 +vn -0.415589 0.868276 -0.270893 +vn -0.956466 0.225392 -0.185393 +vn -0.906237 -0.421417 -0.033801 +vn -0.334718 -0.938752 0.081905 +vn 0.368281 -0.924951 0.093995 +vn 0.349583 -0.936255 -0.034898 +vn 0.918264 -0.395784 0.012100 +vn 0.934514 0.309705 -0.175403 +vn 0.950608 0.288502 -0.114501 +vn 0.950608 0.288502 -0.114501 +vn -0.966728 0.107103 0.232307 +vn -0.519102 -0.820002 -0.241101 +vn -0.907621 -0.418210 -0.036401 +vn 0.898955 0.246088 -0.362382 +vn 0.963793 0.258098 0.066999 +vn 0.974688 -0.124798 0.185498 +vn 0.868376 0.111597 -0.483187 +vn 0.425480 0.856159 0.293186 +vn -0.892970 0.037099 -0.448585 +vn -0.575286 0.753482 -0.318292 +vn -0.416093 0.905884 0.078999 +vn -0.957711 0.216602 -0.189402 +vn -0.794675 -0.560582 -0.232893 +vn -0.976401 -0.104500 -0.189000 +vn -0.835356 -0.519435 0.179912 +vn -0.764983 -0.144697 -0.627586 +vn -0.575976 -0.548677 -0.605975 +vn 0.745327 -0.503318 0.437216 +vn 0.716184 -0.550888 -0.428490 +vn 0.881708 -0.126201 -0.454604 +vn -0.079799 -0.876688 0.474394 +vn -0.003400 -0.989660 0.143394 +vn 0.839011 -0.535507 0.096401 +vn -0.667290 0.105498 -0.737289 +vn 0.868376 0.111597 -0.483187 +vn 0.881708 -0.126201 -0.454604 +vn -0.764983 -0.144697 -0.627586 +vn 0.083402 -0.851722 -0.517314 +vn 0.716184 -0.550888 -0.428490 +vn 0.083402 -0.851722 -0.517314 +vn -0.575976 -0.548677 -0.605975 +vn -0.593906 -0.246603 -0.765808 +vn -0.311183 0.746159 -0.588568 +vn 0.418790 0.804181 -0.421790 +vn 0.637700 -0.296100 -0.711100 +vn -0.311183 0.746159 -0.588568 +vn 0.425480 0.856159 0.293186 +vn 0.418790 0.804181 -0.421790 +vn -0.913379 0.178296 -0.365992 +vn -0.593906 -0.246603 -0.765808 +vn -0.913379 0.178296 -0.365992 +vn 0.898955 0.246088 -0.362382 +vn 0.405393 0.912785 0.049799 +vn 0.418790 0.804181 -0.421790 +vn 0.637700 -0.296100 -0.711100 +vn 0.989347 0.127593 -0.070096 +vn 0.405393 0.912785 0.049799 +vn 0.384916 0.831135 -0.401317 +vn 0.965410 0.175202 -0.193102 +vn 0.821477 -0.567284 -0.057998 +vn 0.057300 -0.995893 -0.070100 +vn -0.481973 0.769357 -0.419276 +vn -0.446323 0.828943 -0.337118 +vn 0.400097 0.851193 -0.339697 +vn 0.384916 0.831135 -0.401317 +vn -0.952029 0.123204 -0.280109 +vn -0.959978 0.174596 -0.218995 +vn -0.761999 -0.635399 -0.125000 +vn -0.774898 -0.624599 -0.097000 +vn -0.013300 -0.995781 -0.090798 +vn 0.963167 0.190493 -0.189793 +vn 0.789341 -0.606931 -0.092605 +vn -0.667290 0.105498 -0.737289 +vn 0.400097 0.851193 -0.339697 +vn 0.382497 0.202499 0.901493 +vn -0.578191 -0.815687 -0.018700 +vn -0.539128 -0.839343 -0.069604 +vn -0.020699 -0.169194 0.985365 +vn -0.241000 -0.510200 0.825600 +vn -0.151696 0.208094 0.966274 +vn -0.469383 -0.100396 0.877269 +vn -0.581414 -0.788920 0.198905 +vn -0.670227 -0.710329 0.215009 +vn -0.546903 -0.836404 -0.036400 +vn 0.356486 0.493280 0.793469 +vn 0.343598 0.414898 0.842496 +vn -0.126804 0.299509 0.945629 +vn 0.352111 0.647521 0.675822 +vn 0.499304 0.406203 0.765307 +vn 0.446885 0.537283 0.715277 +vn -0.668303 -0.464802 0.580802 +vn -0.409912 -0.699721 0.585118 +vn -0.494801 -0.825702 0.270901 +vn -0.687289 -0.377494 0.620590 +vn -0.469294 0.009700 0.882989 +vn 0.207510 0.186209 0.960347 +vn 0.330104 0.494406 0.804110 +vn -0.201894 0.344090 0.916974 +vn -0.776808 -0.222802 0.589006 +vn -0.804627 -0.584619 0.103903 +vn -0.478398 0.036200 0.877397 +vn -0.540393 -0.823289 -0.173698 +vn -0.605078 -0.783672 -0.140495 +vn -0.848701 -0.193000 0.492400 +vn -0.567880 0.163194 0.806771 +vn -0.497904 0.068701 0.864507 +vn -0.799108 -0.274803 0.534705 +vn -0.201397 0.477593 0.855187 +vn -0.162599 0.378297 0.911292 +vn -0.829123 -0.558915 -0.013000 +vn 0.112599 0.719892 0.684892 +vn -0.555610 -0.719713 -0.416307 +vn -0.614099 -0.727199 -0.306699 +vn 0.175598 -0.979691 0.096799 +vn 0.098901 -0.875708 0.472604 +vn -0.054800 -0.855498 0.514899 +vn -0.060901 -0.799618 0.597413 +vn -0.038299 -0.991481 0.124498 +vn -0.212603 -0.818312 0.534008 +vn -0.365300 -0.890600 0.270900 +vn -0.258392 -0.955769 0.140495 +vn 0.336901 -0.940904 -0.034600 +vn 0.330283 -0.935952 0.122094 +vn -0.073902 -0.836920 0.542313 +vn -0.426394 -0.901786 0.070499 +vn -0.443301 -0.896302 0.011300 +vn -0.204610 -0.948045 -0.243611 +vn -0.060000 -0.957300 -0.282800 +vn -0.030800 -0.927199 -0.373300 +vn -0.001300 -0.950211 -0.311604 +vn 0.129299 -0.952595 -0.275399 +vn 0.415095 -0.909590 -0.018500 +vn -0.454209 -0.878617 0.147403 +vn 0.228405 -0.895319 0.382408 +vn -0.411421 -0.911347 -0.013401 +vn 0.453810 -0.888020 0.074002 +vn 0.014100 -0.601699 0.798599 +vn 0.105501 -0.624204 0.774105 +vn -0.497498 0.576598 0.648098 +vn 0.435494 0.773790 0.459994 +vn -0.905953 -0.339182 0.253387 +vn -0.853033 0.185907 0.487619 +vn -0.290303 0.308603 -0.905808 +vn -0.786482 0.593986 -0.169196 +vn 0.435494 0.773790 0.459994 +vn -0.497498 0.576598 0.648098 +vn -0.521918 0.838328 0.157505 +vn -0.925433 0.056902 0.374613 +vn -0.764390 -0.244997 0.596393 +vn -0.687190 -0.011300 0.726390 +vn -0.689094 -0.304497 0.657595 +vn -0.498406 -0.082501 0.863010 +vn -0.849582 -0.167396 0.500189 +vn -0.816099 -0.376199 0.438699 +vn -0.524309 -0.362506 0.770513 +vn -0.165603 -0.545309 0.821714 +vn -0.024800 -0.199401 0.979604 +vn -0.308499 -0.110400 0.944797 +vn -0.280393 -0.807679 0.518686 +vn -0.646318 -0.574416 0.502314 +vn -0.780532 -0.609625 0.138306 +vn -0.637415 -0.755917 0.149303 +vn 0.133205 0.859532 0.493418 +vn 0.128001 0.558004 0.819907 +vn 0.105997 0.250593 0.962272 +vn 0.333587 -0.513381 0.790670 +vn 0.176002 -0.621107 0.763708 +vn 0.259000 -0.265400 0.928699 +vn 0.104103 -0.904527 0.413513 +vn -0.340178 -0.921440 0.187688 +vn -0.125599 -0.979290 0.158798 +vn 0.877811 -0.470206 0.091401 +vn 0.805284 -0.379293 0.455691 +vn 0.784287 -0.575790 0.230996 +vn 0.644611 -0.375906 0.665711 +vn 0.703685 -0.534089 0.468590 +vn 0.650717 -0.721219 0.237506 +vn 0.545211 -0.621913 0.562111 +vn 0.461589 -0.863579 0.202895 +vn 0.529225 -0.306114 0.791337 +vn 0.345803 -0.930908 0.117601 +vn 0.500296 -0.864194 -0.053600 +vn 0.530209 -0.813513 -0.238904 +vn 0.442686 -0.872172 -0.208193 +vn 0.534807 -0.844611 -0.024800 +vn 0.158398 -0.987286 0.013300 +vn 0.498717 -0.854328 -0.146305 +vn 0.087400 -0.994604 0.055900 +vn 0.409404 -0.880709 -0.238202 +vn 0.245504 -0.969316 -0.012400 +vn 0.167104 -0.949321 -0.266206 +vn 0.287495 -0.956785 0.043699 +vn 0.600108 -0.783010 -0.163602 +vn 0.649906 -0.760006 0.003600 +vn 0.734587 -0.623089 -0.268595 +vn 0.780297 -0.603497 -0.164099 +vn 0.674204 -0.641304 -0.366302 +vn 0.455388 -0.771280 -0.444689 +vn 0.719903 -0.652103 -0.237701 +vn 0.795596 -0.587697 -0.147099 +vn 0.663326 -0.689428 -0.291012 +vn 0.642189 -0.690489 -0.332894 +vn 0.802958 -0.595869 0.014099 +vn 0.758579 -0.644982 0.092497 +vn 0.068898 -0.986675 0.147396 +vn 0.266204 -0.963914 0.002400 +vn 0.351916 -0.933744 -0.065403 +vn 0.387209 -0.901821 -0.191804 +vn -0.110801 -0.993711 -0.016200 +vn 0.016500 -0.997729 -0.065302 +vn -0.377805 -0.922012 -0.084601 +vn -0.664697 -0.743597 -0.072400 +vn -0.786515 -0.616812 -0.030601 +vn -0.890307 -0.454904 0.020400 +vn -0.093296 -0.983055 -0.157793 +vn 0.074199 -0.985989 -0.149398 +vn -0.283211 -0.949536 -0.134805 +vn -0.558032 -0.778344 -0.287716 +vn -0.674858 -0.737254 -0.031998 +vn -0.671025 -0.739727 -0.050302 +vn -0.880492 -0.414196 -0.230598 +vn -0.846182 -0.458490 0.271594 +vn -0.800579 -0.566785 0.194495 +vn -0.788096 -0.615097 -0.023700 +vn -0.927001 -0.157900 0.340200 +vn -0.981119 -0.146903 0.125802 +vn -0.599408 -0.798311 0.058401 +vn -0.757204 -0.641203 0.124501 +vn -0.976473 0.157496 0.147296 +vn -0.976085 -0.132298 0.172497 +vn -0.768886 -0.634889 0.075699 +vn -0.548873 -0.833359 0.065197 +vn -0.980589 0.195198 0.018500 +vn -0.991781 -0.123098 0.034899 +vn -0.781193 -0.622095 0.052300 +vn -0.532690 -0.831884 0.155597 +vn -0.990237 0.135005 -0.034701 +vn -0.983525 -0.172704 -0.053401 +vn -0.774712 -0.631910 0.022600 +vn -0.541194 -0.824491 0.165298 +vn -0.996893 -0.030800 -0.072499 +vn -0.964546 -0.248812 -0.088004 +vn -0.686113 -0.727214 0.020200 +vn -0.534516 -0.835224 0.129204 +vn -0.981938 -0.185307 -0.038201 +vn -0.968110 -0.239003 -0.075101 +vn -0.858732 -0.504619 -0.089103 +vn -0.817687 -0.572991 -0.055399 +vn 0.572499 -0.667299 -0.476399 +vn 0.682217 -0.713517 0.159604 +vn 0.677793 -0.716592 0.164598 +vn 0.705764 -0.684165 0.183891 +vn 0.740706 -0.631305 0.229802 +vn 0.725194 -0.623094 0.292997 +vn 0.766074 -0.555181 0.323889 +vn -0.306593 0.275894 0.910979 +vn -0.621720 -0.353512 -0.698923 +vn -0.330789 0.160995 0.929870 +vn -0.763598 -0.125900 -0.633299 +vn -0.272299 0.955696 0.111800 +vn -0.882209 0.046000 -0.468605 +vn 0.733090 0.655991 -0.179598 +vn 0.693400 0.648100 -0.314900 +vn 0.693400 0.648100 -0.314900 +vn 0.733090 0.655991 -0.179598 +vn 0.738575 0.570581 -0.359088 +vn 0.738575 0.570581 -0.359088 +vn -0.503603 -0.706804 0.496803 +vn -0.394699 -0.700899 0.594099 +vn -0.296100 -0.620100 0.726499 +vn -0.652181 -0.751478 0.099697 +vn -0.628570 -0.759964 0.165392 +vn -0.504516 -0.820327 -0.269309 +vn -0.536491 -0.799786 -0.269295 +vn -0.056301 -0.193805 0.979423 +vn 0.008500 -0.305186 0.952255 +vn 0.214593 -0.949769 0.227792 +vn -0.071900 -0.970697 0.229299 +vn 0.244107 -0.969629 0.015200 +vn 0.294806 -0.792416 0.534010 +vn 0.216898 -0.771594 0.597995 +vn 0.418380 -0.892957 0.166092 +vn 0.070499 -0.955093 -0.287798 +vn 0.086198 -0.960473 -0.264692 +vn 0.196400 -0.973898 -0.113800 +vn 0.144204 -0.656718 0.740221 +vn -0.240087 -0.622767 0.744661 +vn -0.024800 -0.199401 0.979604 +vn -0.056301 -0.193805 0.979423 +vn 0.239401 0.328101 0.913803 +vn 0.299210 0.542118 0.785227 +vn -0.427617 0.897436 0.108404 +vn -0.807716 0.515610 0.285906 +vn -0.456790 0.584287 0.670785 +vn -0.114194 0.899854 0.420978 +vn 0.614112 0.789216 -0.002300 +vn 0.905818 0.418108 0.068401 +vn 0.852319 0.365608 -0.374009 +vn 0.598510 0.745413 -0.293505 +vn -0.936095 0.072000 0.344298 +vn -0.591588 0.151997 0.791784 +vn 0.999355 -0.000100 0.035898 +vn 0.907976 -0.085098 -0.410289 +vn 0.954076 -0.063798 -0.292693 +vn -0.895933 0.089803 0.435016 +vn -0.657015 0.141303 0.740517 +vn -0.509871 0.160991 0.845052 +vn -0.381903 0.697006 0.606905 +vn -0.548191 0.599090 0.583590 +vn 0.262004 0.939515 -0.220603 +vn 0.005200 0.995982 0.089398 +vn 0.738211 0.337605 -0.584009 +vn 0.628883 -0.144896 -0.763879 +vn 0.863348 -0.095305 -0.495527 +vn -0.343698 0.177799 0.922095 +vn -0.246706 0.724318 0.643816 +vn 0.221106 0.931226 -0.289708 +vn 0.415516 -0.169606 -0.893633 +vn -0.148300 0.189599 0.970597 +vn -0.087206 0.759749 0.644342 +vn 0.165799 0.905594 -0.390397 +vn 0.220104 -0.183203 -0.958118 +vn 0.090399 0.192197 0.977184 +vn 0.109295 0.770362 0.628169 +vn 0.077598 0.895675 -0.437888 +vn -0.013600 -0.189401 -0.981806 +vn 0.300306 0.182003 0.936318 +vn 0.167794 0.985663 -0.017699 +vn -0.261096 -0.182597 -0.947886 +vn 0.564981 0.157295 0.809972 +vn 0.132701 0.989009 -0.065201 +vn -0.640107 -0.146402 -0.754208 +vn 0.845197 0.103700 0.524298 +vn 0.206790 0.962754 -0.174192 +vn -0.851879 -0.100597 -0.513987 +vn 0.982944 0.038602 0.179808 +vn 0.237399 0.930395 -0.279298 +vn -0.960460 -0.055698 -0.272789 +vn 0.986997 -0.029000 -0.158100 +vn 0.102000 0.955701 -0.276100 +vn -0.996884 0.014200 0.077599 +vn 0.880329 -0.091703 -0.465415 +vn 0.064499 0.956980 -0.282894 +vn -0.884078 0.093998 0.457789 +vn 0.598189 -0.156197 -0.785985 +vn 0.042301 0.951121 -0.305907 +vn -0.627435 0.155709 0.762942 +vn 0.137499 -0.201498 -0.969790 +vn -0.020001 0.958227 -0.285308 +vn -0.173401 0.210901 0.962005 +vn -0.062298 -0.204592 -0.976863 +vn -0.011500 0.965676 -0.259494 +vn 0.040800 0.220697 0.974489 +vn -0.241896 0.534992 -0.809488 +vn -0.217400 0.538800 -0.813899 +vn -0.374926 0.508036 -0.775455 +vn -0.395893 0.502691 -0.768486 +vn -0.415216 0.684227 -0.599524 +vn -0.551392 0.781289 -0.292496 +vn -0.586790 0.773387 -0.239896 +vn -0.433682 0.668772 -0.603875 +vn -0.580819 0.812526 0.049502 +vn -0.583295 0.811594 0.032900 +vn -0.441623 0.837344 0.322217 +vn -0.434023 0.843844 0.315517 +vn -0.133798 0.834388 0.534692 +vn -0.097705 0.827746 0.552531 +vn 0.219912 0.873649 0.434024 +vn 0.220192 0.868967 0.443183 +vn 0.519389 0.844282 0.131997 +vn 0.498505 0.848708 0.176602 +vn 0.554171 0.780259 -0.289985 +vn 0.551006 0.781409 -0.292903 +vn 0.282888 0.676471 -0.679971 +vn 0.223506 0.643918 -0.731720 +vn 0.239103 0.663108 -0.709308 +vn 0.268093 0.703881 -0.657782 +vn 0.262798 0.517396 -0.814394 +vn 0.396402 0.568602 -0.720803 +vn 0.987611 0.029100 0.154202 +vn 0.850314 0.448707 0.275005 +vn 0.723405 0.678605 0.127201 +vn 0.984781 0.028199 0.171497 +vn 0.616887 0.747884 0.245195 +vn 0.647002 0.754203 0.112100 +vn 0.294812 0.878035 0.377015 +vn 0.495720 0.864836 0.079503 +vn -0.198004 0.976621 0.083702 +vn 0.638326 0.759631 -0.124505 +vn -0.707118 0.587815 0.393010 +vn -0.838093 0.103099 0.535696 +vn -0.427709 0.169304 0.887919 +vn -0.291789 0.565278 0.771570 +vn 0.019900 0.868496 0.495297 +vn 0.999030 -0.011500 -0.042501 +vn 0.634532 0.139307 0.760238 +vn 0.845589 0.093999 0.525493 +vn -0.291789 0.565278 0.771570 +vn 0.019900 0.868496 0.495297 +vn 0.616887 0.747884 0.245195 +vn 0.850314 0.448707 0.275005 +vn 0.987611 0.029100 0.154202 +vn 0.852319 0.365608 -0.374009 +vn 0.954076 -0.063798 -0.292693 +vn 0.987849 -0.042102 -0.149607 +vn 0.914067 0.363187 -0.180493 +vn 0.909658 0.413281 0.041498 +vn 0.993766 0.004600 0.111396 +vn 0.855881 0.458690 0.238895 +vn 0.931993 0.052200 0.358697 +vn 0.755808 0.522005 0.395304 +vn 0.790468 0.095096 0.605076 +vn 0.546782 0.533883 0.644979 +vn 0.497785 0.145696 0.854975 +vn -0.427617 0.897436 0.108404 +vn 0.109704 0.983433 -0.144305 +vn 0.191296 0.977480 0.089098 +vn -0.310802 0.896406 0.316002 +vn 0.216405 0.959623 0.179704 +vn -0.342983 0.832560 0.434979 +vn 0.241691 0.922067 0.302289 +vn -0.258994 0.814982 0.518388 +vn 0.260094 0.880379 0.396591 +vn -0.159903 0.806916 0.568611 +vn 0.327501 0.722903 0.608403 +vn 0.110299 0.703991 0.701591 +vn -0.741313 0.518709 0.425907 +vn -0.659213 0.485510 0.574211 +vn -0.468275 0.524372 0.711162 +vn -0.248389 0.589974 0.768266 +vn 0.150804 0.545214 0.824621 +vn -0.807716 0.515610 0.285906 +vn -0.849797 0.106700 0.516198 +vn -0.895933 0.089803 0.435016 +vn -0.681042 0.143009 0.718144 +vn -0.417813 0.170305 0.892428 +vn -0.110199 0.184198 0.976692 +vn 0.382693 0.157897 0.910283 +vn 0.598510 0.745413 -0.293505 +vn 0.638116 0.764519 -0.091202 +vn 0.657096 0.751195 0.062700 +vn 0.636991 0.751689 0.170898 +vn 0.568595 0.765493 0.301197 +vn 0.468888 0.642783 0.605784 +vn 0.465496 0.478896 0.744293 +vn 0.701475 0.121996 0.702175 +vn 0.263690 0.599777 0.755471 +vn 0.328301 0.582601 0.743502 +vn 0.348192 0.475489 0.807882 +vn 0.263400 0.429500 0.863800 +vn 0.086996 0.177591 0.980252 +vn 0.227001 0.388602 0.893005 +vn -0.278308 0.176005 0.944228 +vn -0.106602 0.346908 0.931821 +vn 0.679633 0.403920 0.612330 +vn 0.830598 0.357699 0.426799 +vn 0.452906 0.500807 0.737610 +vn 0.830208 0.297703 0.471305 +vn 0.056498 0.395883 0.916561 +vn -0.202897 0.406393 0.890886 +vn -0.292012 0.326013 0.899136 +vn -0.340197 0.173598 0.924191 +vn 0.816626 0.106703 0.567218 +vn -0.323786 0.581075 -0.746668 +vn -0.310993 0.596287 -0.740084 +vn -0.590698 -0.649998 0.478099 +vn -0.220902 -0.550605 0.805007 +vn -0.450679 -0.304186 0.839261 +vn -0.814308 -0.354104 0.459905 +vn 0.770189 -0.619292 0.152598 +vn 0.667974 -0.742971 -0.042498 +vn 0.844086 -0.489492 -0.218896 +vn 0.906138 -0.394917 0.151506 +vn 0.387603 -0.149601 0.909606 +vn 0.333692 -0.122597 0.934676 +vn 0.386789 -0.156596 0.908775 +vn -0.731388 -0.465593 0.498292 +vn -0.599489 -0.720386 0.348793 +vn -0.097501 -0.387705 0.916612 +vn 0.434302 -0.156301 0.887103 +vn -0.150501 -0.972508 0.177701 +vn 0.215605 -0.632416 0.744019 +vn 0.330412 -0.113104 0.937035 +vn 0.422583 -0.901664 0.091796 +vn -0.761035 -0.388218 0.519724 +vn -0.612502 -0.682903 0.398102 +vn -0.119003 -0.300207 0.946422 +vn -0.146203 -0.949622 0.277206 +vn 0.237494 -0.568885 0.787379 +vn 0.370402 -0.922906 0.105101 +vn -0.250397 -0.851289 -0.461094 +vn -0.753939 -0.544556 -0.367470 +vn -0.747936 -0.513325 -0.420820 +vn -0.213808 -0.828529 -0.517518 +vn -0.363794 -0.169197 -0.915984 +vn -0.363287 -0.160394 -0.917767 +vn -0.329797 -0.169599 -0.928693 +vn -0.408697 -0.118899 -0.904892 +vn -0.412801 -0.107100 -0.904503 +vn 0.907976 -0.085098 -0.410289 +vn 0.999355 -0.000100 0.035898 +vn -0.550001 -0.349101 0.758701 +vn -0.384786 -0.432185 0.815571 +vn 0.002800 -0.902628 0.430413 +vn 0.254607 -0.959126 0.123503 +vn 0.732509 -0.528407 -0.429205 +vn 0.863348 -0.095305 -0.495527 +vn -0.248806 -0.441411 0.862121 +vn 0.216908 -0.974137 0.063302 +vn 0.415516 -0.169606 -0.893633 +vn -0.088203 -0.471518 0.877434 +vn -0.148300 0.189599 0.970597 +vn 0.163900 -0.985798 -0.036600 +vn 0.220104 -0.183203 -0.958118 +vn 0.111295 -0.485078 0.867360 +vn 0.090399 0.192197 0.977184 +vn 0.077701 -0.993507 -0.083101 +vn 0.173490 -0.921045 0.348679 +vn 0.300306 0.182003 0.936318 +vn 0.142103 -0.942623 0.302107 +vn 0.564981 0.157295 0.809972 +vn -0.640107 -0.146402 -0.754208 +vn 0.216798 -0.958392 0.185698 +vn -0.851879 -0.100597 -0.513987 +vn 0.245701 -0.966705 0.071500 +vn -0.960460 -0.055698 -0.272789 +vn 0.104005 -0.991248 0.081304 +vn 0.053102 -0.995134 0.083003 +vn 0.026500 -0.997081 0.071599 +vn 0.598189 -0.156197 -0.785985 +vn -0.036398 -0.991553 0.124494 +vn -0.019000 -0.984809 0.172602 +vn -0.062298 -0.204592 -0.976863 +vn 0.244605 -0.520910 0.817815 +vn 0.054199 -0.551390 0.832485 +vn 0.089999 -0.547292 0.832088 +vn 0.272795 -0.514590 0.812884 +vn 0.460195 -0.620693 0.634793 +vn 0.502405 -0.617606 0.605106 +vn 0.629616 -0.730818 0.263607 +vn 0.596835 -0.736043 0.319419 +vn 0.662192 -0.747890 -0.046499 +vn 0.655068 -0.752863 -0.063897 +vn 0.467687 -0.827377 -0.310991 +vn 0.480009 -0.827415 -0.291505 +vn 0.106799 -0.836991 -0.536694 +vn 0.161399 -0.842897 -0.513298 +vn -0.283408 -0.800022 -0.528815 +vn -0.246894 -0.813480 -0.526587 +vn -0.586209 -0.789613 -0.181303 +vn -0.525317 -0.819427 -0.229307 +vn -0.671545 -0.670145 0.316121 +vn -0.670098 -0.695098 0.260399 +vn -0.248003 -0.566706 0.785709 +vn -0.367093 -0.655687 0.659787 +vn 0.316101 -0.731203 0.604502 +vn 0.220210 -0.784637 0.579527 +vn 0.532903 -0.648803 0.543203 +vn 0.529494 -0.648493 0.546894 +vn 0.987611 0.029100 0.154202 +vn 0.984781 0.028199 0.171497 +vn 0.719563 -0.587470 0.370281 +vn 0.866979 -0.368591 0.335392 +vn 0.644030 -0.668731 0.371517 +vn 0.639808 -0.692209 0.333904 +vn 0.193403 -0.903315 0.382906 +vn 0.499893 -0.782889 0.370395 +vn 0.637192 -0.757791 0.140498 +vn -0.193587 -0.885842 0.421672 +vn -0.838093 0.103099 0.535696 +vn -0.707914 -0.405908 0.578011 +vn -0.292804 -0.256904 0.921014 +vn -0.190099 -0.797995 0.571897 +vn 0.999030 -0.011500 -0.042501 +vn 0.634532 0.139307 0.760238 +vn 0.905672 -0.422087 -0.039999 +vn 0.905206 -0.374003 0.201801 +vn 0.860296 -0.328198 0.390098 +vn 0.777088 -0.337595 0.531192 +vn 0.558899 -0.292599 0.775898 +vn -0.069500 -0.889001 0.452600 +vn -0.293009 -0.731124 0.616120 +vn 0.235597 -0.866189 0.440694 +vn 0.207799 -0.909398 0.360299 +vn -0.312312 -0.602923 0.734128 +vn 0.250103 -0.793708 0.554506 +vn -0.239602 -0.547005 0.802107 +vn 0.243716 -0.703647 0.667445 +vn -0.128708 -0.531135 0.837455 +vn 0.222499 -0.664498 0.713398 +vn 0.147407 -0.405919 0.901943 +vn 0.328211 -0.479716 0.813726 +vn -0.741088 -0.314695 0.593090 +vn -0.649590 -0.229596 0.724789 +vn -0.447308 -0.221904 0.866415 +vn -0.219297 -0.267796 0.938187 +vn 0.162005 -0.232007 0.959128 +vn -0.849797 0.106700 0.516198 +vn 0.229297 -0.926586 0.298096 +vn 0.636198 -0.746398 0.195299 +vn 0.654406 -0.648106 0.389503 +vn 0.625614 -0.588514 0.512112 +vn 0.562607 -0.612007 0.555807 +vn 0.485079 -0.442981 0.753967 +vn 0.701475 0.121996 0.702175 +vn 0.466896 -0.202598 0.860792 +vn 0.284387 -0.308086 0.907859 +vn 0.338496 -0.308697 0.888891 +vn 0.349307 -0.190604 0.917418 +vn 0.250700 -0.111700 0.961599 +vn 0.086996 0.177591 0.980252 +vn 0.212597 -0.063299 0.975088 +vn -0.114698 0.005500 0.993385 +vn 0.827396 -0.197099 0.525897 +vn 0.675979 -0.178494 0.714977 +vn 0.460405 -0.229202 0.857609 +vn 0.826087 -0.113998 0.551892 +vn 0.047501 -0.053801 0.997421 +vn -0.207309 -0.067503 0.975944 +vn -0.299203 0.023100 0.953910 +vn -0.340197 0.173598 0.924191 +vn 0.816626 0.106703 0.567218 +vn 0.343281 -0.550270 0.761158 +vn 0.342303 -0.534704 0.772606 +vn 0.109704 0.983433 -0.144305 +vn 0.234410 0.953942 0.187208 +vn 0.294812 0.878035 0.377015 +f 1/1/1 2/2/2 3/3/3 +f 1/1/1 3/3/3 4/4/4 +f 5/5/5 6/6/6 7/7/7 +f 5/5/5 8/8/8 6/6/6 +f 9/9/9 10/10/10 11/11/11 +f 11/11/11 12/12/12 9/9/9 +f 13/13/13 14/14/14 15/15/15 +f 15/15/15 16/16/16 13/13/13 +f 17/17/17 18/18/18 19/19/19 +f 19/19/19 20/20/20 17/17/17 +f 21/21/21 17/17/17 20/20/20 +f 20/20/20 22/22/22 21/21/21 +f 23/23/23 24/24/24 25/25/25 +f 26/26/26 27/27/27 28/28/28 +f 29/29/29 30/30/30 31/31/31 +f 32/32/32 33/33/33 34/34/34 +f 35/35/35 36/36/36 37/37/37 +f 38/38/38 39/39/39 40/40/40 +f 41/41/41 42/42/42 43/43/43 +f 44/44/44 45/45/45 46/46/46 +f 47/47/47 48/48/48 49/49/49 +f 50/50/50 51/51/51 52/52/52 +f 53/53/53 54/54/54 55/55/55 +f 56/56/56 57/57/57 58/58/58 +f 59/59/59 60/60/60 61/61/61 +f 62/62/62 63/63/63 64/64/64 +f 65/65/65 66/66/66 67/67/67 +f 68/68/68 69/69/69 70/70/70 +f 71/71/71 72/72/72 73/73/73 +f 73/73/73 74/74/74 71/71/71 +f 75/75/75 76/76/76 77/77/77 +f 77/77/77 78/78/78 75/75/75 +f 76/76/76 79/79/79 80/80/80 +f 80/80/80 77/77/77 76/76/76 +f 79/79/79 81/81/81 82/82/82 +f 82/82/82 80/80/80 79/79/79 +f 81/81/81 83/83/83 84/84/84 +f 84/84/84 82/82/82 81/81/81 +f 83/83/83 85/85/85 86/86/86 +f 86/86/86 84/84/84 83/83/83 +f 85/85/85 87/87/87 88/88/88 +f 88/88/88 86/86/86 85/85/85 +f 87/87/87 89/89/89 90/90/90 +f 90/90/90 88/88/88 87/87/87 +f 89/89/89 91/91/91 92/92/92 +f 92/92/92 90/90/90 89/89/89 +f 91/91/91 93/93/93 94/94/94 +f 94/94/94 92/92/92 91/91/91 +f 95/95/95 96/96/96 94/94/94 +f 94/94/94 93/93/93 95/95/95 +f 97/97/97 98/98/98 75/75/75 +f 75/75/75 78/78/78 97/97/97 +f 99/99/99 100/100/100 101/101/101 +f 101/101/101 102/102/102 99/99/99 +f 103/103/103 104/104/104 105/105/105 +f 105/105/105 106/106/106 103/103/103 +f 107/107/107 108/108/108 109/109/109 +f 109/109/109 110/110/110 107/107/107 +f 111/111/111 112/112/112 113/113/113 +f 113/113/113 114/114/114 111/111/111 +f 112/112/112 99/99/99 115/115/115 +f 115/115/115 113/113/113 112/112/112 +f 102/102/102 103/103/103 106/106/106 +f 106/106/106 116/116/116 102/102/102 +f 102/102/102 116/116/116 115/115/115 +f 115/115/115 99/99/99 102/102/102 +f 116/116/116 108/108/108 107/107/107 +f 107/107/107 115/115/115 116/116/116 +f 117/117/117 118/118/118 119/119/119 +f 119/119/119 120/120/120 117/117/117 +f 121/121/121 122/122/122 123/123/123 +f 123/123/123 124/124/124 121/121/121 +f 125/125/125 126/126/126 127/127/127 +f 127/127/127 128/128/128 125/125/125 +f 126/126/126 125/125/125 129/129/129 +f 129/129/129 130/130/130 126/126/126 +f 131/131/131 132/132/132 133/133/133 +f 133/133/133 134/134/134 131/131/131 +f 128/128/128 135/135/135 125/125/125 +f 125/125/125 135/135/135 129/129/129 +f 133/133/133 136/136/136 134/134/134 +f 131/131/131 137/137/137 132/132/132 +f 138/138/138 126/126/126 130/130/130 +f 126/126/126 138/138/138 127/127/127 +f 139/139/139 136/136/136 140/140/140 +f 140/140/140 141/141/141 139/139/139 +f 136/136/136 133/133/133 140/140/140 +f 142/142/142 132/132/132 137/137/137 +f 143/143/143 138/138/138 130/130/130 +f 130/130/130 144/144/144 143/143/143 +f 129/129/129 135/135/135 139/139/139 +f 139/139/139 141/141/141 129/129/129 +f 145/145/145 132/132/132 142/142/142 +f 142/142/142 146/146/146 145/145/145 +f 147/147/147 148/148/148 149/149/149 +f 149/149/149 150/150/150 147/147/147 +f 151/151/151 152/152/152 153/153/153 +f 153/153/153 154/154/154 151/151/151 +f 154/154/154 153/153/153 155/155/155 +f 155/155/155 156/156/156 154/154/154 +f 156/156/156 155/155/155 157/157/157 +f 157/157/157 158/158/158 156/156/156 +f 158/158/158 157/157/157 159/159/159 +f 159/159/159 160/160/160 158/158/158 +f 160/160/160 159/159/159 161/161/161 +f 161/161/161 162/162/162 160/160/160 +f 162/162/162 161/161/161 163/163/163 +f 163/163/163 164/164/164 162/162/162 +f 164/164/164 163/163/163 165/165/165 +f 165/165/165 166/166/166 164/164/164 +f 166/166/166 165/165/165 167/167/167 +f 167/167/167 168/168/168 166/166/166 +f 168/168/168 167/167/167 169/169/169 +f 169/169/169 170/170/170 168/168/168 +f 170/170/170 169/169/169 171/171/171 +f 171/171/171 172/172/172 170/170/170 +f 142/142/142 137/137/137 143/143/143 +f 143/143/143 144/144/144 142/142/142 +f 173/173/173 174/174/174 140/140/140 +f 140/140/140 133/133/133 173/173/173 +f 145/145/145 173/173/173 133/133/133 +f 133/133/133 132/132/132 145/145/145 +f 145/145/145 175/175/175 176/176/176 +f 176/176/176 173/173/173 145/145/145 +f 152/152/152 151/151/151 177/177/177 +f 177/177/177 178/178/178 152/152/152 +f 177/177/177 147/147/147 150/150/150 +f 150/150/150 178/178/178 177/177/177 +f 178/178/178 150/150/150 179/179/179 +f 179/179/179 180/180/180 178/178/178 +f 181/181/181 182/182/182 183/183/183 +f 184/184/184 185/185/185 186/186/186 +f 187/187/187 188/188/188 184/184/184 +f 184/184/184 186/186/186 187/187/187 +f 182/182/182 189/189/189 190/190/190 +f 190/190/190 191/191/191 182/182/182 +f 191/191/191 190/190/190 192/192/192 +f 192/192/192 185/185/185 191/191/191 +f 192/192/192 193/193/193 186/186/186 +f 186/186/186 185/185/185 192/192/192 +f 193/193/193 194/194/194 187/187/187 +f 187/187/187 186/186/186 193/193/193 +f 195/195/195 196/196/196 189/189/189 +f 189/189/189 197/197/197 195/195/195 +f 198/198/198 199/199/199 196/196/196 +f 196/196/196 195/195/195 198/198/198 +f 200/200/200 201/201/201 199/199/199 +f 199/199/199 198/198/198 200/200/200 +f 202/202/202 203/203/203 201/201/201 +f 201/201/201 200/200/200 202/202/202 +f 182/182/182 181/181/181 204/204/204 +f 182/182/182 204/204/204 205/205/205 +f 205/205/205 189/189/189 182/182/182 +f 189/189/189 205/205/205 206/206/206 +f 206/206/206 197/197/197 189/189/189 +f 181/181/181 207/207/207 208/208/208 +f 208/208/208 204/204/204 181/181/181 +f 204/204/204 208/208/208 209/209/209 +f 209/209/209 205/205/205 204/204/204 +f 205/205/205 209/209/209 210/210/210 +f 210/210/210 206/206/206 205/205/205 +f 211/211/211 212/212/212 213/213/213 +f 214/214/214 215/215/215 216/216/216 +f 214/214/214 217/217/217 218/218/218 +f 218/218/218 215/215/215 214/214/214 +f 212/212/212 219/219/219 220/220/220 +f 219/219/219 212/212/212 214/214/214 +f 212/212/212 221/221/221 213/213/213 +f 220/220/220 221/221/221 212/212/212 +f 222/222/222 223/223/223 211/211/211 +f 224/224/224 225/225/225 226/226/226 +f 226/226/226 227/227/227 224/224/224 +f 228/228/228 229/229/229 217/217/217 +f 217/217/217 214/214/214 228/228/228 +f 214/214/214 212/212/212 230/230/230 +f 230/230/230 228/228/228 214/214/214 +f 230/230/230 212/212/212 211/211/211 +f 211/211/211 231/231/231 230/230/230 +f 231/231/231 211/211/211 232/232/232 +f 232/232/232 233/233/233 231/231/231 +f 234/234/234 226/226/226 225/225/225 +f 225/225/225 235/235/235 234/234/234 +f 227/227/227 236/236/236 237/237/237 +f 237/237/237 224/224/224 227/227/227 +f 236/236/236 238/238/238 239/239/239 +f 239/239/239 237/237/237 236/236/236 +f 217/217/217 240/240/240 241/241/241 +f 241/241/241 218/218/218 217/217/217 +f 242/242/242 243/243/243 244/244/244 +f 240/240/240 217/217/217 242/242/242 +f 242/242/242 244/244/244 240/240/240 +f 245/245/245 246/246/246 247/247/247 +f 243/243/243 242/242/242 247/247/247 +f 248/248/248 245/245/245 249/249/249 +f 250/250/250 251/251/251 245/245/245 +f 245/245/245 242/242/242 250/250/250 +f 217/217/217 229/229/229 250/250/250 +f 250/250/250 242/242/242 217/217/217 +f 251/251/251 252/252/252 253/253/253 +f 253/253/253 245/245/245 251/251/251 +f 254/254/254 255/255/255 256/256/256 +f 256/256/256 257/257/257 254/254/254 +f 258/258/258 259/259/259 257/257/257 +f 257/257/257 256/256/256 258/258/258 +f 259/259/259 258/258/258 239/239/239 +f 239/239/239 238/238/238 259/259/259 +f 247/247/247 242/242/242 245/245/245 +f 245/245/245 248/248/248 246/246/246 +f 249/249/249 245/245/245 253/253/253 +f 211/211/211 223/223/223 232/232/232 +f 219/219/219 214/214/214 216/216/216 +f 211/211/211 213/213/213 222/222/222 +f 260/260/260 261/261/261 262/262/262 +f 263/263/263 261/261/261 264/264/264 +f 265/265/265 261/261/261 266/266/266 +f 264/264/264 261/261/261 265/265/265 +f 267/267/267 261/261/261 260/260/260 +f 266/266/266 261/261/261 267/267/267 +f 262/262/262 261/261/261 268/268/268 +f 269/269/269 261/261/261 270/270/270 +f 271/271/271 261/261/261 272/272/272 +f 272/272/272 261/261/261 273/273/273 +f 274/274/274 261/261/261 275/275/275 +f 275/275/275 261/261/261 271/271/271 +f 270/270/270 261/261/261 276/276/276 +f 276/276/276 261/261/261 274/274/274 +f 268/268/268 261/261/261 269/269/269 +f 273/273/273 261/261/261 263/263/263 +f 226/226/226 277/277/277 278/278/278 +f 278/278/278 227/227/227 226/226/226 +f 234/234/234 279/279/279 277/277/277 +f 277/277/277 226/226/226 234/234/234 +f 227/227/227 278/278/278 280/280/280 +f 280/280/280 236/236/236 227/227/227 +f 236/236/236 280/280/280 281/281/281 +f 281/281/281 238/238/238 236/236/236 +f 257/257/257 282/282/282 283/283/283 +f 283/283/283 254/254/254 257/257/257 +f 259/259/259 284/284/284 282/282/282 +f 282/282/282 257/257/257 259/259/259 +f 238/238/238 281/281/281 284/284/284 +f 284/284/284 259/259/259 238/238/238 +f 285/285/285 281/281/281 286/286/286 +f 286/286/286 287/287/287 285/285/285 +f 288/288/288 289/289/289 290/290/290 +f 290/290/290 278/278/278 288/288/288 +f 291/291/291 292/292/292 293/293/293 +f 293/293/293 294/294/294 291/291/291 +f 295/295/295 296/296/296 297/297/297 +f 297/297/297 298/298/298 295/295/295 +f 299/299/299 300/300/300 301/301/301 +f 297/297/297 296/296/296 302/302/302 +f 302/302/302 277/277/277 297/297/297 +f 303/303/303 304/304/304 305/305/305 +f 304/304/304 303/303/303 306/306/306 +f 306/306/306 307/307/307 304/304/304 +f 307/307/307 308/308/308 294/294/294 +f 294/294/294 293/293/293 307/307/307 +f 309/309/309 310/310/310 305/305/305 +f 305/305/305 304/304/304 309/309/309 +f 311/311/311 312/312/312 313/313/313 +f 313/313/313 314/314/314 311/311/311 +f 309/309/309 314/314/314 313/313/313 +f 313/313/313 310/310/310 309/309/309 +f 307/307/307 306/306/306 308/308/308 +f 288/288/288 278/278/278 277/277/277 +f 277/277/277 302/302/302 288/288/288 +f 282/282/282 315/315/315 301/301/301 +f 297/297/297 277/277/277 279/279/279 +f 300/300/300 283/283/283 282/282/282 +f 282/282/282 301/301/301 300/300/300 +f 290/290/290 280/280/280 278/278/278 +f 209/209/209 208/208/208 280/280/280 +f 280/280/280 290/290/290 209/209/209 +f 290/290/290 289/289/289 210/210/210 +f 210/210/210 209/209/209 290/290/290 +f 207/207/207 281/281/281 280/280/280 +f 280/280/280 208/208/208 207/207/207 +f 181/181/181 316/316/316 317/317/317 +f 317/317/317 207/207/207 181/181/181 +f 316/316/316 181/181/181 184/184/184 +f 185/185/185 184/184/184 183/183/183 +f 183/183/183 191/191/191 185/185/185 +f 183/183/183 182/182/182 191/191/191 +f 318/318/318 319/319/319 312/312/312 +f 312/312/312 320/320/320 318/318/318 +f 321/321/321 322/322/322 323/323/323 +f 323/323/323 291/291/291 321/321/321 +f 322/322/322 321/321/321 324/324/324 +f 324/324/324 325/325/325 322/322/322 +f 326/326/326 294/294/294 327/327/327 +f 328/328/328 329/329/329 330/330/330 +f 330/330/330 331/331/331 328/328/328 +f 291/291/291 326/326/326 321/321/321 +f 321/321/321 326/326/326 332/332/332 +f 332/332/332 324/324/324 321/321/321 +f 333/333/333 334/334/334 335/335/335 +f 335/335/335 336/336/336 333/333/333 +f 334/334/334 337/337/337 338/338/338 +f 338/338/338 335/335/335 334/334/334 +f 339/339/339 340/340/340 338/338/338 +f 338/338/338 337/337/337 339/339/339 +f 341/341/341 342/342/342 343/343/343 +f 343/343/343 344/344/344 341/341/341 +f 345/345/345 303/303/303 305/305/305 +f 305/305/305 346/346/346 345/345/345 +f 347/347/347 345/345/345 346/346/346 +f 346/346/346 348/348/348 347/347/347 +f 349/349/349 350/350/350 342/342/342 +f 342/342/342 341/341/341 349/349/349 +f 303/303/303 345/345/345 351/351/351 +f 351/351/351 306/306/306 303/303/303 +f 345/345/345 347/347/347 352/352/352 +f 352/352/352 351/351/351 345/345/345 +f 350/350/350 349/349/349 325/325/325 +f 325/325/325 324/324/324 350/350/350 +f 306/306/306 351/351/351 353/353/353 +f 353/353/353 308/308/308 306/306/306 +f 354/354/354 352/352/352 355/355/355 +f 356/356/356 357/357/357 344/344/344 +f 344/344/344 343/343/343 356/356/356 +f 346/346/346 305/305/305 310/310/310 +f 310/310/310 328/328/328 346/346/346 +f 346/346/346 328/328/328 348/348/348 +f 329/329/329 358/358/358 359/359/359 +f 359/359/359 330/330/330 329/329/329 +f 329/329/329 328/328/328 310/310/310 +f 310/310/310 313/313/313 329/329/329 +f 357/357/357 356/356/356 360/360/360 +f 360/360/360 361/361/361 357/357/357 +f 362/362/362 358/358/358 312/312/312 +f 312/312/312 319/319/319 362/362/362 +f 363/363/363 364/364/364 365/365/365 +f 365/365/365 366/366/366 363/363/363 +f 358/358/358 362/362/362 367/367/367 +f 367/367/367 359/359/359 358/358/358 +f 358/358/358 329/329/329 313/313/313 +f 313/313/313 312/312/312 358/358/358 +f 364/364/364 361/361/361 360/360/360 +f 360/360/360 365/365/365 364/364/364 +f 360/360/360 356/356/356 331/331/331 +f 331/331/331 330/330/330 360/360/360 +f 368/368/368 343/343/343 342/342/342 +f 342/342/342 369/369/369 368/368/368 +f 350/350/350 355/355/355 369/369/369 +f 369/369/369 342/342/342 350/350/350 +f 355/355/355 350/350/350 324/324/324 +f 324/324/324 332/332/332 355/355/355 +f 356/356/356 343/343/343 368/368/368 +f 368/368/368 331/331/331 356/356/356 +f 365/365/365 360/360/360 330/330/330 +f 330/330/330 359/359/359 365/365/365 +f 366/366/366 365/365/365 359/359/359 +f 359/359/359 367/367/367 366/366/366 +f 326/326/326 327/327/327 332/332/332 +f 326/326/326 291/291/291 294/294/294 +f 348/348/348 368/368/368 369/369/369 +f 369/369/369 347/347/347 348/348/348 +f 355/355/355 352/352/352 347/347/347 +f 347/347/347 369/369/369 355/355/355 +f 354/354/354 353/353/353 351/351/351 +f 351/351/351 352/352/352 354/354/354 +f 328/328/328 331/331/331 368/368/368 +f 368/368/368 348/348/348 328/328/328 +f 337/337/337 370/370/370 339/339/339 +f 308/308/308 353/353/353 327/327/327 +f 327/327/327 294/294/294 308/308/308 +f 332/332/332 327/327/327 354/354/354 +f 354/354/354 355/355/355 332/332/332 +f 354/354/354 327/327/327 353/353/353 +f 320/320/320 312/312/312 311/311/311 +f 371/371/371 309/309/309 372/372/372 +f 304/304/304 307/307/307 373/373/373 +f 374/374/374 304/304/304 373/373/373 +f 304/304/304 374/374/374 375/375/375 +f 375/375/375 372/372/372 309/309/309 +f 309/309/309 304/304/304 375/375/375 +f 376/376/376 377/377/377 378/378/378 +f 379/379/379 377/377/377 376/376/376 +f 380/380/380 381/381/381 382/382/382 +f 382/382/382 383/383/383 380/380/380 +f 383/383/383 382/382/382 384/384/384 +f 384/384/384 385/385/385 386/386/386 +f 386/386/386 385/385/385 377/377/377 +f 285/285/285 315/315/315 282/282/282 +f 282/282/282 284/284/284 285/285/285 +f 301/301/301 315/315/315 387/387/387 +f 281/281/281 285/285/285 284/284/284 +f 382/382/382 381/381/381 377/377/377 +f 377/377/377 385/385/385 382/382/382 +f 384/384/384 382/382/382 385/385/385 +f 386/386/386 377/377/377 379/379/379 +f 317/317/317 286/286/286 281/281/281 +f 281/281/281 207/207/207 317/317/317 +f 388/388/388 389/389/389 390/390/390 +f 390/390/390 391/391/391 388/388/388 +f 392/392/392 391/391/391 390/390/390 +f 390/390/390 393/393/393 392/392/392 +f 389/389/389 394/394/394 395/395/395 +f 389/389/389 395/395/395 393/393/393 +f 390/390/390 389/389/389 393/393/393 +f 396/396/396 397/397/397 398/398/398 +f 398/398/398 394/394/394 396/396/396 +f 399/399/399 394/394/394 389/389/389 +f 389/389/389 388/388/388 399/399/399 +f 400/400/400 401/401/401 398/398/398 +f 398/398/398 397/397/397 400/400/400 +f 398/398/398 395/395/395 394/394/394 +f 397/397/397 396/396/396 402/402/402 +f 403/403/403 400/400/400 397/397/397 +f 397/397/397 402/402/402 403/403/403 +f 404/404/404 405/405/405 402/402/402 +f 402/402/402 396/396/396 404/404/404 +f 403/403/403 402/402/402 405/405/405 +f 405/405/405 406/406/406 403/403/403 +f 394/394/394 399/399/399 404/404/404 +f 404/404/404 396/396/396 394/394/394 +f 398/398/398 401/401/401 395/395/395 +f 407/407/407 408/408/408 409/409/409 +f 410/410/410 411/411/411 412/412/412 +f 413/413/413 410/410/410 412/412/412 +f 412/412/412 414/414/414 413/413/413 +f 413/413/413 414/414/414 407/407/407 +f 407/407/407 409/409/409 413/413/413 +f 409/409/409 415/415/415 413/413/413 +f 411/411/411 416/416/416 417/417/417 +f 417/417/417 412/412/412 411/411/411 +f 418/418/418 419/419/419 417/417/417 +f 417/417/417 416/416/416 418/418/418 +f 416/416/416 420/420/420 418/418/418 +f 420/420/420 421/421/421 418/418/418 +f 418/418/418 422/422/422 423/423/423 +f 423/423/423 419/419/419 418/418/418 +f 418/418/418 421/421/421 422/422/422 +f 424/424/424 425/425/425 426/426/426 +f 426/426/426 427/427/427 424/424/424 +f 428/428/428 429/429/429 430/430/430 +f 430/430/430 431/431/431 432/432/432 +f 432/432/432 433/433/433 430/430/430 +f 428/428/428 430/430/430 433/433/433 +f 433/433/433 434/434/434 428/428/428 +f 424/424/424 427/427/427 435/435/435 +f 435/435/435 436/436/436 424/424/424 +f 427/427/427 426/426/426 429/429/429 +f 429/429/429 428/428/428 427/427/427 +f 427/427/427 428/428/428 434/434/434 +f 434/434/434 435/435/435 427/427/427 +f 437/437/437 438/438/438 439/439/439 +f 439/439/439 440/440/440 437/437/437 +f 441/441/441 442/442/442 438/438/438 +f 438/438/438 437/437/437 441/441/441 +f 443/443/443 444/444/444 442/442/442 +f 442/442/442 441/441/441 443/443/443 +f 445/445/445 446/446/446 444/444/444 +f 444/444/444 443/443/443 445/445/445 +f 438/438/438 447/447/447 448/448/448 +f 448/448/448 439/439/439 438/438/438 +f 442/442/442 449/449/449 447/447/447 +f 447/447/447 438/438/438 442/442/442 +f 444/444/444 450/450/450 449/449/449 +f 449/449/449 442/442/442 444/444/444 +f 446/446/446 451/451/451 452/452/452 +f 447/447/447 453/453/453 454/454/454 +f 454/454/454 448/448/448 447/447/447 +f 449/449/449 455/455/455 453/453/453 +f 453/453/453 447/447/447 449/449/449 +f 450/450/450 456/456/456 455/455/455 +f 455/455/455 449/449/449 450/450/450 +f 451/451/451 457/457/457 458/458/458 +f 458/458/458 452/452/452 451/451/451 +f 450/450/450 452/452/452 458/458/458 +f 458/458/458 456/456/456 450/450/450 +f 444/444/444 452/452/452 450/450/450 +f 446/446/446 452/452/452 444/444/444 +f 443/443/443 433/433/433 432/432/432 +f 432/432/432 445/445/445 443/443/443 +f 434/434/434 433/433/433 443/443/443 +f 443/443/443 441/441/441 434/434/434 +f 436/436/436 435/435/435 437/437/437 +f 437/437/437 440/440/440 436/436/436 +f 435/435/435 434/434/434 441/441/441 +f 441/441/441 437/437/437 435/435/435 +f 424/424/424 459/459/459 460/460/460 +f 460/460/460 425/425/425 424/424/424 +f 461/461/461 462/462/462 463/463/463 +f 464/464/464 465/465/465 466/466/466 +f 466/466/466 467/467/467 464/464/464 +f 462/462/462 468/468/468 469/469/469 +f 469/469/469 470/470/470 462/462/462 +f 461/461/461 471/471/471 468/468/468 +f 468/468/468 462/462/462 461/461/461 +f 472/472/472 473/473/473 464/464/464 +f 464/464/464 474/474/474 472/472/472 +f 474/474/474 464/464/464 467/467/467 +f 467/467/467 475/475/475 474/474/474 +f 424/424/424 436/436/436 476/476/476 +f 476/476/476 459/459/459 424/424/424 +f 463/463/463 460/460/460 459/459/459 +f 459/459/459 461/461/461 463/463/463 +f 459/459/459 476/476/476 471/471/471 +f 471/471/471 461/461/461 459/459/459 +f 464/464/464 473/473/473 477/477/477 +f 477/477/477 465/465/465 464/464/464 +f 472/472/472 470/470/470 473/473/473 +f 473/473/473 470/470/470 469/469/469 +f 469/469/469 477/477/477 473/473/473 +f 478/478/478 440/440/440 439/439/439 +f 439/439/439 479/479/479 478/478/478 +f 480/480/480 478/478/478 479/479/479 +f 479/479/479 481/481/481 480/480/480 +f 482/482/482 480/480/480 481/481/481 +f 481/481/481 483/483/483 482/482/482 +f 484/484/484 482/482/482 483/483/483 +f 483/483/483 485/485/485 484/484/484 +f 486/486/486 484/484/484 485/485/485 +f 485/485/485 487/487/487 486/486/486 +f 488/488/488 486/486/486 487/487/487 +f 487/487/487 489/489/489 488/488/488 +f 490/490/490 488/488/488 489/489/489 +f 489/489/489 491/491/491 490/490/490 +f 479/479/479 439/439/439 448/448/448 +f 448/448/448 492/492/492 479/479/479 +f 481/481/481 479/479/479 492/492/492 +f 492/492/492 493/493/493 481/481/481 +f 483/483/483 481/481/481 493/493/493 +f 493/493/493 494/494/494 483/483/483 +f 485/485/485 495/495/495 496/496/496 +f 487/487/487 485/485/485 496/496/496 +f 496/496/496 497/497/497 487/487/487 +f 489/489/489 487/487/487 497/497/497 +f 497/497/497 498/498/498 489/489/489 +f 491/491/491 489/489/489 498/498/498 +f 498/498/498 499/499/499 491/491/491 +f 492/492/492 448/448/448 454/454/454 +f 454/454/454 500/500/500 492/492/492 +f 493/493/493 492/492/492 500/500/500 +f 500/500/500 501/501/501 493/493/493 +f 494/494/494 493/493/493 501/501/501 +f 501/501/501 502/502/502 494/494/494 +f 496/496/496 495/495/495 503/503/503 +f 503/503/503 504/504/504 496/496/496 +f 497/497/497 496/496/496 504/504/504 +f 504/504/504 505/505/505 497/497/497 +f 498/498/498 497/497/497 505/505/505 +f 505/505/505 506/506/506 498/498/498 +f 499/499/499 498/498/498 506/506/506 +f 506/506/506 507/507/507 499/499/499 +f 494/494/494 502/502/502 503/503/503 +f 503/503/503 495/495/495 494/494/494 +f 483/483/483 494/494/494 495/495/495 +f 485/485/485 483/483/483 495/495/495 +f 488/488/488 490/490/490 466/466/466 +f 466/466/466 465/465/465 488/488/488 +f 482/482/482 484/484/484 469/469/469 +f 469/469/469 468/468/468 482/482/482 +f 471/471/471 480/480/480 482/482/482 +f 482/482/482 468/468/468 471/471/471 +f 436/436/436 440/440/440 478/478/478 +f 478/478/478 476/476/476 436/436/436 +f 476/476/476 478/478/478 480/480/480 +f 480/480/480 471/471/471 476/476/476 +f 486/486/486 488/488/488 465/465/465 +f 465/465/465 477/477/477 486/486/486 +f 484/484/484 486/486/486 477/477/477 +f 477/477/477 469/469/469 484/484/484 +f 508/508/508 509/509/509 510/510/510 +f 511/511/511 512/512/512 513/513/513 +f 514/514/514 515/515/515 516/516/516 +f 517/517/517 518/518/518 519/519/519 +f 519/519/519 520/520/520 517/517/517 +f 509/509/509 521/521/521 522/522/522 +f 522/522/522 510/510/510 509/509/509 +f 518/518/518 523/523/523 519/519/519 +f 518/518/518 517/517/517 524/524/524 +f 525/525/525 526/526/526 527/527/527 +f 527/527/527 528/528/528 525/525/525 +f 529/529/529 530/530/530 531/531/531 +f 531/531/531 532/532/532 529/529/529 +f 533/533/533 534/534/534 530/530/530 +f 530/530/530 529/529/529 533/533/533 +f 527/527/527 535/535/535 536/536/536 +f 536/536/536 528/528/528 527/527/527 +f 537/537/537 538/538/538 531/531/531 +f 539/539/539 534/534/534 540/540/540 +f 514/514/514 510/510/510 515/515/515 +f 511/511/511 513/513/513 523/523/523 +f 541/541/541 542/542/542 543/543/543 +f 543/543/543 544/544/544 541/541/541 +f 522/522/522 521/521/521 545/545/545 +f 545/545/545 546/546/546 522/522/522 +f 523/523/523 541/541/541 544/544/544 +f 544/544/544 547/547/547 523/523/523 +f 515/515/515 522/522/522 546/546/546 +f 546/546/546 548/548/548 515/515/515 +f 520/520/520 519/519/519 549/549/549 +f 549/549/549 550/550/550 520/520/520 +f 542/542/542 515/515/515 548/548/548 +f 548/548/548 543/543/543 542/542/542 +f 519/519/519 523/523/523 547/547/547 +f 547/547/547 549/549/549 519/519/519 +f 543/543/543 551/551/551 552/552/552 +f 552/552/552 544/544/544 543/543/543 +f 545/545/545 553/553/553 554/554/554 +f 554/554/554 546/546/546 545/545/545 +f 545/545/545 555/555/555 556/556/556 +f 556/556/556 553/553/553 545/545/545 +f 547/547/547 544/544/544 552/552/552 +f 552/552/552 557/557/557 547/547/547 +f 546/546/546 554/554/554 558/558/558 +f 558/558/558 548/548/548 546/546/546 +f 550/550/550 549/549/549 559/559/559 +f 559/559/559 560/560/560 550/550/550 +f 543/543/543 548/548/548 558/558/558 +f 558/558/558 551/551/551 543/543/543 +f 547/547/547 557/557/557 559/559/559 +f 559/559/559 549/549/549 547/547/547 +f 537/537/537 561/561/561 562/562/562 +f 562/562/562 538/538/538 537/537/537 +f 530/530/530 563/563/563 531/531/531 +f 534/534/534 539/539/539 563/563/563 +f 563/563/563 530/530/530 534/534/534 +f 562/562/562 561/561/561 535/535/535 +f 526/526/526 525/525/525 532/532/532 +f 532/532/532 531/531/531 526/526/526 +f 533/533/533 564/564/564 540/540/540 +f 540/540/540 534/534/534 533/533/533 +f 565/565/565 566/566/566 539/539/539 +f 566/566/566 567/567/567 563/563/563 +f 563/563/563 539/539/539 566/566/566 +f 563/563/563 568/568/568 531/531/531 +f 569/569/569 570/570/570 571/571/571 +f 571/571/571 572/572/572 569/569/569 +f 573/573/573 574/574/574 575/575/575 +f 575/575/575 576/576/576 573/573/573 +f 574/574/574 577/577/577 578/578/578 +f 578/578/578 575/575/575 574/574/574 +f 577/577/577 579/579/579 580/580/580 +f 580/580/580 578/578/578 577/577/577 +f 581/581/581 582/582/582 583/583/583 +f 583/583/583 584/584/584 581/581/581 +f 582/582/582 569/569/569 572/572/572 +f 572/572/572 583/583/583 582/582/582 +f 572/572/572 571/571/571 585/585/585 +f 585/585/585 586/586/586 572/572/572 +f 576/576/576 587/587/587 588/588/588 +f 575/575/575 589/589/589 590/590/590 +f 591/591/591 592/592/592 593/593/593 +f 584/584/584 583/583/583 594/594/594 +f 583/583/583 586/586/586 595/595/595 +f 578/578/578 596/596/596 597/597/597 +f 598/598/598 599/599/599 584/584/584 +f 584/584/584 594/594/594 598/598/598 +f 594/594/594 600/600/600 601/601/601 +f 601/601/601 598/598/598 594/594/594 +f 602/602/602 578/578/578 597/597/597 +f 597/597/597 603/603/603 602/602/602 +f 604/604/604 605/605/605 606/606/606 +f 607/607/607 608/608/608 609/609/609 +f 589/589/589 610/610/610 611/611/611 +f 611/611/611 590/590/590 589/589/589 +f 612/612/612 613/613/613 614/614/614 +f 615/615/615 616/616/616 613/613/613 +f 613/613/613 612/612/612 615/615/615 +f 595/595/595 617/617/617 618/618/618 +f 618/618/618 615/615/615 595/595/595 +f 619/619/619 620/620/620 621/621/621 +f 621/621/621 611/611/611 619/619/619 +f 614/614/614 613/613/613 622/622/622 +f 622/622/622 623/623/623 614/614/614 +f 613/613/613 616/616/616 609/609/609 +f 609/609/609 622/622/622 613/613/613 +f 616/616/616 618/618/618 607/607/607 +f 607/607/607 609/609/609 616/616/616 +f 586/586/586 585/585/585 624/624/624 +f 624/624/624 625/625/625 586/586/586 +f 587/587/587 626/626/626 627/627/627 +f 627/627/627 588/588/588 587/587/587 +f 628/628/628 595/595/595 629/629/629 +f 629/629/629 630/630/630 628/628/628 +f 595/595/595 586/586/586 625/625/625 +f 625/625/625 629/629/629 595/595/595 +f 625/625/625 624/624/624 631/631/631 +f 632/632/632 633/633/633 634/634/634 +f 634/634/634 627/627/627 632/632/632 +f 630/630/630 635/635/635 636/636/636 +f 636/636/636 637/637/637 630/630/630 +f 629/629/629 625/625/625 631/631/631 +f 631/631/631 635/635/635 629/629/629 +f 638/638/638 639/639/639 640/640/640 +f 640/640/640 597/597/597 638/638/638 +f 599/599/599 641/641/641 642/642/642 +f 642/642/642 643/643/643 599/599/599 +f 601/601/601 644/644/644 641/641/641 +f 641/641/641 598/598/598 601/601/601 +f 601/601/601 645/645/645 644/644/644 +f 646/646/646 639/639/639 638/638/638 +f 638/638/638 647/647/647 646/646/646 +f 578/578/578 580/580/580 596/596/596 +f 580/580/580 647/647/647 596/596/596 +f 597/597/597 596/596/596 638/638/638 +f 638/638/638 596/596/596 647/647/647 +f 591/591/591 593/593/593 644/644/644 +f 644/644/644 645/645/645 591/591/591 +f 644/644/644 593/593/593 642/642/642 +f 642/642/642 641/641/641 644/644/644 +f 599/599/599 598/598/598 641/641/641 +f 602/602/602 648/648/648 610/610/610 +f 610/610/610 589/589/589 602/602/602 +f 648/648/648 649/649/649 620/620/620 +f 620/620/620 619/619/619 648/648/648 +f 590/590/590 650/650/650 626/626/626 +f 626/626/626 587/587/587 590/590/590 +f 650/650/650 651/651/651 633/633/633 +f 633/633/633 632/632/632 650/650/650 +f 576/576/576 575/575/575 587/587/587 +f 575/575/575 590/590/590 587/587/587 +f 578/578/578 602/602/602 589/589/589 +f 575/575/575 578/578/578 589/589/589 +f 583/583/583 595/595/595 594/594/594 +f 583/583/583 572/572/572 586/586/586 +f 648/648/648 619/619/619 610/610/610 +f 600/600/600 594/594/594 612/612/612 +f 612/612/612 614/614/614 600/600/600 +f 610/610/610 619/619/619 611/611/611 +f 612/612/612 594/594/594 595/595/595 +f 595/595/595 615/615/615 612/612/612 +f 618/618/618 616/616/616 615/615/615 +f 608/608/608 622/622/622 609/609/609 +f 608/608/608 623/623/623 622/622/622 +f 592/592/592 642/642/642 593/593/593 +f 592/592/592 643/643/643 642/642/642 +f 605/605/605 637/637/637 636/636/636 +f 605/605/605 636/636/636 606/606/606 +f 652/652/652 653/653/653 532/532/532 +f 654/654/654 655/655/655 564/564/564 +f 564/564/564 533/533/533 654/654/654 +f 655/655/655 656/656/656 564/564/564 +f 657/657/657 652/652/652 532/532/532 +f 532/532/532 525/525/525 657/657/657 +f 658/658/658 654/654/654 533/533/533 +f 533/533/533 529/529/529 658/658/658 +f 659/659/659 660/660/660 528/528/528 +f 528/528/528 536/536/536 659/659/659 +f 653/653/653 658/658/658 529/529/529 +f 529/529/529 532/532/532 653/653/653 +f 660/660/660 657/657/657 525/525/525 +f 525/525/525 528/528/528 660/660/660 +f 531/531/531 568/568/568 537/537/537 +f 531/531/531 538/538/538 526/526/526 +f 537/537/537 661/661/661 662/662/662 +f 662/662/662 561/561/561 537/537/537 +f 561/561/561 663/663/663 535/535/535 +f 538/538/538 562/562/562 527/527/527 +f 527/527/527 526/526/526 538/538/538 +f 527/527/527 562/562/562 535/535/535 +f 650/650/650 632/632/632 626/626/626 +f 626/626/626 632/632/632 627/627/627 +f 606/606/606 636/636/636 635/635/635 +f 635/635/635 631/631/631 606/606/606 +f 604/604/604 606/606/606 631/631/631 +f 631/631/631 624/624/624 604/604/604 +f 630/630/630 629/629/629 635/635/635 +f 567/567/567 664/664/664 665/665/665 +f 665/665/665 568/568/568 567/567/567 +f 539/539/539 540/540/540 565/565/565 +f 568/568/568 563/563/563 567/567/567 +f 565/565/565 666/666/666 667/667/667 +f 667/667/667 566/566/566 565/565/565 +f 667/667/667 664/664/664 567/567/567 +f 567/567/567 566/566/566 667/667/667 +f 568/568/568 665/665/665 661/661/661 +f 661/661/661 537/537/537 568/568/568 +f 662/662/662 668/668/668 663/663/663 +f 663/663/663 561/561/561 662/662/662 +f 516/516/516 542/542/542 541/541/541 +f 541/541/541 513/513/513 516/516/516 +f 513/513/513 541/541/541 523/523/523 +f 515/515/515 542/542/542 516/516/516 +f 510/510/510 522/522/522 515/515/515 +f 518/518/518 511/511/511 523/523/523 +f 514/514/514 516/516/516 513/513/513 +f 513/513/513 512/512/512 514/514/514 +f 551/551/551 653/653/653 652/652/652 +f 652/652/652 552/552/552 551/551/551 +f 553/553/553 655/655/655 654/654/654 +f 654/654/654 554/554/554 553/553/553 +f 656/656/656 655/655/655 553/553/553 +f 553/553/553 556/556/556 656/656/656 +f 652/652/652 657/657/657 557/557/557 +f 557/557/557 552/552/552 652/652/652 +f 554/554/554 654/654/654 658/658/658 +f 658/658/658 558/558/558 554/554/554 +f 660/660/660 659/659/659 560/560/560 +f 560/560/560 559/559/559 660/660/660 +f 658/658/658 653/653/653 551/551/551 +f 551/551/551 558/558/558 658/658/658 +f 657/657/657 660/660/660 559/559/559 +f 559/559/559 557/557/557 657/657/657 +f 669/669/669 670/670/670 521/521/521 +f 521/521/521 509/509/509 669/669/669 +f 521/521/521 670/670/670 555/555/555 +f 555/555/555 545/545/545 521/521/521 +f 671/671/671 672/672/672 673/673/673 +f 673/673/673 674/674/674 671/671/671 +f 675/675/675 676/676/676 677/677/677 +f 677/677/677 678/678/678 675/675/675 +f 676/676/676 679/679/679 677/677/677 +f 674/674/674 680/680/680 681/681/681 +f 681/681/681 682/682/682 674/674/674 +f 680/680/680 683/683/683 684/684/684 +f 684/684/684 681/681/681 680/680/680 +f 685/685/685 686/686/686 684/684/684 +f 687/687/687 688/688/688 686/686/686 +f 686/686/686 685/685/685 687/687/687 +f 677/677/677 679/679/679 689/689/689 +f 689/689/689 690/690/690 677/677/677 +f 682/682/682 681/681/681 691/691/691 +f 691/691/691 692/692/692 682/682/682 +f 681/681/681 684/684/684 693/693/693 +f 693/693/693 691/691/691 681/681/681 +f 694/694/694 695/695/695 696/696/696 +f 696/696/696 697/697/697 694/694/694 +f 697/697/697 696/696/696 698/698/698 +f 698/698/698 699/699/699 697/697/697 +f 700/700/700 677/677/677 690/690/690 +f 690/690/690 701/701/701 700/700/700 +f 689/689/689 679/679/679 682/682/682 +f 682/682/682 692/692/692 689/689/689 +f 693/693/693 684/684/684 695/695/695 +f 695/695/695 694/694/694 693/693/693 +f 702/702/702 703/703/703 704/704/704 +f 704/704/704 705/705/705 702/702/702 +f 703/703/703 706/706/706 704/704/704 +f 707/707/707 708/708/708 709/709/709 +f 710/710/710 711/711/711 708/708/708 +f 708/708/708 712/712/712 710/710/710 +f 710/710/710 713/713/713 711/711/711 +f 714/714/714 702/702/702 705/705/705 +f 705/705/705 704/704/704 715/715/715 +f 715/715/715 716/716/716 705/705/705 +f 715/715/715 704/704/704 706/706/706 +f 706/706/706 717/717/717 715/715/715 +f 707/707/707 718/718/718 717/717/717 +f 717/717/717 706/706/706 707/707/707 +f 718/718/718 707/707/707 709/709/709 +f 709/709/709 719/719/719 718/718/718 +f 719/719/719 709/709/709 720/720/720 +f 720/720/720 721/721/721 719/719/719 +f 720/720/720 713/713/713 722/722/722 +f 722/722/722 721/721/721 720/720/720 +f 723/723/723 724/724/724 722/722/722 +f 722/722/722 713/713/713 723/723/723 +f 725/725/725 714/714/714 705/705/705 +f 705/705/705 716/716/716 725/725/725 +f 676/676/676 674/674/674 679/679/679 +f 672/672/672 671/671/671 726/726/726 +f 726/726/726 727/727/727 672/672/672 +f 680/680/680 674/674/674 673/673/673 +f 673/673/673 728/728/728 680/680/680 +f 729/729/729 687/687/687 685/685/685 +f 685/685/685 730/730/730 729/729/729 +f 685/685/685 683/683/683 731/731/731 +f 731/731/731 730/730/730 685/685/685 +f 671/671/671 676/676/676 675/675/675 +f 675/675/675 726/726/726 671/671/671 +f 688/688/688 695/695/695 686/686/686 +f 711/711/711 720/720/720 709/709/709 +f 709/709/709 708/708/708 711/711/711 +f 707/707/707 712/712/712 708/708/708 +f 713/713/713 720/720/720 711/711/711 +f 677/677/677 700/700/700 732/732/732 +f 732/732/732 678/678/678 677/677/677 +f 683/683/683 680/680/680 728/728/728 +f 728/728/728 731/731/731 683/683/683 +f 683/683/683 685/685/685 684/684/684 +f 674/674/674 682/682/682 679/679/679 +f 676/676/676 671/671/671 674/674/674 +f 702/702/702 690/690/690 689/689/689 +f 689/689/689 703/703/703 702/702/702 +f 706/706/706 692/692/692 691/691/691 +f 691/691/691 707/707/707 706/706/706 +f 691/691/691 693/693/693 712/712/712 +f 712/712/712 707/707/707 691/691/691 +f 710/710/710 694/694/694 697/697/697 +f 697/697/697 713/713/713 710/710/710 +f 697/697/697 699/699/699 723/723/723 +f 723/723/723 713/713/713 697/697/697 +f 701/701/701 690/690/690 702/702/702 +f 702/702/702 714/714/714 701/701/701 +f 703/703/703 689/689/689 692/692/692 +f 692/692/692 706/706/706 703/703/703 +f 712/712/712 693/693/693 694/694/694 +f 694/694/694 710/710/710 712/712/712 +f 733/733/733 734/734/734 698/698/698 +f 698/698/698 696/696/696 733/733/733 +f 735/735/735 736/736/736 737/737/737 +f 737/737/737 738/738/738 735/735/735 +f 739/739/739 735/735/735 738/738/738 +f 740/740/740 741/741/741 742/742/742 +f 742/742/742 743/743/743 740/740/740 +f 744/744/744 745/745/745 741/741/741 +f 741/741/741 740/740/740 744/744/744 +f 746/746/746 747/747/747 748/748/748 +f 749/749/749 750/750/750 751/751/751 +f 746/746/746 744/744/744 752/752/752 +f 752/752/752 753/753/753 746/746/746 +f 749/749/749 753/753/753 752/752/752 +f 752/752/752 754/754/754 749/749/749 +f 736/736/736 750/750/750 749/749/749 +f 749/749/749 737/737/737 736/736/736 +f 755/755/755 756/756/756 757/757/757 +f 757/757/757 758/758/758 755/755/755 +f 745/745/745 744/744/744 746/746/746 +f 746/746/746 753/753/753 747/747/747 +f 759/759/759 753/753/753 751/751/751 +f 753/753/753 759/759/759 747/747/747 +f 753/753/753 749/749/749 751/751/751 +f 760/760/760 761/761/761 762/762/762 +f 760/760/760 762/762/762 757/757/757 +f 756/756/756 760/760/760 757/757/757 +f 763/763/763 764/764/764 765/765/765 +f 765/765/765 766/766/766 763/763/763 +f 767/767/767 768/768/768 769/769/769 +f 769/769/769 742/742/742 767/767/767 +f 770/770/770 771/771/771 767/767/767 +f 767/767/767 742/742/742 770/770/770 +f 772/772/772 755/755/755 758/758/758 +f 758/758/758 773/773/773 772/772/772 +f 772/772/772 773/773/773 764/764/764 +f 764/764/764 763/763/763 772/772/772 +f 774/774/774 743/743/743 742/742/742 +f 742/742/742 769/769/769 774/774/774 +f 775/775/775 776/776/776 735/735/735 +f 735/735/735 739/739/739 775/775/775 +f 777/777/777 778/778/778 779/779/779 +f 779/779/779 780/780/780 777/777/777 +f 752/752/752 781/781/781 782/782/782 +f 782/782/782 754/754/754 752/752/752 +f 783/783/783 784/784/784 785/785/785 +f 785/785/785 786/786/786 783/783/783 +f 787/787/787 783/783/783 786/786/786 +f 786/786/786 788/788/788 787/787/787 +f 789/789/789 787/787/787 788/788/788 +f 788/788/788 790/790/790 789/789/789 +f 781/781/781 789/789/789 790/790/790 +f 790/790/790 791/791/791 781/781/781 +f 792/792/792 778/778/778 782/782/782 +f 782/782/782 793/793/793 792/792/792 +f 794/794/794 745/745/745 746/746/746 +f 746/746/746 748/748/748 794/794/794 +f 745/745/745 794/794/794 770/770/770 +f 770/770/770 741/741/741 745/745/745 +f 741/741/741 770/770/770 742/742/742 +f 749/749/749 754/754/754 737/737/737 +f 754/754/754 777/777/777 738/738/738 +f 738/738/738 737/737/737 754/754/754 +f 777/777/777 780/780/780 739/739/739 +f 739/739/739 738/738/738 777/777/777 +f 792/792/792 795/795/795 779/779/779 +f 779/779/779 778/778/778 792/792/792 +f 781/781/781 791/791/791 793/793/793 +f 793/793/793 782/782/782 781/781/781 +f 743/743/743 774/774/774 784/784/784 +f 784/784/784 783/783/783 743/743/743 +f 740/740/740 743/743/743 783/783/783 +f 783/783/783 787/787/787 740/740/740 +f 744/744/744 740/740/740 787/787/787 +f 787/787/787 789/789/789 744/744/744 +f 752/752/752 744/744/744 789/789/789 +f 789/789/789 781/781/781 752/752/752 +f 777/777/777 754/754/754 782/782/782 +f 782/782/782 778/778/778 777/777/777 +f 336/336/336 335/335/335 796/796/796 +f 796/796/796 797/797/797 336/336/336 +f 798/798/798 799/799/799 800/800/800 +f 800/800/800 801/801/801 798/798/798 +f 335/335/335 802/802/802 803/803/803 +f 802/802/802 804/804/804 805/805/805 +f 805/805/805 803/803/803 802/802/802 +f 806/806/806 807/807/807 808/808/808 +f 805/805/805 809/809/809 810/810/810 +f 810/810/810 811/811/811 805/805/805 +f 810/810/810 812/812/812 811/811/811 +f 813/813/813 814/814/814 811/811/811 +f 811/811/811 812/812/812 813/813/813 +f 814/814/814 813/813/813 815/815/815 +f 815/815/815 816/816/816 814/814/814 +f 817/817/817 818/818/818 806/806/806 +f 806/806/806 818/818/818 819/819/819 +f 819/819/819 800/800/800 806/806/806 +f 800/800/800 819/819/819 820/820/820 +f 820/820/820 801/801/801 800/800/800 +f 800/800/800 799/799/799 807/807/807 +f 807/807/807 806/806/806 800/800/800 +f 806/806/806 808/808/808 821/821/821 +f 821/821/821 817/817/817 806/806/806 +f 817/817/817 821/821/821 822/822/822 +f 822/822/822 804/804/804 817/817/817 +f 811/811/811 823/823/823 805/805/805 +f 818/818/818 817/817/817 804/804/804 +f 804/804/804 802/802/802 818/818/818 +f 822/822/822 809/809/809 805/805/805 +f 805/805/805 804/804/804 822/822/822 +f 335/335/335 338/338/338 818/818/818 +f 818/818/818 802/802/802 335/335/335 +f 810/810/810 809/809/809 824/824/824 +f 824/824/824 825/825/825 810/810/810 +f 810/810/810 825/825/825 812/812/812 +f 808/808/808 826/826/826 827/827/827 +f 827/827/827 821/821/821 808/808/808 +f 821/821/821 827/827/827 828/828/828 +f 828/828/828 822/822/822 821/821/821 +f 809/809/809 822/822/822 828/828/828 +f 828/828/828 824/824/824 809/809/809 +f 798/798/798 829/829/829 830/830/830 +f 830/830/830 799/799/799 798/798/798 +f 799/799/799 830/830/830 831/831/831 +f 831/831/831 807/807/807 799/799/799 +f 807/807/807 831/831/831 826/826/826 +f 826/826/826 808/808/808 807/807/807 +f 796/796/796 814/814/814 816/816/816 +f 816/816/816 797/797/797 796/796/796 +f 796/796/796 335/335/335 803/803/803 +f 818/818/818 338/338/338 819/819/819 +f 819/819/819 338/338/338 340/340/340 +f 340/340/340 820/820/820 819/819/819 +f 832/832/832 833/833/833 834/834/834 +f 834/834/834 835/835/835 832/832/832 +f 834/834/834 836/836/836 837/837/837 +f 837/837/837 835/835/835 834/834/834 +f 838/838/838 833/833/833 832/832/832 +f 837/837/837 836/836/836 839/839/839 +f 837/837/837 839/839/839 825/825/825 +f 825/825/825 824/824/824 837/837/837 +f 826/826/826 838/838/838 832/832/832 +f 832/832/832 827/827/827 826/826/826 +f 827/827/827 832/832/832 835/835/835 +f 835/835/835 828/828/828 827/827/827 +f 835/835/835 837/837/837 824/824/824 +f 824/824/824 828/828/828 835/835/835 +f 829/829/829 840/840/840 841/841/841 +f 841/841/841 830/830/830 829/829/829 +f 830/830/830 841/841/841 842/842/842 +f 842/842/842 831/831/831 830/830/830 +f 831/831/831 842/842/842 838/838/838 +f 838/838/838 826/826/826 831/831/831 +f 825/825/825 839/839/839 812/812/812 +f 805/805/805 823/823/823 803/803/803 +f 803/803/803 823/823/823 796/796/796 +f 823/823/823 811/811/811 814/814/814 +f 814/814/814 796/796/796 823/823/823 +f 843/843/843 844/844/844 845/845/845 +f 846/846/846 843/843/843 845/845/845 +f 846/846/846 845/845/845 847/847/847 +f 848/848/848 849/849/849 850/850/850 +f 851/851/851 846/846/846 852/852/852 +f 849/849/849 848/848/848 853/853/853 +f 843/843/843 849/849/849 853/853/853 +f 844/844/844 843/843/843 853/853/853 +f 854/854/854 849/849/849 855/855/855 +f 843/843/843 856/856/856 857/857/857 +f 849/849/849 843/843/843 857/857/857 +f 849/849/849 857/857/857 858/858/858 +f 846/846/846 859/859/859 860/860/860 +f 843/843/843 846/846/846 860/860/860 +f 843/843/843 860/860/860 856/856/856 +f 846/846/846 851/851/851 861/861/861 +f 850/850/850 849/849/849 862/862/862 +f 863/863/863 846/846/846 847/847/847 +f 864/864/864 849/849/849 858/858/858 +f 846/846/846 865/865/865 859/859/859 +f 862/862/862 849/849/849 854/854/854 +f 855/855/855 849/849/849 864/864/864 +f 852/852/852 846/846/846 863/863/863 +f 846/846/846 861/861/861 865/865/865 +f 863/863/863 847/847/847 866/866/866 +f 848/848/848 850/850/850 867/867/867 +f 868/868/868 869/869/869 870/870/870 +f 870/870/870 871/871/871 868/868/868 +f 870/870/870 869/869/869 872/872/872 +f 872/872/872 873/873/873 870/870/870 +f 872/872/872 874/874/874 875/875/875 +f 875/875/875 873/873/873 872/872/872 +f 871/871/871 870/870/870 876/876/876 +f 870/870/870 873/873/873 876/876/876 +f 873/873/873 875/875/875 876/876/876 +f 877/877/877 878/878/878 879/879/879 +f 880/880/880 881/881/881 882/882/882 +f 883/883/883 881/881/881 880/880/880 +f 880/880/880 884/884/884 883/883/883 +f 879/879/879 885/885/885 886/886/886 +f 886/886/886 887/887/887 879/879/879 +f 885/885/885 882/882/882 888/888/888 +f 888/888/888 886/886/886 885/885/885 +f 881/881/881 889/889/889 888/888/888 +f 888/888/888 882/882/882 881/881/881 +f 883/883/883 890/890/890 889/889/889 +f 889/889/889 881/881/881 883/883/883 +f 891/891/891 892/892/892 887/887/887 +f 887/887/887 893/893/893 891/891/891 +f 894/894/894 891/891/891 893/893/893 +f 893/893/893 895/895/895 894/894/894 +f 896/896/896 894/894/894 895/895/895 +f 895/895/895 897/897/897 896/896/896 +f 898/898/898 896/896/896 897/897/897 +f 897/897/897 899/899/899 898/898/898 +f 879/879/879 900/900/900 877/877/877 +f 879/879/879 887/887/887 901/901/901 +f 901/901/901 900/900/900 879/879/879 +f 887/887/887 892/892/892 206/206/206 +f 206/206/206 901/901/901 887/887/887 +f 877/877/877 900/900/900 902/902/902 +f 902/902/902 903/903/903 877/877/877 +f 900/900/900 901/901/901 904/904/904 +f 904/904/904 902/902/902 900/900/900 +f 901/901/901 206/206/206 210/210/210 +f 210/210/210 904/904/904 901/901/901 +f 905/905/905 906/906/906 907/907/907 +f 908/908/908 909/909/909 910/910/910 +f 908/908/908 910/910/910 911/911/911 +f 911/911/911 912/912/912 908/908/908 +f 907/907/907 913/913/913 914/914/914 +f 914/914/914 908/908/908 907/907/907 +f 907/907/907 906/906/906 915/915/915 +f 913/913/913 907/907/907 915/915/915 +f 916/916/916 905/905/905 917/917/917 +f 918/918/918 919/919/919 920/920/920 +f 920/920/920 921/921/921 918/918/918 +f 922/922/922 908/908/908 912/912/912 +f 912/912/912 923/923/923 922/922/922 +f 908/908/908 922/922/922 924/924/924 +f 924/924/924 907/907/907 908/908/908 +f 924/924/924 925/925/925 905/905/905 +f 905/905/905 907/907/907 924/924/924 +f 925/925/925 926/926/926 927/927/927 +f 927/927/927 905/905/905 925/925/925 +f 928/928/928 929/929/929 921/921/921 +f 921/921/921 920/920/920 928/928/928 +f 919/919/919 918/918/918 930/930/930 +f 930/930/930 931/931/931 919/919/919 +f 931/931/931 930/930/930 932/932/932 +f 932/932/932 933/933/933 931/931/931 +f 912/912/912 934/934/934 935/935/935 +f 934/934/934 912/912/912 911/911/911 +f 936/936/936 937/937/937 938/938/938 +f 935/935/935 937/937/937 936/936/936 +f 936/936/936 912/912/912 935/935/935 +f 939/939/939 940/940/940 941/941/941 +f 938/938/938 940/940/940 936/936/936 +f 942/942/942 943/943/943 939/939/939 +f 944/944/944 936/936/936 939/939/939 +f 939/939/939 945/945/945 944/944/944 +f 912/912/912 936/936/936 944/944/944 +f 944/944/944 923/923/923 912/912/912 +f 945/945/945 939/939/939 946/946/946 +f 946/946/946 947/947/947 945/945/945 +f 948/948/948 949/949/949 950/950/950 +f 950/950/950 951/951/951 948/948/948 +f 952/952/952 950/950/950 949/949/949 +f 949/949/949 953/953/953 952/952/952 +f 953/953/953 933/933/933 932/932/932 +f 932/932/932 952/952/952 953/953/953 +f 940/940/940 939/939/939 936/936/936 +f 939/939/939 941/941/941 942/942/942 +f 943/943/943 946/946/946 939/939/939 +f 905/905/905 927/927/927 917/917/917 +f 914/914/914 909/909/909 908/908/908 +f 905/905/905 916/916/916 906/906/906 +f 954/954/954 955/955/955 956/956/956 +f 957/957/957 958/958/958 956/956/956 +f 959/959/959 960/960/960 956/956/956 +f 958/958/958 959/959/959 956/956/956 +f 961/961/961 954/954/954 956/956/956 +f 960/960/960 961/961/961 956/956/956 +f 955/955/955 962/962/962 956/956/956 +f 963/963/963 964/964/964 956/956/956 +f 965/965/965 966/966/966 956/956/956 +f 966/966/966 967/967/967 956/956/956 +f 968/968/968 969/969/969 956/956/956 +f 969/969/969 965/965/965 956/956/956 +f 964/964/964 970/970/970 956/956/956 +f 970/970/970 968/968/968 956/956/956 +f 962/962/962 963/963/963 956/956/956 +f 967/967/967 957/957/957 956/956/956 +f 920/920/920 919/919/919 971/971/971 +f 971/971/971 972/972/972 920/920/920 +f 928/928/928 920/920/920 972/972/972 +f 972/972/972 973/973/973 928/928/928 +f 919/919/919 931/931/931 974/974/974 +f 974/974/974 971/971/971 919/919/919 +f 931/931/931 933/933/933 975/975/975 +f 975/975/975 974/974/974 931/931/931 +f 949/949/949 948/948/948 976/976/976 +f 976/976/976 977/977/977 949/949/949 +f 953/953/953 949/949/949 977/977/977 +f 977/977/977 978/978/978 953/953/953 +f 933/933/933 953/953/953 978/978/978 +f 978/978/978 975/975/975 933/933/933 +f 979/979/979 980/980/980 981/981/981 +f 981/981/981 975/975/975 979/979/979 +f 288/288/288 971/971/971 982/982/982 +f 982/982/982 289/289/289 288/288/288 +f 983/983/983 984/984/984 985/985/985 +f 985/985/985 986/986/986 983/983/983 +f 295/295/295 987/987/987 988/988/988 +f 988/988/988 296/296/296 295/295/295 +f 989/989/989 990/990/990 991/991/991 +f 988/988/988 972/972/972 302/302/302 +f 302/302/302 296/296/296 988/988/988 +f 992/992/992 993/993/993 994/994/994 +f 994/994/994 995/995/995 996/996/996 +f 996/996/996 992/992/992 994/994/994 +f 995/995/995 985/985/985 984/984/984 +f 984/984/984 997/997/997 995/995/995 +f 998/998/998 994/994/994 993/993/993 +f 993/993/993 999/999/999 998/998/998 +f 1000/1000/1000 1001/1001/1001 1002/1002/1002 +f 1002/1002/1002 1003/1003/1003 1000/1000/1000 +f 998/998/998 999/999/999 1002/1002/1002 +f 1002/1002/1002 1001/1001/1001 998/998/998 +f 995/995/995 997/997/997 996/996/996 +f 288/288/288 302/302/302 972/972/972 +f 972/972/972 971/971/971 288/288/288 +f 977/977/977 990/990/990 1004/1004/1004 +f 988/988/988 973/973/973 972/972/972 +f 991/991/991 990/990/990 977/977/977 +f 977/977/977 976/976/976 991/991/991 +f 982/982/982 971/971/971 974/974/974 +f 904/904/904 982/982/982 974/974/974 +f 974/974/974 902/902/902 904/904/904 +f 982/982/982 904/904/904 210/210/210 +f 210/210/210 289/289/289 982/982/982 +f 903/903/903 902/902/902 974/974/974 +f 974/974/974 975/975/975 903/903/903 +f 877/877/877 903/903/903 1005/1005/1005 +f 1005/1005/1005 1006/1006/1006 877/877/877 +f 1006/1006/1006 880/880/880 877/877/877 +f 882/882/882 885/885/885 878/878/878 +f 878/878/878 880/880/880 882/882/882 +f 878/878/878 885/885/885 879/879/879 +f 1007/1007/1007 1008/1008/1008 1003/1003/1003 +f 1003/1003/1003 1009/1009/1009 1007/1007/1007 +f 323/323/323 322/322/322 1010/1010/1010 +f 1010/1010/1010 983/983/983 323/323/323 +f 1011/1011/1011 1010/1010/1010 322/322/322 +f 322/322/322 325/325/325 1011/1011/1011 +f 1012/1012/1012 1013/1013/1013 984/984/984 +f 1014/1014/1014 1015/1015/1015 1016/1016/1016 +f 1016/1016/1016 1017/1017/1017 1014/1014/1014 +f 983/983/983 1010/1010/1010 1012/1012/1012 +f 1018/1018/1018 1012/1012/1012 1010/1010/1010 +f 1010/1010/1010 1011/1011/1011 1018/1018/1018 +f 333/333/333 336/336/336 1019/1019/1019 +f 1019/1019/1019 1020/1020/1020 333/333/333 +f 1020/1020/1020 1019/1019/1019 1021/1021/1021 +f 1021/1021/1021 1022/1022/1022 1020/1020/1020 +f 339/339/339 1022/1022/1022 1021/1021/1021 +f 1021/1021/1021 340/340/340 339/339/339 +f 341/341/341 344/344/344 1023/1023/1023 +f 1023/1023/1023 1024/1024/1024 341/341/341 +f 1025/1025/1025 1026/1026/1026 993/993/993 +f 993/993/993 992/992/992 1025/1025/1025 +f 1026/1026/1026 1025/1025/1025 1027/1027/1027 +f 1027/1027/1027 1028/1028/1028 1026/1026/1026 +f 349/349/349 341/341/341 1024/1024/1024 +f 1024/1024/1024 1029/1029/1029 349/349/349 +f 992/992/992 996/996/996 1030/1030/1030 +f 1030/1030/1030 1025/1025/1025 992/992/992 +f 1031/1031/1031 1027/1027/1027 1025/1025/1025 +f 1025/1025/1025 1030/1030/1030 1031/1031/1031 +f 1029/1029/1029 1011/1011/1011 325/325/325 +f 325/325/325 349/349/349 1029/1029/1029 +f 996/996/996 997/997/997 1032/1032/1032 +f 1032/1032/1032 1030/1030/1030 996/996/996 +f 1033/1033/1033 1034/1034/1034 1031/1031/1031 +f 1035/1035/1035 1023/1023/1023 344/344/344 +f 344/344/344 357/357/357 1035/1035/1035 +f 1026/1026/1026 1014/1014/1014 999/999/999 +f 999/999/999 993/993/993 1026/1026/1026 +f 1026/1026/1026 1028/1028/1028 1014/1014/1014 +f 1017/1017/1017 1016/1016/1016 1036/1036/1036 +f 1036/1036/1036 1037/1037/1037 1017/1017/1017 +f 1017/1017/1017 1002/1002/1002 999/999/999 +f 999/999/999 1014/1014/1014 1017/1017/1017 +f 357/357/357 1038/1038/1038 1039/1039/1039 +f 1039/1039/1039 1035/1035/1035 357/357/357 +f 1040/1040/1040 1009/1009/1009 1003/1003/1003 +f 1003/1003/1003 1037/1037/1037 1040/1040/1040 +f 363/363/363 1041/1041/1041 1042/1042/1042 +f 1042/1042/1042 364/364/364 363/363/363 +f 1037/1037/1037 1036/1036/1036 1043/1043/1043 +f 1043/1043/1043 1040/1040/1040 1037/1037/1037 +f 1037/1037/1037 1003/1003/1003 1002/1002/1002 +f 1002/1002/1002 1017/1017/1017 1037/1037/1037 +f 364/364/364 1042/1042/1042 1039/1039/1039 +f 1039/1039/1039 1038/1038/1038 364/364/364 +f 1039/1039/1039 1016/1016/1016 1015/1015/1015 +f 1015/1015/1015 1035/1035/1035 1039/1039/1039 +f 1044/1044/1044 1045/1045/1045 1024/1024/1024 +f 1024/1024/1024 1023/1023/1023 1044/1044/1044 +f 1029/1029/1029 1024/1024/1024 1045/1045/1045 +f 1045/1045/1045 1034/1034/1034 1029/1029/1029 +f 1034/1034/1034 1018/1018/1018 1011/1011/1011 +f 1011/1011/1011 1029/1029/1029 1034/1034/1034 +f 1035/1035/1035 1015/1015/1015 1044/1044/1044 +f 1044/1044/1044 1023/1023/1023 1035/1035/1035 +f 1042/1042/1042 1036/1036/1036 1016/1016/1016 +f 1016/1016/1016 1039/1039/1039 1042/1042/1042 +f 1041/1041/1041 1043/1043/1043 1036/1036/1036 +f 1036/1036/1036 1042/1042/1042 1041/1041/1041 +f 1012/1012/1012 1018/1018/1018 1013/1013/1013 +f 1012/1012/1012 984/984/984 983/983/983 +f 1028/1028/1028 1027/1027/1027 1045/1045/1045 +f 1045/1045/1045 1044/1044/1044 1028/1028/1028 +f 1034/1034/1034 1045/1045/1045 1027/1027/1027 +f 1027/1027/1027 1031/1031/1031 1034/1034/1034 +f 1030/1030/1030 1032/1032/1032 1033/1033/1033 +f 1033/1033/1033 1031/1031/1031 1030/1030/1030 +f 1044/1044/1044 1015/1015/1015 1014/1014/1014 +f 1014/1014/1014 1028/1028/1028 1044/1044/1044 +f 1022/1022/1022 339/339/339 370/370/370 +f 1013/1013/1013 1032/1032/1032 997/997/997 +f 997/997/997 984/984/984 1013/1013/1013 +f 1018/1018/1018 1034/1034/1034 1033/1033/1033 +f 1033/1033/1033 1013/1013/1013 1018/1018/1018 +f 1033/1033/1033 1032/1032/1032 1013/1013/1013 +f 1008/1008/1008 1000/1000/1000 1003/1003/1003 +f 1046/1046/1046 1047/1047/1047 998/998/998 +f 994/994/994 1048/1048/1048 995/995/995 +f 1049/1049/1049 1048/1048/1048 994/994/994 +f 994/994/994 1050/1050/1050 1049/1049/1049 +f 1050/1050/1050 994/994/994 998/998/998 +f 998/998/998 1047/1047/1047 1050/1050/1050 +f 1051/1051/1051 1052/1052/1052 1053/1053/1053 +f 1054/1054/1054 1051/1051/1051 1053/1053/1053 +f 1055/1055/1055 1056/1056/1056 1057/1057/1057 +f 1057/1057/1057 1058/1058/1058 1055/1055/1055 +f 1056/1056/1056 1059/1059/1059 1057/1057/1057 +f 1059/1059/1059 1060/1060/1060 1061/1061/1061 +f 1060/1060/1060 1053/1053/1053 1061/1061/1061 +f 979/979/979 978/978/978 977/977/977 +f 977/977/977 1004/1004/1004 979/979/979 +f 990/990/990 1062/1062/1062 1004/1004/1004 +f 975/975/975 978/978/978 979/979/979 +f 1057/1057/1057 1061/1061/1061 1053/1053/1053 +f 1053/1053/1053 1058/1058/1058 1057/1057/1057 +f 1059/1059/1059 1061/1061/1061 1057/1057/1057 +f 1060/1060/1060 1054/1054/1054 1053/1053/1053 +f 1005/1005/1005 903/903/903 975/975/975 +f 975/975/975 981/981/981 1005/1005/1005 +f 1063/1063/1063 1064/1064/1064 1065/1065/1065 +f 1065/1065/1065 1066/1066/1066 1063/1063/1063 +f 392/392/392 393/393/393 1065/1065/1065 +f 1065/1065/1065 1064/1064/1064 392/392/392 +f 395/395/395 1067/1067/1067 1066/1066/1066 +f 393/393/393 395/395/395 1066/1066/1066 +f 1065/1065/1065 393/393/393 1066/1066/1066 +f 1068/1068/1068 1067/1067/1067 1069/1069/1069 +f 1069/1069/1069 1070/1070/1070 1068/1068/1068 +f 1071/1071/1071 1063/1063/1063 1066/1066/1066 +f 1066/1066/1066 1067/1067/1067 1071/1071/1071 +f 400/400/400 1070/1070/1070 1069/1069/1069 +f 1069/1069/1069 401/401/401 400/400/400 +f 1069/1069/1069 1067/1067/1067 395/395/395 +f 1070/1070/1070 1072/1072/1072 1068/1068/1068 +f 403/403/403 1072/1072/1072 1070/1070/1070 +f 1070/1070/1070 400/400/400 403/403/403 +f 1073/1073/1073 1068/1068/1068 1072/1072/1072 +f 1072/1072/1072 1074/1074/1074 1073/1073/1073 +f 403/403/403 406/406/406 1074/1074/1074 +f 1074/1074/1074 1072/1072/1072 403/403/403 +f 1067/1067/1067 1068/1068/1068 1073/1073/1073 +f 1073/1073/1073 1071/1071/1071 1067/1067/1067 +f 1069/1069/1069 395/395/395 401/401/401 +f 407/407/407 1075/1075/1075 408/408/408 +f 1076/1076/1076 1077/1077/1077 1078/1078/1078 +f 1079/1079/1079 1080/1080/1080 1077/1077/1077 +f 1077/1077/1077 1076/1076/1076 1079/1079/1079 +f 1079/1079/1079 1075/1075/1075 407/407/407 +f 407/407/407 1080/1080/1080 1079/1079/1079 +f 1075/1075/1075 1079/1079/1079 1081/1081/1081 +f 1078/1078/1078 1077/1077/1077 1082/1082/1082 +f 1082/1082/1082 1083/1083/1083 1078/1078/1078 +f 1084/1084/1084 1083/1083/1083 1082/1082/1082 +f 1082/1082/1082 1085/1085/1085 1084/1084/1084 +f 1083/1083/1083 1084/1084/1084 1086/1086/1086 +f 1086/1086/1086 1084/1084/1084 1087/1087/1087 +f 1084/1084/1084 1085/1085/1085 1088/1088/1088 +f 1088/1088/1088 1089/1089/1089 1084/1084/1084 +f 1084/1084/1084 1089/1089/1089 1087/1087/1087 +f 1090/1090/1090 1091/1091/1091 1092/1092/1092 +f 1092/1092/1092 1093/1093/1093 1090/1090/1090 +f 1094/1094/1094 1095/1095/1095 1090/1090/1090 +f 1090/1090/1090 1096/1096/1096 1094/1094/1094 +f 1095/1095/1095 1097/1097/1097 1091/1091/1091 +f 1091/1091/1091 1090/1090/1090 1095/1095/1095 +f 1090/1090/1090 1093/1093/1093 1098/1098/1098 +f 1098/1098/1098 1096/1096/1096 1090/1090/1090 +f 1094/1094/1094 1096/1096/1096 1099/1099/1099 +f 1096/1096/1096 1098/1098/1098 1100/1100/1100 +f 1100/1100/1100 1099/1099/1099 1096/1096/1096 +f 1101/1101/1101 1102/1102/1102 1103/1103/1103 +f 1103/1103/1103 1104/1104/1104 1101/1101/1101 +f 1105/1105/1105 1106/1106/1106 1102/1102/1102 +f 1102/1102/1102 1101/1101/1101 1105/1105/1105 +f 1107/1107/1107 1108/1108/1108 1106/1106/1106 +f 1106/1106/1106 1105/1105/1105 1107/1107/1107 +f 1102/1102/1102 1109/1109/1109 1110/1110/1110 +f 1110/1110/1110 1103/1103/1103 1102/1102/1102 +f 1106/1106/1106 1111/1111/1111 1109/1109/1109 +f 1109/1109/1109 1102/1102/1102 1106/1106/1106 +f 1108/1108/1108 1112/1112/1112 1111/1111/1111 +f 1111/1111/1111 1106/1106/1106 1108/1108/1108 +f 1109/1109/1109 1113/1113/1113 1114/1114/1114 +f 1114/1114/1114 1110/1110/1110 1109/1109/1109 +f 1111/1111/1111 1115/1115/1115 1113/1113/1113 +f 1113/1113/1113 1109/1109/1109 1111/1111/1111 +f 1112/1112/1112 1116/1116/1116 1115/1115/1115 +f 1115/1115/1115 1111/1111/1111 1112/1112/1112 +f 1105/1105/1105 1093/1093/1093 1092/1092/1092 +f 1092/1092/1092 1107/1107/1107 1105/1105/1105 +f 1101/1101/1101 1098/1098/1098 1093/1093/1093 +f 1093/1093/1093 1105/1105/1105 1101/1101/1101 +f 1104/1104/1104 1100/1100/1100 1098/1098/1098 +f 1098/1098/1098 1101/1101/1101 1104/1104/1104 +f 1117/1117/1117 1118/1118/1118 1119/1119/1119 +f 1120/1120/1120 1121/1121/1121 1122/1122/1122 +f 1123/1123/1123 1124/1124/1124 1125/1125/1125 +f 1126/1126/1126 1127/1127/1127 1128/1128/1128 +f 1128/1128/1128 1129/1129/1129 1126/1126/1126 +f 1119/1119/1119 1118/1118/1118 1130/1130/1130 +f 1130/1130/1130 1131/1131/1131 1119/1119/1119 +f 1129/1129/1129 1128/1128/1128 1132/1132/1132 +f 1129/1129/1129 1133/1133/1133 1126/1126/1126 +f 1134/1134/1134 1135/1135/1135 1136/1136/1136 +f 1136/1136/1136 1137/1137/1137 1134/1134/1134 +f 1138/1138/1138 1139/1139/1139 1140/1140/1140 +f 1140/1140/1140 1141/1141/1141 1138/1138/1138 +f 1142/1142/1142 1138/1138/1138 1141/1141/1141 +f 1141/1141/1141 1143/1143/1143 1142/1142/1142 +f 1136/1136/1136 1135/1135/1135 1144/1144/1144 +f 1144/1144/1144 1145/1145/1145 1136/1136/1136 +f 1146/1146/1146 1140/1140/1140 1147/1147/1147 +f 1148/1148/1148 1149/1149/1149 1143/1143/1143 +f 1123/1123/1123 1125/1125/1125 1118/1118/1118 +f 1120/1120/1120 1132/1132/1132 1121/1121/1121 +f 1150/1150/1150 1151/1151/1151 1152/1152/1152 +f 1152/1152/1152 1153/1153/1153 1150/1150/1150 +f 1130/1130/1130 1154/1154/1154 1155/1155/1155 +f 1155/1155/1155 1131/1131/1131 1130/1130/1130 +f 1132/1132/1132 1156/1156/1156 1151/1151/1151 +f 1151/1151/1151 1150/1150/1150 1132/1132/1132 +f 1125/1125/1125 1157/1157/1157 1154/1154/1154 +f 1154/1154/1154 1130/1130/1130 1125/1125/1125 +f 1127/1127/1127 1158/1158/1158 1159/1159/1159 +f 1159/1159/1159 1128/1128/1128 1127/1127/1127 +f 1153/1153/1153 1152/1152/1152 1157/1157/1157 +f 1157/1157/1157 1125/1125/1125 1153/1153/1153 +f 1128/1128/1128 1159/1159/1159 1156/1156/1156 +f 1156/1156/1156 1132/1132/1132 1128/1128/1128 +f 1152/1152/1152 1151/1151/1151 1160/1160/1160 +f 1160/1160/1160 1161/1161/1161 1152/1152/1152 +f 1155/1155/1155 1154/1154/1154 1162/1162/1162 +f 1162/1162/1162 1163/1163/1163 1155/1155/1155 +f 1155/1155/1155 1163/1163/1163 1164/1164/1164 +f 1164/1164/1164 1165/1165/1165 1155/1155/1155 +f 1156/1156/1156 1166/1166/1166 1160/1160/1160 +f 1160/1160/1160 1151/1151/1151 1156/1156/1156 +f 1154/1154/1154 1157/1157/1157 1167/1167/1167 +f 1167/1167/1167 1162/1162/1162 1154/1154/1154 +f 1158/1158/1158 1168/1168/1168 1169/1169/1169 +f 1169/1169/1169 1159/1159/1159 1158/1158/1158 +f 1152/1152/1152 1161/1161/1161 1167/1167/1167 +f 1167/1167/1167 1157/1157/1157 1152/1152/1152 +f 1156/1156/1156 1159/1159/1159 1169/1169/1169 +f 1169/1169/1169 1166/1166/1166 1156/1156/1156 +f 1146/1146/1146 1147/1147/1147 1170/1170/1170 +f 1170/1170/1170 1171/1171/1171 1146/1146/1146 +f 1141/1141/1141 1140/1140/1140 1172/1172/1172 +f 1143/1143/1143 1141/1141/1141 1172/1172/1172 +f 1172/1172/1172 1148/1148/1148 1143/1143/1143 +f 1170/1170/1170 1145/1145/1145 1171/1171/1171 +f 1137/1137/1137 1140/1140/1140 1139/1139/1139 +f 1139/1139/1139 1134/1134/1134 1137/1137/1137 +f 1142/1142/1142 1143/1143/1143 1149/1149/1149 +f 1149/1149/1149 1173/1173/1173 1142/1142/1142 +f 1174/1174/1174 1148/1148/1148 1175/1175/1175 +f 1175/1175/1175 1148/1148/1148 1172/1172/1172 +f 1172/1172/1172 1176/1176/1176 1175/1175/1175 +f 1172/1172/1172 1140/1140/1140 1177/1177/1177 +f 1178/1178/1178 1179/1179/1179 1180/1180/1180 +f 1180/1180/1180 1181/1181/1181 1178/1178/1178 +f 1182/1182/1182 1183/1183/1183 1184/1184/1184 +f 1184/1184/1184 1185/1185/1185 1182/1182/1182 +f 1185/1185/1185 1184/1184/1184 1186/1186/1186 +f 1186/1186/1186 1187/1187/1187 1185/1185/1185 +f 1187/1187/1187 1186/1186/1186 1188/1188/1188 +f 1188/1188/1188 1189/1189/1189 1187/1187/1187 +f 1190/1190/1190 1191/1191/1191 1192/1192/1192 +f 1192/1192/1192 1193/1193/1193 1190/1190/1190 +f 1193/1193/1193 1192/1192/1192 1179/1179/1179 +f 1179/1179/1179 1178/1178/1178 1193/1193/1193 +f 1179/1179/1179 1194/1194/1194 1195/1195/1195 +f 1195/1195/1195 1180/1180/1180 1179/1179/1179 +f 1183/1183/1183 1196/1196/1196 1197/1197/1197 +f 1184/1184/1184 1198/1198/1198 1199/1199/1199 +f 1200/1200/1200 1201/1201/1201 1202/1202/1202 +f 1191/1191/1191 1203/1203/1203 1192/1192/1192 +f 1192/1192/1192 1204/1204/1204 1194/1194/1194 +f 1186/1186/1186 1205/1205/1205 1206/1206/1206 +f 1207/1207/1207 1203/1203/1203 1191/1191/1191 +f 1191/1191/1191 1208/1208/1208 1207/1207/1207 +f 1203/1203/1203 1207/1207/1207 1209/1209/1209 +f 1209/1209/1209 1210/1210/1210 1203/1203/1203 +f 1211/1211/1211 1212/1212/1212 1205/1205/1205 +f 1205/1205/1205 1186/1186/1186 1211/1211/1211 +f 1213/1213/1213 1214/1214/1214 1215/1215/1215 +f 1216/1216/1216 1217/1217/1217 1218/1218/1218 +f 1199/1199/1199 1198/1198/1198 1219/1219/1219 +f 1219/1219/1219 1220/1220/1220 1199/1199/1199 +f 1221/1221/1221 1222/1222/1222 1223/1223/1223 +f 1224/1224/1224 1221/1221/1221 1223/1223/1223 +f 1223/1223/1223 1225/1225/1225 1224/1224/1224 +f 1204/1204/1204 1224/1224/1224 1226/1226/1226 +f 1226/1226/1226 1227/1227/1227 1204/1204/1204 +f 1228/1228/1228 1219/1219/1219 1229/1229/1229 +f 1229/1229/1229 1230/1230/1230 1228/1228/1228 +f 1222/1222/1222 1231/1231/1231 1232/1232/1232 +f 1232/1232/1232 1223/1223/1223 1222/1222/1222 +f 1223/1223/1223 1232/1232/1232 1217/1217/1217 +f 1217/1217/1217 1225/1225/1225 1223/1223/1223 +f 1225/1225/1225 1217/1217/1217 1216/1216/1216 +f 1216/1216/1216 1226/1226/1226 1225/1225/1225 +f 1194/1194/1194 1233/1233/1233 1234/1234/1234 +f 1234/1234/1234 1195/1195/1195 1194/1194/1194 +f 1197/1197/1197 1196/1196/1196 1235/1235/1235 +f 1235/1235/1235 1236/1236/1236 1197/1197/1197 +f 1237/1237/1237 1238/1238/1238 1239/1239/1239 +f 1239/1239/1239 1204/1204/1204 1237/1237/1237 +f 1204/1204/1204 1239/1239/1239 1233/1233/1233 +f 1233/1233/1233 1194/1194/1194 1204/1204/1204 +f 1233/1233/1233 1240/1240/1240 1234/1234/1234 +f 1241/1241/1241 1235/1235/1235 1242/1242/1242 +f 1242/1242/1242 1243/1243/1243 1241/1241/1241 +f 1238/1238/1238 1244/1244/1244 1245/1245/1245 +f 1245/1245/1245 1246/1246/1246 1238/1238/1238 +f 1239/1239/1239 1246/1246/1246 1240/1240/1240 +f 1240/1240/1240 1233/1233/1233 1239/1239/1239 +f 1247/1247/1247 1205/1205/1205 1248/1248/1248 +f 1248/1248/1248 1249/1249/1249 1247/1247/1247 +f 1208/1208/1208 1250/1250/1250 1251/1251/1251 +f 1251/1251/1251 1252/1252/1252 1208/1208/1208 +f 1209/1209/1209 1207/1207/1207 1252/1252/1252 +f 1252/1252/1252 1253/1253/1253 1209/1209/1209 +f 1209/1209/1209 1253/1253/1253 1254/1254/1254 +f 1255/1255/1255 1256/1256/1256 1247/1247/1247 +f 1247/1247/1247 1249/1249/1249 1255/1255/1255 +f 1186/1186/1186 1206/1206/1206 1188/1188/1188 +f 1188/1188/1188 1206/1206/1206 1256/1256/1256 +f 1205/1205/1205 1247/1247/1247 1206/1206/1206 +f 1247/1247/1247 1256/1256/1256 1206/1206/1206 +f 1200/1200/1200 1254/1254/1254 1253/1253/1253 +f 1253/1253/1253 1201/1201/1201 1200/1200/1200 +f 1253/1253/1253 1252/1252/1252 1251/1251/1251 +f 1251/1251/1251 1201/1201/1201 1253/1253/1253 +f 1208/1208/1208 1252/1252/1252 1207/1207/1207 +f 1211/1211/1211 1199/1199/1199 1220/1220/1220 +f 1220/1220/1220 1257/1257/1257 1211/1211/1211 +f 1257/1257/1257 1228/1228/1228 1230/1230/1230 +f 1230/1230/1230 1258/1258/1258 1257/1257/1257 +f 1198/1198/1198 1197/1197/1197 1236/1236/1236 +f 1236/1236/1236 1259/1259/1259 1198/1198/1198 +f 1259/1259/1259 1241/1241/1241 1243/1243/1243 +f 1243/1243/1243 1260/1260/1260 1259/1259/1259 +f 1183/1183/1183 1197/1197/1197 1184/1184/1184 +f 1184/1184/1184 1197/1197/1197 1198/1198/1198 +f 1186/1186/1186 1199/1199/1199 1211/1211/1211 +f 1184/1184/1184 1199/1199/1199 1186/1186/1186 +f 1192/1192/1192 1203/1203/1203 1204/1204/1204 +f 1192/1192/1192 1194/1194/1194 1179/1179/1179 +f 1257/1257/1257 1220/1220/1220 1228/1228/1228 +f 1210/1210/1210 1222/1222/1222 1221/1221/1221 +f 1221/1221/1221 1203/1203/1203 1210/1210/1210 +f 1220/1220/1220 1219/1219/1219 1228/1228/1228 +f 1221/1221/1221 1224/1224/1224 1204/1204/1204 +f 1204/1204/1204 1203/1203/1203 1221/1221/1221 +f 1226/1226/1226 1224/1224/1224 1225/1225/1225 +f 1218/1218/1218 1217/1217/1217 1232/1232/1232 +f 1218/1218/1218 1232/1232/1232 1231/1231/1231 +f 1202/1202/1202 1201/1201/1201 1251/1251/1251 +f 1202/1202/1202 1251/1251/1251 1250/1250/1250 +f 1215/1215/1215 1245/1245/1245 1244/1244/1244 +f 1215/1215/1215 1214/1214/1214 1245/1245/1245 +f 1261/1261/1261 1139/1139/1139 1262/1262/1262 +f 1263/1263/1263 1142/1142/1142 1173/1173/1173 +f 1173/1173/1173 1264/1264/1264 1263/1263/1263 +f 1264/1264/1264 1173/1173/1173 1265/1265/1265 +f 1266/1266/1266 1134/1134/1134 1139/1139/1139 +f 1139/1139/1139 1261/1261/1261 1266/1266/1266 +f 1267/1267/1267 1138/1138/1138 1142/1142/1142 +f 1142/1142/1142 1263/1263/1263 1267/1267/1267 +f 1268/1268/1268 1144/1144/1144 1135/1135/1135 +f 1135/1135/1135 1269/1269/1269 1268/1268/1268 +f 1262/1262/1262 1139/1139/1139 1138/1138/1138 +f 1138/1138/1138 1267/1267/1267 1262/1262/1262 +f 1269/1269/1269 1135/1135/1135 1134/1134/1134 +f 1134/1134/1134 1266/1266/1266 1269/1269/1269 +f 1140/1140/1140 1146/1146/1146 1177/1177/1177 +f 1140/1140/1140 1137/1137/1137 1147/1147/1147 +f 1146/1146/1146 1171/1171/1171 1270/1270/1270 +f 1270/1270/1270 1271/1271/1271 1146/1146/1146 +f 1171/1171/1171 1145/1145/1145 1272/1272/1272 +f 1147/1147/1147 1137/1137/1137 1136/1136/1136 +f 1136/1136/1136 1170/1170/1170 1147/1147/1147 +f 1136/1136/1136 1145/1145/1145 1170/1170/1170 +f 1259/1259/1259 1236/1236/1236 1241/1241/1241 +f 1236/1236/1236 1235/1235/1235 1241/1241/1241 +f 1214/1214/1214 1240/1240/1240 1246/1246/1246 +f 1246/1246/1246 1245/1245/1245 1214/1214/1214 +f 1213/1213/1213 1234/1234/1234 1240/1240/1240 +f 1240/1240/1240 1214/1214/1214 1213/1213/1213 +f 1238/1238/1238 1246/1246/1246 1239/1239/1239 +f 1176/1176/1176 1177/1177/1177 1273/1273/1273 +f 1273/1273/1273 1274/1274/1274 1176/1176/1176 +f 1148/1148/1148 1174/1174/1174 1149/1149/1149 +f 1177/1177/1177 1176/1176/1176 1172/1172/1172 +f 1174/1174/1174 1175/1175/1175 1275/1275/1275 +f 1275/1275/1275 1276/1276/1276 1174/1174/1174 +f 1275/1275/1275 1175/1175/1175 1176/1176/1176 +f 1176/1176/1176 1274/1274/1274 1275/1275/1275 +f 1177/1177/1177 1146/1146/1146 1271/1271/1271 +f 1271/1271/1271 1273/1273/1273 1177/1177/1177 +f 1270/1270/1270 1171/1171/1171 1272/1272/1272 +f 1272/1272/1272 1277/1277/1277 1270/1270/1270 +f 1124/1124/1124 1121/1121/1121 1150/1150/1150 +f 1150/1150/1150 1153/1153/1153 1124/1124/1124 +f 1121/1121/1121 1132/1132/1132 1150/1150/1150 +f 1125/1125/1125 1124/1124/1124 1153/1153/1153 +f 1118/1118/1118 1125/1125/1125 1130/1130/1130 +f 1129/1129/1129 1132/1132/1132 1120/1120/1120 +f 1123/1123/1123 1122/1122/1122 1121/1121/1121 +f 1121/1121/1121 1124/1124/1124 1123/1123/1123 +f 1161/1161/1161 1160/1160/1160 1261/1261/1261 +f 1261/1261/1261 1262/1262/1262 1161/1161/1161 +f 1163/1163/1163 1162/1162/1162 1263/1263/1263 +f 1263/1263/1263 1264/1264/1264 1163/1163/1163 +f 1265/1265/1265 1164/1164/1164 1163/1163/1163 +f 1163/1163/1163 1264/1264/1264 1265/1265/1265 +f 1261/1261/1261 1160/1160/1160 1166/1166/1166 +f 1166/1166/1166 1266/1266/1266 1261/1261/1261 +f 1162/1162/1162 1167/1167/1167 1267/1267/1267 +f 1267/1267/1267 1263/1263/1263 1162/1162/1162 +f 1269/1269/1269 1169/1169/1169 1168/1168/1168 +f 1168/1168/1168 1268/1268/1268 1269/1269/1269 +f 1267/1267/1267 1167/1167/1167 1161/1161/1161 +f 1161/1161/1161 1262/1262/1262 1267/1267/1267 +f 1266/1266/1266 1166/1166/1166 1169/1169/1169 +f 1169/1169/1169 1269/1269/1269 1266/1266/1266 +f 1278/1278/1278 1119/1119/1119 1131/1131/1131 +f 1131/1131/1131 1279/1279/1279 1278/1278/1278 +f 1131/1131/1131 1155/1155/1155 1165/1165/1165 +f 1165/1165/1165 1279/1279/1279 1131/1131/1131 +f 1280/1280/1280 1281/1281/1281 1282/1282/1282 +f 1282/1282/1282 1283/1283/1283 1280/1280/1280 +f 675/675/675 678/678/678 1284/1284/1284 +f 1284/1284/1284 1285/1285/1285 675/675/675 +f 1285/1285/1285 1284/1284/1284 1286/1286/1286 +f 1283/1283/1283 1287/1287/1287 1288/1288/1288 +f 1288/1288/1288 1289/1289/1289 1283/1283/1283 +f 1289/1289/1289 1288/1288/1288 1290/1290/1290 +f 1290/1290/1290 1291/1291/1291 1289/1289/1289 +f 1292/1292/1292 1290/1290/1290 1293/1293/1293 +f 687/687/687 1292/1292/1292 1293/1293/1293 +f 1293/1293/1293 1294/1294/1294 687/687/687 +f 1284/1284/1284 1295/1295/1295 1296/1296/1296 +f 1296/1296/1296 1286/1286/1286 1284/1284/1284 +f 1287/1287/1287 1297/1297/1297 1298/1298/1298 +f 1298/1298/1298 1288/1288/1288 1287/1287/1287 +f 1288/1288/1288 1298/1298/1298 1299/1299/1299 +f 1299/1299/1299 1290/1290/1290 1288/1288/1288 +f 1300/1300/1300 1301/1301/1301 1302/1302/1302 +f 1302/1302/1302 1303/1303/1303 1300/1300/1300 +f 1301/1301/1301 1304/1304/1304 1305/1305/1305 +f 1305/1305/1305 1302/1302/1302 1301/1301/1301 +f 1306/1306/1306 1307/1307/1307 1295/1295/1295 +f 1295/1295/1295 1284/1284/1284 1306/1306/1306 +f 1296/1296/1296 1297/1297/1297 1287/1287/1287 +f 1287/1287/1287 1286/1286/1286 1296/1296/1296 +f 1299/1299/1299 1300/1300/1300 1303/1303/1303 +f 1303/1303/1303 1290/1290/1290 1299/1299/1299 +f 1308/1308/1308 1309/1309/1309 1310/1310/1310 +f 1310/1310/1310 1311/1311/1311 1308/1308/1308 +f 1311/1311/1311 1310/1310/1310 1312/1312/1312 +f 1313/1313/1313 1314/1314/1314 1315/1315/1315 +f 1316/1316/1316 1317/1317/1317 1315/1315/1315 +f 1315/1315/1315 1318/1318/1318 1316/1316/1316 +f 1316/1316/1316 1318/1318/1318 1319/1319/1319 +f 1320/1320/1320 1309/1309/1309 1308/1308/1308 +f 1309/1309/1309 1321/1321/1321 1322/1322/1322 +f 1322/1322/1322 1310/1310/1310 1309/1309/1309 +f 1322/1322/1322 1323/1323/1323 1312/1312/1312 +f 1312/1312/1312 1310/1310/1310 1322/1322/1322 +f 1313/1313/1313 1312/1312/1312 1323/1323/1323 +f 1323/1323/1323 1324/1324/1324 1313/1313/1313 +f 1324/1324/1324 1325/1325/1325 1314/1314/1314 +f 1314/1314/1314 1313/1313/1313 1324/1324/1324 +f 1325/1325/1325 1326/1326/1326 1327/1327/1327 +f 1327/1327/1327 1314/1314/1314 1325/1325/1325 +f 1327/1327/1327 1326/1326/1326 1328/1328/1328 +f 1328/1328/1328 1319/1319/1319 1327/1327/1327 +f 1329/1329/1329 1319/1319/1319 1328/1328/1328 +f 1328/1328/1328 1330/1330/1330 1329/1329/1329 +f 1331/1331/1331 1321/1321/1321 1309/1309/1309 +f 1309/1309/1309 1320/1320/1320 1331/1331/1331 +f 1285/1285/1285 1286/1286/1286 1283/1283/1283 +f 1281/1281/1281 727/727/727 726/726/726 +f 726/726/726 1282/1282/1282 1281/1281/1281 +f 1289/1289/1289 1332/1332/1332 1280/1280/1280 +f 1280/1280/1280 1283/1283/1283 1289/1289/1289 +f 729/729/729 1333/1333/1333 1292/1292/1292 +f 1292/1292/1292 687/687/687 729/729/729 +f 1292/1292/1292 1333/1333/1333 1334/1334/1334 +f 1334/1334/1334 1291/1291/1291 1292/1292/1292 +f 1282/1282/1282 726/726/726 675/675/675 +f 675/675/675 1285/1285/1285 1282/1282/1282 +f 1294/1294/1294 1303/1303/1303 733/733/733 +f 1318/1318/1318 1315/1315/1315 1314/1314/1314 +f 1314/1314/1314 1327/1327/1327 1318/1318/1318 +f 1313/1313/1313 1315/1315/1315 1317/1317/1317 +f 1319/1319/1319 1318/1318/1318 1327/1327/1327 +f 732/732/732 1306/1306/1306 1284/1284/1284 +f 1284/1284/1284 678/678/678 732/732/732 +f 1291/1291/1291 1334/1334/1334 1332/1332/1332 +f 1332/1332/1332 1289/1289/1289 1291/1291/1291 +f 1291/1291/1291 1290/1290/1290 1292/1292/1292 +f 1283/1283/1283 1286/1286/1286 1287/1287/1287 +f 1285/1285/1285 1283/1283/1283 1282/1282/1282 +f 1308/1308/1308 1311/1311/1311 1296/1296/1296 +f 1296/1296/1296 1295/1295/1295 1308/1308/1308 +f 1312/1312/1312 1313/1313/1313 1298/1298/1298 +f 1298/1298/1298 1297/1297/1297 1312/1312/1312 +f 1298/1298/1298 1313/1313/1313 1317/1317/1317 +f 1317/1317/1317 1299/1299/1299 1298/1298/1298 +f 1316/1316/1316 1319/1319/1319 1301/1301/1301 +f 1301/1301/1301 1300/1300/1300 1316/1316/1316 +f 1301/1301/1301 1319/1319/1319 1329/1329/1329 +f 1329/1329/1329 1304/1304/1304 1301/1301/1301 +f 1307/1307/1307 1320/1320/1320 1308/1308/1308 +f 1308/1308/1308 1295/1295/1295 1307/1307/1307 +f 1311/1311/1311 1312/1312/1312 1297/1297/1297 +f 1297/1297/1297 1296/1296/1296 1311/1311/1311 +f 1317/1317/1317 1316/1316/1316 1300/1300/1300 +f 1300/1300/1300 1299/1299/1299 1317/1317/1317 +f 733/733/733 1302/1302/1302 1305/1305/1305 +f 1305/1305/1305 734/734/734 733/733/733 +f 1335/1335/1335 1336/1336/1336 1337/1337/1337 +f 1337/1337/1337 1338/1338/1338 1335/1335/1335 +f 1339/1339/1339 1336/1336/1336 1335/1335/1335 +f 1340/1340/1340 1341/1341/1341 1342/1342/1342 +f 1342/1342/1342 1343/1343/1343 1340/1340/1340 +f 1344/1344/1344 1340/1340/1340 1343/1343/1343 +f 1343/1343/1343 1345/1345/1345 1344/1344/1344 +f 1346/1346/1346 1347/1347/1347 1348/1348/1348 +f 1349/1349/1349 1350/1350/1350 1351/1351/1351 +f 1346/1346/1346 1352/1352/1352 1353/1353/1353 +f 1353/1353/1353 1344/1344/1344 1346/1346/1346 +f 1349/1349/1349 1354/1354/1354 1353/1353/1353 +f 1353/1353/1353 1352/1352/1352 1349/1349/1349 +f 1338/1338/1338 1337/1337/1337 1349/1349/1349 +f 1349/1349/1349 1351/1351/1351 1338/1338/1338 +f 1355/1355/1355 1356/1356/1356 1357/1357/1357 +f 1357/1357/1357 1358/1358/1358 1355/1355/1355 +f 1345/1345/1345 1346/1346/1346 1344/1344/1344 +f 1346/1346/1346 1348/1348/1348 1352/1352/1352 +f 1359/1359/1359 1350/1350/1350 1352/1352/1352 +f 1352/1352/1352 1348/1348/1348 1359/1359/1359 +f 1352/1352/1352 1350/1350/1350 1349/1349/1349 +f 1360/1360/1360 1361/1361/1361 1362/1362/1362 +f 1357/1357/1357 1360/1360/1360 1362/1362/1362 +f 1358/1358/1358 1357/1357/1357 1362/1362/1362 +f 1363/1363/1363 1364/1364/1364 1365/1365/1365 +f 1365/1365/1365 1366/1366/1366 1363/1363/1363 +f 1367/1367/1367 1342/1342/1342 1368/1368/1368 +f 1368/1368/1368 1369/1369/1369 1367/1367/1367 +f 1370/1370/1370 1342/1342/1342 1367/1367/1367 +f 1367/1367/1367 1371/1371/1371 1370/1370/1370 +f 1372/1372/1372 1373/1373/1373 1356/1356/1356 +f 1356/1356/1356 1355/1355/1355 1372/1372/1372 +f 1372/1372/1372 1363/1363/1363 1366/1366/1366 +f 1366/1366/1366 1373/1373/1373 1372/1372/1372 +f 1374/1374/1374 1368/1368/1368 1342/1342/1342 +f 1342/1342/1342 1341/1341/1341 1374/1374/1374 +f 1375/1375/1375 1339/1339/1339 1335/1335/1335 +f 1335/1335/1335 1376/1376/1376 1375/1375/1375 +f 1377/1377/1377 1378/1378/1378 1379/1379/1379 +f 1379/1379/1379 1380/1380/1380 1377/1377/1377 +f 1353/1353/1353 1354/1354/1354 1381/1381/1381 +f 1381/1381/1381 1382/1382/1382 1353/1353/1353 +f 1383/1383/1383 1384/1384/1384 1385/1385/1385 +f 1385/1385/1385 1386/1386/1386 1383/1383/1383 +f 1387/1387/1387 1388/1388/1388 1384/1384/1384 +f 1384/1384/1384 1383/1383/1383 1387/1387/1387 +f 1389/1389/1389 1390/1390/1390 1388/1388/1388 +f 1388/1388/1388 1387/1387/1387 1389/1389/1389 +f 1382/1382/1382 1391/1391/1391 1390/1390/1390 +f 1390/1390/1390 1389/1389/1389 1382/1382/1382 +f 1392/1392/1392 1393/1393/1393 1381/1381/1381 +f 1381/1381/1381 1380/1380/1380 1392/1392/1392 +f 1394/1394/1394 1347/1347/1347 1346/1346/1346 +f 1346/1346/1346 1345/1345/1345 1394/1394/1394 +f 1345/1345/1345 1343/1343/1343 1370/1370/1370 +f 1370/1370/1370 1394/1394/1394 1345/1345/1345 +f 1343/1343/1343 1342/1342/1342 1370/1370/1370 +f 1349/1349/1349 1337/1337/1337 1354/1354/1354 +f 1354/1354/1354 1337/1337/1337 1336/1336/1336 +f 1336/1336/1336 1377/1377/1377 1354/1354/1354 +f 1377/1377/1377 1336/1336/1336 1339/1339/1339 +f 1339/1339/1339 1378/1378/1378 1377/1377/1377 +f 1392/1392/1392 1380/1380/1380 1379/1379/1379 +f 1379/1379/1379 1395/1395/1395 1392/1392/1392 +f 1382/1382/1382 1381/1381/1381 1393/1393/1393 +f 1393/1393/1393 1391/1391/1391 1382/1382/1382 +f 1341/1341/1341 1383/1383/1383 1386/1386/1386 +f 1386/1386/1386 1374/1374/1374 1341/1341/1341 +f 1340/1340/1340 1387/1387/1387 1383/1383/1383 +f 1383/1383/1383 1341/1341/1341 1340/1340/1340 +f 1344/1344/1344 1389/1389/1389 1387/1387/1387 +f 1387/1387/1387 1340/1340/1340 1344/1344/1344 +f 1353/1353/1353 1382/1382/1382 1389/1389/1389 +f 1389/1389/1389 1344/1344/1344 1353/1353/1353 +f 1377/1377/1377 1380/1380/1380 1381/1381/1381 +f 1381/1381/1381 1354/1354/1354 1377/1377/1377 +f 336/336/336 797/797/797 1396/1396/1396 +f 1396/1396/1396 1019/1019/1019 336/336/336 +f 798/798/798 801/801/801 1397/1397/1397 +f 1397/1397/1397 1398/1398/1398 798/798/798 +f 1019/1019/1019 1399/1399/1399 1400/1400/1400 +f 1400/1400/1400 1399/1399/1399 1401/1401/1401 +f 1401/1401/1401 1402/1402/1402 1400/1400/1400 +f 1403/1403/1403 1404/1404/1404 1405/1405/1405 +f 1401/1401/1401 1406/1406/1406 1407/1407/1407 +f 1407/1407/1407 1408/1408/1408 1401/1401/1401 +f 1407/1407/1407 1406/1406/1406 1409/1409/1409 +f 1406/1406/1406 1410/1410/1410 1411/1411/1411 +f 1411/1411/1411 1409/1409/1409 1406/1406/1406 +f 1410/1410/1410 816/816/816 815/815/815 +f 815/815/815 1411/1411/1411 1410/1410/1410 +f 1412/1412/1412 1403/1403/1403 1413/1413/1413 +f 1403/1403/1403 1397/1397/1397 1414/1414/1414 +f 1414/1414/1414 1413/1413/1413 1403/1403/1403 +f 1397/1397/1397 801/801/801 820/820/820 +f 820/820/820 1414/1414/1414 1397/1397/1397 +f 1397/1397/1397 1403/1403/1403 1405/1405/1405 +f 1405/1405/1405 1398/1398/1398 1397/1397/1397 +f 1403/1403/1403 1412/1412/1412 1415/1415/1415 +f 1415/1415/1415 1404/1404/1404 1403/1403/1403 +f 1412/1412/1412 1402/1402/1402 1416/1416/1416 +f 1416/1416/1416 1415/1415/1415 1412/1412/1412 +f 1406/1406/1406 1401/1401/1401 1417/1417/1417 +f 1413/1413/1413 1400/1400/1400 1402/1402/1402 +f 1402/1402/1402 1412/1412/1412 1413/1413/1413 +f 1416/1416/1416 1402/1402/1402 1401/1401/1401 +f 1401/1401/1401 1408/1408/1408 1416/1416/1416 +f 1019/1019/1019 1400/1400/1400 1413/1413/1413 +f 1413/1413/1413 1021/1021/1021 1019/1019/1019 +f 1407/1407/1407 1418/1418/1418 1419/1419/1419 +f 1419/1419/1419 1408/1408/1408 1407/1407/1407 +f 1407/1407/1407 1409/1409/1409 1418/1418/1418 +f 1404/1404/1404 1415/1415/1415 1420/1420/1420 +f 1420/1420/1420 1421/1421/1421 1404/1404/1404 +f 1415/1415/1415 1416/1416/1416 1422/1422/1422 +f 1422/1422/1422 1420/1420/1420 1415/1415/1415 +f 1408/1408/1408 1419/1419/1419 1422/1422/1422 +f 1422/1422/1422 1416/1416/1416 1408/1408/1408 +f 798/798/798 1398/1398/1398 1423/1423/1423 +f 1423/1423/1423 829/829/829 798/798/798 +f 1398/1398/1398 1405/1405/1405 1424/1424/1424 +f 1424/1424/1424 1423/1423/1423 1398/1398/1398 +f 1405/1405/1405 1404/1404/1404 1421/1421/1421 +f 1421/1421/1421 1424/1424/1424 1405/1405/1405 +f 1396/1396/1396 797/797/797 816/816/816 +f 816/816/816 1410/1410/1410 1396/1396/1396 +f 1396/1396/1396 1399/1399/1399 1019/1019/1019 +f 1413/1413/1413 1414/1414/1414 1021/1021/1021 +f 1414/1414/1414 820/820/820 340/340/340 +f 340/340/340 1021/1021/1021 1414/1414/1414 +f 1425/1425/1425 1426/1426/1426 1427/1427/1427 +f 1427/1427/1427 1428/1428/1428 1425/1425/1425 +f 1427/1427/1427 1426/1426/1426 1429/1429/1429 +f 1429/1429/1429 1430/1430/1430 1427/1427/1427 +f 1431/1431/1431 1425/1425/1425 1428/1428/1428 +f 1429/1429/1429 1432/1432/1432 1430/1430/1430 +f 1429/1429/1429 1419/1419/1419 1418/1418/1418 +f 1418/1418/1418 1432/1432/1432 1429/1429/1429 +f 1421/1421/1421 1420/1420/1420 1425/1425/1425 +f 1425/1425/1425 1431/1431/1431 1421/1421/1421 +f 1420/1420/1420 1422/1422/1422 1426/1426/1426 +f 1426/1426/1426 1425/1425/1425 1420/1420/1420 +f 1426/1426/1426 1422/1422/1422 1419/1419/1419 +f 1419/1419/1419 1429/1429/1429 1426/1426/1426 +f 829/829/829 1423/1423/1423 1433/1433/1433 +f 1433/1433/1433 840/840/840 829/829/829 +f 1423/1423/1423 1424/1424/1424 1434/1434/1434 +f 1434/1434/1434 1433/1433/1433 1423/1423/1423 +f 1424/1424/1424 1421/1421/1421 1431/1431/1431 +f 1431/1431/1431 1434/1434/1434 1424/1424/1424 +f 1418/1418/1418 1409/1409/1409 1432/1432/1432 +f 1401/1401/1401 1399/1399/1399 1417/1417/1417 +f 1399/1399/1399 1396/1396/1396 1417/1417/1417 +f 1417/1417/1417 1396/1396/1396 1410/1410/1410 +f 1410/1410/1410 1406/1406/1406 1417/1417/1417 +f 1435/1435/1435 1436/1436/1436 1437/1437/1437 +f 1437/1437/1437 1438/1438/1438 1439/1439/1439 +f 1435/1435/1435 1437/1437/1437 1439/1439/1439 +f 1440/1440/1440 1441/1441/1441 1442/1442/1442 +f 1443/1443/1443 1444/1444/1444 1435/1435/1435 +f 1445/1445/1445 1440/1440/1440 1442/1442/1442 +f 1445/1445/1445 1442/1442/1442 1439/1439/1439 +f 1438/1438/1438 1445/1445/1445 1439/1439/1439 +f 1446/1446/1446 1447/1447/1447 1442/1442/1442 +f 1442/1442/1442 1448/1448/1448 1449/1449/1449 +f 1449/1449/1449 1450/1450/1450 1439/1439/1439 +f 1442/1442/1442 1449/1449/1449 1439/1439/1439 +f 1439/1439/1439 1450/1450/1450 1451/1451/1451 +f 1451/1451/1451 1452/1452/1452 1435/1435/1435 +f 1439/1439/1439 1451/1451/1451 1435/1435/1435 +f 1435/1435/1435 1453/1453/1453 1443/1443/1443 +f 1441/1441/1441 1454/1454/1454 1442/1442/1442 +f 1455/1455/1455 1436/1436/1436 1435/1435/1435 +f 1456/1456/1456 1448/1448/1448 1442/1442/1442 +f 1435/1435/1435 1452/1452/1452 1457/1457/1457 +f 1454/1454/1454 1446/1446/1446 1442/1442/1442 +f 1447/1447/1447 1456/1456/1456 1442/1442/1442 +f 1444/1444/1444 1455/1455/1455 1435/1435/1435 +f 1435/1435/1435 1457/1457/1457 1453/1453/1453 +f 1455/1455/1455 1458/1458/1458 1436/1436/1436 +f 1440/1440/1440 1459/1459/1459 1441/1441/1441 +f 1460/1460/1460 1461/1461/1461 1462/1462/1462 +f 1462/1462/1462 1463/1463/1463 1460/1460/1460 +f 1462/1462/1462 1464/1464/1464 1465/1465/1465 +f 1465/1465/1465 1463/1463/1463 1462/1462/1462 +f 1465/1465/1465 1464/1464/1464 1466/1466/1466 +f 1466/1466/1466 1467/1467/1467 1465/1465/1465 +f 1461/1461/1461 1468/1468/1468 1462/1462/1462 +f 1462/1462/1462 1468/1468/1468 1464/1464/1464 +f 1464/1464/1464 1468/1468/1468 1466/1466/1466 +f 1469/1469/1469 1470/1470/1470 1471/1471/1471 +f 1470/1470/1470 1472/1472/1472 1473/1473/1473 +f 1473/1473/1473 1471/1471/1471 1470/1470/1470 +f 1470/1470/1470 1474/1474/1474 1475/1475/1475 +f 1475/1475/1475 1476/1476/1476 1472/1472/1472 +f 1470/1470/1470 1475/1475/1475 1472/1472/1472 +f 1477/1477/1477 1478/1478/1478 1479/1479/1479 +f 1479/1479/1479 1476/1476/1476 1477/1477/1477 +f 1480/1480/1480 1477/1477/1477 1476/1476/1476 +f 1476/1476/1476 1481/1481/1481 1480/1480/1480 +f 1481/1481/1481 1482/1482/1482 1483/1483/1483 +f 195/195/195 197/197/197 1484/1484/1484 +f 1484/1484/1484 1485/1485/1485 195/195/195 +f 198/198/198 195/195/195 1485/1485/1485 +f 1485/1485/1485 1486/1486/1486 198/198/198 +f 1487/1487/1487 202/202/202 200/200/200 +f 1488/1488/1488 1489/1489/1489 1478/1478/1478 +f 1478/1478/1478 1477/1477/1477 1488/1488/1488 +f 1490/1490/1490 1488/1488/1488 1477/1477/1477 +f 1477/1477/1477 1480/1480/1480 1490/1490/1490 +f 1480/1480/1480 1491/1491/1491 1492/1492/1492 +f 1493/1493/1493 1494/1494/1494 1495/1495/1495 +f 1494/1494/1494 1496/1496/1496 1497/1497/1497 +f 1497/1497/1497 1495/1495/1495 1494/1494/1494 +f 1498/1498/1498 1499/1499/1499 1500/1500/1500 +f 1488/1488/1488 1499/1499/1499 1501/1501/1501 +f 1501/1501/1501 1489/1489/1489 1488/1488/1488 +f 1490/1490/1490 1500/1500/1500 1499/1499/1499 +f 1499/1499/1499 1488/1488/1488 1490/1490/1490 +f 1500/1500/1500 1490/1490/1490 1502/1502/1502 +f 1503/1503/1503 1504/1504/1504 1505/1505/1505 +f 1505/1505/1505 1506/1506/1506 1503/1503/1503 +f 1505/1505/1505 1504/1504/1504 1507/1507/1507 +f 1508/1508/1508 1507/1507/1507 1509/1509/1509 +f 1509/1509/1509 1510/1510/1510 1508/1508/1508 +f 1510/1510/1510 1509/1509/1509 1511/1511/1511 +f 1511/1511/1511 1512/1512/1512 1510/1510/1510 +f 1512/1512/1512 1511/1511/1511 1513/1513/1513 +f 1514/1514/1514 1505/1505/1505 1507/1507/1507 +f 1507/1507/1507 1508/1508/1508 1514/1514/1514 +f 1514/1514/1514 1515/1515/1515 1506/1506/1506 +f 1506/1506/1506 1505/1505/1505 1514/1514/1514 +f 1514/1514/1514 1508/1508/1508 1516/1516/1516 +f 1516/1516/1516 1517/1517/1517 1514/1514/1514 +f 1508/1508/1508 1510/1510/1510 1518/1518/1518 +f 1518/1518/1518 1516/1516/1516 1508/1508/1508 +f 1510/1510/1510 1512/1512/1512 1519/1519/1519 +f 1517/1517/1517 1516/1516/1516 1520/1520/1520 +f 1520/1520/1520 1521/1521/1521 1517/1517/1517 +f 1516/1516/1516 1518/1518/1518 1522/1522/1522 +f 1522/1522/1522 1520/1520/1520 1516/1516/1516 +f 1522/1522/1522 1518/1518/1518 1523/1523/1523 +f 1515/1515/1515 1514/1514/1514 1517/1517/1517 +f 1517/1517/1517 1524/1524/1524 1515/1515/1515 +f 1524/1524/1524 1517/1517/1517 1521/1521/1521 +f 1521/1521/1521 1525/1525/1525 1524/1524/1524 +f 1506/1506/1506 1515/1515/1515 1498/1498/1498 +f 1498/1498/1498 1500/1500/1500 1506/1506/1506 +f 1503/1503/1503 1506/1506/1506 1500/1500/1500 +f 1500/1500/1500 1502/1502/1502 1503/1503/1503 +f 1515/1515/1515 1524/1524/1524 1495/1495/1495 +f 1495/1495/1495 1498/1498/1498 1515/1515/1515 +f 1524/1524/1524 1525/1525/1525 1493/1493/1493 +f 1493/1493/1493 1495/1495/1495 1524/1524/1524 +f 1503/1503/1503 1502/1502/1502 1526/1526/1526 +f 1526/1526/1526 1527/1527/1527 1503/1503/1503 +f 1503/1503/1503 1528/1528/1528 1529/1529/1529 +f 1503/1503/1503 1527/1527/1527 1528/1528/1528 +f 1492/1492/1492 1530/1530/1530 1531/1531/1531 +f 1531/1531/1531 1526/1526/1526 1492/1492/1492 +f 1530/1530/1530 1492/1492/1492 1491/1491/1491 +f 1491/1491/1491 1532/1532/1532 1530/1530/1530 +f 1532/1532/1532 1491/1491/1491 1483/1483/1483 +f 1483/1483/1483 1533/1533/1533 1532/1532/1532 +f 1533/1533/1533 1483/1483/1483 1482/1482/1482 +f 1534/1534/1534 1482/1482/1482 1535/1535/1535 +f 1531/1531/1531 1536/1536/1536 1537/1537/1537 +f 1530/1530/1530 1532/1532/1532 1538/1538/1538 +f 1532/1532/1532 1539/1539/1539 1538/1538/1538 +f 1530/1530/1530 1536/1536/1536 1531/1531/1531 +f 1532/1532/1532 1533/1533/1533 1539/1539/1539 +f 1533/1533/1533 1540/1540/1540 1541/1541/1541 +f 1533/1533/1533 1534/1534/1534 1540/1540/1540 +f 1534/1534/1534 1535/1535/1535 1542/1542/1542 +f 1543/1543/1543 1475/1475/1475 1544/1544/1544 +f 1544/1544/1544 1545/1545/1545 1543/1543/1543 +f 1546/1546/1546 1474/1474/1474 1469/1469/1469 +f 1469/1469/1469 1547/1547/1547 1546/1546/1546 +f 1548/1548/1548 1545/1545/1545 1544/1544/1544 +f 1544/1544/1544 1549/1549/1549 1548/1548/1548 +f 1546/1546/1546 1547/1547/1547 1550/1550/1550 +f 1550/1550/1550 1551/1551/1551 1546/1546/1546 +f 1548/1548/1548 1549/1549/1549 1552/1552/1552 +f 1552/1552/1552 1553/1553/1553 1548/1548/1548 +f 1551/1551/1551 1550/1550/1550 1554/1554/1554 +f 1554/1554/1554 1555/1555/1555 1551/1551/1551 +f 1553/1553/1553 1552/1552/1552 1556/1556/1556 +f 1556/1556/1556 1557/1557/1557 1553/1553/1553 +f 1555/1555/1555 1554/1554/1554 1558/1558/1558 +f 1558/1558/1558 1559/1559/1559 1555/1555/1555 +f 1557/1557/1557 1556/1556/1556 1560/1560/1560 +f 1560/1560/1560 1561/1561/1561 1557/1557/1557 +f 1559/1559/1559 1558/1558/1558 1562/1562/1562 +f 1562/1562/1562 1563/1563/1563 1559/1559/1559 +f 1561/1561/1561 1564/1564/1564 1565/1565/1565 +f 1563/1563/1563 1562/1562/1562 1566/1566/1566 +f 1561/1561/1561 1560/1560/1560 1564/1564/1564 +f 1563/1563/1563 1566/1566/1566 1567/1567/1567 +f 1537/1537/1537 1527/1527/1527 1531/1531/1531 +f 1531/1531/1531 1527/1527/1527 1526/1526/1526 +f 1498/1498/1498 1495/1495/1495 1497/1497/1497 +f 1497/1497/1497 1499/1499/1499 1498/1498/1498 +f 1502/1502/1502 1490/1490/1490 1526/1526/1526 +f 1526/1526/1526 1490/1490/1490 1480/1480/1480 +f 1480/1480/1480 1492/1492/1492 1526/1526/1526 +f 1481/1481/1481 1483/1483/1483 1480/1480/1480 +f 1481/1481/1481 1543/1543/1543 1482/1482/1482 +f 1470/1470/1470 1469/1469/1469 1474/1474/1474 +f 1564/1564/1564 1568/1568/1568 1569/1569/1569 +f 1544/1544/1544 1475/1475/1475 1474/1474/1474 +f 1474/1474/1474 1546/1546/1546 1544/1544/1544 +f 1551/1551/1551 1549/1549/1549 1544/1544/1544 +f 1544/1544/1544 1546/1546/1546 1551/1551/1551 +f 1555/1555/1555 1552/1552/1552 1549/1549/1549 +f 1549/1549/1549 1551/1551/1551 1555/1555/1555 +f 1559/1559/1559 1556/1556/1556 1552/1552/1552 +f 1552/1552/1552 1555/1555/1555 1559/1559/1559 +f 1563/1563/1563 1560/1560/1560 1556/1556/1556 +f 1556/1556/1556 1559/1559/1559 1563/1563/1563 +f 1560/1560/1560 1563/1563/1563 1568/1568/1568 +f 1568/1568/1568 1564/1564/1564 1560/1560/1560 +f 1545/1545/1545 1535/1535/1535 1482/1482/1482 +f 1482/1482/1482 1543/1543/1543 1545/1545/1545 +f 1480/1480/1480 1483/1483/1483 1491/1491/1491 +f 1476/1476/1476 1543/1543/1543 1481/1481/1481 +f 1476/1476/1476 1475/1475/1475 1543/1543/1543 +f 1533/1533/1533 1482/1482/1482 1534/1534/1534 +f 1510/1510/1510 1519/1519/1519 1570/1570/1570 +f 1510/1510/1510 1570/1570/1570 1518/1518/1518 +f 197/197/197 206/206/206 1484/1484/1484 +f 1493/1493/1493 1525/1525/1525 1571/1571/1571 +f 1571/1571/1571 1572/1572/1572 1493/1493/1493 +f 1572/1572/1572 1571/1571/1571 1573/1573/1573 +f 1573/1573/1573 1574/1574/1574 1572/1572/1572 +f 1574/1574/1574 1575/1575/1575 1576/1576/1576 +f 1574/1574/1574 1573/1573/1573 1575/1575/1575 +f 1469/1469/1469 1577/1577/1577 1578/1578/1578 +f 1578/1578/1578 1547/1547/1547 1469/1469/1469 +f 1577/1577/1577 1579/1579/1579 1580/1580/1580 +f 1580/1580/1580 1578/1578/1578 1577/1577/1577 +f 1579/1579/1579 1581/1581/1581 1582/1582/1582 +f 1579/1579/1579 1582/1582/1582 1580/1580/1580 +f 1469/1469/1469 1583/1583/1583 1584/1584/1584 +f 1584/1584/1584 1577/1577/1577 1469/1469/1469 +f 1578/1578/1578 1585/1585/1585 1586/1586/1586 +f 1586/1586/1586 1547/1547/1547 1578/1578/1578 +f 1577/1577/1577 1584/1584/1584 1587/1587/1587 +f 1587/1587/1587 1579/1579/1579 1577/1577/1577 +f 1580/1580/1580 1585/1585/1585 1578/1578/1578 +f 1579/1579/1579 1587/1587/1587 1581/1581/1581 +f 1581/1581/1581 1588/1588/1588 1582/1582/1582 +f 1582/1582/1582 1588/1588/1588 1585/1585/1585 +f 1585/1585/1585 1580/1580/1580 1582/1582/1582 +f 1589/1589/1589 1590/1590/1590 1591/1591/1591 +f 1589/1589/1589 1592/1592/1592 1593/1593/1593 +f 1593/1593/1593 1590/1590/1590 1589/1589/1589 +f 1592/1592/1592 1594/1594/1594 1595/1595/1595 +f 1595/1595/1595 1593/1593/1593 1592/1592/1592 +f 1595/1595/1595 1594/1594/1594 1596/1596/1596 +f 1596/1596/1596 1597/1597/1597 1595/1595/1595 +f 1598/1598/1598 1599/1599/1599 1600/1600/1600 +f 1601/1601/1601 1602/1602/1602 1598/1598/1598 +f 1598/1598/1598 1603/1603/1603 1601/1601/1601 +f 1604/1604/1604 1605/1605/1605 1606/1606/1606 +f 1606/1606/1606 1600/1600/1600 1604/1604/1604 +f 1606/1606/1606 1603/1603/1603 1598/1598/1598 +f 1598/1598/1598 1600/1600/1600 1606/1606/1606 +f 1601/1601/1601 1607/1607/1607 1602/1602/1602 +f 1590/1590/1590 1608/1608/1608 1591/1591/1591 +f 1604/1604/1604 1487/1487/1487 200/200/200 +f 200/200/200 1605/1605/1605 1604/1604/1604 +f 202/202/202 1487/1487/1487 1597/1597/1597 +f 1597/1597/1597 1609/1609/1609 202/202/202 +f 1609/1609/1609 1597/1597/1597 1596/1596/1596 +f 1610/1610/1610 200/200/200 198/198/198 +f 198/198/198 1486/1486/1486 1610/1610/1610 +f 1610/1610/1610 1611/1611/1611 1612/1612/1612 +f 1612/1612/1612 200/200/200 1610/1610/1610 +f 1613/1613/1613 1614/1614/1614 1615/1615/1615 +f 1615/1615/1615 1616/1616/1616 1613/1613/1613 +f 1617/1617/1617 1618/1618/1618 1619/1619/1619 +f 1619/1619/1619 1620/1620/1620 1617/1617/1617 +f 1621/1621/1621 1622/1622/1622 1615/1615/1615 +f 1618/1618/1618 1623/1623/1623 1624/1624/1624 +f 1619/1619/1619 1624/1624/1624 1625/1625/1625 +f 1624/1624/1624 1619/1619/1619 1618/1618/1618 +f 1614/1614/1614 1626/1626/1626 1621/1621/1621 +f 1621/1621/1621 1615/1615/1615 1614/1614/1614 +f 1627/1627/1627 1628/1628/1628 1629/1629/1629 +f 1629/1629/1629 1630/1630/1630 1627/1627/1627 +f 1630/1630/1630 1629/1629/1629 1631/1631/1631 +f 1631/1631/1631 1632/1632/1632 1630/1630/1630 +f 1632/1632/1632 1631/1631/1631 1633/1633/1633 +f 1633/1633/1633 1631/1631/1631 1634/1634/1634 +f 1634/1634/1634 1635/1635/1635 1633/1633/1633 +f 1628/1628/1628 1636/1636/1636 1637/1637/1637 +f 1637/1637/1637 1629/1629/1629 1628/1628/1628 +f 1629/1629/1629 1637/1637/1637 1638/1638/1638 +f 1638/1638/1638 1631/1631/1631 1629/1629/1629 +f 1631/1631/1631 1638/1638/1638 1639/1639/1639 +f 1639/1639/1639 1634/1634/1634 1631/1631/1631 +f 1636/1636/1636 1640/1640/1640 1641/1641/1641 +f 1641/1641/1641 1637/1637/1637 1636/1636/1636 +f 1637/1637/1637 1641/1641/1641 1642/1642/1642 +f 1642/1642/1642 1638/1638/1638 1637/1637/1637 +f 1638/1638/1638 1642/1642/1642 1643/1643/1643 +f 1643/1643/1643 1639/1639/1639 1638/1638/1638 +f 1640/1640/1640 1644/1644/1644 1645/1645/1645 +f 1645/1645/1645 1641/1641/1641 1640/1640/1640 +f 1641/1641/1641 1645/1645/1645 1646/1646/1646 +f 1646/1646/1646 1642/1642/1642 1641/1641/1641 +f 1642/1642/1642 1646/1646/1646 1647/1647/1647 +f 1647/1647/1647 1643/1643/1643 1642/1642/1642 +f 1644/1644/1644 1648/1648/1648 1649/1649/1649 +f 1649/1649/1649 1645/1645/1645 1644/1644/1644 +f 1645/1645/1645 1649/1649/1649 1646/1646/1646 +f 1646/1646/1646 1649/1649/1649 1650/1650/1650 +f 1650/1650/1650 1647/1647/1647 1646/1646/1646 +f 1648/1648/1648 1651/1651/1651 1652/1652/1652 +f 1652/1652/1652 1649/1649/1649 1648/1648/1648 +f 1649/1649/1649 1652/1652/1652 1653/1653/1653 +f 1653/1653/1653 1650/1650/1650 1649/1649/1649 +f 1651/1651/1651 1654/1654/1654 1655/1655/1655 +f 1655/1655/1655 1652/1652/1652 1651/1651/1651 +f 1652/1652/1652 1655/1655/1655 1656/1656/1656 +f 1656/1656/1656 1653/1653/1653 1652/1652/1652 +f 1654/1654/1654 1657/1657/1657 1658/1658/1658 +f 1658/1658/1658 1655/1655/1655 1654/1654/1654 +f 1655/1655/1655 1658/1658/1658 1659/1659/1659 +f 1659/1659/1659 1656/1656/1656 1655/1655/1655 +f 1657/1657/1657 1660/1660/1660 1661/1661/1661 +f 1661/1661/1661 1658/1658/1658 1657/1657/1657 +f 1658/1658/1658 1661/1661/1661 1662/1662/1662 +f 1662/1662/1662 1659/1659/1659 1658/1658/1658 +f 1660/1660/1660 1663/1663/1663 1664/1664/1664 +f 1664/1664/1664 1661/1661/1661 1660/1660/1660 +f 1661/1661/1661 1664/1664/1664 1665/1665/1665 +f 1665/1665/1665 1662/1662/1662 1661/1661/1661 +f 1663/1663/1663 1666/1666/1666 1667/1667/1667 +f 1667/1667/1667 1664/1664/1664 1663/1663/1663 +f 1664/1664/1664 1667/1667/1667 1668/1668/1668 +f 1668/1668/1668 1665/1665/1665 1664/1664/1664 +f 1666/1666/1666 1669/1669/1669 1670/1670/1670 +f 1670/1670/1670 1667/1667/1667 1666/1666/1666 +f 1667/1667/1667 1670/1670/1670 1671/1671/1671 +f 1671/1671/1671 1668/1668/1668 1667/1667/1667 +f 1670/1670/1670 1669/1669/1669 1672/1672/1672 +f 1672/1672/1672 1673/1673/1673 1670/1670/1670 +f 1671/1671/1671 1670/1670/1670 1673/1673/1673 +f 1673/1673/1673 1674/1674/1674 1671/1671/1671 +f 1675/1675/1675 1676/1676/1676 1677/1677/1677 +f 1677/1677/1677 1678/1678/1678 1675/1675/1675 +f 1679/1679/1679 1680/1680/1680 1681/1681/1681 +f 1681/1681/1681 1682/1682/1682 1679/1679/1679 +f 1680/1680/1680 1683/1683/1683 1684/1684/1684 +f 1684/1684/1684 1681/1681/1681 1680/1680/1680 +f 1683/1683/1683 1685/1685/1685 1686/1686/1686 +f 1686/1686/1686 1684/1684/1684 1683/1683/1683 +f 1685/1685/1685 1687/1687/1687 1688/1688/1688 +f 1688/1688/1688 1686/1686/1686 1685/1685/1685 +f 1687/1687/1687 1689/1689/1689 1690/1690/1690 +f 1690/1690/1690 1688/1688/1688 1687/1687/1687 +f 1689/1689/1689 1691/1691/1691 1692/1692/1692 +f 1692/1692/1692 1690/1690/1690 1689/1689/1689 +f 1691/1691/1691 1693/1693/1693 1694/1694/1694 +f 1694/1694/1694 1692/1692/1692 1691/1691/1691 +f 1693/1693/1693 1695/1695/1695 1696/1696/1696 +f 1696/1696/1696 1694/1694/1694 1693/1693/1693 +f 1695/1695/1695 1697/1697/1697 1698/1698/1698 +f 1698/1698/1698 1696/1696/1696 1695/1695/1695 +f 1697/1697/1697 1699/1699/1699 1700/1700/1700 +f 1700/1700/1700 1698/1698/1698 1697/1697/1697 +f 1701/1701/1701 1702/1702/1702 1703/1703/1703 +f 1703/1703/1703 1704/1704/1704 1701/1701/1701 +f 1702/1702/1702 1705/1705/1705 1706/1706/1706 +f 1706/1706/1706 1703/1703/1703 1702/1702/1702 +f 1707/1707/1707 1708/1708/1708 1706/1706/1706 +f 1706/1706/1706 1705/1705/1705 1707/1707/1707 +f 1708/1708/1708 1709/1709/1709 1710/1710/1710 +f 1627/1627/1627 1630/1630/1630 1711/1711/1711 +f 1711/1711/1711 1712/1712/1712 1627/1627/1627 +f 1710/1710/1710 1709/1709/1709 1632/1632/1632 +f 1713/1713/1713 1712/1712/1712 1711/1711/1711 +f 1711/1711/1711 1714/1714/1714 1713/1713/1713 +f 1714/1714/1714 1711/1711/1711 1709/1709/1709 +f 1709/1709/1709 1715/1715/1715 1714/1714/1714 +f 1715/1715/1715 1709/1709/1709 1708/1708/1708 +f 1708/1708/1708 1707/1707/1707 1715/1715/1715 +f 1632/1632/1632 1709/1709/1709 1711/1711/1711 +f 1711/1711/1711 1630/1630/1630 1632/1632/1632 +f 1633/1633/1633 1710/1710/1710 1632/1632/1632 +f 1633/1633/1633 1635/1635/1635 1716/1716/1716 +f 1716/1716/1716 1710/1710/1710 1633/1633/1633 +f 1703/1703/1703 1706/1706/1706 1717/1717/1717 +f 1717/1717/1717 1704/1704/1704 1703/1703/1703 +f 1708/1708/1708 1718/1718/1718 1717/1717/1717 +f 1717/1717/1717 1706/1706/1706 1708/1708/1708 +f 1710/1710/1710 1716/1716/1716 1718/1718/1718 +f 1718/1718/1718 1708/1708/1708 1710/1710/1710 +f 1616/1616/1616 1615/1615/1615 1719/1719/1719 +f 1719/1719/1719 1720/1720/1720 1616/1616/1616 +f 1618/1618/1618 1617/1617/1617 1721/1721/1721 +f 1721/1721/1721 1722/1722/1722 1618/1618/1618 +f 1618/1618/1618 1722/1722/1722 1723/1723/1723 +f 1723/1723/1723 1623/1623/1623 1618/1618/1618 +f 1724/1724/1724 1725/1725/1725 1726/1726/1726 +f 1726/1726/1726 1727/1727/1727 1724/1724/1724 +f 1728/1728/1728 1727/1727/1727 1726/1726/1726 +f 1726/1726/1726 1729/1729/1729 1728/1728/1728 +f 1730/1730/1730 1728/1728/1728 1729/1729/1729 +f 1729/1729/1729 1731/1731/1731 1730/1730/1730 +f 1732/1732/1732 1730/1730/1730 1731/1731/1731 +f 1731/1731/1731 1733/1733/1733 1732/1732/1732 +f 1734/1734/1734 1732/1732/1732 1733/1733/1733 +f 1733/1733/1733 1735/1735/1735 1734/1734/1734 +f 1736/1736/1736 1737/1737/1737 1738/1738/1738 +f 1738/1738/1738 1739/1739/1739 1736/1736/1736 +f 1739/1739/1739 1738/1738/1738 1740/1740/1740 +f 1740/1740/1740 1741/1741/1741 1739/1739/1739 +f 1741/1741/1741 1740/1740/1740 1742/1742/1742 +f 1742/1742/1742 1743/1743/1743 1741/1741/1741 +f 1743/1743/1743 1742/1742/1742 1744/1744/1744 +f 1744/1744/1744 1745/1745/1745 1743/1743/1743 +f 1745/1745/1745 1744/1744/1744 1746/1746/1746 +f 1746/1746/1746 1747/1747/1747 1745/1745/1745 +f 1748/1748/1748 1739/1739/1739 1741/1741/1741 +f 1741/1741/1741 1749/1749/1749 1748/1748/1748 +f 1749/1749/1749 1741/1741/1741 1743/1743/1743 +f 1743/1743/1743 1750/1750/1750 1749/1749/1749 +f 1750/1750/1750 1743/1743/1743 1745/1745/1745 +f 1745/1745/1745 1751/1751/1751 1750/1750/1750 +f 1751/1751/1751 1745/1745/1745 1747/1747/1747 +f 1747/1747/1747 1752/1752/1752 1751/1751/1751 +f 1736/1736/1736 1739/1739/1739 1748/1748/1748 +f 1748/1748/1748 1753/1753/1753 1736/1736/1736 +f 1753/1753/1753 1748/1748/1748 1754/1754/1754 +f 1754/1754/1754 1755/1755/1755 1753/1753/1753 +f 1754/1754/1754 1748/1748/1748 1749/1749/1749 +f 1749/1749/1749 1756/1756/1756 1754/1754/1754 +f 1756/1756/1756 1749/1749/1749 1750/1750/1750 +f 1750/1750/1750 1757/1757/1757 1756/1756/1756 +f 1757/1757/1757 1750/1750/1750 1751/1751/1751 +f 1751/1751/1751 1758/1758/1758 1757/1757/1757 +f 1758/1758/1758 1751/1751/1751 1752/1752/1752 +f 1752/1752/1752 1759/1759/1759 1758/1758/1758 +f 1760/1760/1760 1761/1761/1761 1738/1738/1738 +f 1738/1738/1738 1737/1737/1737 1760/1760/1760 +f 1761/1761/1761 1762/1762/1762 1740/1740/1740 +f 1740/1740/1740 1738/1738/1738 1761/1761/1761 +f 1762/1762/1762 1763/1763/1763 1742/1742/1742 +f 1742/1742/1742 1740/1740/1740 1762/1762/1762 +f 1763/1763/1763 1764/1764/1764 1744/1744/1744 +f 1744/1744/1744 1742/1742/1742 1763/1763/1763 +f 1764/1764/1764 1765/1765/1765 1746/1746/1746 +f 1746/1746/1746 1744/1744/1744 1764/1764/1764 +f 1760/1760/1760 1724/1724/1724 1727/1727/1727 +f 1727/1727/1727 1761/1761/1761 1760/1760/1760 +f 1727/1727/1727 1728/1728/1728 1762/1762/1762 +f 1762/1762/1762 1761/1761/1761 1727/1727/1727 +f 1728/1728/1728 1730/1730/1730 1763/1763/1763 +f 1763/1763/1763 1762/1762/1762 1728/1728/1728 +f 1730/1730/1730 1732/1732/1732 1764/1764/1764 +f 1764/1764/1764 1763/1763/1763 1730/1730/1730 +f 1734/1734/1734 1765/1765/1765 1764/1764/1764 +f 1764/1764/1764 1732/1732/1732 1734/1734/1734 +f 1752/1752/1752 1766/1766/1766 1767/1767/1767 +f 1767/1767/1767 1759/1759/1759 1752/1752/1752 +f 1747/1747/1747 1768/1768/1768 1766/1766/1766 +f 1766/1766/1766 1752/1752/1752 1747/1747/1747 +f 1746/1746/1746 1769/1769/1769 1768/1768/1768 +f 1768/1768/1768 1747/1747/1747 1746/1746/1746 +f 1765/1765/1765 1770/1770/1770 1769/1769/1769 +f 1769/1769/1769 1746/1746/1746 1765/1765/1765 +f 1734/1734/1734 1771/1771/1771 1770/1770/1770 +f 1770/1770/1770 1765/1765/1765 1734/1734/1734 +f 1735/1735/1735 1772/1772/1772 1771/1771/1771 +f 1771/1771/1771 1734/1734/1734 1735/1735/1735 +f 1771/1771/1771 1773/1773/1773 1770/1770/1770 +f 1772/1772/1772 1774/1774/1774 1775/1775/1775 +f 1775/1775/1775 1771/1771/1771 1772/1772/1772 +f 1767/1767/1767 1766/1766/1766 1776/1776/1776 +f 1776/1776/1776 1777/1777/1777 1767/1767/1767 +f 1766/1766/1766 1778/1778/1778 1776/1776/1776 +f 1767/1767/1767 1777/1777/1777 1779/1779/1779 +f 1778/1778/1778 1766/1766/1766 1768/1768/1768 +f 1771/1771/1771 1780/1780/1780 1773/1773/1773 +f 1781/1781/1781 1775/1775/1775 1782/1782/1782 +f 1783/1783/1783 1782/1782/1782 1775/1775/1775 +f 1775/1775/1775 1774/1774/1774 1783/1783/1783 +f 1771/1771/1771 1775/1775/1775 1780/1780/1780 +f 1780/1780/1780 1775/1775/1775 1781/1781/1781 +f 1767/1767/1767 1779/1779/1779 1784/1784/1784 +f 1682/1682/1682 1785/1785/1785 1786/1786/1786 +f 1786/1786/1786 1679/1679/1679 1682/1682/1682 +f 1787/1787/1787 1788/1788/1788 1789/1789/1789 +f 1789/1789/1789 1790/1790/1790 1787/1787/1787 +f 1791/1791/1791 1792/1792/1792 1793/1793/1793 +f 1793/1793/1793 1794/1794/1794 1791/1791/1791 +f 1795/1795/1795 1796/1796/1796 1797/1797/1797 +f 1798/1798/1798 1799/1799/1799 1800/1800/1800 +f 1797/1797/1797 1801/1801/1801 1795/1795/1795 +f 1800/1800/1800 1799/1799/1799 1802/1802/1802 +f 1802/1802/1802 1803/1803/1803 1800/1800/1800 +f 1795/1795/1795 1804/1804/1804 1796/1796/1796 +f 1803/1803/1803 1802/1802/1802 1805/1805/1805 +f 1806/1806/1806 1807/1807/1807 1808/1808/1808 +f 1808/1808/1808 1807/1807/1807 1809/1809/1809 +f 1809/1809/1809 1810/1810/1810 1808/1808/1808 +f 1810/1810/1810 1809/1809/1809 1811/1811/1811 +f 1811/1811/1811 1809/1809/1809 1812/1812/1812 +f 1812/1812/1812 1809/1809/1809 1807/1807/1807 +f 1807/1807/1807 1813/1813/1813 1812/1812/1812 +f 1813/1813/1813 1807/1807/1807 1806/1806/1806 +f 1798/1798/1798 1814/1814/1814 1799/1799/1799 +f 1799/1799/1799 1814/1814/1814 1815/1815/1815 +f 1815/1815/1815 1802/1802/1802 1799/1799/1799 +f 1802/1802/1802 1815/1815/1815 1805/1805/1805 +f 1816/1816/1816 1817/1817/1817 1818/1818/1818 +f 1819/1819/1819 1820/1820/1820 1817/1817/1817 +f 1816/1816/1816 1819/1819/1819 1817/1817/1817 +f 1621/1621/1621 1789/1789/1789 1622/1622/1622 +f 1794/1794/1794 1821/1821/1821 1822/1822/1822 +f 1793/1793/1793 1725/1725/1725 1821/1821/1821 +f 1821/1821/1821 1794/1794/1794 1793/1793/1793 +f 1790/1790/1790 1621/1621/1621 1626/1626/1626 +f 1621/1621/1621 1790/1790/1790 1789/1789/1789 +f 1627/1627/1627 1823/1823/1823 1824/1824/1824 +f 1824/1824/1824 1628/1628/1628 1627/1627/1627 +f 1823/1823/1823 1825/1825/1825 1826/1826/1826 +f 1826/1826/1826 1824/1824/1824 1823/1823/1823 +f 1825/1825/1825 1827/1827/1827 1826/1826/1826 +f 1827/1827/1827 1828/1828/1828 1634/1634/1634 +f 1634/1634/1634 1826/1826/1826 1827/1827/1827 +f 1628/1628/1628 1824/1824/1824 1829/1829/1829 +f 1829/1829/1829 1636/1636/1636 1628/1628/1628 +f 1824/1824/1824 1826/1826/1826 1830/1830/1830 +f 1830/1830/1830 1829/1829/1829 1824/1824/1824 +f 1826/1826/1826 1634/1634/1634 1831/1831/1831 +f 1831/1831/1831 1830/1830/1830 1826/1826/1826 +f 1636/1636/1636 1829/1829/1829 1832/1832/1832 +f 1832/1832/1832 1833/1833/1833 1636/1636/1636 +f 1829/1829/1829 1830/1830/1830 1834/1834/1834 +f 1834/1834/1834 1832/1832/1832 1829/1829/1829 +f 1830/1830/1830 1831/1831/1831 1835/1835/1835 +f 1835/1835/1835 1834/1834/1834 1830/1830/1830 +f 1833/1833/1833 1832/1832/1832 1836/1836/1836 +f 1836/1836/1836 1837/1837/1837 1833/1833/1833 +f 1832/1832/1832 1834/1834/1834 1838/1838/1838 +f 1838/1838/1838 1836/1836/1836 1832/1832/1832 +f 1834/1834/1834 1835/1835/1835 1647/1647/1647 +f 1647/1647/1647 1838/1838/1838 1834/1834/1834 +f 1837/1837/1837 1836/1836/1836 1839/1839/1839 +f 1839/1839/1839 1840/1840/1840 1837/1837/1837 +f 1836/1836/1836 1838/1838/1838 1839/1839/1839 +f 1838/1838/1838 1647/1647/1647 1650/1650/1650 +f 1650/1650/1650 1839/1839/1839 1838/1838/1838 +f 1840/1840/1840 1839/1839/1839 1841/1841/1841 +f 1841/1841/1841 1842/1842/1842 1840/1840/1840 +f 1839/1839/1839 1650/1650/1650 1843/1843/1843 +f 1843/1843/1843 1841/1841/1841 1839/1839/1839 +f 1842/1842/1842 1841/1841/1841 1844/1844/1844 +f 1844/1844/1844 1654/1654/1654 1842/1842/1842 +f 1841/1841/1841 1843/1843/1843 1845/1845/1845 +f 1845/1845/1845 1844/1844/1844 1841/1841/1841 +f 1654/1654/1654 1844/1844/1844 1846/1846/1846 +f 1846/1846/1846 1657/1657/1657 1654/1654/1654 +f 1844/1844/1844 1845/1845/1845 1847/1847/1847 +f 1847/1847/1847 1846/1846/1846 1844/1844/1844 +f 1657/1657/1657 1846/1846/1846 1848/1848/1848 +f 1848/1848/1848 1660/1660/1660 1657/1657/1657 +f 1846/1846/1846 1847/1847/1847 1662/1662/1662 +f 1662/1662/1662 1848/1848/1848 1846/1846/1846 +f 1660/1660/1660 1848/1848/1848 1849/1849/1849 +f 1849/1849/1849 1663/1663/1663 1660/1660/1660 +f 1848/1848/1848 1662/1662/1662 1665/1665/1665 +f 1665/1665/1665 1849/1849/1849 1848/1848/1848 +f 1663/1663/1663 1849/1849/1849 1850/1850/1850 +f 1850/1850/1850 1851/1851/1851 1663/1663/1663 +f 1849/1849/1849 1665/1665/1665 1668/1668/1668 +f 1668/1668/1668 1850/1850/1850 1849/1849/1849 +f 1851/1851/1851 1850/1850/1850 1852/1852/1852 +f 1852/1852/1852 1669/1669/1669 1851/1851/1851 +f 1850/1850/1850 1668/1668/1668 1671/1671/1671 +f 1671/1671/1671 1852/1852/1852 1850/1850/1850 +f 1852/1852/1852 1853/1853/1853 1854/1854/1854 +f 1854/1854/1854 1669/1669/1669 1852/1852/1852 +f 1671/1671/1671 1674/1674/1674 1853/1853/1853 +f 1853/1853/1853 1852/1852/1852 1671/1671/1671 +f 1855/1855/1855 1856/1856/1856 1857/1857/1857 +f 1857/1857/1857 1858/1858/1858 1855/1855/1855 +f 1859/1859/1859 1860/1860/1860 1861/1861/1861 +f 1861/1861/1861 1862/1862/1862 1859/1859/1859 +f 1862/1862/1862 1861/1861/1861 1863/1863/1863 +f 1863/1863/1863 1864/1864/1864 1862/1862/1862 +f 1864/1864/1864 1863/1863/1863 1865/1865/1865 +f 1865/1865/1865 1866/1866/1866 1864/1864/1864 +f 1866/1866/1866 1865/1865/1865 1867/1867/1867 +f 1867/1867/1867 1868/1868/1868 1866/1866/1866 +f 1868/1868/1868 1867/1867/1867 1869/1869/1869 +f 1869/1869/1869 1870/1870/1870 1868/1868/1868 +f 1870/1870/1870 1869/1869/1869 1871/1871/1871 +f 1871/1871/1871 1872/1872/1872 1870/1870/1870 +f 1872/1872/1872 1871/1871/1871 1873/1873/1873 +f 1873/1873/1873 1874/1874/1874 1872/1872/1872 +f 1874/1874/1874 1873/1873/1873 1875/1875/1875 +f 1875/1875/1875 1876/1876/1876 1874/1874/1874 +f 1876/1876/1876 1875/1875/1875 1877/1877/1877 +f 1877/1877/1877 1878/1878/1878 1876/1876/1876 +f 1879/1879/1879 1878/1878/1878 1877/1877/1877 +f 1877/1877/1877 1880/1880/1880 1879/1879/1879 +f 1881/1881/1881 1882/1882/1882 1883/1883/1883 +f 1883/1883/1883 1884/1884/1884 1881/1881/1881 +f 1884/1884/1884 1883/1883/1883 1885/1885/1885 +f 1885/1885/1885 1886/1886/1886 1884/1884/1884 +f 1887/1887/1887 1886/1886/1886 1885/1885/1885 +f 1885/1885/1885 1888/1888/1888 1887/1887/1887 +f 1888/1888/1888 1889/1889/1889 1890/1890/1890 +f 1627/1627/1627 1891/1891/1891 1892/1892/1892 +f 1892/1892/1892 1823/1823/1823 1627/1627/1627 +f 1889/1889/1889 1825/1825/1825 1890/1890/1890 +f 1713/1713/1713 1893/1893/1893 1892/1892/1892 +f 1892/1892/1892 1891/1891/1891 1713/1713/1713 +f 1893/1893/1893 1894/1894/1894 1890/1890/1890 +f 1890/1890/1890 1892/1892/1892 1893/1893/1893 +f 1894/1894/1894 1887/1887/1887 1888/1888/1888 +f 1888/1888/1888 1890/1890/1890 1894/1894/1894 +f 1825/1825/1825 1823/1823/1823 1892/1892/1892 +f 1892/1892/1892 1890/1890/1890 1825/1825/1825 +f 1827/1827/1827 1825/1825/1825 1889/1889/1889 +f 1827/1827/1827 1889/1889/1889 1895/1895/1895 +f 1895/1895/1895 1828/1828/1828 1827/1827/1827 +f 1883/1883/1883 1882/1882/1882 1896/1896/1896 +f 1896/1896/1896 1885/1885/1885 1883/1883/1883 +f 1888/1888/1888 1885/1885/1885 1896/1896/1896 +f 1896/1896/1896 1718/1718/1718 1888/1888/1888 +f 1889/1889/1889 1888/1888/1888 1718/1718/1718 +f 1718/1718/1718 1895/1895/1895 1889/1889/1889 +f 1788/1788/1788 1894/1894/1894 1893/1893/1893 +f 1893/1893/1893 1789/1789/1789 1788/1788/1788 +f 1794/1794/1794 1884/1884/1884 1886/1886/1886 +f 1886/1886/1886 1791/1791/1791 1794/1794/1794 +f 1794/1794/1794 1822/1822/1822 1881/1881/1881 +f 1881/1881/1881 1884/1884/1884 1794/1794/1794 +f 1793/1793/1793 1897/1897/1897 1726/1726/1726 +f 1726/1726/1726 1725/1725/1725 1793/1793/1793 +f 1898/1898/1898 1729/1729/1729 1726/1726/1726 +f 1726/1726/1726 1897/1897/1897 1898/1898/1898 +f 1899/1899/1899 1731/1731/1731 1729/1729/1729 +f 1729/1729/1729 1898/1898/1898 1899/1899/1899 +f 1900/1900/1900 1733/1733/1733 1731/1731/1731 +f 1731/1731/1731 1899/1899/1899 1900/1900/1900 +f 1901/1901/1901 1735/1735/1735 1733/1733/1733 +f 1733/1733/1733 1900/1900/1900 1901/1901/1901 +f 1902/1902/1902 1903/1903/1903 1904/1904/1904 +f 1904/1904/1904 1905/1905/1905 1902/1902/1902 +f 1903/1903/1903 1906/1906/1906 1907/1907/1907 +f 1907/1907/1907 1904/1904/1904 1903/1903/1903 +f 1906/1906/1906 1908/1908/1908 1909/1909/1909 +f 1909/1909/1909 1907/1907/1907 1906/1906/1906 +f 1908/1908/1908 1910/1910/1910 1911/1911/1911 +f 1911/1911/1911 1909/1909/1909 1908/1908/1908 +f 1910/1910/1910 1912/1912/1912 1913/1913/1913 +f 1913/1913/1913 1911/1911/1911 1910/1910/1910 +f 1902/1902/1902 1787/1787/1787 1903/1903/1903 +f 1914/1914/1914 1915/1915/1915 1906/1906/1906 +f 1906/1906/1906 1903/1903/1903 1914/1914/1914 +f 1915/1915/1915 1916/1916/1916 1908/1908/1908 +f 1908/1908/1908 1906/1906/1906 1915/1915/1915 +f 1916/1916/1916 1917/1917/1917 1910/1910/1910 +f 1910/1910/1910 1908/1908/1908 1916/1916/1916 +f 1917/1917/1917 1918/1918/1918 1912/1912/1912 +f 1912/1912/1912 1910/1910/1910 1917/1917/1917 +f 1787/1787/1787 1790/1790/1790 1914/1914/1914 +f 1914/1914/1914 1903/1903/1903 1787/1787/1787 +f 1790/1790/1790 1626/1626/1626 1919/1919/1919 +f 1919/1919/1919 1914/1914/1914 1790/1790/1790 +f 1919/1919/1919 1756/1756/1756 1915/1915/1915 +f 1915/1915/1915 1914/1914/1914 1919/1919/1919 +f 1756/1756/1756 1757/1757/1757 1916/1916/1916 +f 1916/1916/1916 1915/1915/1915 1756/1756/1756 +f 1757/1757/1757 1758/1758/1758 1917/1917/1917 +f 1917/1917/1917 1916/1916/1916 1757/1757/1757 +f 1758/1758/1758 1759/1759/1759 1918/1918/1918 +f 1918/1918/1918 1917/1917/1917 1758/1758/1758 +f 1920/1920/1920 1905/1905/1905 1904/1904/1904 +f 1904/1904/1904 1921/1921/1921 1920/1920/1920 +f 1921/1921/1921 1904/1904/1904 1907/1907/1907 +f 1907/1907/1907 1922/1922/1922 1921/1921/1921 +f 1922/1922/1922 1907/1907/1907 1909/1909/1909 +f 1909/1909/1909 1923/1923/1923 1922/1922/1922 +f 1923/1923/1923 1909/1909/1909 1911/1911/1911 +f 1911/1911/1911 1924/1924/1924 1923/1923/1923 +f 1924/1924/1924 1911/1911/1911 1913/1913/1913 +f 1913/1913/1913 1925/1925/1925 1924/1924/1924 +f 1792/1792/1792 1920/1920/1920 1921/1921/1921 +f 1792/1792/1792 1921/1921/1921 1897/1897/1897 +f 1897/1897/1897 1793/1793/1793 1792/1792/1792 +f 1897/1897/1897 1921/1921/1921 1922/1922/1922 +f 1922/1922/1922 1898/1898/1898 1897/1897/1897 +f 1898/1898/1898 1922/1922/1922 1923/1923/1923 +f 1923/1923/1923 1899/1899/1899 1898/1898/1898 +f 1899/1899/1899 1923/1923/1923 1924/1924/1924 +f 1924/1924/1924 1900/1900/1900 1899/1899/1899 +f 1901/1901/1901 1900/1900/1900 1924/1924/1924 +f 1924/1924/1924 1925/1925/1925 1901/1901/1901 +f 1918/1918/1918 1759/1759/1759 1926/1926/1926 +f 1926/1926/1926 1927/1927/1927 1918/1918/1918 +f 1912/1912/1912 1918/1918/1918 1927/1927/1927 +f 1927/1927/1927 1928/1928/1928 1912/1912/1912 +f 1913/1913/1913 1912/1912/1912 1928/1928/1928 +f 1928/1928/1928 1929/1929/1929 1913/1913/1913 +f 1925/1925/1925 1913/1913/1913 1929/1929/1929 +f 1929/1929/1929 1930/1930/1930 1925/1925/1925 +f 1901/1901/1901 1925/1925/1925 1930/1930/1930 +f 1930/1930/1930 1931/1931/1931 1901/1901/1901 +f 1735/1735/1735 1901/1901/1901 1931/1931/1931 +f 1931/1931/1931 1932/1932/1932 1735/1735/1735 +f 1931/1931/1931 1930/1930/1930 1933/1933/1933 +f 1932/1932/1932 1931/1931/1931 1934/1934/1934 +f 1934/1934/1934 1774/1774/1774 1932/1932/1932 +f 1926/1926/1926 1935/1935/1935 1936/1936/1936 +f 1936/1936/1936 1927/1927/1927 1926/1926/1926 +f 1927/1927/1927 1936/1936/1936 1937/1937/1937 +f 1926/1926/1926 1938/1938/1938 1935/1935/1935 +f 1937/1937/1937 1928/1928/1928 1927/1927/1927 +f 1931/1931/1931 1933/1933/1933 1939/1939/1939 +f 1940/1940/1940 1941/1941/1941 1934/1934/1934 +f 1942/1942/1942 1774/1774/1774 1934/1934/1934 +f 1934/1934/1934 1941/1941/1941 1942/1942/1942 +f 1931/1931/1931 1939/1939/1939 1934/1934/1934 +f 1939/1939/1939 1940/1940/1940 1934/1934/1934 +f 1926/1926/1926 1943/1943/1943 1938/1938/1938 +f 1944/1944/1944 1860/1860/1860 1859/1859/1859 +f 1859/1859/1859 1945/1945/1945 1944/1944/1944 +f 1713/1713/1713 1622/1622/1622 1789/1789/1789 +f 1789/1789/1789 1893/1893/1893 1713/1713/1713 +f 1615/1615/1615 1622/1622/1622 1713/1713/1713 +f 1713/1713/1713 1719/1719/1719 1615/1615/1615 +f 1620/1620/1620 1946/1946/1946 1947/1947/1947 +f 1947/1947/1947 1617/1617/1617 1620/1620/1620 +f 1946/1946/1946 1613/1613/1613 1616/1616/1616 +f 1616/1616/1616 1947/1947/1947 1946/1946/1946 +f 1947/1947/1947 1616/1616/1616 1720/1720/1720 +f 1720/1720/1720 1948/1948/1948 1947/1947/1947 +f 1721/1721/1721 1617/1617/1617 1947/1947/1947 +f 1947/1947/1947 1948/1948/1948 1721/1721/1721 +f 895/895/895 893/893/893 886/886/886 +f 886/886/886 888/888/888 895/895/895 +f 889/889/889 897/897/897 895/895/895 +f 895/895/895 888/888/888 889/889/889 +f 890/890/890 899/899/899 897/897/897 +f 897/897/897 889/889/889 890/890/890 +f 1472/1472/1472 1476/1476/1476 1479/1479/1479 +f 1479/1479/1479 1473/1473/1473 1472/1472/1472 +f 190/190/190 196/196/196 199/199/199 +f 199/199/199 192/192/192 190/190/190 +f 199/199/199 201/201/201 193/193/193 +f 193/193/193 192/192/192 199/199/199 +f 201/201/201 203/203/203 194/194/194 +f 194/194/194 193/193/193 201/201/201 +f 1501/1501/1501 1499/1499/1499 1497/1497/1497 +f 1497/1497/1497 1496/1496/1496 1501/1501/1501 +f 189/189/189 196/196/196 190/190/190 +f 886/886/886 893/893/893 887/887/887 +f 1290/1290/1290 1303/1303/1303 1293/1293/1293 +f 686/686/686 695/695/695 684/684/684 +f 1294/1294/1294 1293/1293/1293 1303/1303/1303 +f 1303/1303/1303 1302/1302/1302 733/733/733 +f 688/688/688 733/733/733 695/695/695 +f 733/733/733 696/696/696 695/695/695 diff --git a/demos/art/models/witch/witch_diffuse.tga.png b/demos/art/models/witch/witch_diffuse.tga.png new file mode 100644 index 0000000..87037db Binary files /dev/null and b/demos/art/models/witch/witch_diffuse.tga.png differ diff --git a/demos/art/models/witch/witch_object.obj b/demos/art/models/witch/witch_object.obj new file mode 100644 index 0000000..5b28d09 --- /dev/null +++ b/demos/art/models/witch/witch_object.obj @@ -0,0 +1,6303 @@ +v 23.975399 23.334700 15.837900 +v 24.897400 23.689400 16.783199 +v 23.911499 23.265699 15.936800 +v 23.507401 22.966000 15.422199 +v 24.007401 23.400099 15.630500 +v 24.195700 23.495600 15.848100 +v 8.278299 14.696999 8.869900 +v 7.721300 14.686099 8.869900 +v 9.197599 17.234900 8.869900 +v 7.198000 14.939099 8.869900 +v 6.917100 15.416499 8.869900 +v 6.893700 16.013498 8.869900 +v 17.607399 17.080099 19.532299 +v 17.615000 16.412399 18.817200 +v 17.876200 16.895498 18.436699 +v 17.876200 16.895498 18.436699 +v 17.833799 17.608999 19.101299 +v 17.607399 17.080099 19.532299 +v 17.615000 16.412399 18.817200 +v 16.917801 15.466799 18.146099 +v 17.209900 15.872099 17.817501 +v 17.209900 15.872099 17.817501 +v 17.876200 16.895498 18.436699 +v 17.615000 16.412399 18.817200 +v 16.917801 15.466799 18.146099 +v 16.780500 15.316699 18.471500 +v 16.949499 15.527199 17.939800 +v 16.949499 15.527199 17.939800 +v 17.209900 15.872099 17.817501 +v 16.917801 15.466799 18.146099 +v 13.265000 17.552999 24.966400 +v 12.613500 17.119499 25.402901 +v 12.955100 16.802500 25.382799 +v 12.955100 16.802500 25.382799 +v 13.595500 17.198500 24.994999 +v 13.265000 17.552999 24.966400 +v 12.955100 16.802500 25.382799 +v 12.613500 17.119499 25.402901 +v 12.016399 16.400299 25.503399 +v 12.016399 16.400299 25.503399 +v 12.368900 16.088900 25.467699 +v 12.955100 16.802500 25.382799 +v 12.368900 16.088900 25.467699 +v 12.016399 16.400299 25.503399 +v 11.615600 15.870799 25.187599 +v 11.615600 15.870799 25.187599 +v 11.968500 15.522499 25.193399 +v 12.368900 16.088900 25.467699 +v 6.201200 19.734400 19.939199 +v 5.985300 19.937500 19.788601 +v 6.189800 19.882099 20.043499 +v 6.149100 19.665199 20.031399 +v 6.201200 19.734400 19.939199 +v 6.189800 19.882099 20.043499 +v 6.189800 19.882099 20.043499 +v 6.135400 19.809399 20.134100 +v 6.149100 19.665199 20.031399 +v 5.858500 20.102299 19.381199 +v 5.922200 20.001299 19.691500 +v 5.847100 20.250000 19.485600 +v 5.734900 20.110899 19.355598 +v 5.858500 20.102299 19.381199 +v 5.847100 20.250000 19.485600 +v 5.847100 20.250000 19.485600 +v 5.721800 20.254499 19.459200 +v 5.734900 20.110899 19.355598 +v 5.624100 19.905899 19.796000 +v 5.652100 19.931299 19.893400 +v 7.723400 20.641899 19.113201 +v 7.695400 20.616499 19.015699 +v 5.652100 19.931299 19.893400 +v 5.691000 19.833500 19.907700 +v 7.762300 20.544100 19.127499 +v 7.723400 20.641899 19.113201 +v 5.691000 19.833500 19.907700 +v 5.663000 19.808100 19.810200 +v 7.734300 20.518700 19.029999 +v 7.762300 20.544100 19.127499 +v 5.663000 19.808100 19.810200 +v 5.624100 19.905899 19.796000 +v 7.695400 20.616499 19.015699 +v 7.734300 20.518700 19.029999 +v 5.739900 19.980099 19.875099 +v 5.829200 19.736898 20.228901 +v 5.841500 19.576899 20.115900 +v 5.752200 19.820000 19.762001 +v 5.692300 19.489498 20.564100 +v 5.704600 19.329498 20.451099 +v 5.365900 19.304199 20.790901 +v 5.378200 19.144098 20.677900 +v 4.937500 19.230499 20.848499 +v 4.949800 19.070499 20.735500 +v 4.521700 19.288399 20.721401 +v 4.533999 19.128300 20.608398 +v 4.230100 19.462099 20.443800 +v 4.242400 19.302099 20.330700 +v 4.140800 19.705299 20.089901 +v 4.153100 19.545300 19.976900 +v 4.277699 19.952700 19.754700 +v 4.290000 19.792700 19.641600 +v 4.604100 20.137999 19.527901 +v 4.616400 19.977999 19.414900 +v 5.032500 20.211599 19.470299 +v 5.044800 20.051600 19.357300 +v 5.448300 20.153799 19.597401 +v 5.460600 19.993799 19.484299 +v 7.762300 20.544100 19.127499 +v 7.734300 20.518700 19.029999 +v 7.695400 20.616499 19.015699 +v 7.723400 20.641899 19.113201 +v 17.743999 10.822699 9.400100 +v 17.751999 10.742699 9.419200 +v 17.685400 10.790899 9.211300 +v 17.743999 10.822699 9.400100 +v 17.611500 10.798999 9.469800 +v 17.751999 10.742699 9.419200 +v 18.114700 11.786699 9.450300 +v 18.219200 11.535199 9.040600 +v 18.140800 11.716399 9.079800 +v 18.140800 11.716399 9.079800 +v 18.127501 11.793999 9.154400 +v 18.114700 11.786699 9.450300 +v 18.169399 11.928999 9.036500 +v 18.145399 11.963099 9.069300 +v 18.127501 11.793999 9.154400 +v 18.127501 11.793999 9.154400 +v 18.140800 11.716399 9.079800 +v 18.169399 11.928999 9.036500 +v 18.269501 12.041199 8.993600 +v 18.407700 12.129000 8.950700 +v 18.397999 12.144799 8.962700 +v 18.397999 12.144799 8.962700 +v 18.283199 12.067599 9.017200 +v 18.269501 12.041199 8.993600 +v 18.269501 12.041199 8.993600 +v 18.283199 12.067599 9.017200 +v 18.145399 11.963099 9.069300 +v 18.145399 11.963099 9.069300 +v 18.169399 11.928999 9.036500 +v 18.269501 12.041199 8.993600 +v 18.407700 12.129000 8.950700 +v 18.445099 12.313499 8.908100 +v 18.397999 12.144799 8.962700 +v 17.189100 11.343899 9.388700 +v 17.166300 11.279099 9.200600 +v 17.108400 11.345798 9.406000 +v 17.189100 11.343899 9.388700 +v 17.108400 11.345798 9.406000 +v 17.174601 11.211199 9.460800 +v 18.114700 11.786699 9.450300 +v 18.127501 11.793999 9.154400 +v 18.055300 11.804600 9.078500 +v 18.055300 11.804600 9.078500 +v 17.869400 11.867399 9.033900 +v 18.114700 11.786699 9.450300 +v 18.115000 11.943499 9.036300 +v 18.055300 11.804600 9.078500 +v 18.127501 11.793999 9.154400 +v 18.127501 11.793999 9.154400 +v 18.145399 11.963099 9.069300 +v 18.115000 11.943499 9.036300 +v 18.262300 12.080699 8.993700 +v 18.283199 12.067599 9.017200 +v 18.397999 12.144799 8.962700 +v 18.397999 12.144799 8.962700 +v 18.383801 12.142699 8.951099 +v 18.262300 12.080699 8.993700 +v 18.262300 12.080699 8.993700 +v 18.115000 11.943499 9.036300 +v 18.145399 11.963099 9.069300 +v 18.145399 11.963099 9.069300 +v 18.283199 12.067599 9.017200 +v 18.262300 12.080699 8.993700 +v 18.383801 12.142699 8.951099 +v 18.397999 12.144799 8.962700 +v 18.445099 12.313499 8.908100 +v 17.424601 10.569499 8.958500 +v 17.210899 10.448399 8.991700 +v 16.867901 10.774399 8.984300 +v 16.867901 10.774399 8.984300 +v 16.972200 10.996399 8.948999 +v 17.424601 10.569499 8.958500 +v 17.210899 10.448399 8.991700 +v 16.747900 10.294899 8.958899 +v 16.867901 10.774399 8.984300 +v 18.055700 11.076699 9.007299 +v 17.699699 10.753099 8.904799 +v 17.134399 11.284299 8.893200 +v 17.134399 11.284299 8.893200 +v 17.426201 11.666999 8.994600 +v 18.055700 11.076699 9.007299 +v 18.222599 11.358299 9.010500 +v 18.055700 11.076699 9.007299 +v 17.426201 11.666999 8.994600 +v 17.426201 11.666999 8.994600 +v 17.693399 11.855999 9.000100 +v 18.222599 11.358299 9.010500 +v 17.541599 10.651999 8.934900 +v 17.424601 10.569499 8.958500 +v 16.972200 10.996399 8.948999 +v 16.972200 10.996399 8.948999 +v 17.045601 11.119200 8.924600 +v 17.541599 10.651999 8.934900 +v 18.140800 11.716399 9.079800 +v 18.219200 11.535199 9.040600 +v 17.869400 11.867399 9.033900 +v 17.869400 11.867399 9.033900 +v 18.055300 11.804600 9.078500 +v 18.140800 11.716399 9.079800 +v 18.169399 11.928999 9.036500 +v 18.140800 11.716399 9.079800 +v 18.055300 11.804600 9.078500 +v 18.055300 11.804600 9.078500 +v 18.115000 11.943499 9.036300 +v 18.169399 11.928999 9.036500 +v 18.407700 12.129000 8.950700 +v 18.269501 12.041199 8.993600 +v 18.262300 12.080699 8.993700 +v 18.262300 12.080699 8.993700 +v 18.383801 12.142699 8.951099 +v 18.407700 12.129000 8.950700 +v 18.269501 12.041199 8.993600 +v 18.169399 11.928999 9.036500 +v 18.115000 11.943499 9.036300 +v 18.115000 11.943499 9.036300 +v 18.262300 12.080699 8.993700 +v 18.269501 12.041199 8.993600 +v 18.407700 12.129000 8.950700 +v 18.383801 12.142699 8.951099 +v 18.445099 12.313499 8.908100 +v 17.679001 10.561600 8.871300 +v 17.541599 10.651999 8.934900 +v 17.699699 10.753099 8.904799 +v 16.945900 11.247599 8.856100 +v 17.134399 11.284299 8.893200 +v 17.045601 11.119200 8.924600 +v 18.396200 11.421000 8.958000 +v 18.222599 11.358299 9.010500 +v 18.219200 11.535199 9.040600 +v 17.743299 12.032899 8.945100 +v 17.869400 11.867399 9.033900 +v 17.693399 11.855999 9.000100 +v 17.699699 10.753099 8.904799 +v 17.541599 10.651999 8.934900 +v 17.045601 11.119200 8.924600 +v 17.045601 11.119200 8.924600 +v 17.134399 11.284299 8.893200 +v 17.699699 10.753099 8.904799 +v 18.219200 11.535199 9.040600 +v 18.222599 11.358299 9.010500 +v 17.693399 11.855999 9.000100 +v 17.693399 11.855999 9.000100 +v 17.869400 11.867399 9.033900 +v 18.219200 11.535199 9.040600 +v 5.032500 20.211599 19.470299 +v 4.604100 20.137999 19.527901 +v 5.365900 19.304199 20.790901 +v 5.692300 19.489498 20.564100 +v 5.829200 19.736898 20.228901 +v 5.739900 19.980099 19.875099 +v 5.448300 20.153799 19.597401 +v 5.841500 19.576899 20.115900 +v 5.704600 19.329498 20.451099 +v 5.378200 19.144098 20.677900 +v 4.616400 19.977999 19.414900 +v 5.044800 20.051600 19.357300 +v 5.460600 19.993799 19.484299 +v 5.752200 19.820000 19.762001 +v 4.949800 19.070499 20.735500 +v 4.533999 19.128300 20.608398 +v 4.242400 19.302099 20.330700 +v 4.153100 19.545300 19.976900 +v 4.290000 19.792700 19.641600 +v 4.277699 19.952700 19.754700 +v 4.140800 19.705299 20.089901 +v 4.230100 19.462099 20.443800 +v 4.521700 19.288399 20.721401 +v 4.937500 19.230499 20.848499 +v 5.919300 19.679998 19.985401 +v 5.908400 19.823999 20.095299 +v 5.840899 19.896999 19.838299 +v 6.135400 19.809399 20.134100 +v 6.007200 19.789799 20.155201 +v 6.011600 19.640900 20.050800 +v 6.149100 19.665199 20.031399 +v 6.201200 19.734400 19.939199 +v 5.919300 19.679998 19.985401 +v 5.985300 19.937500 19.788601 +v 5.840899 19.896999 19.838299 +v 6.354900 19.853899 19.826199 +v 6.479600 19.763399 19.894400 +v 6.334000 19.832100 19.907799 +v 6.333600 19.780499 20.025799 +v 6.249100 19.858599 19.906000 +v 6.154900 19.927799 19.875801 +v 6.187800 19.929399 19.788601 +v 5.982100 19.957699 19.814701 +v 6.045000 19.921598 19.872499 +v 5.985300 19.937500 19.788601 +v 6.189800 19.882099 20.043499 +v 5.967200 20.167099 19.438700 +v 5.902600 20.252098 19.311399 +v 6.004900 20.192398 19.362200 +v 5.962000 20.139000 19.555901 +v 6.070100 20.211599 19.215700 +v 6.081600 20.153000 19.373199 +v 6.040700 20.090399 19.544800 +v 5.913300 20.033098 19.700500 +v 5.895900 20.084700 19.625500 +v 5.636400 19.982899 19.526001 +v 5.620000 20.132898 19.626699 +v 5.785300 19.952599 19.753601 +v 5.721800 20.254499 19.459200 +v 5.623000 20.202900 19.528700 +v 5.629600 20.051498 19.428200 +v 5.734900 20.110899 19.355598 +v 5.858500 20.102299 19.381199 +v 5.636400 19.982899 19.526001 +v 5.922200 20.001299 19.691500 +v 5.785300 19.952599 19.753601 +v 5.922200 20.001299 19.691500 +v 5.847100 20.250000 19.485600 +v 17.424601 10.569499 8.958500 +v 17.433701 10.635699 9.308400 +v 17.210899 10.448399 8.991700 +v 16.926201 10.494799 9.258200 +v 16.747900 10.294899 8.958899 +v 17.051600 10.634699 9.442699 +v 17.685400 10.790899 9.211300 +v 17.751999 10.742699 9.419200 +v 17.611500 10.798999 9.469800 +v 17.237400 10.841000 9.669300 +v 18.054600 11.147399 9.492599 +v 17.743999 10.822699 9.400100 +v 17.699699 10.753099 8.904799 +v 18.055700 11.076699 9.007299 +v 17.865000 11.106199 9.790100 +v 17.679001 10.561600 8.871300 +v 18.221199 11.373798 9.138900 +v 18.222599 11.358299 9.010500 +v 18.095200 11.421700 9.799100 +v 18.165600 11.452099 9.507000 +v 17.541599 10.651999 8.934900 +v 18.396200 11.421000 8.958000 +v 18.114700 11.786699 9.450300 +v 18.033699 11.705899 9.760200 +v 17.861599 11.522900 9.918900 +v 17.577099 11.214600 9.917000 +v 18.219200 11.535199 9.040600 +v 16.972200 10.996399 8.948999 +v 16.867901 10.774399 8.984300 +v 17.029600 11.017699 9.300000 +v 16.926201 10.494799 9.258200 +v 16.747900 10.294899 8.958899 +v 17.051600 10.634699 9.442699 +v 17.174601 11.211199 9.460800 +v 17.108400 11.345798 9.406000 +v 17.166300 11.279099 9.200600 +v 17.237400 10.841000 9.669300 +v 17.189100 11.343899 9.388700 +v 17.485901 11.681199 9.481200 +v 17.134399 11.284299 8.893200 +v 17.426201 11.666999 8.994600 +v 17.453199 11.494900 9.781800 +v 16.945900 11.247599 8.856100 +v 17.706200 11.858398 9.128799 +v 17.693399 11.855999 9.000100 +v 17.749100 11.749800 9.792399 +v 17.780300 11.816599 9.499500 +v 17.045601 11.119200 8.924600 +v 17.743299 12.032899 8.945100 +v 18.033699 11.705899 9.760200 +v 18.114700 11.786699 9.450300 +v 17.861599 11.522900 9.918900 +v 17.577099 11.214600 9.917000 +v 17.869400 11.867399 9.033900 +v 19.248400 14.765299 16.743099 +v 19.584400 14.651799 17.171900 +v 19.244200 15.115799 17.149101 +v 19.012800 15.322799 16.868200 +v 19.776001 14.866999 17.602900 +v 19.151300 15.510499 17.422298 +v 19.626600 15.278199 17.895500 +v 19.230999 15.621399 17.902100 +v 18.844400 15.670300 17.619400 +v 18.708000 15.519899 17.165100 +v 18.830601 14.782299 16.367399 +v 19.470200 14.431999 16.371500 +v 19.393101 14.260799 16.011000 +v 19.452999 14.321799 16.241100 +v 18.647800 14.837299 15.873699 +v 18.409700 15.394499 15.980400 +v 18.286499 15.224499 15.593400 +v 18.361700 14.798399 15.403100 +v 18.013901 15.639099 15.563100 +v 18.038099 15.075399 15.089200 +v 17.723499 15.506399 15.143100 +v 17.580700 15.819299 15.515200 +v 17.700500 15.807499 15.964500 +v 18.014400 15.599199 16.055300 +v 18.994999 14.515299 15.761800 +v 19.200300 14.339199 15.854900 +v 16.512100 14.737999 17.913300 +v 16.224300 15.030399 18.135300 +v 16.023300 15.053499 17.816799 +v 16.327000 14.759999 17.640999 +v 17.018299 14.908998 18.561699 +v 16.855999 14.941799 18.695700 +v 16.358700 14.807199 18.484001 +v 16.807899 14.808199 18.193699 +v 16.547699 14.999999 18.834700 +v 16.225100 15.052299 18.745201 +v 16.071800 15.069299 18.483500 +v 17.166700 14.536098 18.106800 +v 17.389799 14.845499 18.575001 +v 15.733900 14.879899 17.520800 +v 15.697400 15.082399 17.065100 +v 15.919300 15.039799 16.945301 +v 16.181900 14.882199 17.259100 +v 15.700800 15.109599 17.832100 +v 15.468600 15.142699 17.642200 +v 15.477900 15.130299 17.342100 +v 15.935800 15.028299 16.707100 +v 16.415600 14.622399 16.917599 +v 16.952801 14.457499 17.761000 +v 17.588699 14.805299 18.412399 +v 17.719200 14.771599 18.102299 +v 16.141500 14.985600 16.506399 +v 16.695400 14.889599 16.490299 +v 17.014999 14.550799 17.006100 +v 17.415800 14.501499 17.551800 +v 16.598499 14.502099 17.306499 +v 17.679899 14.754899 17.454500 +v 18.017900 14.896399 17.099998 +v 17.232700 14.808899 16.817499 +v 17.771200 14.921399 16.616199 +v 18.323799 15.116099 16.615000 +v 17.540600 15.301999 16.302399 +v 18.069099 15.391099 16.323000 +v 18.705500 15.372799 16.788700 +v 18.554699 15.064999 16.491400 +v 18.342899 15.415400 16.238300 +v 18.566500 15.381199 16.973999 +v 18.161800 15.237000 17.462500 +v 17.721600 14.943299 17.678101 +v 17.032299 15.062699 16.628799 +v 18.452200 15.206099 17.741899 +v 18.092501 15.269199 17.802299 +v 18.746201 15.477499 17.744099 +v 18.125000 15.365699 18.036301 +v 18.254400 15.427399 18.158001 +v 18.447899 15.442499 18.132599 +v 17.346800 15.331099 15.936899 +v 17.108400 15.380799 16.202099 +v 17.511900 15.616899 15.721300 +v 16.920500 15.501699 16.063999 +v 16.882000 15.582099 15.902000 +v 17.006300 15.604899 15.759700 +v 15.468600 15.142699 17.642200 +v 15.700800 15.109599 17.832100 +v 15.959900 15.386499 17.511299 +v 15.477900 15.130299 17.342100 +v 15.697400 15.082399 17.065100 +v 15.919300 15.039799 16.945301 +v 16.313099 15.546999 16.841700 +v 15.935800 15.028299 16.707100 +v 16.141500 14.985600 16.506399 +v 16.695400 14.889599 16.490299 +v 17.588699 14.805299 18.412399 +v 17.719200 14.771599 18.102299 +v 17.340500 15.421899 18.273600 +v 17.389799 14.845499 18.575001 +v 17.018299 14.908998 18.561699 +v 16.855999 14.941799 18.695700 +v 16.541800 15.317099 18.361401 +v 16.547699 14.999999 18.834700 +v 16.225100 15.052299 18.745201 +v 16.071800 15.069299 18.483500 +v 16.224300 15.030399 18.135300 +v 16.023300 15.053499 17.816799 +v 17.540600 15.301999 16.302399 +v 17.032299 15.062699 16.628799 +v 17.136499 15.466399 16.938099 +v 17.448400 15.803599 16.574100 +v 17.721600 14.943299 17.678101 +v 18.161800 15.237000 17.462500 +v 17.936699 15.692299 17.457800 +v 17.516199 15.391199 17.499300 +v 18.092501 15.269199 17.802299 +v 18.131399 15.676600 17.955601 +v 18.746201 15.477499 17.744099 +v 18.708000 15.519899 17.165100 +v 18.450199 15.790400 17.003000 +v 18.125000 15.365699 18.036301 +v 18.254400 15.427399 18.158001 +v 18.447899 15.442499 18.132599 +v 17.108400 15.380799 16.202099 +v 17.035700 15.800200 16.155701 +v 18.014400 15.599199 16.055300 +v 17.511900 15.616899 15.721300 +v 18.067801 15.832799 16.393900 +v 16.920500 15.501699 16.063999 +v 16.882000 15.582099 15.902000 +v 17.006300 15.604899 15.759700 +v 18.130800 15.788099 16.720600 +v 16.620300 15.220999 18.156799 +v 15.410800 14.816099 18.185900 +v 15.267500 15.163599 18.022800 +v 16.292200 15.410899 17.702900 +v 14.485000 14.574799 18.582001 +v 14.369100 14.965599 18.405300 +v 13.700800 14.381899 18.984001 +v 13.575700 14.792099 18.796700 +v 12.847500 14.626299 19.234200 +v 13.000800 14.217400 19.445599 +v 12.383400 14.168999 20.052601 +v 12.206700 14.555899 19.805700 +v 11.901300 14.271899 20.828899 +v 11.695100 14.630699 20.600800 +v 11.525400 14.434799 21.687401 +v 11.262500 14.804299 21.611099 +v 10.909100 14.987799 22.737801 +v 11.297200 14.679299 22.661999 +v 17.607399 17.080099 19.532299 +v 17.833799 17.608999 19.101299 +v 17.529400 18.328499 19.968500 +v 17.485300 17.688000 20.347900 +v 17.059000 18.013300 21.044600 +v 16.976700 18.677399 20.706100 +v 16.420500 18.834700 21.522200 +v 16.631800 18.216200 21.837700 +v 15.805600 18.741598 22.315599 +v 16.059299 18.166199 22.604099 +v 15.225400 18.584000 23.111700 +v 15.491100 17.998499 23.360600 +v 14.898600 17.783199 23.975100 +v 14.658700 18.306898 23.740799 +v 13.966800 17.975800 24.401600 +v 14.270200 17.518299 24.488100 +v 13.265000 17.552999 24.966400 +v 13.595500 17.198500 24.994999 +v 19.968300 17.433399 13.923900 +v 19.869499 17.355000 14.891200 +v 19.654301 17.466099 14.870199 +v 19.553600 17.650499 13.913099 +v 19.464800 17.734098 13.151300 +v 19.950800 17.494699 13.308200 +v 19.739901 17.357498 15.421499 +v 19.751900 16.929600 12.396299 +v 19.931400 16.517899 12.017300 +v 20.017300 16.774698 12.349100 +v 20.101900 16.761799 12.923500 +v 19.793600 16.976898 13.059700 +v 18.954000 17.949499 12.232900 +v 19.294100 17.494999 12.086900 +v 19.325300 17.523199 12.667600 +v 19.313700 17.768599 12.697300 +v 19.608101 16.987200 12.056200 +v 19.910500 16.869200 11.559000 +v 20.193901 17.073799 13.243999 +v 19.912300 17.252600 13.275900 +v 19.487801 17.486500 13.153500 +v 19.484900 17.343599 11.659800 +v 19.855900 17.547699 14.863000 +v 19.869499 17.355000 14.891200 +v 19.968300 17.433399 13.923900 +v 19.927099 17.848499 13.885700 +v 19.553600 17.650499 13.913099 +v 19.654301 17.466099 14.870199 +v 19.950800 17.494699 13.308200 +v 19.908501 17.962700 13.090700 +v 19.464800 17.734098 13.151300 +v 19.739901 17.357498 15.421499 +v 19.590599 18.103399 12.632600 +v 19.613400 18.197498 12.158199 +v 19.313700 17.768599 12.697300 +v 19.657799 17.849499 11.606400 +v 19.484900 17.343599 11.659800 +v 18.954000 17.949499 12.232900 +v 19.898899 17.682999 11.476600 +v 19.910500 16.869200 11.559000 +v 20.306801 17.926498 13.200600 +v 20.255100 17.431299 13.318100 +v 20.310400 18.169298 12.998800 +v 20.267399 18.305899 12.528700 +v 20.301800 18.225399 12.119800 +v 20.255100 17.431299 13.318100 +v 20.199299 18.577900 11.243200 +v 19.857399 18.162600 11.474899 +v 19.891201 18.330099 11.783000 +v 20.141800 18.666399 11.821600 +v 20.061699 18.296598 12.055099 +v 20.301800 18.225399 12.119800 +v 20.759800 18.151899 12.082900 +v 20.558599 18.420500 12.057899 +v 21.001499 18.315399 12.214200 +v 20.704800 18.616999 12.296300 +v 20.299299 18.921299 12.075600 +v 20.901600 19.186600 12.782300 +v 21.088699 18.838598 12.826300 +v 21.220800 20.116199 12.612200 +v 21.415899 19.862799 13.021500 +v 21.709301 20.247599 12.803300 +v 21.526100 20.549099 12.367100 +v 21.639700 19.505098 13.203100 +v 22.052299 19.902498 12.976400 +v 20.809799 19.451899 12.471500 +v 22.564899 20.608299 12.465799 +v 22.524101 20.616999 12.243900 +v 22.035000 20.740200 12.280600 +v 22.427799 20.579699 12.558200 +v 20.301600 18.184599 11.004000 +v 19.991600 17.866798 11.294000 +v 19.982201 19.149599 11.522099 +v 20.459499 19.145899 11.772900 +v 20.019001 18.874899 11.046900 +v 21.025799 19.557499 12.067699 +v 21.060101 19.003899 11.335199 +v 20.380400 19.309799 11.481400 +v 21.444901 20.089800 11.966900 +v 21.505199 19.342299 11.947500 +v 21.637300 20.776899 11.804300 +v 21.816700 19.750500 11.783300 +v 21.661100 20.315300 11.604500 +v 22.181200 20.992599 11.786200 +v 22.447201 20.508699 12.687799 +v 20.753000 19.662498 10.685900 +v 20.854799 19.599998 10.461800 +v 20.930401 19.859499 10.757900 +v 20.676500 19.585600 10.309800 +v 20.577400 19.482899 10.457000 +v 20.926600 19.842400 10.404500 +v 20.842800 19.572800 9.623400 +v 20.828701 19.477499 8.818100 +v 20.831600 19.556700 8.793400 +v 20.834099 19.676899 9.605900 +v 20.754900 19.476799 8.817500 +v 20.743700 19.582800 9.635400 +v 20.368099 18.979698 11.067600 +v 20.019001 18.874899 11.046900 +v 20.380400 19.309799 11.481400 +v 22.357000 21.171299 11.385900 +v 22.181200 20.992599 11.786200 +v 22.109501 20.347198 11.800800 +v 22.401001 20.869299 11.165500 +v 22.639200 21.265200 10.741300 +v 22.478199 21.281399 10.614900 +v 22.725500 21.582699 9.295000 +v 22.807400 21.583099 9.250600 +v 22.111700 20.850399 10.992900 +v 21.661100 20.315300 11.604500 +v 22.426399 21.219599 10.730100 +v 22.596901 21.416100 11.075500 +v 22.784800 21.654499 9.223900 +v 22.649399 21.478899 10.926000 +v 20.680700 19.395300 8.782000 +v 20.786200 19.368799 8.778900 +v 20.775101 19.352400 8.715100 +v 20.854900 19.469700 8.779900 +v 20.669600 19.379000 8.718200 +v 20.831600 19.556700 8.793400 +v 20.854900 19.469700 8.779900 +v 20.775101 19.352400 8.715100 +v 20.669600 19.379000 8.718200 +v 22.642200 21.481800 9.272099 +v 22.752300 21.474098 9.248500 +v 22.740601 21.459799 9.201200 +v 22.630501 21.467499 9.224800 +v 22.784800 21.654499 9.223900 +v 22.807400 21.583099 9.250600 +v 22.740601 21.459799 9.201200 +v 22.630501 21.467499 9.224800 +v 20.654600 19.778999 10.453000 +v 20.753000 19.662498 10.685900 +v 20.930401 19.859499 10.757900 +v 20.926600 19.842400 10.404500 +v 20.727200 19.678299 9.615000 +v 20.834099 19.676899 9.605900 +v 20.831600 19.556700 8.793400 +v 20.750000 19.549299 8.812400 +v 19.982201 19.149599 11.522099 +v 20.380400 19.309799 11.481400 +v 22.357000 21.171299 11.385900 +v 22.082901 21.133099 11.163799 +v 21.637300 20.776899 11.804300 +v 22.181200 20.992599 11.786200 +v 22.436001 21.434399 10.740499 +v 22.709200 21.661699 9.250600 +v 22.596901 21.416100 11.075500 +v 22.649399 21.478899 10.926000 +v 22.784800 21.654499 9.223900 +v 20.646299 19.496300 8.768700 +v 20.635201 19.480000 8.704900 +v 20.743000 19.571798 8.771800 +v 20.635201 19.480000 8.704900 +v 20.743000 19.571798 8.771800 +v 22.612000 21.586199 9.248500 +v 22.600300 21.571999 9.201200 +v 22.600300 21.571999 9.201200 +v 22.709200 21.661699 9.250600 +v 20.802200 18.682499 10.928400 +v 20.368099 18.979698 11.067600 +v 22.109501 20.347198 11.800800 +v 22.142799 20.159599 11.874100 +v 22.562000 21.010199 13.606800 +v 22.647701 21.105799 13.528299 +v 19.891201 18.330099 11.783000 +v 19.857399 18.162600 11.474899 +v 19.991600 17.866798 11.294000 +v 20.061699 18.296598 12.055099 +v 20.694201 17.331799 13.814699 +v 21.130501 17.429600 13.675800 +v 21.264801 17.240499 14.627800 +v 21.043301 17.190599 14.714500 +v 21.014599 17.516998 12.917999 +v 20.548901 17.410898 13.218200 +v 21.316200 17.136700 15.184299 +v 20.316500 16.850498 12.311300 +v 20.482500 16.880400 12.955999 +v 21.276600 17.624199 11.883300 +v 21.031601 17.528000 12.438800 +v 20.946301 17.296099 12.423600 +v 20.761400 17.245699 11.858800 +v 20.361900 16.880098 11.942500 +v 20.509899 17.168798 13.186000 +v 20.927401 17.284800 12.936800 +v 20.379101 17.157398 11.515200 +v 21.099499 17.373499 14.675800 +v 20.832600 17.721699 13.749399 +v 20.694201 17.331799 13.814699 +v 21.043301 17.190599 14.714500 +v 21.264801 17.240499 14.627800 +v 21.130501 17.429600 13.675800 +v 20.650799 17.858700 12.979000 +v 20.548901 17.410898 13.218200 +v 21.014599 17.516998 12.917999 +v 21.316200 17.136700 15.184299 +v 20.690899 17.774799 11.951300 +v 20.842800 17.912399 12.441600 +v 21.031601 17.528000 12.438800 +v 20.195301 17.503500 11.480900 +v 21.276600 17.624199 11.883300 +v 20.379101 17.157398 11.515200 +v 20.645300 17.828400 11.076700 +v 21.183300 17.868299 11.343000 +v 20.518299 17.719500 11.602600 +v 20.288500 17.707100 11.344999 +v 20.511299 17.969398 11.944000 +v 20.943399 17.925699 11.880400 +v 21.285999 18.211899 12.062799 +v 21.533199 18.292599 11.521900 +v 21.465599 18.720200 12.782300 +v 22.338501 19.211399 12.641500 +v 22.716499 19.612000 12.331800 +v 22.525700 19.790699 12.823999 +v 22.049400 19.356298 13.040000 +v 21.743500 18.679998 12.471500 +v 22.560699 20.495098 12.580800 +v 22.757099 20.145699 12.141600 +v 21.454300 18.542999 11.028200 +v 21.446701 18.642199 11.331100 +v 21.006100 18.287600 11.036300 +v 21.806601 18.911900 12.067699 +v 21.332001 18.981499 10.741600 +v 22.199501 19.382700 12.039000 +v 22.713299 19.681599 11.689100 +v 22.223099 19.561499 11.674200 +v 22.740900 20.244900 11.542299 +v 20.860500 18.499300 9.799400 +v 21.053699 18.597500 9.927400 +v 21.147499 18.761398 9.716599 +v 20.864500 18.262098 9.811200 +v 20.899601 18.311100 10.032800 +v 20.986599 18.501099 9.569600 +v 20.584600 18.007898 9.185900 +v 20.673500 18.005798 9.128300 +v 20.326000 17.530399 8.587300 +v 20.258699 17.521299 8.632100 +v 20.281399 17.455898 8.653500 +v 20.634201 17.936199 9.235600 +v 20.909000 18.666698 10.775599 +v 21.006100 18.287600 11.036300 +v 21.332001 18.981499 10.741600 +v 22.712500 20.379398 11.090799 +v 22.388199 20.323999 11.011700 +v 22.240900 20.060598 11.797899 +v 22.740900 20.244900 11.542299 +v 22.513401 20.557598 10.446199 +v 22.044500 20.249199 9.000999 +v 22.059500 20.168699 9.045400 +v 22.510300 20.377199 10.329400 +v 22.358700 19.998098 10.873700 +v 22.223099 19.561499 11.674200 +v 22.509899 20.335098 10.463600 +v 22.755800 20.607100 10.690500 +v 22.745499 20.643898 10.524600 +v 22.118900 20.240398 8.974299 +v 20.222000 17.359900 8.659400 +v 20.164900 17.448198 8.638500 +v 20.143400 17.416100 8.580000 +v 20.236099 17.528999 8.593200 +v 20.200001 17.327299 8.596600 +v 20.326000 17.530399 8.587300 +v 20.200001 17.327299 8.596600 +v 20.143400 17.416100 8.580000 +v 20.236099 17.528999 8.593200 +v 21.976000 20.067900 9.022400 +v 21.947800 20.174599 8.998899 +v 21.936001 20.160398 8.951600 +v 21.964199 20.053699 8.975200 +v 22.118900 20.240398 8.974299 +v 21.964199 20.053699 8.975200 +v 21.936001 20.160398 8.951600 +v 22.044500 20.249199 9.000999 +v 21.053699 18.597500 9.927400 +v 21.088400 18.357199 9.795200 +v 21.147499 18.761398 9.716599 +v 20.986599 18.501099 9.569600 +v 20.717600 17.924898 9.183500 +v 20.348600 17.463999 8.630100 +v 20.326000 17.530399 8.587300 +v 20.673500 18.005798 9.128300 +v 21.454300 18.542999 11.028200 +v 21.332001 18.981499 10.741600 +v 22.712500 20.379398 11.090799 +v 22.740900 20.244900 11.542299 +v 22.713299 19.681599 11.689100 +v 22.678501 20.067299 10.924600 +v 22.701099 20.394999 10.389200 +v 22.140100 20.167500 9.000999 +v 22.755800 20.607100 10.690500 +v 22.118900 20.240398 8.974299 +v 22.745499 20.643898 10.524600 +v 20.324200 17.345798 8.631700 +v 20.303299 17.314598 8.572200 +v 20.364000 17.447399 8.590400 +v 20.364000 17.447399 8.590400 +v 20.303299 17.314598 8.572200 +v 22.084301 20.057899 8.998899 +v 22.072500 20.043699 8.951600 +v 22.140100 20.167500 9.000999 +v 22.072500 20.043699 8.951600 +v 20.909000 18.666698 10.775599 +v 22.240900 20.060598 11.797899 +v 22.663700 21.722300 14.444099 +v 22.575199 21.976799 15.184900 +v 22.647701 21.105799 13.528299 +v 22.798700 21.779499 13.830500 +v 22.782600 21.892799 14.270100 +v 23.054901 22.514399 15.063000 +v 23.110901 22.854399 15.908199 +v 23.112000 22.395199 14.146700 +v 23.197100 22.584499 14.790000 +v 23.619900 23.093300 15.082200 +v 23.507401 22.966000 15.422199 +v 24.007401 23.400099 15.630500 +v 23.975399 23.334700 15.837900 +v 24.165300 23.526100 15.607500 +v 20.288500 17.707100 11.344999 +v 20.518299 17.719500 11.602600 +v 20.511299 17.969398 11.944000 +v 4.263600 27.659500 49.508099 +v 3.804100 26.956299 49.090199 +v 4.018100 27.348598 48.979401 +v 4.313200 27.799900 49.320301 +v 4.223400 27.822899 49.458698 +v 5.319900 28.674099 49.470398 +v 3.924300 27.491899 49.322498 +v 3.914900 26.449900 48.457699 +v 4.111000 26.974899 48.560799 +v 3.677200 27.209400 49.044601 +v 3.551900 26.969599 48.672298 +v 4.267400 26.212099 47.964798 +v 4.511000 25.958099 48.359299 +v 3.640000 26.614300 48.227100 +v 4.485500 26.672100 48.399700 +v 4.607100 26.100399 48.013100 +v 5.122400 25.518700 48.354198 +v 5.251100 26.240499 48.442299 +v 4.958500 25.630798 47.790501 +v 5.719300 24.686199 47.933899 +v 6.019900 25.571699 48.236301 +v 4.748199 25.313700 47.366699 +v 4.898499 24.248199 47.120098 +v 6.340000 24.952099 47.385201 +v 6.391500 25.279900 47.975899 +v 6.037900 24.827198 46.861801 +v 4.142200 25.199099 47.156898 +v 6.308500 24.639299 46.533901 +v 5.834599 24.081499 46.675201 +v 4.555400 22.863199 44.277699 +v 4.213700 22.887400 44.315498 +v 4.059500 22.584599 43.654900 +v 3.938100 24.425900 46.914902 +v 4.738900 23.880699 46.772099 +v 3.907500 22.966400 44.300301 +v 3.750400 23.135099 45.427898 +v 3.614700 23.619099 46.148201 +v 3.337200 24.007000 46.020699 +v 3.498700 23.408199 45.348900 +v 5.903100 23.536999 45.617199 +v 6.437000 24.445198 45.916698 +v 6.469500 24.517399 46.156601 +v 5.387800 23.257900 45.961601 +v 3.883000 22.877199 44.738800 +v 4.289000 22.973099 44.798599 +v 4.721100 22.686298 44.757198 +v 5.033200 22.870399 44.527298 +v 4.367400 23.138298 45.363701 +v 4.440800 23.370399 45.916599 +v 5.031199 22.806900 45.365101 +v 3.577100 25.144798 46.811600 +v 3.065400 25.081600 46.401199 +v 3.049000 25.051899 46.062199 +v 5.448600 23.029499 45.196098 +v 3.664200 23.134199 44.702000 +v 5.076000 23.032499 44.245098 +v 4.071300 23.100300 42.847698 +v 4.474200 24.615599 42.355598 +v 5.343200 24.484299 42.333302 +v 5.972500 23.585499 44.860100 +v 6.584500 23.946400 45.324600 +v 5.566000 22.710598 43.845901 +v 5.541800 23.078999 43.057098 +v 6.294000 23.177700 43.549702 +v 6.971500 23.361000 44.348000 +v 6.504900 23.098999 44.778000 +v 7.075300 24.327900 45.434299 +v 7.458400 24.335499 45.246700 +v 6.959200 23.586599 45.186001 +v 7.332500 23.577999 44.784298 +v 7.347300 24.339699 43.854198 +v 5.659300 23.384899 44.457600 +v 6.114799 24.389399 42.558800 +v 6.825600 24.346498 43.094799 +v 7.603100 24.341198 44.710098 +v 3.463600 23.368898 44.431301 +v 2.908600 23.292599 44.192902 +v 2.319600 24.129099 43.956600 +v 2.786099 23.638899 43.330299 +v 2.407300 24.547699 45.200401 +v 1.975100 24.547899 44.852600 +v 3.001100 23.976200 44.884602 +v 2.762500 24.494598 45.339699 +v 2.513800 25.136099 45.667599 +v 2.130199 25.176100 45.616798 +v 1.753900 25.192600 45.153900 +v 2.110500 25.024300 43.591999 +v 2.805700 24.883400 42.984901 +v 1.760100 25.146500 44.409500 +v 3.649700 24.748600 42.593700 +v 4.258700 27.960199 49.315601 +v 4.313200 27.799900 49.320301 +v 4.018100 27.348598 48.979401 +v 3.808800 27.477898 48.879700 +v 4.223400 27.822899 49.458698 +v 5.319900 28.674099 49.470398 +v 3.924300 27.491899 49.322498 +v 4.111000 26.974899 48.560799 +v 3.946000 27.025999 48.277298 +v 3.677200 27.209400 49.044601 +v 3.551900 26.969599 48.672298 +v 4.267400 26.212099 47.964798 +v 3.640000 26.614300 48.227100 +v 4.570500 26.756798 48.100498 +v 4.485500 26.672100 48.399700 +v 4.607100 26.100399 48.013100 +v 5.175800 26.482300 48.102901 +v 5.251100 26.240499 48.442299 +v 4.958500 25.630798 47.790501 +v 6.019900 25.571699 48.236301 +v 5.802299 25.829300 47.626301 +v 5.187200 25.849098 46.511700 +v 4.748199 25.313700 47.366699 +v 6.391500 25.279900 47.975899 +v 6.340000 24.952099 47.385201 +v 6.037900 24.827198 46.861801 +v 4.142200 25.199099 47.156898 +v 6.308500 24.639299 46.533901 +v 5.994200 25.334799 46.413101 +v 4.204500 25.688700 46.518700 +v 6.469500 24.517399 46.156601 +v 6.437000 24.445198 45.916698 +v 6.134000 25.996099 45.598198 +v 5.153800 26.581398 45.713200 +v 4.040300 26.609100 45.872299 +v 3.577100 25.144798 46.811600 +v 3.065400 25.081600 46.401199 +v 3.049000 25.051899 46.062199 +v 4.964800 26.221199 42.851200 +v 5.343200 24.484299 42.333302 +v 4.474200 24.615599 42.355598 +v 7.205100 25.802700 44.695900 +v 6.307500 26.690998 44.183800 +v 6.283300 25.961399 43.058800 +v 5.287200 27.070799 43.977501 +v 6.817700 25.639900 43.551201 +v 7.075300 24.327900 45.434299 +v 7.458400 24.335499 45.246700 +v 6.114799 24.389399 42.558800 +v 6.825600 24.346498 43.094799 +v 7.347300 24.339699 43.854198 +v 7.603100 24.341198 44.710098 +v 3.835800 27.021799 44.439400 +v 3.261300 26.380600 43.335400 +v 2.518200 26.267199 43.960598 +v 2.363200 26.448099 45.024101 +v 2.513800 25.136099 45.667599 +v 2.130199 25.176100 45.616798 +v 1.753900 25.192600 45.153900 +v 2.110500 25.024300 43.591999 +v 2.805700 24.883400 42.984901 +v 1.760100 25.146500 44.409500 +v 3.649700 24.748600 42.593700 +v 7.417800 29.449699 13.275499 +v 7.596400 29.215199 13.615499 +v 8.546300 29.087299 13.706800 +v 8.648300 29.337200 13.302400 +v 8.170500 28.169600 15.053499 +v 7.928100 28.375000 14.033000 +v 7.682600 28.275898 14.163700 +v 7.971300 27.891899 15.065701 +v 8.033199 27.919399 16.051600 +v 8.147699 28.223698 16.317400 +v 6.925000 28.811298 13.878099 +v 6.753600 29.061298 13.490299 +v 4.042300 31.966900 11.708000 +v 5.058000 31.237499 13.456900 +v 6.763500 32.065998 13.011400 +v 6.504000 32.675598 11.323000 +v 7.145500 30.715199 13.391600 +v 6.063200 30.111500 13.721800 +v 4.271900 31.629999 9.942500 +v 6.630100 32.170399 9.455400 +v 4.605800 30.926498 9.260600 +v 6.901400 31.531498 8.972000 +v 6.569000 27.891899 14.188000 +v 7.538500 27.891899 14.328400 +v 6.173600 27.891899 13.971200 +v 6.173600 27.891899 13.971200 +v 6.753600 29.061298 13.490299 +v 5.147799 28.674198 13.986099 +v 3.796600 29.547899 13.787600 +v 2.791700 30.174900 12.326000 +v 2.883300 30.174999 10.635500 +v 3.282500 30.174900 9.662000 +v 8.507500 27.919399 16.032000 +v 8.406000 27.891899 15.055500 +v 8.265699 28.275898 14.073200 +v 9.379200 27.891899 13.920200 +v 8.507999 27.891899 14.239100 +v 9.557800 27.891899 13.473700 +v 9.398700 31.917000 12.858800 +v 8.929300 30.583298 13.328800 +v 9.370100 32.646400 11.327900 +v 9.297199 32.309498 9.575200 +v 9.098900 31.336599 8.960299 +v 11.707100 30.238899 12.964200 +v 10.462600 29.000698 13.387199 +v 12.017099 30.671499 11.582500 +v 11.947100 30.671499 9.875800 +v 11.262500 30.238899 9.143200 +v 12.608600 27.891800 13.025600 +v 10.901700 27.891899 13.410500 +v 13.193800 27.891800 11.569600 +v 12.973800 27.891800 10.007800 +v 12.289300 27.891800 9.290100 +v 3.389400 27.891800 13.849700 +v 4.982400 27.891800 14.055599 +v 2.206100 27.891800 12.549100 +v 2.239000 27.891800 10.769500 +v 2.638200 27.891800 9.815599 +v 9.098900 31.336599 8.960299 +v 11.262500 30.238899 9.143200 +v 12.289300 27.891800 9.290100 +v 11.262500 25.544800 9.066000 +v 9.098900 24.447100 8.808000 +v 6.901400 24.252199 8.819600 +v 4.605800 24.857199 9.108300 +v 3.282500 25.608700 9.509700 +v 2.638200 27.891800 9.815599 +v 3.282500 30.174900 9.662000 +v 4.605800 30.926498 9.260600 +v 6.901400 31.531498 8.972000 +v 7.417800 26.334099 13.275600 +v 8.648300 26.446499 13.302600 +v 8.546300 26.696400 13.706900 +v 7.596400 26.568499 13.615600 +v 8.113300 27.614199 15.053600 +v 7.971300 27.891899 15.065701 +v 7.682700 27.507799 14.163799 +v 7.928100 27.408699 14.033100 +v 8.147699 27.566700 16.317499 +v 8.033199 27.919399 16.051600 +v 6.925000 26.972399 13.878201 +v 6.753600 26.722500 13.490400 +v 4.112300 23.816799 11.681100 +v 6.504900 23.108099 11.031500 +v 6.763500 23.717699 13.011499 +v 5.058000 24.546200 13.457000 +v 7.145500 25.068499 13.391700 +v 6.063200 25.672199 13.721900 +v 4.271900 24.153698 9.770700 +v 6.630100 23.613298 9.283600 +v 4.605800 24.857199 9.108300 +v 6.901400 24.252199 8.819600 +v 7.538500 27.891899 14.328400 +v 6.569000 27.891899 14.188000 +v 6.173600 27.891899 13.971200 +v 6.173600 27.891899 13.971200 +v 5.147799 27.109499 13.986099 +v 6.753600 26.722500 13.490400 +v 3.796600 26.235798 13.787700 +v 2.765300 25.608799 12.241000 +v 2.883300 25.608700 10.463699 +v 3.282500 25.608700 9.509700 +v 8.406000 27.891899 15.055500 +v 8.507500 27.919399 16.032000 +v 8.265699 27.507799 14.073299 +v 9.379200 27.891899 13.920200 +v 8.507999 27.891899 14.239100 +v 9.557800 27.891899 13.473700 +v 9.398700 23.866699 12.858900 +v 8.929300 25.200399 13.328899 +v 9.443501 23.137299 11.011300 +v 9.297199 23.474199 9.403500 +v 9.098900 24.447100 8.808000 +v 11.707000 25.544800 12.964300 +v 10.462600 26.782999 13.387199 +v 12.104200 25.112198 11.303900 +v 11.947100 25.112198 9.704000 +v 11.262500 25.544800 9.066000 +v 12.608600 27.891800 13.025600 +v 10.901700 27.891899 13.410500 +v 13.193800 27.891800 11.569600 +v 12.973800 27.891800 10.007800 +v 12.289300 27.891800 9.290100 +v 4.982400 27.891800 14.055599 +v 3.389400 27.891800 13.849700 +v 2.206100 27.891800 12.549100 +v 2.239000 27.891800 10.769500 +v 2.638200 27.891800 9.815599 +v 10.019600 28.521198 16.366199 +v 10.139400 28.416500 16.746201 +v 10.104400 27.980598 16.954700 +v 9.739500 28.163498 15.885201 +v 9.815001 27.489998 16.783100 +v 9.604799 27.295799 16.386000 +v 9.569700 27.514599 16.018200 +v 9.604799 27.295799 16.386000 +v 9.569700 27.514599 16.018200 +v 6.836200 27.679899 16.654100 +v 6.807100 27.616999 16.407499 +v 8.228300 27.629499 16.613800 +v 9.815001 27.489998 16.783100 +v 10.104400 27.980598 16.954700 +v 8.266600 27.919399 16.708000 +v 6.878900 27.919399 16.751400 +v 10.019600 28.521198 16.366199 +v 9.739500 28.163498 15.885201 +v 6.807100 28.195999 16.473801 +v 6.828200 27.919399 16.228100 +v 6.836200 28.176199 16.653900 +v 8.228300 28.160900 16.613600 +v 10.139400 28.416500 16.746201 +v 9.739500 28.163498 15.885201 +v 6.836200 27.679899 16.654100 +v 6.878900 27.919399 16.751400 +v 6.836200 28.176199 16.653900 +v 6.807100 28.195999 16.473801 +v 6.828200 27.919399 16.228100 +v 6.807100 27.616999 16.407499 +v 6.828200 27.919399 16.228100 +v 7.417800 29.449699 13.275499 +v 8.648300 29.337200 13.302400 +v 9.557800 27.891899 13.473700 +v 7.417800 26.334099 13.275600 +v 8.648300 26.446499 13.302600 +v 9.557800 27.891899 13.473700 +v 7.044000 15.411098 10.107600 +v 7.333500 14.939099 10.033100 +v 9.161300 17.199499 9.599600 +v 8.278299 14.696999 8.869900 +v 9.197599 17.234900 8.869900 +v 8.342700 14.709599 9.791100 +v 7.804500 14.695399 9.917600 +v 7.721300 14.686099 8.869900 +v 7.198000 14.939099 8.869900 +v 6.893700 16.013498 8.869900 +v 6.917100 15.416499 8.869900 +v 7.034600 16.039999 10.140400 +v 2.211900 32.021698 13.388100 +v 2.181700 32.050301 13.686600 +v 2.640900 32.313999 14.054300 +v 2.840300 32.366100 13.802200 +v 1.966700 32.084698 14.918500 +v 2.254100 31.834400 14.312700 +v 2.082800 31.739700 14.340300 +v 1.885800 31.879700 14.980600 +v 1.498800 32.183701 15.423000 +v 1.416600 32.451298 15.448400 +v 1.765500 31.696400 13.774600 +v 1.818100 31.665100 13.450800 +v 0.910800 31.816498 10.683200 +v 0.760700 32.242298 12.050200 +v 1.742900 33.099197 12.005600 +v 2.265400 32.870697 10.933300 +v 1.904700 32.623798 12.840000 +v 1.268700 32.052498 12.921300 +v 1.804900 31.232100 10.107800 +v 3.165500 32.137600 10.352100 +v 2.329500 30.787199 10.192400 +v 3.568300 31.760399 10.479800 +v 1.539200 31.188799 14.187000 +v 1.975700 31.538799 14.529500 +v 1.428500 31.002399 13.976800 +v 1.428500 31.002399 13.976800 +v 1.818100 31.665100 13.450800 +v 0.824200 31.083500 13.364799 +v 0.134900 31.049099 12.526800 +v 0.179000 30.652199 11.332100 +v 0.939300 30.219099 10.611300 +v 1.554100 30.080999 10.296400 +v 1.749599 32.330700 15.551399 +v 2.112400 32.016598 15.101800 +v 2.419100 31.902298 14.468900 +v 3.089100 32.018700 14.881399 +v 2.509100 31.826000 14.770400 +v 3.368900 31.953899 14.735600 +v 3.168700 33.826900 12.761499 +v 2.855600 33.111397 13.382299 +v 3.731600 33.778000 11.776400 +v 4.465600 33.099899 11.119200 +v 4.715100 32.362198 11.190300 +v 4.462200 33.725300 14.166200 +v 3.763800 32.797401 14.502800 +v 5.163200 33.671700 13.467100 +v 5.847600 33.182297 12.692400 +v 5.847400 32.536900 12.348600 +v 5.117900 32.811798 15.419900 +v 4.082700 32.368500 15.096399 +v 6.031500 32.601398 14.945499 +v 6.578100 32.103401 14.191500 +v 6.531000 31.687000 13.676200 +v 0.056200 30.074398 13.118000 +v 0.783800 30.642700 13.669600 +v 0.000000 29.338299 12.200800 +v 0.767800 28.861900 11.423700 +v 1.374500 28.729198 11.117400 +v 4.715100 32.362198 11.190300 +v 5.847400 32.536900 12.348600 +v 6.531000 31.687000 13.676200 +v 6.321100 30.075699 14.246000 +v 5.426900 28.739100 13.957900 +v 4.316700 27.934799 13.407800 +v 2.964300 27.590500 12.622499 +v 2.047600 27.665699 12.108000 +v 1.374500 28.729198 11.117400 +v 1.554100 30.080999 10.296400 +v 2.329500 30.787199 10.192400 +v 3.568300 31.760399 10.479800 +v 2.504600 30.402199 14.670200 +v 3.111900 30.863499 14.991699 +v 2.865600 31.071199 15.038099 +v 2.430400 30.674500 14.775700 +v 1.989600 31.777599 15.130600 +v 1.885800 31.879700 14.980600 +v 2.154900 31.340500 14.656400 +v 2.344900 31.332100 14.710400 +v 1.478300 32.109798 15.718800 +v 1.498800 32.183701 15.423000 +v 1.938300 30.740499 14.531400 +v 2.037900 30.449398 14.413200 +v 1.724000 27.595100 14.045200 +v 3.288100 27.817898 14.741600 +v 2.527500 28.759598 15.440800 +v 1.389500 28.764099 14.803600 +v 2.435300 29.688599 15.163500 +v 1.685900 29.744900 14.747999 +v 2.580100 27.298800 13.108200 +v 4.042300 27.642498 13.797300 +v 2.964300 27.590500 12.622499 +v 4.316700 27.934799 13.407800 +v 1.975700 31.538799 14.529500 +v 1.539200 31.188799 14.187000 +v 1.428500 31.002399 13.976800 +v 1.428500 31.002399 13.976800 +v 0.971200 30.270199 14.008699 +v 2.037900 30.449398 14.413200 +v 0.446200 29.327400 13.889700 +v 0.630500 28.246799 13.165700 +v 1.440900 27.798500 12.414200 +v 2.047600 27.665699 12.108000 +v 2.112400 32.016598 15.101800 +v 1.749599 32.330700 15.551399 +v 2.491199 31.503099 14.785000 +v 3.089100 32.018700 14.881399 +v 2.509100 31.826000 14.770400 +v 3.368900 31.953899 14.735600 +v 3.925300 29.642200 16.074200 +v 3.361500 30.313198 15.597400 +v 4.796400 28.771900 15.570499 +v 5.368500 28.460098 14.678800 +v 5.426900 28.739100 13.957900 +v 4.903399 31.285198 16.097799 +v 3.972199 31.644699 15.415300 +v 5.847800 30.733599 15.656700 +v 6.442600 30.245399 14.903999 +v 6.321100 30.075699 14.246000 +v 5.117900 32.811798 15.419900 +v 4.082700 32.368500 15.096399 +v 6.031500 32.601398 14.945499 +v 6.578100 32.103401 14.191500 +v 6.531000 31.687000 13.676200 +v 0.783800 30.642700 13.669600 +v 0.056200 30.074398 13.118000 +v 0.000000 29.338299 12.200800 +v 0.767800 28.861900 11.423700 +v 1.374500 28.729198 11.117400 +v 2.325200 33.221001 15.888900 +v 2.235900 33.308998 16.134600 +v 2.171000 33.128197 16.396000 +v 2.418500 32.813400 15.742399 +v 2.141500 32.733200 16.438299 +v 2.219900 32.456001 16.281900 +v 2.336600 32.457901 16.019100 +v 2.219900 32.456001 16.281900 +v 2.336600 32.457901 16.019100 +v 0.655000 31.839298 15.441700 +v 0.750100 31.729799 15.350100 +v 1.388600 32.249500 15.847200 +v 2.141500 32.733200 16.438299 +v 2.171000 33.128197 16.396000 +v 1.341100 32.438301 15.780600 +v 0.613300 32.004200 15.398500 +v 2.325200 33.221001 15.888900 +v 2.418500 32.813400 15.742399 +v 0.667700 32.048897 15.141200 +v 0.808200 31.844700 15.152500 +v 0.608400 32.097298 15.237400 +v 1.338700 32.525600 15.628500 +v 2.235900 33.308998 16.134600 +v 2.418500 32.813400 15.742399 +v 0.655000 31.839298 15.441700 +v 0.613300 32.004200 15.398500 +v 0.608400 32.097298 15.237400 +v 0.667700 32.048897 15.141200 +v 0.808200 31.844700 15.152500 +v 0.750100 31.729799 15.350100 +v 0.808200 31.844700 15.152500 +v 2.211900 32.021698 13.388100 +v 2.840300 32.366100 13.802200 +v 3.368900 31.953899 14.735600 +v 2.504600 30.402199 14.670200 +v 3.111900 30.863499 14.991699 +v 3.368900 31.953899 14.735600 +v 22.709099 30.372299 13.662300 +v 22.919100 30.203699 13.902900 +v 23.609299 30.480700 14.112400 +v 23.583300 30.789600 13.875500 +v 23.708900 29.382698 14.877700 +v 23.494301 29.688198 14.105499 +v 23.368099 29.490898 14.141000 +v 23.696499 29.121199 14.795400 +v 23.689100 28.901398 15.557400 +v 23.622601 29.071699 15.840500 +v 22.635601 29.604099 13.901500 +v 22.425499 29.798399 13.629100 +v 19.398701 31.040199 12.398600 +v 20.334801 30.517200 13.750401 +v 21.133101 31.854698 13.881300 +v 20.754799 32.589897 12.683400 +v 21.967300 31.042198 13.960400 +v 21.493099 30.130899 13.897300 +v 19.766500 31.386999 11.035200 +v 21.130800 32.814400 11.190400 +v 20.324501 31.252598 10.434700 +v 21.611000 32.643299 10.742100 +v 22.788101 28.788998 13.886000 +v 23.433599 29.142799 14.162000 +v 22.530800 28.687199 13.652700 +v 22.530800 28.687199 13.652700 +v 22.425499 29.798399 13.629100 +v 21.498800 28.770500 13.643700 +v 20.216301 28.837799 13.434900 +v 19.321600 29.223099 12.281500 +v 19.446400 29.710199 11.019199 +v 19.750700 30.130398 10.353100 +v 24.008099 29.097900 15.625700 +v 23.988600 29.299299 14.863899 +v 23.762800 29.750198 14.174800 +v 24.684299 29.994198 14.176000 +v 24.087700 29.557800 14.264400 +v 24.820900 30.185099 13.869699 +v 22.972801 32.863098 14.197399 +v 23.224701 31.694199 14.198700 +v 22.691200 33.726398 13.183500 +v 22.855499 33.947701 11.776800 +v 23.171799 33.408199 11.078800 +v 25.253799 32.691498 14.339600 +v 24.945400 31.283499 14.188900 +v 25.323900 33.461601 13.437300 +v 25.340801 33.887699 12.134600 +v 25.098301 33.529400 11.372400 +v 26.885399 31.535400 14.065599 +v 25.725300 30.744198 14.057400 +v 27.332600 32.159100 13.067101 +v 27.243401 32.486198 11.847700 +v 26.810699 32.401001 11.184999 +v 20.666599 27.596100 13.072900 +v 21.728100 28.184099 13.507799 +v 19.921000 27.464899 11.882100 +v 20.009600 27.952000 10.542300 +v 20.313200 28.366999 9.890900 +v 23.171799 33.408199 11.078800 +v 25.098301 33.529400 11.372400 +v 26.810699 32.401001 11.184999 +v 27.158699 30.542900 10.357100 +v 26.197300 29.035400 9.559000 +v 24.807301 28.020798 9.143000 +v 22.990499 27.405199 9.082200 +v 21.757900 27.245899 9.307100 +v 20.313200 28.366999 9.890900 +v 19.750700 30.130398 10.353100 +v 20.324501 31.252598 10.434700 +v 21.611000 32.643299 10.742100 +v 24.074699 28.376499 13.027300 +v 24.850399 28.937799 13.286200 +v 24.657200 28.949099 13.625099 +v 24.079100 28.508198 13.363500 +v 23.914000 29.003799 14.754601 +v 23.696499 29.121199 14.795400 +v 23.704800 28.998798 13.984500 +v 23.917900 29.069099 13.908600 +v 23.910599 28.650799 15.706600 +v 23.689100 28.901398 15.557400 +v 23.441601 28.426098 13.526700 +v 23.450600 28.300098 13.152400 +v 23.019100 25.854698 10.729100 +v 24.959900 26.539099 10.512800 +v 24.792301 26.506798 12.179500 +v 23.267700 26.230700 12.386400 +v 24.442299 27.424999 12.809400 +v 23.438900 27.287098 12.992399 +v 23.049900 26.643499 9.381200 +v 24.887901 27.378599 9.316000 +v 22.990499 27.405199 9.082200 +v 24.807301 28.020798 9.143000 +v 23.433599 29.142799 14.162000 +v 22.788101 28.788998 13.886000 +v 22.530800 28.687199 13.652700 +v 22.530800 28.687199 13.652700 +v 22.184601 27.768099 13.324800 +v 23.450600 28.300098 13.152400 +v 21.668100 26.716099 12.759800 +v 21.308599 26.310099 11.281700 +v 21.454300 26.830898 9.958500 +v 21.757900 27.245899 9.307100 +v 23.988600 29.299299 14.863899 +v 24.008099 29.097900 15.625700 +v 24.099499 29.258099 14.018300 +v 24.684299 29.994198 14.176000 +v 24.087700 29.557800 14.264400 +v 24.820900 30.185099 13.869699 +v 26.501400 27.706100 12.556400 +v 25.584200 28.245899 13.101400 +v 26.920300 27.748899 11.018400 +v 26.734501 28.333698 9.845700 +v 26.197300 29.035400 9.559000 +v 27.311300 29.684399 13.382700 +v 25.917400 29.862900 13.736799 +v 27.829601 30.009598 12.108600 +v 27.784000 30.372099 10.871300 +v 27.158699 30.542900 10.357100 +v 26.885399 31.535400 14.065599 +v 25.725300 30.744198 14.057400 +v 27.332600 32.159100 13.067101 +v 27.243401 32.486198 11.847700 +v 26.810699 32.401001 11.184999 +v 21.728100 28.184099 13.507799 +v 20.666599 27.596100 13.072900 +v 19.921000 27.464899 11.882100 +v 20.009600 27.952000 10.542300 +v 20.313200 28.366999 9.890900 +v 24.746799 30.004599 16.266100 +v 24.858900 29.884699 16.552999 +v 25.018700 29.535799 16.615799 +v 24.733601 29.790398 15.780399 +v 25.045900 29.150398 16.335199 +v 25.004799 29.046898 15.958500 +v 24.899101 29.270798 15.718900 +v 25.004799 29.046898 15.958500 +v 24.899101 29.270798 15.718900 +v 22.968100 28.104500 15.754299 +v 22.985300 28.118099 15.549999 +v 23.926100 28.644699 15.957600 +v 25.045900 29.150398 16.335199 +v 25.018700 29.535799 16.615799 +v 23.821100 28.820698 16.094601 +v 22.888100 28.249298 15.884199 +v 24.746799 30.004599 16.266100 +v 24.733601 29.790398 15.780399 +v 22.729099 28.471399 15.718200 +v 22.873699 28.368099 15.479600 +v 22.750599 28.422499 15.855400 +v 23.693199 28.985100 16.065800 +v 24.858900 29.884699 16.552999 +v 24.733601 29.790398 15.780399 +v 22.968100 28.104500 15.754299 +v 22.888100 28.249298 15.884199 +v 22.750599 28.422499 15.855400 +v 22.729099 28.471399 15.718200 +v 22.873699 28.368099 15.479600 +v 22.985300 28.118099 15.549999 +v 22.873699 28.368099 15.479600 +v 22.709099 30.372299 13.662300 +v 23.583300 30.789600 13.875500 +v 24.820900 30.185099 13.869699 +v 24.074699 28.376499 13.027300 +v 24.850399 28.937799 13.286200 +v 24.820900 30.185099 13.869699 +vt 0.043100 0.297100 +vt 0.068900 0.343700 +vt 0.053500 0.296000 +vt 0.041900 0.267600 +vt 0.029400 0.294700 +vt 0.035900 0.309800 +vt 0.451600 0.351400 +vt 0.452500 0.355900 +vt 0.434800 0.353900 +vt 0.450800 0.360800 +vt 0.447000 0.365200 +vt 0.442000 0.367100 +vt 0.045700 0.639300 +vt 0.045700 0.598400 +vt 0.007900 0.598000 +vt 0.007900 0.598000 +vt 0.007900 0.636900 +vt 0.045700 0.639300 +vt 0.045700 0.598400 +vt 0.045700 0.548700 +vt 0.007900 0.550800 +vt 0.007900 0.550800 +vt 0.007900 0.598000 +vt 0.045700 0.598400 +vt 0.045700 0.548700 +vt 0.045700 0.524700 +vt 0.007900 0.527900 +vt 0.007900 0.527900 +vt 0.007900 0.550800 +vt 0.045700 0.548700 +vt 0.007900 0.925700 +vt 0.007900 0.952800 +vt 0.045700 0.948500 +vt 0.045700 0.948500 +vt 0.045700 0.920900 +vt 0.007900 0.925700 +vt 0.045700 0.948500 +vt 0.007900 0.952800 +vt 0.007900 0.976500 +vt 0.007900 0.976500 +vt 0.045700 0.973200 +vt 0.045700 0.948500 +vt 0.045700 0.973200 +vt 0.007900 0.976500 +vt 0.007900 0.994000 +vt 0.007900 0.994000 +vt 0.045700 0.991400 +vt 0.045700 0.973200 +vt 0.714800 0.719300 +vt 0.691900 0.773500 +vt 0.695500 0.727600 +vt 0.692000 0.697600 +vt 0.714800 0.719300 +vt 0.695500 0.727600 +vt 0.695500 0.727600 +vt 0.685600 0.719100 +vt 0.692000 0.697600 +vt 0.714800 0.719300 +vt 0.691900 0.773500 +vt 0.695500 0.727600 +vt 0.692000 0.697600 +vt 0.714800 0.719300 +vt 0.695500 0.727600 +vt 0.695500 0.727600 +vt 0.685600 0.719100 +vt 0.692000 0.697600 +vt 0.015700 0.514700 +vt 0.007500 0.514400 +vt 0.014500 0.333200 +vt 0.022700 0.333500 +vt 0.040400 0.515700 +vt 0.032100 0.515300 +vt 0.039200 0.334100 +vt 0.047500 0.334500 +vt 0.032100 0.515300 +vt 0.024000 0.515000 +vt 0.031000 0.333800 +vt 0.039200 0.334100 +vt 0.024000 0.515000 +vt 0.015700 0.514700 +vt 0.022700 0.333500 +vt 0.031000 0.333800 +vt 0.353500 0.958200 +vt 0.362600 0.974000 +vt 0.357000 0.979300 +vt 0.346100 0.960100 +vt 0.378200 0.983400 +vt 0.375800 0.990800 +vt 0.395800 0.984100 +vt 0.397700 0.991600 +vt 0.410600 0.974700 +vt 0.416400 0.980000 +vt 0.419500 0.958900 +vt 0.426900 0.960700 +vt 0.419400 0.940600 +vt 0.426900 0.938700 +vt 0.410300 0.924800 +vt 0.415900 0.919500 +vt 0.394700 0.915400 +vt 0.397100 0.908000 +vt 0.377200 0.914700 +vt 0.375200 0.907200 +vt 0.362400 0.924100 +vt 0.356600 0.918800 +vt 0.353500 0.939900 +vt 0.346000 0.938100 +vt 0.571100 0.062900 +vt 0.551700 0.044200 +vt 0.570800 0.024600 +vt 0.590100 0.043300 +vt 0.265700 0.945000 +vt 0.253800 0.948900 +vt 0.249100 0.936600 +vt 0.265700 0.945000 +vt 0.255400 0.958300 +vt 0.253800 0.948900 +vt 0.337400 0.933000 +vt 0.320200 0.902500 +vt 0.334900 0.907200 +vt 0.334900 0.907200 +vt 0.340000 0.912900 +vt 0.337400 0.933000 +vt 0.348700 0.903500 +vt 0.351300 0.906100 +vt 0.340000 0.912900 +vt 0.340000 0.912900 +vt 0.334900 0.907200 +vt 0.348700 0.903500 +vt 0.361900 0.899600 +vt 0.375200 0.896100 +vt 0.376100 0.897400 +vt 0.376100 0.897400 +vt 0.363700 0.901700 +vt 0.361900 0.899600 +vt 0.361900 0.899600 +vt 0.363700 0.901700 +vt 0.351300 0.906100 +vt 0.351300 0.906100 +vt 0.348700 0.903500 +vt 0.361900 0.899600 +vt 0.375200 0.896100 +vt 0.388700 0.893100 +vt 0.376100 0.897400 +vt 0.265100 0.944300 +vt 0.249400 0.936400 +vt 0.252500 0.949000 +vt 0.265100 0.944300 +vt 0.252500 0.949000 +vt 0.255300 0.957400 +vt 0.336200 0.932800 +vt 0.339200 0.912500 +vt 0.333800 0.907100 +vt 0.333800 0.907100 +vt 0.319000 0.902200 +vt 0.336200 0.932800 +vt 0.348300 0.902800 +vt 0.333800 0.907100 +vt 0.339200 0.912500 +vt 0.339200 0.912500 +vt 0.350900 0.905400 +vt 0.348300 0.902800 +vt 0.361700 0.898700 +vt 0.363400 0.900800 +vt 0.375700 0.896100 +vt 0.375700 0.896100 +vt 0.374900 0.895100 +vt 0.361700 0.898700 +vt 0.361700 0.898700 +vt 0.348300 0.902800 +vt 0.350900 0.905400 +vt 0.350900 0.905400 +vt 0.363400 0.900800 +vt 0.361700 0.898700 +vt 0.374900 0.895100 +vt 0.375700 0.896100 +vt 0.388300 0.892100 +vt 0.218300 0.937200 +vt 0.205400 0.949500 +vt 0.205400 0.949500 +vt 0.205400 0.949500 +vt 0.218300 0.937200 +vt 0.218300 0.937200 +vt 0.205400 0.949500 +vt 0.175800 0.968500 +vt 0.205400 0.949500 +vt 0.276800 0.908300 +vt 0.241400 0.914900 +vt 0.241400 0.914900 +vt 0.241400 0.914900 +vt 0.276800 0.908300 +vt 0.276800 0.908300 +vt 0.300400 0.900000 +vt 0.276800 0.908300 +vt 0.276800 0.908300 +vt 0.276800 0.908300 +vt 0.300400 0.900000 +vt 0.300400 0.900000 +vt 0.225700 0.929000 +vt 0.218300 0.937200 +vt 0.218300 0.937200 +vt 0.218300 0.937200 +vt 0.225700 0.929000 +vt 0.225700 0.929000 +vt 0.334900 0.907200 +vt 0.320200 0.902500 +vt 0.320200 0.902500 +vt 0.320200 0.902500 +vt 0.334900 0.907200 +vt 0.334900 0.907200 +vt 0.348700 0.903500 +vt 0.334900 0.907200 +vt 0.334900 0.907200 +vt 0.334900 0.907200 +vt 0.348700 0.903500 +vt 0.348700 0.903500 +vt 0.375200 0.896100 +vt 0.361900 0.899600 +vt 0.361900 0.899600 +vt 0.361900 0.899600 +vt 0.375200 0.896100 +vt 0.375200 0.896100 +vt 0.361900 0.899600 +vt 0.348700 0.903500 +vt 0.348700 0.903500 +vt 0.348700 0.903500 +vt 0.361900 0.899600 +vt 0.361900 0.899600 +vt 0.375200 0.896100 +vt 0.375200 0.896100 +vt 0.388700 0.893100 +vt 0.227400 0.916200 +vt 0.227400 0.916200 +vt 0.241400 0.914900 +vt 0.227200 0.915700 +vt 0.241000 0.914800 +vt 0.227200 0.915700 +vt 0.311000 0.890300 +vt 0.300400 0.900000 +vt 0.311000 0.890300 +vt 0.310600 0.889700 +vt 0.310600 0.889700 +vt 0.300500 0.899800 +vt 0.241400 0.914900 +vt 0.227400 0.916200 +vt 0.227400 0.916200 +vt 0.227400 0.916200 +vt 0.241400 0.914900 +vt 0.241400 0.914900 +vt 0.311000 0.890300 +vt 0.300400 0.900000 +vt 0.300400 0.900000 +vt 0.300400 0.900000 +vt 0.311000 0.890300 +vt 0.311000 0.890300 +vt 0.362400 0.924100 +vt 0.377200 0.914700 +vt 0.395800 0.984100 +vt 0.378200 0.983400 +vt 0.362600 0.974000 +vt 0.353500 0.958200 +vt 0.353500 0.939900 +vt 0.134700 0.947700 +vt 0.128700 0.968400 +vt 0.113300 0.983300 +vt 0.073500 0.910400 +vt 0.094300 0.905200 +vt 0.114800 0.911300 +vt 0.129600 0.926800 +vt 0.092500 0.988400 +vt 0.072000 0.982400 +vt 0.057200 0.966800 +vt 0.052100 0.945900 +vt 0.058100 0.925300 +vt 0.394700 0.915400 +vt 0.410300 0.924800 +vt 0.419400 0.940600 +vt 0.419500 0.958900 +vt 0.410600 0.974700 +vt 0.641700 0.725800 +vt 0.663600 0.733800 +vt 0.660200 0.772700 +vt 0.685600 0.719100 +vt 0.670700 0.722400 +vt 0.659000 0.702100 +vt 0.692000 0.697600 +vt 0.704300 0.680400 +vt 0.645000 0.679800 +vt 0.681900 0.626600 +vt 0.653400 0.632100 +vt 0.377900 0.861800 +vt 0.352100 0.895000 +vt 0.357200 0.858200 +vt 0.324700 0.860400 +vt 0.360200 0.836400 +vt 0.375300 0.811800 +vt 0.394300 0.818300 +vt 0.396100 0.774800 +vt 0.373300 0.783400 +vt 0.691900 0.773500 +vt 0.695500 0.727600 +vt 0.360200 0.836400 +vt 0.324700 0.860400 +vt 0.357200 0.858200 +vt 0.375300 0.811800 +vt 0.352100 0.895000 +vt 0.377900 0.861800 +vt 0.394300 0.818300 +vt 0.396100 0.774800 +vt 0.373300 0.783400 +vt 0.641700 0.725800 +vt 0.663600 0.733800 +vt 0.660200 0.772700 +vt 0.685600 0.719100 +vt 0.670700 0.722400 +vt 0.659000 0.702100 +vt 0.692000 0.697600 +vt 0.704300 0.680400 +vt 0.645000 0.679800 +vt 0.681900 0.626600 +vt 0.653400 0.632100 +vt 0.691900 0.773500 +vt 0.695500 0.727600 +vt 0.218300 0.937200 +vt 0.235300 0.956600 +vt 0.205400 0.949500 +vt 0.203600 0.978000 +vt 0.175800 0.968500 +vt 0.222600 0.983900 +vt 0.249100 0.936600 +vt 0.253800 0.948900 +vt 0.255400 0.958300 +vt 0.249800 0.989900 +vt 0.291700 0.939000 +vt 0.265700 0.945000 +vt 0.241400 0.914900 +vt 0.276800 0.908300 +vt 0.289800 0.963900 +vt 0.227400 0.916200 +vt 0.306000 0.908500 +vt 0.300400 0.900000 +vt 0.312800 0.954800 +vt 0.313600 0.935200 +vt 0.225700 0.929000 +vt 0.311000 0.890300 +vt 0.337400 0.933000 +vt 0.331800 0.954800 +vt 0.318900 0.972000 +vt 0.291700 0.986600 +vt 0.320200 0.902500 +vt 0.218500 0.936800 +vt 0.205700 0.948800 +vt 0.235600 0.955900 +vt 0.203900 0.977200 +vt 0.176100 0.967700 +vt 0.222900 0.983000 +vt 0.255300 0.957400 +vt 0.252500 0.949000 +vt 0.249400 0.936400 +vt 0.249900 0.988800 +vt 0.265100 0.944300 +vt 0.291500 0.938800 +vt 0.241000 0.914800 +vt 0.276700 0.908200 +vt 0.289600 0.963200 +vt 0.227200 0.915700 +vt 0.305300 0.908100 +vt 0.300500 0.899800 +vt 0.311900 0.954300 +vt 0.312800 0.935000 +vt 0.226000 0.928200 +vt 0.310600 0.889700 +vt 0.330900 0.954200 +vt 0.336200 0.932800 +vt 0.318100 0.971000 +vt 0.291400 0.985300 +vt 0.319000 0.902200 +vt 0.887500 0.420900 +vt 0.871200 0.431900 +vt 0.869000 0.405800 +vt 0.882100 0.394200 +vt 0.847600 0.431900 +vt 0.852200 0.392500 +vt 0.830600 0.415400 +vt 0.829700 0.392600 +vt 0.845000 0.378000 +vt 0.867800 0.377300 +vt 0.911200 0.406700 +vt 0.893800 0.433500 +vt 0.918300 0.442900 +vt 0.904500 0.440900 +vt 0.939900 0.407900 +vt 0.938500 0.385800 +vt 0.954500 0.391300 +vt 0.957400 0.419500 +vt 0.972100 0.387300 +vt 0.976800 0.419800 +vt 0.996400 0.409700 +vt 0.992200 0.387600 +vt 0.976800 0.370000 +vt 0.952400 0.370900 +vt 0.938100 0.426700 +vt 0.930700 0.435800 +vt 0.891400 0.266400 +vt 0.888600 0.246800 +vt 0.907100 0.246100 +vt 0.906700 0.265400 +vt 0.852200 0.269900 +vt 0.849400 0.260400 +vt 0.869500 0.248000 +vt 0.872600 0.269300 +vt 0.850200 0.244800 +vt 0.861400 0.233800 +vt 0.876400 0.233500 +vt 0.871100 0.288600 +vt 0.844600 0.284800 +vt 0.926700 0.245400 +vt 0.947700 0.252400 +vt 0.946900 0.264000 +vt 0.926600 0.266200 +vt 0.914800 0.233300 +vt 0.928900 0.229300 +vt 0.941600 0.237000 +vt 0.955900 0.271800 +vt 0.931900 0.287000 +vt 0.888600 0.288500 +vt 0.847000 0.297200 +vt 0.857500 0.310000 +vt 0.959000 0.285500 +vt 0.946700 0.307400 +vt 0.916400 0.306300 +vt 0.887300 0.308800 +vt 0.913000 0.285600 +vt 0.884800 0.324000 +vt 0.892900 0.344700 +vt 0.921700 0.321800 +vt 0.916600 0.345600 +vt 0.905200 0.369500 +vt 0.935800 0.349900 +vt 0.931600 0.366500 +vt 0.888300 0.383700 +vt 0.908700 0.383800 +vt 0.928000 0.377200 +vt 0.881200 0.375600 +vt 0.873200 0.349600 +vt 0.871400 0.326100 +vt 0.938800 0.323900 +vt 0.854900 0.351200 +vt 0.860900 0.338000 +vt 0.844700 0.364600 +vt 0.849400 0.332600 +vt 0.839900 0.335100 +vt 0.836200 0.343900 +vt 0.956600 0.349400 +vt 0.947300 0.336600 +vt 0.972200 0.357900 +vt 0.959100 0.330100 +vt 0.969500 0.331600 +vt 0.974600 0.340500 +vt 0.794500 0.441000 +vt 0.781500 0.441200 +vt 0.783800 0.419400 +vt 0.804900 0.432900 +vt 0.811400 0.419500 +vt 0.814700 0.410400 +vt 0.796900 0.389100 +vt 0.823000 0.400800 +vt 0.826500 0.386900 +vt 0.819000 0.363300 +vt 0.693800 0.386200 +vt 0.699400 0.370200 +vt 0.722600 0.391300 +vt 0.695200 0.399500 +vt 0.705700 0.414300 +vt 0.708900 0.422300 +vt 0.735900 0.421600 +vt 0.714000 0.435500 +vt 0.725400 0.443900 +vt 0.738800 0.444700 +vt 0.752600 0.435800 +vt 0.769600 0.434900 +vt 0.804700 0.340800 +vt 0.801100 0.359500 +vt 0.776700 0.358700 +vt 0.781500 0.329900 +vt 0.719200 0.362800 +vt 0.718900 0.348100 +vt 0.735500 0.333600 +vt 0.742500 0.360200 +vt 0.708400 0.344200 +vt 0.711000 0.327100 +vt 0.706000 0.300200 +vt 0.730900 0.290900 +vt 0.745200 0.301400 +vt 0.700400 0.335900 +vt 0.696000 0.327800 +vt 0.694600 0.318900 +vt 0.814100 0.329000 +vt 0.803600 0.315500 +vt 0.771400 0.290600 +vt 0.797900 0.288700 +vt 0.771800 0.305000 +vt 0.817800 0.318100 +vt 0.818500 0.309000 +vt 0.816100 0.300500 +vt 0.752400 0.310700 +vt 0.874500 0.018700 +vt 0.894400 0.057900 +vt 0.915200 0.044900 +vt 0.897500 0.005500 +vt 0.912100 0.095300 +vt 0.931400 0.082800 +vt 0.925300 0.129300 +vt 0.944600 0.117700 +vt 0.956000 0.151300 +vt 0.936500 0.162100 +vt 0.947800 0.196300 +vt 0.966700 0.185900 +vt 0.958000 0.231900 +vt 0.976400 0.223800 +vt 0.966100 0.269600 +vt 0.985200 0.268100 +vt 0.994100 0.315200 +vt 0.973700 0.310000 +vt 0.045700 0.639300 +vt 0.007900 0.636900 +vt 0.007900 0.682800 +vt 0.045700 0.681300 +vt 0.045700 0.717800 +vt 0.007900 0.721200 +vt 0.007900 0.759800 +vt 0.045700 0.756000 +vt 0.007900 0.797000 +vt 0.045700 0.793900 +vt 0.007900 0.832900 +vt 0.045700 0.830100 +vt 0.045700 0.861900 +vt 0.007900 0.863100 +vt 0.007900 0.895800 +vt 0.045700 0.891100 +vt 0.007900 0.925700 +vt 0.045700 0.920900 +vt 0.386000 0.648200 +vt 0.387600 0.720500 +vt 0.407200 0.720300 +vt 0.423900 0.650000 +vt 0.436200 0.594500 +vt 0.390700 0.602700 +vt 0.395500 0.760600 +vt 0.400800 0.512200 +vt 0.382200 0.471600 +vt 0.377100 0.502500 +vt 0.358600 0.547200 +vt 0.385900 0.558100 +vt 0.500400 0.538500 +vt 0.454300 0.509400 +vt 0.441400 0.549800 +vt 0.447900 0.555800 +vt 0.419500 0.491700 +vt 0.416200 0.446200 +vt 0.358300 0.582200 +vt 0.383900 0.586400 +vt 0.423800 0.582100 +vt 0.456500 0.472700 +vt 0.091100 0.029800 +vt 0.086900 0.022500 +vt 0.057000 0.051600 +vt 0.067300 0.065600 +vt 0.084400 0.072700 +vt 0.100300 0.033200 +vt 0.041500 0.071200 +vt 0.046800 0.093300 +vt 0.069100 0.102200 +vt 0.106800 0.009700 +vt 0.048900 0.120000 +vt 0.042100 0.141500 +vt 0.076500 0.124700 +vt 0.021400 0.168000 +vt 0.029300 0.178000 +vt 0.066600 0.154100 +vt 0.007100 0.174000 +vt 0.013400 0.200200 +vt 0.028300 0.085800 +vt 0.030500 0.067900 +vt 0.025400 0.094500 +vt 0.018400 0.110900 +vt 0.013800 0.127100 +vt 0.364800 0.607600 +vt 0.284600 0.038900 +vt 0.304300 0.048400 +vt 0.288900 0.062500 +vt 0.270500 0.050900 +vt 0.282700 0.079400 +vt 0.280700 0.090900 +vt 0.264900 0.087500 +vt 0.265700 0.071300 +vt 0.251500 0.088800 +vt 0.248300 0.071200 +vt 0.249400 0.052500 +vt 0.218100 0.079800 +vt 0.223600 0.095400 +vt 0.186200 0.067600 +vt 0.187100 0.085500 +vt 0.159100 0.079700 +vt 0.163400 0.065300 +vt 0.192500 0.103300 +vt 0.157900 0.100500 +vt 0.216100 0.062400 +vt 0.125600 0.077900 +vt 0.125200 0.048800 +vt 0.140500 0.057700 +vt 0.132600 0.083200 +vt 0.300200 0.022300 +vt 0.316600 0.041100 +vt 0.262600 0.037700 +vt 0.243400 0.037000 +vt 0.275900 0.027800 +vt 0.212600 0.045100 +vt 0.240200 0.008300 +vt 0.252900 0.022700 +vt 0.185700 0.049600 +vt 0.204600 0.022800 +vt 0.157200 0.049900 +vt 0.178600 0.022000 +vt 0.158900 0.035200 +vt 0.148000 0.041600 +vt 0.136600 0.090000 +vt 0.211100 0.233400 +vt 0.184300 0.229000 +vt 0.211800 0.255900 +vt 0.159800 0.217400 +vt 0.173500 0.200500 +vt 0.186300 0.257000 +vt 0.139700 0.266000 +vt 0.113300 0.314400 +vt 0.117500 0.315300 +vt 0.145800 0.271300 +vt 0.105200 0.308500 +vt 0.130500 0.259000 +vt 0.250000 0.178400 +vt 0.223900 0.146300 +vt 0.264200 0.204800 +vt 0.505100 0.560100 +vt 0.477800 0.552200 +vt 0.452600 0.576000 +vt 0.503200 0.584400 +vt 0.542100 0.587100 +vt 0.544900 0.609300 +vt 0.630200 0.623800 +vt 0.633000 0.614400 +vt 0.502400 0.607800 +vt 0.448600 0.609000 +vt 0.533900 0.608100 +vt 0.531700 0.566000 +vt 0.629200 0.607000 +vt 0.546300 0.571300 +vt 0.096000 0.328500 +vt 0.107400 0.328200 +vt 0.110300 0.334100 +vt 0.117200 0.324000 +vt 0.093300 0.333500 +vt 0.112900 0.363400 +vt 0.127400 0.375600 +vt 0.154900 0.370000 +vt 0.155000 0.347200 +vt 0.642300 0.626300 +vt 0.641900 0.616900 +vt 0.647300 0.611600 +vt 0.651700 0.628900 +vt 0.134100 0.359400 +vt 0.134400 0.358800 +vt 0.134400 0.358800 +vt 0.134100 0.359400 +vt 0.143900 0.197200 +vt 0.135300 0.171800 +vt 0.113700 0.176600 +vt 0.119800 0.204200 +vt 0.121800 0.253600 +vt 0.114100 0.252300 +vt 0.093300 0.304300 +vt 0.097700 0.307100 +vt 0.197700 0.114200 +vt 0.159700 0.114600 +vt 0.503900 0.654500 +vt 0.501700 0.630500 +vt 0.452300 0.641800 +vt 0.478300 0.664700 +vt 0.538300 0.627400 +vt 0.629400 0.636000 +vt 0.528100 0.646900 +vt 0.541700 0.644600 +vt 0.626300 0.641400 +vt 0.089600 0.320200 +vt 0.082400 0.321100 +vt 0.086000 0.310500 +vt 0.139800 0.330500 +vt 0.115500 0.344500 +vt 0.637900 0.636600 +vt 0.640100 0.643700 +vt 0.134400 0.358800 +vt 0.134400 0.358800 +vt 0.267500 0.007200 +vt 0.266100 0.014000 +vt 0.146100 0.029100 +vt 0.140800 0.019400 +vt 0.122200 0.128900 +vt 0.115300 0.125700 +vt 0.017600 0.146800 +vt 0.012500 0.160500 +vt 0.004600 0.171200 +vt 0.019100 0.136600 +vt 0.386000 0.648200 +vt 0.423900 0.650000 +vt 0.407200 0.720300 +vt 0.387600 0.720500 +vt 0.436200 0.594500 +vt 0.390700 0.602700 +vt 0.395500 0.760600 +vt 0.400800 0.512200 +vt 0.385900 0.558100 +vt 0.500400 0.538500 +vt 0.447900 0.555800 +vt 0.441400 0.549800 +vt 0.454300 0.509400 +vt 0.419500 0.491700 +vt 0.383900 0.586400 +vt 0.423800 0.582100 +vt 0.456500 0.472700 +vt 0.091100 0.029800 +vt 0.067300 0.065600 +vt 0.057000 0.051600 +vt 0.086900 0.022500 +vt 0.100300 0.033200 +vt 0.084400 0.072700 +vt 0.046800 0.093300 +vt 0.041500 0.071200 +vt 0.069100 0.102200 +vt 0.106800 0.009700 +vt 0.042100 0.141500 +vt 0.048900 0.120000 +vt 0.076500 0.124700 +vt 0.021400 0.168000 +vt 0.066600 0.154100 +vt 0.029300 0.178000 +vt 0.284600 0.038900 +vt 0.270500 0.050900 +vt 0.288900 0.062500 +vt 0.304300 0.048400 +vt 0.282700 0.079400 +vt 0.265700 0.071300 +vt 0.248300 0.071200 +vt 0.249400 0.052500 +vt 0.218100 0.079800 +vt 0.186200 0.067600 +vt 0.163400 0.065300 +vt 0.159100 0.079700 +vt 0.187100 0.085500 +vt 0.216100 0.062400 +vt 0.132600 0.083200 +vt 0.140500 0.057700 +vt 0.262600 0.037700 +vt 0.243400 0.037000 +vt 0.275900 0.027800 +vt 0.212600 0.045100 +vt 0.252900 0.022700 +vt 0.185700 0.049600 +vt 0.157200 0.049900 +vt 0.158900 0.035200 +vt 0.148000 0.041600 +vt 0.184300 0.229000 +vt 0.211100 0.233400 +vt 0.211800 0.255900 +vt 0.159800 0.217400 +vt 0.173500 0.200500 +vt 0.186300 0.257000 +vt 0.139700 0.266000 +vt 0.145800 0.271300 +vt 0.117500 0.315300 +vt 0.113300 0.314400 +vt 0.105200 0.308500 +vt 0.130500 0.259000 +vt 0.250000 0.178400 +vt 0.223900 0.146300 +vt 0.264200 0.204800 +vt 0.505100 0.560100 +vt 0.503200 0.584400 +vt 0.452600 0.576000 +vt 0.477800 0.552200 +vt 0.542100 0.587100 +vt 0.633000 0.614400 +vt 0.630200 0.623800 +vt 0.544900 0.609300 +vt 0.502400 0.607800 +vt 0.448600 0.609000 +vt 0.533900 0.608100 +vt 0.531700 0.566000 +vt 0.546300 0.571300 +vt 0.629200 0.607000 +vt 0.096000 0.328500 +vt 0.107400 0.328200 +vt 0.110300 0.334100 +vt 0.117200 0.324000 +vt 0.093300 0.333500 +vt 0.112900 0.363400 +vt 0.155000 0.347200 +vt 0.154900 0.370000 +vt 0.127400 0.375600 +vt 0.642300 0.626300 +vt 0.641900 0.616900 +vt 0.647300 0.611600 +vt 0.651700 0.628900 +vt 0.134100 0.359400 +vt 0.134100 0.359400 +vt 0.134400 0.358800 +vt 0.134400 0.358800 +vt 0.135300 0.171800 +vt 0.143900 0.197200 +vt 0.113700 0.176600 +vt 0.119800 0.204200 +vt 0.121800 0.253600 +vt 0.097700 0.307100 +vt 0.093300 0.304300 +vt 0.114100 0.252300 +vt 0.197700 0.114200 +vt 0.159700 0.114600 +vt 0.503900 0.654500 +vt 0.478300 0.664700 +vt 0.452300 0.641800 +vt 0.501700 0.630500 +vt 0.538300 0.627400 +vt 0.629400 0.636000 +vt 0.528100 0.646900 +vt 0.626300 0.641400 +vt 0.541700 0.644600 +vt 0.089600 0.320200 +vt 0.082400 0.321100 +vt 0.086000 0.310500 +vt 0.115500 0.344500 +vt 0.139800 0.330500 +vt 0.637900 0.636600 +vt 0.640100 0.643700 +vt 0.134400 0.358800 +vt 0.134400 0.358800 +vt 0.266100 0.014000 +vt 0.146100 0.029100 +vt 0.098500 0.176500 +vt 0.103100 0.197500 +vt 0.115300 0.125700 +vt 0.076600 0.158900 +vt 0.082000 0.175500 +vt 0.054400 0.220700 +vt 0.063500 0.239500 +vt 0.011800 0.218000 +vt 0.031300 0.237900 +vt 0.021300 0.266800 +vt 0.041900 0.267600 +vt 0.029400 0.294700 +vt 0.043100 0.297100 +vt 0.021900 0.300100 +vt 0.012500 0.160500 +vt 0.017600 0.146800 +vt 0.019100 0.136600 +vt 0.185500 0.319700 +vt 0.202600 0.285700 +vt 0.207100 0.303600 +vt 0.192800 0.326300 +vt 0.178700 0.321000 +vt 0.173700 0.378600 +vt 0.184800 0.302200 +vt 0.230800 0.266900 +vt 0.224300 0.289500 +vt 0.191600 0.283100 +vt 0.202500 0.266500 +vt 0.251700 0.250700 +vt 0.263900 0.270200 +vt 0.221400 0.253200 +vt 0.241300 0.289500 +vt 0.266100 0.254800 +vt 0.293100 0.280900 +vt 0.273800 0.303300 +vt 0.292100 0.252100 +vt 0.336700 0.275900 +vt 0.315900 0.310700 +vt 0.307800 0.236800 +vt 0.353300 0.226200 +vt 0.364600 0.299800 +vt 0.340100 0.315200 +vt 0.379000 0.272900 +vt 0.306600 0.197200 +vt 0.401500 0.274300 +vt 0.391700 0.244800 +vt 0.450400 0.140500 +vt 0.440200 0.129100 +vt 0.460000 0.110700 +vt 0.335000 0.184600 +vt 0.366700 0.209100 +vt 0.432000 0.118700 +vt 0.395200 0.141600 +vt 0.365700 0.155600 +vt 0.351200 0.140300 +vt 0.386200 0.130600 +vt 0.436800 0.221300 +vt 0.439900 0.266400 +vt 0.421100 0.270800 +vt 0.414500 0.205400 +vt 0.420300 0.129000 +vt 0.426000 0.142600 +vt 0.440000 0.156700 +vt 0.453700 0.161700 +vt 0.408900 0.158900 +vt 0.392200 0.175400 +vt 0.428500 0.179000 +vt 0.302700 0.170900 +vt 0.303400 0.140300 +vt 0.311700 0.118700 +vt 0.440900 0.190400 +vt 0.412600 0.119600 +vt 0.465000 0.158900 +vt 0.482500 0.087200 +vt 0.522000 0.038800 +vt 0.555700 0.077600 +vt 0.466800 0.209300 +vt 0.469800 0.243400 +vt 0.495100 0.160600 +vt 0.520700 0.138900 +vt 0.529700 0.175400 +vt 0.524500 0.216500 +vt 0.497100 0.212400 +vt 0.485000 0.269200 +vt 0.513200 0.272900 +vt 0.495500 0.239900 +vt 0.519800 0.238000 +vt 0.569600 0.226500 +vt 0.471700 0.187800 +vt 0.578600 0.126000 +vt 0.583800 0.179500 +vt 0.541200 0.260600 +vt 0.411200 0.104400 +vt 0.413800 0.083300 +vt 0.399900 0.045500 +vt 0.435400 0.054100 +vt 0.347800 0.075600 +vt 0.357300 0.052900 +vt 0.375800 0.096000 +vt 0.346600 0.094500 +vt 0.316400 0.087000 +vt 0.316300 0.068300 +vt 0.329500 0.042300 +vt 0.396900 0.004900 +vt 0.441400 0.003100 +vt 0.356700 0.020300 +vt 0.484400 0.015200 +vt 0.675300 0.395900 +vt 0.669000 0.393300 +vt 0.667500 0.366100 +vt 0.678200 0.363700 +vt 0.683000 0.394000 +vt 0.676700 0.448600 +vt 0.690100 0.378000 +vt 0.658900 0.342600 +vt 0.669600 0.334100 +vt 0.691900 0.360000 +vt 0.690200 0.342400 +vt 0.667300 0.297700 +vt 0.685800 0.320500 +vt 0.649300 0.316800 +vt 0.642400 0.327100 +vt 0.655400 0.291000 +vt 0.628400 0.298800 +vt 0.612300 0.302800 +vt 0.649200 0.269600 +vt 0.589200 0.267200 +vt 0.618100 0.257500 +vt 0.671600 0.223700 +vt 0.658900 0.264200 +vt 0.579200 0.248000 +vt 0.587700 0.223800 +vt 0.610100 0.206500 +vt 0.692000 0.267000 +vt 0.601200 0.188100 +vt 0.634600 0.195100 +vt 0.704900 0.248600 +vt 0.598200 0.171700 +vt 0.596600 0.156000 +vt 0.668300 0.168000 +vt 0.710900 0.198000 +vt 0.739700 0.219400 +vt 0.713500 0.278900 +vt 0.742500 0.286600 +vt 0.766300 0.283300 +vt 0.792900 0.103800 +vt 0.787500 0.022200 +vt 0.829900 0.049500 +vt 0.656500 0.099900 +vt 0.717800 0.126000 +vt 0.735300 0.075200 +vt 0.759200 0.145800 +vt 0.697000 0.068200 +vt 0.591800 0.111100 +vt 0.593700 0.084500 +vt 0.736800 0.008100 +vt 0.684900 0.012100 +vt 0.642400 0.031400 +vt 0.608700 0.058800 +vt 0.781100 0.177600 +vt 0.821000 0.147800 +vt 0.826800 0.176700 +vt 0.802100 0.213600 +vt 0.799700 0.272700 +vt 0.820700 0.266400 +vt 0.845100 0.246500 +vt 0.879100 0.170800 +vt 0.876400 0.124300 +vt 0.865200 0.214300 +vt 0.857900 0.083100 +vt 0.515000 0.279100 +vt 0.516500 0.297000 +vt 0.489100 0.318700 +vt 0.477600 0.304500 +vt 0.538600 0.372000 +vt 0.524300 0.335600 +vt 0.539000 0.333100 +vt 0.549800 0.368600 +vt 0.565000 0.400500 +vt 0.563200 0.424600 +vt 0.547000 0.297200 +vt 0.544400 0.279000 +vt 0.865500 0.840900 +vt 0.788900 0.809000 +vt 0.817500 0.749200 +vt 0.888100 0.761100 +vt 0.766000 0.734400 +vt 0.742600 0.783600 +vt 0.937400 0.843300 +vt 0.965300 0.760400 +vt 0.980300 0.842800 +vt 0.995600 0.747700 +vt 0.581800 0.314800 +vt 0.556100 0.339800 +vt 0.592000 0.300900 +vt 0.659500 0.817800 +vt 0.704000 0.765200 +vt 0.699300 0.841200 +vt 0.751800 0.872900 +vt 0.824300 0.900200 +vt 0.899600 0.910800 +vt 0.948800 0.904100 +vt 0.538200 0.410100 +vt 0.529000 0.380100 +vt 0.513800 0.346700 +vt 0.469300 0.369300 +vt 0.507100 0.364600 +vt 0.450400 0.362200 +vt 0.829000 0.661000 +vt 0.773300 0.663600 +vt 0.890800 0.668600 +vt 0.954900 0.667800 +vt 0.990800 0.658300 +vt 0.809500 0.567200 +vt 0.743200 0.585400 +vt 0.866700 0.558900 +vt 0.931900 0.558000 +vt 0.965300 0.564000 +vt 0.767900 0.492500 +vt 0.719300 0.548000 +vt 0.837400 0.457600 +vt 0.916400 0.450300 +vt 0.959900 0.463800 +vt 0.721000 0.924000 +vt 0.677400 0.865300 +vt 0.791100 0.972500 +vt 0.878100 0.991300 +vt 0.928100 0.992600 +vt 0.647900 0.881600 +vt 0.611300 0.956400 +vt 0.531800 0.993900 +vt 0.450100 0.960400 +vt 0.409400 0.887200 +vt 0.400400 0.812000 +vt 0.419700 0.732000 +vt 0.446600 0.684200 +vt 0.525300 0.659400 +vt 0.604200 0.680800 +vt 0.632300 0.727300 +vt 0.654000 0.806400 +vt 0.696700 0.556000 +vt 0.672700 0.576300 +vt 0.665900 0.565900 +vt 0.684600 0.551500 +vt 0.639000 0.520200 +vt 0.645500 0.510600 +vt 0.662700 0.530400 +vt 0.660200 0.539000 +vt 0.603300 0.482400 +vt 0.627800 0.491700 +vt 0.688900 0.532700 +vt 0.701700 0.536900 +vt 0.181600 0.766400 +vt 0.153300 0.686900 +vt 0.234300 0.680300 +vt 0.256100 0.740400 +vt 0.289400 0.670800 +vt 0.306500 0.717800 +vt 0.111100 0.772500 +vt 0.084800 0.689000 +vt 0.078900 0.779200 +vt 0.056700 0.686900 +vt 0.662400 0.518500 +vt 0.684300 0.509100 +vt 0.692400 0.503700 +vt 0.385500 0.753000 +vt 0.342200 0.773100 +vt 0.348600 0.698100 +vt 0.289200 0.801900 +vt 0.218500 0.830800 +vt 0.147900 0.842100 +vt 0.107900 0.837600 +vt 0.630300 0.527600 +vt 0.610200 0.512300 +vt 0.651200 0.545800 +vt 0.628600 0.569100 +vt 0.637800 0.547000 +vt 0.629100 0.581800 +vt 0.231100 0.591100 +vt 0.286400 0.598900 +vt 0.155400 0.590500 +vt 0.095000 0.595300 +vt 0.054300 0.598300 +vt 0.251100 0.496000 +vt 0.315600 0.513600 +vt 0.177500 0.481500 +vt 0.107700 0.487500 +vt 0.066700 0.500800 +vt 0.279000 0.413400 +vt 0.335900 0.469400 +vt 0.197100 0.382400 +vt 0.108700 0.385600 +vt 0.059400 0.404500 +vt 0.362100 0.794300 +vt 0.324300 0.847100 +vt 0.265000 0.897400 +vt 0.184300 0.923900 +vt 0.136400 0.925800 +vt 0.756200 0.499200 +vt 0.739700 0.519100 +vt 0.709400 0.517800 +vt 0.749500 0.458300 +vt 0.685900 0.488900 +vt 0.686000 0.458200 +vt 0.709000 0.444100 +vt 0.543600 0.532800 +vt 0.561300 0.546900 +vt 0.645400 0.425500 +vt 0.654300 0.433700 +vt 0.588400 0.471400 +vt 0.520600 0.521900 +vt 0.493000 0.509600 +vt 0.575400 0.456600 +vt 0.636800 0.417200 +vt 0.474200 0.469800 +vt 0.476500 0.435100 +vt 0.628700 0.396800 +vt 0.619000 0.379900 +vt 0.631900 0.405000 +vt 0.568100 0.441800 +vt 0.478200 0.490400 +vt 0.585100 0.573900 +vt 0.503000 0.390200 +vt 0.499600 0.413000 +vt 0.479700 0.427300 +vt 0.465100 0.420400 +vt 0.459700 0.388200 +vt 0.487300 0.374000 +vt 0.666400 0.447300 +vt 0.722100 0.722900 +vt 0.728300 0.659600 +vt 0.690600 0.592200 +vt 0.335100 0.655900 +vt 0.326500 0.591900 +vt 0.363900 0.515900 +vt 0.404300 0.358700 +vt 0.392300 0.384400 +vt 0.304200 0.347100 +vt 0.335900 0.460000 +vt 0.266000 0.332100 +vt 0.346700 0.418000 +vt 0.371100 0.405700 +vt 0.394100 0.439400 +vt 0.426300 0.405200 +vt 0.425500 0.302500 +vt 0.443400 0.358900 +vt 0.400000 0.334700 +vt 0.515000 0.279100 +vt 0.516500 0.297000 +vt 0.489100 0.318700 +vt 0.477600 0.304500 +vt 0.538600 0.372000 +vt 0.524300 0.335600 +vt 0.539000 0.333100 +vt 0.549800 0.368600 +vt 0.565000 0.400500 +vt 0.563200 0.424600 +vt 0.547000 0.297200 +vt 0.544400 0.279000 +vt 0.865500 0.840900 +vt 0.788900 0.809000 +vt 0.817500 0.749200 +vt 0.888100 0.761100 +vt 0.766000 0.734400 +vt 0.742600 0.783600 +vt 0.937400 0.843300 +vt 0.965300 0.760400 +vt 0.980300 0.842800 +vt 0.995600 0.747700 +vt 0.581800 0.314800 +vt 0.556100 0.339800 +vt 0.592000 0.300900 +vt 0.659500 0.817800 +vt 0.704000 0.765200 +vt 0.699300 0.841200 +vt 0.751800 0.872900 +vt 0.824300 0.900200 +vt 0.899600 0.910800 +vt 0.948800 0.904100 +vt 0.538200 0.410100 +vt 0.529000 0.380100 +vt 0.513800 0.346700 +vt 0.469300 0.369300 +vt 0.507100 0.364600 +vt 0.450400 0.362200 +vt 0.829000 0.661000 +vt 0.773300 0.663600 +vt 0.890800 0.668600 +vt 0.954900 0.667800 +vt 0.990800 0.658300 +vt 0.809500 0.567200 +vt 0.743200 0.585400 +vt 0.866700 0.558900 +vt 0.931900 0.558000 +vt 0.965300 0.564000 +vt 0.767900 0.492500 +vt 0.719300 0.548000 +vt 0.837400 0.457600 +vt 0.916400 0.450300 +vt 0.959900 0.463800 +vt 0.721000 0.924000 +vt 0.677400 0.865300 +vt 0.791100 0.972500 +vt 0.878100 0.991300 +vt 0.928100 0.992600 +vt 0.647900 0.881600 +vt 0.611300 0.956400 +vt 0.531800 0.993900 +vt 0.450100 0.960400 +vt 0.409400 0.887200 +vt 0.400400 0.812000 +vt 0.419700 0.732000 +vt 0.446600 0.684200 +vt 0.525300 0.659400 +vt 0.604200 0.680800 +vt 0.632300 0.727300 +vt 0.654000 0.806400 +vt 0.696700 0.556000 +vt 0.672700 0.576300 +vt 0.665900 0.565900 +vt 0.684600 0.551500 +vt 0.639000 0.520200 +vt 0.645500 0.510600 +vt 0.662700 0.530400 +vt 0.660200 0.539000 +vt 0.603300 0.482400 +vt 0.627800 0.491700 +vt 0.688900 0.532700 +vt 0.701700 0.536900 +vt 0.181600 0.766400 +vt 0.153300 0.686900 +vt 0.234300 0.680300 +vt 0.256100 0.740400 +vt 0.289400 0.670800 +vt 0.306500 0.717800 +vt 0.111100 0.772500 +vt 0.084800 0.689000 +vt 0.078900 0.779200 +vt 0.056700 0.686900 +vt 0.662400 0.518500 +vt 0.684300 0.509100 +vt 0.692400 0.503700 +vt 0.385500 0.753000 +vt 0.342200 0.773100 +vt 0.348600 0.698100 +vt 0.289200 0.801900 +vt 0.218500 0.830800 +vt 0.147900 0.842100 +vt 0.107900 0.837600 +vt 0.630300 0.527600 +vt 0.610200 0.512300 +vt 0.651200 0.545800 +vt 0.628600 0.569100 +vt 0.637800 0.547000 +vt 0.629100 0.581800 +vt 0.231100 0.591100 +vt 0.286400 0.598900 +vt 0.155400 0.590500 +vt 0.095000 0.595300 +vt 0.054300 0.598300 +vt 0.251100 0.496000 +vt 0.315600 0.513600 +vt 0.177500 0.481500 +vt 0.107700 0.487500 +vt 0.066700 0.500800 +vt 0.279000 0.413400 +vt 0.335900 0.469400 +vt 0.197100 0.382400 +vt 0.108700 0.385600 +vt 0.059400 0.404500 +vt 0.362100 0.794300 +vt 0.324300 0.847100 +vt 0.265000 0.897400 +vt 0.184300 0.923900 +vt 0.136400 0.925800 +vt 0.756200 0.499200 +vt 0.739700 0.519100 +vt 0.709400 0.517800 +vt 0.749500 0.458300 +vt 0.685900 0.488900 +vt 0.686000 0.458200 +vt 0.709000 0.444100 +vt 0.543600 0.532800 +vt 0.561300 0.546900 +vt 0.645400 0.425500 +vt 0.654300 0.433700 +vt 0.588400 0.471400 +vt 0.520600 0.521900 +vt 0.493000 0.509600 +vt 0.575400 0.456600 +vt 0.636800 0.417200 +vt 0.474200 0.469800 +vt 0.476500 0.435100 +vt 0.628700 0.396800 +vt 0.619000 0.379900 +vt 0.631900 0.405000 +vt 0.568100 0.441800 +vt 0.478200 0.490400 +vt 0.585100 0.573900 +vt 0.503000 0.390200 +vt 0.499600 0.413000 +vt 0.479700 0.427300 +vt 0.465100 0.420400 +vt 0.459700 0.388200 +vt 0.487300 0.374000 +vt 0.666400 0.447300 +vt 0.722100 0.722900 +vt 0.728300 0.659600 +vt 0.690600 0.592200 +vt 0.335100 0.655900 +vt 0.326500 0.591900 +vt 0.363900 0.515900 +vt 0.515000 0.279100 +vt 0.516500 0.297000 +vt 0.489100 0.318700 +vt 0.477600 0.304500 +vt 0.538600 0.372000 +vt 0.524300 0.335600 +vt 0.539000 0.333100 +vt 0.549800 0.368600 +vt 0.565000 0.400500 +vt 0.563200 0.424600 +vt 0.547000 0.297200 +vt 0.544400 0.279000 +vt 0.865500 0.840900 +vt 0.788900 0.809000 +vt 0.817500 0.749200 +vt 0.888100 0.761100 +vt 0.766000 0.734400 +vt 0.742600 0.783600 +vt 0.937400 0.843300 +vt 0.965300 0.760400 +vt 0.980300 0.842800 +vt 0.995600 0.747700 +vt 0.581800 0.314800 +vt 0.556100 0.339800 +vt 0.592000 0.300900 +vt 0.659500 0.817800 +vt 0.704000 0.765200 +vt 0.699300 0.841200 +vt 0.751800 0.872900 +vt 0.824300 0.900200 +vt 0.899600 0.910800 +vt 0.948800 0.904100 +vt 0.538200 0.410100 +vt 0.529000 0.380100 +vt 0.513800 0.346700 +vt 0.469300 0.369300 +vt 0.507100 0.364600 +vt 0.450400 0.362200 +vt 0.829000 0.661000 +vt 0.773300 0.663600 +vt 0.890800 0.668600 +vt 0.954900 0.667800 +vt 0.990800 0.658300 +vt 0.809500 0.567200 +vt 0.743200 0.585400 +vt 0.866700 0.558900 +vt 0.931900 0.558000 +vt 0.965300 0.564000 +vt 0.767900 0.492500 +vt 0.719300 0.548000 +vt 0.837400 0.457600 +vt 0.916400 0.450300 +vt 0.959900 0.463800 +vt 0.721000 0.924000 +vt 0.677400 0.865300 +vt 0.791100 0.972500 +vt 0.878100 0.991300 +vt 0.928100 0.992600 +vt 0.647900 0.881600 +vt 0.611300 0.956400 +vt 0.531800 0.993900 +vt 0.450100 0.960400 +vt 0.409400 0.887200 +vt 0.400400 0.812000 +vt 0.419700 0.732000 +vt 0.446600 0.684200 +vt 0.525300 0.659400 +vt 0.604200 0.680800 +vt 0.632300 0.727300 +vt 0.654000 0.806400 +vt 0.696700 0.556000 +vt 0.672700 0.576300 +vt 0.665900 0.565900 +vt 0.684600 0.551500 +vt 0.639000 0.520200 +vt 0.645500 0.510600 +vt 0.662700 0.530400 +vt 0.660200 0.539000 +vt 0.603300 0.482400 +vt 0.627800 0.491700 +vt 0.688900 0.532700 +vt 0.701700 0.536900 +vt 0.181600 0.766400 +vt 0.153300 0.686900 +vt 0.234300 0.680300 +vt 0.256100 0.740400 +vt 0.289400 0.670800 +vt 0.306500 0.717800 +vt 0.111100 0.772500 +vt 0.084800 0.689000 +vt 0.078900 0.779200 +vt 0.056700 0.686900 +vt 0.662400 0.518500 +vt 0.684300 0.509100 +vt 0.692400 0.503700 +vt 0.385500 0.753000 +vt 0.342200 0.773100 +vt 0.348600 0.698100 +vt 0.289200 0.801900 +vt 0.218500 0.830800 +vt 0.147900 0.842100 +vt 0.107900 0.837600 +vt 0.630300 0.527600 +vt 0.610200 0.512300 +vt 0.651200 0.545800 +vt 0.628600 0.569100 +vt 0.637800 0.547000 +vt 0.629100 0.581800 +vt 0.231100 0.591100 +vt 0.286400 0.598900 +vt 0.155400 0.590500 +vt 0.095000 0.595300 +vt 0.054300 0.598300 +vt 0.251100 0.496000 +vt 0.315600 0.513600 +vt 0.177500 0.481500 +vt 0.107700 0.487500 +vt 0.066700 0.500800 +vt 0.279000 0.413400 +vt 0.335900 0.469400 +vt 0.197100 0.382400 +vt 0.108700 0.385600 +vt 0.059400 0.404500 +vt 0.362100 0.794300 +vt 0.324300 0.847100 +vt 0.265000 0.897400 +vt 0.184300 0.923900 +vt 0.136400 0.925800 +vt 0.756200 0.499200 +vt 0.739700 0.519100 +vt 0.709400 0.517800 +vt 0.749500 0.458300 +vt 0.685900 0.488900 +vt 0.686000 0.458200 +vt 0.709000 0.444100 +vt 0.543600 0.532800 +vt 0.561300 0.546900 +vt 0.645400 0.425500 +vt 0.654300 0.433700 +vt 0.588400 0.471400 +vt 0.520600 0.521900 +vt 0.493000 0.509600 +vt 0.575400 0.456600 +vt 0.636800 0.417200 +vt 0.474200 0.469800 +vt 0.476500 0.435100 +vt 0.628700 0.396800 +vt 0.619000 0.379900 +vt 0.631900 0.405000 +vt 0.568100 0.441800 +vt 0.478200 0.490400 +vt 0.585100 0.573900 +vt 0.503000 0.390200 +vt 0.499600 0.413000 +vt 0.479700 0.427300 +vt 0.465100 0.420400 +vt 0.459700 0.388200 +vt 0.487300 0.374000 +vt 0.666400 0.447300 +vt 0.722100 0.722900 +vt 0.728300 0.659600 +vt 0.690600 0.592200 +vt 0.335100 0.655900 +vt 0.326500 0.591900 +vt 0.363900 0.515900 +vn 0.597532 -0.786143 -0.157909 +vn 0.538365 -0.813546 -0.219786 +vn 0.604013 -0.781717 -0.155203 +vn 0.664002 -0.742403 -0.089100 +vn 0.587168 -0.793557 -0.159691 +vn 0.587168 -0.793557 -0.159691 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 0.000000 -1.000000 +vn 0.919628 -0.282209 0.273208 +vn 0.919628 -0.282209 0.273208 +vn 0.919628 -0.282209 0.273208 +vn 0.945686 -0.189697 0.263996 +vn 0.945686 -0.189697 0.263996 +vn 0.945686 -0.189697 0.263996 +vn 0.807878 -0.589284 -0.008800 +vn 0.807878 -0.589284 -0.008800 +vn 0.807878 -0.589284 -0.008800 +vn 0.854329 -0.515317 -0.067602 +vn 0.854329 -0.515317 -0.067602 +vn 0.854329 -0.515317 -0.067602 +vn 0.523884 -0.835775 -0.164395 +vn 0.523884 -0.835775 -0.164395 +vn 0.523884 -0.835775 -0.164395 +vn 0.786427 -0.614821 -0.059402 +vn 0.786427 -0.614821 -0.059402 +vn 0.786427 -0.614821 -0.059402 +vn 0.361076 0.334078 0.870641 +vn 0.361076 0.334078 0.870641 +vn 0.361076 0.334078 0.870641 +vn 0.310198 0.360197 0.879793 +vn 0.310198 0.360197 0.879793 +vn 0.310198 0.360197 0.879793 +vn 0.105599 0.051100 0.993095 +vn 0.105599 0.051100 0.993095 +vn 0.105599 0.051100 0.993095 +vn 0.118702 0.020500 0.992718 +vn 0.118702 0.020500 0.992718 +vn 0.118702 0.020500 0.992718 +vn -0.230201 -0.364001 0.902503 +vn -0.230201 -0.364001 0.902503 +vn -0.230201 -0.364001 0.902503 +vn -0.270117 -0.258216 0.927557 +vn -0.270117 -0.258216 0.927557 +vn -0.270117 -0.258216 0.927557 +vn 0.750594 0.418796 -0.511096 +vn 0.750594 0.418796 -0.511096 +vn 0.750594 0.418796 -0.511096 +vn 0.909186 -0.191397 0.369794 +vn 0.909186 -0.191397 0.369794 +vn 0.909186 -0.191397 0.369794 +vn 0.901340 -0.191009 0.388717 +vn 0.901340 -0.191009 0.388717 +vn 0.901340 -0.191009 0.388717 +vn -0.974044 -0.175708 0.142706 +vn -0.974044 -0.175708 0.142706 +vn -0.974044 -0.175708 0.142706 +vn -0.204494 -0.575284 0.791978 +vn -0.204494 -0.575284 0.791978 +vn -0.204494 -0.575284 0.791978 +vn -0.187306 -0.585919 0.788426 +vn -0.187306 -0.585919 0.788426 +vn -0.187306 -0.585919 0.788426 +vn -0.366510 0.920626 -0.134604 +vn -0.366510 0.920626 -0.134604 +vn -0.366510 0.920626 -0.134604 +vn -0.366510 0.920626 -0.134604 +vn 0.267906 0.242906 0.932321 +vn 0.267906 0.242906 0.932321 +vn 0.267906 0.242906 0.932321 +vn 0.267906 0.242906 0.932321 +vn 0.366510 -0.920626 0.134604 +vn 0.366510 -0.920626 0.134604 +vn 0.366510 -0.920626 0.134604 +vn 0.366510 -0.920626 0.134604 +vn -0.267906 -0.242906 -0.932321 +vn -0.267906 -0.242906 -0.932321 +vn -0.267906 -0.242906 -0.932321 +vn -0.267906 -0.242906 -0.932321 +vn 0.890988 0.305696 -0.335695 +vn 0.996459 0.018599 0.081997 +vn 0.996459 0.018599 0.081997 +vn 0.890988 0.305696 -0.335695 +vn 0.834899 -0.273400 0.477699 +vn 0.834899 -0.273400 0.477699 +vn 0.449617 -0.492119 0.745428 +vn 0.449617 -0.492119 0.745428 +vn -0.056100 -0.578998 0.813397 +vn -0.056100 -0.578998 0.813397 +vn -0.546798 -0.510798 0.663398 +vn -0.546798 -0.510798 0.663398 +vn -0.890988 -0.305696 0.335695 +vn -0.890988 -0.305696 0.335695 +vn -0.996457 -0.018699 -0.081996 +vn -0.996457 -0.018699 -0.081996 +vn -0.834899 0.273400 -0.477699 +vn -0.834899 0.273400 -0.477699 +vn -0.449617 0.492119 -0.745428 +vn -0.449617 0.492119 -0.745428 +vn 0.056100 0.578998 -0.813397 +vn 0.056100 0.578998 -0.813397 +vn 0.546798 0.510798 -0.663398 +vn 0.546798 0.510798 -0.663398 +vn 0.890988 0.305696 -0.335695 +vn 0.891017 0.305706 -0.335607 +vn 0.890988 0.305696 -0.335695 +vn 0.891015 0.305605 -0.335706 +vn 0.953590 0.023400 -0.300197 +vn 0.953590 0.023400 -0.300197 +vn 0.953590 0.023400 -0.300197 +vn 0.415311 0.250106 0.874622 +vn 0.415311 0.250106 0.874622 +vn 0.415311 0.250106 0.874622 +vn 0.916835 0.399115 -0.011000 +vn 0.916835 0.399115 -0.011000 +vn 0.916835 0.399115 -0.011000 +vn 0.990972 0.125896 0.046099 +vn 0.990972 0.125896 0.046099 +vn 0.990972 0.125896 0.046099 +vn 0.865927 0.148505 0.477615 +vn 0.865927 0.148505 0.477615 +vn 0.865927 0.148505 0.477615 +vn 0.964202 -0.077800 0.253500 +vn 0.964202 -0.077800 0.253500 +vn 0.964202 -0.077800 0.253500 +vn 0.467003 -0.334602 0.818505 +vn 0.467003 -0.334602 0.818505 +vn 0.467003 -0.334602 0.818505 +vn 0.633813 -0.670513 0.385608 +vn 0.633813 -0.670513 0.385608 +vn 0.633813 -0.670513 0.385608 +vn 0.646260 -0.665759 0.372977 +vn 0.646260 -0.665759 0.372977 +vn 0.646260 -0.665759 0.372977 +vn 0.620077 -0.272290 0.735773 +vn 0.620077 -0.272290 0.735773 +vn 0.620077 -0.272290 0.735773 +vn 0.771387 -0.009900 0.636289 +vn 0.771387 -0.009900 0.636289 +vn 0.771387 -0.009900 0.636289 +vn -0.046001 0.946229 -0.320210 +vn -0.046001 0.946229 -0.320210 +vn -0.046001 0.946229 -0.320210 +vn 0.196512 0.451327 0.870452 +vn 0.196512 0.451327 0.870452 +vn 0.196512 0.451327 0.870452 +vn 0.113906 0.993051 0.029602 +vn 0.113906 0.993051 0.029602 +vn 0.113906 0.993051 0.029602 +vn 0.321288 0.946964 -0.005700 +vn 0.321288 0.946964 -0.005700 +vn 0.321288 0.946964 -0.005700 +vn -0.610895 0.458696 0.645295 +vn -0.610895 0.458696 0.645295 +vn -0.610895 0.458696 0.645295 +vn -0.783374 0.343689 0.517883 +vn -0.783374 0.343689 0.517883 +vn -0.783374 0.343689 0.517883 +vn -0.217292 0.757772 0.615277 +vn -0.217292 0.757772 0.615277 +vn -0.217292 0.757772 0.615277 +vn -0.357614 0.891434 0.278311 +vn -0.357614 0.891434 0.278311 +vn -0.357614 0.891434 0.278311 +vn -0.648929 0.744933 0.154807 +vn -0.648929 0.744933 0.154807 +vn -0.648929 0.744933 0.154807 +vn -0.288602 0.704104 0.648804 +vn -0.288602 0.704104 0.648804 +vn -0.288602 0.704104 0.648804 +vn -0.613385 0.392790 0.685183 +vn -0.613385 0.392790 0.685183 +vn -0.613385 0.392790 0.685183 +vn -0.088504 -0.115605 -0.989344 +vn -0.088504 -0.115605 -0.989344 +vn -0.088504 -0.115605 -0.989344 +vn -0.088698 -0.115998 -0.989281 +vn -0.088698 -0.115998 -0.989281 +vn -0.088698 -0.115998 -0.989281 +vn 0.058098 0.038499 -0.997568 +vn 0.058098 0.038499 -0.997568 +vn 0.058098 0.038499 -0.997568 +vn 0.152994 0.141394 -0.978060 +vn 0.152994 0.141394 -0.978060 +vn 0.152994 0.141394 -0.978060 +vn 0.153102 0.142302 -0.977911 +vn 0.153102 0.142302 -0.977911 +vn 0.153102 0.142302 -0.977911 +vn 0.019800 -0.000300 -0.999804 +vn 0.019800 -0.000300 -0.999804 +vn 0.019800 -0.000300 -0.999804 +vn 0.020100 0.000400 -0.999798 +vn 0.020100 0.000400 -0.999798 +vn 0.020100 0.000400 -0.999798 +vn -0.104700 -0.132800 -0.985597 +vn -0.104700 -0.132800 -0.985597 +vn -0.104700 -0.132800 -0.985597 +vn -0.104698 -0.132898 -0.985584 +vn -0.104698 -0.132898 -0.985584 +vn -0.104698 -0.132898 -0.985584 +vn 0.335588 0.335688 -0.880167 +vn 0.335588 0.335688 -0.880167 +vn 0.335588 0.335688 -0.880167 +vn 0.315616 0.292915 -0.902545 +vn 0.315616 0.292915 -0.902545 +vn 0.315616 0.292915 -0.902545 +vn -0.166198 -0.175297 -0.970386 +vn -0.166198 -0.175297 -0.970386 +vn -0.166198 -0.175297 -0.970386 +vn -0.066799 -0.263697 -0.962290 +vn -0.066799 -0.263697 -0.962290 +vn -0.066799 -0.263697 -0.962290 +vn -0.269998 -0.045300 -0.961795 +vn -0.269998 -0.045300 -0.961795 +vn -0.269998 -0.045300 -0.961795 +vn -0.182798 -0.288098 -0.939992 +vn -0.182798 -0.288098 -0.939992 +vn -0.182798 -0.288098 -0.939992 +vn -0.075698 -0.296690 -0.951969 +vn -0.075698 -0.296690 -0.951969 +vn -0.075698 -0.296690 -0.951969 +vn -0.242597 -0.040300 -0.969290 +vn -0.242597 -0.040300 -0.969290 +vn -0.242597 -0.040300 -0.969290 +vn -0.131400 -0.197299 -0.971497 +vn -0.131400 -0.197299 -0.971497 +vn -0.131400 -0.197299 -0.971497 +vn -0.302805 0.196304 -0.932617 +vn -0.302805 0.196304 -0.932617 +vn -0.302805 0.196304 -0.932617 +vn 0.240591 -0.304688 -0.921565 +vn 0.240591 -0.304688 -0.921565 +vn 0.240591 -0.304688 -0.921565 +vn -0.336198 0.151699 -0.929494 +vn -0.336198 0.151699 -0.929494 +vn -0.336198 0.151699 -0.929494 +vn 0.198906 -0.341410 -0.918627 +vn 0.198906 -0.341410 -0.918627 +vn 0.198906 -0.341410 -0.918627 +vn -0.103498 -0.131598 -0.985885 +vn -0.103498 -0.131598 -0.985885 +vn -0.103498 -0.131598 -0.985885 +vn -0.103605 -0.131806 -0.985846 +vn -0.103605 -0.131806 -0.985846 +vn -0.103605 -0.131806 -0.985846 +vn 0.177595 0.168495 -0.969572 +vn 0.177595 0.168495 -0.969572 +vn 0.177595 0.168495 -0.969572 +vn 0.175990 0.165691 -0.970347 +vn 0.175990 0.165691 -0.970347 +vn 0.175990 0.165691 -0.970347 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn 0.062701 -0.815108 -0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.062701 0.815108 0.575906 +vn -0.607302 -0.775003 -0.174801 +vn -0.687607 0.319303 0.652106 +vn -0.794546 0.341120 -0.502329 +vn 0.108303 0.301709 0.947229 +vn -0.190500 0.064000 0.979599 +vn -0.153197 -0.952584 0.262896 +vn 0.172999 -0.984897 -0.007000 +vn 0.020700 -0.707905 -0.706005 +vn -0.607302 -0.775003 -0.174801 +vn -0.332994 0.530290 -0.779685 +vn -0.794546 0.341120 -0.502329 +vn -0.408309 -0.859919 -0.306307 +vn -0.427415 -0.840329 -0.333412 +vn -0.352492 -0.856080 -0.377991 +vn -0.266794 -0.882380 -0.387591 +vn -0.191091 -0.663168 -0.723666 +vn -0.227107 -0.947828 -0.223707 +vn -0.287906 -0.949320 -0.126103 +vn -0.043701 -0.940629 -0.336611 +vn 0.062701 -0.815108 -0.575906 +vn -0.332994 0.530290 -0.779685 +vn -0.095704 0.891538 0.442719 +vn 0.077797 -0.957458 -0.277888 +vn -0.289696 -0.857389 -0.425394 +vn -0.335408 -0.874720 -0.349808 +vn -0.404778 -0.753359 -0.518272 +vn -0.402988 -0.867073 -0.292891 +vn -0.418487 -0.848774 -0.323190 +vn -0.507098 -0.709397 -0.489498 +vn -0.216696 -0.751286 -0.623388 +vn 0.062701 -0.815108 -0.575906 +vn 0.585990 0.798186 0.139698 +vn 0.703928 -0.337014 -0.625225 +vn -0.019000 0.543986 -0.838879 +vn 0.439191 -0.897281 -0.044699 +vn 0.743281 -0.665483 -0.068298 +vn 0.690433 0.368117 0.622730 +vn 0.330110 0.437514 0.836426 +vn -0.172899 0.873494 0.455097 +vn 0.585990 0.798186 0.139698 +vn -0.562773 0.444279 -0.697067 +vn -0.019000 0.543986 -0.838879 +vn -0.562773 0.444279 -0.697067 +vn -0.041302 -0.742441 -0.668637 +vn 0.539773 -0.831159 0.133493 +vn 0.334797 -0.876192 0.346697 +vn 0.316283 -0.889351 0.330182 +vn -0.521600 -0.544200 0.657100 +vn -0.515204 -0.538704 0.666605 +vn -0.467317 -0.484017 0.739826 +vn 0.596628 -0.802138 0.024701 +vn 0.247100 -0.947100 0.204800 +vn 0.182108 -0.756334 0.628328 +vn -0.382999 -0.391899 0.836497 +vn 0.791713 -0.570709 0.217904 +vn 0.629401 -0.758902 0.167100 +vn 0.907992 -0.409097 0.090499 +vn 0.749602 -0.654802 0.096600 +vn 0.420876 -0.608265 0.672961 +vn 0.463292 -0.738688 0.489592 +vn 0.933229 -0.273309 0.233207 +vn 0.637073 -0.764368 0.099396 +vn 0.776611 -0.131002 0.616209 +vn 0.976758 -0.063597 0.204691 +vn 0.215806 -0.912923 0.346409 +vn 0.730015 -0.148903 0.667013 +vn 0.659529 0.709831 0.247311 +vn 0.561799 0.607999 0.560999 +vn 0.139806 0.169608 0.975545 +vn -0.229110 -0.223309 0.947440 +vn 0.865092 0.282797 0.414296 +vn -0.874366 0.473882 0.104496 +vn -0.918817 0.255405 0.300906 +vn -0.907608 0.273502 0.318503 +vn -0.521600 -0.544200 0.657100 +vn -0.515204 -0.538704 0.666605 +vn -0.467317 -0.484017 0.739826 +vn -0.783228 0.135805 0.606722 +vn -0.968136 0.174407 0.179707 +vn -0.847533 0.530721 -0.004900 +vn -0.382999 -0.391899 0.836497 +vn -0.810223 0.569716 0.137704 +vn -0.636827 0.747531 0.188808 +vn -0.482176 0.873857 0.062297 +vn -0.714520 0.696419 0.066802 +vn -0.653813 0.386808 0.650313 +vn -0.784093 0.412196 0.463996 +vn -0.351993 0.912782 0.207196 +vn -0.814771 0.575579 0.069697 +vn -0.198004 0.776414 0.598311 +vn -0.138197 0.973277 0.183396 +vn -0.934754 0.148693 0.322684 +vn -0.221595 0.728883 0.647785 +vn 0.561799 0.607999 0.560999 +vn 0.659529 0.709831 0.247311 +vn 0.139806 0.169608 0.975545 +vn -0.229110 -0.223309 0.947440 +vn 0.207898 0.891592 0.402296 +vn -0.640442 -0.571337 0.513233 +vn -0.704418 -0.507413 0.496313 +vn -0.690235 -0.575029 0.439223 +vn -0.499999 -0.840899 0.207100 +vn -0.685911 -0.524209 0.504708 +vn -0.484604 -0.740207 0.466104 +vn -0.684483 -0.620385 0.382891 +vn -0.523112 -0.807219 0.273406 +vn -0.270613 -0.909342 0.316015 +vn 0.585719 -0.623620 0.517717 +vn -0.625085 -0.674584 0.392690 +vn -0.397498 -0.719096 0.569997 +vn -0.531091 -0.787187 0.313495 +vn -0.464391 -0.754786 0.463291 +vn -0.790010 -0.527707 0.312104 +vn -0.411108 -0.875917 0.252505 +vn -0.730998 -0.514199 0.448599 +vn -0.773506 -0.387403 0.501604 +vn -0.649220 -0.714122 0.261808 +vn -0.737921 -0.452613 0.500614 +vn -0.532583 -0.628580 0.566782 +vn -0.394606 -0.870814 0.293205 +vn -0.471091 -0.882082 0.002100 +vn -0.468795 -0.504595 -0.724993 +vn -0.667623 -0.731025 0.141005 +vn -0.602571 -0.765163 0.226789 +vn -0.447183 -0.798669 0.402685 +vn -0.945216 -0.252504 0.206903 +vn -0.581292 -0.340795 0.738890 +vn -0.598699 -0.782399 0.171500 +vn 0.198295 -0.515586 0.833577 +vn 0.334507 -0.252205 0.908019 +vn -0.330203 -0.938809 0.098001 +vn -0.292196 -0.878187 0.378694 +vn 0.097905 -0.026301 0.994848 +vn -0.590303 0.113201 0.799204 +vn -0.987847 0.138107 0.071303 +vn -0.017899 -0.918558 0.394882 +vn 0.281189 -0.234491 0.930563 +vn -0.297308 -0.944726 0.138204 +vn -0.610510 0.025000 -0.791614 +vn -0.919232 -0.198507 -0.340012 +vn -0.487704 -0.872507 0.029600 +vn -0.326502 0.019000 0.945006 +vn -0.905918 0.144303 0.398108 +vn -0.923899 0.121300 -0.362900 +vn -0.908788 -0.094599 -0.406394 +vn -0.373806 -0.889614 -0.262404 +vn -0.157499 -0.972194 0.173299 +vn 0.746739 -0.303116 0.592031 +vn 0.910742 -0.317315 0.264312 +vn -0.414626 -0.152309 -0.897155 +vn 0.102402 -0.249605 -0.962918 +vn 0.198106 -0.891727 -0.406912 +vn 0.405290 -0.907178 -0.112997 +vn -0.337906 -0.941118 -0.010800 +vn 0.633817 -0.772320 -0.042401 +vn 0.487520 -0.870436 0.068303 +vn 0.234096 -0.773187 -0.589390 +vn 0.137903 -0.836321 -0.530613 +vn 0.157999 -0.972695 -0.169999 +vn -0.250612 -0.886443 -0.389119 +vn -0.032200 -0.847199 -0.530299 +vn 0.045401 -0.895516 0.442708 +vn -0.429483 -0.878464 0.209392 +vn -0.324191 -0.901376 -0.287092 +vn 0.319118 -0.919753 0.228513 +vn 0.201006 -0.896328 0.395212 +vn 0.913379 -0.004000 0.407091 +vn 0.009400 0.092705 -0.995649 +vn 0.306291 -0.936673 0.169795 +vn -0.683400 -0.692500 0.231100 +vn 0.946838 0.102004 0.305112 +vn -0.691316 -0.468111 0.550413 +vn -0.184998 -0.171898 0.967588 +vn 0.502189 -0.011500 0.864681 +vn -0.034000 -0.889487 -0.455693 +vn -0.558425 -0.701731 0.442420 +vn 0.304089 0.240191 -0.921867 +vn -0.848419 -0.456110 0.268606 +vn -0.934390 -0.054899 -0.351996 +vn -0.462013 0.114703 -0.879424 +vn -0.905918 0.144303 0.398108 +vn -0.326502 0.019000 0.945006 +vn -0.383811 0.911425 0.148304 +vn -0.923899 0.121300 -0.362900 +vn -0.610510 0.025000 -0.791614 +vn -0.919232 -0.198507 -0.340012 +vn -0.258391 0.865468 -0.429184 +vn -0.908788 -0.094599 -0.406394 +vn -0.414626 -0.152309 -0.897155 +vn 0.102402 -0.249605 -0.962918 +vn 0.746739 -0.303116 0.592031 +vn 0.910742 -0.317315 0.264312 +vn 0.409492 0.790585 0.455292 +vn 0.281189 -0.234491 0.930563 +vn 0.198295 -0.515586 0.833577 +vn 0.334507 -0.252205 0.908019 +vn -0.210506 0.898926 0.384211 +vn 0.097905 -0.026301 0.994848 +vn -0.590303 0.113201 0.799204 +vn -0.987847 0.138107 0.071303 +vn -0.945216 -0.252504 0.206903 +vn -0.581292 -0.340795 0.738890 +vn -0.250612 -0.886443 -0.389119 +vn 0.009400 0.092705 -0.995649 +vn -0.283205 0.929517 -0.236204 +vn -0.505421 0.839634 0.198908 +vn 0.913379 -0.004000 0.407091 +vn 0.201006 -0.896328 0.395212 +vn -0.412996 0.794391 0.445395 +vn 0.080703 0.877728 0.472315 +vn -0.683400 -0.692500 0.231100 +vn -0.431590 0.737682 0.519187 +vn 0.946838 0.102004 0.305112 +vn 0.585719 -0.623620 0.517717 +vn 0.179296 0.977479 0.111298 +vn -0.691316 -0.468111 0.550413 +vn -0.184998 -0.171898 0.967588 +vn 0.502189 -0.011500 0.864681 +vn -0.558425 -0.701731 0.442420 +vn -0.585378 0.776170 0.234291 +vn -0.468795 -0.504595 -0.724993 +vn 0.304089 0.240191 -0.921867 +vn 0.034198 0.985752 -0.164692 +vn -0.848419 -0.456110 0.268606 +vn -0.934390 -0.054899 -0.351996 +vn -0.462013 0.114703 -0.879424 +vn -0.033499 0.992269 0.119496 +vn 0.092003 -0.807230 -0.583022 +vn -0.062500 -0.445099 -0.893298 +vn -0.067703 -0.523821 -0.849134 +vn 0.081100 -0.897205 -0.434103 +vn -0.283800 -0.467499 -0.837199 +vn -0.282609 -0.464215 -0.839427 +vn -0.342698 -0.483797 -0.805294 +vn -0.335385 -0.473978 -0.814163 +vn -0.446797 -0.528296 -0.721995 +vn -0.460412 -0.552115 -0.695119 +vn -0.644011 -0.598410 -0.476608 +vn -0.618721 -0.597721 -0.509818 +vn -0.740197 -0.602698 -0.298099 +vn -0.725110 -0.614909 -0.310004 +vn -0.766612 -0.618310 -0.173203 +vn -0.782201 -0.594801 -0.185400 +vn -0.678506 -0.734506 -0.011400 +vn -0.615074 -0.786666 0.053298 +vn 0.952559 -0.045603 0.300919 +vn 0.918757 -0.077796 0.387082 +vn 0.928063 0.123395 0.351386 +vn 0.948965 0.109696 0.295689 +vn 0.858423 0.294308 0.420111 +vn 0.826130 0.358513 0.434716 +vn 0.716391 0.495394 0.491294 +vn 0.729894 0.476096 0.490496 +vn 0.637017 0.542014 0.548115 +vn 0.632970 0.546374 0.548474 +vn 0.573484 0.534785 0.620582 +vn 0.561408 0.526307 0.638609 +vn 0.445291 0.525189 0.725185 +vn 0.471291 0.519990 0.712387 +vn 0.445509 0.442609 0.778215 +vn 0.429986 0.455385 0.779575 +vn 0.399786 0.430785 0.809071 +vn 0.398693 0.436792 0.806385 +vn 0.588601 -0.807502 -0.038600 +vn 0.570379 -0.818069 0.073697 +vn -0.988415 0.015300 0.151002 +vn -0.993404 0.014100 0.113800 +vn -0.815280 0.314392 0.486288 +vn 0.151096 -0.234194 0.960377 +vn -0.095100 -0.171400 0.980601 +vn -0.674387 -0.703487 0.224296 +vn -0.146002 -0.987511 -0.059201 +vn -0.077097 -0.931763 0.354786 +vn -0.085001 -0.945310 0.314903 +vn -0.564905 -0.675006 0.474604 +vn -0.933604 0.358301 0.002000 +vn -0.828299 -0.556999 -0.060600 +vn -0.866506 -0.421503 0.267402 +vn -0.828795 0.339098 0.445097 +vn -0.812231 -0.581022 -0.051902 +vn -0.200307 -0.604222 -0.771228 +vn 0.061702 -0.492315 0.868227 +vn -0.235608 -0.289810 0.927631 +vn -0.711187 -0.276395 0.646389 +vn -0.702924 -0.271709 -0.657322 +vn 0.523393 0.790289 0.318596 +vn 0.570379 -0.818069 0.073697 +vn 0.588601 -0.807502 -0.038600 +vn 0.482802 0.856904 0.180601 +vn -0.993404 0.014100 0.113800 +vn -0.988415 0.015300 0.151002 +vn 0.151096 -0.234194 0.960377 +vn -0.145701 0.820004 0.553503 +vn -0.815280 0.314392 0.486288 +vn -0.095100 -0.171400 0.980601 +vn -0.478698 0.843596 0.243299 +vn -0.302607 0.947823 -0.100302 +vn -0.828795 0.339098 0.445097 +vn -0.621500 0.308300 -0.720200 +vn -0.702924 -0.271709 -0.657322 +vn -0.933604 0.358301 0.002000 +vn -0.403599 -0.189499 -0.895097 +vn -0.200307 -0.604222 -0.771228 +vn 0.191801 0.411703 0.890906 +vn 0.154392 0.041298 0.987146 +vn 0.187500 0.858002 0.478201 +vn 0.128099 0.989590 0.065599 +vn 0.258001 0.937003 0.235501 +vn 0.154392 0.041298 0.987146 +vn -0.990791 -0.106199 -0.083999 +vn -0.817094 0.314298 -0.483296 +vn -0.864080 0.458189 0.208395 +vn -0.869966 0.176393 0.460482 +vn -0.316199 0.705198 0.634598 +vn 0.258001 0.937003 0.235501 +vn -0.083796 -0.446880 0.890661 +vn -0.351887 -0.136295 0.926066 +vn -0.158305 -0.647521 0.745424 +vn -0.519599 -0.401499 0.754198 +vn -0.741687 0.175097 0.647489 +vn -0.756285 -0.008500 0.654187 +vn -0.460005 -0.474006 0.750809 +vn -0.767022 0.578216 0.278108 +vn -0.400572 0.427271 0.810544 +vn -0.189402 0.629806 0.753307 +vn -0.518797 0.766396 0.378798 +vn -0.042099 -0.016100 0.998984 +vn 0.198601 0.297601 0.933805 +vn -0.848935 0.458919 0.262111 +vn 0.676524 0.736026 0.024101 +vn 0.631321 0.747825 -0.205407 +vn -0.087799 0.879786 0.467192 +vn -0.347715 0.897940 0.269812 +vn -0.409000 -0.156000 -0.899101 +vn -0.594702 -0.294401 -0.748103 +vn -0.872971 0.429286 0.231592 +vn -0.243703 0.954313 0.172902 +vn -0.840443 -0.131007 -0.525827 +vn -0.481886 0.610982 -0.628082 +vn 0.214206 0.887124 -0.408811 +vn 0.147699 0.952591 0.265998 +vn -0.702225 0.117304 -0.702225 +vn 0.137193 0.218488 -0.966148 +vn -0.761568 0.642273 -0.086596 +vn -0.081898 -0.146796 -0.985771 +vn -0.464800 -0.543600 -0.698900 +vn 0.440191 0.826884 0.349993 +vn -0.132004 0.073402 0.988528 +vn 0.397105 0.247003 0.883910 +vn 0.765677 -0.643181 0.007600 +vn 0.434397 0.450797 0.779795 +vn -0.631684 -0.720782 -0.285393 +vn -0.456020 -0.463520 -0.759733 +vn 0.712395 0.688395 -0.136399 +vn 0.718412 -0.694512 0.039201 +vn 0.713883 -0.503688 0.486488 +vn 0.639234 0.645734 -0.417622 +vn 0.674395 0.729995 -0.110899 +vn -0.458905 -0.451004 0.765508 +vn -0.678602 -0.734502 0.002700 +vn 0.543788 0.008200 -0.839182 +vn -0.840443 -0.131007 -0.525827 +vn 0.147699 0.952591 0.265998 +vn 0.453906 0.633408 0.626708 +vn 0.440191 0.826884 0.349993 +vn 0.626603 -0.404402 -0.666204 +vn 0.786461 -0.605970 -0.119494 +vn 0.780179 -0.615684 -0.110697 +vn -0.592404 -0.702105 -0.395103 +vn -0.560798 -0.694598 0.450599 +vn 0.961158 -0.242389 -0.131994 +vn -0.371277 -0.430773 -0.822549 +vn -0.464800 -0.543600 -0.698900 +vn -0.423978 -0.504774 -0.751961 +vn 0.528300 0.612500 0.588000 +vn 0.521204 0.644005 -0.560004 +vn 0.604090 0.709188 0.363494 +vn -0.535527 -0.532927 0.655133 +vn 0.446394 -0.737491 0.506794 +vn 0.308203 -0.698807 -0.645507 +vn 0.969889 -0.157698 -0.185598 +vn -0.532386 -0.623284 -0.572785 +vn 0.639234 0.645734 -0.417622 +vn 0.969889 -0.157698 -0.185598 +vn 0.308203 -0.698807 -0.645507 +vn -0.532386 -0.623284 -0.572785 +vn -0.412091 -0.509189 0.755584 +vn 0.529195 -0.649494 0.545995 +vn 0.283500 -0.708100 -0.646700 +vn -0.580386 -0.717482 -0.385190 +vn 0.521204 0.644005 -0.560004 +vn 0.961158 -0.242389 -0.131994 +vn 0.283500 -0.708100 -0.646700 +vn -0.580386 -0.717482 -0.385190 +vn -0.680778 0.730377 -0.055598 +vn 0.397105 0.247003 0.883910 +vn 0.434397 0.450797 0.779795 +vn 0.712395 0.688395 -0.136399 +vn -0.749314 0.648412 -0.134502 +vn 0.674395 0.729995 -0.110899 +vn 0.639234 0.645734 -0.417622 +vn -0.606087 0.712085 0.354392 +vn -0.872971 0.429286 0.231592 +vn 0.147699 0.952591 0.265998 +vn 0.453906 0.633408 0.626708 +vn -0.738991 0.654392 -0.160198 +vn -0.761568 0.642273 -0.086596 +vn 0.440191 0.826884 0.349993 +vn -0.736223 0.668621 -0.104503 +vn -0.431217 0.893336 -0.126505 +vn 0.528300 0.612500 0.588000 +vn 0.604090 0.709188 0.363494 +vn 0.521204 0.644005 -0.560004 +vn -0.801394 0.423097 0.422797 +vn -0.672595 0.207598 -0.710295 +vn -0.210694 0.939872 -0.268792 +vn -0.672595 0.207598 -0.710295 +vn -0.210694 0.939872 -0.268792 +vn -0.741486 0.389993 0.545990 +vn -0.752991 0.135198 -0.643992 +vn -0.752991 0.135198 -0.643992 +vn -0.431217 0.893336 -0.126505 +vn -0.343699 0.301399 -0.889398 +vn 0.543788 0.008200 -0.839182 +vn 0.626603 -0.404402 -0.666204 +vn 0.303896 0.195698 -0.932389 +vn 0.779834 -0.457820 0.426919 +vn -0.769487 0.618089 -0.160797 +vn -0.864080 0.458189 0.208395 +vn -0.817094 0.314298 -0.483296 +vn -0.594702 -0.294401 -0.748103 +vn -0.316199 0.705198 0.634598 +vn -0.769088 -0.617291 0.165697 +vn 0.949470 -0.258092 -0.178594 +vn 0.955970 -0.256992 -0.141695 +vn -0.722728 -0.636925 0.268310 +vn 0.974646 0.068803 0.212910 +vn 0.075603 -0.223610 0.971742 +vn 0.324514 -0.230210 0.917439 +vn 0.495874 -0.867154 0.046398 +vn 0.474798 -0.820597 0.318099 +vn 0.916422 0.256506 -0.307207 +vn 0.982827 0.148304 0.109803 +vn 0.759964 -0.649469 0.025399 +vn 0.593211 -0.748614 -0.296105 +vn 0.574805 -0.774806 -0.263202 +vn 0.406490 -0.379790 0.830979 +vn 0.765884 -0.483290 0.424091 +vn 0.357399 -0.394599 -0.846497 +vn -0.176507 0.888335 0.423917 +vn -0.161099 0.947094 0.277598 +vn -0.769088 -0.617291 0.165697 +vn -0.722728 -0.636925 0.268310 +vn 0.955970 -0.256992 -0.141695 +vn 0.949470 -0.258092 -0.178594 +vn 0.520207 0.727409 0.447506 +vn 0.075603 -0.223610 0.971742 +vn 0.974646 0.068803 0.212910 +vn 0.324514 -0.230210 0.917439 +vn 0.471801 0.751402 -0.461301 +vn 0.751604 0.656404 -0.065000 +vn 0.982827 0.148304 0.109803 +vn 0.142496 -0.232194 -0.962175 +vn 0.916422 0.256506 -0.307207 +vn 0.357399 -0.394599 -0.846497 +vn 0.083403 -0.683226 -0.725428 +vn 0.538911 -0.818616 -0.198604 +vn 0.356902 -0.896705 0.261802 +vn 0.041202 -0.782144 -0.621735 +vn 0.899713 -0.200503 0.387706 +vn 0.187706 -0.891727 0.411812 +vn 0.409897 -0.814793 0.409997 +vn 0.909237 -0.405917 -0.092304 +vn 0.160301 -0.694303 0.701603 +vn 0.688710 -0.681010 0.248804 +vn 0.916409 -0.367304 0.159002 +vn 0.707211 -0.047401 0.705411 +vn 0.435905 -0.338004 0.834110 +vn 0.646280 -0.734377 0.207394 +vn 0.935614 -0.040401 0.350705 +vn 0.981350 0.153292 0.115994 +vn 0.935972 -0.212794 -0.280492 +vn 0.917988 0.344395 -0.196697 +vn -0.018500 -0.461003 -0.887206 +vn 0.777992 -0.376596 -0.502895 +vn 0.432902 0.842503 -0.320601 +vn 0.255805 -0.723115 -0.641614 +vn 0.759882 -0.595586 -0.260494 +vn -0.631108 -0.659908 -0.407705 +vn 0.726615 0.685814 -0.041101 +vn -0.865525 0.500815 -0.007100 +vn 0.383806 0.923414 -0.000300 +vn 0.471723 0.859042 -0.198810 +vn -0.465590 -0.739884 0.485590 +vn -0.537877 -0.833065 0.129195 +vn 0.197701 0.501603 -0.842205 +vn -0.866587 0.498893 0.011500 +vn 0.349210 0.582717 -0.733821 +vn 0.283299 0.501599 -0.817398 +vn -0.608914 0.671915 0.421609 +vn -0.102998 -0.189397 0.976484 +vn -0.394478 -0.663564 0.635665 +vn -0.902471 0.316590 -0.292091 +vn -0.018500 -0.461003 -0.887206 +vn 0.432902 0.842503 -0.320601 +vn 0.663284 0.690484 0.288593 +vn -0.772495 0.628696 0.089399 +vn -0.572783 0.500485 -0.649180 +vn 0.726615 0.685814 -0.041101 +vn -0.756765 0.652170 0.044498 +vn -0.538218 0.831727 -0.136204 +vn -0.423510 -0.434811 0.794719 +vn -0.606889 -0.780886 0.147997 +vn -0.602108 -0.577708 -0.551108 +vn -0.631108 -0.659908 -0.407705 +vn -0.641991 -0.623791 -0.445794 +vn 0.602409 0.755612 0.257204 +vn 0.579529 0.812941 -0.057203 +vn 0.412583 0.532078 -0.739370 +vn -0.251798 -0.442497 0.860694 +vn -0.736588 0.369494 0.566491 +vn -0.858530 -0.055802 -0.509718 +vn -0.474307 0.804012 -0.358605 +vn -0.518331 -0.794847 -0.315519 +vn 0.283299 0.501599 -0.817398 +vn -0.518331 -0.794847 -0.315519 +vn -0.858530 -0.055802 -0.509718 +vn -0.474307 0.804012 -0.358605 +vn -0.422699 -0.500398 0.755597 +vn -0.737396 0.397798 0.545897 +vn -0.748732 0.145406 -0.646728 +vn -0.595706 -0.704807 -0.385204 +vn 0.412583 0.532078 -0.739370 +vn -0.595706 -0.704807 -0.385204 +vn -0.748732 0.145406 -0.646728 +vn -0.538218 0.831727 -0.136204 +vn 0.383806 0.923414 -0.000300 +vn 0.851419 -0.514112 -0.103802 +vn 0.471723 0.859042 -0.198810 +vn 0.197701 0.501603 -0.842205 +vn 0.802627 -0.592120 -0.072002 +vn 0.883096 -0.296299 0.363798 +vn 0.283299 0.501599 -0.817398 +vn 0.349210 0.582717 -0.733821 +vn 0.935972 -0.212794 -0.280492 +vn 0.432902 0.842503 -0.320601 +vn 0.663284 0.690484 0.288593 +vn 0.726615 0.685814 -0.041101 +vn 0.759882 -0.595586 -0.260494 +vn 0.737525 -0.585620 -0.336312 +vn 0.788810 -0.523107 -0.322704 +vn 0.867889 -0.434495 -0.240797 +vn 0.602409 0.755612 0.257204 +vn 0.412583 0.532078 -0.739370 +vn 0.579529 0.812941 -0.057203 +vn 0.676205 -0.526604 0.515204 +vn 0.298305 -0.773714 -0.558910 +vn 0.926512 -0.067801 -0.370105 +vn 0.926512 -0.067801 -0.370105 +vn 0.298305 -0.773714 -0.558910 +vn 0.522282 -0.655077 0.545981 +vn 0.274200 -0.714199 -0.643999 +vn 0.867889 -0.434495 -0.240797 +vn 0.274200 -0.714199 -0.643999 +vn -0.902471 0.316590 -0.292091 +vn -0.572783 0.500485 -0.649180 +vn 0.919478 -0.324192 0.222395 +vn 0.887703 -0.401301 0.225701 +vn 0.830690 -0.485994 0.271597 +vn 0.836389 -0.512393 0.194697 +vn 0.723809 -0.661309 0.196903 +vn 0.756057 -0.626264 0.190189 +vn 0.762412 -0.616310 0.197203 +vn 0.703187 -0.701887 0.113498 +vn 0.700620 -0.703320 0.120303 +vn 0.721407 -0.692507 -0.002500 +vn 0.709970 -0.703270 -0.036798 +vn 0.650774 -0.757370 -0.053698 +vn 0.679417 -0.723318 -0.123303 +vn 0.623289 -0.781986 -0.003000 +vn 0.041202 -0.782144 -0.621735 +vn 0.356902 -0.896705 0.261802 +vn 0.899713 -0.200503 0.387706 +vn 0.339904 -0.437105 0.832709 +vn -0.040701 -0.643316 0.764518 +vn 0.937385 -0.000900 -0.348294 +vn 0.706507 -0.295403 -0.643106 +vn -0.542399 0.427199 0.723398 +vn 0.773629 0.631124 0.056402 +vn -0.716302 0.162100 0.678701 +vn -0.310402 -0.745904 0.589303 +vn 0.919096 0.310498 0.242599 +vn -0.897025 0.115903 0.426512 +vn -0.998961 -0.003900 -0.045398 +vn -0.361603 -0.429504 -0.827508 +vn -0.431104 -0.732907 0.526305 +vn -0.839126 -0.247408 -0.484415 +vn 0.350295 0.467393 0.811688 +vn -0.266701 -0.338701 -0.902304 +vn -0.400305 -0.534007 0.744710 +vn 0.353505 0.380806 0.854413 +vn -0.991441 0.086704 -0.097604 +vn 0.084504 -0.787239 0.610830 +vn 0.610415 0.247206 0.752518 +vn -0.746797 0.299099 0.593998 +vn -0.081502 -0.606212 0.791116 +vn 0.931344 -0.277813 -0.235411 +vn 0.923082 0.042099 0.382292 +vn 0.932729 -0.073002 0.353111 +vn -0.388003 0.330903 0.860207 +vn 0.864511 -0.017600 0.502306 +vn 0.482013 -0.594816 0.643317 +vn 0.137798 -0.988784 -0.057599 +vn -0.155899 -0.968993 0.191699 +vn -0.197606 -0.976829 -0.082202 +vn -0.465892 -0.396793 0.790886 +vn -0.074900 -0.778305 0.623404 +vn -0.497886 -0.858576 0.122297 +vn -0.415314 -0.845028 0.336811 +vn -0.495693 -0.657191 0.567792 +vn -0.814372 -0.428685 0.391187 +vn -0.775479 -0.597784 0.203194 +vn 0.643200 -0.711600 0.282700 +vn 0.899017 -0.238904 0.367007 +vn 0.965112 -0.147702 0.216203 +vn 0.279506 -0.769517 0.574213 +vn -0.340315 -0.937442 0.073403 +vn -0.175493 -0.950563 0.256190 +vn -0.047001 -0.998827 0.011600 +vn 0.429905 -0.855910 -0.287403 +vn -0.191602 -0.906509 0.376204 +vn -0.118292 -0.806944 0.578660 +vn 0.106404 -0.940532 0.322611 +vn -0.575178 0.216092 0.788970 +vn -0.881042 0.123906 0.456522 +vn -0.886288 0.052099 0.460194 +vn 0.596514 -0.800319 0.060501 +vn -0.752909 -0.658008 -0.012400 +vn 0.297394 -0.935983 0.188396 +vn -0.203903 -0.610009 -0.765711 +vn -0.145792 0.030498 -0.988845 +vn 0.137900 -0.055300 -0.988901 +vn 0.217102 -0.874807 0.433104 +vn 0.237701 -0.640204 0.730505 +vn 0.156507 -0.983241 0.093504 +vn 0.256900 -0.698699 -0.667699 +vn 0.544595 -0.697994 -0.464996 +vn 0.633596 -0.732295 -0.249598 +vn 0.077903 -0.863138 0.498922 +vn 0.464194 -0.085599 0.881588 +vn 0.754810 -0.013300 0.655809 +vn 0.240197 -0.593194 0.768392 +vn 0.776884 -0.616888 0.126097 +vn 0.888585 -0.120898 -0.442493 +vn 0.112496 -0.903671 0.413187 +vn 0.447915 -0.122604 -0.885630 +vn 0.705104 -0.144601 -0.694204 +vn 0.999173 -0.035799 -0.019299 +vn -0.516364 -0.768147 0.378574 +vn -0.543183 -0.794475 0.271591 +vn -0.856383 -0.473990 -0.204796 +vn -0.666618 -0.505814 -0.547515 +vn -0.428594 -0.613692 0.663091 +vn -0.780717 -0.550112 0.296407 +vn -0.553694 -0.677693 0.483895 +vn -0.591091 -0.566191 0.574491 +vn -0.416118 -0.117105 0.901739 +vn -0.486287 -0.082698 0.869877 +vn -0.930815 -0.018800 0.365006 +vn -0.820613 0.045001 -0.569709 +vn -0.517276 0.072297 -0.852760 +vn -0.977761 0.044298 -0.204992 +vn -0.354013 0.088603 -0.931034 +vn -0.414592 0.803984 -0.426291 +vn 0.706507 -0.295403 -0.643106 +vn 0.937385 -0.000900 -0.348294 +vn -0.357197 0.831993 -0.424497 +vn -0.542399 0.427199 0.723398 +vn 0.773629 0.631124 0.056402 +vn -0.716302 0.162100 0.678701 +vn 0.919096 0.310498 0.242599 +vn -0.030600 0.733500 -0.679000 +vn -0.897025 0.115903 0.426512 +vn -0.998961 -0.003900 -0.045398 +vn -0.361603 -0.429504 -0.827508 +vn -0.839126 -0.247408 -0.484415 +vn 0.298684 0.798957 -0.521972 +vn 0.350295 0.467393 0.811688 +vn -0.266701 -0.338701 -0.902304 +vn 0.403707 0.850715 -0.336606 +vn 0.353505 0.380806 0.854413 +vn -0.991441 0.086704 -0.097604 +vn 0.610415 0.247206 0.752518 +vn 0.507800 0.809500 -0.294700 +vn 0.172696 0.867880 0.465789 +vn -0.746797 0.299099 0.593998 +vn 0.923082 0.042099 0.382292 +vn 0.931344 -0.277813 -0.235411 +vn 0.932729 -0.073002 0.353111 +vn -0.388003 0.330903 0.860207 +vn 0.864511 -0.017600 0.502306 +vn 0.657605 0.530704 0.534704 +vn -0.119700 0.686198 0.717498 +vn 0.965112 -0.147702 0.216203 +vn 0.899017 -0.238904 0.367007 +vn 0.592410 0.589610 0.549009 +vn 0.238210 0.827135 0.509022 +vn -0.149199 0.779795 0.607996 +vn -0.575178 0.216092 0.788970 +vn -0.881042 0.123906 0.456522 +vn -0.886288 0.052099 0.460194 +vn 0.031499 0.581583 -0.812877 +vn 0.137900 -0.055300 -0.988901 +vn -0.145792 0.030498 -0.988845 +vn 0.836413 0.513108 0.192703 +vn 0.535123 0.840436 -0.085504 +vn 0.497515 0.504416 -0.705722 +vn 0.178393 0.956462 -0.230991 +vn 0.786379 0.370790 -0.494087 +vn 0.464194 -0.085599 0.881588 +vn 0.754810 -0.013300 0.655809 +vn 0.447915 -0.122604 -0.885630 +vn 0.705104 -0.144601 -0.694204 +vn 0.888585 -0.120898 -0.442493 +vn 0.999173 -0.035799 -0.019299 +vn -0.183296 0.982177 -0.041599 +vn -0.363100 0.638801 -0.678301 +vn -0.649780 0.644380 -0.403188 +vn -0.641408 0.711009 0.288204 +vn -0.416118 -0.117105 0.901739 +vn -0.486287 -0.082698 0.869877 +vn -0.930815 -0.018800 0.365006 +vn -0.820613 0.045001 -0.569709 +vn -0.517276 0.072297 -0.852760 +vn -0.977761 0.044298 -0.204992 +vn -0.354013 0.088603 -0.931034 +vn 0.058498 0.338989 0.938970 +vn -0.032099 0.697280 0.716080 +vn 0.317117 0.609333 0.726739 +vn 0.087096 0.316286 0.944657 +vn 0.065601 0.986522 0.149903 +vn 0.103798 0.711785 0.694686 +vn -0.198693 0.654178 0.729775 +vn -0.948467 0.061298 0.310889 +vn -0.857547 0.072104 -0.509328 +vn -0.095596 0.973256 -0.208890 +vn -0.280915 0.570530 0.771740 +vn 0.090700 0.385599 0.918198 +vn -0.555017 0.816325 0.159905 +vn -0.244005 0.546511 0.801116 +vn -0.025401 0.710320 0.703420 +vn -0.130802 0.990812 0.034400 +vn 0.121901 0.159702 0.979610 +vn 0.153904 0.171304 0.973123 +vn -0.531998 0.688498 -0.492898 +vn -0.147198 0.806091 -0.573193 +vn -0.317118 0.244414 -0.916351 +vn -0.073203 0.178907 -0.981139 +vn -0.340514 0.000000 0.940239 +vn -0.516994 0.000100 0.855989 +vn -0.083302 0.000000 0.996524 +vn -0.083302 0.000000 0.996524 +vn 0.090700 0.385599 0.918198 +vn -0.000900 0.119295 0.992858 +vn -0.429880 0.292887 0.854061 +vn -0.835066 0.446082 0.321987 +vn -0.872696 0.431498 -0.228499 +vn -0.626809 0.430406 -0.649509 +vn 0.466586 0.069098 -0.881773 +vn 0.998921 -0.039201 0.024901 +vn 0.290909 0.648621 0.703323 +vn 0.727601 0.000000 0.686001 +vn 0.769474 0.000100 0.638678 +vn 0.389183 0.000000 0.921160 +vn 0.260890 0.666074 0.698772 +vn 0.058499 0.167797 0.984084 +vn 0.289503 0.952310 0.096401 +vn 0.236200 0.815801 -0.527900 +vn 0.098102 0.262906 -0.959821 +vn 0.557124 0.438319 0.705330 +vn 0.120801 0.102101 0.987412 +vn 0.776084 0.611588 0.153797 +vn 0.735101 0.551001 -0.395000 +vn 0.354117 0.242012 -0.903344 +vn 0.699314 -0.000100 0.714814 +vn 0.154596 -0.000600 0.987978 +vn 0.993508 0.009100 0.113401 +vn 0.920717 0.015900 -0.389907 +vn 0.481804 0.017400 -0.876107 +vn -0.508291 -0.000200 0.861185 +vn -0.035202 0.008300 0.999346 +vn -0.937085 -0.002200 0.349094 +vn -0.981963 0.005500 -0.188993 +vn -0.688287 0.025600 -0.724987 +vn 0.098102 0.262906 -0.959821 +vn 0.354117 0.242012 -0.903344 +vn 0.481804 0.017400 -0.876107 +vn 0.344119 -0.204211 -0.916450 +vn 0.096503 -0.200506 -0.974928 +vn -0.071803 -0.169308 -0.982944 +vn -0.326289 -0.254491 -0.910368 +vn -0.618713 -0.396508 -0.678215 +vn -0.688287 0.025600 -0.724987 +vn -0.626809 0.430406 -0.649509 +vn -0.317118 0.244414 -0.916351 +vn -0.073203 0.178907 -0.981139 +vn 0.058498 -0.338989 0.938970 +vn 0.087195 -0.316283 0.944649 +vn 0.317207 -0.609314 0.726716 +vn -0.032099 -0.697280 0.716080 +vn -0.083701 -0.978615 0.187903 +vn -0.948467 0.061298 0.310889 +vn -0.204196 -0.655588 0.726986 +vn 0.105097 -0.712380 0.693880 +vn -0.110599 -0.969188 -0.220097 +vn -0.857547 0.072104 -0.509328 +vn -0.280893 -0.570486 0.771781 +vn 0.090703 -0.385514 0.918233 +vn -0.545406 -0.820208 0.172602 +vn -0.131096 -0.991369 -0.001400 +vn -0.023499 -0.726774 0.686475 +vn -0.252797 -0.551294 0.795091 +vn 0.121901 -0.159702 0.979610 +vn 0.153904 -0.171304 0.973123 +vn -0.539504 -0.687205 -0.486503 +vn -0.146596 -0.795478 -0.587984 +vn -0.326289 -0.254491 -0.910368 +vn -0.071803 -0.169308 -0.982944 +vn -0.516994 0.000100 0.855989 +vn -0.340514 0.000000 0.940239 +vn -0.083302 0.000000 0.996524 +vn -0.083302 0.000000 0.996524 +vn 0.010900 -0.124101 0.992210 +vn 0.090703 -0.385514 0.918233 +vn -0.438395 -0.300696 0.846990 +vn -0.839210 -0.452506 0.301604 +vn -0.871682 -0.429891 -0.235295 +vn -0.618713 -0.396508 -0.678215 +vn 0.998921 -0.039201 0.024901 +vn 0.466586 0.069098 -0.881773 +vn 0.290889 -0.648575 0.703373 +vn 0.727601 0.000000 0.686001 +vn 0.769474 0.000100 0.638678 +vn 0.389183 0.000000 0.921160 +vn 0.259900 -0.685900 0.679700 +vn 0.058499 -0.167797 0.984084 +vn 0.296908 -0.953227 0.056602 +vn 0.233096 -0.803487 -0.547791 +vn 0.096503 -0.200506 -0.974928 +vn 0.558327 -0.446322 0.699334 +vn 0.121506 -0.101905 0.987346 +vn 0.786576 -0.605781 0.119696 +vn 0.721468 -0.530376 -0.445180 +vn 0.344119 -0.204211 -0.916450 +vn 0.699314 -0.000100 0.714814 +vn 0.154596 -0.000600 0.987978 +vn 0.993508 0.009100 0.113401 +vn 0.920717 0.015900 -0.389907 +vn 0.481804 0.017400 -0.876107 +vn -0.035202 0.008300 0.999346 +vn -0.508291 -0.000200 0.861185 +vn -0.937085 -0.002200 0.349094 +vn -0.981963 0.005500 -0.188993 +vn -0.688287 0.025600 -0.724987 +vn 0.594088 0.742185 -0.310194 +vn 0.735205 0.557204 0.386003 +vn 0.722227 -0.111404 0.682625 +vn 0.407395 0.276497 -0.870390 +vn 0.489605 -0.702208 0.516906 +vn 0.335991 -0.931176 -0.141496 +vn 0.341209 -0.607715 -0.717118 +vn 0.335991 -0.931176 -0.141496 +vn 0.341209 -0.607715 -0.717118 +vn -0.601777 -0.573078 0.556279 +vn -0.681022 -0.690022 -0.245108 +vn -0.097294 -0.723758 0.683160 +vn 0.489605 -0.702208 0.516906 +vn 0.722227 -0.111404 0.682625 +vn -0.058198 0.029799 0.997860 +vn -0.442310 -0.008700 0.896820 +vn 0.594088 0.742185 -0.310194 +vn 0.407395 0.276497 -0.870390 +vn -0.678587 0.701487 -0.217796 +vn -0.640032 0.072304 -0.764939 +vn -0.558762 0.619858 0.550963 +vn -0.068003 0.747536 0.660732 +vn 0.735205 0.557204 0.386003 +vn 0.407395 0.276497 -0.870390 +vn -0.601777 -0.573078 0.556279 +vn -0.442310 -0.008700 0.896820 +vn -0.558762 0.619858 0.550963 +vn -0.678587 0.701487 -0.217796 +vn -0.640032 0.072304 -0.764939 +vn -0.681022 -0.690022 -0.245108 +vn -0.640032 0.072304 -0.764939 +vn 0.058498 0.338989 0.938970 +vn 0.087096 0.316286 0.944657 +vn 0.389183 0.000000 0.921160 +vn 0.058498 -0.338989 0.938970 +vn 0.087195 -0.316283 0.944649 +vn 0.389183 0.000000 0.921160 +vn -0.599016 -0.236806 0.764920 +vn -0.373622 -0.576034 0.727043 +vn 0.570608 0.654510 0.496007 +vn 0.571079 -0.820670 -0.019199 +vn 0.656796 0.750896 0.069100 +vn 0.561873 -0.643369 0.519975 +vn -0.026798 -0.742455 0.669360 +vn -0.223194 -0.974372 0.027999 +vn -0.685737 -0.724339 0.071404 +vn -0.874892 0.477395 0.081599 +vn -0.956404 -0.275501 0.096900 +vn -0.552894 0.393296 0.734592 +vn -0.594624 0.674927 0.436917 +vn -0.573633 0.818548 0.030402 +vn -0.301399 0.922198 0.242300 +vn -0.573194 0.673393 0.466895 +vn -0.182901 0.858604 -0.478902 +vn -0.458397 0.886294 0.066000 +vn -0.703485 0.710685 -0.006000 +vn -0.928792 -0.280597 -0.242098 +vn -0.343902 -0.563803 -0.750904 +vn -0.077899 0.624296 -0.777295 +vn -0.780911 0.623509 0.037601 +vn -0.563571 0.718063 0.408379 +vn -0.639185 0.432690 -0.635785 +vn -0.767925 0.634521 0.087603 +vn -0.562397 0.826695 0.016900 +vn -0.260606 0.720417 -0.642716 +vn -0.546706 0.582806 0.601207 +vn -0.519814 0.604517 0.603617 +vn -0.192292 0.077697 -0.978257 +vn 0.135705 0.320912 -0.937336 +vn 0.301008 -0.336909 -0.892125 +vn 0.537383 -0.297091 -0.789276 +vn -0.852610 0.220803 0.473606 +vn -0.934260 0.101696 0.341785 +vn -0.691659 0.367278 0.621863 +vn -0.691659 0.367278 0.621863 +vn -0.563571 0.718063 0.408379 +vn -0.643205 0.497904 0.581704 +vn -0.907636 0.370015 0.198208 +vn -0.903176 0.077098 -0.422289 +vn -0.582894 -0.177198 -0.792992 +vn -0.129698 -0.232196 -0.963983 +vn 0.902301 -0.082700 -0.423100 +vn 0.752615 0.459209 0.471909 +vn -0.312210 0.930531 0.191406 +vn 0.123297 0.629587 0.767084 +vn 0.185001 0.630402 0.753902 +vn -0.283309 0.563217 0.776223 +vn -0.334706 0.927817 0.164703 +vn -0.599089 0.560490 0.571790 +vn 0.026599 0.917657 -0.396482 +vn 0.398506 0.531008 -0.747812 +vn 0.642920 -0.141104 -0.752824 +vn -0.080599 0.895893 0.436897 +vn -0.544402 0.540702 0.641302 +vn 0.409901 0.910102 0.060800 +vn 0.732893 0.619194 -0.281897 +vn 0.805790 -0.011300 -0.592093 +vn 0.083503 0.627622 0.774027 +vn -0.504484 0.477485 0.719377 +vn 0.685909 0.530207 0.498407 +vn 0.946539 0.295012 0.130505 +vn 0.917616 -0.113202 -0.381007 +vn -0.930869 0.107796 0.349088 +vn -0.657904 0.397902 0.639404 +vn -0.935241 -0.308913 -0.172908 +vn -0.631472 -0.544276 -0.552275 +vn -0.072302 -0.606717 -0.791623 +vn 0.642920 -0.141104 -0.752824 +vn 0.805790 -0.011300 -0.592093 +vn 0.917616 -0.113202 -0.381007 +vn 0.868997 -0.367899 -0.330899 +vn 0.716301 -0.507700 -0.478700 +vn 0.588420 -0.567519 -0.575920 +vn 0.360301 -0.726203 -0.585502 +vn 0.010700 -0.881906 -0.471303 +vn -0.072302 -0.606717 -0.791623 +vn -0.129698 -0.232196 -0.963983 +vn 0.301008 -0.336909 -0.892125 +vn 0.537383 -0.297091 -0.789276 +vn -0.499481 0.148694 0.853468 +vn -0.484417 0.182307 0.855631 +vn -0.130299 -0.023800 0.991189 +vn -0.377893 -0.264095 0.887384 +vn -0.044999 -0.723088 0.689289 +vn -0.928792 -0.280597 -0.242098 +vn -0.522096 -0.309898 0.794594 +vn -0.257011 -0.219110 0.941242 +vn 0.190304 -0.895417 0.402508 +vn -0.343902 -0.563803 -0.750904 +vn -0.620794 -0.262297 0.738793 +vn -0.455378 0.119394 0.882256 +vn -0.410184 -0.827968 0.382385 +vn 0.039902 -0.833137 0.551625 +vn -0.348492 -0.294994 0.889681 +vn -0.616717 -0.224406 0.754521 +vn -0.501900 0.334800 0.797500 +vn -0.471699 0.338499 0.814198 +vn -0.008900 -0.991223 -0.131903 +vn 0.370286 -0.928165 0.037399 +vn 0.360301 -0.726203 -0.585502 +vn 0.588420 -0.567519 -0.575920 +vn -0.934260 0.101696 0.341785 +vn -0.852610 0.220803 0.473606 +vn -0.691659 0.367278 0.621863 +vn -0.691659 0.367278 0.621863 +vn -0.599600 0.314300 0.736000 +vn -0.455378 0.119394 0.882256 +vn -0.826374 -0.097697 0.554582 +vn -0.767413 -0.630810 0.114702 +vn -0.457010 -0.848219 -0.267706 +vn 0.010700 -0.881906 -0.471303 +vn 0.752615 0.459209 0.471909 +vn 0.902301 -0.082700 -0.423100 +vn -0.130094 -0.076396 0.988554 +vn 0.123297 0.629587 0.767084 +vn 0.185001 0.630402 0.753902 +vn -0.283309 0.563217 0.776223 +vn -0.133596 -0.129996 0.982473 +vn -0.552003 0.300002 0.778005 +vn 0.324884 -0.574372 0.751363 +vn 0.636077 -0.735673 0.232791 +vn 0.716301 -0.507700 -0.478700 +vn 0.048300 0.207201 0.977105 +vn -0.515193 0.382695 0.766890 +vn 0.610215 -0.043901 0.791019 +vn 0.905935 -0.247410 0.343613 +vn 0.868997 -0.367899 -0.330899 +vn 0.083503 0.627622 0.774027 +vn -0.504484 0.477485 0.719377 +vn 0.685909 0.530207 0.498407 +vn 0.946539 0.295012 0.130505 +vn 0.917616 -0.113202 -0.381007 +vn -0.657904 0.397902 0.639404 +vn -0.930869 0.107796 0.349088 +vn -0.935241 -0.308913 -0.172908 +vn -0.631472 -0.544276 -0.552275 +vn -0.072302 -0.606717 -0.791623 +vn 0.545029 0.734539 -0.404222 +vn 0.239891 0.943163 0.229991 +vn 0.136905 0.539119 0.831029 +vn 0.820805 0.054500 -0.568603 +vn 0.146704 -0.098902 0.984224 +vn 0.476403 -0.619403 0.624003 +vn 0.797827 -0.601020 0.047402 +vn 0.476403 -0.619403 0.624003 +vn 0.797827 -0.601020 0.047402 +vn -0.729684 -0.506389 0.459490 +vn -0.268699 -0.962696 -0.031900 +vn -0.403190 -0.329392 0.853779 +vn 0.146704 -0.098902 0.984224 +vn 0.136905 0.539119 0.831029 +vn -0.677524 0.402914 0.615322 +vn -0.901722 0.147404 0.406410 +vn 0.545029 0.734539 -0.404222 +vn 0.820805 0.054500 -0.568603 +vn -0.479412 0.129903 -0.867922 +vn -0.016800 -0.563589 -0.825884 +vn -0.860904 0.438202 -0.258501 +vn -0.573326 0.817637 -0.052602 +vn 0.239891 0.943163 0.229991 +vn 0.820805 0.054500 -0.568603 +vn -0.729684 -0.506389 0.459490 +vn -0.901722 0.147404 0.406410 +vn -0.860904 0.438202 -0.258501 +vn -0.479412 0.129903 -0.867922 +vn -0.016800 -0.563589 -0.825884 +vn -0.268699 -0.962696 -0.031900 +vn -0.016800 -0.563589 -0.825884 +vn -0.594624 0.674927 0.436917 +vn -0.573194 0.673393 0.466895 +vn -0.283309 0.563217 0.776223 +vn -0.499481 0.148694 0.853468 +vn -0.484417 0.182307 0.855631 +vn -0.283309 0.563217 0.776223 +vn -0.179903 -0.011500 0.983617 +vn -0.441018 0.302913 0.844835 +vn -0.101401 0.404704 0.908808 +vn -0.143906 -0.017101 0.989444 +vn -0.490986 0.770678 0.406188 +vn -0.334310 0.389911 0.858025 +vn -0.557493 0.180198 0.810389 +vn -0.841223 -0.531114 0.101303 +vn -0.732873 -0.204592 -0.648876 +vn -0.601801 0.798101 0.029500 +vn -0.582521 0.058202 0.810729 +vn -0.177502 0.048800 0.982910 +vn -0.917518 0.319506 0.236805 +vn -0.539887 0.047799 0.840379 +vn -0.441982 0.320887 0.837666 +vn -0.652196 0.713695 0.255498 +vn -0.030900 -0.136200 0.990199 +vn -0.010200 -0.108702 0.994022 +vn -0.798018 0.445610 -0.405709 +vn -0.536623 0.759632 -0.367416 +vn -0.355986 0.339687 -0.870567 +vn -0.113198 0.431493 -0.894986 +vn -0.328680 -0.483070 0.811550 +vn -0.472299 -0.543799 0.693698 +vn -0.116098 -0.372394 0.920784 +vn -0.116098 -0.372394 0.920784 +vn -0.177502 0.048800 0.982910 +vn -0.112199 -0.234599 0.965596 +vn -0.559313 -0.265706 0.785219 +vn -0.957080 -0.170596 0.234295 +vn -0.954914 -0.018500 -0.296304 +vn -0.729105 0.243802 -0.639505 +vn 0.393583 0.582276 -0.711370 +vn 0.855701 0.462700 0.231700 +vn -0.143698 0.430693 0.890985 +vn 0.576617 0.138304 0.805224 +vn 0.613700 0.175000 0.769901 +vn 0.282606 -0.109902 0.952920 +vn -0.178100 0.431001 0.884601 +vn -0.088503 -0.163105 0.982631 +vn -0.282504 0.873711 0.396005 +vn -0.223400 0.945098 -0.238500 +vn -0.016800 0.577508 -0.816212 +vn 0.193698 0.395996 0.897590 +vn -0.000800 -0.185297 0.982682 +vn 0.307898 0.827395 0.469697 +vn 0.332283 0.940451 -0.071696 +vn 0.206105 0.670915 -0.712316 +vn 0.551618 0.114304 0.826227 +vn 0.083600 -0.250500 0.964500 +vn 0.820684 0.469191 0.326094 +vn 0.779518 0.605014 -0.162204 +vn 0.434305 0.546806 -0.715808 +vn -0.465180 -0.541377 0.700370 +vn -0.080601 -0.342606 0.936015 +vn -0.798820 -0.588715 0.123703 +vn -0.815473 -0.426586 -0.391187 +vn -0.555892 -0.085099 -0.826887 +vn -0.016800 0.577508 -0.816212 +vn 0.206105 0.670915 -0.712316 +vn 0.434305 0.546806 -0.715808 +vn 0.442103 0.314002 -0.840207 +vn 0.235687 0.211889 -0.948449 +vn 0.078202 0.154905 -0.984829 +vn -0.091497 -0.065198 -0.993669 +vn -0.269292 -0.402588 -0.874874 +vn -0.555892 -0.085099 -0.826887 +vn -0.729105 0.243802 -0.639505 +vn -0.355986 0.339687 -0.870567 +vn -0.113198 0.431493 -0.894986 +vn 0.190305 -0.552614 0.811420 +vn 0.201596 -0.521991 0.828785 +vn 0.564204 -0.568004 0.599204 +vn 0.320680 -0.810250 0.490570 +vn 0.455697 -0.885594 -0.089799 +vn -0.841223 -0.531114 0.101303 +vn 0.153402 -0.867111 0.473906 +vn 0.444702 -0.746003 0.495702 +vn 0.447115 -0.756225 -0.477716 +vn -0.732873 -0.204592 -0.648876 +vn 0.040702 -0.852634 0.520921 +vn 0.243603 -0.566808 0.787011 +vn -0.016200 -0.986194 -0.164799 +vn 0.431807 -0.856813 -0.281804 +vn 0.345302 -0.819704 0.457002 +vn 0.052701 -0.830909 0.553906 +vn 0.143493 -0.391182 0.909058 +vn 0.176898 -0.382196 0.906991 +vn -0.053199 -0.658393 -0.750792 +vn 0.339210 -0.513615 -0.788122 +vn -0.091497 -0.065198 -0.993669 +vn 0.078202 0.154905 -0.984829 +vn -0.472299 -0.543799 0.693698 +vn -0.328680 -0.483070 0.811550 +vn -0.116098 -0.372394 0.920784 +vn -0.116098 -0.372394 0.920784 +vn 0.030700 -0.422801 0.905702 +vn 0.243603 -0.566808 0.787011 +vn -0.241907 -0.741422 0.625919 +vn -0.468797 -0.883194 -0.014100 +vn -0.483309 -0.703313 -0.521310 +vn -0.269292 -0.402588 -0.874874 +vn 0.855701 0.462700 0.231700 +vn 0.393583 0.582276 -0.711370 +vn 0.564816 -0.604717 0.561516 +vn 0.576617 0.138304 0.805224 +vn 0.613700 0.175000 0.769901 +vn 0.282606 -0.109902 0.952920 +vn 0.560325 -0.642329 0.522924 +vn 0.094701 -0.430907 0.897414 +vn 0.766302 -0.630401 -0.124000 +vn 0.659297 -0.342398 -0.669396 +vn 0.235687 0.211889 -0.948449 +vn 0.678177 -0.307490 0.667477 +vn 0.111195 -0.347784 0.930958 +vn 0.983152 -0.127794 0.130694 +vn 0.913842 0.087104 -0.396618 +vn 0.442103 0.314002 -0.840207 +vn 0.551618 0.114304 0.826227 +vn 0.083600 -0.250500 0.964500 +vn 0.820684 0.469191 0.326094 +vn 0.779518 0.605014 -0.162204 +vn 0.434305 0.546806 -0.715808 +vn -0.080601 -0.342606 0.936015 +vn -0.465180 -0.541377 0.700370 +vn -0.798820 -0.588715 0.123703 +vn -0.815473 -0.426586 -0.391187 +vn -0.555892 -0.085099 -0.826887 +vn 0.106003 0.994026 0.026001 +vn 0.292521 0.686350 0.665849 +vn 0.633096 0.047800 0.772596 +vn 0.230199 0.714298 -0.660898 +vn 0.768967 -0.485779 0.415582 +vn 0.796225 -0.527416 -0.296409 +vn 0.650681 -0.075598 -0.755578 +vn 0.796225 -0.527416 -0.296409 +vn 0.650681 -0.075598 -0.755578 +vn -0.216192 -0.944567 0.247091 +vn -0.181293 -0.811869 -0.554979 +vn 0.282090 -0.853270 0.438584 +vn 0.768967 -0.485779 0.415582 +vn 0.633096 0.047800 0.772596 +vn -0.111502 -0.336607 0.935020 +vn -0.406899 -0.526899 0.746198 +vn 0.106003 0.994026 0.026001 +vn 0.230199 0.714298 -0.660898 +vn -0.940539 0.291012 -0.175207 +vn -0.539207 -0.010300 -0.842110 +vn -0.831556 0.031098 0.554570 +vn -0.495991 0.343294 0.797585 +vn 0.292521 0.686350 0.665849 +vn 0.230199 0.714298 -0.660898 +vn -0.216192 -0.944567 0.247091 +vn -0.406899 -0.526899 0.746198 +vn -0.831556 0.031098 0.554570 +vn -0.940539 0.291012 -0.175207 +vn -0.539207 -0.010300 -0.842110 +vn -0.181293 -0.811869 -0.554979 +vn -0.539207 -0.010300 -0.842110 +vn -0.179903 -0.011500 0.983617 +vn -0.143906 -0.017101 0.989444 +vn 0.282606 -0.109902 0.952920 +vn 0.190305 -0.552614 0.811420 +vn 0.201596 -0.521991 0.828785 +vn 0.282606 -0.109902 0.952920 +f 1/1/1 2/2/2 3/3/3 +f 1/1/1 3/3/3 4/4/4 +f 5/5/5 6/6/6 1/1/1 +f 7/7/7 8/8/8 9/9/9 +f 8/8/8 10/10/10 9/9/9 +f 11/11/11 12/12/12 9/9/9 +f 10/10/10 11/11/11 9/9/9 +f 13/13/13 14/14/14 15/15/15 +f 16/16/16 17/17/17 18/18/18 +f 19/19/19 20/20/20 21/21/21 +f 22/22/22 23/23/23 24/24/24 +f 25/25/25 26/26/26 27/27/27 +f 28/28/28 29/29/29 30/30/30 +f 31/31/31 32/32/32 33/33/33 +f 34/34/34 35/35/35 36/36/36 +f 37/37/37 38/38/38 39/39/39 +f 40/40/40 41/41/41 42/42/42 +f 43/43/43 44/44/44 45/45/45 +f 46/46/46 47/47/47 48/48/48 +f 49/49/49 50/50/50 51/51/51 +f 52/52/52 53/53/53 54/54/54 +f 55/55/55 56/56/56 57/57/57 +f 58/58/58 59/59/59 60/60/60 +f 61/61/61 62/62/62 63/63/63 +f 64/64/64 65/65/65 66/66/66 +f 67/67/67 68/68/68 69/69/69 +f 69/69/69 70/70/70 67/67/67 +f 71/71/71 72/72/72 73/73/73 +f 73/73/73 74/74/74 71/71/71 +f 75/75/75 76/76/76 77/77/77 +f 77/77/77 78/78/78 75/75/75 +f 79/79/79 80/80/80 81/81/81 +f 81/81/81 82/82/82 79/79/79 +f 83/83/83 84/84/84 85/85/85 +f 85/85/85 86/86/86 83/83/83 +f 84/84/84 87/87/87 88/88/88 +f 88/88/88 85/85/85 84/84/84 +f 87/87/87 89/89/89 90/90/90 +f 90/90/90 88/88/88 87/87/87 +f 89/89/89 91/91/91 92/92/92 +f 92/92/92 90/90/90 89/89/89 +f 91/91/91 93/93/93 94/94/94 +f 94/94/94 92/92/92 91/91/91 +f 93/93/93 95/95/95 96/96/96 +f 96/96/96 94/94/94 93/93/93 +f 95/95/95 97/97/97 98/98/98 +f 98/98/98 96/96/96 95/95/95 +f 97/97/97 99/99/99 100/100/100 +f 100/100/100 98/98/98 97/97/97 +f 99/99/99 101/101/101 102/102/102 +f 102/102/102 100/100/100 99/99/99 +f 101/101/101 103/103/103 104/104/104 +f 104/104/104 102/102/102 101/101/101 +f 103/103/103 105/105/105 106/106/106 +f 106/106/106 104/104/104 103/103/103 +f 105/105/105 83/83/83 86/86/86 +f 86/86/86 106/106/106 105/105/105 +f 107/107/107 108/108/108 109/109/109 +f 109/109/109 110/110/110 107/107/107 +f 111/111/111 112/112/112 113/113/113 +f 114/114/114 115/115/115 116/116/116 +f 117/117/117 118/118/118 119/119/119 +f 120/120/120 121/121/121 122/122/122 +f 123/123/123 124/124/124 125/125/125 +f 126/126/126 127/127/127 128/128/128 +f 129/129/129 130/130/130 131/131/131 +f 132/132/132 133/133/133 134/134/134 +f 135/135/135 136/136/136 137/137/137 +f 138/138/138 139/139/139 140/140/140 +f 141/141/141 142/142/142 143/143/143 +f 144/144/144 145/145/145 146/146/146 +f 147/147/147 148/148/148 149/149/149 +f 150/150/150 151/151/151 152/152/152 +f 153/153/153 154/154/154 155/155/155 +f 156/156/156 157/157/157 158/158/158 +f 159/159/159 160/160/160 161/161/161 +f 162/162/162 163/163/163 164/164/164 +f 165/165/165 166/166/166 167/167/167 +f 168/168/168 169/169/169 170/170/170 +f 171/171/171 172/172/172 173/173/173 +f 174/174/174 175/175/175 176/176/176 +f 177/177/177 178/178/178 179/179/179 +f 180/180/180 181/181/181 182/182/182 +f 183/183/183 184/184/184 185/185/185 +f 186/186/186 187/187/187 188/188/188 +f 189/189/189 190/190/190 191/191/191 +f 192/192/192 193/193/193 194/194/194 +f 195/195/195 196/196/196 197/197/197 +f 198/198/198 199/199/199 200/200/200 +f 201/201/201 202/202/202 203/203/203 +f 204/204/204 205/205/205 206/206/206 +f 207/207/207 208/208/208 209/209/209 +f 210/210/210 211/211/211 212/212/212 +f 213/213/213 214/214/214 215/215/215 +f 216/216/216 217/217/217 218/218/218 +f 219/219/219 220/220/220 221/221/221 +f 222/222/222 223/223/223 224/224/224 +f 225/225/225 226/226/226 227/227/227 +f 228/228/228 229/229/229 230/230/230 +f 231/231/231 232/232/232 233/233/233 +f 234/234/234 235/235/235 236/236/236 +f 237/237/237 238/238/238 239/239/239 +f 240/240/240 241/241/241 242/242/242 +f 243/243/243 244/244/244 245/245/245 +f 246/246/246 247/247/247 248/248/248 +f 249/249/249 250/250/250 251/251/251 +f 252/252/252 253/253/253 254/254/254 +f 255/255/255 256/256/256 257/257/257 +f 257/257/257 258/258/258 259/259/259 +f 257/257/257 259/259/259 260/260/260 +f 255/255/255 257/257/257 260/260/260 +f 261/261/261 255/255/255 260/260/260 +f 262/262/262 263/263/263 264/264/264 +f 264/264/264 265/265/265 266/266/266 +f 262/262/262 264/264/264 266/266/266 +f 262/262/262 266/266/266 267/267/267 +f 268/268/268 262/262/262 267/267/267 +f 269/269/269 270/270/270 271/271/271 +f 271/271/271 272/272/272 273/273/273 +f 271/271/271 273/273/273 265/265/265 +f 269/269/269 271/271/271 265/265/265 +f 264/264/264 269/269/269 265/265/265 +f 274/274/274 275/275/275 276/276/276 +f 276/276/276 277/277/277 278/278/278 +f 276/276/276 278/278/278 257/257/257 +f 274/274/274 276/276/276 257/257/257 +f 256/256/256 274/274/274 257/257/257 +f 279/279/279 280/280/280 281/281/281 +f 282/282/282 283/283/283 284/284/284 +f 284/284/284 285/285/285 282/282/282 +f 286/286/286 285/285/285 284/284/284 +f 286/286/286 284/284/284 287/287/287 +f 288/288/288 286/286/286 287/287/287 +f 288/288/288 287/287/287 289/289/289 +f 290/290/290 291/291/291 292/292/292 +f 292/292/292 293/293/293 294/294/294 +f 292/292/292 294/294/294 295/295/295 +f 290/290/290 292/292/292 295/295/295 +f 296/296/296 290/290/290 295/295/295 +f 297/297/297 296/296/296 295/295/295 +f 298/298/298 297/297/297 295/295/295 +f 299/299/299 281/281/281 280/280/280 +f 283/283/283 282/282/282 300/300/300 +f 280/280/280 283/283/283 300/300/300 +f 299/299/299 280/280/280 300/300/300 +f 301/301/301 302/302/302 303/303/303 +f 304/304/304 301/301/301 303/303/303 +f 303/303/303 305/305/305 306/306/306 +f 304/304/304 303/303/303 306/306/306 +f 304/304/304 306/306/306 307/307/307 +f 304/304/304 307/307/307 308/308/308 +f 309/309/309 304/304/304 308/308/308 +f 283/283/283 280/280/280 279/279/279 +f 279/279/279 284/284/284 283/283/283 +f 310/310/310 311/311/311 312/312/312 +f 313/313/313 314/314/314 315/315/315 +f 315/315/315 316/316/316 313/313/313 +f 317/317/317 316/316/316 315/315/315 +f 317/317/317 315/315/315 318/318/318 +f 319/319/319 317/317/317 318/318/318 +f 319/319/319 318/318/318 320/320/320 +f 321/321/321 312/312/312 311/311/311 +f 314/314/314 313/313/313 322/322/322 +f 311/311/311 314/314/314 322/322/322 +f 321/321/321 311/311/311 322/322/322 +f 314/314/314 311/311/311 310/310/310 +f 310/310/310 315/315/315 314/314/314 +f 323/323/323 324/324/324 325/325/325 +f 326/326/326 327/327/327 325/325/325 +f 324/324/324 328/328/328 326/326/326 +f 326/326/326 325/325/325 324/324/324 +f 324/324/324 329/329/329 330/330/330 +f 330/330/330 331/331/331 324/324/324 +f 332/332/332 328/328/328 324/324/324 +f 324/324/324 331/331/331 332/332/332 +f 333/333/333 334/334/334 329/329/329 +f 333/333/333 329/329/329 335/335/335 +f 336/336/336 333/333/333 335/335/335 +f 337/337/337 331/331/331 334/334/334 +f 334/334/334 333/333/333 337/337/337 +f 335/335/335 329/329/329 338/338/338 +f 339/339/339 333/333/333 336/336/336 +f 336/336/336 340/340/340 339/339/339 +f 341/341/341 337/337/337 333/333/333 +f 333/333/333 342/342/342 341/341/341 +f 323/323/323 343/343/343 329/329/329 +f 329/329/329 324/324/324 323/323/323 +f 344/344/344 339/339/339 340/340/340 +f 341/341/341 342/342/342 345/345/345 +f 345/345/345 346/346/346 341/341/341 +f 329/329/329 343/343/343 338/338/338 +f 347/347/347 348/348/348 337/337/337 +f 337/337/337 341/341/341 347/347/347 +f 346/346/346 347/347/347 341/341/341 +f 348/348/348 332/332/332 331/331/331 +f 331/331/331 337/337/337 348/348/348 +f 349/349/349 342/342/342 339/339/339 +f 344/344/344 349/349/349 339/339/339 +f 339/339/339 342/342/342 333/333/333 +f 349/349/349 345/345/345 342/342/342 +f 350/350/350 351/351/351 352/352/352 +f 353/353/353 351/351/351 354/354/354 +f 352/352/352 351/351/351 353/353/353 +f 353/353/353 355/355/355 352/352/352 +f 352/352/352 356/356/356 357/357/357 +f 357/357/357 358/358/358 352/352/352 +f 359/359/359 356/356/356 352/352/352 +f 352/352/352 355/355/355 359/359/359 +f 358/358/358 360/360/360 361/361/361 +f 362/362/362 358/358/358 361/361/361 +f 363/363/363 362/362/362 361/361/361 +f 364/364/364 361/361/361 360/360/360 +f 360/360/360 356/356/356 364/364/364 +f 362/362/362 365/365/365 358/358/358 +f 366/366/366 367/367/367 363/363/363 +f 363/363/363 361/361/361 366/366/366 +f 368/368/368 369/369/369 361/361/361 +f 361/361/361 364/364/364 368/368/368 +f 350/350/350 352/352/352 358/358/358 +f 358/358/358 370/370/370 350/350/350 +f 371/371/371 367/367/367 366/366/366 +f 368/368/368 372/372/372 373/373/373 +f 373/373/373 369/369/369 368/368/368 +f 358/358/358 365/365/365 370/370/370 +f 374/374/374 368/368/368 364/364/364 +f 364/364/364 375/375/375 374/374/374 +f 372/372/372 368/368/368 374/374/374 +f 375/375/375 364/364/364 356/356/356 +f 356/356/356 359/359/359 375/375/375 +f 376/376/376 366/366/366 369/369/369 +f 371/371/371 366/366/366 376/376/376 +f 366/366/366 361/361/361 369/369/369 +f 376/376/376 369/369/369 373/373/373 +f 377/377/377 378/378/378 379/379/379 +f 379/379/379 380/380/380 377/377/377 +f 378/378/378 381/381/381 382/382/382 +f 382/382/382 379/379/379 378/378/378 +f 381/381/381 383/383/383 382/382/382 +f 383/383/383 384/384/384 382/382/382 +f 385/385/385 386/386/386 382/382/382 +f 382/382/382 384/384/384 385/385/385 +f 377/377/377 380/380/380 387/387/387 +f 377/377/377 387/387/387 388/388/388 +f 389/389/389 390/390/390 387/387/387 +f 391/391/391 392/392/392 393/393/393 +f 393/393/393 394/394/394 391/391/391 +f 394/394/394 393/393/393 395/395/395 +f 395/395/395 396/396/396 394/394/394 +f 396/396/396 395/395/395 397/397/397 +f 397/397/397 395/395/395 398/398/398 +f 399/399/399 398/398/398 395/395/395 +f 395/395/395 400/400/400 399/399/399 +f 391/391/391 387/387/387 392/392/392 +f 391/391/391 401/401/401 387/387/387 +f 387/387/387 401/401/401 402/402/402 +f 403/403/403 404/404/404 405/405/405 +f 405/405/405 406/406/406 403/403/403 +f 407/407/407 408/408/408 409/409/409 +f 409/409/409 410/410/410 407/407/407 +f 408/408/408 411/411/411 409/409/409 +f 404/404/404 410/410/410 409/409/409 +f 411/411/411 412/412/412 409/409/409 +f 413/413/413 404/404/404 409/409/409 +f 412/412/412 413/413/413 409/409/409 +f 414/414/414 415/415/415 407/407/407 +f 407/407/407 410/410/410 414/414/414 +f 416/416/416 417/417/417 418/418/418 +f 418/418/418 419/419/419 416/416/416 +f 419/419/419 405/405/405 416/416/416 +f 420/420/420 421/421/421 416/416/416 +f 405/405/405 420/420/420 416/416/416 +f 416/416/416 422/422/422 417/417/417 +f 416/416/416 421/421/421 422/422/422 +f 418/418/418 423/423/423 424/424/424 +f 424/424/424 419/419/419 418/418/418 +f 403/403/403 425/425/425 414/414/414 +f 414/414/414 410/410/410 403/403/403 +f 414/414/414 426/426/426 415/415/415 +f 414/414/414 427/427/427 426/426/426 +f 423/423/423 428/428/428 424/424/424 +f 428/428/428 429/429/429 424/424/424 +f 430/430/430 431/431/431 425/425/425 +f 425/425/425 432/432/432 430/430/430 +f 419/419/419 424/424/424 432/432/432 +f 432/432/432 406/406/406 419/419/419 +f 432/432/432 424/424/424 430/430/430 +f 403/403/403 406/406/406 432/432/432 +f 432/432/432 425/425/425 403/403/403 +f 431/431/431 433/433/433 427/427/427 +f 427/427/427 414/414/414 431/431/431 +f 419/419/419 406/406/406 405/405/405 +f 404/404/404 403/403/403 410/410/410 +f 434/434/434 433/433/433 435/435/435 +f 435/435/435 436/436/436 434/434/434 +f 437/437/437 436/436/436 438/438/438 +f 438/438/438 439/439/439 437/437/437 +f 387/387/387 380/380/380 440/440/440 +f 440/440/440 441/441/441 387/387/387 +f 441/441/441 437/437/437 439/439/439 +f 439/439/439 442/442/442 441/441/441 +f 392/392/392 387/387/387 441/441/441 +f 441/441/441 442/442/442 392/392/392 +f 441/441/441 440/440/440 443/443/443 +f 443/443/443 437/437/437 441/441/441 +f 443/443/443 444/444/444 434/434/434 +f 434/434/434 437/437/437 443/443/443 +f 437/437/437 434/434/434 436/436/436 +f 433/433/433 434/434/434 444/444/444 +f 444/444/444 445/445/445 433/433/433 +f 435/435/435 446/446/446 438/438/438 +f 438/438/438 436/436/436 435/435/435 +f 447/447/447 448/448/448 444/444/444 +f 444/444/444 443/443/443 447/447/447 +f 443/443/443 386/386/386 449/449/449 +f 449/449/449 447/447/447 443/443/443 +f 450/450/450 448/448/448 447/447/447 +f 451/451/451 450/450/450 447/447/447 +f 452/452/452 451/451/451 447/447/447 +f 449/449/449 452/452/452 447/447/447 +f 386/386/386 443/443/443 379/379/379 +f 379/379/379 382/382/382 386/386/386 +f 443/443/443 380/380/380 379/379/379 +f 443/443/443 440/440/440 380/380/380 +f 453/453/453 439/439/439 438/438/438 +f 438/438/438 454/454/454 453/453/453 +f 439/439/439 453/453/453 455/455/455 +f 455/455/455 400/400/400 439/439/439 +f 456/456/456 453/453/453 454/454/454 +f 457/457/457 453/453/453 456/456/456 +f 458/458/458 453/453/453 457/457/457 +f 455/455/455 453/453/453 458/458/458 +f 400/400/400 395/395/395 393/393/393 +f 393/393/393 439/439/439 400/400/400 +f 439/439/439 393/393/393 392/392/392 +f 439/439/439 392/392/392 442/442/442 +f 435/435/435 433/433/433 431/431/431 +f 431/431/431 430/430/430 435/435/435 +f 414/414/414 425/425/425 431/431/431 +f 430/430/430 424/424/424 429/429/429 +f 429/429/429 435/435/435 430/430/430 +f 390/390/390 388/388/388 387/387/387 +f 402/402/402 389/389/389 387/387/387 +f 435/435/435 429/429/429 446/446/446 +f 427/427/427 433/433/433 445/445/445 +f 459/459/459 460/460/460 461/461/461 +f 462/462/462 459/459/459 461/461/461 +f 463/463/463 462/462/462 461/461/461 +f 464/464/464 463/463/463 461/461/461 +f 461/461/461 465/465/465 464/464/464 +f 466/466/466 464/464/464 465/465/465 +f 467/467/467 466/466/466 465/465/465 +f 468/468/468 467/467/467 465/465/465 +f 469/469/469 470/470/470 471/471/471 +f 472/472/472 469/469/469 471/471/471 +f 473/473/473 472/472/472 471/471/471 +f 474/474/474 473/473/473 471/471/471 +f 471/471/471 475/475/475 474/474/474 +f 476/476/476 474/474/474 475/475/475 +f 477/477/477 476/476/476 475/475/475 +f 478/478/478 477/477/477 475/475/475 +f 479/479/479 478/478/478 475/475/475 +f 480/480/480 479/479/479 475/475/475 +f 475/475/475 461/461/461 480/480/480 +f 460/460/460 480/480/480 461/461/461 +f 481/481/481 482/482/482 483/483/483 +f 483/483/483 484/484/484 481/481/481 +f 485/485/485 486/486/486 487/487/487 +f 487/487/487 488/488/488 485/485/485 +f 487/487/487 486/486/486 489/489/489 +f 489/489/489 490/490/490 487/487/487 +f 491/491/491 492/492/492 493/493/493 +f 493/493/493 490/490/490 491/491/491 +f 489/489/489 494/494/494 490/490/490 +f 494/494/494 495/495/495 490/490/490 +f 495/495/495 496/496/496 490/490/490 +f 496/496/496 491/491/491 490/490/490 +f 497/497/497 481/481/481 484/484/484 +f 484/484/484 498/498/498 497/497/497 +f 499/499/499 500/500/500 498/498/498 +f 498/498/498 501/501/501 499/499/499 +f 502/502/502 497/497/497 498/498/498 +f 503/503/503 502/502/502 498/498/498 +f 504/504/504 503/503/503 498/498/498 +f 500/500/500 504/504/504 498/498/498 +f 482/482/482 468/468/468 465/465/465 +f 465/465/465 483/483/483 482/482/482 +f 470/470/470 485/485/485 488/488/488 +f 488/488/488 471/471/471 470/470/470 +f 475/475/475 471/471/471 465/465/465 +f 465/465/465 461/461/461 475/475/475 +f 471/471/471 488/488/488 483/483/483 +f 483/483/483 465/465/465 471/471/471 +f 488/488/488 487/487/487 484/484/484 +f 484/484/484 483/483/483 488/488/488 +f 490/490/490 493/493/493 505/505/505 +f 505/505/505 487/487/487 490/490/490 +f 501/501/501 498/498/498 484/484/484 +f 484/484/484 505/505/505 501/501/501 +f 484/484/484 487/487/487 505/505/505 +f 506/506/506 507/507/507 508/508/508 +f 508/508/508 509/509/509 506/506/506 +f 510/510/510 511/511/511 508/508/508 +f 508/508/508 507/507/507 510/510/510 +f 511/511/511 510/510/510 512/512/512 +f 512/512/512 513/513/513 511/511/511 +f 514/514/514 513/513/513 512/512/512 +f 512/512/512 515/515/515 514/514/514 +f 515/515/515 516/516/516 517/517/517 +f 517/517/517 514/514/514 515/515/515 +f 518/518/518 519/519/519 517/517/517 +f 517/517/517 516/516/516 518/518/518 +f 519/519/519 518/518/518 520/520/520 +f 520/520/520 521/521/521 519/519/519 +f 522/522/522 521/521/521 520/520/520 +f 520/520/520 523/523/523 522/522/522 +f 524/524/524 525/525/525 526/526/526 +f 526/526/526 527/527/527 524/524/524 +f 528/528/528 527/527/527 526/526/526 +f 526/526/526 529/529/529 528/528/528 +f 529/529/529 530/530/530 531/531/531 +f 531/531/531 528/528/528 529/529/529 +f 532/532/532 533/533/533 531/531/531 +f 531/531/531 530/530/530 532/532/532 +f 533/533/533 532/532/532 534/534/534 +f 534/534/534 535/535/535 533/533/533 +f 536/536/536 535/535/535 534/534/534 +f 534/534/534 537/537/537 536/536/536 +f 537/537/537 538/538/538 539/539/539 +f 539/539/539 536/536/536 537/537/537 +f 540/540/540 541/541/541 539/539/539 +f 539/539/539 538/538/538 540/540/540 +f 542/542/542 543/543/543 544/544/544 +f 544/544/544 545/545/545 542/542/542 +f 546/546/546 547/547/547 542/542/542 +f 542/542/542 545/545/545 546/546/546 +f 543/543/543 548/548/548 544/544/544 +f 549/549/549 550/550/550 551/551/551 +f 552/552/552 553/553/553 549/549/549 +f 549/549/549 551/551/551 552/552/552 +f 554/554/554 555/555/555 556/556/556 +f 556/556/556 557/557/557 554/554/554 +f 550/550/550 558/558/558 559/559/559 +f 560/560/560 561/561/561 553/553/553 +f 553/553/553 552/552/552 560/560/560 +f 556/556/556 562/562/562 557/557/557 +f 555/555/555 554/554/554 563/563/563 +f 553/553/553 561/561/561 562/562/562 +f 558/558/558 550/550/550 549/549/549 +f 564/564/564 565/565/565 566/566/566 +f 566/566/566 567/567/567 564/564/564 +f 567/567/567 568/568/568 569/569/569 +f 569/569/569 564/564/564 567/567/567 +f 567/567/567 566/566/566 570/570/570 +f 570/570/570 571/571/571 567/567/567 +f 571/571/571 572/572/572 568/568/568 +f 568/568/568 567/567/567 571/571/571 +f 564/564/564 569/569/569 573/573/573 +f 564/564/564 573/573/573 565/565/565 +f 546/546/546 557/557/557 562/562/562 +f 574/574/574 575/575/575 576/576/576 +f 577/577/577 578/578/578 579/579/579 +f 579/579/579 575/575/575 577/577/577 +f 580/580/580 581/581/581 578/578/578 +f 578/578/578 577/577/577 580/580/580 +f 582/582/582 571/571/571 583/583/583 +f 582/582/582 584/584/584 571/571/571 +f 585/585/585 574/574/574 571/571/571 +f 571/571/571 584/584/584 585/585/585 +f 586/586/586 575/575/575 574/574/574 +f 574/574/574 585/585/585 586/586/586 +f 547/547/547 561/561/561 560/560/560 +f 560/560/560 587/587/587 547/547/547 +f 546/546/546 562/562/562 561/561/561 +f 561/561/561 547/547/547 546/546/546 +f 571/571/571 570/570/570 583/583/583 +f 572/572/572 571/571/571 574/574/574 +f 574/574/574 576/576/576 572/572/572 +f 558/558/558 563/563/563 559/559/559 +f 575/575/575 579/579/579 576/576/576 +f 588/588/588 589/589/589 590/590/590 +f 590/590/590 591/591/591 588/588/588 +f 592/592/592 593/593/593 594/594/594 +f 594/594/594 595/595/595 592/592/592 +f 595/595/595 594/594/594 596/596/596 +f 596/596/596 597/597/597 595/595/595 +f 591/591/591 590/590/590 592/592/592 +f 592/592/592 595/595/595 591/591/591 +f 598/598/598 591/591/591 595/595/595 +f 595/595/595 597/597/597 598/598/598 +f 599/599/599 597/597/597 596/596/596 +f 596/596/596 600/600/600 599/599/599 +f 601/601/601 602/602/602 603/603/603 +f 603/603/603 604/604/604 601/601/601 +f 602/602/602 605/605/605 606/606/606 +f 606/606/606 603/603/603 602/602/602 +f 601/601/601 607/607/607 599/599/599 +f 599/599/599 602/602/602 601/601/601 +f 599/599/599 600/600/600 605/605/605 +f 605/605/605 602/602/602 599/599/599 +f 607/607/607 598/598/598 597/597/597 +f 597/597/597 599/599/599 607/607/607 +f 608/608/608 609/609/609 610/610/610 +f 610/610/610 611/611/611 608/608/608 +f 588/588/588 612/612/612 613/613/613 +f 613/613/613 589/589/589 588/588/588 +f 614/614/614 598/598/598 615/615/615 +f 588/588/588 591/591/591 614/614/614 +f 616/616/616 588/588/588 614/614/614 +f 607/607/607 617/617/617 615/615/615 +f 615/615/615 598/598/598 607/607/607 +f 618/618/618 619/619/619 615/615/615 +f 607/607/607 601/601/601 620/620/620 +f 620/620/620 617/617/617 607/607/607 +f 621/621/621 618/618/618 615/615/615 +f 615/615/615 617/617/617 621/621/621 +f 620/620/620 601/601/601 622/622/622 +f 621/621/621 617/617/617 620/620/620 +f 620/620/620 623/623/623 621/621/621 +f 622/622/622 624/624/624 620/620/620 +f 610/610/610 609/609/609 625/625/625 +f 626/626/626 611/611/611 603/603/603 +f 603/603/603 606/606/606 626/626/626 +f 610/610/610 604/604/604 603/603/603 +f 603/603/603 611/611/611 610/610/610 +f 622/622/622 604/604/604 610/610/610 +f 625/625/625 622/622/622 610/610/610 +f 627/627/627 628/628/628 629/629/629 +f 630/630/630 628/628/628 631/631/631 +f 632/632/632 629/629/629 628/628/628 +f 633/633/633 634/634/634 635/635/635 +f 635/635/635 636/636/636 633/633/633 +f 637/637/637 634/634/634 633/633/633 +f 633/633/633 638/638/638 637/637/637 +f 630/630/630 638/638/638 633/633/633 +f 633/633/633 628/628/628 630/630/630 +f 628/628/628 633/633/633 636/636/636 +f 636/636/636 632/632/632 628/628/628 +f 639/639/639 640/640/640 631/631/631 +f 631/631/631 628/628/628 639/639/639 +f 627/627/627 641/641/641 639/639/639 +f 639/639/639 628/628/628 627/627/627 +f 642/642/642 643/643/643 644/644/644 +f 644/644/644 645/645/645 642/642/642 +f 646/646/646 647/647/647 648/648/648 +f 648/648/648 649/649/649 646/646/646 +f 650/650/650 645/645/645 644/644/644 +f 644/644/644 651/651/651 650/650/650 +f 645/645/645 650/650/650 652/652/652 +f 652/652/652 646/646/646 645/645/645 +f 653/653/653 642/642/642 645/645/645 +f 645/645/645 646/646/646 653/653/653 +f 649/649/649 654/654/654 655/655/655 +f 655/655/655 646/646/646 649/649/649 +f 655/655/655 653/653/653 646/646/646 +f 652/652/652 647/647/647 646/646/646 +f 656/656/656 657/657/657 634/634/634 +f 634/634/634 637/637/637 656/656/656 +f 658/658/658 659/659/659 634/634/634 +f 634/634/634 657/657/657 658/658/658 +f 660/660/660 658/658/658 657/657/657 +f 657/657/657 656/656/656 660/660/660 +f 659/659/659 635/635/635 634/634/634 +f 661/661/661 662/662/662 663/663/663 +f 663/663/663 664/664/664 661/661/661 +f 665/665/665 666/666/666 649/649/649 +f 649/649/649 648/648/648 665/665/665 +f 666/666/666 667/667/667 649/649/649 +f 668/668/668 667/667/667 666/666/666 +f 666/666/666 665/665/665 668/668/668 +f 669/669/669 670/670/670 671/671/671 +f 671/671/671 672/672/672 669/669/669 +f 673/673/673 674/674/674 675/675/675 +f 673/673/673 630/630/630 631/631/631 +f 675/675/675 676/676/676 673/673/673 +f 677/677/677 678/678/678 679/679/679 +f 679/679/679 680/680/680 677/677/677 +f 637/637/637 638/638/638 677/677/677 +f 677/677/677 680/680/680 637/637/637 +f 630/630/630 673/673/673 677/677/677 +f 677/677/677 638/638/638 630/630/630 +f 673/673/673 676/676/676 678/678/678 +f 678/678/678 677/677/677 673/673/673 +f 681/681/681 673/673/673 631/631/631 +f 631/631/631 640/640/640 681/681/681 +f 674/674/674 673/673/673 681/681/681 +f 681/681/681 682/682/682 674/674/674 +f 683/683/683 684/684/684 685/685/685 +f 685/685/685 686/686/686 683/683/683 +f 687/687/687 688/688/688 648/648/648 +f 648/648/648 647/647/647 687/687/687 +f 650/650/650 651/651/651 685/685/685 +f 685/685/685 684/684/684 650/650/650 +f 684/684/684 687/687/687 652/652/652 +f 652/652/652 650/650/650 684/684/684 +f 689/689/689 687/687/687 684/684/684 +f 684/684/684 683/683/683 689/689/689 +f 688/688/688 687/687/687 690/690/690 +f 690/690/690 691/691/691 688/688/688 +f 689/689/689 690/690/690 687/687/687 +f 647/647/647 652/652/652 687/687/687 +f 656/656/656 637/637/637 680/680/680 +f 680/680/680 692/692/692 656/656/656 +f 693/693/693 692/692/692 680/680/680 +f 680/680/680 694/694/694 693/693/693 +f 660/660/660 656/656/656 692/692/692 +f 692/692/692 693/693/693 660/660/660 +f 694/694/694 680/680/680 679/679/679 +f 661/661/661 664/664/664 695/695/695 +f 695/695/695 696/696/696 661/661/661 +f 665/665/665 648/648/648 688/688/688 +f 688/688/688 697/697/697 665/665/665 +f 698/698/698 697/697/697 688/688/688 +f 668/668/668 665/665/665 697/697/697 +f 697/697/697 698/698/698 668/668/668 +f 669/669/669 672/672/672 699/699/699 +f 699/699/699 700/700/700 669/669/669 +f 701/701/701 702/702/702 619/619/619 +f 619/619/619 618/618/618 701/701/701 +f 701/701/701 612/612/612 616/616/616 +f 616/616/616 702/702/702 701/701/701 +f 619/619/619 614/614/614 615/615/615 +f 609/609/609 703/703/703 625/625/625 +f 704/704/704 623/623/623 703/703/703 +f 609/609/609 704/704/704 703/703/703 +f 705/705/705 611/611/611 626/626/626 +f 706/706/706 608/608/608 611/611/611 +f 705/705/705 706/706/706 611/611/611 +f 707/707/707 708/708/708 577/577/577 +f 708/708/708 709/709/709 580/580/580 +f 580/580/580 577/577/577 708/708/708 +f 586/586/586 710/710/710 575/575/575 +f 710/710/710 707/707/707 577/577/577 +f 577/577/577 575/575/575 710/710/710 +f 612/612/612 588/588/588 616/616/616 +f 703/703/703 623/623/623 620/620/620 +f 620/620/620 624/624/624 703/703/703 +f 549/549/549 553/553/553 562/562/562 +f 562/562/562 556/556/556 549/549/549 +f 591/591/591 598/598/598 614/614/614 +f 558/558/558 549/549/549 556/556/556 +f 556/556/556 555/555/555 558/558/558 +f 563/563/563 558/558/558 555/555/555 +f 601/601/601 604/604/604 622/622/622 +f 711/711/711 712/712/712 713/713/713 +f 713/713/713 714/714/714 711/711/711 +f 715/715/715 712/712/712 711/711/711 +f 711/711/711 716/716/716 715/715/715 +f 717/717/717 714/714/714 713/713/713 +f 550/550/550 718/718/718 551/551/551 +f 552/552/552 551/551/551 718/718/718 +f 718/718/718 719/719/719 552/552/552 +f 720/720/720 721/721/721 722/722/722 +f 722/722/722 723/723/723 720/720/720 +f 724/724/724 550/550/550 559/559/559 +f 560/560/560 552/552/552 719/719/719 +f 719/719/719 725/725/725 560/560/560 +f 726/726/726 722/722/722 721/721/721 +f 720/720/720 723/723/723 727/727/727 +f 725/725/725 719/719/719 726/726/726 +f 550/550/550 724/724/724 718/718/718 +f 728/728/728 729/729/729 730/730/730 +f 730/730/730 731/731/731 728/728/728 +f 729/729/729 728/728/728 732/732/732 +f 732/732/732 733/733/733 729/729/729 +f 729/729/729 734/734/734 735/735/735 +f 735/735/735 730/730/730 729/729/729 +f 734/734/734 729/729/729 733/733/733 +f 733/733/733 736/736/736 734/734/734 +f 732/732/732 728/728/728 737/737/737 +f 737/737/737 728/728/728 731/731/731 +f 721/721/721 715/715/715 726/726/726 +f 738/738/738 739/739/739 740/740/740 +f 741/741/741 738/738/738 742/742/742 +f 742/742/742 743/743/743 741/741/741 +f 580/580/580 741/741/741 743/743/743 +f 743/743/743 581/581/581 580/580/580 +f 734/734/734 582/582/582 583/583/583 +f 584/584/584 582/582/582 734/734/734 +f 585/585/585 584/584/584 734/734/734 +f 734/734/734 739/739/739 585/585/585 +f 586/586/586 585/585/585 739/739/739 +f 739/739/739 738/738/738 586/586/586 +f 716/716/716 587/587/587 560/560/560 +f 560/560/560 725/725/725 716/716/716 +f 715/715/715 716/716/716 725/725/725 +f 725/725/725 726/726/726 715/715/715 +f 735/735/735 734/734/734 583/583/583 +f 734/734/734 736/736/736 739/739/739 +f 740/740/740 739/739/739 736/736/736 +f 727/727/727 724/724/724 559/559/559 +f 742/742/742 738/738/738 740/740/740 +f 744/744/744 745/745/745 746/746/746 +f 746/746/746 747/747/747 744/744/744 +f 748/748/748 749/749/749 594/594/594 +f 594/594/594 593/593/593 748/748/748 +f 749/749/749 750/750/750 596/596/596 +f 596/596/596 594/594/594 749/749/749 +f 745/745/745 749/749/749 748/748/748 +f 748/748/748 746/746/746 745/745/745 +f 751/751/751 750/750/750 749/749/749 +f 749/749/749 745/745/745 751/751/751 +f 752/752/752 600/600/600 596/596/596 +f 596/596/596 750/750/750 752/752/752 +f 753/753/753 754/754/754 755/755/755 +f 755/755/755 756/756/756 753/753/753 +f 756/756/756 755/755/755 606/606/606 +f 606/606/606 605/605/605 756/756/756 +f 753/753/753 756/756/756 752/752/752 +f 752/752/752 757/757/757 753/753/753 +f 752/752/752 756/756/756 605/605/605 +f 605/605/605 600/600/600 752/752/752 +f 757/757/757 752/752/752 750/750/750 +f 750/750/750 751/751/751 757/757/757 +f 608/608/608 758/758/758 759/759/759 +f 759/759/759 609/609/609 608/608/608 +f 744/744/744 747/747/747 613/613/613 +f 613/613/613 612/612/612 744/744/744 +f 751/751/751 760/760/760 761/761/761 +f 745/745/745 744/744/744 760/760/760 +f 744/744/744 762/762/762 760/760/760 +f 757/757/757 751/751/751 761/761/761 +f 761/761/761 763/763/763 757/757/757 +f 764/764/764 618/618/618 761/761/761 +f 757/757/757 763/763/763 765/765/765 +f 765/765/765 753/753/753 757/757/757 +f 621/621/621 763/763/763 761/761/761 +f 761/761/761 618/618/618 621/621/621 +f 753/753/753 765/765/765 766/766/766 +f 621/621/621 623/623/623 765/765/765 +f 765/765/765 763/763/763 621/621/621 +f 767/767/767 766/766/766 765/765/765 +f 609/609/609 759/759/759 768/768/768 +f 626/626/626 606/606/606 755/755/755 +f 755/755/755 758/758/758 626/626/626 +f 759/759/759 758/758/758 755/755/755 +f 755/755/755 754/754/754 759/759/759 +f 754/754/754 766/766/766 759/759/759 +f 766/766/766 768/768/768 759/759/759 +f 769/769/769 770/770/770 771/771/771 +f 769/769/769 772/772/772 773/773/773 +f 771/771/771 774/774/774 769/769/769 +f 775/775/775 776/776/776 777/777/777 +f 777/777/777 778/778/778 775/775/775 +f 779/779/779 780/780/780 775/775/775 +f 775/775/775 778/778/778 779/779/779 +f 772/772/772 769/769/769 775/775/775 +f 775/775/775 780/780/780 772/772/772 +f 769/769/769 774/774/774 776/776/776 +f 776/776/776 775/775/775 769/769/769 +f 781/781/781 769/769/769 773/773/773 +f 773/773/773 782/782/782 781/781/781 +f 770/770/770 769/769/769 781/781/781 +f 781/781/781 783/783/783 770/770/770 +f 784/784/784 785/785/785 786/786/786 +f 786/786/786 787/787/787 784/784/784 +f 788/788/788 789/789/789 790/790/790 +f 790/790/790 791/791/791 788/788/788 +f 792/792/792 793/793/793 786/786/786 +f 786/786/786 785/785/785 792/792/792 +f 785/785/785 788/788/788 794/794/794 +f 794/794/794 792/792/792 785/785/785 +f 795/795/795 788/788/788 785/785/785 +f 785/785/785 784/784/784 795/795/795 +f 789/789/789 788/788/788 796/796/796 +f 796/796/796 797/797/797 789/789/789 +f 795/795/795 796/796/796 788/788/788 +f 791/791/791 794/794/794 788/788/788 +f 798/798/798 779/779/779 778/778/778 +f 778/778/778 799/799/799 798/798/798 +f 800/800/800 799/799/799 778/778/778 +f 778/778/778 801/801/801 800/800/800 +f 802/802/802 798/798/798 799/799/799 +f 799/799/799 800/800/800 802/802/802 +f 777/777/777 801/801/801 778/778/778 +f 803/803/803 804/804/804 805/805/805 +f 805/805/805 806/806/806 803/803/803 +f 807/807/807 790/790/790 789/789/789 +f 789/789/789 808/808/808 807/807/807 +f 809/809/809 808/808/808 789/789/789 +f 810/810/810 807/807/807 808/808/808 +f 808/808/808 809/809/809 810/810/810 +f 811/811/811 812/812/812 813/813/813 +f 813/813/813 814/814/814 811/811/811 +f 815/815/815 816/816/816 817/817/817 +f 772/772/772 816/816/816 773/773/773 +f 818/818/818 817/817/817 816/816/816 +f 819/819/819 820/820/820 821/821/821 +f 821/821/821 822/822/822 819/819/819 +f 779/779/779 820/820/820 819/819/819 +f 819/819/819 780/780/780 779/779/779 +f 772/772/772 780/780/780 819/819/819 +f 819/819/819 816/816/816 772/772/772 +f 816/816/816 819/819/819 822/822/822 +f 822/822/822 818/818/818 816/816/816 +f 823/823/823 782/782/782 773/773/773 +f 773/773/773 816/816/816 823/823/823 +f 815/815/815 824/824/824 823/823/823 +f 823/823/823 816/816/816 815/815/815 +f 825/825/825 826/826/826 827/827/827 +f 827/827/827 828/828/828 825/825/825 +f 829/829/829 791/791/791 790/790/790 +f 790/790/790 830/830/830 829/829/829 +f 792/792/792 828/828/828 827/827/827 +f 827/827/827 793/793/793 792/792/792 +f 828/828/828 792/792/792 794/794/794 +f 794/794/794 829/829/829 828/828/828 +f 831/831/831 825/825/825 828/828/828 +f 828/828/828 829/829/829 831/831/831 +f 830/830/830 832/832/832 833/833/833 +f 833/833/833 829/829/829 830/830/830 +f 833/833/833 831/831/831 829/829/829 +f 794/794/794 791/791/791 829/829/829 +f 798/798/798 834/834/834 820/820/820 +f 820/820/820 779/779/779 798/798/798 +f 835/835/835 836/836/836 820/820/820 +f 820/820/820 834/834/834 835/835/835 +f 802/802/802 835/835/835 834/834/834 +f 834/834/834 798/798/798 802/802/802 +f 820/820/820 836/836/836 821/821/821 +f 803/803/803 837/837/837 838/838/838 +f 838/838/838 804/804/804 803/803/803 +f 807/807/807 839/839/839 830/830/830 +f 830/830/830 790/790/790 807/807/807 +f 839/839/839 840/840/840 830/830/830 +f 810/810/810 840/840/840 839/839/839 +f 839/839/839 807/807/807 810/810/810 +f 811/811/811 841/841/841 842/842/842 +f 842/842/842 812/812/812 811/811/811 +f 701/701/701 618/618/618 764/764/764 +f 764/764/764 843/843/843 701/701/701 +f 701/701/701 843/843/843 762/762/762 +f 762/762/762 612/612/612 701/701/701 +f 760/760/760 764/764/764 761/761/761 +f 844/844/844 609/609/609 768/768/768 +f 623/623/623 704/704/704 844/844/844 +f 704/704/704 609/609/609 844/844/844 +f 758/758/758 705/705/705 626/626/626 +f 845/845/845 846/846/846 705/705/705 +f 705/705/705 847/847/847 845/845/845 +f 848/848/848 849/849/849 846/846/846 +f 846/846/846 845/845/845 848/848/848 +f 848/848/848 850/850/850 851/851/851 +f 851/851/851 849/849/849 848/848/848 +f 850/850/850 852/852/852 853/853/853 +f 853/853/853 851/851/851 850/850/850 +f 854/854/854 855/855/855 853/853/853 +f 853/853/853 852/852/852 854/854/854 +f 855/855/855 854/854/854 856/856/856 +f 856/856/856 857/857/857 855/855/855 +f 858/858/858 856/856/856 854/854/854 +f 608/608/608 847/847/847 758/758/758 +f 847/847/847 705/705/705 758/758/758 +f 859/859/859 860/860/860 741/741/741 +f 859/859/859 741/741/741 580/580/580 +f 580/580/580 709/709/709 859/859/859 +f 861/861/861 586/586/586 738/738/738 +f 861/861/861 738/738/738 741/741/741 +f 741/741/741 860/860/860 861/861/861 +f 744/744/744 612/612/612 762/762/762 +f 844/844/844 767/767/767 765/765/765 +f 765/765/765 623/623/623 844/844/844 +f 718/718/718 722/722/722 726/726/726 +f 726/726/726 719/719/719 718/718/718 +f 751/751/751 745/745/745 760/760/760 +f 724/724/724 723/723/723 722/722/722 +f 722/722/722 718/718/718 724/724/724 +f 724/724/724 727/727/727 723/723/723 +f 754/754/754 753/753/753 766/766/766 +f 862/862/862 863/863/863 864/864/864 +f 864/864/864 865/865/865 862/862/862 +f 866/866/866 862/862/862 867/867/867 +f 865/865/865 867/867/867 862/862/862 +f 868/868/868 863/863/863 862/862/862 +f 862/862/862 866/866/866 868/868/868 +f 863/863/863 869/869/869 870/870/870 +f 870/870/870 864/864/864 863/863/863 +f 871/871/871 872/872/872 863/863/863 +f 868/868/868 871/871/871 863/863/863 +f 873/873/873 874/874/874 869/869/869 +f 869/869/869 875/875/875 873/873/873 +f 869/869/869 874/874/874 876/876/876 +f 876/876/876 870/870/870 869/869/869 +f 872/872/872 875/875/875 869/869/869 +f 872/872/872 869/869/869 863/863/863 +f 873/873/873 877/877/877 874/874/874 +f 878/878/878 879/879/879 876/876/876 +f 876/876/876 874/874/874 878/878/878 +f 877/877/877 880/880/880 878/878/878 +f 878/878/878 874/874/874 877/877/877 +f 878/878/878 881/881/881 882/882/882 +f 882/882/882 879/879/879 878/878/878 +f 880/880/880 881/881/881 878/878/878 +f 880/880/880 883/883/883 884/884/884 +f 884/884/884 881/881/881 880/880/880 +f 881/881/881 885/885/885 886/886/886 +f 881/881/881 886/886/886 882/882/882 +f 881/881/881 884/884/884 887/887/887 +f 887/887/887 885/885/885 881/881/881 +f 884/884/884 883/883/883 888/888/888 +f 889/889/889 887/887/887 890/890/890 +f 891/891/891 892/892/892 893/893/893 +f 894/894/894 895/895/895 884/884/884 +f 884/884/884 888/888/888 894/894/894 +f 893/893/893 892/892/892 896/896/896 +f 897/897/897 898/898/898 899/899/899 +f 899/899/899 900/900/900 897/897/897 +f 901/901/901 902/902/902 903/903/903 +f 903/903/903 904/904/904 901/901/901 +f 905/905/905 892/892/892 906/906/906 +f 906/906/906 892/892/892 907/907/907 +f 891/891/891 908/908/908 907/907/907 +f 907/907/907 892/892/892 891/891/891 +f 897/897/897 909/909/909 910/910/910 +f 910/910/910 898/898/898 897/897/897 +f 910/910/910 909/909/909 911/911/911 +f 911/911/911 904/904/904 910/910/910 +f 910/910/910 895/895/895 894/894/894 +f 894/894/894 898/898/898 910/910/910 +f 903/903/903 889/889/889 890/890/890 +f 890/890/890 904/904/904 903/903/903 +f 890/890/890 887/887/887 884/884/884 +f 884/884/884 895/895/895 890/890/890 +f 890/890/890 895/895/895 910/910/910 +f 910/910/910 904/904/904 890/890/890 +f 888/888/888 912/912/912 894/894/894 +f 912/912/912 913/913/913 898/898/898 +f 898/898/898 894/894/894 912/912/912 +f 899/899/899 898/898/898 913/913/913 +f 913/913/913 914/914/914 899/899/899 +f 901/901/901 904/904/904 911/911/911 +f 911/911/911 915/915/915 901/901/901 +f 905/905/905 916/916/916 896/896/896 +f 896/896/896 892/892/892 905/905/905 +f 917/917/917 908/908/908 891/891/891 +f 918/918/918 919/919/919 920/920/920 +f 901/901/901 921/921/921 922/922/922 +f 922/922/922 902/902/902 901/901/901 +f 923/923/923 893/893/893 918/918/918 +f 918/918/918 924/924/924 923/923/923 +f 923/923/923 924/924/924 925/925/925 +f 923/923/923 925/925/925 926/926/926 +f 926/926/926 927/927/927 923/923/923 +f 901/901/901 915/915/915 921/921/921 +f 928/928/928 902/902/902 922/922/922 +f 929/929/929 928/928/928 930/930/930 +f 930/930/930 931/931/931 929/929/929 +f 931/931/931 926/926/926 932/932/932 +f 917/917/917 891/891/891 893/893/893 +f 893/893/893 923/923/923 917/917/917 +f 923/923/923 933/933/933 917/917/917 +f 921/921/921 933/933/933 923/923/923 +f 923/923/923 927/927/927 921/921/921 +f 928/928/928 922/922/922 930/930/930 +f 930/930/930 922/922/922 921/921/921 +f 921/921/921 927/927/927 930/930/930 +f 920/920/920 934/934/934 924/924/924 +f 924/924/924 918/918/918 920/920/920 +f 934/934/934 935/935/935 925/925/925 +f 925/925/925 924/924/924 934/934/934 +f 925/925/925 935/935/935 932/932/932 +f 932/932/932 926/926/926 925/925/925 +f 931/931/931 930/930/930 927/927/927 +f 927/927/927 926/926/926 931/931/931 +f 931/931/931 936/936/936 929/929/929 +f 908/908/908 917/917/917 933/933/933 +f 893/893/893 896/896/896 937/937/937 +f 937/937/937 938/938/938 893/893/893 +f 937/937/937 896/896/896 916/916/916 +f 938/938/938 939/939/939 940/940/940 +f 938/938/938 941/941/941 942/942/942 +f 942/942/942 939/939/939 938/938/938 +f 937/937/937 916/916/916 900/900/900 +f 900/900/900 943/943/943 937/937/937 +f 944/944/944 914/914/914 945/945/945 +f 945/945/945 946/946/946 941/941/941 +f 941/941/941 944/944/944 945/945/945 +f 944/944/944 943/943/943 899/899/899 +f 899/899/899 914/914/914 944/944/944 +f 940/940/940 918/918/918 893/893/893 +f 893/893/893 938/938/938 940/940/940 +f 938/938/938 937/937/937 943/943/943 +f 943/943/943 944/944/944 941/941/941 +f 941/941/941 938/938/938 943/943/943 +f 947/947/947 942/942/942 941/941/941 +f 941/941/941 946/946/946 947/947/947 +f 948/948/948 949/949/949 940/940/940 +f 940/940/940 939/939/939 948/948/948 +f 950/950/950 948/948/948 939/939/939 +f 939/939/939 942/942/942 950/950/950 +f 918/918/918 951/951/951 919/919/919 +f 949/949/949 951/951/951 918/918/918 +f 918/918/918 940/940/940 949/949/949 +f 947/947/947 950/950/950 942/942/942 +f 897/897/897 900/900/900 916/916/916 +f 916/916/916 905/905/905 897/897/897 +f 911/911/911 907/907/907 908/908/908 +f 908/908/908 915/915/915 911/911/911 +f 905/905/905 906/906/906 909/909/909 +f 909/909/909 897/897/897 905/905/905 +f 909/909/909 906/906/906 907/907/907 +f 907/907/907 911/911/911 909/909/909 +f 915/915/915 908/908/908 933/933/933 +f 933/933/933 921/921/921 915/915/915 +f 900/900/900 899/899/899 943/943/943 +f 931/931/931 932/932/932 936/936/936 +f 952/952/952 953/953/953 954/954/954 +f 954/954/954 955/955/955 952/952/952 +f 956/956/956 957/957/957 952/952/952 +f 953/953/953 952/952/952 957/957/957 +f 958/958/958 956/956/956 952/952/952 +f 952/952/952 955/955/955 958/958/958 +f 955/955/955 954/954/954 959/959/959 +f 959/959/959 960/960/960 955/955/955 +f 961/961/961 955/955/955 962/962/962 +f 958/958/958 955/955/955 961/961/961 +f 963/963/963 964/964/964 960/960/960 +f 960/960/960 965/965/965 963/963/963 +f 960/960/960 959/959/959 966/966/966 +f 966/966/966 965/965/965 960/960/960 +f 962/962/962 960/960/960 964/964/964 +f 962/962/962 955/955/955 960/960/960 +f 963/963/963 965/965/965 967/967/967 +f 968/968/968 965/965/965 966/966/966 +f 966/966/966 969/969/969 968/968/968 +f 967/967/967 965/965/965 968/968/968 +f 968/968/968 970/970/970 967/967/967 +f 968/968/968 969/969/969 971/971/971 +f 971/971/971 972/972/972 968/968/968 +f 970/970/970 968/968/968 972/972/972 +f 970/970/970 972/972/972 973/973/973 +f 973/973/973 974/974/974 970/970/970 +f 972/972/972 975/975/975 976/976/976 +f 972/972/972 971/971/971 975/975/975 +f 972/972/972 976/976/976 977/977/977 +f 977/977/977 973/973/973 972/972/972 +f 973/973/973 978/978/978 974/974/974 +f 979/979/979 980/980/980 977/977/977 +f 973/973/973 981/981/981 978/978/978 +f 982/982/982 983/983/983 984/984/984 +f 985/985/985 986/986/986 981/981/981 +f 981/981/981 973/973/973 985/985/985 +f 982/982/982 984/984/984 980/980/980 +f 980/980/980 979/979/979 982/982/982 +f 977/977/977 980/980/980 973/973/973 +f 980/980/980 984/984/984 985/985/985 +f 985/985/985 973/973/973 980/980/980 +f 978/978/978 981/981/981 987/987/987 +f 987/987/987 981/981/981 986/986/986 +f 986/986/986 988/988/988 987/987/987 +f 986/986/986 989/989/989 988/988/988 +f 990/990/990 991/991/991 992/992/992 +f 984/984/984 983/983/983 993/993/993 +f 994/994/994 995/995/995 990/990/990 +f 990/990/990 996/996/996 994/994/994 +f 994/994/994 997/997/997 995/995/995 +f 993/993/993 997/997/997 994/994/994 +f 994/994/994 996/996/996 985/985/985 +f 985/985/985 984/984/984 994/994/994 +f 998/998/998 993/993/993 983/983/983 +f 993/993/993 998/998/998 999/999/999 +f 991/991/991 990/990/990 995/995/995 +f 995/995/995 1000/1000/1000 991/991/991 +f 1000/1000/1000 995/995/995 997/997/997 +f 997/997/997 1001/1001/1001 1000/1000/1000 +f 997/997/997 993/993/993 1002/1002/1002 +f 1002/1002/1002 1001/1001/1001 997/997/997 +f 993/993/993 999/999/999 1003/1003/1003 +f 1004/1004/1004 1005/1005/1005 1006/1006/1006 +f 1004/1004/1004 1006/1006/1006 1007/1007/1007 +f 1004/1004/1004 986/986/986 985/985/985 +f 985/985/985 996/996/996 1004/1004/1004 +f 1007/1007/1007 1008/1008/1008 989/989/989 +f 1008/1008/1008 1007/1007/1007 1009/1009/1009 +f 1007/1007/1007 986/986/986 1004/1004/1004 +f 1005/1005/1005 1004/1004/1004 996/996/996 +f 996/996/996 990/990/990 1005/1005/1005 +f 1009/1009/1009 1007/1007/1007 1010/1010/1010 +f 1011/1011/1011 1006/1006/1006 1005/1005/1005 +f 1005/1005/1005 1012/1012/1012 1011/1011/1011 +f 1013/1013/1013 1007/1007/1007 1006/1006/1006 +f 1006/1006/1006 1011/1011/1011 1013/1013/1013 +f 990/990/990 992/992/992 1014/1014/1014 +f 1012/1012/1012 1005/1005/1005 990/990/990 +f 990/990/990 1014/1014/1014 1012/1012/1012 +f 1010/1010/1010 1007/1007/1007 1013/1013/1013 +f 993/993/993 1003/1003/1003 1002/1002/1002 +f 984/984/984 993/993/993 994/994/994 +f 1007/1007/1007 989/989/989 986/986/986 +f 1015/1015/1015 1016/1016/1016 1017/1017/1017 +f 1017/1017/1017 1018/1018/1018 1015/1015/1015 +f 1019/1019/1019 1020/1020/1020 1021/1021/1021 +f 1021/1021/1021 1022/1022/1022 1019/1019/1019 +f 1019/1019/1019 1022/1022/1022 1023/1023/1023 +f 1023/1023/1023 1024/1024/1024 1019/1019/1019 +f 1021/1021/1021 1020/1020/1020 1016/1016/1016 +f 1016/1016/1016 1025/1025/1025 1021/1021/1021 +f 1026/1026/1026 1025/1025/1025 1016/1016/1016 +f 1016/1016/1016 1015/1015/1015 1026/1026/1026 +f 1027/1027/1027 1028/1028/1028 1029/1029/1029 +f 1029/1029/1029 1030/1030/1030 1027/1027/1027 +f 1031/1031/1031 1029/1029/1029 1028/1028/1028 +f 1028/1028/1028 1032/1032/1032 1031/1031/1031 +f 1033/1033/1033 1027/1027/1027 1030/1030/1030 +f 1030/1030/1030 1034/1034/1034 1033/1033/1033 +f 1035/1035/1035 1033/1033/1033 1034/1034/1034 +f 1034/1034/1034 1036/1036/1036 1035/1035/1035 +f 1025/1025/1025 1037/1037/1037 1038/1038/1038 +f 1038/1038/1038 1021/1021/1021 1025/1025/1025 +f 1039/1039/1039 1037/1037/1037 1025/1025/1025 +f 1025/1025/1025 1026/1026/1026 1039/1039/1039 +f 1040/1040/1040 1041/1041/1041 1032/1032/1032 +f 1032/1032/1032 1042/1042/1042 1040/1040/1040 +f 1043/1043/1043 1028/1028/1028 1027/1027/1027 +f 1027/1027/1027 1044/1044/1044 1043/1043/1043 +f 1045/1045/1045 1044/1044/1044 1027/1027/1027 +f 1027/1027/1027 1033/1033/1033 1045/1045/1045 +f 1035/1035/1035 1046/1046/1046 1045/1045/1045 +f 1045/1045/1045 1033/1033/1033 1035/1035/1035 +f 1021/1021/1021 1038/1038/1038 1022/1022/1022 +f 1024/1024/1024 1047/1047/1047 1048/1048/1048 +f 1048/1048/1048 1019/1019/1019 1024/1024/1024 +f 1048/1048/1048 1049/1049/1049 1020/1020/1020 +f 1020/1020/1020 1019/1019/1019 1048/1048/1048 +f 1016/1016/1016 1020/1020/1020 1049/1049/1049 +f 1049/1049/1049 1017/1017/1017 1016/1016/1016 +f 1050/1050/1050 1017/1017/1017 1049/1049/1049 +f 1049/1049/1049 1051/1051/1051 1050/1050/1050 +f 1051/1051/1051 1049/1049/1049 1048/1048/1048 +f 1052/1052/1052 1018/1018/1018 1017/1017/1017 +f 1017/1017/1017 1050/1050/1050 1052/1052/1052 +f 1053/1053/1053 1029/1029/1029 1031/1031/1031 +f 1031/1031/1031 1054/1054/1054 1053/1053/1053 +f 1055/1055/1055 1030/1030/1030 1029/1029/1029 +f 1029/1029/1029 1053/1053/1053 1055/1055/1055 +f 1055/1055/1055 1056/1056/1056 1034/1034/1034 +f 1034/1034/1034 1030/1030/1030 1055/1055/1055 +f 1056/1056/1056 1057/1057/1057 1036/1036/1036 +f 1036/1036/1036 1034/1034/1034 1056/1056/1056 +f 1058/1058/1058 1053/1053/1053 1054/1054/1054 +f 1054/1054/1054 1059/1059/1059 1058/1058/1058 +f 1060/1060/1060 1055/1055/1055 1053/1053/1053 +f 1053/1053/1053 1058/1058/1058 1060/1060/1060 +f 1055/1055/1055 1060/1060/1060 1061/1061/1061 +f 1061/1061/1061 1056/1056/1056 1055/1055/1055 +f 1062/1062/1062 1057/1057/1057 1056/1056/1056 +f 1056/1056/1056 1061/1061/1061 1062/1062/1062 +f 1063/1063/1063 1058/1058/1058 1059/1059/1059 +f 1059/1059/1059 1064/1064/1064 1063/1063/1063 +f 1058/1058/1058 1063/1063/1063 1065/1065/1065 +f 1065/1065/1065 1060/1060/1060 1058/1058/1058 +f 1060/1060/1060 1065/1065/1065 1066/1066/1066 +f 1066/1066/1066 1061/1061/1061 1060/1060/1060 +f 1061/1061/1061 1066/1066/1066 1067/1067/1067 +f 1067/1067/1067 1062/1062/1062 1061/1061/1061 +f 1068/1068/1068 1069/1069/1069 1042/1042/1042 +f 1042/1042/1042 1043/1043/1043 1068/1068/1068 +f 1044/1044/1044 1070/1070/1070 1068/1068/1068 +f 1068/1068/1068 1043/1043/1043 1044/1044/1044 +f 1045/1045/1045 1071/1071/1071 1070/1070/1070 +f 1070/1070/1070 1044/1044/1044 1045/1045/1045 +f 1046/1046/1046 1072/1072/1072 1071/1071/1071 +f 1071/1071/1071 1045/1045/1045 1046/1046/1046 +f 1073/1073/1073 1074/1074/1074 1075/1075/1075 +f 1075/1075/1075 1076/1076/1076 1077/1077/1077 +f 1077/1077/1077 1078/1078/1078 1079/1079/1079 +f 1075/1075/1075 1077/1077/1077 1079/1079/1079 +f 1073/1073/1073 1075/1075/1075 1079/1079/1079 +f 1079/1079/1079 1080/1080/1080 1081/1081/1081 +f 1073/1073/1073 1079/1079/1079 1081/1081/1081 +f 1081/1081/1081 1082/1082/1082 1083/1083/1083 +f 1073/1073/1073 1081/1081/1081 1083/1083/1083 +f 1084/1084/1084 1073/1073/1073 1083/1083/1083 +f 1085/1085/1085 1086/1086/1086 1087/1087/1087 +f 1087/1087/1087 1088/1088/1088 1085/1085/1085 +f 1089/1089/1089 1090/1090/1090 1091/1091/1091 +f 1091/1091/1091 1092/1092/1092 1089/1089/1089 +f 1089/1089/1089 1093/1093/1093 1094/1094/1094 +f 1094/1094/1094 1090/1090/1090 1089/1089/1089 +f 1091/1091/1091 1095/1095/1095 1088/1088/1088 +f 1088/1088/1088 1092/1092/1092 1091/1091/1091 +f 1096/1096/1096 1085/1085/1085 1088/1088/1088 +f 1088/1088/1088 1095/1095/1095 1096/1096/1096 +f 1097/1097/1097 1098/1098/1098 1099/1099/1099 +f 1099/1099/1099 1100/1100/1100 1097/1097/1097 +f 1100/1100/1100 1099/1099/1099 1101/1101/1101 +f 1101/1101/1101 1102/1102/1102 1100/1100/1100 +f 1103/1103/1103 1104/1104/1104 1098/1098/1098 +f 1098/1098/1098 1097/1097/1097 1103/1103/1103 +f 1105/1105/1105 1106/1106/1106 1104/1104/1104 +f 1104/1104/1104 1103/1103/1103 1105/1105/1105 +f 1095/1095/1095 1091/1091/1091 1107/1107/1107 +f 1107/1107/1107 1108/1108/1108 1095/1095/1095 +f 1109/1109/1109 1096/1096/1096 1095/1095/1095 +f 1095/1095/1095 1108/1108/1108 1109/1109/1109 +f 1110/1110/1110 1111/1111/1111 1102/1102/1102 +f 1102/1102/1102 1112/1112/1112 1110/1110/1110 +f 1113/1113/1113 1114/1114/1114 1097/1097/1097 +f 1097/1097/1097 1100/1100/1100 1113/1113/1113 +f 1115/1115/1115 1103/1103/1103 1097/1097/1097 +f 1097/1097/1097 1114/1114/1114 1115/1115/1115 +f 1105/1105/1105 1103/1103/1103 1115/1115/1115 +f 1115/1115/1115 1116/1116/1116 1105/1105/1105 +f 1091/1091/1091 1090/1090/1090 1107/1107/1107 +f 1093/1093/1093 1089/1089/1089 1117/1117/1117 +f 1117/1117/1117 1118/1118/1118 1093/1093/1093 +f 1117/1117/1117 1089/1089/1089 1092/1092/1092 +f 1092/1092/1092 1119/1119/1119 1117/1117/1117 +f 1088/1088/1088 1087/1087/1087 1119/1119/1119 +f 1119/1119/1119 1092/1092/1092 1088/1088/1088 +f 1120/1120/1120 1121/1121/1121 1119/1119/1119 +f 1119/1119/1119 1087/1087/1087 1120/1120/1120 +f 1121/1121/1121 1117/1117/1117 1119/1119/1119 +f 1122/1122/1122 1120/1120/1120 1087/1087/1087 +f 1087/1087/1087 1086/1086/1086 1122/1122/1122 +f 1123/1123/1123 1124/1124/1124 1101/1101/1101 +f 1101/1101/1101 1099/1099/1099 1123/1123/1123 +f 1125/1125/1125 1123/1123/1123 1099/1099/1099 +f 1099/1099/1099 1098/1098/1098 1125/1125/1125 +f 1125/1125/1125 1098/1098/1098 1104/1104/1104 +f 1104/1104/1104 1126/1126/1126 1125/1125/1125 +f 1126/1126/1126 1104/1104/1104 1106/1106/1106 +f 1106/1106/1106 1127/1127/1127 1126/1126/1126 +f 1128/1128/1128 1129/1129/1129 1124/1124/1124 +f 1124/1124/1124 1123/1123/1123 1128/1128/1128 +f 1130/1130/1130 1128/1128/1128 1123/1123/1123 +f 1123/1123/1123 1125/1125/1125 1130/1130/1130 +f 1125/1125/1125 1126/1126/1126 1131/1131/1131 +f 1131/1131/1131 1130/1130/1130 1125/1125/1125 +f 1132/1132/1132 1131/1131/1131 1126/1126/1126 +f 1126/1126/1126 1127/1127/1127 1132/1132/1132 +f 1128/1128/1128 1133/1133/1133 1134/1134/1134 +f 1134/1134/1134 1129/1129/1129 1128/1128/1128 +f 1128/1128/1128 1130/1130/1130 1135/1135/1135 +f 1135/1135/1135 1133/1133/1133 1128/1128/1128 +f 1130/1130/1130 1131/1131/1131 1136/1136/1136 +f 1136/1136/1136 1135/1135/1135 1130/1130/1130 +f 1131/1131/1131 1132/1132/1132 1137/1137/1137 +f 1137/1137/1137 1136/1136/1136 1131/1131/1131 +f 1113/1113/1113 1111/1111/1111 1138/1138/1138 +f 1138/1138/1138 1139/1139/1139 1113/1113/1113 +f 1114/1114/1114 1113/1113/1113 1139/1139/1139 +f 1139/1139/1139 1140/1140/1140 1114/1114/1114 +f 1115/1115/1115 1114/1114/1114 1140/1140/1140 +f 1140/1140/1140 1141/1141/1141 1115/1115/1115 +f 1116/1116/1116 1115/1115/1115 1141/1141/1141 +f 1141/1141/1141 1142/1142/1142 1116/1116/1116 +f 1143/1143/1143 1144/1144/1144 1145/1145/1145 +f 1146/1146/1146 1143/1143/1143 1145/1145/1145 +f 1145/1145/1145 1147/1147/1147 1148/1148/1148 +f 1146/1146/1146 1145/1145/1145 1148/1148/1148 +f 1146/1146/1146 1148/1148/1148 1149/1149/1149 +f 1150/1150/1150 1093/1093/1093 1118/1118/1118 +f 1118/1118/1118 1151/1151/1151 1150/1150/1150 +f 1152/1152/1152 1153/1153/1153 1093/1093/1093 +f 1093/1093/1093 1154/1154/1154 1152/1152/1152 +f 1154/1154/1154 1093/1093/1093 1150/1150/1150 +f 1150/1150/1150 1155/1155/1155 1154/1154/1154 +f 1155/1155/1155 1156/1156/1156 1157/1157/1157 +f 1157/1157/1157 1154/1154/1154 1155/1155/1155 +f 1152/1152/1152 1154/1154/1154 1157/1157/1157 +f 1157/1157/1157 1158/1158/1158 1152/1152/1152 +f 1159/1159/1159 1160/1160/1160 1047/1047/1047 +f 1047/1047/1047 1024/1024/1024 1159/1159/1159 +f 1161/1161/1161 1024/1024/1024 1023/1023/1023 +f 1023/1023/1023 1162/1162/1162 1161/1161/1161 +f 1163/1163/1163 1164/1164/1164 1024/1024/1024 +f 1024/1024/1024 1161/1161/1161 1163/1163/1163 +f 1164/1164/1164 1165/1165/1165 1159/1159/1159 +f 1159/1159/1159 1024/1024/1024 1164/1164/1164 +f 1165/1165/1165 1164/1164/1164 1157/1157/1157 +f 1157/1157/1157 1156/1156/1156 1165/1165/1165 +f 1163/1163/1163 1158/1158/1158 1157/1157/1157 +f 1157/1157/1157 1164/1164/1164 1163/1163/1163 +f 1151/1151/1151 1118/1118/1118 1166/1166/1166 +f 1167/1167/1167 1168/1168/1168 1169/1169/1169 +f 1167/1167/1167 1169/1169/1169 1170/1170/1170 +f 1167/1167/1167 1170/1170/1170 1171/1171/1171 +f 1172/1172/1172 1167/1167/1167 1171/1171/1171 +f 1153/1153/1153 1173/1173/1173 1094/1094/1094 +f 1094/1094/1094 1093/1093/1093 1153/1153/1153 +f 1174/1174/1174 1031/1031/1031 1032/1032/1032 +f 1032/1032/1032 1041/1041/1041 1174/1174/1174 +f 1043/1043/1043 1042/1042/1042 1032/1032/1032 +f 1032/1032/1032 1028/1028/1028 1043/1043/1043 +f 1054/1054/1054 1031/1031/1031 1174/1174/1174 +f 1174/1174/1174 1175/1175/1175 1054/1054/1054 +f 1054/1054/1054 1175/1175/1175 1176/1176/1176 +f 1176/1176/1176 1059/1059/1059 1054/1054/1054 +f 1064/1064/1064 1059/1059/1059 1176/1176/1176 +f 1042/1042/1042 1069/1069/1069 1040/1040/1040 +f 1102/1102/1102 1101/1101/1101 1177/1177/1177 +f 1177/1177/1177 1112/1112/1112 1102/1102/1102 +f 1102/1102/1102 1111/1111/1111 1113/1113/1113 +f 1113/1113/1113 1100/1100/1100 1102/1102/1102 +f 1177/1177/1177 1101/1101/1101 1124/1124/1124 +f 1124/1124/1124 1178/1178/1178 1177/1177/1177 +f 1124/1124/1124 1129/1129/1129 1179/1179/1179 +f 1179/1179/1179 1178/1178/1178 1124/1124/1124 +f 1129/1129/1129 1134/1134/1134 1179/1179/1179 +f 1138/1138/1138 1111/1111/1111 1110/1110/1110 +f 1180/1180/1180 1181/1181/1181 1182/1182/1182 +f 1183/1183/1183 1184/1184/1184 1182/1182/1182 +f 1182/1182/1182 1185/1185/1185 1183/1183/1183 +f 1185/1185/1185 1186/1186/1186 1187/1187/1187 +f 1187/1187/1187 1183/1183/1183 1185/1185/1185 +f 1182/1182/1182 1186/1186/1186 1185/1185/1185 +f 1186/1186/1186 1181/1181/1181 1188/1188/1188 +f 1188/1188/1188 1187/1187/1187 1186/1186/1186 +f 1189/1189/1189 1190/1190/1190 1180/1180/1180 +f 1180/1180/1180 1191/1191/1191 1189/1189/1189 +f 1188/1188/1188 1181/1181/1181 1180/1180/1180 +f 1180/1180/1180 1190/1190/1190 1188/1188/1188 +f 1191/1191/1191 1180/1180/1180 1182/1182/1182 +f 1182/1182/1182 1181/1181/1181 1186/1186/1186 +f 1189/1189/1189 1191/1191/1191 1182/1182/1182 +f 1182/1182/1182 1184/1184/1184 1189/1189/1189 +f 1192/1192/1192 1193/1193/1193 1194/1194/1194 +f 1194/1194/1194 1195/1195/1195 1192/1192/1192 +f 1196/1196/1196 1197/1197/1197 1198/1198/1198 +f 1198/1198/1198 1199/1199/1199 1196/1196/1196 +f 1196/1196/1196 1199/1199/1199 1200/1200/1200 +f 1200/1200/1200 1201/1201/1201 1196/1196/1196 +f 1198/1198/1198 1197/1197/1197 1193/1193/1193 +f 1193/1193/1193 1202/1202/1202 1198/1198/1198 +f 1203/1203/1203 1202/1202/1202 1193/1193/1193 +f 1193/1193/1193 1192/1192/1192 1203/1203/1203 +f 1204/1204/1204 1205/1205/1205 1206/1206/1206 +f 1206/1206/1206 1207/1207/1207 1204/1204/1204 +f 1208/1208/1208 1206/1206/1206 1205/1205/1205 +f 1205/1205/1205 1209/1209/1209 1208/1208/1208 +f 1210/1210/1210 1204/1204/1204 1207/1207/1207 +f 1207/1207/1207 1211/1211/1211 1210/1210/1210 +f 1212/1212/1212 1210/1210/1210 1211/1211/1211 +f 1211/1211/1211 1213/1213/1213 1212/1212/1212 +f 1202/1202/1202 1214/1214/1214 1215/1215/1215 +f 1215/1215/1215 1198/1198/1198 1202/1202/1202 +f 1216/1216/1216 1214/1214/1214 1202/1202/1202 +f 1202/1202/1202 1203/1203/1203 1216/1216/1216 +f 1217/1217/1217 1218/1218/1218 1209/1209/1209 +f 1209/1209/1209 1219/1219/1219 1217/1217/1217 +f 1220/1220/1220 1205/1205/1205 1204/1204/1204 +f 1204/1204/1204 1221/1221/1221 1220/1220/1220 +f 1222/1222/1222 1221/1221/1221 1204/1204/1204 +f 1204/1204/1204 1210/1210/1210 1222/1222/1222 +f 1212/1212/1212 1223/1223/1223 1222/1222/1222 +f 1222/1222/1222 1210/1210/1210 1212/1212/1212 +f 1198/1198/1198 1215/1215/1215 1199/1199/1199 +f 1201/1201/1201 1224/1224/1224 1225/1225/1225 +f 1225/1225/1225 1196/1196/1196 1201/1201/1201 +f 1225/1225/1225 1226/1226/1226 1197/1197/1197 +f 1197/1197/1197 1196/1196/1196 1225/1225/1225 +f 1193/1193/1193 1197/1197/1197 1226/1226/1226 +f 1226/1226/1226 1194/1194/1194 1193/1193/1193 +f 1227/1227/1227 1194/1194/1194 1226/1226/1226 +f 1226/1226/1226 1228/1228/1228 1227/1227/1227 +f 1228/1228/1228 1226/1226/1226 1225/1225/1225 +f 1229/1229/1229 1195/1195/1195 1194/1194/1194 +f 1194/1194/1194 1227/1227/1227 1229/1229/1229 +f 1230/1230/1230 1206/1206/1206 1208/1208/1208 +f 1208/1208/1208 1231/1231/1231 1230/1230/1230 +f 1232/1232/1232 1207/1207/1207 1206/1206/1206 +f 1206/1206/1206 1230/1230/1230 1232/1232/1232 +f 1232/1232/1232 1233/1233/1233 1211/1211/1211 +f 1211/1211/1211 1207/1207/1207 1232/1232/1232 +f 1233/1233/1233 1234/1234/1234 1213/1213/1213 +f 1213/1213/1213 1211/1211/1211 1233/1233/1233 +f 1235/1235/1235 1230/1230/1230 1231/1231/1231 +f 1231/1231/1231 1236/1236/1236 1235/1235/1235 +f 1237/1237/1237 1232/1232/1232 1230/1230/1230 +f 1230/1230/1230 1235/1235/1235 1237/1237/1237 +f 1232/1232/1232 1237/1237/1237 1238/1238/1238 +f 1238/1238/1238 1233/1233/1233 1232/1232/1232 +f 1239/1239/1239 1234/1234/1234 1233/1233/1233 +f 1233/1233/1233 1238/1238/1238 1239/1239/1239 +f 1240/1240/1240 1235/1235/1235 1236/1236/1236 +f 1236/1236/1236 1241/1241/1241 1240/1240/1240 +f 1235/1235/1235 1240/1240/1240 1242/1242/1242 +f 1242/1242/1242 1237/1237/1237 1235/1235/1235 +f 1237/1237/1237 1242/1242/1242 1243/1243/1243 +f 1243/1243/1243 1238/1238/1238 1237/1237/1237 +f 1238/1238/1238 1243/1243/1243 1244/1244/1244 +f 1244/1244/1244 1239/1239/1239 1238/1238/1238 +f 1245/1245/1245 1246/1246/1246 1219/1219/1219 +f 1219/1219/1219 1220/1220/1220 1245/1245/1245 +f 1221/1221/1221 1247/1247/1247 1245/1245/1245 +f 1245/1245/1245 1220/1220/1220 1221/1221/1221 +f 1222/1222/1222 1248/1248/1248 1247/1247/1247 +f 1247/1247/1247 1221/1221/1221 1222/1222/1222 +f 1223/1223/1223 1249/1249/1249 1248/1248/1248 +f 1248/1248/1248 1222/1222/1222 1223/1223/1223 +f 1250/1250/1250 1251/1251/1251 1252/1252/1252 +f 1252/1252/1252 1253/1253/1253 1254/1254/1254 +f 1254/1254/1254 1255/1255/1255 1256/1256/1256 +f 1252/1252/1252 1254/1254/1254 1256/1256/1256 +f 1250/1250/1250 1252/1252/1252 1256/1256/1256 +f 1256/1256/1256 1257/1257/1257 1258/1258/1258 +f 1250/1250/1250 1256/1256/1256 1258/1258/1258 +f 1258/1258/1258 1259/1259/1259 1260/1260/1260 +f 1250/1250/1250 1258/1258/1258 1260/1260/1260 +f 1261/1261/1261 1250/1250/1250 1260/1260/1260 +f 1262/1262/1262 1263/1263/1263 1264/1264/1264 +f 1264/1264/1264 1265/1265/1265 1262/1262/1262 +f 1266/1266/1266 1267/1267/1267 1268/1268/1268 +f 1268/1268/1268 1269/1269/1269 1266/1266/1266 +f 1266/1266/1266 1270/1270/1270 1271/1271/1271 +f 1271/1271/1271 1267/1267/1267 1266/1266/1266 +f 1268/1268/1268 1272/1272/1272 1265/1265/1265 +f 1265/1265/1265 1269/1269/1269 1268/1268/1268 +f 1273/1273/1273 1262/1262/1262 1265/1265/1265 +f 1265/1265/1265 1272/1272/1272 1273/1273/1273 +f 1274/1274/1274 1275/1275/1275 1276/1276/1276 +f 1276/1276/1276 1277/1277/1277 1274/1274/1274 +f 1277/1277/1277 1276/1276/1276 1278/1278/1278 +f 1278/1278/1278 1279/1279/1279 1277/1277/1277 +f 1280/1280/1280 1281/1281/1281 1275/1275/1275 +f 1275/1275/1275 1274/1274/1274 1280/1280/1280 +f 1282/1282/1282 1283/1283/1283 1281/1281/1281 +f 1281/1281/1281 1280/1280/1280 1282/1282/1282 +f 1272/1272/1272 1268/1268/1268 1284/1284/1284 +f 1284/1284/1284 1285/1285/1285 1272/1272/1272 +f 1286/1286/1286 1273/1273/1273 1272/1272/1272 +f 1272/1272/1272 1285/1285/1285 1286/1286/1286 +f 1287/1287/1287 1288/1288/1288 1279/1279/1279 +f 1279/1279/1279 1289/1289/1289 1287/1287/1287 +f 1290/1290/1290 1291/1291/1291 1274/1274/1274 +f 1274/1274/1274 1277/1277/1277 1290/1290/1290 +f 1292/1292/1292 1280/1280/1280 1274/1274/1274 +f 1274/1274/1274 1291/1291/1291 1292/1292/1292 +f 1282/1282/1282 1280/1280/1280 1292/1292/1292 +f 1292/1292/1292 1293/1293/1293 1282/1282/1282 +f 1268/1268/1268 1267/1267/1267 1284/1284/1284 +f 1270/1270/1270 1266/1266/1266 1294/1294/1294 +f 1294/1294/1294 1295/1295/1295 1270/1270/1270 +f 1294/1294/1294 1266/1266/1266 1269/1269/1269 +f 1269/1269/1269 1296/1296/1296 1294/1294/1294 +f 1265/1265/1265 1264/1264/1264 1296/1296/1296 +f 1296/1296/1296 1269/1269/1269 1265/1265/1265 +f 1297/1297/1297 1298/1298/1298 1296/1296/1296 +f 1296/1296/1296 1264/1264/1264 1297/1297/1297 +f 1298/1298/1298 1294/1294/1294 1296/1296/1296 +f 1299/1299/1299 1297/1297/1297 1264/1264/1264 +f 1264/1264/1264 1263/1263/1263 1299/1299/1299 +f 1300/1300/1300 1301/1301/1301 1278/1278/1278 +f 1278/1278/1278 1276/1276/1276 1300/1300/1300 +f 1302/1302/1302 1300/1300/1300 1276/1276/1276 +f 1276/1276/1276 1275/1275/1275 1302/1302/1302 +f 1302/1302/1302 1275/1275/1275 1281/1281/1281 +f 1281/1281/1281 1303/1303/1303 1302/1302/1302 +f 1303/1303/1303 1281/1281/1281 1283/1283/1283 +f 1283/1283/1283 1304/1304/1304 1303/1303/1303 +f 1305/1305/1305 1306/1306/1306 1301/1301/1301 +f 1301/1301/1301 1300/1300/1300 1305/1305/1305 +f 1307/1307/1307 1305/1305/1305 1300/1300/1300 +f 1300/1300/1300 1302/1302/1302 1307/1307/1307 +f 1302/1302/1302 1303/1303/1303 1308/1308/1308 +f 1308/1308/1308 1307/1307/1307 1302/1302/1302 +f 1309/1309/1309 1308/1308/1308 1303/1303/1303 +f 1303/1303/1303 1304/1304/1304 1309/1309/1309 +f 1305/1305/1305 1310/1310/1310 1311/1311/1311 +f 1311/1311/1311 1306/1306/1306 1305/1305/1305 +f 1305/1305/1305 1307/1307/1307 1312/1312/1312 +f 1312/1312/1312 1310/1310/1310 1305/1305/1305 +f 1307/1307/1307 1308/1308/1308 1313/1313/1313 +f 1313/1313/1313 1312/1312/1312 1307/1307/1307 +f 1308/1308/1308 1309/1309/1309 1314/1314/1314 +f 1314/1314/1314 1313/1313/1313 1308/1308/1308 +f 1290/1290/1290 1288/1288/1288 1315/1315/1315 +f 1315/1315/1315 1316/1316/1316 1290/1290/1290 +f 1291/1291/1291 1290/1290/1290 1316/1316/1316 +f 1316/1316/1316 1317/1317/1317 1291/1291/1291 +f 1292/1292/1292 1291/1291/1291 1317/1317/1317 +f 1317/1317/1317 1318/1318/1318 1292/1292/1292 +f 1293/1293/1293 1292/1292/1292 1318/1318/1318 +f 1318/1318/1318 1319/1319/1319 1293/1293/1293 +f 1320/1320/1320 1321/1321/1321 1322/1322/1322 +f 1323/1323/1323 1320/1320/1320 1322/1322/1322 +f 1322/1322/1322 1324/1324/1324 1325/1325/1325 +f 1323/1323/1323 1322/1322/1322 1325/1325/1325 +f 1323/1323/1323 1325/1325/1325 1326/1326/1326 +f 1327/1327/1327 1270/1270/1270 1295/1295/1295 +f 1295/1295/1295 1328/1328/1328 1327/1327/1327 +f 1329/1329/1329 1330/1330/1330 1270/1270/1270 +f 1270/1270/1270 1331/1331/1331 1329/1329/1329 +f 1331/1331/1331 1270/1270/1270 1327/1327/1327 +f 1327/1327/1327 1332/1332/1332 1331/1331/1331 +f 1332/1332/1332 1333/1333/1333 1334/1334/1334 +f 1334/1334/1334 1331/1331/1331 1332/1332/1332 +f 1329/1329/1329 1331/1331/1331 1334/1334/1334 +f 1334/1334/1334 1335/1335/1335 1329/1329/1329 +f 1336/1336/1336 1337/1337/1337 1224/1224/1224 +f 1224/1224/1224 1201/1201/1201 1336/1336/1336 +f 1338/1338/1338 1201/1201/1201 1200/1200/1200 +f 1200/1200/1200 1339/1339/1339 1338/1338/1338 +f 1340/1340/1340 1341/1341/1341 1201/1201/1201 +f 1201/1201/1201 1338/1338/1338 1340/1340/1340 +f 1341/1341/1341 1342/1342/1342 1336/1336/1336 +f 1336/1336/1336 1201/1201/1201 1341/1341/1341 +f 1342/1342/1342 1341/1341/1341 1334/1334/1334 +f 1334/1334/1334 1333/1333/1333 1342/1342/1342 +f 1340/1340/1340 1335/1335/1335 1334/1334/1334 +f 1334/1334/1334 1341/1341/1341 1340/1340/1340 +f 1328/1328/1328 1295/1295/1295 1343/1343/1343 +f 1344/1344/1344 1345/1345/1345 1346/1346/1346 +f 1344/1344/1344 1346/1346/1346 1347/1347/1347 +f 1344/1344/1344 1347/1347/1347 1348/1348/1348 +f 1349/1349/1349 1344/1344/1344 1348/1348/1348 +f 1330/1330/1330 1350/1350/1350 1271/1271/1271 +f 1271/1271/1271 1270/1270/1270 1330/1330/1330 +f 1351/1351/1351 1208/1208/1208 1209/1209/1209 +f 1209/1209/1209 1218/1218/1218 1351/1351/1351 +f 1220/1220/1220 1219/1219/1219 1209/1209/1209 +f 1209/1209/1209 1205/1205/1205 1220/1220/1220 +f 1231/1231/1231 1208/1208/1208 1351/1351/1351 +f 1351/1351/1351 1352/1352/1352 1231/1231/1231 +f 1231/1231/1231 1352/1352/1352 1353/1353/1353 +f 1353/1353/1353 1236/1236/1236 1231/1231/1231 +f 1241/1241/1241 1236/1236/1236 1353/1353/1353 +f 1219/1219/1219 1246/1246/1246 1217/1217/1217 +f 1279/1279/1279 1278/1278/1278 1354/1354/1354 +f 1354/1354/1354 1289/1289/1289 1279/1279/1279 +f 1279/1279/1279 1288/1288/1288 1290/1290/1290 +f 1290/1290/1290 1277/1277/1277 1279/1279/1279 +f 1354/1354/1354 1278/1278/1278 1301/1301/1301 +f 1301/1301/1301 1355/1355/1355 1354/1354/1354 +f 1301/1301/1301 1306/1306/1306 1356/1356/1356 +f 1356/1356/1356 1355/1355/1355 1301/1301/1301 +f 1306/1306/1306 1311/1311/1311 1356/1356/1356 +f 1315/1315/1315 1288/1288/1288 1287/1287/1287 +f 1357/1357/1357 1358/1358/1358 1359/1359/1359 +f 1359/1359/1359 1360/1360/1360 1357/1357/1357 +f 1361/1361/1361 1362/1362/1362 1363/1363/1363 +f 1363/1363/1363 1364/1364/1364 1361/1361/1361 +f 1361/1361/1361 1364/1364/1364 1365/1365/1365 +f 1365/1365/1365 1366/1366/1366 1361/1361/1361 +f 1363/1363/1363 1362/1362/1362 1358/1358/1358 +f 1358/1358/1358 1367/1367/1367 1363/1363/1363 +f 1368/1368/1368 1367/1367/1367 1358/1358/1358 +f 1358/1358/1358 1357/1357/1357 1368/1368/1368 +f 1369/1369/1369 1370/1370/1370 1371/1371/1371 +f 1371/1371/1371 1372/1372/1372 1369/1369/1369 +f 1373/1373/1373 1371/1371/1371 1370/1370/1370 +f 1370/1370/1370 1374/1374/1374 1373/1373/1373 +f 1375/1375/1375 1369/1369/1369 1372/1372/1372 +f 1372/1372/1372 1376/1376/1376 1375/1375/1375 +f 1377/1377/1377 1375/1375/1375 1376/1376/1376 +f 1376/1376/1376 1378/1378/1378 1377/1377/1377 +f 1367/1367/1367 1379/1379/1379 1380/1380/1380 +f 1380/1380/1380 1363/1363/1363 1367/1367/1367 +f 1381/1381/1381 1379/1379/1379 1367/1367/1367 +f 1367/1367/1367 1368/1368/1368 1381/1381/1381 +f 1382/1382/1382 1383/1383/1383 1374/1374/1374 +f 1374/1374/1374 1384/1384/1384 1382/1382/1382 +f 1385/1385/1385 1370/1370/1370 1369/1369/1369 +f 1369/1369/1369 1386/1386/1386 1385/1385/1385 +f 1387/1387/1387 1386/1386/1386 1369/1369/1369 +f 1369/1369/1369 1375/1375/1375 1387/1387/1387 +f 1377/1377/1377 1388/1388/1388 1387/1387/1387 +f 1387/1387/1387 1375/1375/1375 1377/1377/1377 +f 1363/1363/1363 1380/1380/1380 1364/1364/1364 +f 1366/1366/1366 1389/1389/1389 1390/1390/1390 +f 1390/1390/1390 1361/1361/1361 1366/1366/1366 +f 1390/1390/1390 1391/1391/1391 1362/1362/1362 +f 1362/1362/1362 1361/1361/1361 1390/1390/1390 +f 1358/1358/1358 1362/1362/1362 1391/1391/1391 +f 1391/1391/1391 1359/1359/1359 1358/1358/1358 +f 1392/1392/1392 1359/1359/1359 1391/1391/1391 +f 1391/1391/1391 1393/1393/1393 1392/1392/1392 +f 1393/1393/1393 1391/1391/1391 1390/1390/1390 +f 1394/1394/1394 1360/1360/1360 1359/1359/1359 +f 1359/1359/1359 1392/1392/1392 1394/1394/1394 +f 1395/1395/1395 1371/1371/1371 1373/1373/1373 +f 1373/1373/1373 1396/1396/1396 1395/1395/1395 +f 1397/1397/1397 1372/1372/1372 1371/1371/1371 +f 1371/1371/1371 1395/1395/1395 1397/1397/1397 +f 1397/1397/1397 1398/1398/1398 1376/1376/1376 +f 1376/1376/1376 1372/1372/1372 1397/1397/1397 +f 1398/1398/1398 1399/1399/1399 1378/1378/1378 +f 1378/1378/1378 1376/1376/1376 1398/1398/1398 +f 1400/1400/1400 1395/1395/1395 1396/1396/1396 +f 1396/1396/1396 1401/1401/1401 1400/1400/1400 +f 1402/1402/1402 1397/1397/1397 1395/1395/1395 +f 1395/1395/1395 1400/1400/1400 1402/1402/1402 +f 1397/1397/1397 1402/1402/1402 1403/1403/1403 +f 1403/1403/1403 1398/1398/1398 1397/1397/1397 +f 1404/1404/1404 1399/1399/1399 1398/1398/1398 +f 1398/1398/1398 1403/1403/1403 1404/1404/1404 +f 1405/1405/1405 1400/1400/1400 1401/1401/1401 +f 1401/1401/1401 1406/1406/1406 1405/1405/1405 +f 1400/1400/1400 1405/1405/1405 1407/1407/1407 +f 1407/1407/1407 1402/1402/1402 1400/1400/1400 +f 1402/1402/1402 1407/1407/1407 1408/1408/1408 +f 1408/1408/1408 1403/1403/1403 1402/1402/1402 +f 1403/1403/1403 1408/1408/1408 1409/1409/1409 +f 1409/1409/1409 1404/1404/1404 1403/1403/1403 +f 1410/1410/1410 1411/1411/1411 1384/1384/1384 +f 1384/1384/1384 1385/1385/1385 1410/1410/1410 +f 1386/1386/1386 1412/1412/1412 1410/1410/1410 +f 1410/1410/1410 1385/1385/1385 1386/1386/1386 +f 1387/1387/1387 1413/1413/1413 1412/1412/1412 +f 1412/1412/1412 1386/1386/1386 1387/1387/1387 +f 1388/1388/1388 1414/1414/1414 1413/1413/1413 +f 1413/1413/1413 1387/1387/1387 1388/1388/1388 +f 1415/1415/1415 1416/1416/1416 1417/1417/1417 +f 1417/1417/1417 1418/1418/1418 1419/1419/1419 +f 1419/1419/1419 1420/1420/1420 1421/1421/1421 +f 1417/1417/1417 1419/1419/1419 1421/1421/1421 +f 1415/1415/1415 1417/1417/1417 1421/1421/1421 +f 1421/1421/1421 1422/1422/1422 1423/1423/1423 +f 1415/1415/1415 1421/1421/1421 1423/1423/1423 +f 1423/1423/1423 1424/1424/1424 1425/1425/1425 +f 1415/1415/1415 1423/1423/1423 1425/1425/1425 +f 1426/1426/1426 1415/1415/1415 1425/1425/1425 +f 1427/1427/1427 1428/1428/1428 1429/1429/1429 +f 1429/1429/1429 1430/1430/1430 1427/1427/1427 +f 1431/1431/1431 1432/1432/1432 1433/1433/1433 +f 1433/1433/1433 1434/1434/1434 1431/1431/1431 +f 1431/1431/1431 1435/1435/1435 1436/1436/1436 +f 1436/1436/1436 1432/1432/1432 1431/1431/1431 +f 1433/1433/1433 1437/1437/1437 1430/1430/1430 +f 1430/1430/1430 1434/1434/1434 1433/1433/1433 +f 1438/1438/1438 1427/1427/1427 1430/1430/1430 +f 1430/1430/1430 1437/1437/1437 1438/1438/1438 +f 1439/1439/1439 1440/1440/1440 1441/1441/1441 +f 1441/1441/1441 1442/1442/1442 1439/1439/1439 +f 1442/1442/1442 1441/1441/1441 1443/1443/1443 +f 1443/1443/1443 1444/1444/1444 1442/1442/1442 +f 1445/1445/1445 1446/1446/1446 1440/1440/1440 +f 1440/1440/1440 1439/1439/1439 1445/1445/1445 +f 1447/1447/1447 1448/1448/1448 1446/1446/1446 +f 1446/1446/1446 1445/1445/1445 1447/1447/1447 +f 1437/1437/1437 1433/1433/1433 1449/1449/1449 +f 1449/1449/1449 1450/1450/1450 1437/1437/1437 +f 1451/1451/1451 1438/1438/1438 1437/1437/1437 +f 1437/1437/1437 1450/1450/1450 1451/1451/1451 +f 1452/1452/1452 1453/1453/1453 1444/1444/1444 +f 1444/1444/1444 1454/1454/1454 1452/1452/1452 +f 1455/1455/1455 1456/1456/1456 1439/1439/1439 +f 1439/1439/1439 1442/1442/1442 1455/1455/1455 +f 1457/1457/1457 1445/1445/1445 1439/1439/1439 +f 1439/1439/1439 1456/1456/1456 1457/1457/1457 +f 1447/1447/1447 1445/1445/1445 1457/1457/1457 +f 1457/1457/1457 1458/1458/1458 1447/1447/1447 +f 1433/1433/1433 1432/1432/1432 1449/1449/1449 +f 1435/1435/1435 1431/1431/1431 1459/1459/1459 +f 1459/1459/1459 1460/1460/1460 1435/1435/1435 +f 1459/1459/1459 1431/1431/1431 1434/1434/1434 +f 1434/1434/1434 1461/1461/1461 1459/1459/1459 +f 1430/1430/1430 1429/1429/1429 1461/1461/1461 +f 1461/1461/1461 1434/1434/1434 1430/1430/1430 +f 1462/1462/1462 1463/1463/1463 1461/1461/1461 +f 1461/1461/1461 1429/1429/1429 1462/1462/1462 +f 1463/1463/1463 1459/1459/1459 1461/1461/1461 +f 1464/1464/1464 1462/1462/1462 1429/1429/1429 +f 1429/1429/1429 1428/1428/1428 1464/1464/1464 +f 1465/1465/1465 1466/1466/1466 1443/1443/1443 +f 1443/1443/1443 1441/1441/1441 1465/1465/1465 +f 1467/1467/1467 1465/1465/1465 1441/1441/1441 +f 1441/1441/1441 1440/1440/1440 1467/1467/1467 +f 1467/1467/1467 1440/1440/1440 1446/1446/1446 +f 1446/1446/1446 1468/1468/1468 1467/1467/1467 +f 1468/1468/1468 1446/1446/1446 1448/1448/1448 +f 1448/1448/1448 1469/1469/1469 1468/1468/1468 +f 1470/1470/1470 1471/1471/1471 1466/1466/1466 +f 1466/1466/1466 1465/1465/1465 1470/1470/1470 +f 1472/1472/1472 1470/1470/1470 1465/1465/1465 +f 1465/1465/1465 1467/1467/1467 1472/1472/1472 +f 1467/1467/1467 1468/1468/1468 1473/1473/1473 +f 1473/1473/1473 1472/1472/1472 1467/1467/1467 +f 1474/1474/1474 1473/1473/1473 1468/1468/1468 +f 1468/1468/1468 1469/1469/1469 1474/1474/1474 +f 1470/1470/1470 1475/1475/1475 1476/1476/1476 +f 1476/1476/1476 1471/1471/1471 1470/1470/1470 +f 1470/1470/1470 1472/1472/1472 1477/1477/1477 +f 1477/1477/1477 1475/1475/1475 1470/1470/1470 +f 1472/1472/1472 1473/1473/1473 1478/1478/1478 +f 1478/1478/1478 1477/1477/1477 1472/1472/1472 +f 1473/1473/1473 1474/1474/1474 1479/1479/1479 +f 1479/1479/1479 1478/1478/1478 1473/1473/1473 +f 1455/1455/1455 1453/1453/1453 1480/1480/1480 +f 1480/1480/1480 1481/1481/1481 1455/1455/1455 +f 1456/1456/1456 1455/1455/1455 1481/1481/1481 +f 1481/1481/1481 1482/1482/1482 1456/1456/1456 +f 1457/1457/1457 1456/1456/1456 1482/1482/1482 +f 1482/1482/1482 1483/1483/1483 1457/1457/1457 +f 1458/1458/1458 1457/1457/1457 1483/1483/1483 +f 1483/1483/1483 1484/1484/1484 1458/1458/1458 +f 1485/1485/1485 1486/1486/1486 1487/1487/1487 +f 1488/1488/1488 1485/1485/1485 1487/1487/1487 +f 1487/1487/1487 1489/1489/1489 1490/1490/1490 +f 1488/1488/1488 1487/1487/1487 1490/1490/1490 +f 1488/1488/1488 1490/1490/1490 1491/1491/1491 +f 1492/1492/1492 1435/1435/1435 1460/1460/1460 +f 1460/1460/1460 1493/1493/1493 1492/1492/1492 +f 1494/1494/1494 1495/1495/1495 1435/1435/1435 +f 1435/1435/1435 1496/1496/1496 1494/1494/1494 +f 1496/1496/1496 1435/1435/1435 1492/1492/1492 +f 1492/1492/1492 1497/1497/1497 1496/1496/1496 +f 1497/1497/1497 1498/1498/1498 1499/1499/1499 +f 1499/1499/1499 1496/1496/1496 1497/1497/1497 +f 1494/1494/1494 1496/1496/1496 1499/1499/1499 +f 1499/1499/1499 1500/1500/1500 1494/1494/1494 +f 1501/1501/1501 1502/1502/1502 1389/1389/1389 +f 1389/1389/1389 1366/1366/1366 1501/1501/1501 +f 1503/1503/1503 1366/1366/1366 1365/1365/1365 +f 1365/1365/1365 1504/1504/1504 1503/1503/1503 +f 1505/1505/1505 1506/1506/1506 1366/1366/1366 +f 1366/1366/1366 1503/1503/1503 1505/1505/1505 +f 1506/1506/1506 1507/1507/1507 1501/1501/1501 +f 1501/1501/1501 1366/1366/1366 1506/1506/1506 +f 1507/1507/1507 1506/1506/1506 1499/1499/1499 +f 1499/1499/1499 1498/1498/1498 1507/1507/1507 +f 1505/1505/1505 1500/1500/1500 1499/1499/1499 +f 1499/1499/1499 1506/1506/1506 1505/1505/1505 +f 1493/1493/1493 1460/1460/1460 1508/1508/1508 +f 1509/1509/1509 1510/1510/1510 1511/1511/1511 +f 1509/1509/1509 1511/1511/1511 1512/1512/1512 +f 1509/1509/1509 1512/1512/1512 1513/1513/1513 +f 1514/1514/1514 1509/1509/1509 1513/1513/1513 +f 1495/1495/1495 1515/1515/1515 1436/1436/1436 +f 1436/1436/1436 1435/1435/1435 1495/1495/1495 +f 1516/1516/1516 1373/1373/1373 1374/1374/1374 +f 1374/1374/1374 1383/1383/1383 1516/1516/1516 +f 1385/1385/1385 1384/1384/1384 1374/1374/1374 +f 1374/1374/1374 1370/1370/1370 1385/1385/1385 +f 1396/1396/1396 1373/1373/1373 1516/1516/1516 +f 1516/1516/1516 1517/1517/1517 1396/1396/1396 +f 1396/1396/1396 1517/1517/1517 1518/1518/1518 +f 1518/1518/1518 1401/1401/1401 1396/1396/1396 +f 1406/1406/1406 1401/1401/1401 1518/1518/1518 +f 1384/1384/1384 1411/1411/1411 1382/1382/1382 +f 1444/1444/1444 1443/1443/1443 1519/1519/1519 +f 1519/1519/1519 1454/1454/1454 1444/1444/1444 +f 1444/1444/1444 1453/1453/1453 1455/1455/1455 +f 1455/1455/1455 1442/1442/1442 1444/1444/1444 +f 1519/1519/1519 1443/1443/1443 1466/1466/1466 +f 1466/1466/1466 1520/1520/1520 1519/1519/1519 +f 1466/1466/1466 1471/1471/1471 1521/1521/1521 +f 1521/1521/1521 1520/1520/1520 1466/1466/1466 +f 1471/1471/1471 1476/1476/1476 1521/1521/1521 +f 1480/1480/1480 1453/1453/1453 1452/1452/1452 diff --git a/demos/art/models/witch/witch_object_diffuse.tga.png b/demos/art/models/witch/witch_object_diffuse.tga.png new file mode 100644 index 0000000..b0e591c Binary files /dev/null and b/demos/art/models/witch/witch_object_diffuse.tga.png differ diff --git a/demos/fwk_netsync.h b/demos/fwk_netsync.h new file mode 100644 index 0000000..bd1e153 --- /dev/null +++ b/demos/fwk_netsync.h @@ -0,0 +1,716 @@ +// high-level, socket-less networking api. inspired by Quake, MPI and RenderBuckets theories. +// - rlyeh, public domain +// +// Usage: +// 1. configure networked memory buffers with flags (world, player1, player2, etc). network_buffer(); +// 2. then during game loop: +// - modify your buffers as much as needed. +// - sync buffers at least once per frame. network_sync(); +// - render your world +// 3. optionally, monitor network status & variables. network_get(); +// +// @todo: maybe network_send(msg) + msg *network_recv(); instead of event queue of network_sync() ? + +//enum { NETWORK_HANDSHAKE, NETWORK_ENCRYPT, NETWORK_VERSIONED, NETWORK_CHECKSUM }; // negotiation +//enum { NETWORK_TCP, NETWORK_UDP, NETWORK_KCP, NETWORK_ENET, NETWORK_WEBSOCKET }; // transport, where +enum { NETWORK_BIND = 2, NETWORK_CONNECT = 4, NETWORK_NOFAIL = 8 }; +enum { MAX_CLIENTS = 32 }; +API void network_create(const char *ip, const char *port, unsigned flags); // both ip and port can be null + +//enum { NETWORK_LOSSY, NETWORK_COMPRESS }; // post-processes +//enum { NETWORK_UNRELIABLE, NETWORK_UNORDERED, NETWORK_PRIORITY }; // how +//enum { NETWORK_PREDICT, NETWORK_RECONCILE, NETWORK_INTERPOLATE, NETWORK_COMPENSATE }; // time authority, when +//enum { NETWORK_LAGS, NETWORK_DROPS, NETWORK_THROTTLES, NETWORK_DUPES }; // quality sim, how much +//enum { NETWORK_CONST = 1, NETWORK_64,NETWORK_32,NETWORK_16,NETWORK_8, NETWORK_FLT, NETWORK_STR, NETWORK_BLOB }; // type, what +enum { NETWORK_SEND = 2, NETWORK_RECV = 4 }; +API void* network_buffer(void *ptr, unsigned sz, unsigned flags, int64_t rank); // configures a shared/networked buffer +API char** network_sync(unsigned timeout_ms); // syncs all buffers & returns null-terminated list of network events + +enum { NETWORK_RANK = 0 }; // [0..N] where 0 is server +enum { NETWORK_PING = 1 }; // NETWORK_BANDWIDTH, NETWORK_QUALITY }; +enum { NETWORK_PORT = 2, NETWORK_IP, NETWORK_LIVE }; +//enum { NETWORK_USERID, NETWORK_SALT, NETWORK_COUNT/*N users*/ /*...*/, +API int64_t network_get(uint64_t key); +API int64_t network_put(uint64_t key, int64_t value); + +API void network_rpc(const char *signature, void *function); +API void network_rpc_send_to(int64_t rank, unsigned id, const char *cmdline); +API void network_rpc_send(unsigned id, const char *cmdline); + +// ----------------------------------------------------------------------------- +// low-level api (sockets based) + +API bool server_bind(int max_clients, int port); +API void server_poll(); +API void server_broadcast_bin(const void *ptr, int len); +API void server_broadcast(const char *msg); +API void server_terminate(); +API void server_send(int64_t handle, const char *msg); +API void server_send_bin(int64_t handle, const void *ptr, int len); +API void server_drop(int64_t handle); + +API int64_t client_join(const char *ip, int port); +#define client_send(msg) server_broadcast(msg) +#define client_send_bin(ptr,len) server_broadcast_bin(ptr, len) +#define client_terminate() server_terminate() + +#define ANYHOST_IPV4 "0.0.0.0" +#define ANYHOST_IPV6 "::0" + +#define LOCALHOST_IPV4 "127.0.0.1" +#define LOCALHOST_IPV6 "::1" + +// ----------------------------------------------------------------------------- +// implementation + +typedef void* (*rpc_function)(); + +typedef struct rpc_call { + char *method; + rpc_function function; + uint64_t function_hash; +} rpc_call; + +#define RPC_SIGNATURE_i_iii UINT64_C(0x78409099752fa48a) // printf("%llx\n, HASH_STR("int(int,int,int)")); +#define RPC_SIGNATURE_i_ii UINT64_C(0x258290edf43985a5) // printf("%llx\n, HASH_STR("int(int,int)")); +#define RPC_SIGNATURE_s_s UINT64_C(0x97deedd17d9afb12) // printf("%llx\n, HASH_STR("char*(char*)")); +#define RPC_SIGNATURE_s_v UINT64_C(0x09c16a1242049b80) // printf("%llx\n, HASH_STR("char*(void)")); + +static +rpc_call rpc_new_call(const char *signature, rpc_function function) { + if( signature && function ) { + array(char*)tokens = strsplit(signature, "(,)"); + if( array_count(tokens) >= 1 ) { + char *method = strrchr(tokens[0], ' ')+1; + char *rettype = va("%.*s", (int)(method - tokens[0] - 1), tokens[0]); + int num_args = array_count(tokens) - 1; + char* hash_sig = va("%s(%s)", rettype, num_args ? (array_pop_front(tokens), strjoin(tokens, ",")) : "void"); + uint64_t hash = hash_str(hash_sig); + method = va("%s%d", method, num_args ); +#if RPC_DEBUG + printf("%p %p %s `%s` %s(", function, (void*)hash, rettype, hash_sig, method); for(int i = 0, end = array_count(tokens); i < end; ++i) printf("%s%s", tokens[i], i == (end-1)? "":", "); puts(");"); +#endif + return (rpc_call) { strdup(method), function, hash }; // LEAK + } + } + return (rpc_call) {0}; +} + +static map(char*, rpc_call) rpc_calls = 0; + +static +void rpc_insert(const char *signature, void *function ) { + rpc_call call = rpc_new_call(signature, function); + if( call.method ) { + if( !rpc_calls ) map_init(rpc_calls, less_str, hash_str); + if( map_find(rpc_calls, call.method)) { + map_erase(rpc_calls, call.method); + } + map_insert(rpc_calls, call.method, call); + } +} + +static +char *rpc_full(unsigned id, const char* method, unsigned num_args, char *args[]) { +#if RPC_DEBUG + printf("id:%x method:%s args:", id, method ); + for( int i = 0; i < num_args; ++i ) printf("%s,", args[i]); puts(""); +#endif + + method = va("%s%d", method, num_args); + rpc_call *found = map_find(rpc_calls, (char*)method); + if( found ) { + switch(found->function_hash) { + case RPC_SIGNATURE_i_iii: return va("%d %d", id, (int)(uintptr_t)found->function(atoi(args[0]), atoi(args[1]), atoi(args[2])) ); + case RPC_SIGNATURE_i_ii: return va("%d %d", id, (int)(uintptr_t)found->function(atoi(args[0]), atoi(args[1])) ); + case RPC_SIGNATURE_s_s: return va("%d %s", id, (char*)found->function(args[0]) ); + case RPC_SIGNATURE_s_v: return va("%d %s", id, (char*)found->function() ); + default: break; + } + } + return va("%d -1", id); +} + +static +array(char*) rpc_parse_args( const char *cmdline, bool quote_whitespaces ) { // parse cmdline arguments. must array_free() after use + // - supports quotes: "abc" "abc def" "abc \"def\"" "abc \"def\"""ghi" etc. + // - #comments removed + array(char*) args = 0; // LEAK + for( int i = 0; cmdline[i]; ) { + char buf[256] = {0}, *ptr = buf; + while(cmdline[i] && isspace(cmdline[i])) ++i; + bool quoted = cmdline[i] == '\"'; + if( quoted ) { + while(cmdline[++i]) { + char ch = cmdline[i]; + /**/ if (ch == '\\' && cmdline[i + 1] == '\"') *ptr++ = '\"', ++i; + else if (ch == '\"' && cmdline[i + 1] == '\"') ++i; + else if (ch == '\"' && (!cmdline[i + 1] || isspace(cmdline[i + 1]))) { + ++i; break; + } + else *ptr++ = ch; + } + } else { + while(cmdline[i] && !isspace(cmdline[i])) *ptr++ = cmdline[i++]; + } + if (buf[0] && buf[0] != '#') { // exclude empty args + comments + if( quote_whitespaces && quoted ) + array_push(args, va("\"%s\"",buf)); + else + array_push(args, va("%s",buf)); + } + } + return args; +} + +static +char* rpc(unsigned id, const char* cmdline) { + array(char*) args = rpc_parse_args(cmdline, false); + int num_args = array_count(args); + char *ret = num_args ? rpc_full(id, args[0], num_args - 1, &args[1]) : rpc_full(id, "", 0, NULL); + array_free(args); + return ret; +} + +static void enet_quit(void) { + do_once { + // enet_deinitialize(); + } +} +static void enet_init() { + do_once { + if( enet_initialize() != 0 ) { + PANIC("cannot initialize enet"); + } + atexit( enet_quit ); + } +} + +static ENetHost *Server; +static map(ENetPeer *, int64_t) clients; +static map(int64_t, ENetPeer *) peers; +static int64_t next_client_id = 1; // assumes ID 0 is server +enum { MSG_INIT, MSG_BUF, MSG_RPC, MSG_RPC_RESP }; + +bool server_bind(int max_clients, int port) { + map_init(clients, less_64, hash_64); + map_init(peers, less_64, hash_64); + assert(port == 0 || (port > 1024 && port < 65500)); + ENetAddress address = {0}; + address.host = ENET_HOST_ANY; + address.port = port; + Server = enet_host_create(&address, max_clients, 2 /*channels*/, 0 /*in bandwidth*/, 0 /*out bandwidth*/); + return Server != NULL; +} + +static +void server_drop_client(int64_t handle) { + map_erase(clients, *(ENetPeer **)map_find(peers, handle)); + map_erase(peers, *(int64_t *)handle); +} + +static +void server_drop_client_peer(ENetPeer *peer) { + map_erase(peers, *(int64_t *)map_find(clients, peer)); + map_erase(clients, peer); +} + +void server_poll() { + ENetEvent event; + while( enet_host_service(Server, &event, 2 /*timeout,ms*/) > 0 ) { + switch (event.type) { + case ENET_EVENT_TYPE_CONNECT:; + char ip[128]; enet_peer_get_ip(event.peer, ip, 128); + PRINTF( "A new client connected from ::%s:%u.\n", ip, event.peer->address.port ); + /* Store any relevant client information here. */ + event.peer->data = "Client information"; + + int64_t client_id = next_client_id++; + map_find_or_add(clients, event.peer, client_id); + map_find_or_add(peers, client_id, event.peer); + break; + case ENET_EVENT_TYPE_RECEIVE: + PRINTF( "A packet of length %zu containing %s was received from %s on channel %u.\n", + event.packet->dataLength, + event.packet->data, + (char *)event.peer->data, + event.channelID ); + + char *dbg = (char *)event.peer->data; + char *ptr = event.packet->data; + unsigned sz = event.packet->dataLength; + + uint32_t mid = *(uint32_t*)ptr; + ptr += 4; + + // @todo: propagate event to user + switch (mid) { + case MSG_INIT: { + uint64_t *cid = map_find(clients, event.peer); + if (cid) { + char init_msg[12]; + *(uint32_t*)&init_msg[0] = MSG_INIT; + *(uint64_t*)&init_msg[4] = *cid; + ENetPacket *packet = enet_packet_create(init_msg, 12, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, packet); + } else { + PRINTF("ignoring unk MSG_INIT client packet.\n"); + } + } break; + case MSG_RPC: + case MSG_RPC_RESP: + // @todo: process and send a response back + break; + default: + PRINTF("recving unk %d sz %d from peer %s\n", mid, sz, dbg); + } + + /* Clean up the packet now that we're done using it. */ + enet_packet_destroy( event.packet ); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + PRINTF( "%s disconnected.\n", (char *)event.peer->data ); + /* Reset the peer's client information. */ + event.peer->data = NULL; + server_drop_client_peer(event.peer); + break; + + case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: + PRINTF( "%s timeout.\n", (char *)event.peer->data ); + event.peer->data = NULL; + server_drop_client_peer(event.peer); + break; + + case ENET_EVENT_TYPE_NONE: break; + } + } +} + +void client_poll() { + ENetEvent event; + while( enet_host_service(Server, &event, 2 /*timeout,ms*/) > 0 ) { + switch (event.type) { + case ENET_EVENT_TYPE_CONNECT:; + break; + case ENET_EVENT_TYPE_RECEIVE: + PRINTF( "A packet of length %zu containing %s was received from %s on channel %u.\n", + event.packet->dataLength, + event.packet->data, + (char *)event.peer->data, + event.channelID ); + + char *dbg = (char *)event.peer->data; + char *ptr = event.packet->data; + unsigned sz = event.packet->dataLength; + + uint32_t mid = *(uint32_t*)ptr; + ptr += 4; + + // @todo: propagate event to user + switch (mid) { + case MSG_INIT: + /* handled during client_join */ + break; + case MSG_RPC: + case MSG_RPC_RESP: + // @todo: process and send a response back + break; + default: + PRINTF("recving unk %d sz %d from peer %s\n", mid, sz, dbg); + } + + /* Clean up the packet now that we're done using it. */ + enet_packet_destroy( event.packet ); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + PRINTF( "%s disconnected.\n", (char *)event.peer->data ); + /* Reset the peer's client information. */ + event.peer->data = NULL; + server_drop_client_peer(event.peer); + break; + + case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: + PRINTF( "%s timeout.\n", (char *)event.peer->data ); + event.peer->data = NULL; + server_drop_client_peer(event.peer); + break; + + case ENET_EVENT_TYPE_NONE: break; + } + } +} + +void server_broadcast_bin(const void *msg, int len) { + ENetPacket *packet = enet_packet_create(msg, len, ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(Server, 0, packet); + //enet_host_flush(Server); // flush if needed +} +void server_broadcast(const char *msg) { + server_broadcast_bin(msg, strlen(msg)+1); +} +void server_terminate() { + enet_host_destroy(Server); + Server = 0; +} + +volatile int client_join_connected = 0; +static int client_join_threaded(void *userdata) { + ENetHost *host = (ENetHost *)userdata; + + ENetPacket *packet = enet_packet_create("", 1, ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(Server, 0, packet); + + /* Wait up to 5 seconds for the connection attempt to succeed. */ + ENetEvent event; + client_join_connected = 0; + client_join_connected = enet_host_service(host, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT; + return 0; +} + +int64_t client_join(const char *ip, int port) { + assert(port > 1024 && port < 65500); + ENetAddress address = {0}; +// address.host = ENET_HOST_ANY; + enet_address_set_host(&address, !strcmp(ip, "localhost") ? "127.0.0.1" : ip); + address.port = port; + + ENetHost *host = enet_host_create(NULL, 1 /*outgoing connections*/, 2 /*channels*/, 0 /*in bandwidth*/, 0 /*out bandwidth*/); + if(!host) return -1; + ENetPeer *peer = enet_host_connect(host, &address, 2, 0); + if(!peer) return -1; + Server = host; + +#if 1 +#if 0 + // sync wait (not working in localhost, unless threaded) + thread_ptr_t th = thread_init(client_join_threaded, host, "client_join_threaded()", 0 ); + thread_join( th ); + thread_destroy( th ); +#else + ENetEvent event; + bool client_join_connected = enet_host_service(host, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT; +#endif + if(!client_join_connected) { enet_peer_reset(peer); return -1; } +#endif + + // ask for server slot + char init_msg[4]; *(uint32_t*)init_msg = MSG_INIT; + server_broadcast_bin(init_msg, sizeof(init_msg)); + + // wait for the response + bool msg_received = enet_host_service(host, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_RECEIVE; + if (!msg_received) { enet_peer_reset(peer); return -1; } + + char *ptr = (char *)event.packet->data; + int64_t cid = -1; + + // decapsulate incoming packet. + uint32_t mid = *(uint32_t*)(ptr + 0); + ptr += 4; + + switch (mid) { + case MSG_INIT: + cid = *(int64_t*)ptr; + break; + default: + enet_peer_reset(peer); + return -1; + } + + /* Clean up the packet now that we're done using it. */ + enet_packet_destroy( event.packet ); + + return cid; +} +void server_drop(int64_t handle) { + enet_peer_disconnect_now(*(ENetPeer **)map_find(peers, handle), 0); + server_drop_client(handle); +} + +void server_send_bin(int64_t handle, const void *ptr, int len) { + ENetPacket *packet = enet_packet_create(ptr, len, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(*(ENetPeer **)map_find(peers, handle), 0, packet); +} + +void server_send(int64_t handle, const char *msg) { + server_send_bin(handle, msg, strlen(msg)+1); +} + +// --- + +typedef struct netbuffer_t { + int64_t owner; + void *ptr; + unsigned sz; + unsigned flags; +} netbuffer_t; + +static array(char*) events; // @todo: make event 128 bytes max? +static array(int64_t) values; // @todo: map instead? +static map( int64_t, array(netbuffer_t) ) buffers; // map> + +void network_create(const char *ip, const char *port_, unsigned flags) { + if (buffers) map_clear(buffers); + do_once { + array_resize(values, 128); + map_init(buffers, less_64, hash_64); + + enet_init(); + } + + ip = ip ? ip : "0.0.0.0"; + int port = atoi(port_ ? port_ : "1234"); + + // network_put(NETWORK_IP, 0x7F000001); // 127.0.0.1 + network_put(NETWORK_PORT, port); + network_put(NETWORK_LIVE, -1); + + if( !(flags&NETWORK_CONNECT) || flags&NETWORK_BIND ) { + // server, else client + PRINTF("Trying to bind server, else we connect as a client...\n"); + network_put(NETWORK_RANK, 0); + if( server_bind(MAX_CLIENTS, port) ) { + network_put(NETWORK_LIVE, 1); + PRINTF("Server bound\n"); + } else { + network_put(NETWORK_RANK, -1); /* unassigned until we connect successfully */ + int64_t socket = client_join(ip, port); + if( socket >= 0 ) { + PRINTF("Client connected, id %lld\n", socket); + network_put(NETWORK_LIVE, 1); + network_put(NETWORK_RANK, socket); + } else { + PRINTF("!Client conn failed\n"); + network_put(NETWORK_LIVE, 0); + + if (!(flags&NETWORK_NOFAIL)) + PANIC("cannot neither connect to %s:%d, nor create a server", ip, port); + } + } + } else { + // client only + PRINTF("Connecting to server...\n"); + network_put(NETWORK_RANK, -1); /* unassigned until we connect successfully */ + int64_t socket = client_join(ip, port); + if( socket > 0 ) { + PRINTF("Client connected, id %lld\n", socket); + network_put(NETWORK_LIVE, 1); + network_put(NETWORK_RANK, socket); + } else { + PRINTF("!Client conn failed\n"); + network_put(NETWORK_LIVE, 0); + if (!(flags&NETWORK_NOFAIL)) + PANIC("cannot connect to server %s:%d", ip, port); + } + } + + PRINTF("Network rank:%lld ip:%s port:%lld\n", network_get(NETWORK_RANK), ip, network_get(NETWORK_PORT)); +} + +int64_t network_put(uint64_t key, int64_t value) { + int64_t *found = key < array_count(values) ? &values[key] : NULL; + if(found) *found = value; + return value; +} +int64_t network_get(uint64_t key) { + int64_t *found = key < array_count(values) ? &values[key] : NULL; + return found ? *found : 0; +} + +void* network_buffer(void *ptr, unsigned sz, unsigned flags, int64_t rank) { + assert(flags); + array(netbuffer_t) *found = map_find_or_add(buffers, rank, NULL); + + netbuffer_t nb; + nb.owner = rank; + nb.ptr = ptr; + nb.sz = sz; + nb.flags = flags; + array_push(*found, nb); + + return ptr; +} + +char** network_sync(unsigned timeout_ms) { + array_clear(events); + + int64_t whoami = network_get(NETWORK_RANK); + bool is_server = whoami == 0; + bool is_client = !is_server; + if(timeout_ms < 2) timeout_ms = 2; + // sleep_ms(timeout_ms); // @fixme. server only? + + // Split buffers into clients @todo + // clients need to do this before network polling; servers should do this after polling. + map_foreach(buffers, int64_t, rank, array(netbuffer_t), list) { + for(int i = 0, end = array_count(list); i < end; ++i) { + netbuffer_t *nb = &list[i]; + if (!is_server && !(nb->flags & NETWORK_SEND)) + continue; + static array(char) encapsulate; + array_resize(encapsulate, nb->sz + 28); + uint32_t *mid = (uint32_t*)&encapsulate[0]; *mid = MSG_BUF; + uint64_t *st = (uint64_t*)&encapsulate[4]; *st = nb->flags; + uint32_t *idx = (uint32_t*)&encapsulate[12]; *idx = i; + uint32_t *len = (uint32_t*)&encapsulate[16]; *len = nb->sz; + uint64_t *who = (uint64_t*)&encapsulate[20]; *who = nb->owner; + // PRINTF("sending %llx %u %lld %u\n", *st, *idx, *who, *len); + memcpy(&encapsulate[28], nb->ptr, nb->sz); + server_broadcast_bin(&encapsulate[0], nb->sz + 28); + } + } + + // network poll + for( ENetEvent event; Server && enet_host_service(Server, &event, timeout_ms) > 0; ) { + char *msg = 0; + char ip[128]; enet_peer_get_ip(event.peer, ip, 128); + + switch (event.type) { + default: // case ENET_EVENT_TYPE_NONE: + break; + + case ENET_EVENT_TYPE_CONNECT:; + msg = va( "A new client connected from ::%s:%u", ip, event.peer->address.port ); + /* Store any relevant client information here. */ + event.peer->data = "Client information"; + + /* ensure we have free slot for client */ + if (map_count(clients) >= MAX_CLIENTS) { + msg = va("%s\n", "Server is at maximum capacity, disconnecting the peer..."); + enet_peer_disconnect_now(event.peer, 1); + break; + } + + int64_t client_id = next_client_id++; + map_find_or_add(clients, event.peer, client_id); + map_find_or_add(peers, client_id, event.peer); + break; + + case ENET_EVENT_TYPE_RECEIVE:; + /* + msg = va( "A packet of length %u containing %s was received from %s on channel %u", + (unsigned)event.packet->dataLength, + event.packet->data, + (char *)event.peer->data, + event.channelID ); + */ + char *dbg = (char *)event.peer->data; + char *ptr = (char *)event.packet->data; + unsigned sz = (unsigned)event.packet->dataLength; + unsigned id = (unsigned)event.channelID; + + // debug + // puts(dbg); + // hexdump(ptr, sz); + + // decapsulate incoming packet. + uint32_t mid = *(uint32_t*)(ptr + 0); + ptr += 4; + + switch (mid) { + case MSG_INIT: + if (is_server) { + uint64_t *cid = map_find(clients, event.peer); + if (cid) { + char init_msg[12]; + *(uint32_t*)&init_msg[0] = MSG_INIT; + *(int64_t*)&init_msg[4] = *cid; + ENetPacket *packet = enet_packet_create(init_msg, 12, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, packet); + PRINTF("Client req id %lld for peer ::%s:%u\n", *cid, ip, event.peer->address.port); + } else { + PRINTF("!Ignoring unk MSG_INIT client packet.\n"); + } + } + break; + case MSG_BUF: { + uint64_t *flags = (uint64_t*)(ptr + 0); + uint32_t *idx = (uint32_t*)(ptr + 8); + uint32_t *len = (uint32_t*)(ptr + 12); + uint64_t *who = (uint64_t*)(ptr + 16); + // PRINTF("recving %d %llx %u %u %lld\n", mid, *flags, *idx, *len, *who); + ptr += 24; + + // validate if peer owns the buffer + uint8_t client_valid = 0; + + if (is_server) { + int64_t *cid = map_find(clients, event.peer); + client_valid = cid ? *cid == *who : 0; + } + + // apply incoming packet. + if( is_client ? *who != whoami : client_valid ) { // clients merge always foreign packets. servers merge foreign packets. + array(netbuffer_t) *list = map_find(buffers, *who); + assert( list ); + assert( *idx < array_count(*list) ); + netbuffer_t *nb = &(*list)[*idx]; + assert( *len == nb->sz ); + memcpy(nb->ptr, ptr, *len); + } + } break; + case MSG_RPC: { + unsigned id = *(uint32_t*)ptr; ptr += 4; + char *cmdline = ptr; + char *resp = rpc(id, cmdline); + char *resp_msg = va("%*.s%s", 4, "", resp); + *(uint32_t*)&resp_msg[0] = MSG_RPC_RESP; + ENetPacket *packet = enet_packet_create(resp_msg, 4 + strlen(resp), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, packet); + } break; + case MSG_RPC_RESP: { + // @todo: react on response? + msg = ptr; + } break; + default: + // PRINTF("!Receiving unk %d sz %d from peer ::%s:%u\n", mid, sz, ip, event.peer->address.port); + break; + } + /* Clean up the packet now that we're done using it. */ + enet_packet_destroy( event.packet ); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + msg = va( "%s disconnected", (char *)event.peer->data ); + /* Reset the peer's client information. */ + event.peer->data = NULL; + if (is_server) server_drop_client_peer(event.peer); + else {network_put(NETWORK_RANK, -1); network_put(NETWORK_LIVE, 0);} + break; + + case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: + msg = va( "%s timeout", (char *)event.peer->data ); + event.peer->data = NULL; + if (is_server) server_drop_client_peer(event.peer); + else {network_put(NETWORK_RANK, -1); network_put(NETWORK_LIVE, 0);} + break; + } + + if(msg) array_push(events, va("%d %s", event.type, msg)); +// if(msg) server_broadcast(msg); + } + + array_push(events, NULL); + return events; +} + +void network_rpc(const char *signature, void *function) { + rpc_insert(signature, function); +} + +void network_rpc_send_to(int64_t rank, unsigned id, const char *cmdline) { + assert(network_get(NETWORK_RANK) == 0); /* must be a host */ + char *msg = va("%*.s%s", 8, "", cmdline); + *(uint32_t*)&msg[0] = MSG_RPC; + *(uint32_t*)&msg[4] = id; + server_send_bin(rank, msg, 8 + strlen(cmdline)); +} + +void network_rpc_send(unsigned id, const char *cmdline) { + char *msg = va("%*.s%s", 8, "", cmdline); + *(uint32_t*)&msg[0] = MSG_RPC; + *(uint32_t*)&msg[4] = id; + server_broadcast_bin(msg, 8 + strlen(cmdline)); +} diff --git a/demos/html5/MAKE.bat b/demos/html5/MAKE.bat new file mode 100644 index 0000000..d715ec2 --- /dev/null +++ b/demos/html5/MAKE.bat @@ -0,0 +1,58 @@ +#!/bin/bash 2>nul || goto :windows + +if [ "$1" = "" ]; then + sh MAKE.bat demo_ui.c + exit +fi + +## clone emscripten sdk +git clone https://github.com/emscripten-core/emsdk ../../../../../emsdk +pushd ../../../../../emsdk + ./emsdk install 3.0.0 ## latest + ./emsdk activate 3.0.0 ## latest + source ./emsdk_env.sh +popd + +## cook art +if [ "$(uname)" = "Darwin" ]; then + chmod +x ../../tools/cook.osx + ../../tools/cook.osx --cook-jobs=1 --cook-ini=../../tools/cook.ini + cp .art[00].zip index.data +else + chmod +x ../../tools/cook.linux + ../../tools/cook.linux --cook-jobs=1 --cook-ini=../../tools/cook.ini + cp .art[00].zip index.data +fi + +## host webserver, compile and launch +python -m http.server --bind 127.0.0.1 8000 1> /dev/null 2> /dev/null & +emcc $@ -g ../../engine/fwk.c -I../../engine -o index.html -s FULL_ES3 -s USE_PTHREADS -s USE_GLFW=3 -s SINGLE_FILE=1 -s PRECISE_F32=1 -s TOTAL_MEMORY=256mb -s ENVIRONMENT=worker,web --shell-file template.html -Wfatal-errors --preload-file .art[00].zip -s ALLOW_MEMORY_GROWTH=1 -lidbfs.js && xdg-open http://localhost:8000/index.html + +exit + +:windows + +if "%1"=="" MAKE.bat demo_collide.c + +rem clone emscripten sdk +if not exist "emsdk" ( + git clone https://github.com/emscripten-core/emsdk emsdk + pushd emsdk + call emsdk install 3.0.0 && rem latest + call emsdk activate 3.0.0 && rem latest + popd +) +if "%EMSDK%"=="" call emsdk\emsdk_env.bat --system + +rem cook art +..\..\tools\cook.exe --cook-jobs=1 --cook-ini=..\..\tools\cook.ini +copy .art[00].zip index.data + +rem host webserver:8000 if not open , compile and launch +netstat /ano | find /i "listening" | find ":8000" >nul 2>nul && ( + rem start python -m http.server --bind 127.0.0.1 8000 +) || ( + start python -m http.server --bind 127.0.0.1 8000 +) + +emcc %* -g ..\..\engine\fwk.c -I..\..\engine -o index.html -pthread -s FULL_ES3 -s USE_PTHREADS -s USE_GLFW=3 -s SINGLE_FILE=1 -s PRECISE_F32=1 -s TOTAL_MEMORY=256mb -s ENVIRONMENT=worker,web --shell-file template.html -Wfatal-errors --preload-file .art[00].zip -s ALLOW_MEMORY_GROWTH=1 -lidbfs.js && start "" http://localhost:8000/index.html diff --git a/demos/html5/README.md b/demos/html5/README.md new file mode 100644 index 0000000..5fd015d --- /dev/null +++ b/demos/html5/README.md @@ -0,0 +1,21 @@ +## Known HTML5 issues: +- [x] Game loop is event based (fixed: see `window_loop()`) +- [x] No automated emsdk installation (fixed: see `demos/html5/MAKE.bat`) +- [x] Art must be cooked beforehand (fixed: see `demos/html5/MAKE.bat`) +- [x] No VFS loading (fixed) +- [x] No UI rendering (fixed) +- [x] No cooker (fixed: win,osx,linux) +- [x] No input (fixed) +- [x] No gamepads (fixed) +- [x] No multi-touch (fixed) +- [x] No threads (fixed) +- [ ] No audio +- [ ] No file writing (untested) +- [ ] No glTexture1D() +- [ ] No network +- [ ] No fbos +- [ ] No callstacks +- [ ] No pbos +- [ ] Shaders require (automated?) GL->GLES translation (No postfxs, models, skyboxes, pbrs, ...) (@todo: embed Spir-v/glslcross tools?) +- [ ] Shaders: `vec2 iResolution = vec2(iWidth, iHeight); // ERROR: '=' : global variable initializers must be constant expressions` +- [ ] Shaders: `uniform float var = 1.0f; // ERROR: 'uniform' : cannot initialize this type of qualifier` diff --git a/demos/html5/demo_collide.c b/demos/html5/demo_collide.c new file mode 100644 index 0000000..20ce438 --- /dev/null +++ b/demos/html5/demo_collide.c @@ -0,0 +1,616 @@ +// original code by @vurtun (public domain) +// - rlyeh, public domain. +// +// @todo: fix leaks: poly_free() + +#include "fwk.h" + +// -- demo + +int paused; +camera_t cam; + +void game_loop(void *userdata) { + // key handler + if (input_down(KEY_F11) ) window_fullscreen( window_has_fullscreen()^1 ); + if (input_down(KEY_ESC) ) window_loop_exit(); // @todo: break -> window_close() + + // animation + static float dx = 0, dy = 0; + if (input_down(KEY_SPACE)) paused ^= 1; + float delta = (0.25f / 60.f) * !paused; + dx = dx + delta * 2.0f; + dy = dy + delta * 0.8f; + + // fps camera + { + vec3 move = {0}; + vec2 view = {0}; + + // show/hide cursor + bool dragging = input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + if( ui_active() || ui_hover() || gizmo_active() || input_touch_active() ) dragging = false; + window_cursor( !dragging ); + + // keyboard/mouse + if( dragging ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * dragging); + move = add3(move, wasdec); + view = add2(view, mouse); + + // gamepad + vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f /*15% deadzone*/); + vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), 0.15f /*15% deadzone*/); + vec3 gamepad_move = scale3(vec3(filtered_lpad.x, input(GAMEPAD_LT) - input(GAMEPAD_RT), filtered_lpad.y), 1.0f); + vec2 gamepad_view = scale2(filtered_rpad, 1.0f); + move = add3(move, gamepad_move); + view = add2(view, gamepad_view); + + // multi-touch + vec2 touch_move = input_touch_delta_from_origin(0, 0.0125f /*sensitivityFwd*/); // button #0 (left border) + vec2 touch_view = input_touch_delta(1, 0.125f /*sensitivityRot*/); // button #1 (right border) + move = add3(move, vec3(touch_move.x, 0, -touch_move.y)); + view = add2(view, vec2(touch_view.x, -touch_view.y)); + + // apply inputs + camera_move(&cam, move.x,move.y,move.z); + camera_fps(&cam, view.x,view.y); + } + + // projview matrix + mat44 projview; multiply44x2(projview, cam.proj, cam.view); + + // rendering + viewport_color3(vec3(0.15,0.15,0.15)); +#if 0 + viewport_clear(true, true); + viewport_clip(vec2(0,0), vec2(window_width(), window_height())); +#endif + + // debug draw collisions + { + // 3D + glEnable(GL_DEPTH_TEST); + + // grid + ddraw_grid(0); + + { + // Triangle-Ray Intersection*/ + vec3 ro, rd; + int suc; + + triangle tri = { vec3(-9,1,28), vec3(-10,0,28), vec3(-11,1,28) }; + + // ray + ro = vec3(-10,-1,20); + rd = vec3(-10+0.4f*sinf(dx), 2.0f*cosf(dy), 29.81023f); + rd = sub3(rd, ro); + rd = norm3(rd); + + ray r = ray(ro, rd); + hit *hit = ray_hit_triangle(r, tri); + if (hit) { + // point of intersection + ddraw_color(RED); + ddraw_box(hit->p, vec3(0.10f, 0.10f, 0.10f)); + + // intersection normal + ddraw_color(BLUE); + vec3 v = add3(hit->p, hit->n); + ddraw_arrow(hit->p, v); + } + + // line + ddraw_color(RED); + rd = scale3(rd,10); + rd = add3(ro,rd); + ddraw_line(ro, rd); + + // triangle + if (hit) ddraw_color(RED); + else ddraw_color(WHITE); + ddraw_triangle(tri.p0,tri.p1,tri.p2); + } + { + // Plane-Ray Intersection*/ + vec3 ro, rd; + mat33 rot; + + // ray + static float d = 0; + d += delta * 2.0f; + ro = vec3(0,-1,20); + rd = vec3(0.1f, 0.5f, 9.81023f); + rd = sub3(rd, ro); + rd = norm3(rd); + + // rotation + rotation33(rot, deg(d), 0,1,0); + rd = mulv33(rot, rd); + + // intersection + ray r = ray(ro, rd); + plane pl = plane(vec3(0,0,28), vec3(0,0,1)); + hit *hit = ray_hit_plane(r, pl); + if (hit) { + // point of intersection + ddraw_color(RED); + ddraw_box(hit->p, vec3(0.10f, 0.10f, 0.10f)); + + // intersection normal + ddraw_color(BLUE); + vec3 v = add3(hit->p, hit->n); + ddraw_arrow(hit->p, v); + ddraw_color(RED); + } + // line + ddraw_color(RED); + rd = scale3(rd,9); + rd = add3(ro,rd); + ddraw_line(ro, rd); + + // plane + if (hit) ddraw_color(RED); + else ddraw_color(WHITE); + ddraw_plane(vec3(0,0,28), vec3(0,0,1), 3.0f); + } + { + // Sphere-Ray Intersection*/ + vec3 ro, rd; + sphere s; + + // ray + ro = vec3(0,-1,0); + rd = vec3(0.4f*sinf(dx), 2.0f*cosf(dy), 9.81023f); + rd = sub3(rd, ro); + rd = norm3(rd); + + ray r = ray(ro, rd); + s = sphere(vec3(0,0,8), 1); + hit *hit = ray_hit_sphere(r, s); + if(hit) { + // points of intersection + vec3 in = add3(ro,scale3(rd,hit->t0)); + + ddraw_color(GREEN); + ddraw_box(in, vec3(0.05f, 0.05f, 0.05f)); + + in = add3(ro,scale3(rd,hit->t1)); + + ddraw_color(YELLOW); + ddraw_box(in, vec3(0.05f, 0.05f, 0.05f)); + + // intersection normal + ddraw_color(BLUE); + vec3 v = add3(hit->p, hit->n); + ddraw_arrow(hit->p, v); + ddraw_color(RED); + } + // line + ddraw_color(RED); + rd = scale3(rd,10); + rd = add3(ro,rd); + ddraw_line(ro, rd); + + // sphere + if (hit) ddraw_color(RED); + else ddraw_color(WHITE); + ddraw_sphere(vec3(0,0,8), 1); + } + { // ray-aabb + aabb bounds = aabb(vec3(10-0.5f,-0.5f,7.5f), vec3(10.5f,0.5f,8.5f)); + + vec3 ro = vec3(10,-1,0); + vec3 rd = vec3(10+0.4f*sinf(dx), 2.0f*cosf(dy), 9.81023f); + rd = norm3(sub3(rd, ro)); + ray r = ray(ro, rd); + + hit *hit = ray_hit_aabb(r, bounds); + if(hit) { + // points of intersection + vec3 in; + in = scale3(rd,hit->t0); + in = add3(ro,in); + + ddraw_color(RED); + ddraw_box(in, vec3(0.05f, 0.05f, 0.05f)); + + in = scale3(rd,hit->t1); + in = add3(ro,in); + + ddraw_color(RED); + ddraw_box(in, vec3(0.05f, 0.05f, 0.05f)); + + // intersection normal + ddraw_color(BLUE); + vec3 v = add3(hit->p, hit->n); + ddraw_arrow(hit->p, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + ddraw_box(vec3(10,0,8), vec3(1,1,1)); + + // line + ddraw_color(RED); + rd = scale3(rd,10); + rd = add3(ro,rd); + ddraw_line(ro, rd); + } + { + // Sphere-Sphere intersection*/ + sphere a = sphere(vec3(-10,0,8), 1); + sphere b = sphere(vec3(-10+0.6f*sinf(dx), 3.0f*cosf(dy),8), 1); + hit *m = sphere_hit_sphere(a, b); + if (m) { + vec3 v; + ddraw_color(BLUE); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + v = add3(m->contact_point, m->normal); + ddraw_arrow(m->contact_point, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + + ddraw_sphere(a.c, 1); + ddraw_sphere(b.c, 1); + } + { + // AABB-AABB intersection*/ + const float x = 10+0.6f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = 20.0f; + + aabb a = aabb(vec3(10-0.5f,-0.5f,20-0.5f), vec3(10+0.5f,0.5f,20.5f)); + aabb b = aabb(vec3(x-0.5f,y-0.5f,z-0.5f), vec3(x+0.5f,y+0.5f,z+0.5f)); + hit *m = aabb_hit_aabb(a, b); + if(m) { + vec3 v; + ddraw_color(BLUE); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + v = add3(m->contact_point, m->normal); + ddraw_arrow(m->contact_point, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + + ddraw_box(vec3(10,0,20), vec3(1,1,1)); + ddraw_box(vec3(x,y,z), vec3(1,1,1)); + } + { + // Capsule-Capsule intersection*/ + const float x = 20+0.4f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = 28.5f; + + capsule a = capsule(vec3(20.0f,-1.0f,28.0f), vec3(20.0f,1.0f,28.0f), 0.2f); + capsule b = capsule(vec3(x,y-1.0f,z), vec3(x,y+1.0f,z-1.0f), 0.2f); + hit *m = capsule_hit_capsule(a, b); + if( m ) { + vec3 v; + ddraw_color(BLUE); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + v = add3(m->contact_point, m->normal); + ddraw_arrow(m->contact_point, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + ddraw_capsule(vec3(x,y-1.0f,z), vec3(x,y+1.0f,z-1.0f), 0.2f); + ddraw_capsule(vec3(20.0f,-1.0f,28.0f), vec3(20.0f,1.0f,28.0f), 0.2f); + } + { + // AABB-Sphere intersection*/ + aabb a = aabb(vec3(20-0.5f,-0.5f,7.5f), vec3(20.5f,0.5f,8.5f)); + sphere s = sphere(vec3(20+0.6f*sinf(dx), 3.0f*cosf(dy),8), 1); + hit *m = aabb_hit_sphere(a, s); + if(m) { + vec3 v; + ddraw_color(BLUE); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + v = add3(m->contact_point, m->normal); + ddraw_arrow(m->contact_point, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + + ddraw_box(vec3(20,0,8), vec3(1,1,1)); + ddraw_sphere(s.c, 1); + } + { + // Sphere-AABB intersection*/ + const float x = 10+0.6f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = -8.0f; + + sphere s = sphere(vec3(10,0,-8), 1); + aabb a = aabb(vec3(x-0.5f,y-0.5f,z-0.5f), vec3(x+0.5f,y+0.5f,z+0.5f)); + hit *m = sphere_hit_aabb(s, a); + if(m) { + vec3 v; + ddraw_color(BLUE); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + v = add3(m->contact_point, m->normal); + ddraw_arrow(m->contact_point, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + + ddraw_box(vec3(x,y,z), vec3(1,1,1)); + ddraw_sphere(s.c, 1); + } + { + // Capsule-Sphere intersection*/ + capsule c = capsule(vec3(-20.5f,-1.0f,7.5f), vec3(-20+0.5f,1.0f,8.5f), 0.2f); + sphere b = sphere(vec3(-20+0.6f*sinf(dx), 3.0f*cosf(dy),8), 1); + hit *m = capsule_hit_sphere(c, b); + if(m) { + vec3 v; + ddraw_color(BLUE); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + v = add3(m->contact_point, m->normal); + ddraw_arrow(m->contact_point, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + ddraw_sphere(b.c, 1); + ddraw_capsule(vec3(-20.5f,-1.0f,7.5f), vec3(-20+0.5f,1.0f,8.5f), 0.2f); + } + { + // Sphere-Capsule intersection*/ + const float x = 20+0.4f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = -8; + + sphere s = sphere(vec3(20,0,-8), 1); + capsule c = capsule(vec3(x,y-1.0f,z), vec3(x,y+1.0f,z-1.0f), 0.2f); + hit *m = sphere_hit_capsule(s, c); + if(m) { + vec3 v; + ddraw_color(BLUE); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + v = add3(m->contact_point, m->normal); + ddraw_arrow(m->contact_point, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + + ddraw_capsule(vec3(x,y-1.0f,z), vec3(x,y+1.0f,z-1.0f), 0.2f); + ddraw_sphere(s.c, 1); + } + { + // Capsule-AABB intersection*/ + const float x = -20+0.6f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = 28.0f; + + capsule c = capsule(vec3(-20.5f,-1.0f,27.5f), vec3(-20+0.5f,1.0f,28.5f), 0.2f); + aabb b = aabb(vec3(x-0.5f,y-0.5f,z-0.5f), vec3(x+0.5f,y+0.5f,z+0.5f)); + hit *m = capsule_hit_aabb(c, b); + if(m) { + vec3 v; + ddraw_color(BLUE); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + v = add3(m->contact_point, m->normal); + ddraw_arrow(m->contact_point, v); + ddraw_color(RED); + } else ddraw_color(WHITE); + ddraw_box(vec3(x,y,z), vec3(1,1,1)); + ddraw_capsule(vec3(-20.5f,-1.0f,27.5f), vec3(-20+0.5f,1.0f,28.5f), 0.2f); + } + { + // AABB-Capsule intersection*/ + const float x = 0.4f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = -8; + + aabb a = aabb(vec3(-0.5f,-0.5f,-8.5f), vec3(0.5f,0.5f,-7.5f)); + capsule c = capsule(vec3(x,y-1.0f,z), vec3(x,y+1.0f,z-1.0f), 0.2f); + hit *m = aabb_hit_capsule(a, c); + if(m) { + ddraw_color(RED); + ddraw_box(m->contact_point, vec3(0.05f, 0.05f, 0.05f)); + ddraw_arrow(m->contact_point, add3(m->contact_point, m->normal)); + } else ddraw_color(WHITE); + + ddraw_capsule(vec3(x,y-1.0f,z), vec3(x,y+1.0f,z-1.0f), 0.2f); + ddraw_box(vec3(0,0,-8.0f), vec3(1,1,1)); + } + { + // poly(Pyramid)-Sphere (GJK) intersection*/ + sphere s = sphere(vec3(-10+0.6f*sinf(dx), 3.0f*cosf(dy),-8), 1); + poly pyr = pyramid(vec3(-10.5f,-0.5f,-7.5f), vec3(-10.5f,1.0f,-7.5f), 1.0f); + + gjk_result gjk; + if (poly_hit_sphere(&gjk, pyr, s)) + ddraw_color(RED); + else ddraw_color(WHITE); + + ddraw_sphere(s.c, 1); + ddraw_pyramid(vec3(-10.5f,-0.5f,-7.5f), 0.5f/*vec3(-10.5f,1.0f,-7.5f)*/, 1.0f); + + poly_free(&pyr); + + ddraw_box(gjk.p0, vec3(0.05f, 0.05f, 0.05f)); + ddraw_box(gjk.p1, vec3(0.05f, 0.05f, 0.05f)); + ddraw_line(gjk.p0, gjk.p1); + } + { + // poly(Diamond)-Sphere (GJK) intersection*/ + + sphere s = sphere(vec3(-20+0.6f*sinf(dx), 3.0f*cosf(dy),-8), 1); + poly dmd = diamond(vec3(-20.5f,-0.5f,-7.5f), vec3(-20.5f,1.0f,-7.5f), 0.5f); + + gjk_result gjk; + if (poly_hit_sphere(&gjk, dmd, s)) + ddraw_color(RED); + else ddraw_color(WHITE); + + ddraw_sphere(s.c, 1); + ddraw_diamond(vec3(-20.5f,-0.5f,-7.5f), vec3(-20.5f,1.0f,-7.5f), 0.5f); + + poly_free(&dmd); + + ddraw_box(gjk.p0, vec3(0.05f, 0.05f, 0.05f)); + ddraw_box(gjk.p1, vec3(0.05f, 0.05f, 0.05f)); + ddraw_line(gjk.p0, gjk.p1); + } + { + // poly(Pyramid)-Capsule (GJK) intersection*/ + + const float x = 0.4f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = -15; + + capsule c = capsule(vec3(x,y-1.0f,z), vec3(x,y+1.0f,z), 0.2f); + poly pyr = pyramid(vec3(-0.5f,-0.5f,-15.5f), vec3(-0.5f,1.0f,-15.5f), 1.0f); + + gjk_result gjk; + if (poly_hit_capsule(&gjk, pyr, c)) + ddraw_color(RED); + else ddraw_color(WHITE); + + ddraw_capsule(c.a, c.b, c.r); + ddraw_pyramid(vec3(-0.5f,-0.5f,-15.5f), 0.5f/*vec3(-0.5f,1.0f,-15.5f)*/, 1.0f); + + poly_free(&pyr); + + ddraw_box(gjk.p0, vec3(0.05f, 0.05f, 0.05f)); + ddraw_box(gjk.p1, vec3(0.05f, 0.05f, 0.05f)); + ddraw_line(gjk.p0, gjk.p1); + } + + { + // poly(Diamond)-Capsule (GJK) intersection*/ + + const float x = -10 + 0.4f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = -15; + + capsule c = capsule(vec3(x,y-1.0f,z), vec3(x,y+1.0f,z), 0.2f); + poly dmd = diamond(vec3(-10.5f,-0.5f,-15.5f), vec3(-10.5f,1.0f,-15.5f), 0.5f); + + gjk_result gjk; + if (poly_hit_capsule(&gjk, dmd, c)) + ddraw_color(RED); + else ddraw_color(WHITE); + + ddraw_capsule(c.a, c.b, c.r); + ddraw_diamond(vec3(-10.5f,-0.5f,-15.5f), vec3(-10.5f,1.0f,-15.5f), 0.5f); + + poly_free(&dmd); + + ddraw_box(gjk.p0, vec3(0.05f, 0.05f, 0.05f)); + ddraw_box(gjk.p1, vec3(0.05f, 0.05f, 0.05f)); + ddraw_line(gjk.p0, gjk.p1); + } + + { + // poly(Diamond)-poly(Pyramid) (GJK) intersection*/ + + const float x = -20 + 0.4f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = -15; + + poly pyr = pyramid(vec3(x,y-0.5f,z), vec3(x,y+1,z), 0.8f); + poly dmd = diamond(vec3(-20.5f,-0.5f,-15.5f), vec3(-20.5f,1.0f,-15.5f), 0.5f); + + gjk_result gjk; + if (poly_hit_poly(&gjk, dmd, pyr)) + ddraw_color(RED); + else ddraw_color(WHITE); + + ddraw_pyramid(vec3(x,y-0.5f,z), 1/*vec3(x,y+1,z)*/, 1/*0.8f*/); + ddraw_diamond(vec3(-20.5f,-0.5f,-15.5f), vec3(-20.5f,1.0f,-15.5f), 0.5f); + + poly_free(&dmd); + poly_free(&pyr); + + ddraw_box(gjk.p0, vec3(0.05f, 0.05f, 0.05f)); + ddraw_box(gjk.p1, vec3(0.05f, 0.05f, 0.05f)); + ddraw_line(gjk.p0, gjk.p1); + } + { + // poly(Pyramid)-poly(Diamond) (GJK) intersection*/ + + const float x = 10 + 0.4f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = -15; + + poly dmd = diamond(vec3(x,y-0.5f,z), vec3(x,y+1,z), 0.5f); + poly pyr = pyramid(vec3(10.5f,-0.5f,-15.5f), vec3(10.5f,1.0f,-15.5f), 1.0f); + + gjk_result gjk; + if (poly_hit_poly(&gjk, dmd, pyr)) + ddraw_color(RED); + else ddraw_color(WHITE); + + ddraw_diamond(vec3(x,y-0.5f,z), vec3(x,y+1,z), 0.5f); + ddraw_pyramid(vec3(10.5f,-0.5f,-15.5f), 0.5f/*vec3(10.5f,1.0f,-15.5f)*/, 1.0f); + + poly_free(&dmd); + poly_free(&pyr); + + ddraw_box(gjk.p0, vec3(0.05f, 0.05f, 0.05f)); + ddraw_box(gjk.p1, vec3(0.05f, 0.05f, 0.05f)); + ddraw_line(gjk.p0, gjk.p1); + } + { + // poly(Diamond)-AABB (GJK) intersection*/ + + const float x = 20 + 0.4f*sinf(dx); + const float y = 3.0f*cosf(dy); + const float z = -15; + + poly dmd = diamond(vec3(x,y-0.5f,z), vec3(x,y+1,z), 0.5f); + aabb a = aabb(vec3(19.5f,-0.5f,-14.5f), vec3(20.5f,0.5f,-15.5f)); + + gjk_result gjk; + if (poly_hit_aabb(&gjk, dmd, a)) + ddraw_color(RED); + else ddraw_color(WHITE); + + poly_free(&dmd); + + ddraw_diamond(vec3(x,y-0.5f,z), vec3(x,y+1,z), 0.5f); + ddraw_box(vec3(20,0,-15), vec3(1,1,1)); + + ddraw_box(gjk.p0, vec3(0.05f, 0.05f, 0.05f)); + ddraw_box(gjk.p1, vec3(0.05f, 0.05f, 0.05f)); + ddraw_line(gjk.p0, gjk.p1); + } + } + + //fx_begin(); + //ddraw_flush(); + //fx_end(); + + if( ui_panel("Audio", 0) ) { + if( ui_button("test audio") ) { + // audio (both clips & streams) + static audio_t voice; voice = audio_clip("coin.wav"); // "pew.sfxr" + static audio_t stream; stream = audio_stream("wrath_of_the_djinn.xm"); // "larry.mid" + audio_play(voice, 0); + audio_play(stream, 0); + } + ui_panel_end(); + } + + if( ui_panel("FX", 0) ) { + for( int i = 0; i < 64; ++i ) { + char *name = fx_name(i); if( !name ) break; + bool b = fx_enabled(i); + if( ui_bool(name, &b) ) fx_enable(i, fx_enabled(i) ^ 1); + } + ui_panel_end(); + } +} + + +int main(void) { + // 75% sized, msaa x4 enabled + window_create(0.75f, WINDOW_MSAA4); + window_title( "FWK - SPACE pauses simulation" ); + +// for(const char **list = file_list("fx**.fs"); *list; list++) { +// //fx_load(*list); +// } + + // camera that points to origin + cam = camera(); + + // main loop + window_loop(game_loop, NULL); +} diff --git a/demos/html5/demo_ui.c b/demos/html5/demo_ui.c new file mode 100644 index 0000000..c5b44a2 --- /dev/null +++ b/demos/html5/demo_ui.c @@ -0,0 +1,57 @@ +#include "fwk.h" + +void render(void *arg) { + static int integer = 42; + static bool toggle = true; + static bool boolean = true; + static float floating = 3.14159; + static float float2[2] = {1,2}; + static float float3[3] = {1,2,3}; + static float float4[4] = {1,2,3,4}; + static float rgb[3] = {0.84,0.67,0.17}; + static float rgba[4] = {0.67,0.90,0.12,1}; + static float slider = 0.5f; + static float slider2 = 0.5f; + static char string[64] = "hello world 123"; + static int item = 0; const char *list[] = {"one","two","three"}; + static bool show_dialog = false; + static uint8_t bitmask = 0x55; + + if( ui_panel("UI", 0)) { + if( ui_label("my label")) {} + if( ui_label("my label with tooltip@built on " __DATE__ " " __TIME__)) {} + if( ui_separator() ) {} + if( ui_bool("my bool", &boolean) ) puts("bool changed"); + if( ui_int("my int", &integer) ) puts("int changed"); + if( ui_float("my float", &floating) ) puts("float changed"); + if( ui_buffer("my string", string, 64) ) puts("string changed"); + if( ui_separator() ) {} + if( ui_slider("my slider", &slider)) puts("slider changed"); + if( ui_slider2("my slider 2", &slider2, va("%.2f", slider2))) puts("slider2 changed"); + if( ui_separator() ) {} + if( ui_list("my list", list, 3, &item ) ) puts("list changed"); + if( ui_separator() ) {} + if( ui_color3f("my color3", rgb) ) puts("color3 changed"); + if( ui_color4f("my color4@this is a tooltip", rgba) ) puts("color4 changed"); + if( ui_separator() ) {} + if( ui_float2("my float2", float2) ) puts("float2 changed"); + if( ui_float3("my float3", float3) ) puts("float3 changed"); + if( ui_float4("my float4", float4) ) puts("float4 changed"); + if( ui_bits8("my bitmask", &bitmask) ) printf("bitmask changed %x\n", bitmask); + if( ui_separator() ) {} + if( ui_toggle("my toggle", &toggle) ) printf("toggle %s\n", toggle ? "on":"off"); + if( ui_separator() ) {} + if( ui_image("my image", texture_checker().id, 0, 0) ) { puts("image clicked"); } + if( ui_button("my button") ) { puts("button clicked"); show_dialog = true; } + if( ui_dialog("my dialog", __FILE__ "\n" __DATE__ "\n" "Public Domain.", 2/*two buttons*/, &show_dialog) ) {} + + ui_panel_end(); + } + + input_demo(); +} + +int main() { + window_create(0.75f, 0); + window_loop(render, NULL); +} diff --git a/demos/html5/index.worker.coi.min.js b/demos/html5/index.worker.coi.min.js new file mode 100644 index 0000000..e79fce1 --- /dev/null +++ b/demos/html5/index.worker.coi.min.js @@ -0,0 +1,2 @@ +/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ +let coepCredentialless=!1;"undefined"==typeof window?(self.addEventListener("install",(()=>self.skipWaiting())),self.addEventListener("activate",(e=>e.waitUntil(self.clients.claim()))),self.addEventListener("message",(e=>{e.data&&("deregister"===e.data.type?self.registration.unregister().then((()=>self.clients.matchAll())).then((e=>{e.forEach((e=>e.navigate(e.url)))})):"coepCredentialless"===e.data.type&&(coepCredentialless=e.data.value))})),self.addEventListener("fetch",(function(e){const r=e.request;if("only-if-cached"===r.cache&&"same-origin"!==r.mode)return;const s=coepCredentialless&&"no-cors"===r.mode?new Request(r,{credentials:"omit"}):r;e.respondWith(fetch(s).then((e=>{if(0===e.status)return e;const r=new Headers(e.headers);return r.set("Cross-Origin-Embedder-Policy",coepCredentialless?"credentialless":"require-corp"),coepCredentialless||r.set("Cross-Origin-Resource-Policy","cross-origin"),r.set("Cross-Origin-Opener-Policy","same-origin"),new Response(e.body,{status:e.status,statusText:e.statusText,headers:r})})).catch((e=>console.error(e))))}))):(()=>{const e={shouldRegister:()=>!0,shouldDeregister:()=>!1,coepCredentialless:()=>(window.chrome!==undefined||window.netscape!==undefined),doReload:()=>window.location.reload(),quiet:!1,...window.coi},r=navigator;r.serviceWorker&&r.serviceWorker.controller&&(r.serviceWorker.controller.postMessage({type:"coepCredentialless",value:e.coepCredentialless()}),e.shouldDeregister()&&r.serviceWorker.controller.postMessage({type:"deregister"})),!1===window.crossOriginIsolated&&e.shouldRegister()&&(window.isSecureContext?r.serviceWorker&&r.serviceWorker.register(window.document.currentScript.src).then((s=>{!e.quiet&&console.log("COOP/COEP Service Worker registered",s.scope),s.addEventListener("updatefound",(()=>{!e.quiet&&console.log("Reloading page to make use of updated COOP/COEP Service Worker."),e.doReload()})),s.active&&!r.serviceWorker.controller&&(!e.quiet&&console.log("Reloading page to make use of COOP/COEP Service Worker."),e.doReload())}),(r=>{!e.quiet&&console.error("COOP/COEP Service Worker failed to register:",r)})):!e.quiet&&console.log("COOP/COEP Service Worker not registered, a secure context is required."))})(); diff --git a/demos/html5/template.html b/demos/html5/template.html new file mode 100644 index 0000000..7eb37df --- /dev/null +++ b/demos/html5/template.html @@ -0,0 +1,142 @@ + + + + + + + + + + + + +
+ +
+ + + {{{ SCRIPT }}} + + + + + + + diff --git a/tools/plugins/.gitkeep b/engine/bind/; type `make bind` to generate bindings similarity index 100% rename from tools/plugins/.gitkeep rename to engine/bind/; type `make bind` to generate bindings diff --git a/engine/bind/; type `make dll` to generate dll b/engine/bind/; type `make dll` to generate dll new file mode 100644 index 0000000..e69de29 diff --git a/engine/bind/MAKE.bat b/engine/bind/MAKE.bat new file mode 100644 index 0000000..68d12e1 --- /dev/null +++ b/engine/bind/MAKE.bat @@ -0,0 +1,17 @@ +#!/bin/bash 2>nul || goto :windows + +sh ../../MAKE.bat dll +sh ../../MAKE.bat bind + +./luajit.osx hello.lua +./luajit.linux hello.lua +python hello.py + +exit +:windows + +call ..\..\make.bat dll +call ..\..\make.bat bind + +luajit hello.lua +python hello.py diff --git a/demos/lua/fwk.lua b/engine/bind/fwk.lua similarity index 100% rename from demos/lua/fwk.lua rename to engine/bind/fwk.lua diff --git a/engine/bind/fwk.py b/engine/bind/fwk.py new file mode 100644 index 0000000..2f0c882 --- /dev/null +++ b/engine/bind/fwk.py @@ -0,0 +1,26 @@ +import os +import sys +import ctypes +import cffi + +ffi = cffi.FFI() +with open('./fwk.lua') as f: + lines = [line for line in f if not line.startswith('#')] + lines = [line for line in lines if not 'va_list' in line] + lines = [line for line in lines if not 'inline ' in line] + lines = [line for line in lines if not line.startswith('typedef union ') ] + lines = [line for line in lines if not '//lcpp INF' in line ] + data = ''.join(lines) + data = data[data.find('[[')+2:data.find(']]')] + data = ''' + typedef struct vec2i { float x,y; } vec2i; + typedef struct vec3i { float x,y,z; } vec3i; + typedef struct vec2 { float x,y; } vec2; + typedef struct vec3 { float x,y,z; } vec3; + typedef struct vec4 { float x,y,z,w; } vec4; + typedef struct quat { float x,y,z,w; } quat; + typedef union frustum frustum; + typedef union json_t json_t; + ''' + data + ffi.cdef(data) +fwk = ffi.dlopen('./fwk.dll') diff --git a/engine/bind/hello.lua b/engine/bind/hello.lua new file mode 100644 index 0000000..522ec4c --- /dev/null +++ b/engine/bind/hello.lua @@ -0,0 +1,56 @@ +-- this will run on vanilla luajit.exe, provided that fwk.dll and this file are all present in same folder + +local fwk=require('fwk') + +-- specify location of cookbook +fwk.cook_config("../../tools/cook.ini"); + +-- create 75% sized + MSAAx2 anti-aliased window +fwk.window_create(75.0, fwk.WINDOW_MSAA2) + +-- set window title +fwk.window_title("hello luajit") + +-- config girl +local girl = fwk.model('kgirl/kgirls01.fbx', 0) +local girl_frame = 0 +local girl_pivot = fwk.mat44() +fwk.rotationq44(girl_pivot, fwk.eulerq(fwk.vec3(0,0,0))) +fwk.scale44(girl_pivot, 2,2,2) + +-- config & play music +local music = fwk.audio_stream('larry.mid') -- 'wrath_of_the_djinn.xm' +fwk.audio_play(music, 0); + +-- config camera +local cam = fwk.camera() + +-- main loop +while fwk.window_swap() == 1 do + -- fps camera + local grabbed = fwk.input(fwk.MOUSE_L) == 1 or fwk.input(fwk.MOUSE_R) == 1 + fwk.window_cursor( fwk.ui_active() == 1 or fwk.ui_hover() == 1 and 1 or (not grabbed) ) + if( fwk.window_has_cursor() ~= 1 ) then + local wasdec3 = fwk.vec3(fwk.input(fwk.KEY_D)-fwk.input(fwk.KEY_A),fwk.input(fwk.KEY_E)-(fwk.input(fwk.KEY_C)),fwk.input(fwk.KEY_W)-fwk.input(fwk.KEY_S)) + local look2 = fwk.scale2(fwk.vec2(fwk.input_diff(fwk.MOUSE_X), -fwk.input_diff(fwk.MOUSE_Y)), 0.2) + local move3 = fwk.scale3(wasdec3, cam.speed) + fwk.camera_move(cam, wasdec3.x,wasdec3.y,wasdec3.z) + fwk.camera_fps(cam, look2.x,look2.y) + end + + -- draw grid/axis + fwk.ddraw_grid(0) + fwk.ddraw_flush() + + -- animate girl + local delta = fwk.window_delta() * 30 -- 30fps anim + girl_frame = fwk.model_animate(girl, girl_frame + delta) + + -- draw girl + fwk.model_render(girl, cam.proj, cam.view, girl_pivot, 0) + + -- showcase ui + if fwk.ui_panel("luajit", 0) == 1 then + fwk.ui_panel_end() + end +end diff --git a/engine/bind/hello.py b/engine/bind/hello.py new file mode 100644 index 0000000..54e5421 --- /dev/null +++ b/engine/bind/hello.py @@ -0,0 +1,10 @@ +import os +from fwk import fwk + +fwk.window_create(75.0, fwk.WINDOW_MSAA2) +fwk.window_title(b'hello Python') +cam = fwk.camera() +while fwk.window_swap(): + fwk.ddraw_grid(0) + +os._exit(0) \ No newline at end of file diff --git a/engine/bind/libluajit.dylib b/engine/bind/libluajit.dylib new file mode 100644 index 0000000..4d3b945 Binary files /dev/null and b/engine/bind/libluajit.dylib differ diff --git a/engine/bind/libluajit.so b/engine/bind/libluajit.so new file mode 100644 index 0000000..0ee9928 Binary files /dev/null and b/engine/bind/libluajit.so differ diff --git a/engine/bind/lua51.dll b/engine/bind/lua51.dll new file mode 100644 index 0000000..eef6c27 Binary files /dev/null and b/engine/bind/lua51.dll differ diff --git a/engine/bind/luajit.exe b/engine/bind/luajit.exe new file mode 100644 index 0000000..e7c6f97 Binary files /dev/null and b/engine/bind/luajit.exe differ diff --git a/engine/bind/luajit.linux b/engine/bind/luajit.linux new file mode 100644 index 0000000..741a312 Binary files /dev/null and b/engine/bind/luajit.linux differ diff --git a/engine/bind/luajit.osx b/engine/bind/luajit.osx new file mode 100644 index 0000000..9e957c3 Binary files /dev/null and b/engine/bind/luajit.osx differ diff --git a/engine/fwk b/engine/fwk index 5c60a9c..2f15f18 100644 --- a/engine/fwk +++ b/engine/fwk @@ -235410,7 +235410,7 @@ int thread_detach( thread_ptr_t thread ) return CloseHandle( (HANDLE) thread ) != 0; - #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return pthread_detach( (pthread_t) thread ) == 0; diff --git a/engine/fwk.c b/engine/fwk.c index 41faa36..78bc96d 100644 --- a/engine/fwk.c +++ b/engine/fwk.c @@ -18316,7 +18316,7 @@ int ui_label_(const char *label, int alignment) { struct nk_style *style = &ui_ctx->style; bool bold = label[0] == '*'; label += bold; -struct nk_font *font = bold ? nk_glfw.atlas.fonts->next->next /*3rd font*/ : NULL; // list +struct nk_font *font = bold && nk_glfw.atlas.fonts->next ? nk_glfw.atlas.fonts->next->next /*3rd font*/ : NULL; // list if( !has_icon ) { // set bold style and color if needed diff --git a/engine/fwk.html b/engine/fwk.html index 6303334..1630390 100644 --- a/engine/fwk.html +++ b/engine/fwk.html @@ -593,13 +593,13 @@ details > summary::-webkit-details-marker { **F·W·K** -|Version: | {{VERSION}} | +|Version: | 2023.7 | |:--------------|:------------| |Branch: | main | -|Commit: | 1 | +|Commit: | 2 | -# [F·W·K {{VERSION}}](https://github.com/r-lyeh/FWK) +# [F·W·K 2023.7 ](https://github.com/r-lyeh/FWK) ## a b o u t - https://github.com/r-lyeh/FWK is a 3D game framework in C, with Luajit bindings. @@ -646,12 +646,8 @@ void assert_positive( int my_int ) { // lowercase snake_case everywhere
Goals -- [x] ~~C++~~. C. -- [x] ~~Fast~~. Naive. -- [x] ~~Modern~~. Simple. -- [x] ~~Full featured~~. Small. -- [x] ~~Rich build system~~. Single file. -- [x] ~~Royaltie fee~~. Free and unlicensed. +- [x] ~~Full featured~~, ~~Fast~~, ~~Modern C++~~. Small, Naive, Simple C. +- [x] ~~Rich build system~~, ~~Royaltie fee~~. Single file, Freely unlicensed.
@@ -659,7 +655,7 @@ void assert_positive( int my_int ) { // lowercase snake_case everywhere - [x] Pipeline: configurable and integrated [asset pipeline](tools/cook.ini). -- [x] Embedded: single-file header, all dependencies included. +- [x] Embedded: [single-file header](engine/joint/fwk.h), all dependencies included. - [x] Compiler: MSVC, MINGW64, TCC, GCC, clang, clang-cl and emscripten. - [x] Linkage: Both static linkage and dynamic .dll/.so/.dylib support. - [x] Platform: Windows, Linux and OSX. Partial HTML5/Web support. @@ -694,7 +690,7 @@ void assert_positive( int my_int ) { // lowercase snake_case everywhere - [x] Scene handling. - [x] Profiler, stats and leaks finder. - [x] [Editor (wip)](https://user-images.githubusercontent.com/35402248/174457347-f787a6a2-aac8-404c-a5da-f44310c3d432.mp4). -- [x] [Documentation (wip)](https://bit.ly/-f-w-k-). +- [x] [Documentation (wip)](https://bit.ly/fwk2023). @@ -702,21 +698,32 @@ void assert_positive( int my_int ) { // lowercase snake_case everywhere - [ ] AI pass: actors, waypoints, pathfinding, behavior trees (h/fsm,goap), and navmesh generation. -- [ ] Render pass: reverse-Z, automatic LODs, impostors, decals. - - [ ] Materials: (colors✱, textures✱, matcaps✱, videos✱, shadertoys✱). Shadertoys as post-fx✱. - - [ ] Lighting: Hard/soft shadow mapping (VSM,CCSM). Baked lightmaps. Refl probes. Integrated PBR. - [ ] Network/VM pass: Entity/component/systems and worlds. + - [ ] Core pass: struct serialization. - [ ] Message pipeline and replication. - [ ] Digital signals, message buffering and event polling. - [ ] World streaming and level loading. - [ ] Scenegraphs and spatial partioning. BVH, PVS, occluders, frustum culling. - [ ] Server/client architecture. Hybrid P2P. - [ ] NAT traversal. Socketless API, message API and pub/sub wrappers (enet/websocket). +- [ ] Editor pass = netbased + offline rendering + virtual input. + - [ ] Basic: Gizmos✱, scene tree, property editor✱, load/save✱, undo/redo✱, copy/paste, on/off (vis,tick,ddraw,log), vcs. + - [ ] Scenenode: node singleton display, node console, node labels, node outlines✱. + - [ ] Debug: toggles on/off (billboards✱, materials, un/lit, cast shadows, wireframe, skybox✱/mie✱, fog/atmosphere, collide✱, physics). + - [ ] Level: volumes, triggers, platforms, level streaming. + - [ ] Sub-editor: timeline and data tracks, node graphs. + - [ ] Sub-editor: Procedural content, brushes, noise and CSG. + - [ ] Sub-editor: blendshapes, additive anims, head/foot/hand IKs. + - [ ] Script pass: DLL✱ (module->plugin/sys), Lua✱, Luajit✱, Teal✱ and TypeScript. +- [ ] Render pass: reverse-Z, automatic LODs, impostors, decals. + - [ ] Materials: (colors✱, textures✱, matcaps✱, videos✱, shadertoys✱). Shadertoys as post-fx✱. + - [ ] Lighting: Hard/soft shadow mapping (VSM,CCSM). Baked lightmaps. Refl probes. Integrated PBR. - [ ] Tools pass - [ ] Extend shaders + bindings. Per-platform✱, per-type✱, per-asset options. GIF, PKM. - [ ] Extend atlas (sprite/lightmaps). Fit packing (sprites). - [ ] Extend bindings and messaging: parse C headers during cooking stage. - [ ] API pass + - [ ] Extend math: quat2, bezier, catmull. - [ ] Discuss API and freeze it. - [ ] Document everything. @@ -833,17 +840,6 @@ int main() { } ``` -```C -#include "fwk.h" // Minimal HTML5 sample -void render(void *arg) { - if( !input(KEY_ESC) ) puts("hello FWK from HTML5!"); -} -int main() { - window_create(75.0, 0); // 75% size, no extra flags - window_loop(render, NULL); // game loop -} -``` - ```lua local fwk = require("fwk") -- Minimal Lua sample fwk.window_create(75.0,0) -- 75% size, no extra flags @@ -857,6 +853,12 @@ end
Quickstart +- Double-click `MAKE.bat` (Win) or `sh MAKE.bat` (Linux/OSX) to quick start. +- `MAKE.bat all` (Win) or `sh MAKE.bat all` (Linux/OSX) to build everything. +- `MAKE.bat proj` (Win) or `sh MAKE.bat proj` (Linux/OSX) to generate solutions/makefiles. +- `MAKE.bat help` (Win) or `sh MAKE.bat help` (Linux/OSX) for a bunch of options. +- `MAKE.bat hello.c` (Win) or `sh MAKE.bat hello.c` (Linux/OSX) to build a single executable. +- Alternatively, ```bat echo win/vc && cl hello.c echo win/clang-cl && clang-cl hello.c @@ -873,13 +875,9 @@ echo osx && cc -ObjC hello.c -framework cocoa -framework iokit -fr
Cook -- Most asset types need to be cooked before being used in your application. Some other assets like `.png` do not. -- Cooked assets will be written into .zipfiles close to your executable, and mounted before entering game loop. -- Cooked .zipfiles and your executable are the only required assets when releasing your game. -- Cook manually your assets by invoking supplied [`tools/cook` standalone binary](tools/). -- Cook automatically your assets by just playing your game: a runtime cook is already embedded into your binary. - - In order to achieve this, ensure the [`tools/` folder](tools/) is close to your executable. - - This folder contains all the related binaries to perform any asset conversion plus the [cookbook](tools/cook.ini) to do so. +- Assets need to be cooked before being consumed in your application. The [tools/](tools/) folder contains all the related binaries to perform any asset processing plus the [cookbook](tools/cook.ini) to do so. +- Your game will cook all your assets as long as the [`tools/`](tools/) folder is next to your executable. Alternatively, cook them all just by invoking supplied [`tools/cook` standalone binary](tools/). +- In both cases, assets will be cooked and packed into .zipfiles next to your executable, then mounted before entering game loop. These .zipfiles plus your executable are the only required files when releasing your game.
@@ -888,86 +886,100 @@ echo osx && cc -ObjC hello.c -framework cocoa -framework iokit -fr - Any ico/png file named after the executable name will be automatically used as app icon. - Similar to the ico/png case above, the cooked .zipfiles can be named after the main executable as well. -- Dropped files into game window will be imported & saved into [`import/`](art/engine/import) folder. -- Update the gamepad controller database by upgrading the [`gamecontrollerdb.txt`](art/engine/input) file. -- Depending on your IDE, you might need to browse to [`split/`](split/) sources when debugging FWK. +- Dropped files into game window will be imported & saved into [`import/`](engine/art/import/) folder. +- Update the gamepad controller database by upgrading the [`gamecontrollerdb.txt`](engine/art/input/) file. +- Depending on your IDE, you might need to browse to [`engine/split/`](engine/split/) sources when debugging FWK. - Cook assets on demand, as opposed to cook all existing assets on depot, by using `--cook-on-demand` flag. - Linux/OSX users can optionally install wine and use the Windows tools instead (by using `--cook-wine` flag). - Disable automatic cooking by using `--cook-jobs=0` flag (not recommended). -- Generate a project solution by dropping `split/fwk.h, fwk.c and fwk` files into it. +- Generate a project solution by dropping `engine/fwk.h, fwk.c and fwk` files into it. +- Auto-generated Luajit and Python bindings can be found in the [`engine/bind/`](engine/bind/) folder. +
-
Credits (Artwork + demos) +
Credits -- [Nanofactory](https://sketchfab.com/3d-models/kgirls01-d2f946f58a8040ae993cda70c97b302c), for kgirls01 3D model (CC BY-NC-ND 4.0). -- [RottingPixels](https://opengameart.org/content/2d-castle-platformer-tileset-16x16), for castle-tileset (CC0). -- [wwwtyro](https://github.com/wwwtyro/glsl-atmosphere), for nicest rayleigh/mie scattering shader around (CC0). +**Artwork** +[Dean Evans, Raijin](https://youtu.be/RRvYkrrpMKo?t=147 "for the Map song (c)"), +[FMS_Cat](https://gist.github.com/FMS-Cat/a1ccea3ce866c34706084e3526204f4f "for nicest VHS/VCR shader around (MIT)"), +[Goblin165cm](https://sketchfab.com/3d-models/halloween-little-witch-ccc023590bfb4789af9322864e42d1ab "for witch 3D model (CC BY 4.0)"), +[Nuulbee](https://sketchfab.com/3d-models/kgirls01-d2f946f58a8040ae993cda70c97b302c "for kgirls01 3D model (CC BY-NC-ND 4.0)"), +[Quaternius](https://www.patreon.com/quaternius "for the lovely 3D robots (CC0)"), +[Rotting Pixels](https://opengameart.org/content/2d-castle-platformer-tileset-16x16 "for castle-tileset (CC0)"), +[Tom Lewandowski](https://QuestStudios.com "for his MIDI recordings (c)"), +[Rye Terrell](https://github.com/wwwtyro/glsl-atmosphere "for nicest rayleigh/mie scattering shader around (CC0)"), +**Tools** +[Aaron Barany](https://github.com/akb825/Cuttlefish "for cuttlefish (APACHE2)"), +[Arseny Kapoulkine](https://github.com/zeux/pugixml/ "for pugixml (MIT)"), +[Assimp authors](https://github.com/assimp/assimp "for assimp (BSD3)"), +[Bernhard Schelling](https://github.com/schellingb/TinySoundFont "for tml.h (Zlib) and tsf.h (MIT)"), +[Christian Collins](http://www.schristiancollins.com "for GeneralUser GS soundfont (PD)"), +[FFMPEG authors](https://www.ffmpeg.org/ "for ffmpeg (LGPL21)"), +[Imagination](https://developer.imaginationtech.com/pvrtextool/ "for pvrtextoolcli (ITL)"), +[Krzysztof Gabis](https://github.com/kgabis/ape "for split.py/join.py (MIT)"), +[Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo "for iqm.cpp (PD)"), +[Martín Lucas Golini](https://github.com/SpartanJ/eepp/commit/8552941da19380d7a629c4da80a976aec5d39e5c "for emscripten-fs.html (CC0)"), +[Mattias Gustavsson](https://github.com/mattiasgustavsson/libs "for mid.h (PD)"), +[Michael Schmoock](http://github.com/willsteel/lcpp "for lcpp (MIT)"), +[Morgan McGuire](https://casual-effects.com/markdeep/ "for markdeep (BSD2)"), +[Olivier Lapicque, Konstanty Bialkowski](https://github.com/Konstanty/libmodplug "for libmodplug (PD)"), +[Polyglot Team](https://docs.google.com/spreadsheets/d/17f0dQawb-s_Fd7DHgmVvJoEGDMH_yoSd8EYigrb0zmM/edit "for polyglot gamedev (CC0)"), +[Tildearrow](https://github.com/tildearrow/furnace/ "for Furnace (GPL2)"), +[Tomas Pettersson](http://www.drpetter.se/ "for sfxr (PD)"), +[Tor Andersson](https://github.com/ccxvii/asstools "for assiqe.c (BSD)"), +**Runtime** +[Andreas Mantler](https://github.com/ands "for their math library (PD)"), +[Barerose](https://github.com/barerose "for swrap (CC0) and math library (CC0)"), +[Camilla Löwy](https://github.com/elmindreda "for glfw3 (Zlib)"), +[Dave Rand](https://tools.ietf.org/html/rfc1978 "for ppp (PD)"), +[David Herberth](https://github.com/dav1dde/ "for glad generated code (PD)"), +[David Reid](https://github.com/mackron "for miniaudio (PD)"), +[Dominic Szablewski](https://github.com/phoboslab/pl_mpeg "for pl_mpeg (MIT)"), +[Dominik Madarász](https://github.com/zaklaus "for json5 parser (PD)"), +[Eduard Suica](https://github.com/eduardsui/tlse "for tlse (PD)"), +[Evan Wallace](https://github.com/evanw "for their math library (CC0)"), +[Gargaj+cce/Peisik](https://github.com/gargaj/foxotron "for Foxotron/PBR shaders (UNLICENSE)"), +[Guilherme Lampert](https://github.com/glampert "for their math library (PD)"), +[Guillaume Vareille](http://tinyfiledialogs.sourceforge.net "for tinyfiledialogs (ZLIB)"), +[Haruhiko Okumura](https://oku.edu.mie-u.ac.jp/~okumura/compression/ "for lzss (PD)"), +[Igor Pavlov](https://www.7-zip.org/ "for LZMA (PD)"), +[Ilya Muravyov](https://github.com/encode84 "for bcm, balz, crush, ulz, lz4x (PD)"), +[Jon Olick](https://www.jonolick.com/ "for jo_mp1 and jo_mpeg (PD)"), +[Joonas Pihlajamaa](https://github.com/jokkebk/JUnzip "for JUnzip library (PD)"), +[Juliette Focault](https://github.com/juliettef/IconFontCppHeaders/blob/main/IconsMaterialDesign.h "for the generated MD header (ZLIB)"), +[Kristoffer Grönlund](https://github.com/krig "for their math library (CC0)"), +[Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo "for IQM spec & player (PD)"), +[Lee Salzman, V.Hrytsenko, D.Madarász](https://github.com/zpl-c/enet/ "for enet (MIT)"), +[Libtomcrypt](https://github.com/libtom/libtomcrypt "for libtomcrypt (Unlicense)"), +[Lua authors](https://www.lua.org/ "for Lua language (MIT)"), +[Mattias Gustavsson](https://github.com/mattiasgustavsson/libs "for thread.h and https.h (PD)"), +[Micha Mettke](https://github.com/vurtun "for their math library (PD)"), +[Micha Mettke, Chris Willcocks, Dmitry Hrabrov](https://github.com/vurtun/nuklear "for nuklear (PD)"), +[Michael Galetzka](https://github.com/Cultrarius/Swarmz "for swarmz (UNLICENSE)"), +[Morten Vassvik](https://github.com/vassvik/mv_easy_font "for mv_easy_font (Unlicense)"), +[Mārtiņš Možeiko](https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275 "for A* pathfinding (PD)"), +[Omar Cornut, vaiorabbit](https://github.com/ocornut/imgui/pull/3627 "for tables of unicode ranges (MIT-0)"), +[Rabia Alhaffar](https://github.com/Rabios/ice_libs "for ice_batt.h (PD)"), +[Rich Geldreich](https://github.com/richgel999/miniz "for miniz (PD)"), +[Ross Williams](http://ross.net/compression/lzrw3a.html "for lzrw3a (PD)"), +[Samuli Raivio](https://github.com/bqqbarbhg/bq_websocket "for bq_websocket (PD)"), +[Sean Barrett](https://github.com/nothings "for stb_image, stb_image_write, stb_sprintf, stb_truetype and stb_vorbis (PD)"), +[Sebastian Steinhauer](https://github.com/kieselsteini "for sts_mixer (PD)"), +[Stan Melax, Cloud Wu](https://web.archive.org/web/20031204035320/http://www.melax.com/polychop/gdmag.pdf "for polychop C algorithm (PD)"), +[Stefan Gustavson](https://github.com/stegu/perlin-noise "for simplex noise (PD)"), +[Sterling Orsten](https://github.com/sgorsten "for their math library (UNLICENSE)"), +[Tor Andersson](https://github.com/ccxvii/minilibs "for xml.c (PD)"), +[Wolfgang Draxinger](https://github.com/datenwolf "for their math library (WTFPL2)"), -
- -
Credits (Tools) - - -- [Aaron Barany](https://github.com/akb825/Cuttlefish), for cuttlefish (APACHE2). -- [Arseny Kapoulkine](https://github.com/zeux/pugixml/), for pugixml (MIT). -- [Assimp authors](https://github.com/assimp/assimp), for assimp (BSD3). -- [Bernhard Schelling](https://github.com/schellingb/TinySoundFont), for tml.h (Zlib) and tsf.h (MIT). -- [ffmpeg authors](https://www.ffmpeg.org/), for ffmpeg (LGPL21). -- [Imagination](https://developer.imaginationtech.com/pvrtextool/), for pvrtextoolcli (ITL). -- [Krzysztof Gabis](https://github.com/kgabis/ape), for split.py/join.py (MIT). -- [Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo), for iqm.cpp (PD). -- [Mattias Gustavsson](https://github.com/mattiasgustavsson/libs), for mid.h (PD). -- [Michael Schmoock](http://github.com/willsteel/lcpp), for lcpp (MIT). -- [Olivier Lapicque, Konstanty Bialkowski](https://github.com/Konstanty/libmodplug), for libmodplug (PD). -- [Polyglot Team](https://docs.google.com/spreadsheets/d/17f0dQawb-s_Fd7DHgmVvJoEGDMH_yoSd8EYigrb0zmM/edit), for polyglot gamedev (CC0). -- [Tildearrow](https://github.com/tildearrow/furnace/), for Furnace (GPL2). -- [Tomas Pettersson](http://www.drpetter.se/), for sfxr (PD). -- [Tor Andersson](https://github.com/ccxvii/asstools), for assiqe.c (BSD). - -
- -
Credits (Runtime) - - -- [Barerose](https://github.com/barerose), for swrap (CC0). -- [Camilla Löwy](https://github.com/elmindreda), for glfw3 (Zlib). -- [Dave Rand](https://tools.ietf.org/html/rfc1978) for ppp (PD). -- [David Herberth](https://github.com/dav1dde/), for glad generated code (PD). -- [David Reid](https://github.com/mackron), for miniaudio (PD). -- [Dominic Szablewski](https://github.com/phoboslab/pl_mpeg), for pl_mpeg (MIT). -- [Dominik Madarász](https://github.com/zaklaus), for json5 parser (PD). -- [Eduard Suica](https://github.com/eduardsui/tlse), for tlse (PD). -- [Gargaj+cce/Peisik](https://github.com/gargaj/foxotron), for Foxotron/PBR shaders (UNLICENSE). -- [Guillaume Vareille](http://tinyfiledialogs.sourceforge.net), for tinyfiledialogs (ZLIB). -- [Haruhiko Okumura](https://oku.edu.mie-u.ac.jp/~okumura/compression/) for lzss (PD). -- [Igor Pavlov](https://www.7-zip.org/) for LZMA (PD). -- [Ilya Muravyov](https://github.com/encode84) for bcm, balz, crush, ulz, lz4x (PD). -- [Jon Olick](https://www.jonolick.com/), for jo_mp1 and jo_mpeg (PD). -- [Joonas Pihlajamaa](https://github.com/jokkebk/JUnzip), for JUnzip library (PD). -- [Juliette Focault](https://github.com/juliettef/IconFontCppHeaders/blob/main/IconsMaterialDesign.h), for the generated MD header (ZLIB). -- [Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo), for IQM spec & player (PD). -- [Lee Salzman, V.Hrytsenko, D.Madarász](https://github.com/zpl-c/enet/), for enet (MIT). -- [Libtomcrypt](https://github.com/libtom/libtomcrypt), for libtomcrypt (Unlicense). -- [Lua authors](https://www.lua.org/), for Lua language (MIT). -- [Mārtiņš Možeiko](https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275), for A* pathfinding (PD). -- [Mattias Gustavsson](https://github.com/mattiasgustavsson/libs), for thread.h and https.h (PD). -- [Micha Mettke, Chris Willcocks, Dmitry Hrabrov](https://github.com/vurtun/nuklear), for nuklear (PD). -- [Michael Galetzka](https://github.com/Cultrarius/Swarmz), for swarmz (UNLICENSE). -- [Omar Cornut, vaiorabbit](https://github.com/ocornut/imgui/pull/3627), for tables of unicode ranges (MIT-0). -- [Rabia Alhaffar](https://github.com/Rabios/ice_libs), for ice_batt.h (PD). -- [Rich Geldreich](https://github.com/richgel999/miniz), for miniz (PD). -- [Ross Williams](http://ross.net/compression/lzrw3a.html) for lzrw3a (PD). -- [Samuli Raivio](https://github.com/bqqbarbhg/bq_websocket), for bq_websocket (PD). -- [Sean Barrett](https://github.com/nothings), for stb_image, stb_image_write, stb_sprintf, stb_truetype and stb_vorbis (PD). -- [Sebastian Steinhauer](https://github.com/kieselsteini), for sts_mixer (PD). -- [Stan Melax, Cloud Wu](https://web.archive.org/web/20031204035320/http://www.melax.com/polychop/gdmag.pdf), for polychop C algorithm (PD). -- [Stefan Gustavson](https://github.com/stegu/perlin-noise), for simplex noise (PD). -- [Tor Andersson](https://github.com/ccxvii/minilibs), for xml.c (PD). -- [Vassvik](https://github.com/vassvik/mv_easy_font), for mv_easy_font (Unlicense). -- Special thanks to [@ands](https://github.com/ands), [@barerose](https://github.com/barerose), [@datenwolf](https://github.com/datenwolf), [@evanw](https://github.com/evanw), [@glampert](https://github.com/glampert), [@krig](https://github.com/krig), [@sgorsten](https://github.com/sgorsten) and [@vurtun](https://github.com/vurtun) for their math libraries (PD,CC0,WTFPL2,CC0,PD,CC0,Unlicense,PD). +
@@ -981,13 +993,9 @@ This software is released into the [public domain](https://unlicense.org/). Also
Links -

-Issues -Discord
+Still looking for alternatives? [amulet](https://github.com/ianmaclarty/amulet), [aroma](https://github.com/leafo/aroma/), [astera](https://github.com/tek256/astera), [blendelf](https://github.com/jesterKing/BlendELF), [bullordengine](https://github.com/MarilynDafa/Bulllord-Engine), [candle](https://github.com/EvilPudding/candle), [cave](https://github.com/kieselsteini/cave), [chickpea](https://github.com/ivansafrin/chickpea), [corange](https://github.com/orangeduck/Corange), [cute](https://github.com/RandyGaul/cute_framework), [dos-like](https://github.com/mattiasgustavsson/dos-like), [ejoy2d](https://github.com/ejoy/ejoy2d), [exengine](https://github.com/exezin/exengine), [gunslinger](https://github.com/MrFrenik/gunslinger), [hate](https://github.com/excessive/hate), [island](https://github.com/island-org/island), [juno](https://github.com/rxi/juno), [l](https://github.com/Lyatus/L), [lgf](https://github.com/Planimeter/lgf), [limbus](https://github.com/redien/limbus), [love](https://github.com/love2d/love/), [lovr](https://github.com/bjornbytes/lovr), [mini3d](https://github.com/mini3d/mini3d), [mintaro](https://github.com/mackron/mintaro), [mio](https://github.com/ccxvii/mio), [olive.c](https://github.com/tsoding/olive.c), [opensource](https://github.com/w23/OpenSource), [ouzel](https://github.com/elnormous/ouzel/), [pez](https://github.com/prideout/pez), [pixie](https://github.com/mattiasgustavsson/pixie), [punity](https://github.com/martincohen/Punity), [r96](https://github.com/badlogic/r96), [ricotech](https://github.com/dbechrd/RicoTech), [rizz](https://github.com/septag/rizz), [tigr](https://github.com/erkkah/tigr), [yourgamelib](https://github.com/duddel/yourgamelib) -Still looking for alternatives? -[amulet](https://github.com/ianmaclarty/amulet), [aroma](https://github.com/leafo/aroma/), [astera](https://github.com/tek256/astera), [blendelf](https://github.com/jesterKing/BlendELF), [bullordengine](https://github.com/MarilynDafa/Bulllord-Engine), [candle](https://github.com/EvilPudding/candle), [cave](https://github.com/kieselsteini/cave), [chickpea](https://github.com/ivansafrin/chickpea), [corange](https://github.com/orangeduck/Corange), [cute](https://github.com/RandyGaul/cute_framework), [dos-like](https://github.com/mattiasgustavsson/dos-like), [ejoy2d](https://github.com/ejoy/ejoy2d), [exengine](https://github.com/exezin/exengine), [gunslinger](https://github.com/MrFrenik/gunslinger), [hate](https://github.com/excessive/hate), [island](https://github.com/island-org/island), [juno](https://github.com/rxi/juno), [l](https://github.com/Lyatus/L), [lgf](https://github.com/Planimeter/lgf), [limbus](https://github.com/redien/limbus), [love](https://github.com/love2d/love/), [lovr](https://github.com/bjornbytes/lovr), [mini3d](https://github.com/mini3d/mini3d), [mintaro](https://github.com/mackron/mintaro), [mio](https://github.com/ccxvii/mio), [olive.c](https://github.com/tsoding/olive.c), [opensource](https://github.com/w23/OpenSource), [ouzel](https://github.com/elnormous/ouzel/), [pez](https://github.com/prideout/pez), [pixie](https://github.com/mattiasgustavsson/pixie), [punity](https://github.com/martincohen/Punity), [r96](https://github.com/badlogic/r96), [ricotech](https://github.com/dbechrd/RicoTech), [rizz](https://github.com/septag/rizz), [tigr](https://github.com/erkkah/tigr), [yourgamelib](https://github.com/duddel/yourgamelib) -

+Issues Discord
## config diff --git a/engine/joint/fwk.h b/engine/joint/fwk.h index b3bad19..0ccbffe 100644 --- a/engine/joint/fwk.h +++ b/engine/joint/fwk.h @@ -252589,7 +252589,7 @@ int thread_detach( thread_ptr_t thread ) return CloseHandle( (HANDLE) thread ) != 0; - #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return pthread_detach( (pthread_t) thread ) == 0; @@ -347177,7 +347177,7 @@ int ui_label_(const char *label, int alignment) { struct nk_style *style = &ui_ctx->style; bool bold = label[0] == '*'; label += bold; -struct nk_font *font = bold ? nk_glfw.atlas.fonts->next->next /*3rd font*/ : NULL; // list +struct nk_font *font = bold && nk_glfw.atlas.fonts->next ? nk_glfw.atlas.fonts->next->next /*3rd font*/ : NULL; // list if( !has_icon ) { // set bold style and color if needed diff --git a/engine/split/3rd_thread.h b/engine/split/3rd_thread.h index 64c51de..456b84c 100644 --- a/engine/split/3rd_thread.h +++ b/engine/split/3rd_thread.h @@ -873,7 +873,7 @@ int thread_detach( thread_ptr_t thread ) return CloseHandle( (HANDLE) thread ) != 0; - #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return pthread_detach( (pthread_t) thread ) == 0; diff --git a/engine/split/fwk_ui.c b/engine/split/fwk_ui.c index 8426453..35085f6 100644 --- a/engine/split/fwk_ui.c +++ b/engine/split/fwk_ui.c @@ -1436,7 +1436,7 @@ int ui_label_(const char *label, int alignment) { struct nk_style *style = &ui_ctx->style; bool bold = label[0] == '*'; label += bold; -struct nk_font *font = bold ? nk_glfw.atlas.fonts->next->next /*3rd font*/ : NULL; // list +struct nk_font *font = bold && nk_glfw.atlas.fonts->next ? nk_glfw.atlas.fonts->next->next /*3rd font*/ : NULL; // list if( !has_icon ) { // set bold style and color if needed diff --git a/hello.c b/hello.c index 033a1da..03f29a2 100644 --- a/hello.c +++ b/hello.c @@ -3,8 +3,8 @@ // // # quickstart // - win/vc : cl hello.c -// - win/clang-cl : clang-cl hello.c -// - win/tcc : tcc hello.c -m64 +// - win/clang-cl : clang-cl hello.c +// - win/tcc : tools\tcc hello.c -m64 // - win/mingw : gcc hello.c -lws2_32 -lwinmm -ldbghelp -lole32 -luser32 -lgdi32 -lcomdlg32 // - win/clang : clang hello.c -lws2_32 -lwinmm -ldbghelp -lole32 -luser32 -lgdi32 -lcomdlg32 // - linux : cc hello.c -lm -ldl -lpthread -lX11 @@ -12,7 +12,7 @@ // - osx : cc -ObjC hello.c -framework cocoa -framework iokit -framework audiotoolbox #define FWK_IMPLEMENTATION // unrolls single-header implementation -#include "./engine/joint/fwk.h" // single-header file +#include "engine/joint/fwk.h" // single-header file int main() { // options @@ -115,6 +115,7 @@ int main() { bool enabled = fx_enabled(i); if( ui_bool(fx_name(i), &enabled) ) fx_enable(i, enabled); } + ui_panel_end(); } } diff --git a/tools/debugger.lua b/tools/debugger.lua new file mode 100644 index 0000000..b5c420e --- /dev/null +++ b/tools/debugger.lua @@ -0,0 +1,678 @@ +--[[ + Copyright (c) 2020 Scott Lembcke and Howling Moon Software + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + TODO: + * Print short function arguments as part of stack location. + * Properly handle being reentrant due to coroutines. +]] + +local dbg + +-- Use ANSI color codes in the prompt by default. +local COLOR_GRAY = "" +local COLOR_RED = "" +local COLOR_BLUE = "" +local COLOR_YELLOW = "" +local COLOR_RESET = "" +local GREEN_CARET = " => " + +local function pretty(obj, max_depth) + if max_depth == nil then max_depth = dbg.pretty_depth end + + -- Returns true if a table has a __tostring metamethod. + local function coerceable(tbl) + local meta = getmetatable(tbl) + return (meta and meta.__tostring) + end + + local function recurse(obj, depth) + if type(obj) == "string" then + -- Dump the string so that escape sequences are printed. + return string.format("%q", obj) + elseif type(obj) == "table" and depth < max_depth and not coerceable(obj) then + local str = "{" + + for k, v in pairs(obj) do + local pair = pretty(k, 0).." = "..recurse(v, depth + 1) + str = str..(str == "{" and pair or ", "..pair) + end + + return str.."}" + else + -- tostring() can fail if there is an error in a __tostring metamethod. + local success, value = pcall(function() return tostring(obj) end) + return (success and value or "") + end + end + + return recurse(obj, 0) +end + +-- The stack level that cmd_* functions use to access locals or info +-- The structure of the code very carefully ensures this. +local CMD_STACK_LEVEL = 6 + +-- Location of the top of the stack outside of the debugger. +-- Adjusted by some debugger entrypoints. +local stack_top = 0 + +-- The current stack frame index. +-- Changed using the up/down commands +local stack_inspect_offset = 0 + +-- LuaJIT has an off by one bug when setting local variables. +local LUA_JIT_SETLOCAL_WORKAROUND = 0 + +-- Default dbg.read function +local function dbg_read(prompt) + dbg.write(prompt) + io.flush() + return io.read() +end + +-- Default dbg.write function +local function dbg_write(str) + io.write(str) +end + +local function dbg_writeln(str, ...) + if select("#", ...) == 0 then + dbg.write((str or "").."\n") + else + dbg.write(string.format(str.."\n", ...)) + end +end + +local function format_loc(file, line) return COLOR_BLUE..file..COLOR_RESET..":"..COLOR_YELLOW..line..COLOR_RESET end +local function format_stack_frame_info(info) + local filename = info.source:match("@(.*)") + local source = filename and dbg.shorten_path(filename) or info.short_src + local namewhat = (info.namewhat == "" and "chunk at" or info.namewhat) + local name = (info.name and "'"..COLOR_BLUE..info.name..COLOR_RESET.."'" or format_loc(source, info.linedefined)) + return format_loc(source, info.currentline).." in "..namewhat.." "..name +end + +local repl + +-- Return false for stack frames without source, +-- which includes C frames, Lua bytecode, and `loadstring` functions +local function frame_has_line(info) return info.currentline >= 0 end + +local function hook_factory(repl_threshold) + return function(offset, reason) + return function(event, _) + -- Skip events that don't have line information. + if not frame_has_line(debug.getinfo(2)) then return end + + -- Tail calls are specifically ignored since they also will have tail returns to balance out. + if event == "call" then + offset = offset + 1 + elseif event == "return" and offset > repl_threshold then + offset = offset - 1 + elseif event == "line" and offset <= repl_threshold then + repl(reason) + end + end + end +end + +local hook_step = hook_factory(1) +local hook_next = hook_factory(0) +local hook_finish = hook_factory(-1) + +-- Create a table of all the locally accessible variables. +-- Globals are not included when running the locals command, but are when running the print command. +local function local_bindings(offset, include_globals) + local level = offset + stack_inspect_offset + CMD_STACK_LEVEL + local func = debug.getinfo(level).func + local bindings = {} + + -- Retrieve the upvalues + do local i = 1; while true do + local name, value = debug.getupvalue(func, i) + if not name then break end + bindings[name] = value + i = i + 1 + end end + + -- Retrieve the locals (overwriting any upvalues) + do local i = 1; while true do + local name, value = debug.getlocal(level, i) + if not name then break end + bindings[name] = value + i = i + 1 + end end + + -- Retrieve the varargs (works in Lua 5.2 and LuaJIT) + local varargs = {} + do local i = 1; while true do + local name, value = debug.getlocal(level, -i) + if not name then break end + varargs[i] = value + i = i + 1 + end end + if #varargs > 0 then bindings["..."] = varargs end + + if include_globals then + -- In Lua 5.2, you have to get the environment table from the function's locals. + local env = (_VERSION <= "Lua 5.1" and getfenv(func) or bindings._ENV) + return setmetatable(bindings, {__index = env or _G}) + else + return bindings + end +end + +-- Used as a __newindex metamethod to modify variables in cmd_eval(). +local function mutate_bindings(_, name, value) + local FUNC_STACK_OFFSET = 3 -- Stack depth of this function. + local level = stack_inspect_offset + FUNC_STACK_OFFSET + CMD_STACK_LEVEL + + -- Set a local. + do local i = 1; repeat + local var = debug.getlocal(level, i) + if name == var then + dbg_writeln(COLOR_YELLOW.."debugger.lua"..GREEN_CARET.."Set local variable "..COLOR_BLUE..name..COLOR_RESET) + return debug.setlocal(level + LUA_JIT_SETLOCAL_WORKAROUND, i, value) + end + i = i + 1 + until var == nil end + + -- Set an upvalue. + local func = debug.getinfo(level).func + do local i = 1; repeat + local var = debug.getupvalue(func, i) + if name == var then + dbg_writeln(COLOR_YELLOW.."debugger.lua"..GREEN_CARET.."Set upvalue "..COLOR_BLUE..name..COLOR_RESET) + return debug.setupvalue(func, i, value) + end + i = i + 1 + until var == nil end + + -- Set a global. + dbg_writeln(COLOR_YELLOW.."debugger.lua"..GREEN_CARET.."Set global variable "..COLOR_BLUE..name..COLOR_RESET) + _G[name] = value +end + +-- Compile an expression with the given variable bindings. +local function compile_chunk(block, env) + local source = "debugger.lua REPL" + local chunk = nil + + if _VERSION <= "Lua 5.1" then + chunk = loadstring(block, source) + if chunk then setfenv(chunk, env) end + else + -- The Lua 5.2 way is a bit cleaner + chunk = load(block, source, "t", env) + end + + if not chunk then dbg_writeln(COLOR_RED.."Error: Could not compile block:\n"..COLOR_RESET..block) end + return chunk +end + +local SOURCE_CACHE = {} + +local function where(info, context_lines) + local source = SOURCE_CACHE[info.source] + if not source then + source = {} + local filename = info.source:match("@(.*)") + if filename then + pcall(function() for line in io.lines(filename) do table.insert(source, line) end end) + elseif info.source then + for line in info.source:gmatch("(.-)\n") do table.insert(source, line) end + end + SOURCE_CACHE[info.source] = source + end + + if source and source[info.currentline] then + for i = info.currentline - context_lines, info.currentline + context_lines do + local tab_or_caret = (i == info.currentline and GREEN_CARET or " ") + local line = source[i] + if line then dbg_writeln(COLOR_GRAY.."% 4d"..tab_or_caret.."%s", i, line) end + end + else + dbg_writeln(COLOR_RED.."Error: Source not available for "..COLOR_BLUE..info.short_src); + end + + return false +end + +-- Wee version differences +local unpack = unpack or table.unpack +local pack = function(...) return {n = select("#", ...), ...} end + +local function cmd_step() + stack_inspect_offset = stack_top + return true, hook_step +end + +local function cmd_next() + stack_inspect_offset = stack_top + return true, hook_next +end + +local function cmd_finish() + local offset = stack_top - stack_inspect_offset + stack_inspect_offset = stack_top + return true, offset < 0 and hook_factory(offset - 1) or hook_finish +end + +local function cmd_print(expr) + local env = local_bindings(1, true) + local chunk = compile_chunk("return "..expr, env) + if chunk == nil then return false end + + -- Call the chunk and collect the results. + local results = pack(pcall(chunk, unpack(rawget(env, "...") or {}))) + + -- The first result is the pcall error. + if not results[1] then + dbg_writeln(COLOR_RED.."Error:"..COLOR_RESET.." "..results[2]) + else + local output = "" + for i = 2, results.n do + output = output..(i ~= 2 and ", " or "")..pretty(results[i]) + end + + if output == "" then output = "" end + dbg_writeln(COLOR_BLUE..expr.. GREEN_CARET..output) + end + + return false +end + +local function cmd_eval(code) + local env = local_bindings(1, true) + local mutable_env = setmetatable({}, { + __index = env, + __newindex = mutate_bindings, + }) + + local chunk = compile_chunk(code, mutable_env) + if chunk == nil then return false end + + -- Call the chunk and collect the results. + local success, err = pcall(chunk, unpack(rawget(env, "...") or {})) + if not success then + dbg_writeln(COLOR_RED.."Error:"..COLOR_RESET.." "..tostring(err)) + end + + return false +end + +local function cmd_down() + local offset = stack_inspect_offset + local info + + repeat -- Find the next frame with a file. + offset = offset + 1 + info = debug.getinfo(offset + CMD_STACK_LEVEL) + until not info or frame_has_line(info) + + if info then + stack_inspect_offset = offset + dbg_writeln("Inspecting frame: "..format_stack_frame_info(info)) + if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + else + info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) + dbg_writeln("Already at the bottom of the stack.") + end + + return false +end + +local function cmd_up() + local offset = stack_inspect_offset + local info + + repeat -- Find the next frame with a file. + offset = offset - 1 + if offset < stack_top then info = nil; break end + info = debug.getinfo(offset + CMD_STACK_LEVEL) + until frame_has_line(info) + + if info then + stack_inspect_offset = offset + dbg_writeln("Inspecting frame: "..format_stack_frame_info(info)) + if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + else + info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) + dbg_writeln("Already at the top of the stack.") + end + + return false +end + +local function cmd_where(context_lines) + local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL) + return (info and where(info, tonumber(context_lines) or 5)) +end + +local function cmd_trace() + dbg_writeln("Inspecting frame %d", stack_inspect_offset - stack_top) + local i = 0; while true do + local info = debug.getinfo(stack_top + CMD_STACK_LEVEL + i) + if not info then break end + + local is_current_frame = (i + stack_top == stack_inspect_offset) + local tab_or_caret = (is_current_frame and GREEN_CARET or " ") + dbg_writeln(COLOR_GRAY.."% 4d"..COLOR_RESET..tab_or_caret.."%s", i, format_stack_frame_info(info)) + i = i + 1 + end + + return false +end + +local function cmd_locals() + local bindings = local_bindings(1, false) + + -- Get all the variable binding names and sort them + local keys = {} + for k, _ in pairs(bindings) do table.insert(keys, k) end + table.sort(keys) + + for _, k in ipairs(keys) do + local v = bindings[k] + + -- Skip the debugger object itself, "(*internal)" values, and Lua 5.2's _ENV object. + if not rawequal(v, dbg) and k ~= "_ENV" and not k:match("%(.*%)") then + dbg_writeln(" "..COLOR_BLUE..k.. GREEN_CARET..pretty(v)) + end + end + + return false +end + +local function cmd_help() + dbg.write("" + .. COLOR_BLUE.." "..GREEN_CARET.."re-run last command\n" + .. COLOR_BLUE.." c"..COLOR_YELLOW.."(ontinue)"..GREEN_CARET.."continue execution\n" + .. COLOR_BLUE.." s"..COLOR_YELLOW.."(tep)"..GREEN_CARET.."step forward by one line (into functions)\n" + .. COLOR_BLUE.." n"..COLOR_YELLOW.."(ext)"..GREEN_CARET.."step forward by one line (skipping over functions)\n" + .. COLOR_BLUE.." f"..COLOR_YELLOW.."(inish)"..GREEN_CARET.."step forward until exiting the current function\n" + .. COLOR_BLUE.." u"..COLOR_YELLOW.."(p)"..GREEN_CARET.."move up the stack by one frame\n" + .. COLOR_BLUE.." d"..COLOR_YELLOW.."(own)"..GREEN_CARET.."move down the stack by one frame\n" + .. COLOR_BLUE.." w"..COLOR_YELLOW.."(here) "..COLOR_BLUE.."[line count]"..GREEN_CARET.."print source code around the current line\n" + .. COLOR_BLUE.." e"..COLOR_YELLOW.."(val) "..COLOR_BLUE.."[statement]"..GREEN_CARET.."execute the statement\n" + .. COLOR_BLUE.." p"..COLOR_YELLOW.."(rint) "..COLOR_BLUE.."[expression]"..GREEN_CARET.."execute the expression and print the result\n" + .. COLOR_BLUE.." t"..COLOR_YELLOW.."(race)"..GREEN_CARET.."print the stack trace\n" + .. COLOR_BLUE.." l"..COLOR_YELLOW.."(ocals)"..GREEN_CARET.."print the function arguments, locals and upvalues.\n" + .. COLOR_BLUE.." h"..COLOR_YELLOW.."(elp)"..GREEN_CARET.."print this message\n" + .. COLOR_BLUE.." q"..COLOR_YELLOW.."(uit)"..GREEN_CARET.."halt execution\n" + ) + return false +end + +local last_cmd = false + +local commands = { + ["^c$"] = function() return true end, + ["^s$"] = cmd_step, + ["^n$"] = cmd_next, + ["^f$"] = cmd_finish, + ["^p%s+(.*)$"] = cmd_print, + ["^e%s+(.*)$"] = cmd_eval, + ["^u$"] = cmd_up, + ["^d$"] = cmd_down, + ["^w%s*(%d*)$"] = cmd_where, + ["^t$"] = cmd_trace, + ["^l$"] = cmd_locals, + ["^h$"] = cmd_help, + ["^q$"] = function() dbg.exit(0); return true end, +} + +local function match_command(line) + for pat, func in pairs(commands) do + -- Return the matching command and capture argument. + if line:find(pat) then return func, line:match(pat) end + end +end + +-- Run a command line +-- Returns true if the REPL should exit and the hook function factory +local function run_command(line) + -- GDB/LLDB exit on ctrl-d + if line == nil then dbg.exit(1); return true end + + -- Re-execute the last command if you press return. + if line == "" then line = last_cmd or "h" end + + local command, command_arg = match_command(line) + if command then + last_cmd = line + -- unpack({...}) prevents tail call elimination so the stack frame indices are predictable. + return unpack({command(command_arg)}) + elseif dbg.auto_eval then + return unpack({cmd_eval(line)}) + else + dbg_writeln(COLOR_RED.."Error:"..COLOR_RESET.." command '%s' not recognized.\nType 'h' and press return for a command list.", line) + return false + end +end + +repl = function(reason) + -- Skip frames without source info. + while not frame_has_line(debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3)) do + stack_inspect_offset = stack_inspect_offset + 1 + end + + local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3) + reason = reason and (COLOR_YELLOW.."break via "..COLOR_RED..reason..GREEN_CARET) or "" + dbg_writeln(reason..format_stack_frame_info(info)) + + if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + + repeat + local success, done, hook = pcall(run_command, dbg.read(COLOR_RED.."debugger.lua> "..COLOR_RESET)) + if success then + debug.sethook(hook and hook(0), "crl") + else + local message = COLOR_RED.."INTERNAL DEBUGGER.LUA ERROR. ABORTING\n:"..COLOR_RESET.." "..done + dbg_writeln(message) + error(message) + end + until done +end + +-- Make the debugger object callable like a function. +dbg = setmetatable({}, { + __call = function(_, condition, top_offset, source) + if condition then return end + + top_offset = (top_offset or 0) + stack_inspect_offset = top_offset + stack_top = top_offset + + debug.sethook(hook_next(1, source or "dbg()"), "crl") + return + end, +}) + +-- Expose the debugger's IO functions. +dbg.read = dbg_read +dbg.write = dbg_write +dbg.shorten_path = function (path) return path end +dbg.exit = function(err) os.exit(err) end + +dbg.writeln = dbg_writeln + +dbg.pretty_depth = 3 +dbg.pretty = pretty +dbg.pp = function(value, depth) dbg_writeln(pretty(value, depth)) end + +dbg.auto_where = false +dbg.auto_eval = false + +local lua_error, lua_assert = error, assert + +-- Works like error(), but invokes the debugger. +function dbg.error(err, level) + level = level or 1 + dbg_writeln(COLOR_RED.."ERROR: "..COLOR_RESET..pretty(err)) + dbg(false, level, "dbg.error()") + + lua_error(err, level) +end + +-- Works like assert(), but invokes the debugger on a failure. +function dbg.assert(condition, message) + if not condition then + dbg_writeln(COLOR_RED.."ERROR:"..COLOR_RESET..message) + dbg(false, 1, "dbg.assert()") + end + + return lua_assert(condition, message) +end + +-- Works like pcall(), but invokes the debugger on an error. +function dbg.call(f, ...) + return xpcall(f, function(err) + dbg_writeln(COLOR_RED.."ERROR: "..COLOR_RESET..pretty(err)) + dbg(false, 1, "dbg.call()") + + return err + end, ...) +end + +-- Error message handler that can be used with lua_pcall(). +function dbg.msgh(...) + if debug.getinfo(2) then + dbg_writeln(COLOR_RED.."ERROR: "..COLOR_RESET..pretty(...)) + dbg(false, 1, "dbg.msgh()") + else + dbg_writeln(COLOR_RED.."debugger.lua: "..COLOR_RESET.."Error did not occur in Lua code. Execution will continue after dbg_pcall().") + end + + return ... +end + +-- Assume stdin/out are TTYs unless we can use LuaJIT's FFI to properly check them. +local stdin_isatty = true +local stdout_isatty = true + +-- Conditionally enable the LuaJIT FFI. +local ffi = (jit and require("ffi")) +if ffi then + ffi.cdef[[ + int isatty(int); // Unix + int _isatty(int); // Windows + void free(void *ptr); + + char *readline(const char *); + int add_history(const char *); + ]] + + local function get_func_or_nil(sym) + local success, func = pcall(function() return ffi.C[sym] end) + return success and func or nil + end + + local isatty = get_func_or_nil("isatty") or get_func_or_nil("_isatty") + stdin_isatty = isatty(0) + stdout_isatty = isatty(1) +end + +-- Conditionally enable color support. +local color_maybe_supported = (stdout_isatty and os.getenv("TERM") and os.getenv("TERM") ~= "dumb") +if color_maybe_supported and not os.getenv("DBG_NOCOLOR") then + COLOR_GRAY = string.char(27) .. "[90m" + COLOR_RED = string.char(27) .. "[91m" + COLOR_BLUE = string.char(27) .. "[94m" + COLOR_YELLOW = string.char(27) .. "[33m" + COLOR_RESET = string.char(27) .. "[0m" + GREEN_CARET = string.char(27) .. "[92m => "..COLOR_RESET +end + +if stdin_isatty and not os.getenv("DBG_NOREADLINE") then + pcall(function() + local linenoise = require 'linenoise' + + -- Load command history from ~/.lua_history + local hist_path = os.getenv('HOME') .. '/.lua_history' + linenoise.historyload(hist_path) + linenoise.historysetmaxlen(50) + + local function autocomplete(env, input, matches) + for name, _ in pairs(env) do + if name:match('^' .. input .. '.*') then + linenoise.addcompletion(matches, name) + end + end + end + + -- Auto-completion for locals and globals + linenoise.setcompletion(function(matches, input) + -- First, check the locals and upvalues. + local env = local_bindings(1, true) + autocomplete(env, input, matches) + + -- Then, check the implicit environment. + env = getmetatable(env).__index + autocomplete(env, input, matches) + end) + + dbg.read = function(prompt) + local str = linenoise.linenoise(prompt) + if str and not str:match "^%s*$" then + linenoise.historyadd(str) + linenoise.historysave(hist_path) + end + return str + end + dbg_writeln(COLOR_YELLOW.."debugger.lua: "..COLOR_RESET.."Linenoise support enabled.") + end) + + -- Conditionally enable LuaJIT readline support. + pcall(function() + if dbg.read == nil and ffi then + local readline = ffi.load("readline") + dbg.read = function(prompt) + local cstr = readline.readline(prompt) + if cstr ~= nil then + local str = ffi.string(cstr) + if string.match(str, "[^%s]+") then + readline.add_history(cstr) + end + + ffi.C.free(cstr) + return str + else + return nil + end + end + dbg_writeln(COLOR_YELLOW.."debugger.lua: "..COLOR_RESET.."Readline support enabled.") + end + end) +end + +-- Detect Lua version. +if jit then -- LuaJIT + LUA_JIT_SETLOCAL_WORKAROUND = -1 + dbg_writeln(COLOR_YELLOW.."debugger.lua: "..COLOR_RESET.."Loaded for "..jit.version) +elseif "Lua 5.1" <= _VERSION and _VERSION <= "Lua 5.4" then + dbg_writeln(COLOR_YELLOW.."debugger.lua: "..COLOR_RESET.."Loaded for ".._VERSION) +else + dbg_writeln(COLOR_YELLOW.."debugger.lua: "..COLOR_RESET.."Not tested against ".._VERSION) + dbg_writeln("Please send me feedback!") +end + +return dbg diff --git a/tools/editor/3rd_eval.h b/tools/editor/3rd_eval.h new file mode 100644 index 0000000..ead93ac --- /dev/null +++ b/tools/editor/3rd_eval.h @@ -0,0 +1,501 @@ +/* A mathematical expression evaluator. + * It uses a recursive descent parser internally. + * Author: Werner Stoop + * This is free and unencumbered software released into the public domain. + * http://unlicense.org/ + */ + +#include +#include +#include /* remember to compile with -lm */ +#include +#include +#include + +/* Special tokens used by the lexer function lex() + * they've been chosen as non-printable characters + * so that printable characters can be used for other + * purposes + */ +#define TOK_END 0 /* end of text */ +#define TOK_INI 1 /* Initial state */ +#define TOK_ID 2 /* identifier */ +#define TOK_NUM 3 /* number */ + +/* Types of errors */ + // 0 /* "no error" */ +#define ERR_MEMORY 1 /* "out of memory" */ +#define ERR_LEXER 2 /* "unknown token" */ +#define ERR_LONGID 3 /* "identifier too long" */ +#define ERR_VALUE 4 /* "value expected" */ +#define ERR_BRACKET 5 /* "missing ')'" */ +#define ERR_FUNC 6 /* "unknown function" */ +#define ERR_ARGS 7 /* "wrong number of arguments" */ +#define ERR_CONST 8 /* "unknown constant" */ + +/* Other definitions */ +#define MAX_ID_LEN 11 /* Max length of an identifier */ +#define OPERATORS "+-*/%(),^" /* Valid operators */ + +#define EVAL_PI 3.141592654 +#define EVAL_E 2.718281828 +#define EVAL_DEG (EVAL_PI/180) + +/* Internal structure for the parser/evaluator */ +struct eval { + + jmp_buf j; /* For error handling */ + + const char *p; /* Position in the text being parsed */ + + double *st; /* Stack */ + int st_size; /* Stack size */ + int sp; /* Stack pointer */ + + /* The current and next tokens identified by the lexer */ + struct { + int type; /* Type of the token */ + double n_val; /* Numeric value of the previous lexed token */ + char s_val[MAX_ID_LEN]; /* String (identifier) value of the previous lexed token */ + } token[2]; + + int cur_tok; /* Current token, either 0 or 1 (see the comments of lex()) */ +}; + +/* Prototypes */ +static double pop(struct eval *ev); +static void push(struct eval *ev, double d); +static int lex(struct eval *ev); + +/* Prototypes for the recursive descent parser */ +static void expr(struct eval *ev); +static void add_expr(struct eval *ev); +static void mul_expr(struct eval *ev); +static void pow_expr(struct eval *ev); +static void uni_expr(struct eval *ev); +static void bra_expr(struct eval *ev); +static void id_expr(struct eval *ev); +static void num_expr(struct eval *ev); + +/* + * Evaluates a mathemeatical expression + */ +double eval(const char *exp/*, int *ep*/) { +int _ep, *ep = &_ep; + struct eval ev; + double ans = 0.0; + + assert(ep != NULL); + + /* Allocate a stack */ + ev.st_size = 10; + ev.st = CALLOC(ev.st_size, sizeof *ev.st); + if(!ev.st) + { + *ep = ERR_MEMORY; + return NAN; //0.0; + } + ev.sp = 0; + + /* Manage errors */ + *ep = setjmp(ev.j); + if(*ep != 0) + { + FREE(ev.st); + return NAN; //0.0; + } + + /* Initialize the lexer */ + ev.token[0].type = TOK_INI; + ev.token[0].s_val[0] = '\0'; + ev.token[1].type = TOK_INI; + ev.token[1].s_val[0] = '\0'; + ev.cur_tok = 0; + + /* Initialize the parser */ + ev.p = exp; + + /* lex once to initialize the lexer */ + if(lex(&ev) != TOK_END) + { + expr(&ev); + ans = pop(&ev); + } + + FREE(ev.st); + return ans; +} + +/* + * Pushes a value onto the stack, increases the stack size if necessary + */ +static void push(struct eval *ev, double d) { + if(ev->sp == ev->st_size) { + /* Resize the stack by 1.5 */ + double *old = ev->st; + int new_size = ev->st_size + (ev->st_size >> 1); + ev->st = REALLOC(ev->st, new_size); + if(!ev->st) { + ev->st = old; + longjmp(ev->j, ERR_MEMORY); + } + + ev->st_size = new_size; + } + + ev->st[ev->sp++] = d; +} + +// Pops a value from the top of the stack +static double pop(struct eval *ev) { + assert(ev->sp > 0); + return ev->st[--ev->sp]; +} + +// stricmp() is common, but not standard, so I provide my own +static int istrcmp(const char *p, const char *q) { + for(; tolower(p[0]) == tolower(q[0]) && p[0]; p++, q++); + return tolower(p[0]) - tolower(q[0]); +} + +/* + * Lexical analyzer function + * + * In order to implement LL(1), struct eval has an array of two token structures, + * and its cur_tok member is used to point to the _current_ token, while the other + * element contains the _next_ token. This implements a 2 element ring buffer where + * the lexer always writes to the _next_ token so that the recursive descent parser can + * _peek_ at the next token. + */ + +static int lex(struct eval *ev) { + int next_tok; + +start: + /* Cycle the tokens */ + next_tok = ev->cur_tok; + ev->cur_tok = ev->cur_tok?0:1; + + while(isspace(ev->p[0])) ev->p++; + + if(!ev->p[0]) { + /* End of the expression */ + ev->token[next_tok].type = TOK_END; + goto end; + } + else if(isdigit(ev->p[0]) || ev->p[0] == '.') { + /* Number */ + char *endp; + ev->token[next_tok].type = TOK_NUM; + ev->token[next_tok].n_val = strtod(ev->p, &endp); + ev->p = endp; + goto end; + } + else if(isalpha(ev->p[0])) { + /* Identifier */ + int i; + for(i = 0; isalnum(ev->p[0]) && i < MAX_ID_LEN - 1; i++, ev->p++) + ev->token[next_tok].s_val[i] = ev->p[0]; + + if(isalpha(ev->p[0])) longjmp(ev->j, ERR_LONGID); + + ev->token[next_tok].s_val[i] = '\0'; + ev->token[next_tok].type = TOK_ID; + goto end; + } + else if(strchr(OPERATORS, ev->p[0])) { + /* Operator */ + ev->token[next_tok].type = ev->p[0]; + ev->p++; + goto end; + } + else /* Unknown token */ + longjmp(ev->j, ERR_LEXER); + +end: + + /* If this was the first call, cycle the tokens again */ + if(ev->token[ev->cur_tok].type == TOK_INI) + goto start; + + return ev->token[ev->cur_tok].type; +} + +#define EVAL_TYPE(e) (e->token[e->cur_tok].type) +#define EVAL_ERROR(c) longjmp(ev->j, (c)) + +// num_expr ::= NUMBER +static void num_expr(struct eval *ev) { + if(EVAL_TYPE(ev) != TOK_NUM) + EVAL_ERROR(ERR_VALUE); + push(ev, ev->token[ev->cur_tok].n_val); + lex(ev); +} + +// expr ::= add_expr +static void expr(struct eval *ev) { + add_expr(ev); +} + +// add_expr ::= mul_expr [('+'|'-') mul_expr] +static void add_expr(struct eval *ev) { + int t; + mul_expr(ev); + while((t =EVAL_TYPE(ev)) == '+' || t == '-') { + double a,b; + lex(ev); + mul_expr(ev); + b = pop(ev); + a = pop(ev); + + if(t == '+') + push(ev, a + b); + else + push(ev, a - b); + } +} + +// mul_expr ::= pow_expr [('*'|'/'|'%') pow_expr] +static void mul_expr(struct eval *ev) { + int t; + pow_expr(ev); + while((t = EVAL_TYPE(ev)) == '*' || t == '/' || t == '%') { + double a,b; + lex(ev); + pow_expr(ev); + b = pop(ev); + a = pop(ev); + + if(t == '*') + push(ev, a * b); + else if(t == '/') + push(ev, a / b); + else + push(ev, fmod(a, b)); + } +} + +// pow_expr ::= uni_expr ['^' pow_expr] +static void pow_expr(struct eval *ev) { + /* Note that exponentiation is right associative: + 2^3^4 is 2^(3^4), not (2^3)^4 */ + uni_expr(ev); + if(EVAL_TYPE(ev) == '^') { + double a,b; + lex(ev); + pow_expr(ev); + b = pop(ev); + a = pop(ev); + push(ev, pow(a,b)); + } +} + +// uni_expr ::= ['+'|'-'] bra_expr +static void uni_expr(struct eval *ev) { + int t = '+'; + if(EVAL_TYPE(ev) == '-' || EVAL_TYPE(ev) == '+') { + t = EVAL_TYPE(ev); + lex(ev); + } + + bra_expr(ev); + + if(t == '-') { + double a = pop(ev); + push(ev, -a); + } +} + +// bra_expr ::= '(' add_expr ')' | id_expr +static void bra_expr(struct eval *ev) { + if(EVAL_TYPE(ev) == '(') { + lex(ev); + add_expr(ev); + if(EVAL_TYPE(ev) != ')') + EVAL_ERROR(ERR_BRACKET); + lex(ev); + } + else + id_expr(ev); +} + +// id_expr ::= ID '(' add_expr [',' add_expr]* ')' | ID | num_expr +static void id_expr(struct eval *ev) { + int nargs = 0; + char id[MAX_ID_LEN]; + if(EVAL_TYPE(ev) != TOK_ID) { + num_expr(ev); + } else { + strcpy(id, ev->token[ev->cur_tok].s_val); + lex(ev); + if(EVAL_TYPE(ev) != '(') { + /**/ if(!istrcmp(id, "true")) push(ev, 1.0); + else if(!istrcmp(id, "false")) push(ev, 0.0); + else if(!istrcmp(id, "on")) push(ev, 1.0); + else if(!istrcmp(id, "off")) push(ev, 0.0); + // pi - 3.141592654 + else if(!istrcmp(id, "pi")) + push(ev, EVAL_PI); + // e - base of natural logarithms, 2.718281828 + else if(!istrcmp(id, "e")) + push(ev, EVAL_E); + // deg - deg2rad, allows to degree conversion `sin(90*deg) = 1` + else if(!istrcmp(id, "deg")) + push(ev, EVAL_DEG); + else + EVAL_ERROR(ERR_CONST); + } else { + lex(ev); + + while(EVAL_TYPE(ev) != ')') { + add_expr(ev); + nargs++; + if(EVAL_TYPE(ev) == ')') break; + + if(EVAL_TYPE(ev) != ',') + EVAL_ERROR(ERR_BRACKET); + lex(ev); + } + lex(ev); + + // abs(x) - absolute value of x + if(!istrcmp(id, "abs")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, fabs(pop(ev))); + } + // ceil(x) - smallest integer greater than x + else if(!istrcmp(id, "ceil")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, ceil(pop(ev))); + } + // floor(x) - largest integer smaller than x + else if(!istrcmp(id, "floor")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, floor(pop(ev))); + } + // sin(x) - sine of x, in radians + else if(!istrcmp(id, "sin")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, sin(pop(ev))); + } + // asin(x) - arcsine of x, in radians + else if(!istrcmp(id, "asin")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, asin(pop(ev))); + } + // cos(x) - cosine of x, in radians + else if(!istrcmp(id, "cos")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, cos(pop(ev))); + } + // acos(x) - arccosine of x, in radians + else if(!istrcmp(id, "acos")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, acos(pop(ev))); + } + // tan(x) - tangent of x, in radians + else if(!istrcmp(id, "tan")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, tan(pop(ev))); + } + // atan(x) - arctangent of x, in radians + else if(!istrcmp(id, "atan")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, atan(pop(ev))); + } + // atan(y,x) - arctangent of y/x, in radians. + else if(!istrcmp(id, "atan2")) { + double a, b; + if(nargs != 2) EVAL_ERROR(ERR_ARGS); + b = pop(ev); + a = pop(ev); + push(ev, atan2(a,b)); + } + // sinh(x) - hyperbolic sine of x, in radians + else if(!istrcmp(id, "sinh")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, sinh(pop(ev))); + } + // cosh(x) - hyperbolic cosine of x, in radians + else if(!istrcmp(id, "cosh")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, cosh(pop(ev))); + } + // tanh(x) - hyperbolic tangent of x, in radians + else if(!istrcmp(id, "tanh")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, tanh(pop(ev))); + } + // log(x) - natural logarithm of x + else if(!istrcmp(id, "log")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, log(pop(ev))); + } + // log10(x) - logarithm of x, base-10 + else if(!istrcmp(id, "log10")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, log10(pop(ev))); + } + // exp(x) - computes e^x + else if(!istrcmp(id, "exp")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, exp(pop(ev))); + } + // sqrt(x) - square root of x + else if(!istrcmp(id, "sqrt")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, sqrt(pop(ev))); + } + // rad(x) - converts x from degrees to radians + else if(!istrcmp(id, "rad")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, pop(ev)*EVAL_PI/180); + } + // deg(x) - converts x from radians to degrees + else if(!istrcmp(id, "deg")) { + if(nargs != 1) EVAL_ERROR(ERR_ARGS); + push(ev, pop(ev)*180/EVAL_PI); + } + // pow(x,y) - computes x^y + else if(!istrcmp(id, "pow")) { + double a, b; + if(nargs != 2) EVAL_ERROR(ERR_ARGS); + b = pop(ev); + a = pop(ev); + push(ev, pow(a,b)); + } + // hypot(x,y) - computes sqrt(x*x + y*y) + else if(!istrcmp(id, "hypot")) { + double a, b; + if(nargs != 2) EVAL_ERROR(ERR_ARGS); + b = pop(ev); + a = pop(ev); + push(ev, sqrt(a*a + b*b)); + } + else + EVAL_ERROR(ERR_FUNC); + } + } +} + +// + +#ifdef EVALDEMO +#include +int main(int argc, char *argv[]) { + int i; + double e; + + for(i = 1; i < argc; i++) { + e = eval(argv[i]); + if(e != e) + fprintf(stderr, "Error in expression %s\n", argv[i]); + else + printf("%s = %g\n", argv[i], e); + } + + assert( eval("1+1") == 2 ); + assert( eval("1+") != eval("1+") ); + assert(~puts("Ok") ); +} +#endif diff --git a/tools/editor/editor b/tools/editor/editor new file mode 100644 index 0000000..6122a69 --- /dev/null +++ b/tools/editor/editor @@ -0,0 +1,82 @@ +#ifdef ICON +// 2 states +ICON(DONE),ICON(DONE_ALL), +ICON(CHECK_BOX),ICON(CHECK_BOX_OUTLINE_BLANK), +ICON(VISIBILITY),ICON(VISIBILITY_OFF), +ICON(NOTIFICATIONS),ICON(NOTIFICATIONS_OFF), + +ICON(TOGGLE_ON),ICON(TOGGLE_OFF), +ICON(LIGHTBULB),ICON(LIGHTBULB_OUTLINE), +ICON(FULLSCREEN),ICON(FULLSCREEN_EXIT), +ICON(VIDEOCAM),ICON(VIDEOCAM_OFF), + +ICON(FIBER_MANUAL_RECORD),ICON(FIBER_SMART_RECORD), +ICON(EXPAND_MORE),ICON(EXPAND_LESS), +ICON(SPORTS_ESPORTS),ICON(VIDEOGAME_ASSET),ICON(GAMEPAD),//ICON(STADIA_CONTROLLER), +ICON(FILE_DOWNLOAD),ICON(FILE_UPLOAD), + +// 3 states +ICON(FOLDER_OPEN),ICON(CREATE_NEW_FOLDER),ICON(FOLDER_SPECIAL), +ICON(CONTENT_CUT),ICON(CONTENT_COPY),ICON(CONTENT_PASTE), +ICON(STAR),ICON(STAR_HALF),ICON(STAR_OUTLINE), +ICON(VOLUME_DOWN),ICON(VOLUME_UP),ICON(VOLUME_OFF), + +// 6 states +ICON(FAST_REWIND),ICON(SKIP_PREVIOUS),ICON(PLAY_ARROW),ICON(SKIP_NEXT),ICON(FAST_FORWARD),ICON(REPEAT), + +// 12 states +ICON(POWER),ICON(BATTERY_CHARGING_FULL),ICON(BATTERY_FULL),ICON(BATTERY_6_BAR),ICON(BATTERY_5_BAR),ICON(BATTERY_4_BAR),ICON(BATTERY_3_BAR),ICON(BATTERY_2_BAR),ICON(BATTERY_1_BAR),ICON(BATTERY_0_BAR),ICON(BATTERY_ALERT), + + +ICON(MOVIE), +ICON(CAMERA),ICON(PHOTO_CAMERA), +ICON(SPEED), +ICON(ROOM), +ICON(PUSH_PIN), +ICON(FLAG), + +ICON(G_TRANSLATE), +ICON(SETTINGS), +ICON(CLOSED_CAPTION), + +ICON(BUILD), +ICON(ROCKET_LAUNCH), + +ICON(TODAY),ICON(EVENT_NOTE), + +ICON(3D_ROTATION), +ICON(LAUNCH), +ICON(SEARCH), +ICON(TIMELAPSE), +ICON(ARROW_DOWNWARD), +ICON(CALL_MERGE), +ICON(ARCHIVE), +ICON(SHOW_CHART), +ICON(WARNING), +ICON(CREATE),ICON(ADD), + +ICON(TEXT_FORMAT), +ICON(CHECK), +ICON(SAVE), +ICON(CANCEL), +ICON(DELETE), +ICON(CLOSE), +ICON(REFRESH), +ICON(SYNC), +ICON(HIGHLIGHT_OFF), +ICON(SYSTEM_UPDATE), +ICON(UNDO),ICON(REDO), +ICON(CLASS), +ICON(TITLE), +ICON(HD), + +ICON(VIEW_QUILT), + +ICON(FINGERPRINT), +ICON(VPN_KEY), +ICON(FACE),ICON(PERSON), +ICON(CHAT_BUBBLE), + +ICON(COPYRIGHT), +#undef ICON +#endif diff --git a/tools/editor/editor.c b/tools/editor/editor.c new file mode 100644 index 0000000..05b220b --- /dev/null +++ b/tools/editor/editor.c @@ -0,0 +1,1836 @@ +// in-game editor +// - rlyeh, public domain. + +// ## Design +// ### editor (v1) +// The v1 editor is a tool that understands Assets and is able to *edit the details of such Assets*. +// This understanding is configured via reflection fields in .ini files. Can be reflected from C as well. +// The reflected properties will enable loading, saving and creating generic widget views to edit the Assets. +// Because we load and save the state of Assets, we can also undo and redo changes by simulating load/saves from/into memory. +// And we can also dump the contents into disk, and create diffs and patches from them. +// - [x] Load Assets +// - [x] Edit Assets +// - [x] Save Assets +// - [x] Undo Assets (automatic, via loading older state) +// - [x] Redo Assets (automatic, via loading newer state) +// - [x] Diff Assets (from two states) +// - [x] Mend Assets (from two states) +// +// Note that the editor is dumb and does not tick/draw your GameObjects. It does not handle the Scene/World either. +// Those are game-driven systems. Your game should provide the meanings to actually: +// - [?] Spawn Assets +// - [?] Delete Assets +// - [x] Draw Assets +// - [x] Tick Assets +// - [*] Scene Traversals (parent->children*, visibility, collisions and such) +// +// PS: Asset pipeline is external to the editor. Exotic assets could use some fancy plugins to deal with the import/export; eg, Substance 3D. +// PS: Asset versioning is also external to the editor. We could integrate a few VCS plugins within the editor: p4, git, svn, ... +// +// ### editor (v2) +// The v2 editor adds container support and modding features from previous editor. +// +// Your game could use fancy containers everywhere now. However, for simplicity purposes, Editor would be ignorant about them as well. +// Editor can only use containers that can decay to vectors and strides. Examples: +// - [x] Vectors: already supported in the Editor. +// - [?] Arrays: can decay to a fixed/immutable vector. +// - [?] Sparse/Unordered/Ordered Sets: can decay to vector of values. +// - [?] Sparse/Unordered/Ordered Maps: can decay to vector of keys + vector of values. +// - [?] Other fancy containers: can iterate on elements and send each item to editor individually; might be slow. +// +// We also allow here for others to extend or *override the behavior and look of each window and/or widget* by using .lua and .dll plugins: +// - [ ] Draw Windows --> Custom overrides to alter or enhance the renderer while editing. Via C/C++/dll/lua plugins +// - [ ] Tick Windows --> Custom overrides to alter or enhance the behavior while editing. Via C/C++/dll/lua plugins +// +// ### editor (v3) +// v3 brings in data driven control; ie, be able to parse & interpret text commands from any input stream. +// This would allow for remote control (via OSC), extending scripts, offline commands, Telnet sessions or external tools; like GDB does. +// - [ ] Data driven +// +// The v3 editor is also a bootstrapped v2 editor with tons of .luas. The C skeleton is only a window manager at this point. +// The intention here is to *leverage editing workflow purely into data-driven files*, so the engine can grow up exponentially from here. +// Data-driven on steroids. It would be totally a success if the editor could be bootstrapped to include these kind of sub-editors without much work on the C codebase: +// - [ ] Level 2D/Blockout editor +// - [ ] Level 3D/Blockout editor +// - [*] World outliner +// - [ ] Nodegraph editor (ShaderGraph, AnimGraph, AudioGraph, MaterialGraph, DialogGraph, AIGraph, QuestGraph, ...) +// - [ ] Sequencer +// - [ ] Tracker (music tracker) +// - [ ] Etc... +// +// ### editor (v4) +// Bring in remote datas into the editor. +// Go social & marketplace. Allow others to expand, share, publish, subscribe, discuss their sub-editors within a small community. +// I really like the way the way OpenFrameworks.cc does their addons, and I think we should do same: just discover and monitor github repos, and list everything on a website (fwk- prefix?). +// Wishlist for a github-based community flow: discovery, transparent installs, publish on github, star there, watch commits & releases, track issues+discussions, etc +// +// We should have a generic, extensible, script/plugin-driven, working editor at this point (hopefully) that does not require maintenance. + +// ## Roadmaps +// ### v1 roadmap (current) +// - [*] menu: open, save, save as, save all, reload +// - [x] basic gizmos (@todo: fixed screen size, snapping) +// - [ ] add/rem entities, add/rem components, add/rem/pause/resume systems +// - [ ] cut/copy/paste (ctrl-c to serialize) +// - [ ] F1, detach from game (long press will send F1 key to game) +// - [ ] TAB, isolated view of selected entity on/off. (long press will send TAB key to game) +// - [ ] standardise binary format. msgpack(<->json)? go .ini instead? .ini+blob? .kvdb? +// - [*] object processing from game: tick,draw*,spawn,delete,un/load from bvh stream, +// - [ ] cut/copy/paste <--> add/del events into game (ctrl-c to serialize) +// - [x] multiple selections/select all +// - [x] tree traversal from game (parent->child) +// - [ ] operations on trees: load/save -> as filesystem or .zipped level +// +// ### v2 roadmap (mid-term) +// - [ ] add keyboard shortcuts +// - [ ] tree traversal from game +// - [ ] bvh and collision queries +// - [ ] visibility and pvs queries +// - [ ] art/ vs prefabs/ discrimination: prefabs/ are archetypes (composed types); ie, data-classes. art/ contains data files. +// - [ ] can prefabs be done with ecs maybe? +// - [ ] example: levels are prefabs, composed of other sub-prefabs or art assets. +// - [ ] example: hitboxes+events. girl=pivot(p,r,s)+model(mesh,tex)+curframe +// - [ ] extend widgets vec3 as range;customized mesh,texture,audio,any other asset,combo of anything) +// +// ### v3 roadmap (long-term) +// ### v4 roadmap (long-term) +// - [ ] osc server for properties and editor behavior +// + +// ## Implementation ideas +// ### editor +// - [x] editor = tree of nodes. world, levels and objects are nodes, and even widgets are also nodes. +// - [ ] you can perform actions on some or all of these nodes, with or without descendants, from any top-bottom or bottom-top directions. +// - [ ] these actions include load/save, reset, undo/redo, play/render, toggle vis:on/off/alpha logic:on/off/other ddraw:on/off log:on/off, etc. +// and that's all. +// +// ### organization: world as a filesystem +// - [ ] anything can be serialized into disk. any object, any entity, any property or any widget can be serialized into disk. +// - [ ] groups of them as well. the whole world state can be serialized into disk as a filesystem snapshot: +// - [ ] - entities are folders. you can attach nodes on nodes (ie, create folders inside folders). +// - [ ] - systems are dlls/scripts. you can modify them on the fly and they should reload. +// - [ ] - components are data files. each component is a file. some components may be pure datas (ie, raw textures) but some others can be human-readable and editable. +// inside of that, every line is a JSON/INI property that you can tweak, modify or inspect. +// +// ### replication: diffing zips +// - [ ] the whole world/filesystem will be compressed into a zipfile and delivered to the network when sharding/replicating in a network scenario. +// - [ ] clients will be diffing/patching their filesystems on every received packet. there will be 3 operations to support internally that will reflect what the E/C/S world is doing behind the curtains: +// - [ ] - added files/folders [+] : when creating entities/components/systems +// - [ ] - deleted files/folders [-] : when removing entities/components/systems +// - [ ] - modifying files/folders [*] : when altering entities/components/systems +// +// ### communication: osc messages +// - [ ] any living entity in the game, or within the editor, can be inspected, debugged or tweaked from external tools. +// - [ ] in order to achieve that, an opensoundserver is listening on a binding IP and you can send UDP packets to every node in the world. +// - [ ] the UDP port number matches current year (2021, 2022, 2023...) +// +// ### augmentation: widgets escalate from bottom +// - [x] there are only a few basic supplied widgets. +// - [x] and they correlate to C types: bool, u/int 8/16/32/64, float/double, strings and enums. +// - [x] structs are covered by reflecting and editing all members separately. +// - [ ] optionally, you can extend some basic types to have better visualization widgets. +// ie, you could alias x4 float widgets together into a new shiny vec4 widget that is more compact, fancy and convenient to use. +// then you can also alias that very same vec4 into a color picker for example; or maybe convert that vec3 into a position gizmo. +// then maybe alias x2 color pickers and create a gradient widget. and so on... + +// ## old notes below +// ================== +// - [ ] editor (json level): load/save jsons, property editor for anything (remote osc server/client) +// - gizmo: proportional, orbit/arcball XY (+shift for Z/tilt) +// - scene: scenegraph, obj naming, ~~obj picking, obj bounds,~~ obj collisions, obj/scene streaming +// - placeholders google +// - vcs +// - [ ] Level objects: ~~volumes, triggers, platforms, streaming~~. +// - level: emitters: particles, lights, lightmaps, sound sources, triggers, etc +// - level: box triggers, start/end, spawn, streaming, checkpoints, +// - level: jump, shoots, platforms, collisions +// - level: 60s, 70s, 80s, 90s +// - [ ] Core: wecs+replication +// - modules: script or dll + ram load/save/diff/patch + play/stop/init/ + attach/detach +// - logic tree/ << [] |> || >> +// - - scene |> +// - - enemies +// - ecs: sys are modules, ecs: char *messaging, ecs: filesystem (e/dir,c/files,s/dll) +// - world: streaming, migration + +#include "fwk.h" + +// #include "labs.vm/ecs.c" + +#define EDITOR_VERSION "2022.7" + +#if 1 +#define EDITOR_PRINTF PRINTF +#else +#define EDITOR_PRINTF(...) do {} while(0) +#endif + +// editor controls + +//static int editor_attached = 1; +static int editor_enabled = 1; +static void* editor_selected_obj = 0; +static int editor_key = 0; +static vec2 editor_mouse = {0}; // 2d coord for ray/picking +static bool editor_gamepad = 1; +static int editor_hz = 60; +static int editor_hz_mid = 18; +static int editor_hz_low = 5; +static bool editor_power_saving = 0; +static double editor_t = 0, editor_dt = 0; +static bool editor_lit = 1; +static bool editor_ddraw = 1; + +static +void editor_init_variables() { +} + +bool editor_active() { + return ui_hover() || ui_active() || gizmo_active() ? editor_enabled : 0; +} +double editor_ss() { + return 1000 + editor_t; +} +double editor_delta() { + return editor_dt; +} + + +enum editor_keys { + key_none, + key_pause, + key_reload, + key_browser, + key_recording, + key_fullscreen, + key_screenshot, // @todo: add meta-info in exif or invisibile pixels (cam details, player details, map level, map location, level state, etc) + key_quit, + key_mute, + key_battery, + key_profiler, + key_stop, + key_outliner, + key_undo, + key_redo, + key_save_mem, + key_save_disk, + key_load_disk, + key_reset, + key_debugger, + key_gamepad, + key_lit, + key_ddraw, +}; + +// editor core + +typedef void* obj_t; +typedef array(obj_t) objs_t; + +typedef struct property { // meta: "vec3 namespace.position = {1,2,3}; // minv=(0,0,0) key1=value1 key2=value2 [...] @this is a tooltip @@this is a comment" + char *mark; // namespace + char *name; // display name + char *type; // pointed type + char *hint; // name@tooltip + char *minv; // min value + char *maxv; // max value +#if 0 // @todo: implement me + char *incv; // inc value + char *defv; // default value + char *isro; // is read-only/enabled + char *issv; // is save pending +#endif + void *value; + unsigned typebits; + unsigned flags; +} property; + + +// low-level operations +int save1(bool apply, array(char) *buffer, array(property) arrp) { + // iterate and save + unsigned total = 0; + for each_array_ptr(arrp, property, p) { + unsigned bytes = 0; + /**/ if( p->type[0] == 'f') bytes = sizeof(float); + else if( p->type[0] == 'v') bytes = sizeof(vec3); + else if( p->type[0] == 'i') bytes = sizeof(int); + else if( p->type[0] == 'b') bytes = sizeof(bool); + + if( !apply ) continue; + + if( bytes ) { + array_resize(*buffer, array_count(*buffer) + bytes); + memcpy( &(*buffer)[array_count(*buffer) - bytes], p->value, bytes); + total += bytes; + } + } + + EDITOR_PRINTF("%d bytes written\n", total); + return total; +} +int load1(bool apply, const char *buffer, unsigned buflen, array(property) arrp, unsigned skip_bytes) { + // iterate and load properties + unsigned cursor = 0, loaded = 0, limit = buflen; + + while( cursor <= skip_bytes ) + for each_array_ptr(arrp, property, p) { + unsigned bytes = 0; + /**/ if( p->type[0] == 'f') bytes = sizeof(float); + else if( p->type[0] == 'v') bytes = sizeof(vec3); + else if( p->type[0] == 'i') bytes = sizeof(int); + else if( p->type[0] == 'b') bytes = sizeof(bool); + + if( (cursor + bytes) > limit ) { + return -1; + } + + if( apply && cursor >= skip_bytes ) { + memcpy( p->value, &buffer[cursor], bytes); + loaded += bytes; + } + + cursor += bytes; + } + + EDITOR_PRINTF("%d bytes read, %d bytes loaded\n", cursor, loaded); + return cursor; +} + +int diff1( array(char) src, array(char) dst, array(char) *patch ) { // @testme + int slen = array_count(src); + int dlen = array_count(dst); + if( dlen != slen ) return -1; + + for( int i = 0; i < slen; ++i ) { + int diff = dst[i] - src[i]; + array_push(*patch, (char)diff); + } + + EDITOR_PRINTF("%d bytes diff\n", slen); + return slen; +} + +int patch1( array(char) src, array(char) dst, array(char) patch ) { // @testme + int slen = array_count(src); + int dlen = array_count(dst); + if( dlen != slen ) return -1; + + int plen = array_count(patch); + if( plen != dlen ) return -1; + + for( int i = 0; i < plen; ++i ) { + dst[i] += patch[i]; + } + + EDITOR_PRINTF("%d bytes patched\n", plen); + return plen; +} + +// syntax sugars for collections/containers +// #define bulk_load(obj_min,obj_max,objs,...) for( unsigned i = 0; i < array_count(objs); ++i ) { bool apply = obj_min >= i && i < obj_max; load1(apply, objs[i], __VA_ARGS__); } +// #define bulk_save(obj_min,obj_max,objs,...) for( unsigned i = 0; i < array_count(objs); ++i ) { bool apply = obj_min >= i && i < obj_max; save1(apply, objs[i], __VA_ARGS__); } + + +// state - retained mode + +typedef struct editor_state_t { + array(property) properties; + array(char) buffer; + array(vec2i) history; + unsigned cursor; +} editor_state_t; + +typedef map(char*, char*) editor_dict_t; + +typedef struct editor_call_t { + void* (*call)(); + unsigned bound; + void *vargs[4]; +} editor_call_t; + +typedef struct editor_module_t { + enum { + fn_init, + fn_load, + fn_tick, + fn_draw, + fn_aabb, // hitboxes + fn_debug, // call for debug ui (like loggers and sliders) + fn_save, + fn_quit, + + fn_num_, + } dummy; + + editor_call_t methods[fn_num_]; + +} editor_module_t; + +static map(void*, editor_state_t) editor_state; // world +static map(void*, array(void*)) editor_children; // groups for stacking, bvh and visibility +static map(void*, array(void*)) editor_children_tick; // groups for ticking +static map(void*, array(void*)) editor_children_draw; // groups for drawing +static map(void*, editor_module_t) editor_module; +static map(void*, editor_dict_t) editor_dicts; +static set(void*) editor_world; +static set(void*) editor_selection; // objects selected in scene + +void editor_init_states() { + do_once map_init_ptr(editor_state); + do_once map_init_ptr(editor_module); + do_once map_init_ptr(editor_children); + do_once map_init_ptr(editor_children_tick); + do_once map_init_ptr(editor_children_draw); + do_once map_init_ptr(editor_dicts); + do_once set_init_ptr(editor_world); + do_once set_init_ptr(editor_selection); +} + +// handle selection + +void editor_select(void *obj, bool multi) { + do_once editor_init_states(); + + editor_selected_obj = NULL; + + if(!multi) { + set_clear(editor_selection); + if( obj ) set_find_or_add(editor_selection, editor_selected_obj = obj); + } else { + if( !obj ) return; + bool on = !!set_find(editor_selection, obj); + if(on) set_erase(editor_selection, obj); + else set_find_or_add(editor_selection, editor_selected_obj = obj); + } +} +bool editor_is_selected(void *obj) { + do_once editor_init_states(); + + return !!set_find(editor_selection, obj); +} +void editor_select_none(void) { + editor_select(NULL, false); +} +void editor_select_all(void) { + editor_select_none(); + for each_set_ptr(editor_world, void*, o) { + void *obj = *o; + editor_select(obj, true); + } +} + +// obj/scene: load/save, undo/redo, + +bool editor_clear_redo(void *obj) { + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + + if( ed->cursor >= array_count(ed->history) ) + return false; + + array_resize(ed->buffer, ed->history[ed->cursor].to); + array_resize(ed->history, ed->cursor + 1); + return true; +} + +bool editor_save_disk(const void *obj, const char *outfile) { + editor_state_t *ed = map_find_or_add(editor_state, (void*)obj, (editor_state_t){0}); + + static __thread array(char) buffer = 0; + array_resize(buffer, 0); // <-- reused as an optimization + + bool ok = 0; + if( save1(true, &buffer, ed->properties) > 0 ) { + ok = file_write(outfile, buffer, array_count(buffer)); + } + + ui_notify("Save", ok ? "OK" : ICON_MD_WARNING " Failed!"); + return ok; +} + +bool editor_load_disk(void *obj, const char *infile) { + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + + int buflen; + char *buffer = file_load(infile, &buflen); + if( buffer && buflen ) { + if( load1(true, buffer, buflen, ed->properties, 0) > 0 ) { + return true; + } + } + + return false; +} + +bool editor_save_mem(void *obj) { + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + + static array(char) buffer = 0; + array_resize(buffer, 0); + + // save + int bytes = save1(true, &buffer, ed->properties); + if( bytes <= 0 ) return false; + + // discard save if same size + same content (ie, no changes between this save and previous one) + if( array_count(ed->history) > 1 ) { + vec2i before = *array_back(ed->history); + if( bytes == (before.to - before.from) ) { + if( !memcmp(buffer, ed->buffer + array_count(ed->buffer) - bytes, bytes ) ) { + return false; // puts("discarding save..."); + } + } + } + +#if 0 + // discard redo + if( ed->cursor < array_count(ed->history) ) { + array_resize(ed->buffer, ed->history[ed->cursor].to); + array_resize(ed->history, ed->cursor + 1); + } +#else + editor_clear_redo(obj); +#endif + + // append + int checkpoint = array_count(ed->buffer); + array_resize(ed->buffer, array_count(ed->buffer) + bytes); + memcpy(ed->buffer + checkpoint, buffer, bytes); + + // proceed + array_push(ed->history, vec2i(checkpoint, array_count(ed->buffer))); + + // move cursor to latest + ed->cursor = array_count(ed->history) - 1; + + return true; +} + +bool editor_load_mem(void *obj) { + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + + // load latest & update history + int slots = array_count(ed->history); + if( slots ) + if( load1(true, ed->buffer, array_count(ed->buffer), ed->properties, ed->history[slots - 1].from) > 0 ) + return ed->cursor = slots - 1, true; + + return false; +} + +bool editor_reset(void *obj) { // load first checkpoint + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + + // load first slot + if( load1(true, ed->buffer, array_count(ed->buffer), ed->properties, ed->history[0].from) > 0 ) { + // discard redo + array_resize(ed->buffer, ed->history[0].to); + // update history + array_resize(ed->history, 1); + // move cursor to latest + ed->cursor = array_count(ed->history) - 1; + return true; + } + + return false; +} + +bool editor_undo(void *obj) { + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + + // load previous & rewind history by -1 + if( ed->cursor > 0 ) + if( load1(true, ed->buffer, array_count(ed->buffer), ed->properties, ed->history[ed->cursor - 1].from) >= 0 ) + return ed->cursor -= 1, true; + + return false; +} + +bool editor_redo(void *obj) { + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + + // load next & forward history by +1 + if( ed->cursor < (array_count(ed->history)-1) ) + if( load1(true, ed->buffer, array_count(ed->buffer), ed->properties, ed->history[ed->cursor + 1].from) >= 0 ) + return ed->cursor += 1, true; + + return false; +} + +bool editor_diff(const void *obj1, const void *obj2, array(char) patch) { // @todo + // @todo check: if both valid && both same type + return false; +} +bool editor_patch(void *obj1, array(char) patch) { // @todo + return false; +} + +// obj/module: persist + +char *editor_obj_intern(void *obj, const char *quark, const char *value) { + editor_init_states(); + + editor_dict_t *dict = map_find_or_add(editor_dicts, obj, 0); + if( *dict == 0 ) map_init_str(*dict); + + char **key = map_find_or_add_allocated_key(*dict, STRDUP(quark), 0); + if(*key) FREE(*key); + *key = STRDUP(value); + + return *key; +} + +char *editor_obj_string(const void *obj, const char *quark) { + editor_dict_t *dict = map_find_or_add(editor_dicts, (void*)obj, 0); + if( *dict == 0 ) map_init_str(*dict); + + char **key = map_find_or_add_allocated_key(*dict, STRDUP(quark), 0); + return *key ? *key : ""; +} + +// obj/module: hierarchy + +void editor_obj_childof_tick(void *obj, void *parent) { + array(void*) *found = map_find(editor_children_tick, parent); + if(!found) found = map_insert(editor_children_tick, parent, 0); + if( obj && obj != parent ) { // dont recurse + for( int i = 0; i < array_count(*found); ++i ) { + if( (*found)[i] == obj ) return; // child was already added + } + array_push(*found, obj); + } +} + +void editor_obj_childof_draw(void *obj, void *parent) { + array(void*) *found = map_find(editor_children_draw, parent); + if(!found) found = map_insert(editor_children_draw, parent, 0); + if( obj && obj != parent ) { // dont recurse + for( int i = 0; i < array_count(*found); ++i ) { + if( (*found)[i] == obj ) return; // child was already added + } + array_push(*found, obj); + } +} + +void editor_obj_childof(void *obj, void *parent) { + array(void*) *found = map_find(editor_children, parent); + if(!found) found = map_insert(editor_children, parent, 0); + if( obj && obj != parent ) { // dont recurse + for( int i = 0; i < array_count(*found); ++i ) { + if( (*found)[i] == obj ) return; // child was already added + } + array_push(*found, obj); + } +} + +// obj/module: methods + +typedef void* (*generic_method)(); + +void editor_obj_bind0(const void *obj, unsigned method, generic_method func ) { + do_once editor_init_states(); + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + m->call = func; + m->bound = 0; + + set_find_or_add(editor_world, (void*)obj); +} +void editor_obj_bind1(const void *obj, unsigned method, generic_method func, void *arg1 ) { + do_once editor_init_states(); + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + m->call = func; + m->vargs[0] = arg1; + m->bound = 1; + + set_find_or_add(editor_world, (void*)obj); +} +void editor_obj_bind2(const void *obj, unsigned method, generic_method func, void *arg1, void *arg2 ) { + do_once editor_init_states(); + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + m->call = func; + m->vargs[0] = arg1; + m->vargs[1] = arg2; + m->bound = 2; + + set_find_or_add(editor_world, (void*)obj); +} +void editor_obj_bind3(const void *obj, unsigned method, generic_method func, void *arg1, void *arg2, void *arg3 ) { + do_once editor_init_states(); + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + m->call = func; + m->vargs[0] = arg1; + m->vargs[1] = arg2; + m->vargs[2] = arg3; + m->bound = 3; + + set_find_or_add(editor_world, (void*)obj); +} +void editor_obj_bind4(const void *obj, unsigned method, generic_method func, void *arg1, void *arg2, void *arg3, void *arg4 ) { + do_once editor_init_states(); + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + m->call = func; + m->vargs[0] = arg1; + m->vargs[1] = arg2; + m->vargs[2] = arg3; + m->vargs[3] = arg4; + m->bound = 4; + + set_find_or_add(editor_world, (void*)obj); +} +void *editor_obj_call0(const void *obj, unsigned method ) { + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + if( !m->call ) return 0; + if( m->bound == 1 ) return (m->call)(obj, m->vargs[0]); + if( m->bound == 2 ) return (m->call)(obj, m->vargs[0], m->vargs[1]); + if( m->bound == 3 ) return (m->call)(obj, m->vargs[0], m->vargs[1], m->vargs[2]); + if( m->bound == 4 ) return (m->call)(obj, m->vargs[0], m->vargs[1], m->vargs[2], m->vargs[3]); + return (m->call)(obj); +} +void *editor_obj_call1(const void *obj, unsigned method, void *arg1 ) { + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + return m->call ? m->call(obj, arg1) : 0; +} +void *editor_obj_call2(const void *obj, unsigned method, void *arg1, void *arg2 ) { + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + return m->call ? m->call(obj, arg1, arg2) : 0; +} +void *editor_obj_call3(const void *obj, unsigned method, void *arg1, void *arg2, void *arg3 ) { + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + return m->call ? m->call(obj, arg1, arg2, arg3) : 0; +} +void *editor_obj_call4(const void *obj, unsigned method, void *arg1, void *arg2, void *arg3, void *arg4 ) { + editor_call_t *m = &(map_find_or_add(editor_module, (void*)obj, (editor_module_t){0})->methods[method]); + return m->call ? m->call(obj, arg1, arg2, arg3, arg4) : 0; +} + +// obj/module: ui/property + +void editor_obj_property(void *obj, void *value, const char *metas) { + do_once editor_init_states(); + + ASSERT( obj ); + ASSERT( value ); + ASSERT( metas ); + char *meta = va("%s", metas); + + struct property p = {0}; + + // parse tooltip, if present + for( char *tooltip = strstr(meta, " @"); tooltip; *tooltip = 0, tooltip = 0) { + p.hint = STRDUP(tooltip + 2); + } + + // parse metas, if present + for( char *metas = strstr(meta, "//"); metas; *metas = 0, metas = 0) { + for each_substring(metas + 2, " ", token) { + /**/ if(strbegi(token, "hint=")) token = token + 5 + strspn(token + 5, " "), p.hint = STRDUP(token); + else if(strbegi(token, "minv=")) token = token + 5 + strspn(token + 5, " "), p.minv = STRDUP(token); + else if(strbegi(token, "maxv=")) token = token + 5 + strspn(token + 5, " "), p.maxv = STRDUP(token); + } + } + + // parse declaration + unsigned field = ~0u; + for each_substring(meta, "={,}(); ", token) { + // next field + ++field; + // parse fields + /**/ if(field == 0) p.type = STRDUP(token); + else if(field == 1) { // either name or namespace.name + p.name = strchr(token, '.'); + if( !p.name ) p.name = STRDUP(token); + else p.name = STRDUP(p.name + 1), p.mark = STRDUP(token), *strchr(p.mark, '.') = '\0'; + } + else {} // any initialization values here + } + + // required fields + ASSERT(p.name); + ASSERT(p.type); + + // combine name+hint together + if( p.hint ) { + char *combined = va("%s@%s", p.name, p.hint); + FREE(p.hint), p.hint = 0; + strcatf(&p.hint, "%s", combined); + } else { + p.hint = p.name; + } + + // defaults + p.value = value; + + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + array_push( ed->properties, p ); +} + +void* editor_obj_get_property_by_index(const void *obj, unsigned property_no) { + editor_state_t *ed = map_find_or_add(editor_state, (void*)obj, (editor_state_t){0}); + return property_no < array_count(ed->properties) ? ed->properties[property_no].value : 0; +} + +void* editor_obj_get_property_by_name(const void *obj, const char *property_name) { + editor_state_t *ed = map_find_or_add(editor_state, (void*)obj, (editor_state_t){0}); + for each_array(ed->properties, struct property, p) { + if( !strmatchi(p.name, property_name) ) continue; + return p.value; + } + return 0; +} + +bool editor_obj_render_min_properties(const void *obj, const char *mask) { + editor_state_t *ed = map_find_or_add(editor_state, (void*)obj, (editor_state_t){0}); + + if(!mask) return false; + + const char *section = 0; + for each_array(ed->properties, struct property, p) { + if( p.mark ) { + if( section == 0 || strcmp(section, p.mark) ) { + if( section != 0 ) ui_separator(); + section = p.mark; + ui_label(va("*%s", section)); // '*' adds bold style in labels + } + } + + if( !strmatchi(p.name, mask) ) continue; + + /**/ if( p.type[0] == 'l') ui_label(p.hint); + else if( p.type[0] == 'f') ui_float(p.hint, p.value); + else if( p.type[0] == 'v') ui_float3(p.hint, p.value); + else if( p.type[0] == 'i') ui_int(p.hint, p.value); + else if( p.type[0] == 'b') ui_bool(p.hint, p.value); + } + return true; +} + +void editor_obj_render_max_properties(void *obj, const char *mask) { // headless, needs layout (window/panel) + const char *toolbar_text = va( + ICON_MD_LOOP ";" ICON_MD_SD_CARD ";" ICON_MD_UNDO ";" ICON_MD_REDO ";Save;Load;%s", + va(ICON_MD_BUG_REPORT "@Raise breakpoint (%sebugger detected).", has_debugger() ? "D":"No d")); // ICON_MD_FRONT_HAND + + int button = ui_toolbar(toolbar_text); + if( button ) { + if( button == 1 ) editor_key = key_load_disk; // key_reset; + if( button == 2 ) editor_key = key_save_disk; + if( button == 3 ) editor_key = key_undo; + if( button == 4 ) editor_key = key_redo; + if( button == 5 ) editor_key = key_save_disk; + if( button == 6 ) editor_key = key_load_disk; + if( button == 7 ) editor_key = key_debugger; + } + + ui_separator(); + + editor_obj_render_min_properties(obj, mask); + + ui_separator(); + ui_label("*Debug"); + + editor_state_t *ed = map_find_or_add(editor_state, obj, (editor_state_t){0}); + + static char *s = 0; if(s) *s = 0; + for( int i = 0; i < array_count(ed->history); ++i ) strcatf(&s, ",%s%d..%d", ed->cursor == i ? "->":"", (int)ed->history[i].from, (int)ed->history[i].to); + if(s) ui_label(va("Object Savepoints: %s", s+1)); + + ui_buffer("Object console", va("%s","(empty)"), 7+1); +} + +// main editor interface + +void editor_render_menubar() { + int alts = input(KEY_LALT) || input(KEY_RALT); // @todo: move to fwk.c + int ctrls = input(KEY_LCTRL) || input(KEY_RCTRL); // @todo: move to fwk.c + int shifts = input(KEY_LSHIFT) || input(KEY_RSHIFT); // @todo: move to fwk.c + int mods = alts || ctrls || shifts; // @todo: move to fwk.c + if( input_down(KEY_F5) ) editor_key = key_reload; + if( input_down(KEY_F11) ) editor_key = key_fullscreen; + if( input_down(KEY_PAUSE) ) editor_key = key_pause; + if( input_down(KEY_PRINT) ) editor_key = (mods ? key_recording : key_screenshot); + // if( input_down(KEY_W) && input_held(KEY_LCTRL) ) editor_key = key_quit; + + if( ctrls ) { + /**/ if( input_down(KEY_Z) ) editor_key = key_undo; + else if( input_down(KEY_Y) ) editor_key = key_redo; + else if( input_down(KEY_S) ) editor_key = key_save_disk; + else if( input_down(KEY_A) ) editor_select_all(); + else if( input_down(KEY_D) ) editor_select_none(); + } + + if( !editor_key /*&& !input_anykey()*/ && editor_selected_obj ) { + if( input_up(MOUSE_L) ) editor_key = key_save_mem; + if( input_down(MOUSE_R) ) ui_show("Properties", true); + #if 0 + { + vec2 dims = { 200, 400 }; + if( nk_tooltip_begin(ui_ctx, dims.w)) { + nk_layout_row_dynamic(ui_ctx, dims.h, 1); + editor_obj_render_min_properties(editor_selected_obj); + nk_tooltip_end(ui_ctx); + } + } + #endif + } + + // @fixme: send all editor keys to game? + // if( input_repeat(KEY_ESC, 300)) {} + // if( input_repeat(KEY_F1, 300)) {} + // etc... + + // menubar + + if( ui_menu( ICON_MD_SETTINGS "@Preferences;" + ICON_MD_G_TRANSLATE " Language;" + ICON_MD_FACE " Profile;" // editor account, but also fake profile and 1st party credentials + ICON_MD_MESSAGE " Social;" + ICON_MD_ROCKET_LAUNCH " Game;" // + ICON_MD_KEYBOARD " Keyboard;" + ICON_MD_MOUSE " Mouse;" + ICON_MD_GAMEPAD " Gamepad;" + ICON_MD_MONITOR " Display;" // @todo: RENDER settings, AUDIO settings + ICON_MD_WIFI " Network;" + ICON_MD_SAVINGS " Budget;" // mem,gfx,net,hdd,... also logging + ICON_MD_CREATE_NEW_FOLDER " Folders;" // including project folders + ICON_MD_EXTENSION " Plugins;" // including VCS + ICON_MD_REPLAY " Restart;" + ICON_MD_CLOSE " Quit;" + "-" ICON_MD_RECYCLING " Reset all preferences;" ICON_MD_SAVE_AS " Save all preferences" + ) ) { + if( ui_item() == 3 ) {} // key mappings + if( ui_item() == 4 ) {} // sensitivity, invert xylrw + if( ui_item() == 5 ) {} // sensitivity, invert xy,ab, deadzones + if( ui_item() == 7 ) {} // name,email,icon,link,github + if( ui_item() == 13) editor_key = key_reload; + if( ui_item() == 14) editor_key = key_quit; + } + + if( ui_menu( window_has_pause() ? ICON_MD_PLAY_ARROW "@Tap to Play Game" : ICON_MD_PAUSE "@Tap to Pause Game" )) editor_key = key_pause; + if( ui_menu( ICON_MD_STOP "@Stop game" )) editor_key = key_stop; + if( ui_menu( ICON_MD_CLOSE "@Close game" ) ) {} + static char game_args[16] = "--game-args"; // @fixme @todo remove '_' special char to signal that ui_menu() is writeable (inputbox) + if( ui_menu_editbox( game_args, 16 ) ) {} + + // ICON_MD_TROUBLESHOOT -> PROFILER + // ICON_MD_SCHEMA -> GRAPHNODES + // ICON_MD_ACCOUNT_TREE -> GRAPHNODES + // ICON_MD_TIPS_AND_UPDATES -> BULB + // if( ui_menu( ICON_MD_MENU )) {} + + if( ui_menu( ICON_MD_FOLDER_SPECIAL "@Content browser" )) editor_key = key_browser; + if( ui_menu( va(ICON_MD_VIEW_IN_AR " %d/%d@World outliner", set_count(editor_selection), map_count(editor_state) ))) editor_key = key_outliner; + + if( ui_menu( va(ICON_MD_BUILD "@Build game"))) ui_notify("Build", ICON_MD_WARNING " Not implemented."); + + if( ui_menu( ICON_MD_PHOTO_CAMERA "@Take Screenshot" )) editor_key = key_screenshot; // MD_SCREENSHOT_MONITOR + if( ui_menu( record_active() ? ICON_MD_VIDEOCAM_OFF "@Stop video recording" : ICON_MD_VIDEOCAM "@Start video recording" )) { if(record_active()) record_stop(); else editor_key = key_recording; } + if( ui_menu( editor_gamepad ? ICON_MD_VIDEOGAME_ASSET "@Gamepad is enabled. Tap to disable." : ICON_MD_VIDEOGAME_ASSET_OFF "@Gamepad is disabled. Tap to enable." )) editor_key = key_gamepad; + if( ui_menu( audio_volume_master(-1) > 0 ? ICON_MD_VOLUME_UP "@Audio is enabled. Tap to mute." : ICON_MD_VOLUME_OFF "@Audio is muted. Tap to enable." )) editor_key = key_mute; + if( ui_menu( window_has_fullscreen() ? ICON_MD_FULLSCREEN_EXIT "@Fullscreen. Tap to go Windowed." : ICON_MD_FULLSCREEN "@Windowed. Tap to go Fullscreen." )) editor_key = key_fullscreen; + + if( ui_menu( editor_ddraw ? ICON_MD_IMAGE_SEARCH "@Debug renderer. Tap to go Retail Renderer." : ICON_MD_INSERT_PHOTO "@Retail renderer. Tap to go Debug Renderer." )) editor_key = key_ddraw; // ICON_MD_ADD_PHOTO_ALTERNATE + if( ui_menu( editor_lit ? ICON_MD_LIGHTBULB "@Lit. Tap to disable lighting." : ICON_MD_LIGHTBULB_OUTLINE "@Unlit. Tap to enable lighting." )) editor_key = key_lit; + + // logic: either plug icon (power saving off) or one of the following ones (power saving on): + // if 0% batt (no batt): battery alert + // if discharging: battery levels [alert,0..6,full] + // if charging: battery charging + int battery_read = app_battery(); + int battery_level = abs(battery_read); + int battery_discharging = battery_read < 0 && battery_level < 100; + const char *battery_levels[9] = { // @todo: remap [7%..100%] -> [0..1] ? + ICON_MD_BATTERY_ALERT, + ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR, + ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR, + ICON_MD_BATTERY_4_BAR,ICON_MD_BATTERY_5_BAR, + ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL, + }; + if( ui_menu( !editor_power_saving ? ICON_MD_POWER"@Full power. Tap to save power." : + va("%s@Power saving. Tap to go full power. %3d%% battery.", + battery_read == 0 ? ICON_MD_BATTERY_ALERT : + battery_discharging ? battery_levels[(int)((9-1)*clampf(battery_level/100.f,0,1))] : + ICON_MD_BATTERY_CHARGING_FULL, battery_level) )) + editor_key = key_battery; + + // @todo: combine-in-1? cycle mem -> cpu/profiler -> network mon -> debugger + if( ui_menu(va(ICON_MD_SIGNAL_CELLULAR_ALT " 0/0KiB" ))) {} // SIGNAL_CELLULAR_1_BAR SIGNAL_CELLULAR_2_BAR + if( ui_menu(va(ICON_MD_STORAGE " %s", xstats() ))) {} // 012/136MB + if( ui_menu(va(ICON_MD_SPEED " %5.2f/%d", window_fps(), (int)window_fps_target()))) editor_key = key_profiler; // 012/136MB + + // @todo: alarm/snooze, chrono, time (calendar?) + { + static double base = 0, tap = 0, delta = 0, enabled = 0; + double timer = base + delta; + if( ui_menu( !enabled ? + va(ICON_MD_TODAY "%02d:%02d@Tap to start chrono.", (int)((date() / 10000) % 100), (int)((date() / 100) % 100)) + : + va(ICON_MD_TIMELAPSE "%02dh:%02dm:%02ds:%02df@Tap to reset chrono.",((int)(timer/3600))%24,((int)(timer/60))%60,((int)timer)%60,(int)((timer - (int)timer)*window_fps_target())))) { + base = 0, tap = time_ss(), delta = 0; + enabled = 1; + } + if( editor_key == key_stop ) enabled = 0; + if( enabled ) { + if( !window_has_pause() ) delta = time_ss() - tap; + else base += delta, tap = time_ss(), delta = 0; + } + } + + for each_map_ptr(editor_state, void *, o, editor_state_t, ed) { + profile_incstat("Editor.num_objects", +1); + + void *obj = *o; + + // auto-load from disk during init. @fixme kvdb database + if( array_count(ed->history) == 0 ) + if( editor_load_disk(obj, editor_obj_string(obj, ".path")) ) + {} + + // auto-save in-mem during first edit + if( array_count(ed->history) == 0 ) + editor_save_mem(obj); + + // @todo: continue if obj not found in selection set + if( obj != editor_selected_obj ) + continue; + + if( editor_key == key_debugger ) { breakpoint("User requested breakpoint on this object"); } + if( editor_key == key_reset ) { const char *ok = editor_reset(obj) ? "ok" : "err"; EDITOR_PRINTF("reset: %s\n", ok); } + if( editor_key == key_save_mem ) { const char *ok = editor_save_mem(obj) ? "ok" : "err"; EDITOR_PRINTF("mem saved: %s\n", ok); } + if( editor_key == key_undo ) { const char *ok = editor_undo(obj) ? "ok" : "err"; EDITOR_PRINTF("undo: %s\n", ok); } + if( editor_key == key_redo ) { const char *ok = editor_redo(obj) ? "ok" : "err"; EDITOR_PRINTF("redo: %s\n", ok); } + if( editor_key == key_save_disk ) { const char *ok = editor_save_disk(obj, editor_obj_string(obj, ".path")) ? "ok" : "err"; EDITOR_PRINTF("save: %s\n", ok); } + if( editor_key == key_load_disk ) { const char *ok = editor_load_disk(obj, editor_obj_string(obj, ".path")) ? "ok" : "err"; EDITOR_PRINTF("load: %s\n", ok); } + } + + char *name; + switch( editor_key ) { + default: + break; case key_quit: record_stop(), exit(0); + break; case key_stop: window_pause(1); + break; case key_mute: audio_volume_master( 1 ^ !!audio_volume_master(-1) ); + break; case key_pause: window_pause( window_has_pause() ^ 1 ); + break; case key_reload: window_reload(); + break; case key_battery: editor_power_saving ^= 1; + break; case key_browser: ui_show("File Browser", ui_visible("File Browser") ^ true); + break; case key_outliner: ui_show("Outliner", ui_visible("Outliner") ^ true); + break; case key_recording: name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string()); + break; case key_screenshot: name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string()); + break; case key_profiler: ui_show("Profiler", profile_enable(ui_visible("Profiler") ^ true)); + break; case key_fullscreen: record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand + break; case key_gamepad: editor_gamepad ^= 1; + break; case key_lit: editor_lit ^= 1; + break; case key_ddraw: editor_ddraw ^= 1; + } +} + +int do_context_cmd = 0; +void *do_context_obj = 0; + +void editor_obj_render_properties_recursively(void *obj, const char *mask) { + array(void*) *found = map_find(editor_children, obj); + int num_subobjects = found ? array_count(*found) : 0; + + char *name = editor_obj_string(obj,".name"); name = name[0] ? name : va("%p", obj); + char *title = va("%s%s/ (%d)", + editor_is_selected(obj) ? ICON_MD_CHECK_BOX : ICON_MD_CHECK_BOX_OUTLINE_BLANK, name, num_subobjects); +// if( !strmatchi(title, mask) ) return; + char *id = va("LVL%p",obj); + + int clicked_or_toggled, open; // 1|clicked, 2|toggled + for( int p = (open = ui_collapse(title, id)), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) { + + // contextual menu (open) + if( ui_contextual() ) { + if( ui_button_transparent("C"); + if( choice ) printf("%d\n", choice); + + for( int i = 0; i < num_subobjects; ++i ) { + editor_tick_objs_recursively((*found)[i], flags); + } + } + } else { + for( int i = 0; i < num_subobjects; ++i ) { + editor_tick_objs_recursively((*found)[i], flags); + } + } +} + +void editor_draw_objs_recursively(void *obj, unsigned flags) { + array(void*) *found = map_find(editor_children_draw, obj); + int num_subobjects = found ? array_count(*found) : 0; + + if( flags & DRAW_ENABLED ) editor_obj_call0(obj, fn_draw); + + if( flags & DRAW_DO_UI ) { + char *name = editor_obj_string(obj,".name"); name = name[0] ? name : va("%p", obj); + char *title = va("%s%s/ (%d)", editor_is_selected(obj) ? ICON_MD_CHECK_BOX : ICON_MD_CHECK_BOX_OUTLINE_BLANK, name, num_subobjects); + // if( !strmatchi(title, mask) ) return; + char *id = va("GPU%p",obj); + + int clicked; + for( int p = ui_collapse(title, id), dummy = (clicked = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) { + + int choice = ui_submenu("D"); + if( choice ) printf("%d\n", choice); + + for( int i = 0; i < num_subobjects; ++i ) { + editor_draw_objs_recursively((*found)[i], flags); + } + } + + } else { + for( int i = 0; i < num_subobjects; ++i ) { + editor_draw_objs_recursively((*found)[i], flags); + } + } +} + +void editor_render_windows() { + // content browser + if( ui_window("File Browser", 0) ) { + const char *file = 0; + if( ui_browse(&file, NULL) ) { + const char *sep = ifdef(win32, "\"", "'"); + app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep)); + } + ui_window_end(); + } + + // console/terminal window + if( 0 && ui_window("Console", 0) ) { + ui_console(); + ui_window_end(); + } + + // Scene/nodes + if( ui_window("Outliner", 0) ) { + // @todo: keys up,down,left,right -> tree nav + // @todo: tab -> cycle next node of matching highlighted type + + static int do_filter = 0; + + int choice = ui_toolbar(ICON_MD_SEARCH"@Filter;"ICON_MD_UPLOAD"@Load;"ICON_MD_DOWNLOAD"@Save;"ICON_MD_REFRESH"@Reset;"ICON_MD_UNDO"@Undo;"ICON_MD_REDO"@Redo;"); + if( choice == 1 ) do_filter ^= 1; + + static char filter[128] = {0}; + if( do_filter ) { + ui_buffer(ICON_MD_SEARCH " Filter", filter, 128); + } else { + filter[0] = '\0'; + } + char *mask = filter[0] ? va("*%s*", filter) : "*"; + +#if 0 + static unsigned tabs = 0xFF; + int choice = ui_toolbar( + "LV@Level tree: hierarchical logic datas used when ticking game.;" + "RN@Rendering tree: hierarchical rendering datas used when drawing game.;" + "VS@Visibility tree: hierarchical visibility datas used when ticking game and editor. Also collisions.;" + "ST@Streaming tree: hierarchical streaming datas used when streaming content off disk.;" + "PS@Persist tree: hierarchical storage datas within different game sessions.;" + "PR@Prefabs tree: hierarchical datas of prefabs definitions.;" + "ED@Editor tree: hierarchical datas used when ticking editor.;" + ); +#endif + + for( int c = ui_collapse(ICON_MD_FOLDER_SPECIAL " Art/", "ART"); c; ui_collapse_end(), c = 0) { + static const char *file; + static bool show_browser = 1; + if( ui_browse(&file, &show_browser) ) { + app_exec(va("%s %s", ifdef(win32, "start", ifdef(osx, "open", "xdg-open")), file)); + //puts(file); + } + } + + // + for( int c = ui_collapse(va(ICON_MD_FACTORY " Prefabs/ (%d)", map_count(editor_state)), "PRF"); c; ui_collapse_end(), c = 0) + for each_map_ptr(editor_state, void*, o, editor_state_t, ed) { + void *k = *o; + for( int p = ui_collapse(va("%s",strrchr(editor_obj_string(k,".path"),'/')+1), va("PF#%p",k)); p; ui_collapse_end(), p = 0) { + editor_obj_render_min_properties(k, mask); + } + } + + // dynamic/static bounds: depth + bounds + visibility + do_context_cmd = 0; + do_context_obj = 0; + for( int c = ui_collapse(va(ICON_MD_ACCOUNT_TREE " Levels/ (%d)", map_count(editor_children)), "LVL"); c; ui_collapse_end(), c = 0) + for each_map_ptr(editor_children, void*, o, array(void*), objs) { + void *k = *o; + editor_obj_render_properties_recursively(k, mask); + } + if( do_context_cmd == cc4(list) && do_context_obj ) { + printf("list [%p]\n", do_context_obj); + } + // draw: depth + state (alpha0=off) + // @fixme: make it a tree + for( int c = ui_collapse(va(ICON_MD_PALETTE " Rendering/ (%d)", map_count(editor_children_draw)), "GPU"); c; ui_collapse_end(), c = 0) + for each_map_ptr(editor_children_draw, void*, o, array(void*), objs) { + void *k = *o; + editor_draw_objs_recursively(k, DRAW_DO_UI); + } + // tick: depth + rate (00=off) --> logic + // @todo: physics tick group? anim tick group? any other tick group? + // @fixme: make it a tree + for( int c = ui_collapse(va(ICON_MD_FLAG " Ticking/ (%d)", map_count(editor_children_tick)), "CPU"); c; ui_collapse_end(), c = 0) + for each_map_ptr(editor_children_tick, void*, o, array(void*), objs) { + void *k = *o; + editor_tick_objs_recursively(k, TICK_DO_UI); + } + // init/quit: depth + prio + // @fixme: make it a tree + for( int c = ui_collapse(ICON_MD_CLOUD " Streaming/", "BVH"); c; ui_collapse_end(), c = 0) {} + // save/load: depth + savetomem?yn + savetodisk?yn + quant + lossy/lossless + // @fixme: make it a tree + for( int c = ui_collapse(va(ICON_MD_SD_CARD " Storage/ (%d)", map_count(editor_dicts)), "DSK"); c; ui_collapse_end(), c = 0) + for each_map_ptr(editor_dicts, void*, o, editor_dict_t, d) { + void *k = *o; + for( int p = ui_collapse(editor_obj_string(k,".name"), va("DSK%p",k)); p; ui_collapse_end(), p = 0) { + for each_map(*d, char*, s, char *, v) { + ui_label(va("%s: %s", s, v)); + } + } + } + + // others + for( int c = ui_collapse(ICON_MD_PRECISION_MANUFACTURING " Editors/", "EDT"); c; ui_collapse_end(), c = 0) { + // @todo: add settings here as well? + } + + for( int c = ui_collapse(ICON_MD_INFO " Help", "NFO"); c; ui_collapse_end(), c = 0) { + ui_label("=*General"); + ui_label2("*ESC", ">Editor on/off"); + ui_label2("*F11", ">Fullscreen on/off"); + ui_label2("*F5", ">Refresh"); + ui_separator(); + ui_label("=*Edit"); + ui_label2("*^Z, ^Y", ">Undo, Redo"); + ui_label2("*^X, ^C, ^V", ">Cut, Copy, Paste"); + ui_label2("*^S, ^L, ^R", ">Save, Load*, Restart*"); + ui_separator(); + ui_label("=*Select"); + ui_label2("*LMB, ^A, ^D", ">Select, All, None"); + ui_label2("*RMB", ">Contextual menu*"); + ui_label2("*SPACE@Cycle transform gizmo: position, rotation, scale.", ">Cycle transform gizmo"); + ui_separator(); + ui_label("=*Camera"); + ui_label2("*Q,E,C", ">Camera elevation"); + ui_label2("*W,A,S,D", ">Camera move"); + ui_label2("*LMB/RMB+drag", ">Camera view"); + ui_label2("*WMB", ">Camera speed"); + } + + ui_window_end(); + } + + // UI properties + if( ui_window("Properties", NULL) ) { + if( editor_selected_obj ) + editor_obj_render_max_properties(editor_selected_obj, "*"); + ui_window_end(); + } + + // Icon browser + if( ui_window("Icon Palette", 0 )) { + static const char *icons[] = { + #define ICON(x) ICON_MD_##x + #include "editor" + }; + static const char *titles[] = { + #define ICON(x) #x + #include "editor" + }; + + for( int i = 0, cols = 8; i < countof(icons); i += cols ) { + char buf[256], *p = buf; + for( int j = i, jmax = mini(i+cols, countof(icons)); j < jmax; ++j ) p += sprintf(p, "%s%03d@%s;", icons[j], j, titles[j]); + ui_toolbar(buf); + } + + ui_window_end(); + } +} + +ray *editor_pickup() { +// if(!window_has_cursor()) return NULL; + + // pick entity + bool any_active = ui_active() || ui_hover() || gizmo_active() || gizmo_hover() || input_touch_active(); + if( editor_enabled && !any_active && input_down(MOUSE_L) ) { + editor_mouse = vec2(input(MOUSE_X), input(MOUSE_Y)); + vec3 out = editor_pick(editor_mouse.x, editor_mouse.y); // unprj 2d as 3d coord + vec3 from = camera_get_active()->position, to = out; + static ray last_ray; + last_ray = ray(from, to); + return &last_ray; + } + + return 0; +} + +static +void editor_init() { + // enable outlines + do_once fx_load("editor/art/fx/fxOutline.fs"); + do_once fx_enable(0, 1); + + // init editor + do_once editor_init_states(); + do_once editor_init_variables(); +} + +bool editor() { + do_once editor_init(); + + // timing + editor_dt = window_delta() * !window_has_pause(); if(editor_dt > 1/60.f) editor_dt = 1/60.f; + editor_t += editor_dt; + + // enabled? + if( input_up(KEY_ESC) ) editor_enabled ^= 1; // editor on/off + if( !editor_enabled ) return false; + + // rendering + logic + editor_key = 0; + editor_render_windows(); + editor_render_menubar(); // must be last + + // adaptive framerate + int app_on_background = !window_has_focus(); + int hz = app_on_background ? editor_hz_low : editor_power_saving ? editor_hz_mid : editor_hz; + window_fps_lock( hz < 5 ? 5 : hz ); + + return true; +} + +void editor_camera_fps(void) { + static camera_t cam = {0}; + cam = *camera_get_active(); + + vec3 move = {0}; + vec2 view = {0}; + + // show/hide cursor + bool dragging = input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + bool any_active = ui_active() || ui_hover() || gizmo_active() || input_touch_active(); + if( any_active ) dragging = false; + window_cursor( !dragging ); + + // keyboard/mouse + if( dragging ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_Q)||input(KEY_C)),input(KEY_W)-input(KEY_S)), cam.speed * !any_active); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * dragging * !any_active); + if( !input(KEY_LCTRL) && !input(KEY_RCTRL) ) // invalidate keys if pressing ctrl (ie, when saving CTRL-S) + move = add3(move, wasdec); + view = add2(view, mouse); + + // gamepad + if(0) { + vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f /*15% deadzone*/); + vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), 0.15f /*15% deadzone*/); + vec3 gamepad_move = scale3(vec3(filtered_lpad.x, input(GAMEPAD_LT) - input(GAMEPAD_RT), filtered_lpad.y), 1.0f); + vec2 gamepad_view = scale2(filtered_rpad, 1.0f); + move = add3(move, gamepad_move); + view = add2(view, gamepad_view); + } + + // multi-touch + vec2 touch_move = input_touch_delta_from_origin(0, 0.0125f /*sensitivityFwd*/); // button #0 (left border) + vec2 touch_view = input_touch_delta(1, 0.125f /*sensitivityRot*/); // button #1 (right border) + move = add3(move, vec3(touch_move.x, 0, -touch_move.y)); + view = add2(view, vec2(touch_view.x, -touch_view.y)); + + // apply inputs + camera_move(&cam, move.x,move.y,move.z); + camera_fps(&cam, view.x,view.y); +} + +// sugars +static void *editor_with = 0; +#define editor_obj(x) (editor_with = (x)) +#define editor_obj_childof(...) editor_obj_childof(editor_with, __VA_ARGS__) +#define editor_obj_childof_tick(...) editor_obj_childof_tick(editor_with, __VA_ARGS__) +#define editor_obj_childof_draw(...) editor_obj_childof_draw(editor_with, __VA_ARGS__) +#define editor_obj_intern(...) editor_obj_intern(editor_with, __VA_ARGS__) +#define editor_obj_property(...) editor_obj_property(editor_with, __VA_ARGS__) +#define editor_obj_bind1(...) editor_obj_bind1(editor_with, __VA_ARGS__) +#define editor_obj_bind2(...) editor_obj_bind2(editor_with, __VA_ARGS__) +#define editor_obj_bind3(...) editor_obj_bind3(editor_with, __VA_ARGS__) +#define editor_obj_bind4(...) editor_obj_bind4(editor_with, __VA_ARGS__) + + +// my game + +void* mygrid_draw(void *singleton, float *ground_size) { + ddraw_ontop_push(0); + ddraw_grid(*ground_size); + ddraw_ontop_pop(); + ddraw_flush(); + return 0; +} +void* mymodel_draw(model_t *m, float pivot[16]) { + camera_t *cam = camera_get_active(); + model_render(*m, cam->proj, cam->view, pivot, 0); + return 0; +} +void* mymodel_tick(model_t *m, float pivot[16], vec3 *p, vec3 *r, vec3 *s) { + rotationq44(pivot, eulerq(*r)); + scale44(pivot, s->x,s->y,s->z); + relocate44(pivot,p->x,p->y,p->z); + return 0; +} +void* mymodel_aabb(model_t *m, float pivot[16]) { + static __thread struct aabb aabb[64]; + static __thread int counter = 0; counter = (counter + 1) % 64; + aabb[counter] = model_aabb(*m, pivot); + return &aabb[counter]; +} + + +int main() { + // 80% window, MSAAx2 flag + window_create(80, WINDOW_MSAA2); + window_title("Editor " EDITOR_VERSION " (wip)"); + window_icon("logo.png"); + + // @fixme + camera_t x = camera(); + + // config ground floor + float ground_size = 0; + editor_obj(&ground_size); + editor_obj_intern(".name", "ground"); + editor_obj_intern(".path", editor_path("ground.ini")); + editor_obj_property(&ground_size, "float Size"); + editor_obj_bind1(fn_draw, mygrid_draw, &ground_size); + + // config 3d model #1 + mat44 witch_pivot; + vec3 witch_p = {-5,0,-5}, witch_r={-180,180,0}, witch_s={0.1,-0.1,0.1}; + model_t witch = model("witch/witch.obj", 0); + model_set_texture(witch, texture("witch/witch_diffuse.tga.png", 0)); + + editor_obj(&witch); + editor_obj_childof(&ground_size); + editor_obj_childof_tick(&ground_size); + editor_obj_childof_draw(&ground_size); + editor_obj_intern(".name", "witch"); + editor_obj_intern(".path", editor_path("witch.ini")); + editor_obj_property(&witch_p, "vec3 Position"); // property #0 + editor_obj_property(&witch_r, "vec3 Rotation"); // property #1 + editor_obj_property(&witch_s, "vec3 Scale"); // property #2 + editor_obj_bind1(fn_draw, mymodel_draw, witch_pivot); + editor_obj_bind1(fn_aabb, mymodel_aabb, witch_pivot); + editor_obj_bind4(fn_tick, mymodel_tick, witch_pivot, &witch_p, &witch_r, &witch_s); + + // config 3d model #2 + mat44 girl_pivot; id44(girl_pivot); + model_t girl = model("kgirl/kgirls01.fbx", 0); + vec3 girl_p = {0,0,0}, girl_r = {270,0,0}, girl_s = {2,2,2}; + + editor_obj(&girl); + editor_obj_childof(&ground_size); + editor_obj_childof_tick(&ground_size); + editor_obj_childof_draw(&ground_size); + editor_obj_intern(".name", "girl"); + editor_obj_intern(".path", editor_path("girl.ini")); + editor_obj_property(&girl_p, "vec3 Transform.Position; // @Position in world units"); + editor_obj_property(&girl_r, "vec3 Transform.Rotation; // @Rotation in degrees"); + editor_obj_property(&girl_s, "vec3 Transform.Scale; // @Scale factor (decimal)"); + editor_obj_bind1(fn_draw, mymodel_draw, girl_pivot); + editor_obj_bind1(fn_aabb, mymodel_aabb, girl_pivot); + editor_obj_bind4(fn_tick, mymodel_tick, girl_pivot, &girl_p, &girl_r, &girl_s); + + // meta(&girl_frame, "float Animation.Frame; // @Animation frame"); + + editor_select(&girl, false); + + ui_notify("Hint", "Keys I/J/K/L + Z/X to control the girl"); + + // editor loop + while( window_swap() ) { + + // editor tick + profile("Editor") { + editor(); + //ui_demo(); + } + + // fps camera + if( /*editor_attached ||*/ editor_enabled ) { + profile("Editor.Camera") { + editor_camera_fps(); + } + } else { + profile("Game.Camera") { + camera_t *cam = camera_get_active(); + + static vec3 source; + do_once source = cam->position; + + vec3 target = add3(girl_p, vec3(0,10,0)); + target = add3(target, scale3(norm3(sub3(source, target)), 10.0)); + source = mix3(source, target, 1-0.99f); + + camera_teleport(cam, source); + camera_lookat(cam, vec3(girl_p.x,0,girl_p.z)); + + // @todo: orbit cam w/ right pad + } + } + + double GAME_JUMP_DOWN = input_down(KEY_Z); + double GAME_FIRE_DOWN = input_down(KEY_X); + double GAME_JUMP = input(KEY_Z); + double GAME_FIRE = input(KEY_X); + double GAME_LEFT = input(KEY_J); + double GAME_RIGHT = input(KEY_L); + double GAME_UP = input(KEY_I); + double GAME_DOWN = input(KEY_K); + double GAME_AXISX = input(KEY_L) - input(KEY_J); + double GAME_AXISY = input(KEY_I) - input(KEY_K); + + if( editor_gamepad && !input_anykey() ) { + if( input(GAMEPAD_CONNECTED) ) { + vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f /*15% deadzone*/); + GAME_JUMP_DOWN = input_down(GAMEPAD_A); + GAME_FIRE_DOWN = input_down(GAMEPAD_B) || input_down(GAMEPAD_X) || input_down(GAMEPAD_Y); + GAME_JUMP = input(GAMEPAD_A); + GAME_FIRE = input(GAMEPAD_B) || input(GAMEPAD_X) || input(GAMEPAD_Y); + GAME_AXISX = filtered_lpad.x; + GAME_AXISY = filtered_lpad.y; + } + } + + profile("Game.Animate scene") if( editor_delta() ) { + float delta = editor_delta() * 30; // 30fps anim + + // animate girl + girl.curframe = model_animate(girl, girl.curframe + delta); + + // jump controller: jump duration=1.5s, jump height=6 units, anims (expo->circ) + float jump_delta = 1.0; + static double jump_timer = 0, jump_ss = 1.5, jump_h = 6; + if( GAME_JUMP_DOWN ) if( jump_timer == 0 ) jump_timer = editor_ss(); + jump_delta = clampf(editor_ss() - jump_timer, 0, jump_ss) * (1.0/jump_ss); + if( jump_delta >= 1 ) { jump_timer = 0; } + float y = ease_ping_pong( jump_delta, ease_out_expo, ease_out_circ); + girl_p.y = y * jump_h; + + // punch controller + float punch_delta = 1; + if( jump_delta >= 1 ) { + static vec3 origin; + static double punch_timer = 0, punch_ss = 0.5; + if( GAME_FIRE_DOWN ) if( punch_timer == 0 ) punch_timer = editor_ss(), origin = girl_p; + punch_delta = clampf(editor_ss() - punch_timer, 0, punch_ss) * (1.0/punch_ss); + if( punch_delta >= 1 ) { punch_timer = 0; } + else { + float x = ease_out_expo( punch_delta ); + vec3 fwd = rotate3q(vec3(0,0,1), eulerq(vec3(girl_r.x - 170,girl_r.y,girl_r.z))); + vec3 mix = mix3(girl_p, add3(origin,scale3(fwd,x*2)), x); + girl_p.x = mix.x, girl_p.z = mix.z; + } + } + + int modern_controller = 1; + int running = 0; + + // girl controller + + // locomotion vars + float speed = 0.2f * delta; + float yaw_boost = GAME_AXISY > 0 ? 1.0 : 1.75; + if(punch_delta < 1) yaw_boost = 0.0; // if firing... + else if(punch_delta <= 0.1) yaw_boost = 4.0; // unless initial punch chaining, extra yaw + + // old fashioned locomotion controller (boat controller) + if(!modern_controller) { + running = GAME_AXISY > 0; + + girl_r.x -= 170; + quat q = eulerq(girl_r); // += custom.pivot + vec3 rgt = rotate3q(vec3(1,0,0), q); + vec3 up = rotate3q(vec3(0,1,0), q); + vec3 fwd = rotate3q(vec3(0,0,1), q); + vec3 dir = scale3(fwd, speed * GAME_AXISY * (GAME_AXISY > 0 ? 2.0 : 0.5)); + girl_r.x += speed * 20.0 * yaw_boost * GAME_AXISX; // yaw + girl_p = add3(girl_p, dir); + girl_r.x += 170; + } + + // modern locomotion controller (mario 3d) + if(modern_controller) { + running = GAME_AXISX != 0 || GAME_AXISY != 0; + + camera_t *cam = camera_get_active(); + vec3 fwd = sub3(girl_p, cam->position); fwd.y = 0; fwd = norm3(fwd); + vec3 rgt = norm3(cross3(fwd, vec3(0,1,0))); + + // target + vec3 dir = add3( + scale3(fwd, GAME_AXISY), + scale3(rgt, GAME_AXISX) + ); dir.y = 0; dir = norm3(dir); + + // smoothing + static vec3 olddir; do_once olddir = dir; + dir = mix3(dir, olddir, 1 - (yaw_boost / 4.0) * 0.85); + olddir = dir; + + // vis + // ddraw_arrow(girl_p, add3(girl_p,scale3(dir,10))); + + // apply direction + girl_p = add3(girl_p, scale3(dir, speed * 2)); + + // apply rotation + { + girl_r.x -= 170; + quat q = eulerq(girl_r); + vec3 fwdg = rotate3q(vec3(0,0,1), q); + girl_r.x += 170; + + //float cosAngle = dot3(dir,fwdg); + //float angle = acos(cosAngle) * TO_DEG; + float angle = TO_DEG * ( atan2(fwdg.z, fwdg.x) - atan2(dir.z, dir.x)); + + if( !isnan(angle) ) { + girl_r.x -= angle; + while(girl_r.x> 180) girl_r.x-=360; + while(girl_r.x<-180) girl_r.x+=360; + } + } + } + + // anim loops + if( jump_delta < 1 ) { // jump/kick anim +#if 0 + girl.curframe = clampf(girl.curframe, 184, 202); + if( girl.curframe > 202-4 && GAME_FIRE_DOWN ) girl.curframe = 184+4; +#else + #define loopf(frame, min, max) (frame < min ? min : frame > max ? min + frame - max : frame) + if(girl.curframe >= 203) + girl.curframe = loopf(girl.curframe, 203, 220); + else + girl.curframe = clampf(girl.curframe, 184, 202); + if( girl.curframe > 202-4 && girl.curframe < 208 && GAME_FIRE_DOWN ) girl.curframe = 203; +#endif + } + else if( punch_delta < 1 ) { // punch anim + girl.curframe = clampf(girl.curframe, 90, 101); + if( girl.curframe > 101-6 && GAME_FIRE_DOWN ) girl.curframe = 101-6; + } + else if( running ) { + // loop running anim + if( girl.curframe < 65 ) girl.curframe = 65; + if( girl.curframe > 85 ) girl.curframe = 65; + } + else { // loop idle anim + if( girl.curframe > 59 ) girl.curframe = 0; + } + } + + profile("Game.collisions") { + bool punching = girl.curframe >= 90 && girl.curframe < 101; + bool air_kicking = girl.curframe >= 184 && girl.curframe < 202; + bool jump_kicking = girl.curframe >= 203 && girl.curframe < 220; + bool attacking = punching || air_kicking || jump_kicking; + + if( attacking ) { + aabb boxg = model_aabb(girl, girl_pivot); + aabb boxw = model_aabb(witch, witch_pivot); +#if 0 // halve aabb. ok + { + vec3 diag = sub3(boxg.max, boxg.min); + vec3 halve = scale3(diag, 0.25); + vec3 center = scale3(add3(boxg.min, boxg.max), 0.5); + boxg.min = sub3(center, halve); + boxg.max = add3(center, halve); + } +#endif + hit* h = aabb_hit_aabb(boxg, boxw); + if( h && GAME_FIRE ) { + vec3 dir = norm3(sub3(witch_p, girl_p)); + witch_p = add3(witch_p, mul3(dir,vec3(1,0,1))); + } + + if( editor_enabled && editor_ddraw ) { + ddraw_color_push(h ? RED : GREEN); + ddraw_aabb(boxw.min, boxw.max); + ddraw_aabb(boxg.min, boxg.max); + ddraw_color_pop(); + } + } + } + + camera_t *cam = camera_get_active(); + + profile("Game.Draw scene") { + // draw grid/axis + editor_obj_call0(&ground_size, fn_draw); + + // tick+draw girl + editor_obj_call0(&girl, fn_tick); + editor_obj_call0(&girl, fn_draw); + + // tick+draw witch + editor_obj_call0(&witch, fn_tick); + editor_obj_call0(&witch, fn_draw); + } + + if(!editor_enabled) continue; + + profile("Editor.Draw outline") { + + // handle (multi-)selection + ray *r = editor_pickup(); + if( r ) { + bool found = false; + bool multi_selection = input(KEY_LCTRL) || input(KEY_RCTRL); + for each_map_ptr(editor_state, void*, o, editor_state_t, ed) { + void *obj = *o; + if( obj == &ground_size ) continue; // @fixme: add ray+plane. also, bvh + + aabb *box = editor_obj_call0(obj, fn_aabb); + if( ray_hit_aabb(*r, *box)) { + editor_select(obj, multi_selection); + found = true; + } + } + if( !found ) + if( ray_hit_plane(*r, plane(vec3(0,0,0), vec3(0,1,0)) )) { + editor_select(&ground_size, multi_selection); + } + } + + if(!set_count(editor_selection)) continue; + + // draw silhouettes + fx_begin(); + for each_set_ptr(editor_selection, void*, o) { + void *obj = *o; + + editor_obj_call0(obj, fn_draw); + } + fx_end(); + + // draw gizmos, aabbs, markers, etc + for each_set_ptr(editor_selection, void*, o) { + void *obj = *o; + + // get transform + vec3 *p = editor_obj_get_property_by_name(obj, "position"); + vec3 *r = p ? editor_obj_get_property_by_name(obj, "rotation") : NULL; + vec3 *s = r ? editor_obj_get_property_by_name(obj, "scale") : NULL; + + // debugdraw + ddraw_ontop_push(0); + + // bounding box + aabb *box = editor_obj_call0(obj, fn_aabb); + if( box ) { + ddraw_color_push(YELLOW); + ddraw_aabb(box->min, box->max); + ddraw_color_pop(); + } + + // skeleton anim + // model_render_skeleton(model, pivot); + + // position marker + if( p ) { + static map(void*, vec3) prev_dir = 0; + do_once map_init_ptr(prev_dir); + vec3* dir = map_find_or_add(prev_dir, obj, vec3(1,0,0)); + + static map(void*, vec3) prev_pos = 0; + do_once map_init_ptr(prev_pos); + vec3* found = map_find_or_add(prev_pos, obj, *p), fwd = sub3(*p, *found); + if( (fwd.y = 0, len3sq(fwd)) ) { + *found = *p; + *dir = norm3(fwd); + } + + // float diameter = len2( sub2(vec2(box->max.x,box->max.z), vec2(box->min.x,box->min.z) )); + // float radius = diameter * 0.5; + ddraw_position_dir(*p, *dir, 1); + } + + ddraw_ontop(1); + + // transform gizmo + if( p && r && s ) { + gizmo(p,r,s); + } + + ddraw_ontop_pop(); + } + } + } +} + +// @todo +// editor_add_tick_before() +// editor_add_tick_after() +// editor_add_draw_before() +// editor_add_draw_after() diff --git a/tools/editor/editor.png b/tools/editor/editor.png new file mode 100644 index 0000000..d773b05 Binary files /dev/null and b/tools/editor/editor.png differ diff --git a/tools/editor/editor2.c b/tools/editor/editor2.c new file mode 100644 index 0000000..bb96238 --- /dev/null +++ b/tools/editor/editor2.c @@ -0,0 +1,471 @@ +/* + + + + + + +- [ ] Editor: Gizmos✱, scene tree, property editor✱, load/save✱, undo/redo✱, copy/paste, on/off (vis,tick,ddraw,log), vcs. +- [ ] Editor: Scenenode pass: node singleton display, node console, node labels, node outlines✱. +- [ ] Editor: Debug pass: toggles on/off (billboards✱, materials, un/lit, cast shadows, wireframe, skybox✱/mie✱, fog/atmosphere, collide✱, physics). +- [ ] Editor: Level pass: volumes, triggers, platforms, level streaming. +- [ ] Editor: Edit pass: Procedural content, brushes, noise and CSG. +- [ ] Editor: GUI pass: timeline and data tracks, node graphs. +*/ + +#include "fwk.c" +#include "editor2.h" // old editor interface + +#define ui_push_hspace(px) \ + (int xx = px; xx; xx = 0) \ + for(struct nk_panel *layout = ui_ctx->current->layout; layout; ) \ + for( xx = (layout->at_x += px, layout->bounds.w -= px, 0); layout; layout->at_x -= px, layout->bounds.w += px, layout = 0 ) + +// ---------------------------------------------------------------------------------------- + +#define expr expr2 // 3rd_lua.h +#include "3rd_eval.h" + +// ---------------------------------------------------------------------------------------- + +#include "labs.meta/meta_reflect.c" + +int *meta_changed(void *value) { + static map(void*,int) changes = 0; + do_once map_init_ptr(changes); + + return map_find_or_add(changes, value, 0); +} + +void reflect_ui( const reflect *r, void *value, void *userdata ) { + ui_label_icon_highlight = *meta_changed(value); // @hack: remove ui_label_icon_highlight hack + char *title = va(ICON_MD_UNDO "%s", r->info); + + int changed = 0; + /**/ if( !strcmp(r->type, "int") ) changed = ui_int(title, (int*)value); + else if( !strcmp(r->type, "char") && r->is_ptr ) changed = ui_buffer(title, (char*)value, strlen((char*)value)+1); + else if( !strcmp(r->type, "string") ) changed = ui_string(title, (char**)value); + else if( !strcmp(r->type, "float") ) changed = ui_float(title, (float*)value); + else if( !strcmp(r->type, "double") ) changed = ui_double(title, (double*)value); + else if( !strcmp(r->type, "unsigned") ) changed = ui_unsigned(title, (unsigned*)value); + else if( !strcmp(r->type, "color") ) changed = ui_color4(va("%s #%02X%02X%02X%02X", title, (int)(0[(float*)value]),(int)(1[(float*)value]),(int)(2[(float*)value]),(int)(3[(float*)value])), (float*)value); + // else if( !strcmp(type, "vec3") ) ; // not supported. decays to 3 floats + else ui_label2(title, va("(%s)%s", r->type, r->name)); + + if( changed ) { + *meta_changed(value) = 1; + } + + if( ui_label_icon_clicked_L.x >= 6 && ui_label_icon_clicked_L.x <= 26 ) { // @hack: if clicked on UNDO icon (1st icon) + *meta_changed(value) = 0; + } +} +bool reflect_parse(void *obj, const char *type, const char *val) { + /**/ if( !strcmp(type, "int") ) *((int*)obj) = eval(val); +// else if( !strcmp(r->type, "char") && r->is_ptr ) ; // @fixme: not supported, unless we do strncpy() or similar. + else if( !strcmp(type, "string") ) *((char**)obj) = stringf("%s", val); + else if( !strcmp(type, "float") ) *((float*)obj) = eval(val); // = v[0] == '~' ? (float)~atoi(val+1) : atof(val); // = atof(val); + else if( !strcmp(type, "double") ) *((double*)obj) = eval(val); // = v[0] == '~' ? (float)~atoi(val+1) : atof(val); // = atof(val); + else if( !strcmp(type, "unsigned") ) *((unsigned*)obj) = eval(val); // = v[0] == '~' ? (float)~atoi(val+1) : atof(val); // = atof(val); + else if( !strcmp(type, "color") ) *(((float*)obj)+3) = 255, sscanf(val, "%f %f %f %f", ((float*)obj)+0, ((float*)obj)+1, ((float*)obj)+2, ((float*)obj)+3); + else if( !strcmp(type, "vec3") ) sscanf(val, "%f %f %f", ((float*)obj)+0, ((float*)obj)+1, ((float*)obj)+2); + else return 0; + return 1; +} + +// ---------------------------------------------------------------------------------------- + +typedef void(*obj_ctor)(void*); +static map(char*, obj_ctor) obj_ctors; + +#define STRUCT_CTOR(type, ctor) STRUCT_CTOR(#type, (obj_ctor)ctor) +void (STRUCT_CTOR)( const char *type, obj_ctor ctor ) { + do_once map_init_str(obj_ctors); + map_find_or_add(obj_ctors, STRDUP(type), ctor); +} + +bool obj_make(void *obj, const char *ini_data) { // initialize object from ini datas + char *hint = 0; + + for( ini_t read = ini_from_mem(ini_data); !!read; map_free(read), read = 0) { + for each_map(read, char*, k, char*, v) { + array(char*) tokens = strsplit(k, "."); + if( array_count(tokens) != 2 ) continue; + + const char *type = 0; + void *found = reflect_field( tokens[0], obj, tokens[1], &type ); + if( !found ) continue; + + if( reflect_parse(found, type, v) ) { + hint = tokens[0]; + } + } + + // constructor (post-init call) + obj_ctor *ctor = map_find(obj_ctors, hint); + if( ctor ) (*ctor)( obj ); + } + + return hint != 0; +} + +// ---------------------------------------------------------------------------- + +#define POD_TYPES \ + vec2i v2i; \ + vec2 v2; \ + vec3 v3; \ + vec4 v4; \ + quat q; \ + char *s; \ + double d; \ + int64_t i; \ + unsigned color; \ + void *ptr; \ + void (*fun)(void*); \ + char *nametype; /* "name\0type" */ + +typedef union pod { + POD_TYPES +} pod; + +typedef union var { + POD_TYPES + array(pod) array; + array(pod) pair; // array of 2: first,second. maybe used as key,value + array(pod) tuple; // array of N: first,second,third... maybe used as array of N*2: NAME1,val1,NAME2,val2, ... + map(pod,pod) map; + set(pod) set; +} var; + +typedef struct node { + var k; + var v; + + struct node *up; + struct node *next; + struct node *down; +} node; + +char* node_name(node *n) { + return n->k.nametype; +} +char* node_type(node *n) { + return n->k.nametype + strlen(n->k.nametype) + 1; +} +char* node_set_nametype(node *n, const char *name, const char *type) { + *strchr(n->k.nametype = stringf("%s\1%s", name, type), '\1') = '\0'; // @leak + return n->k.nametype; +} +node* node_find(node *n, const char *path) { + if (!n->k.nametype) return 0; + if( !strcmp(node_name(n), path) ) return n; + node *r = 0; + if( n->next ) r = node_find(n->next, path); + if( n->down && !r ) if( !strcmp(node_name(n->down), path) ) return n->down; // r = node_find(n->down, path); + return r; +} +node* node_find_recurse(node *n, const char *path) { + array(char*) split = strsplit(path, "/"); + while( n && array_count(split) ) { + n = node_find(n, split[0]); + array_pop(split); + } + return n; +} +node* node_attach_sibling(node *n, node *sibling) { + while( n->next ) n = n->next; + return n->next = sibling; +} +node* node_attach_child(node *n, node *child) { + child->up = n; + if( n->down ) return node_attach_sibling(n->down, child); + return n->down = child; +} +unsigned node_children(node *n) { + unsigned c = 0; + if( n->down ) { + n = n->down; + do ++c; while( (n = n->next) ); + } + return c; +} +unsigned node_siblings(node *n) { + return n->up ? node_children( n->up->down ) : 0; +} +void node_init(node *n) { + profile_incstat("Editor.inits", +1); + + if(n->next) node_init(n->next); + if(n->down) node_init(n->down); +} +void node_tick(node *n) { + profile_incstat("Editor.ticks", +1); + + if(n->next) node_tick(n->next); + if(n->down) node_tick(n->down); +} +void node_draw(node *n) { + profile_incstat("Editor.draws", +1); + + if(n->next) node_draw(n->next); + if(n->down) node_draw(n->down); +} +void node_quit(node *n) { + profile_incstat("Editor.quits", +1); + + if(n->next) node_quit(n->next); + if(n->down) node_quit(n->down); +} +void node_edit(node *n, node *root) { + profile_incstat("Editor.edits", +1); + + if( ui_collapse(va("%s %s (%u)", n->down ? ICON_MD_SOURCE : ICON_MD_FOLDER, node_name(n), node_children(n)), va("%p%p",root,n->v.ptr)) ) { // @fixme v.ptr + if( n->down ) node_edit(n->down,root); + + if( reflect_has_fields( node_type(n), n->v.ptr ) ) { + for ui_push_hspace( 4 ) { + #define ICON_DOT ICON_CANCEL // ICON_MD_WIFI_1_BAR // ICON_MD_RADIO_BUTTON_UNCHECKED // ICON_MD_LENS_BLUR + static int flags[4] = {0}; + char *toolbar = va("%s%s%s%s", + flags[3] ? ICON_MD_STAR : ICON_MD_STAR_OUTLINE, // ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER, // flags[3] == 0 ? ICON_MD_STAR_OUTLINE : flags[3] == 1 ? ICON_MD_STAR_HALF : ICON_MD_STAR, + flags[2] ? ICON_MD_CODE : ICON_DOT, + flags[1] ? ICON_MD_FLAG : ICON_DOT, + flags[0] ? ICON_MD_VISIBILITY : ICON_MD_VISIBILITY_OFF + ); + + ui_label_icon_highlight = *meta_changed(n); // @hack: remove ui_label_icon_highlight hack + char *section = va("*" ICON_MD_UNDO "%s", node_type(n)); + + int choice = ui_label2_toolbar(section, toolbar); + if( choice ) flags[ choice - 1 ] = (flags[ choice - 1 ] + 1 ) % ( choice == 4 ? 2/*3*/ : 2); + reflect_iterate_fields( node_type(n), n->v.ptr, reflect_ui, NULL ); // @fixme v.ptr + } + } + + ui_collapse_end(); + } + + if(n->next) node_edit(n->next,root); +} + +// --- + +struct editor_t { + unsigned frame; + // root nodes + node init; + node tick; + node draw; + node edit; + node quit; +} editor; + +enum { EDITOR_BUCKETS = 5 }; // init+tick+draw+edit+quit + +void editor_reset() { + node_quit(&editor.quit); + editor.frame = 0; +} +void editor_frame() { + editor_init(); // old editor interface + editor_tick(); // old editor interface + editor_menubar(); // old editor interface + + if( input_down(KEY_F5) ) { + editor_reset(); + } + if( editor.frame++ == 0 ) { + node_init(&editor.init); + } + node_tick(&editor.tick); + node_draw(&editor.draw); + + // content browser + if( ui_window("File Browser", 0) ) { + const char *file = 0; + if( ui_browse(&file, NULL) ) { + const char *sep = ifdef(win32, "\"", "'"); + app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep)); + } + ui_window_end(); + } + + // console/terminal window + if( 0 && ui_window("Console", 0) ) { // @fixme half-working + ui_console(); + ui_window_end(); + } + + if( ui_window("Outliner", 0) ) { + +#if 1 + static char *filter = 0; + { + static int do_filter = 0; + int choice = ui_toolbar(ICON_MD_SEARCH ";" ICON_MD_REFRESH ";" ICON_MD_SD_CARD); + if( choice == 1 ) do_filter = 1; + if( do_filter ) { + ui_string(ICON_CANCEL " Filter " ICON_MD_SEARCH, &filter); + if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon) + do_filter = 0; + } + } else { + if( filter ) filter[0] = '\0'; + } + char *filter_mask = filter && filter[0] ? va("*%s*", filter) : "*"; + } +#endif + + for( int c = ui_collapse(ICON_MD_FOLDER_SPECIAL " Art/", "ART"); c; ui_collapse_end(), c = 0) { + static const char *file; + static bool show_browser = 1; + if( ui_browse(&file, &show_browser) ) { + app_exec(va("%s %s", ifdef(win32, "start", ifdef(osx, "open", "xdg-open")), file)); + //puts(file); + show_browser = 1; + } + } + for( int c = ui_collapse(ICON_MD_BOOKMARK " Bookmarks/", "BOOK"); c; ui_collapse_end(), c = 0) { + } + for( int c = ui_collapse(ICON_MD_BUBBLE_CHART/*ICON_MD_SCATTER_PLOT*/ " Entities/", "ENT"); c; ui_collapse_end(), c = 0) { + } + for( int c = ui_collapse(ICON_MD_TUNE " Components/", "COM"); c; ui_collapse_end(), c = 0) { + } + for( int c = ui_collapse(ICON_MD_PRECISION_MANUFACTURING " Systems/", "SYS"); c; ui_collapse_end(), c = 0) { + } + for( int c = ui_collapse(ICON_MD_ACCOUNT_TREE " Hierarchy/", "ORD"); c; ui_collapse_end(), c = 0) { + for( int c = ui_collapse(ICON_MD_ACCOUNT_TREE " Init/", "ORD1"); c; ui_collapse_end(), c = 0) {} + for( int c = ui_collapse(ICON_MD_ACCOUNT_TREE " Draw/", "ORD2"); c; ui_collapse_end(), c = 0) {} + for( int c = ui_collapse(ICON_MD_ACCOUNT_TREE " Tick/", "ORD3"); c; ui_collapse_end(), c = 0) {} + for( int c = ui_collapse(ICON_MD_ACCOUNT_TREE " Edit/", "ORD4"); c; ui_collapse_end(), c = 0) {} + for( int c = ui_collapse(ICON_MD_ACCOUNT_TREE " Quit/", "ORD5"); c; ui_collapse_end(), c = 0) {} + + // node_edit(&editor.init,&editor.init); + // node_edit(&editor.draw,&editor.draw); + // node_edit(&editor.tick,&editor.tick); + // node_edit(&editor.edit,&editor.edit); + // node_edit(&editor.quit,&editor.quit); + } + for( int c = ui_collapse(ICON_MD_PUBLIC " World/", "WORLD"); c; ui_collapse_end(), c = 0) { + node_edit(editor.edit.down,&editor.edit); + } + + ui_window_end(); + } +} + +unsigned editor_spawn(const char *path_id, const char *keytype, void *val) { + do_once { + node_set_nametype(&editor.init, "Init Group", "init_nodes"); // @leak + node_set_nametype(&editor.tick, "Tick Group", "tick_nodes"); // @leak + node_set_nametype(&editor.draw, "Draw Group", "draw_nodes"); // @leak + node_set_nametype(&editor.edit, "Edit Group", "edit_nodes"); // @leak + node_set_nametype(&editor.quit, "Quit Group", "quit_nodes"); // @leak + } + + array(char*) tokens = strsplit(path_id, "/"); + char *keyname = *array_back(tokens); + + array(node) n = 0; + array_resize(n, EDITOR_BUCKETS); + for( int i = 0; i < EDITOR_BUCKETS; ++i ) { + node_set_nametype(&n[i], keyname, keytype); // @leak + n[i].v.ptr = val; + } + + array_pop(tokens); + char *joint = array_count(tokens) ? strjoin(tokens, "/") : "/"; + + node *p = 0; + p = node_find_recurse(&editor.init, joint), node_attach_child(p ? p : &editor.init, n+0); + p = node_find_recurse(&editor.tick, joint), node_attach_child(p ? p : &editor.tick, n+1); + p = node_find_recurse(&editor.draw, joint), node_attach_child(p ? p : &editor.draw, n+2); + p = node_find_recurse(&editor.edit, joint), node_attach_child(p ? p : &editor.edit, n+3); + p = node_find_recurse(&editor.quit, joint), node_attach_child(p ? p : &editor.quit, n+4); + return 0; +} + +// demo ----------------------------------------------------------------------- + +typedef struct my_sprite { + char *filename; + vec3 position; + float tilt; + vec4 tint; + // --- private + texture_t texture_; + unsigned bgra_; +} my_sprite; + +void my_sprite_ctor(my_sprite *obj) { + obj->texture_ = texture(obj->filename, TEXTURE_RGBA); + obj->bgra_ = bgraf( obj->tint.r/255.0, obj->tint.g/255.0, obj->tint.b/255.0, obj->tint.a/255.0 ); +} +void my_sprite_draw(my_sprite *obj) { + obj->bgra_ = bgraf( obj->tint.r/255.0, obj->tint.g/255.0, obj->tint.b/255.0, obj->tint.a/255.0 ); // @fixme: del me + sprite( obj->texture_, &(obj->position.x), obj->tilt, obj->bgra_ ); +} + +int main() { + typedef char* string; + typedef vec4 color; + + STRUCT( vec3, float, x, "X" ); + STRUCT( vec3, float, y, "Y" ); + STRUCT( vec3, float, z, "Z" ); + +// STRUCT( texture_t, unsigned, flags, "Flags"); +// STRUCT( texture_t, string, filename, "Filename"); + + STRUCT( my_sprite, string, filename, "Filename" ); + STRUCT( my_sprite, vec3, position, "Position" ); + STRUCT( my_sprite, float, tilt, "Tilt degrees" ); + STRUCT( my_sprite, color, tint, "Tint color" ); + STRUCT_CTOR( my_sprite, my_sprite_ctor ); + + PRINTF("pod:%d, var:%d, node:%d warn\n", (int)sizeof(pod), (int)sizeof(var), (int)sizeof(node)); + PRINTF("reflected:%d bytes vs real:%d bytes warn\n", reflect_sizeof("my_sprite"), (int)sizeof(my_sprite)); + + // cook_config("../../tools/cook.ini"); + window_create(0.80, 0); + + struct my_sprite spr1 = {0}, spr2 = {0}, spr3 = {0}; + obj_make(&spr1, + "[my_sprite]\n" + "filename=cat.png\n" + "position=5 2 100\n" + "tilt=45 + 45 -90\n" + "tint=255 255 0\n" + ); + obj_make(&spr2, + "[my_sprite]\n" + "filename=cat.png\n" + "position=1 2 100\n" + "tilt=45 + 45 -90\n" + "tint=255 0 0\n" + ); + obj_make(&spr3, + "[my_sprite]\n" + "filename=cat.png\n" + "position=1 2 100\n" + "tilt=45\n" + "tint=0 0 255\n" + ); + int hero1 = editor_spawn("/hero1", "my_sprite", &spr1); + int hero2 = editor_spawn("/hero2", "my_sprite", &spr2); + int hero3 = editor_spawn("/hero1/heroB", "my_sprite", &spr3); + + camera_t cam = camera(); + camera_enable(&cam); + + while( window_swap() ) { + editor_frame(); + + // @fixme: this should be drawn by game, not editor! + my_sprite_draw(&spr1); + my_sprite_draw(&spr2); + my_sprite_draw(&spr3); + // spr1.tilt = 5 * sin(time_ss()); + } +} diff --git a/tools/editor/editor2.h b/tools/editor/editor2.h new file mode 100644 index 0000000..e82ab0c --- /dev/null +++ b/tools/editor/editor2.h @@ -0,0 +1,343 @@ +#define EDITOR_VERSION "2022.7" + +#if 1 +#define EDITOR_PRINTF PRINTF +#else +#define EDITOR_PRINTF(...) do {} while(0) +#endif + +#define ICON_PLAY ICON_MD_PLAY_ARROW +#define ICON_PAUSE ICON_MD_PAUSE +#define ICON_STOP ICON_MD_STOP +#define ICON_CANCEL ICON_MD_CLOSE + +#define ICON_WARNING ICON_MD_WARNING +#define ICON_BROWSER ICON_MD_FOLDER_SPECIAL +#define ICON_OUTLINER ICON_MD_VIEW_IN_AR +#define ICON_BUILD ICON_MD_BUILD +#define ICON_SCREENSHOT ICON_MD_PHOTO_CAMERA +#define ICON_CAMERA_ON ICON_MD_VIDEOCAM +#define ICON_CAMERA_OFF ICON_MD_VIDEOCAM_OFF +#define ICON_GAMEPAD_ON ICON_MD_VIDEOGAME_ASSET +#define ICON_GAMEPAD_OFF ICON_MD_VIDEOGAME_ASSET_OFF +#define ICON_AUDIO_ON ICON_MD_VOLUME_UP +#define ICON_AUDIO_OFF ICON_MD_VOLUME_OFF +#define ICON_WINDOWED ICON_MD_FULLSCREEN_EXIT +#define ICON_FULLSCREEN ICON_MD_FULLSCREEN +#define ICON_LIGHTS_ON ICON_MD_LIGHTBULB +#define ICON_LIGHTS_OFF ICON_MD_LIGHTBULB_OUTLINE +#define ICON_RENDER_BASIC ICON_MD_IMAGE_SEARCH +#define ICON_RENDER_FULL ICON_MD_INSERT_PHOTO + +#define ICON_SIGNAL ICON_MD_SIGNAL_CELLULAR_ALT +#define ICON_DISK ICON_MD_STORAGE +#define ICON_RATE ICON_MD_SPEED + +#define ICON_CLOCK ICON_MD_TODAY +#define ICON_CHRONO ICON_MD_TIMELAPSE + +#define ICON_SETTINGS ICON_MD_SETTINGS +#define ICON_LANGUAGE ICON_MD_G_TRANSLATE +#define ICON_PERSONA ICON_MD_FACE +#define ICON_SOCIAL ICON_MD_MESSAGE +#define ICON_GAME ICON_MD_ROCKET_LAUNCH +#define ICON_KEYBOARD ICON_MD_KEYBOARD +#define ICON_MOUSE ICON_MD_MOUSE +#define ICON_GAMEPAD ICON_MD_GAMEPAD +#define ICON_MONITOR ICON_MD_MONITOR +#define ICON_WIFI ICON_MD_WIFI +#define ICON_BUDGET ICON_MD_SAVINGS +#define ICON_NEW_FOLDER ICON_MD_CREATE_NEW_FOLDER +#define ICON_PLUGIN ICON_MD_EXTENSION +#define ICON_RESTART ICON_MD_REPLAY +#define ICON_QUIT ICON_MD_CLOSE + +#define ICON_POWER ICON_MD_BOLT // ICON_MD_POWER +#define ICON_BATTERY_CHARGING ICON_MD_BATTERY_CHARGING_FULL +#define ICON_BATTERY_LEVELS \ + ICON_MD_BATTERY_ALERT, \ + ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR, \ + ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR, \ + ICON_MD_BATTERY_4_BAR,ICON_MD_BATTERY_5_BAR, \ + ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL + +// state - retained mode + +typedef int property; // @fixme + +typedef struct editor_state_t { + array(property) properties; + array(char) buffer; + array(vec2i) history; + unsigned cursor; +} editor_state_t; + +typedef map(char*, char*) editor_dict_t; + +static map(void*, editor_state_t) editor_state; // world +static map(void*, editor_dict_t) editor_dicts; +static set(void*) editor_world; +static set(void*) editor_selection; // objects selected in scene + +// editor controls + +//static int editor_attached = 1; +static int editor_enabled = 1; +static void* editor_selected_obj = 0; +static int editor_key = 0; +static vec2 editor_mouse = {0}; // 2d coord for ray/picking +static bool editor_gamepad = 1; +static int editor_hz = 60; +static int editor_hz_mid = 18; +static int editor_hz_low = 5; +static bool editor_power_saving = 0; +static double editor_t = 0, editor_dt = 0; +static bool editor_lit = 1; +static bool editor_ddraw = 1; + +static +void editor_init() { + do_once { + map_init_ptr(editor_state); + map_init_ptr(editor_dicts); + set_init_ptr(editor_world); + set_init_ptr(editor_selection); + profile_enable( false ); + window_pause( true ); + } +} + +void editor_tick() { + // timing + editor_dt = window_delta() * !window_has_pause(); if(editor_dt > 1/60.f) editor_dt = 1/60.f; +} + +bool editor_active() { + return ui_hover() || ui_active() || gizmo_active() ? editor_enabled : 0; +} +double editor_ss() { + return 1000 + editor_t; +} +double editor_delta() { + return editor_dt; +} + +void editor_select_all() {} +void editor_select_none() {} + + +enum editor_keys { + key_none, + key_pause, + key_reload, + key_browser, + key_recording, + key_fullscreen, + key_screenshot, // @todo: add meta-info in exif or invisibile pixels (cam details, player details, map level, map location, level state, etc) + key_quit, + key_mute, + key_battery, + key_profiler, + key_stop, + key_outliner, + key_undo, + key_redo, + key_save_mem, + key_save_disk, + key_load_disk, + key_reset, + key_debugger, + key_gamepad, + key_lit, + key_ddraw, +}; + +void editor_menubar() { + do_once editor_init(); + + int alts = input(KEY_LALT) || input(KEY_RALT); // @todo: move to fwk.c + int ctrls = input(KEY_LCTRL) || input(KEY_RCTRL); // @todo: move to fwk.c + int shifts = input(KEY_LSHIFT) || input(KEY_RSHIFT); // @todo: move to fwk.c + int mods = alts || ctrls || shifts; // @todo: move to fwk.c + if( input_down(KEY_F5) ) editor_key = key_reload; + if( input_down(KEY_F11) ) editor_key = key_fullscreen; + if( input_down(KEY_PAUSE) ) editor_key = key_pause; + if( input_down(KEY_PRINT) ) editor_key = (mods ? key_recording : key_screenshot); + // if( input_down(KEY_W) && input_held(KEY_LCTRL) ) editor_key = key_quit; + + if( ctrls ) { + /**/ if( input_down(KEY_Z) ) editor_key = key_undo; + else if( input_down(KEY_Y) ) editor_key = key_redo; + else if( input_down(KEY_S) ) editor_key = key_save_disk; + else if( input_down(KEY_A) ) editor_select_all(); + else if( input_down(KEY_D) ) editor_select_none(); + } + + if( !editor_key && editor_selected_obj ) { + if( input_up(MOUSE_L) ) editor_key = key_save_mem; + if( input_down(MOUSE_R) ) ui_show("Properties", true); + } + + // @fixme: send all editor keys to game? + // if( input_repeat(KEY_ESC, 300)) {} + // if( input_repeat(KEY_F1, 300)) {} + // etc... + + // menubar + + if( ui_menu( ICON_SETTINGS "@Preferences;" + ICON_LANGUAGE " Language;" + ICON_PERSONA " Profile;" // editor account, but also fake profile and 1st party credentials + ICON_SOCIAL " Social;" + ICON_GAME " Game;" // + ICON_KEYBOARD " Keyboard;" + ICON_MOUSE " Mouse;" + ICON_GAMEPAD " Gamepad;" + ICON_MONITOR " Display;" // @todo: RENDER settings, AUDIO settings + ICON_WIFI " Network;" + ICON_BUDGET " Budget;" // mem,gfx,net,hdd,... also logging + ICON_NEW_FOLDER " Folders;" // including project folders + ICON_PLUGIN " Plugins;" // including VCS + ICON_RESTART " Restart;" + ICON_QUIT " Quit;" + "-" ICON_MD_RECYCLING " Reset all preferences;" ICON_MD_SAVE_AS " Save all preferences" + ) ) { + if( ui_item() == 3 ) {} // key mappings + if( ui_item() == 4 ) {} // sensitivity, invert xylrw + if( ui_item() == 5 ) {} // sensitivity, invert xy,ab, deadzones + if( ui_item() == 7 ) {} // name,email,icon,link,github + if( ui_item() == 13) editor_key = key_reload; + if( ui_item() == 14) editor_key = key_quit; + } + + static char game_args[16] = "--game-args"; // @fixme @todo remove '_' special char to signal that ui_menu() is writeable (inputbox) + if( ui_menu_editbox( game_args, 16 ) ) {} + + if( ui_menu( ICON_CANCEL "@Cancel" ) ) {} + + if( ui_menu( window_has_pause() ? ICON_PLAY "@Tap to Play Game" : ICON_PAUSE "@Tap to Pause Game" )) editor_key = key_pause; + if( ui_menu( ICON_MD_SKIP_NEXT "@Next frame") ) {} + if( ui_menu( ICON_MD_FAST_FORWARD "@Fast forward") ) {} + //if( ui_menu( ICON_STOP "@Stop game" )) editor_key = key_stop; + + if( ui_menu( va(ICON_BUILD "@Build game"))) ui_notify("Build", ICON_WARNING " Not implemented."); + if( ui_menu( va(ICON_MD_ROCKET_LAUNCH "@Launch game"))) ui_notify("Launch", ICON_WARNING " Not implemented."); + + // ICON_MD_TROUBLESHOOT -> PROFILER + // ICON_MD_SCHEMA -> GRAPHNODES + // ICON_MD_ACCOUNT_TREE -> GRAPHNODES + // ICON_MD_TIPS_AND_UPDATES -> BULB + // if( ui_menu( ICON_MD_MENU )) {} + +// if( ui_menu( ICON_BROWSER "@Content browser" )) editor_key = key_browser; +// if( ui_menu( va(ICON_OUTLINER " %d/%d@World outliner", set_count(editor_selection), map_count(editor_state) ))) editor_key = key_outliner; + + if( ui_menu( ICON_SCREENSHOT "@Take Screenshot" )) editor_key = key_screenshot; // MD_SCREENSHOT_MONITOR + if( ui_menu( record_active() ? ICON_CAMERA_OFF "@Stop video recording" : ICON_CAMERA_ON "@Start video recording" )) { if(record_active()) record_stop(); else editor_key = key_recording; } + if( ui_menu( editor_gamepad ? ICON_GAMEPAD_ON "@Gamepad is enabled. Tap to disable." : ICON_GAMEPAD_OFF "@Gamepad is disabled. Tap to enable." )) editor_key = key_gamepad; + if( ui_menu( audio_volume_master(-1) > 0 ? ICON_AUDIO_ON "@Audio is enabled. Tap to mute." : ICON_AUDIO_OFF "@Audio is muted. Tap to enable." )) editor_key = key_mute; + if( ui_menu( window_has_fullscreen() ? ICON_WINDOWED "@Fullscreen. Tap to go Windowed." : ICON_FULLSCREEN "@Windowed. Tap to go Fullscreen." )) editor_key = key_fullscreen; + + if( ui_menu( editor_ddraw ? ICON_RENDER_BASIC "@Debug renderer. Tap to go Retail Renderer." : ICON_RENDER_FULL "@Retail renderer. Tap to go Debug Renderer." )) editor_key = key_ddraw; // ICON_MD_ADD_PHOTO_ALTERNATE + if( ui_menu( editor_lit ? ICON_LIGHTS_ON "@Lit. Tap to disable lighting." : ICON_LIGHTS_OFF "@Unlit. Tap to enable lighting." )) editor_key = key_lit; + + // logic: either plug icon (power saving off) or one of the following ones (power saving on): + // if 0% batt (no batt): battery alert + // if discharging: battery levels [alert,0..6,full] + // if charging: battery charging + int battery_read = app_battery(); + int battery_level = abs(battery_read); + int battery_discharging = battery_read < 0 && battery_level < 100; + const char *battery_levels[] = { // @todo: remap [7%..100%] -> [0..1] ? + ICON_BATTERY_LEVELS + }; + if( ui_menu( !editor_power_saving ? ICON_POWER"@Full power. Tap to save power." : + va("%s@Power saving. Tap to go full power. %3d%% battery.", + battery_read == 0 ? battery_levels[0] : + battery_discharging ? battery_levels[(int)((countof(battery_levels)-1)*clampf(battery_level/100.f,0,1))] : + ICON_BATTERY_CHARGING, battery_level) )) + editor_key = key_battery; + + // @todo: combine-in-1? cycle mem -> cpu/profiler -> network mon -> debugger + + // bug report, signal status, disk status, framerate status + if( ui_menu(va(ICON_SIGNAL " 0/0KiB" ))) {} // SIGNAL_CELLULAR_1_BAR SIGNAL_CELLULAR_2_BAR + if( ui_menu(va(ICON_DISK " %s", xstats() ))) {} // 012/136MB + if( ui_menu(va(ICON_RATE " %5.2f/%d", window_fps(), (int)window_fps_target()))) editor_key = key_profiler; // 012/136MB + + // bug report, profile, warnings, time/chrono (@todo: alarm/snooze? calendar?) + if( ui_menu( ICON_MD_BUG_REPORT /*"^"*/ "0" ) ) {} + if( ui_menu( ICON_MD_FACE /*"^"*/ "3" ) ) {} // @todo: do both messaging/warnings + profile settings here + { + static double base = 0, tap = 0; + + if( tap == 0 ) tap = time_ss(); + double delta = time_ss() - tap; + tap = time_ss(); + base += delta * !window_has_pause(); + + if( ui_menu( base == 0 ? + va(ICON_CLOCK "%02d:%02d", (int)((date() / 10000) % 100), (int)((date() / 100) % 100)) + : + va(ICON_CHRONO "%03dm:%02ds:%02df@Tap to reset chrono.",((int)(base/60))%60,((int)base)%60,(int)((base - (int)base)*window_fps_target()))) + || editor_key == key_stop + ) { + base = 0, tap = 0; + } + } + + for each_map_ptr(editor_state, void *, o, editor_state_t, ed) { + profile_incstat("Editor.num_objects", +1); + + void *obj = *o; + +#if 1 +#elif 0 + // auto-load from disk during init. @fixme kvdb database + if( array_count(ed->history) == 0 ) + if( editor_load_disk(obj, editor_obj_string(obj, ".path")) ) + {} + + // auto-save in-mem during first edit + if( array_count(ed->history) == 0 ) + editor_save_mem(obj); +#endif + + // @todo: continue if obj not found in selection set + if( obj != editor_selected_obj ) + continue; + + if( editor_key == key_debugger ) { breakpoint("User requested breakpoint on this object"); } +#if 1 +#elif 0 + if( editor_key == key_reset ) { const char *ok = editor_reset(obj) ? "ok" : "err"; EDITOR_PRINTF("reset: %s\n", ok); } + if( editor_key == key_save_mem ) { const char *ok = editor_save_mem(obj) ? "ok" : "err"; EDITOR_PRINTF("mem saved: %s\n", ok); } + if( editor_key == key_undo ) { const char *ok = editor_undo(obj) ? "ok" : "err"; EDITOR_PRINTF("undo: %s\n", ok); } + if( editor_key == key_redo ) { const char *ok = editor_redo(obj) ? "ok" : "err"; EDITOR_PRINTF("redo: %s\n", ok); } + if( editor_key == key_save_disk ) { const char *ok = editor_save_disk(obj, editor_obj_string(obj, ".path")) ? "ok" : "err"; EDITOR_PRINTF("save: %s\n", ok); } + if( editor_key == key_load_disk ) { const char *ok = editor_load_disk(obj, editor_obj_string(obj, ".path")) ? "ok" : "err"; EDITOR_PRINTF("load: %s\n", ok); } +#endif + } + + char *name; + switch( editor_key ) { + default: + break; case key_quit: record_stop(), exit(0); + break; case key_stop: window_pause(1); + break; case key_mute: audio_volume_master( 1 ^ !!audio_volume_master(-1) ); + break; case key_pause: window_pause( window_has_pause() ^ 1 ); + break; case key_reload: window_reload(); + break; case key_battery: editor_power_saving ^= 1; + break; case key_browser: ui_show("File Browser", ui_visible("File Browser") ^ true); + break; case key_outliner: ui_show("Outliner", ui_visible("Outliner") ^ true); + break; case key_recording: name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string()); + break; case key_screenshot: name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string()); + break; case key_profiler: ui_show("Profiler", profile_enable(ui_visible("Profiler") ^ true)); + break; case key_fullscreen: record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand + break; case key_gamepad: editor_gamepad ^= 1; + break; case key_lit: editor_lit ^= 1; + break; case key_ddraw: editor_ddraw ^= 1; + } + + editor_key = 0; +} diff --git a/tools/editor/labs.meta/meta_dna.c b/tools/editor/labs.meta/meta_dna.c new file mode 100644 index 0000000..7db2ba1 --- /dev/null +++ b/tools/editor/labs.meta/meta_dna.c @@ -0,0 +1,328 @@ +// @todo fsave: fputs(DNA), then fwrite +// @todo fread: fgets(DNA), abort if DNA != read; then fread + +#include + +// load/save whole struct acording to its DNA structure +int fload(FILE *infile, void *structure, const char *dna); +int fsave(FILE *outfile, const void *structure, const char *dna); + +// set alignment for next fload/fsave call. resets back to 0 after every fload/fsave call. +// value: 0 (auto, default), 1,2,4,8,16 bytes [...] +void falign(unsigned alignment); + +// override default DNA handlers and switch to a different schema and/or serialization format +typedef int (*size_operator)(char fmt, void *addr, int readmode); +typedef int (*call_operator)(void *out, void *addr, char fmt, int count, int readmode); +void foverride(size_operator size, call_operator call); + +#if 0 +Example Usage +------------- + + struct fat_bootsector { + uint8_t jump_instruction[3]; + uint8_t oem_name[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t fat_copies; + uint16_t max_dirs; + uint16_t sector_count; + uint8_t media_descriptor; + uint16_t sectors_per_fat; + uint16_t sectors_per_head; + uint16_t heads; + uint32_t hidden_sectors; + uint32_t sector_count2; + } fat_struct; + +Now we can read a binary image of the MBR into this structure: + + mreadf(mbr, "i3c8chchchhchhhdd", &fat_struct); + +Directives +---------- + +Supported directives: + + regex meaning + + (blank) ignored + + b read / write uint8_t + w read / write uint16_t (uppercase: use vli encoding; as uint8? ) + u read / write uint32_t (uppercase: use vli encoding; as uint8,16? ) + q read / write uint64_t (uppercase: use vli encoding; as uint8,16,32?) + + m read / write micro + h read / write half (uppercase: use smallest representation; as micro?) + f read / write float (uppercase: use smallest representation; as micro,half?) + d read / write double (uppercase: use smallest representation; as micro,half,float?) + + s read / write string (uppercase: use smallest representation; quarks?) + [] read / write buffer + + < switch to Intel (little endian) byte order + > switch to Motorola (big endian) byte order + ( begin of tight group + ) end of tight group + [0-9]+ next item repeated n times + z skip one byte of input / emit \0 + * consume next structure item but do not read / write +#endif + +// ---------------------------------------------------------------------------- + +#include +#include +#include + +#ifndef __thread +#define __thread __declspec(thread) +#endif + +static __thread int pragma_pack_alignment = 0; + +void falign(unsigned alignment) { + pragma_pack_alignment = alignment; +} + +static +int size_fn(char fmt, uint8_t* addr8, int rd) { + if(addr8) { + // sizeof pointee data & align operator + /**/ if(fmt == 'c' || fmt == 'b') return 1; + else if(fmt == 'w' ) return 2; + else if(fmt == 'i' || fmt == 'u') return 4; + else if(fmt == 'l' || fmt == 'q') return 8; + else if(fmt == 'f' ) return 4; + else if(fmt == 'd' ) return 8; + else if(fmt == 's' ) return !*(char**)addr8 ? 0+1 : !rd ? strlen(*(char**)addr8) + 1 : strcspn(*(char**)addr8,"\x1") + 1; + return -1; + } else { + // sizeof member + /**/ if(fmt == 'w' ) return 2; + else if(fmt == 'i' || fmt == 'u') return 4; + else if(fmt == 'l' || fmt == 'q') return 8; + else if(fmt == 'f' ) return 4; + else if(fmt == 'd' ) return 8; + else if(fmt == 's' ) return sizeof(void*); + return 1; + } +} + +static +int call_fn(void *out, uint8_t *addr8, char fmt, int count, int rd) { // rd/wr operator + FILE *fp = (FILE*)out; + int iterated_bytes = 0, processed_bytes = 0; + while( --count >= 0 ) { + int slot = size_fn(fmt, 0, rd); + int size = size_fn(fmt, addr8, rd); + if(rd) + switch (fmt) { + default: return -1; + break; case 'c': case 'b': fscanf(fp, "%c,", (uint8_t*)addr8); + break; case 'w': fscanf(fp, "%#04llx,", (uint16_t*)addr8); + break; case 'i': case 'u': fscanf(fp, "%#08llx,", (uint32_t*)addr8); + break; case 'l': case 'q': fscanf(fp, "%#16llx,", (uint64_t*)addr8); + break; case 'f': fscanf(fp, "%f,", (float*)addr8); + break; case 'd': fscanf(fp, "%llf,", (double*)addr8); + break; case 's': fscanf(fp, "%[^\x1],", (char*)addr8); + } + else + switch(fmt) { + default: return -1; + break; case 'c': case 'b': fprintf(fp, "%c,", (int)*(uint8_t*)addr8); + break; case 'w': fprintf(fp, "%#04llx,", (uint64_t)*(uint16_t*)addr8); + break; case 'i': case 'u': fprintf(fp, "%#08llx,", (uint64_t)*(uint32_t*)addr8); + break; case 'l': case 'q': fprintf(fp, "%#16llx,", (uint64_t)*(uint64_t*)addr8); + break; case 'f': fprintf(fp, "%f,", (float)*(float*)addr8); + break; case 'd': fprintf(fp, "%f,", (double)*(double*)addr8); + break; case 's': fprintf(fp, "%s\x1,", *(char**)addr8); + } + addr8 += slot; + iterated_bytes += slot; + processed_bytes += size; + } + return iterated_bytes; +} + +static size_operator fsize_fn = size_fn; +static call_operator fcall_fn = call_fn; + +void foverride(size_operator size, call_operator call) { + fsize_fn = size; + fcall_fn = call; +} + +int fdump(FILE *out, void *addr, const char *dna, int rd) { + unsigned pragma_pack = pragma_pack_alignment; pragma_pack_alignment = 0; // reset alignment + uint8_t *addr8 = (uint8_t*)addr; + int size = 0, count = 1, skip = 0; + int last_type = 0; + int written = 0; + int align = 0; + for( int i = 0; dna[i]; ++i) { + char fmt = dna[i]; + /**/ if(fmt <= 32) continue; + else if(fmt >= '0' && fmt <= '9') continue; + else if(fmt == 'z') skip = 1; + else { + + // member alignment + if( last_type != fmt ) { // check if next struct member was found (may need re-alignment) + if( pragma_pack != 1 ) { // forced (>1) or auto-alignment (0)? + //printf("*%p ->", addr8); + align = pragma_pack == 0 ? fsize_fn(fmt, 0, rd) : pragma_pack; + // Round up to N-byte boundary + addr8 = (uint8_t*)(((uintptr_t)(addr8) + ((align) - 1)) & -(align)); + //printf(" %p\n", addr8); + } + } + + last_type = fmt; + + size = fsize_fn(fmt, addr8, rd); + if( size < 0 ) { + fprintf(stderr, "parse error, unknown dna sequence '%c'!\n", fmt); + return -i-1; + } + } + + if( skip ) { skip = 0; continue; } + + char next = dna[i+1]; + if( next >= '0' && next <= '9' ) { + count = next - '0'; + } + int bytes = skip || count == 0 ? 0 : call_fn(out, addr8, dna[i], count, rd); + if( bytes < 0 ) { + fprintf(stderr, "stream fail. rc: %d\n", bytes); + return bytes; + } + written += bytes; + addr8 += bytes; + count = 1; + + fprintf(out, "\n"); + } + return written; +} + +int fsave(FILE *out, const void *structure, const char *dna) { + return fdump(out, (void*)structure, dna, 0); +} +int fload(FILE *inf, void *structure, const char *dna) { + return fdump(inf, structure, dna, 1); +} +int fsavefile(const char *outfile, const void *structure, const char *dna) { + FILE *fp = fopen(outfile, "wb"); + if( !fp ) return 0; + int bytes = fdump(fp, (void*)structure, dna, 0); + fclose(fp); + return bytes; +} +int floadfile(const char *infile, void *structure, const char *dna) { + FILE *fp = fopen(infile, "rb"); + if( !fp ) return 0; + int bytes = fdump(fp, (void*)structure, dna, 1); + fclose(fp); + return bytes; +} + +// --- + +#include + + +// #pragma pack(1) + +struct fat_mbr { + uint8_t jmp[3]; // b3 + uint8_t oem[8]; // b8 + const char* str; // s + uint16_t bytes_per_sector; // w + uint8_t sectors_per_cluster; // b + uint16_t reserved_sectors; // w + uint8_t fat_copies; // b + uint16_t max_dirs; // w + uint16_t sector_count; // w + uint8_t media_descriptor; // b + uint16_t sectors_per_fat; // w + uint16_t sectors_per_head; // w + uint16_t heads; // w + uint32_t hidden_sectors; // u + uint32_t sector_count2; // u + float pi; + char break_alignment; // b + double phi; +}; + + #define FAT_MBR_DNA "b3b8s wb wb w wb www uu fbd" + +// #pragma pack(pop) + + +int main() { + +// foverride(size_fn, write_fn); + + struct fat_mbr mbr = { + {'a','b','c'}, + {'d','e','f','g','h','i','j','k'}, + "hello 'escaped' \"world\"", + 0x100,'l', + 0x101,'m', + 0x102, + 0x103,'n', + 0x104, + 0x105, + 0x106, + 0x01234567, + 0x89abcdef, + 3.14159f,'o', + 1.6069, + }; + +// fdump(stdout, &mbr, FAT_MBR_DNA); exit(0); +// printf("%p\n", &mbr.jmp); +// printf("%p\n", &mbr.oem); +// printf("%p\n", &mbr.str); +// printf("%p\n", &mbr.sector_count); + +// falign(0); + int bytes = fsave(stdout, &mbr, FAT_MBR_DNA); + printf("%d bytes written\n", bytes); + + + typedef struct entitystate { + struct pos { + short trTime; + float trBase[3]; + } pos; + } entitystate; + entitystate e = { 123, 3.14159f, 4.14159f, 5.14159f }; + bytes = fsave(stdout, &e, "wfff"); + printf("%d bytes written\n", bytes); + + +// exit(0); + + struct fat_mbr zero = {0}; + struct fat_mbr src = mbr; + struct fat_mbr dst = zero; + + dst = src; + assert(0 == memcmp(&src,&dst,sizeof(struct fat_mbr))); + + dst = zero; + assert(0 != memcmp(&src,&dst,sizeof(struct fat_mbr))); + + int sbytes = fsavefile(".temp", &src, FAT_MBR_DNA); + int lbytes = floadfile(".temp", &dst, FAT_MBR_DNA); + assert( sbytes == lbytes ); + assert(0 != memcmp(&src,&dst,sizeof(struct fat_mbr))); + + assert(!puts("Ok")); +} diff --git a/tools/editor/labs.meta/meta_info.c b/tools/editor/labs.meta/meta_info.c new file mode 100644 index 0000000..32d1b22 --- /dev/null +++ b/tools/editor/labs.meta/meta_info.c @@ -0,0 +1,292 @@ +//#define META_DEMO + +#include "fwk.h" + +#include +#include +#include +#include +#include +#include + +static +char *reformat(const char *text, const char *blacklist, const char *separator) { + char **list = strsplit(text, blacklist); + char *joint = strjoin(list, separator); + return joint; +} + +const char *c_symbols = "{},>&/+=;:"; // "(){}[].,><&*/+=;:!~"; +const char *c_symbols_extra = "{},>&/+=;:[]()"; // "(){}[].,><&*/+=;:!~"; +const char *c_keywords[] = { "const","long","signed","unsigned","typedef" }; int num_keywords = 4; + +typedef struct meta { + const char *name; + const char *tags; +} meta; +meta metas[2048] = { +/*00*/ {"typedef", ""}, +/*01*/ {"enum", ""}, +/*02*/ {"struct", ""}, +/*03*/ {"function", ""}, + +/*04*/ {"bool", "default 0"}, +/*05*/ {"false", "default 0 bits 1"}, +/*06*/ {"true", "default 1 bits 1"}, + +/*07*/ {"char", "default 0 bits 8"}, +/*08*/ {"int", "default 0"}, +/*09*/ {"float", "default 0 bits 32"}, +/*10*/ {"double", "default 0 bits 64"}, + +/*11*/ {"int8_t", "default 0 bits 8"}, +/*12*/ {"int16_t", "default 0 bits 16"}, +/*13*/ {"int32_t", "default 0 bits 32"}, +/*14*/ {"int64_t", "default 0 bits 64"}, +/*15*/ {"uint8_t", "default 0 bits 8 signed 0"}, +/*16*/ {"uint16_t", "default 0 bits 16 signed 0"}, +/*17*/ {"uint32_t", "default 0 bits 32 signed 0"}, +/*18*/ {"uint64_t", "default 0 bits 64 signed 0"}, +}; +int meta_count = 19; +const char *meta_last_name = 0; +const char *meta_last_struct = 0; + +meta* meta_find(const char *name) { + for( int i = 0; i < meta_count; ++i ) { + if( 0 == strcmp(name, metas[i].name) ) return &metas[i]; + } + return 0; +} +meta* meta_add(const char *name, const char *subtype, const char *tags ) { + { meta *t = meta_find(name); if( t ) return t; } + + if( meta_count >= 2048 ) return 0; + + meta t = { STRDUP(name), reformat(STRDUP(tags), " ", " ") }; + metas[meta_count] = t; + meta_last_struct = !strcmp(subtype, "struct") ? name : meta_last_struct; + meta_last_name = !strcmp(subtype, "struct") || !strcmp(subtype, "enum") || !strcmp(subtype, "typedef") ? name : subtype; + return &metas[meta_count++]; +} +char* meta_find_value(const char *name, const char *prop) { + meta *t = meta_find(name); + if( !t ) return 0; + char *found = strstr(t->tags, prop); + if( !found ) return 0; + + static char copy[256] = {0}; // @fixme + strcpy(copy, found + strlen(prop) + 1); + for(int i=0; copy[i]; ++i) if(copy[i]==' ') { copy[i] = 0; break; } + return copy; +} +void meta_inspectf(FILE *fp) { + if( fp ) { + char buf[255+1]; + + while(fgets(buf, 255, fp)) { + char *mark = strstr(buf, "//""M"); + if( !mark ) continue; + char *tags = mark + 3; + + // must reset last_struct + if( strstr(buf, "typedef") ) { + meta_last_struct = 0; // typedef is a special case + } + + // remove symbols + for( int i = 0; buf[i]; ++i ) { + // symbols off + if(strchr(&buf[i] < tags ? c_symbols : c_symbols_extra, buf[i])) { buf[i] = ' '; continue; } + } + + // remove leading whitespaces + char *spc = buf; + while( spc && spc[0] && spc[0] <= 32 ) ++spc; + + // remove trailing linefeeds and whitespaces + int len = strlen(buf); + while( len > 0 && buf[len-1] <= 32 ) buf[--len] = 0; + + // split buffer into left|right buffers + char *left = spc, *right = tags; right[-1] = 0; + + // remove left keywords + for( int i = 0; left[i]; ++i ) { + for(int j = 0; j < sizeof(c_keywords)/sizeof(0[c_keywords]); ++j) { + int kb = strlen(left+i); + int kl = strlen(c_keywords[j]); + if( kl <= kb && !memcmp(left+i, c_keywords[j], kl) ) { + memset(left+i, ' ', kl); + i += kl; + break; + } + } + } + + // debug + // printf("%s <--> %s\n", left, right); + + // add type + const char *meta_name = 0; + const char *meta_t = 0; + const char *meta_val = ""; + array(char *) tokens = strsplit(left, " \t"); + if(1) for( int i = 0, end = array_count(tokens); i < end; ++i ) { + /**/ if( strstr(tokens[i],"enum") ) meta_last_name = 0, meta_last_struct = 0, meta_t = "enum"; + else if( strstr(tokens[i],"struct") ) meta_last_name = 0, meta_last_struct = 0, meta_t = "struct"; + else if( strstr(tokens[i],"typedef") ) meta_last_name = 0, meta_last_struct = 0, meta_t = "typedef"; // trimmed. never happens + else if( strchr(tokens[i],'(') ) meta_last_name = 0, meta_last_struct = 0, meta_name = tokens[i], meta_t = "function"; + else if( !meta_name ) meta_name = tokens[i]; + else if( !meta_t ) meta_t = tokens[i]; + } + + if( meta_find(meta_name) ) { + const char *swap = meta_name; + meta_name = meta_t; + meta_t = swap; + } + + // if !meta_find defer(meta_eval) && emit(warning) && keep(iterating); + + bool is_pointer = strstr(meta_name, "*"); + meta_name = (char *)reformat(meta_name, "*", ""); + char *found_sqr = strstr(meta_name, "["); + int array_len = found_sqr ? atoi(found_sqr+1) : 0; + if( found_sqr ) *found_sqr = 0; + + bool is_number = false; + char conv[32] = {0}; + double dbl = atof(meta_t); + if(!is_number) { sprintf(conv, "%f", dbl); is_number = !strcmp(conv, meta_t); } + if(!is_number) { sprintf(conv, "%.f", dbl); is_number = !strcmp(conv, meta_t); } + if(is_number) { + meta_val = meta_t; + meta_t = meta_last_name; + } + + char hints[256] = {0}, *p = hints; + if( 1 ) p += sprintf(p, "type %s ", meta_t); + if( is_pointer || array_len ) p += sprintf(p, "pointer %d ", 1); + if( array_len ) p += sprintf(p, "count %d ", array_len); + if( 1 ) p += sprintf(p, "%s ", tags); + if( meta_last_struct ) p += sprintf(p, "struct %s ", meta_last_struct); + if( meta_val[0] ) p += sprintf(p, "default %s ", meta_val); + meta_val = hints; + + // inherited tags (from super type) at the very end, so overriden tags can be found at first place + if( meta_find(meta_t) ) + p += sprintf(p, "%s ", meta_find(meta_t)->tags ); + + + if( !meta_find(meta_name) ) { + meta_add(meta_name, meta_t, meta_val); + + meta *t = meta_find(meta_name); + //printf("%s,%s\n", t->name, t->tags); + } + + } + // clean state + meta_last_name = 0; + meta_last_struct = 0; + } +} + +bool meta_save(FILE *fp) { + if( fp ) + for( int i = 0; i < meta_count; ++i ) { + meta *t = &metas[i]; + fprintf(fp, "%s %s\n", t->name, t->tags); + } + return !!fp; +} +bool meta_load(FILE *fp) { + if( fp ) { + meta_count = 0; + meta_last_name = 0; + meta_last_struct = 0; + + char buf[2048+1]; + while(fgets(buf, 2048, fp)) { + int bl = strlen(buf); while( bl && buf[bl] <= 32) buf[bl--] = 0; + char id[128]; + sscanf(buf, "%s", id); + metas[ meta_count ].name = strdup(id); + metas[ meta_count ].tags = strdup(buf+strlen(id)+1); + ++meta_count; + } + } + return !!fp; +} + +void meta_inspect(const char *name) { + FILE *fp = fopen(name, "rb"); + if( !fp ) return; + meta_inspectf(fp); + fclose(fp); +} +bool meta_savefile(const char *name) { + FILE *fp = fopen(name, "wb"); + if( !fp ) return false; + meta_save(fp); fclose(fp); + return true; +} +bool meta_loadfile(const char *name) { + FILE *fp = fopen(name, "rb"); + if( !fp ) return false; + meta_load(fp); fclose(fp); + return true; +} + + +#ifdef META_DEMO +#pragma once + +typedef int my_array[8]; //M +typedef const char *string; //M + +typedef enum Fruit { //M bits:8 + Banana = -1, //M + Orange = 42 //M +} Fruit; + +struct myObject { //M version:100 + Fruit meal; //M default:Banana + int32_t hitpoints; //M default:100 zigzag:yes + float shininess; //M default:+3.5e2 min:0 max:1 fixed:yes + string text; //M default:"DEMO" + bool visible; //M bits:1 deprecated:1 version:001 + char test; //M serialize:off +}; + +int print(int); //M const:true + +typedef int dummy; //M extra=separators allowed(too) + +int main(int argc, const char **argv) { + +#ifndef NDEBUG + unlink(__FILE__ ".meta"); +#endif + + if( meta_loadfile(__FILE__ ".meta") ) { + puts("Loaded .meta file"); + } else { + puts("Cannot read .meta file. regenerating..."); + meta_inspect(__FILE__); + puts("Saving .meta file..."); + meta_savefile(__FILE__ ".meta"); + } + + meta_save( stdout ); + + puts(meta_find_value("Orange", "type")); + puts(meta_find_value("Orange", "bits")); + puts(meta_find_value("Orange", "default")); + puts(meta_find_value("visible", "type")); + puts(meta_find_value("shininess", "fixed")); +} + +#define main main__ +#endif diff --git a/tools/editor/labs.meta/meta_reflect.c b/tools/editor/labs.meta/meta_reflect.c new file mode 100644 index 0000000..2669701 --- /dev/null +++ b/tools/editor/labs.meta/meta_reflect.c @@ -0,0 +1,386 @@ +// missing //M(eta), DNA, +// meta_parse(file), meta_load, meta_save + +#define REFLECT_C +//#define REFLECT_DEMO + +// C reflection: enums, functions, structs, nested structs, members and pointers. +// - rlyeh, public domain + +#ifndef REFLECT_H +#define REFLECT_H + +// # reflection api +// +// - reflected symbol struct +// +// - define reflected symbol (3 quick macros). +// - define reflected symbol (complete function). +// +// - size of reflected symbol. +// - find reflected function (by name). +// - find reflected enum (by name). +// - find reflected field in struct (by name). +// +// - iterate all reflected fields in struct. +// - iterate all reflected symbols in registry. +// +// - @todo: reflect* reflect_find() +// - @todo: code annotations? "display-name", "min", "max", "range", "default" +// - @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/) + +typedef struct reflect { + union { + void *any; + int offs; + }; + const char *type, *name, *base, *info; + unsigned size:23, is_pod:1, is_ptr:6, internal_type:2; +} reflect; + +#define ENUM(type, name, ...) reflect_add(0, (void*)name, #type, #name, "", "" #__VA_ARGS__ "\0", sizeof(enum type)) +#define FUNCTION(type, name, ...) reflect_add(1, &name, #type, #name, "", "" #__VA_ARGS__ "\0", sizeof(void *)) +#define STRUCT(struct, type, name, ...) reflect_add(2, &(((struct *)0)->name), #type, #name, #struct, "" #__VA_ARGS__ "\0", (int)sizeof(type) ) + +void reflect_add( int internal_type, void *any, const char *type, const char *name, const char *base, const char *info, int size ); + +int reflect_sizeof( const char *type ); +void* reflect_function( const char *name ); +int reflect_enum( const char *name ); +void* reflect_field( const char *type, void *obj, const char *name, const char **opt_type ); + +bool reflect_has_fields( const char *base, void *obj ); +void reflect_iterate_fields( const char *type, void *obj, void (*callback)( const reflect *r, void *value, void *userdata ), void *userdata ); +void reflect_iterate_registry( void (*callback)( const reflect *r, void *userdata ), void *userdata ); + +#endif + +// ---------------------------------------------------------------------------- + +#ifdef REFLECT_C +#pragma once +#include +#include +#include + +#ifndef REFLECT_REGISTRY_LIMIT +#define REFLECT_REGISTRY_LIMIT 0 // 0 for unlimited registry size (using heap), or N for fixed max entries (using stack) +#endif + +#if REFLECT_REGISTRY_LIMIT > 0 +static reflect *registry[REFLECT_REGISTRY_LIMIT] = {0}, registry_[REFLECT_REGISTRY_LIMIT] = {0}; +#else +static reflect **registry = 0; +#endif + +enum { + REFLECT_TYPE_ENUM, + REFLECT_TYPE_FUNCTION, + REFLECT_TYPE_FIELD, +}; + +static int reflect_counter = 0; + +void reflect_add( int internal_type, void *any, const char *type, const char *name, const char *base, const char *info, int size ) { + ++reflect_counter; + +#if REFLECT_REGISTRY_LIMIT > 0 + registry[reflect_counter-1] = ®istry_[reflect_counter-1]; +#else + registry = (reflect **)realloc( registry, reflect_counter * sizeof(reflect *)); + registry[reflect_counter-1] = (reflect *)malloc( sizeof(reflect) ); +#endif + + reflect s = { any, type, name, base, info, size, 1, 0, internal_type }; + + reflect *r = registry[reflect_counter-1]; + *r = s; + + // evaluate is_ptr + for( int i = strlen(r->type); !r->is_ptr && --i; ) { + r->is_ptr = r->type[i] == '*' ? i : 0; + } + for( int i = r->is_ptr; i >= 0; --i ) { + r->is_ptr = r->type[i] == ' ' ? i : r->is_ptr; + } + + // @fixme: try to avoid dynamic allocs + // future me: removing this will break some strcmps(type) below + if( r->is_ptr ) { + char buf[128]; sprintf(buf, "%.*s", r->is_ptr, r->type); + r->type = (const char *)strdup(buf); + } + + // evaluate is_pod. kind of bubble sort. + for( int i = 0; i < reflect_counter; ++i ) { + for( int j = 0; j < reflect_counter; ++j ) { + if( !strcmp(registry[i]->base, registry[j]->type) ) { + registry[j]->is_pod = 0; + } + } + } +} + +int reflect_sizeof( const char *base ) { + int size = 0; + if( base ) for( int i = 0; i < reflect_counter; ++i ) { + if( !strcmp(registry[i]->base, base) ) size += registry[i]->size; + } + return size; +} + +void *reflect_field( const char *base, void *obj, const char *name, const char **opt_type ) { + if( base ) for( int i = 0; i < reflect_counter; ++i ) { + if( registry[i]->internal_type == REFLECT_TYPE_FIELD && strcmp(registry[i]->base, base) ) continue; + if(!strcmp(registry[i]->name, name) ) return (opt_type ? *opt_type = registry[i]->type : NULL), (char*)obj + (long long int)registry[i]->any; + } + return 0; +} + +int reflect_enum( const char *name ) { + if( name ) for( int i = 0; i < reflect_counter; ++i ) { + if( registry[i]->internal_type == REFLECT_TYPE_ENUM && !strcmp( registry[i]->name, name ) ) { + return (int)(long long int)registry[i]->any; + } + } + return 0; +} + +// do not use void(void) signature here +static void* reflect_dummy_call() { + return 0; +} + +void* reflect_function( const char *name ) { + if( name ) for( int i = 0; i < reflect_counter; ++i ) { + if( registry[i]->internal_type == REFLECT_TYPE_FUNCTION && !strcmp( registry[i]->name, name ) ) { + return registry[i]->any; + } + } + return &reflect_dummy_call; // return NULL instead? +} + +void reflect_iterate_registry( void (*callback)( const reflect *r, void *userdata ), void *userdata ) { + for( int i = 0; i < reflect_counter; ++i ) { + callback( registry[i], userdata ); + } +} + +#ifdef _MSC_VER +__declspec(thread) +#else +__thread +#endif +struct reflect_context { + const char *name; + const char *info; +} reflect_ctx = { "", "" }; + +void reflect_iterate_fields( const char *base, void *obj, void (*callback)( const reflect *r, void *value, void *userdata ), void *userdata ) { + if( base ) for( int i = 0, nb = strlen(base); i < reflect_counter; ++i ) { + if( registry[i]->internal_type == REFLECT_TYPE_FIELD && !strncmp(registry[i]->base, base, nb) ) { + for( ; i < reflect_counter && !strncmp(registry[i]->base, base, nb); ++i ) { + void *any = ((char*)obj + (long long int)registry[i]->any); + + if( any && registry[i]->is_ptr ) { + any = (void*)*((long long int *)any); + } + + if( any ) { + struct reflect_context copy = reflect_ctx; + char buf1[128]; sprintf(buf1, "%s.%s", reflect_ctx.name, registry[i]->name); + char buf2[128] = {0}; if( registry[i]->info[0] ) sprintf(buf2, "%s%s%.*s", reflect_ctx.info, reflect_ctx.info[0] ? " > " : "", (int)(strlen(registry[i]->info) - 2), 1+registry[i]->info); + reflect_ctx.name = buf1 + (buf1[0] == '.'); + reflect_ctx.info = buf2; + + if( registry[i]->is_pod ) { + reflect m = *registry[i]; + m.name = reflect_ctx.name; + m.info = reflect_ctx.info; + callback( &m, any, userdata ); + } else { + char buf3[128], *rebase = (char*)registry[i]->type; + if(registry[i]->is_ptr) sprintf( rebase = buf3, "%.*s", registry[i]->is_ptr, registry[i]->type ); + reflect_iterate_fields( rebase, any, callback, userdata ); + } + + reflect_ctx = copy; + } + } + return; + } + } +} + +bool reflect_has_fields( const char *base, void *obj ) { + if( base ) for( int i = 0, nb = strlen(base); i < reflect_counter; ++i ) { + if( registry[i]->internal_type == REFLECT_TYPE_FIELD && !strncmp(registry[i]->base, base, nb) ) { + for( ; i < reflect_counter && !strncmp(registry[i]->base, base, nb); ++i ) { + return true; + } + } + } + return false; +} + +void reflect_dump_registry(FILE *fp) { + fprintf( fp, "%s {\n", __FUNCTION__ ); + fprintf( fp, "\tsizeof(reflect)=%d\n", (int)sizeof(reflect) ); + + for( int i = 0; i < reflect_counter; ++i ) { + reflect *r = registry[i]; + + /**/ if( r->internal_type == REFLECT_TYPE_FUNCTION ) + fprintf(fp, "\tfunction %s = %s; // %d@[%p] %s\n", r->name, r->type, r->size, r->any, r->info); + else if( r->internal_type == REFLECT_TYPE_ENUM ) + fprintf(fp, "\tenum %s%s%s = %d; // %d %s\n", r->type[0] ? r->type : "", r->type[0] ? "." : "", r->name, (int)(long long int)r->any, r->size, r->info ); + else if( r->internal_type == REFLECT_TYPE_FIELD ) + fprintf(fp, "\t%s%s%s %s.%s; // %d@%d %s\n", r->is_pod ? "field " : "struct ", r->type, r->is_ptr ? "*" : "", r->base, r->name, r->size, (int)(long long int)r->any, r->info ); + } + + fprintf( fp, "} %s\n", __FUNCTION__ ); +} + +#endif + +// ---------------------------------------------------------------------------- + +#ifdef META_DEMO_IMMEDIATE +#include +#include +#include +#include + +void echo( const reflect *r, void *value, void *userdata ) { + FILE *fp = (FILE*)userdata; + if( fp ) { + /**/ if( !strcmp(r->type, "int") ) fprintf(fp, "%6s %-32s = %d;\t// %s\n", r->type, r->name, *(int*)value, r->info); + else if( !strcmp(r->type, "string") ) fprintf(fp, "%6s %-32s = %s;\t// %s\n", r->type, r->name, *(char**)value, r->info); + else if( !strcmp(r->type, "float") ) fprintf(fp, "%6s %-32s = %f;\t// %s\n", r->type, r->name, *(float*)value, r->info); + else if( !strcmp(r->type, "double") ) fprintf(fp, "%6s %-32s = %f;\t// %s\n", r->type, r->name, *(double*)value, r->info); + } +} + +enum MY_ENUM { + TEXTURE = 101, + IMAGE = 102, +}; + +typedef char* string; + +typedef struct MyVec3 { + double x,y,z; +} MyVec3; + +typedef struct MyTransform { + MyVec3 location; + MyVec3 rotation; + float scale; +} MyTransform; + +typedef struct MyObject { + struct MyObject *parent; + string id; + int hash; + MyTransform transform; +} MyObject; + +int MyAddFunction( int a,int b ) { + return a + b; +} + + +int main( int argc, char **argv ) { + + // # enums + // - register + // - reflect + + ENUM( MY_ENUM, TEXTURE ); + ENUM( MY_ENUM, IMAGE ); + + assert( 101 == reflect_enum("TEXTURE") ); + assert( 102 == reflect_enum("IMAGE") ); + + // # functions + // - register + // - find + // - call + // - try call (undefined functions are also safe to call) + + FUNCTION( int(int a, int b), MyAddFunction, "Function adding two numbers" ); + FUNCTION( int(const char *fmt, ...), printf, "Print text to console" ); + + int (*add_hook)() = reflect_function("MyAddFunction"); + int (*print_hook)(const char *, ...) = reflect_function("printf"); + + assert( 123 == add_hook(100,23) ); + print_hook("hello from reflected function %d\n", 123); + + print_hook = reflect_function("undefined_symbol_here$(·!!"); + print_hook("this call should never print\n"); + puts("---"); + + // # structs + // - register simple + // - register nested + // - register nested with pointers + // - print struct sizes + // - iterate simple + // - iterate nested + // - iterate nested with pointers + + STRUCT( MyVec3, double, x, "Right" ); + STRUCT( MyVec3, double, y, "Forward" ); + STRUCT( MyVec3, double, z, "Up" ); + + STRUCT( MyTransform, MyVec3, location, "World location (absolute)" ); + STRUCT( MyTransform, MyVec3, rotation, "Local rotation (in degrees)" ); + STRUCT( MyTransform, float, scale, "Local scale (in centimeters)" ); + + STRUCT( MyObject, int, hash, "Actor hash" ); + STRUCT( MyObject, string, id, "Actor name" ); + STRUCT( MyObject, MyTransform, transform, "Actor transform" ); + STRUCT( MyObject, MyObject *, parent, "Actor parent" ); + + printf("reflect_sizeof(MyVec3)=%d\n", reflect_sizeof("MyVec3")); + printf("reflect_sizeof(MyTransform)=%d\n", reflect_sizeof("MyTransform")); + printf("reflect_sizeof(MyObject)=%d\n", reflect_sizeof("MyObject")); + puts("---"); + + MyVec3 vec = {1.1,2.2,3.3}; + reflect_iterate_fields( "MyVec3", &vec, echo, stdout ); + puts("---"); + + MyTransform tf = { {1.1,2.2,3.3}, {45,90,180}, 10 }; + reflect_iterate_fields( "MyTransform", &tf, echo, stdout ); + puts("---"); + + MyObject root = { NULL, "Scene root", 0, { {0,0,0}, {0,0,0}, 1 } }; + MyObject obj = { &root, "Name identifier", 123, { {1.1,2.2,3.3}, {45,90,180}, 10 } }; + reflect_iterate_fields( "MyObject", &obj, echo, stdout ); + puts("---"); + + // # dump internals + + reflect_dump_registry(stdout); + puts("---"); + + // # benchmark + + #ifndef N + #define N (argc > 1 ? atoi(argv[1]) : 500 * 1000) + #endif + + clock_t t0 = clock(); + for( int i = 0; i < N; ++i) { + reflect_iterate_fields( "MyObject", &obj, echo, NULL ); + } + clock_t t1 = clock(); + + double t = (t1 - t0) / (double)CLOCKS_PER_SEC; + printf("Benchmark: processed %.f members in %5.2fs = %5.2f members/sec\n", N*9.0, t, (N*9.0)/t ); // 9 total members in MyObject +} + +#endif + diff --git a/tools/editor/labs.meta/meta_tool.c b/tools/editor/labs.meta/meta_tool.c new file mode 100644 index 0000000..a381339 --- /dev/null +++ b/tools/editor/labs.meta/meta_tool.c @@ -0,0 +1,46 @@ +#define FWK_C +#include "fwk.h" + +bool parse_struct(const char *line) { + return strstr(line, "s""truct "); +} +bool parse_union(const char *line) { + return strstr(line, "u""nion"); +} +bool parse_enum(const char *line) { + return strstr(line, "e""num "); +} +bool parse_typedef(const char *line) { + return strstr(line, "t""ypedef "); +} +bool parse_function(const char *line) { + return strstr(line, "(") && strstr(line, ");"); +} +bool parse_comment(const char *line) { + return strstr(line, "//") || (strstr(line, "/*") && strstr(line, "*/")); +} +bool parse_variable(const char *line) { + return strstr(line, "="); +} +bool parse_member(const char *line) { + return strstr(line, ";"); +} +const char* parse_any(const char *line) { + if(parse_struct(line)) return "STRUCT"; + if(parse_union(line)) return "UNION"; + if(parse_enum(line)) return "ENUM"; + if(parse_typedef(line)) return "TYPEDEF"; + if(parse_variable(line)) return "VARIABLE"; + if(parse_function(line)) return "FUNCTION"; + if(parse_member(line)) return "MEMBER"; + return 0; +} + +int main() { + char *data = file_read(__FILE__); + + for each_substring(data, "\r\n", line) { + const char *type = parse_any(line); + printf("%s%s%s\n", line, type ? " -> " : "", type ? type : ""); + } +} diff --git a/tools/editor/labs.osc/MAKE.bat b/tools/editor/labs.osc/MAKE.bat new file mode 100644 index 0000000..c7ffcd0 --- /dev/null +++ b/tools/editor/labs.osc/MAKE.bat @@ -0,0 +1,42 @@ +@if "%1" == "tidy" ( + del *.zip + del *.mem + del *.exp + del *.lib + del *.exe + del *.obj + del *.o + del *.a + del *.pdb + del *.ilk + del *.def + del *.dll + del oscedit.ini + rd /q /s .vs + exit /b +) + +cl ..\editor2.c -I ..\..\tools -DCOOK_ON_DEMAND + +pushd ..\.. && call make amalgamation && popd + +taskkill /im "oscedit.exe" > nul 2> nul +call ..\..\tools\tcc oscgame.c -I ..\.. -DFWK_IMPLEMENTATION -DCOOK_ON_DEMAND %* +call ..\..\tools\tcc oscsend.c -I ..\.. -DFWK_IMPLEMENTATION -DCOOK_ON_DEMAND %* +call ..\..\tools\tcc oscedit.c -I ..\.. -DFWK_IMPLEMENTATION -DCOOK_ON_DEMAND %* && start oscedit.exe + +timeout 3 + +:: showcase UI widgets creation on demand, which are requested from this very same batch file +oscsend /player/time 5.5 +oscsend /player/name "john doe" +oscsend /player/health 100 +oscsend /player/is_active true + +oscsend /player2/integer 123 +oscsend /player2/string world + +oscsend /player1/string hello + +:: simulate a game running sending OSC commands to our editor +rem oscgame diff --git a/tools/editor/labs.osc/oscedit.c b/tools/editor/labs.osc/oscedit.c new file mode 100644 index 0000000..774d222 --- /dev/null +++ b/tools/editor/labs.osc/oscedit.c @@ -0,0 +1,22 @@ +#include "fwk.h" +#include "oscedit.h" + +// demo + +int main() { + do_once map_init(client_vars, less_str, hash_str); + do_once map_init(server_vars, less_str, hash_str); + + int server_socket = osc_listen("0.0.0.0", OSC_EDIT_PORT); + if( server_socket >= 0 ) { + if( !strcmp("--launch", argv(1)) ) system("oscgame"); // launch game + + cook_config("../../tools/cook.ini"); + + window_create(0.80, WINDOW_SQUARE); + window_title("EDITOR"); + while( window_swap() ) { + osc_edit_sync(server_socket, -1, 4); + } + } +} diff --git a/tools/editor/labs.osc/oscedit.h b/tools/editor/labs.osc/oscedit.h new file mode 100644 index 0000000..e96da08 --- /dev/null +++ b/tools/editor/labs.osc/oscedit.h @@ -0,0 +1,221 @@ +// public api + +int osc_edit(const char *hierarchy_descriptor, char type, void *ptr); // descriptor format: [ifsTF]/path/name +void osc_edit_sync(int server_fd, int client_fd, unsigned timeout_ms); + +int osc_edit_load(const char *mask); +int osc_edit_save(const char *mask); +int osc_edit_reset(const char *mask); + +#define OSC_EDIT_PORT "2023" // maybe use portname("OSC_EDITOR_V1", 000) ? + +// --- impl + +#pragma once +#include "oscpack.h" +#include "oscsend.h" +#include "oscrecv.h" + +#ifndef OSC_EDIT_INI +#define OSC_EDIT_INI "oscedit.ini" +#endif + +typedef struct osc_variant { + char *key; // key address + char type; // variant type + union osc_variant_ { + int64_t i; + char *s; + double f; + uintptr_t up; + } live[1], offline[1]; + bool edited; +} osc_variant; + +map(char*, void*) client_vars; +array(char*) client_outgoing; + +map(char*, osc_variant) server_vars; +array(char*) server_outgoing; + +int osc_edit(const char *hierarchy_descriptor, char type, void *ptr) { + *((void**)map_find_or_add(client_vars, (char*)hierarchy_descriptor, ptr)) = ptr; + array_push(client_outgoing, stringf("%c%s", type, hierarchy_descriptor)); // @leak + return 0; +} + +int osc_edit_load(const char *mask) { + ini_t map = ini(OSC_EDIT_INI); + if( !map ) return 0; + + map_clear(server_vars); // @todo: @leak: iterate variant strings and free() them + + for each_map_ptr_sorted(map, char *, key, char *, val) { + if( strmatchi(*key + 2, mask ) ) { // skip initial char_type+dot + printf("%s=%s\n", *key, *val); + + osc_variant variant = {0}; + variant.type = 0[*key]; + + /**/ if( variant.type == 'i' ) variant.live[0].i = atoi(*val); + else if( variant.type == 'f' ) variant.live[0].f = atof(*val); + else if( variant.type == 's' ) variant.live[0].s = STRDUP(*val); + else if( strchr("bTF",variant.type) ) variant.live[0].i = (*val)[3] == 't'; + + variant.key = STRDUP(*key + 2); + variant.offline[0] = variant.live[0]; + + map_insert(server_vars, STRDUP(*key + 2), variant); // @todo: no need to STRDUP() here. we got variant.key already allocated. + } + } + + map_free(map); + + ui_notify("Loaded.", NULL); + return 1; +} + +int osc_edit_save(const char *mask) { + unlink( OSC_EDIT_INI ); + + for each_map_ptr(server_vars, char *, title, osc_variant, msg) { + if( strmatchi(*title, mask) ) { + msg->edited = false; + + if( msg->type == 's' ) msg->offline[0].s = STRDUP(msg->live[0].s); // @leak + else memcpy( &msg->offline[0], &msg->live[0], sizeof(msg->offline[0]) ); + + if( msg->type == 'i' ) ini_write(OSC_EDIT_INI, "i", msg->key, va("%d", (int)msg->live[0].i )); + if( msg->type == 'f' ) ini_write(OSC_EDIT_INI, "f", msg->key, va("%f", (float)msg->live[0].f )); + if( msg->type == 's' ) ini_write(OSC_EDIT_INI, "s", msg->key, va("%s", msg->live[0].s )); + if( strchr("bTF",msg->type) ) ini_write(OSC_EDIT_INI, "b", msg->key, va("%s", msg->live[0].i ? "true":"false" )); + } + } + + ui_notify("Saved.", NULL); + return 1; +} + +int osc_edit_reset(const char *mask) { + for each_map_ptr(server_vars, char *, title, osc_variant, msg) { + if( strmatchi(*title, mask) ) { + msg->edited = false; + + if( msg->type == 's' ) msg->live[0].s = STRDUP(msg->offline[0].s); // @leak + else memcpy( &msg->live[0], &msg->offline[0], sizeof(msg->live[0]) ); + } + } + return 1; +} + +void osc_edit_sync(int server_fd, int client_fd, unsigned timeout_ms) { + // client logic + if( client_fd >= 0 ) { + // 1. send outgoing vars (widget requests at server) + // 2. recv modified vars (from user tweaking widgets), wait till timeout_ms + // received vars are removed from outgoing queue, so it's never submitted again until it gets modified. + + // [1] + for(int i = 0; i < array_count(client_outgoing); ++i) { + char *out = 0; + /**/ if( client_outgoing[i][0] == 'i' ) out = osc_pack_va(client_outgoing[i]+1, "i", *(int*)*((void**)map_find(client_vars, client_outgoing[i]+1)) ); + else if( client_outgoing[i][0] == 'f' ) out = osc_pack_va(client_outgoing[i]+1, "f", *(float*)*((void**)map_find(client_vars, client_outgoing[i]+1)) ); + else if( client_outgoing[i][0] == 's' ) out = osc_pack_va(client_outgoing[i]+1, "s", *(char**)*((void**)map_find(client_vars, client_outgoing[i]+1)) ); + else if( client_outgoing[i][0] == 'b' ) out = osc_pack_va(client_outgoing[i]+1, *(bool*)*((void**)map_find(client_vars, client_outgoing[i]+1)) ? "T":"F" ); + else if( client_outgoing[i][0] == 'T' ) out = osc_pack_va(client_outgoing[i]+1, "T" ); + else if( client_outgoing[i][0] == 'F' ) out = osc_pack_va(client_outgoing[i]+1, "F" ); + else if( client_outgoing[i][0] == '\0') out = osc_pack_va(client_outgoing[i]+1, "" ); + else ASSERT(0, "unsupported osc_edit command `%c`", client_outgoing[i][0]); + if( out ) osc_send(client_fd, out+4, *(int*)out); + } + + for(int i = 0; i < array_count(client_outgoing); ++i) { + FREE(client_outgoing[i]); + } + + array_clear(client_outgoing); + + // [2] + sleep_ms(timeout_ms); + } + + // server logic + if( server_fd >= 0 ) { + // 1. recv vars from client + // 2. if they do not exist: instantiate UI controls for them + // 3. if they do exist: update values from client + // 4. when UI controls are modified, update our local copies and send its values back to client + + osc_update(server_fd, timeout_ms); // call every frame. reads the udp port and parse all messages found there + + const osc_message *begin; + for( int it = 0, end = osc_list(&begin); it < end; ++it ) { + const osc_message *msg = begin + it; + + osc_variant oscv, zero = {0}; + oscv.key = STRDUP(msg->pattern); + oscv.type = msg->types[0]; + if(oscv.type == 's') oscv.live[0].s = STRDUP(msg->v[0].s), oscv.offline[0].s = STRDUP(msg->v[0].s); + else memcpy(&oscv.live[0], &msg->v[0], sizeof(oscv.live[0])), memcpy(&oscv.offline[0], &msg->v[0], sizeof(oscv.live[0])); + + *map_find_or_add_allocated_key(server_vars, STRDUP(msg->pattern), zero) = oscv; + } + + static int on = 1; + if( ui_window("editor", &on) ) { + + int num_messages = osc_list(&begin); + int prev_title_len = 0; char *prev_title = 0; + + if( !map_count(server_vars) ) { + // create section header + int choice = ui_label2_toolbar("New", ICON_MD_LOOP); + if( choice == 1 ) osc_edit_load("*"); + } + else + for each_map_ptr_sorted(server_vars, char *, title, osc_variant, msg) { + + char *second_slash = strchr(*title + 1, '/'); + int title_len = (int)(second_slash - *title) - 1; // -> /player/title -> player + int different_size = title_len != prev_title_len; + int new_section = different_size || (prev_title && strncmp(prev_title, *title, title_len+1)); + + // create section header + if( new_section ) { + if( prev_title ) ui_separator(); + char *caption = va("*%.*s", title_len + 2, *title); + int choice = ui_label2_toolbar(caption, prev_title ? "" : va(">%d" ICON_MD_EMAIL " " ICON_MD_UNDO " " ICON_MD_LOOP " " ICON_MD_SD_CARD, num_messages)); // ICON_MD_UNDO " " ICON_MD_UPLOAD " " ICON_MD_DOWNLOAD + if( choice == 3 ) osc_edit_reset("*"); + if( choice == 2 ) osc_edit_load("*"); + if( choice == 1 ) osc_edit_save("*"); + prev_title_len = title_len, prev_title = va("%s", *title); + } else { + prev_title_len = title_len; + } + + ui_label_icon_highlight = !!msg->edited; +// vec2 ui_label_icon_clicked_L; // left +// vec2 ui_label_icon_clicked_R; // right + + char *title_copy = va(ICON_MD_UNDO " " "%s", *title + 1 + title_len + 1); // /player/titlexx -> UNDO_ICON titlexx + + // create gui elements + /**/ if( msg->type == '\0') { ui_label(title_copy); } + else if( msg->type == 'i' ) { int i = (int)msg->live[0].i; msg->edited |= ui_int(title_copy, &i); msg->live[0].i = i; } + else if( msg->type == 'f' ) { float f = msg->live[0].f; msg->edited |= ui_float(title_copy, &f); msg->live[0].f = f; } + else if( msg->type == 's' ) { char s[128] = {0}; strncpy(s, msg->live[0].s, sizeof(s)); uint64_t old = hash_str(s); ui_buffer(title_copy, s, sizeof(s)); uint64_t mod = hash_str(s); if(mod != old) msg->edited |= 1, FREE(msg->live[0].s), msg->live[0].s = stringf("%s",s); } + else if( strchr("bTF",msg->type) ) { bool b = !!msg->live[0].i; msg->edited |= ui_bool(title_copy, &b); msg->live[0].i = b; } + else ASSERT(0, "unsupported osc_edit command `%c`", msg->type); + + if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on UNDO icon (1st icon) + osc_edit_reset(*title); + } + } + + ui_window_end(); + } + + ui_demo(); + } +} + diff --git a/tools/editor/labs.osc/oscgame.c b/tools/editor/labs.osc/oscgame.c new file mode 100644 index 0000000..a8698ac --- /dev/null +++ b/tools/editor/labs.osc/oscgame.c @@ -0,0 +1,39 @@ +#include "fwk.h" +#include "oscedit.h" + +// game + +struct player { + char *name; + int health; + bool visible; + float time; +}; + +void render_game(struct player *p) { + printf("%*.s\rgame: %s health=%d time=%f visible=%d", 30, "", p->name, p->health, p->time, p->visible); +} + +// demo +int main() { + do_once map_init(client_vars, less_str, hash_str); + do_once map_init(server_vars, less_str, hash_str); + + int client_socket = osc_open("127.0.0.1", OSC_EDIT_PORT); + + if( client_socket >= 0 ) { + struct player P1 = { STRDUP("Player 1"), 100, true }; + + while( GetAsyncKeyState(VK_ESCAPE) & 0x8000 ^ 0x8000 ) { + P1.time = time_ss(); // << this is the "game", it just advances "time" + + osc_edit("/player/name", 's', &P1.name); + osc_edit("/player/health", 'i', &P1.health); + osc_edit("/player/time", 'f', &P1.time); + osc_edit("/player/visible", 'b', &P1.visible); + osc_edit_sync(-1, client_socket, 4); + + render_game(&P1); + } + } +} diff --git a/tools/editor/labs.osc/osclab1.c b/tools/editor/labs.osc/osclab1.c new file mode 100644 index 0000000..d4b76fb --- /dev/null +++ b/tools/editor/labs.osc/osclab1.c @@ -0,0 +1,74 @@ +#include "fwk.h" + +#define OSCPACK_C +#define OSCRECV_C +#define OSCSEND_C +#include "oscpack.h" +#include "oscrecv.h" +#include "oscsend.h" + +// networked gui, public api +void ui_netconfig(unsigned port); +void ui_netupdate(); + +// networked gui, private api +static int ui_netsocket = -1, ui_netclient, ui_netserver; +static map(char*,int) ui_netvalues_i; + +void ui_netconfig(unsigned port) { + do_once map_init(ui_netvalues_i, less_str, hash_str); + + if( ui_netsocket < 0 ) { + ui_netclient = !(tcp_bind("0.0.0.0",va("%d",port+1), 1) >= 0); + ui_netserver = !ui_netclient; + ui_netsocket = ui_netclient ? osc_open("127.0.0.1", va("%d",port)) : osc_listen("0.0.0.0", va("%d",port)); + if( ui_netsocket >= 0 ) ui_notify("UI Network config", va("Connected (%s).", ui_netclient ? "client (editor)" : "server (game)")); + if( ui_netsocket >= 0 ) window_title(ui_netclient ? "UI client (editor)" : "UI server (game)"); + if( ui_netsocket < 0 ) ui_netclient = ui_netserver = 0; + } +} +void ui_netupdate() { + if( ui_netserver ) { + // map_clear(ui_netvalues_i); + osc_update(ui_netsocket, 16); + } + if( ui_netclient ) { + for each_map_ptr(ui_netvalues_i, char *, name, int, value) { + char msg[4096]; + int msglen = osc_pack(msg, va("/%s", *name), "i", *value); + osc_send(ui_netsocket, msg + 4, msglen - 4); + } + // map_clear(ui_netvalues_i); + } +} + +int ui_netint(const char *name, int *value) { + const osc_message *found = osc_find(va("/%s", name)); // search in reverse order, so newest wins + if( found ) *value = (int)found->v[0].i; + + int changed = ui_int(name, value); + if( changed ) *map_find_or_add(ui_netvalues_i, (char*)name, *value) = *value; + + return changed; +} + +// demo +int main() { + window_create(0.66, WINDOW_SQUARE); + + ui_netconfig(1234); + + while( window_swap() ) { + + ui_netupdate(); + + static int r = 0, g = 0, b = 0; + if( ui_panel("test1", 0) ) { + ui_netint("Color R", &r); r = clampi(r, 0, 255); + ui_netint("Color G", &g); g = clampi(g, 0, 255); + ui_netint("Color B", &b); b = clampi(b, 0, 255); + ui_panel_end(); + } + viewport_color3(vec3(r/255.0,g/255.0,b/255.0)); + } +} diff --git a/tools/editor/labs.osc/osclab2.c b/tools/editor/labs.osc/osclab2.c new file mode 100644 index 0000000..0ebbe0c --- /dev/null +++ b/tools/editor/labs.osc/osclab2.c @@ -0,0 +1,106 @@ +// networked gui demo +// - rlyeh, public domain + +#include "fwk.h" + +#define OSCPACK_C +#define OSCRECV_C +#define OSCSEND_C +#include "oscpack.h" +#include "oscrecv.h" +#include "oscsend.h" + + + +int main() { + // window (80% sized, MSAA x2 flag) + window_create(80, WINDOW_MSAA2); + window_title(__FILE__); + + // scene loading + #define SCENE(...) #__VA_ARGS__ + const char *my_scene = SCENE([ + { + skybox: 'cubemaps/stardust/', + }, + { + position:[-5.0,-2.0,2.0], + rotation: [90.0,0.0,180.0], + scale:0.20, + mesh:'models/witch/witch.obj', + texture:'models/witch/witch_diffuse.tga.png', + flipuv:false, + }, + { + position:[-5.0,-2.0,2.0], + rotation: [90.0,0.0,180.0], + scale:0.20, + mesh:'models/witch/witch_object.obj', + texture:'models/witch/witch_object_diffuse.tga.png', + flipuv:false, + }, + ]); + int num_spawned = scene_merge(my_scene); + object_t *obj1 = scene_index(0); + object_t *obj2 = scene_index(1); + + // camera + camera_t cam = camera(); + cam.speed = 0.2f; + + // demo loop + while (window_swap()) + { + // input + if( input_down(KEY_ESC) ) break; + + // fps camera + bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); + window_cursor( !active ); + + if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f); + vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active); + vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed); + camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z); + camera_fps(&cam, mouse.x,mouse.y); + + // queue model scale bounces + float t = fmod(window_time(), 0.3) / 0.3; + float s = 0.01f * ease_ping_pong(t, ease_in_cubic,ease_out_cubic); + object_scale(obj1, vec3(0.20f - s,0.20f + s,0.20f - s)); + object_scale(obj2, vec3(0.20f - s,0.20f + s,0.20f - s)); + + // flush render scene (background objects: skybox) + profile("Scene background") { + scene_render(SCENE_BACKGROUND); + } + + // queue debug drawcalls + profile("Debugdraw") { + ddraw_grid(0); + ddraw_color(YELLOW); + ddraw_text(vec3(+1,+1,-1), 0.04f, va("(%f,%f,%f)", cam.position.x,cam.position.y,cam.position.z)); + ddraw_color(YELLOW); + ddraw_flush(); + } + + // render scene (foreground objects) with post-effects + profile("Scene foreground") { + int scene_flags = 0; + scene_render(SCENE_FOREGROUND | scene_flags); + } + +#if 0 + do_once ui_netconfig(1234); + ui_netupdate(); + static int r = 0, g = 0, b = 0; + viewport_color3(vec3(r/255.0,g/255.0,b/255.0)); + if( ui_panel("net test1", 0) ) { + ui_netint("Color R", &r); r = clampi(r, 0, 255); + ui_netint("Color G", &g); g = clampi(g, 0, 255); + ui_netint("Color B", &b); b = clampi(b, 0, 255); + ui_panel_end(); + } +#endif + } +} diff --git a/tools/editor/labs.osc/oscpack.h b/tools/editor/labs.osc/oscpack.h new file mode 100644 index 0000000..a7fcf17 --- /dev/null +++ b/tools/editor/labs.osc/oscpack.h @@ -0,0 +1,162 @@ +// OSC buffer packing, +// - rlyeh, public domain. +// +// pack format: (i)nt, (h)int64, (t)ime, (f)loat, (s)tring, (S)ymbol, (c)har, (r)gba, (m)idi, +// (T)true, (N|F)nil+false, (I)nfinity, (b)lob, (d)ouble, @todo: ([)array. +// +// warning: osc_pack() generates OSC compliant messages, however, +// every osc_pack_va() call generates a 32-bit length prefix (in machine dependant order) + +API int osc_bundle( char *buf, uint64_t ts ); +API int osc_pack( char *buf, const char *addr, const char *fmt, ... ); +API char* osc_pack_va( const char *addr, const char *fmt, ... ); + + +#pragma once +/* +#ifdef _WIN32 +#include +#pragma comment(lib, "ws2_32") +#else +#include +#endif +*/ + +int osc__buffer_vl( char *buf, const char *fmt, va_list vl ) { + // if `buf` is NULL, just calc needed space + if( !buf ) { + int bytes = 0; + while( *fmt++ ) { + switch( fmt[-1] ) { + default: // bypass + break; case 'T': case 'F': case 'N': case 'I': bytes += 4; + break; case 'i': case 'c': case 'r': case 'm': bytes += 4; (void)va_arg(vl, uint32_t); + break; case 't': case 'h': bytes += 8; (void)va_arg(vl, uint64_t); + break; case 'd': bytes += 8; (void)va_arg(vl, double); + break; case 'f': bytes += 4; (void)va_arg(vl, double); + break; case 'b': bytes += va_arg(vl, uint32_t); (void)va_arg(vl, const char *); + break; case 's': case 'S': bytes += strlen( va_arg(vl, const char *) ) + 1; + } + } + return bytes; + } + + char *src = buf; + while( *fmt++ ) { + switch( fmt[-1] ) { + default: *buf++ = fmt[-1]; // bypass + break; case 'T': case 'F': case 'N': case 'I': + {} + break; case 'i': case 'c': case 'r': case 'm': + { uint32_t i = va_arg(vl, uint32_t); i = ntohl(i); memcpy(buf, &i, 4); buf += 4; } + break; case 't': case 'h': + { uint64_t l = va_arg(vl, uint64_t); l = ntohll(l); memcpy(buf, &l, 8); buf += 8; } + break; case 'd': + { union { double f; uint64_t i; } u; u.f = va_arg(vl, double); u.i = ntohll(u.i); memcpy(buf, &u.i, 8); buf += 8; } + break; case 'f': + { union { float f; uint32_t i; } u; u.f = (float)va_arg(vl, double); u.i = ntohl(u.i); memcpy(buf, &u.i, 4); buf += 4; } + break; case 'b': + { uint32_t l = va_arg(vl, uint32_t), ll = ntohl(l); memcpy(buf, &ll, 4); buf += 4; /*}*/ + /*{*/ const char *s = va_arg(vl, const char *); int32_t i = 0; + memcpy(buf, s, l); memcpy(buf + l, &i, 4); buf += l; buf += 3 - (((intptr_t)buf+3) & 3); } + break; case 's': case 'S': + { const char *s = va_arg(vl, const char *); int32_t i = 0, l = (int32_t)strlen(s) + 1; + memcpy(buf, s, l); memcpy(buf + l, &i, 4); buf += l; buf += 3 - (((intptr_t)buf+3) & 3); } + } + } + return buf - src; +} + +int osc__buffer( char *buf, const char *fmt, ... ) { + va_list vl; + va_start(vl, fmt); + int l = osc__buffer_vl(buf, fmt, vl); + va_end(vl); + return l; +} + +int osc_pack( char *buf, const char *addr, const char *fmt, ... ) { + char tmp[8192]; + va_list vl; + va_start(vl, fmt); + int l2 = osc__buffer_vl(tmp, fmt, vl); + va_end(vl); + int l1 = osc__buffer(buf, "s,s", addr, fmt); + memcpy(buf+l1, tmp, l2); + return l1+l2; +} + +char* osc_pack_va( const char *addr, const char *fmt, ... ) { // @todo: optimize me + char buf[1024]; + + char tmp[8192]; + va_list vl; + va_start(vl, fmt); + int l2 = osc__buffer_vl(tmp, fmt, vl); + va_end(vl); + int l1 = osc__buffer(buf, "s,s", addr, fmt); + memcpy(buf+l1, tmp, l2); + int total = l1+l2; + + char *out = va("%*.s", 4+total, ""); + memcpy(out, &total, 4); + memcpy(out+4, buf, total); + return out; +} + +int osc_bundle( char *buf, uint64_t ts ) { + return osc__buffer( buf, "sh", "#bundle", ts ); +} + +#ifdef OSCPACK_DEMO + +int main() { + // OSC message + { + char buf[4096]; + int l = osc_pack(buf, "/foo", "iisff", 1000, -1, "hello", 1.234f, 5.678f); + //use as: udp_send(socket, buf+4, l-4); + hexdump(buf, l); + + assert( 0 == memcmp( buf, + "\x2f\x66\x6f\x6f\x00\x00\x00\x00\x2c\x69\x69\x73\x66\x66\x00\x00" + "\x00\x00\x03\xe8\xff\xff\xff\xff\x68\x65\x6c\x6c\x6f\x00\x00\x00" + "\x3f\x9d\xf3\xb6\x40\xb5\xb2\x2d", l-4)); + } + + // OSC message (w/ initial 4-bytes payload) + { + char *buf = osc_pack_va("/foo", "iisff", 1000, -1, "hello", 1.234f, 5.678f); + int l = *(int*)buf; + printf("---------------%x\n", l); + hexdump(buf, l); + + assert( 0 == memcmp( buf+4, //"\x28\x00\x00\x00" + "\x2f\x66\x6f\x6f\x00\x00\x00\x00\x2c\x69\x69\x73\x66\x66\x00\x00" + "\x00\x00\x03\xe8\xff\xff\xff\xff\x68\x65\x6c\x6c\x6f\x00\x00\x00" + "\x3f\x9d\xf3\xb6\x40\xb5\xb2\x2d", l-4)); + } + + // OSC bundle + { + // OSC bundle test taken from Julien Pommier's oscpkt.hh + // wr.startBundle(); + // wr.addMessage("/foo").pushInt32(1000,-1).pushStr("hello").pushFloat(1.234f,5.678f); + // wr.endBundle(); + + char buf[4096]; + int h = osc_bundle(buf, 1ULL); + int m = osc_pack(buf+h, "/foo", "iisff", 1000, -1, "hello", 1.234f, 5.678f); + + int l = h+m; + hexdump(buf, h+m); + + assert( l == 0x3c-4 ); + assert( 0 == memcmp( buf, + "\x23\x62\x75\x6e\x64\x6c\x65\x00\x00\x00\x00\x00\x00\x00\x00\x01" + "\x2f\x66\x6f\x6f\x00\x00\x00\x00\x2c\x69\x69\x73" + "\x66\x66\x00\x00\x00\x00\x03\xe8\xff\xff\xff\xff\x68\x65\x6c\x6c" + "\x6f\x00\x00\x00\x3f\x9d\xf3\xb6\x40\xb5\xb2\x2d", l) ); + } +} +#endif diff --git a/tools/editor/labs.osc/oscrecv.h b/tools/editor/labs.osc/oscrecv.h new file mode 100644 index 0000000..08771b5 --- /dev/null +++ b/tools/editor/labs.osc/oscrecv.h @@ -0,0 +1,299 @@ +// simple osc server. designed for easy integration with a gameloop / immediate mode style. +// - rlyeh, public domain. forked from original code by @mmalex (public domain). +// +// @todo: add support for // wildcard +// @todo: add support for [ array parameters ] + +API int osc_listen( const char *mask, const char *port ); // creates a listening socket +API int osc_update(int s, int timeout_ms); // call every frame. reads the udp port and parse all messages found there +API int osc_list(const struct osc_message **first); // returns number of received messages, also set arg pointer to first item +API int osc_debug( FILE *out, const char *inmsg, int len ); // debugs raw osc buffer to stream +API const struct osc_message *osc_find(const char *addr); // finds most recent message matching 'addr' + +// OSC types from the spec. + +enum { + OSC_INT = 'i', + OSC_FLOAT = 'f', + OSC_STRING = 's', + OSC_SYMBOL = 'S', + OSC_BLOB = 'b', + OSC_INT64 = 'h', + OSC_TIME = 't', + OSC_DOUBLE = 'd', + OSC_CHAR = 'c', + OSC_RGBA = 'r', + OSC_MIDI = 'm', + OSC_TRUE = 'T', + OSC_FALSE = 'F', + OSC_NIL = 'N', + OSC_INFINITY = 'I', + // OSC_ARRAY = '[', // @todo +}; + +// OSC message + +typedef struct osc_message { + const char *pattern;// address in osc message + const char *types; // string of characters taken from the OSC types enum + const char *data; // pointer to raw osc data. for debugging purposes only (?) +#if 0 + int64_t i[8]; // integer interpretation of first 8 params (for blobs & strings, is length) + const char *s[8]; // for blobs and strings + float f[8]; // floating point interpretation of first 8 params +#else + union variant { + int64_t i; + const char *s; + double f; + uintptr_t up; + } v[8]; +#endif +} osc_message; + + +#pragma once + +enum { OSC_MAX_BUF = 8*1024*1024 }; // was: 65536 +enum { OSC_MAX_MESSAGES = 65536 }; // was: 1024 + +static int osc_match_(const char *pat, const char *addr) { + for (int n=0;*pat;addr++,pat++) switch (*pat) { + default: if (*pat!=*addr) return 0; break; + case '?': break; + case '[': n=(*++pat=='!'); for (pat+=n; *pat!=']' && *pat;++pat) { if (pat[1]=='-') { if (*addr>=*pat && *addr<=pat[2]) { n=!n; break; } pat+=pat[2] ? 2 : 1; } else if (*pat==*addr) { n=!n; break; } } + if (!n) return 0; while (*pat && *pat!=']') pat++; break; + case '{': n=0; for (const char *p=++pat; *p && *p!='}' && *p!='/'; pat=++p) { while (*p && *p!='}' && *p!='/' && *p!=',') p++; if (!strncmp(pat,addr,p-pat)) { addr+=p-pat; n=1; break; } } + while (*pat && *pat!='}') pat++; if (!n) return 0; break; + case '*': while (pat[1]=='*') pat++; n=0; if (pat[1]=='/' || pat[1]==0) { while (*addr && *addr!='/') ++addr; n=1; } else for (;*addr!='/' && *addr; ++addr) if (osc_match_(pat + 1, addr)) { n=1; break; } + if (!n) return 0; addr--; break; + // @todo: add // wildcard support + } + return *addr== 0; +} + +static float osc_asfloat_(uint32_t x) { union { float f; uint32_t i; } u; u.i=x; return u.f; } +static double osc_asdouble_(uint64_t x) { union { double f; uint64_t i; } u; u.i=x; return u.f; } + +static int osc_parse_i32_(const char **s, const char *e) { + if (*s+4>e) { *s=e; return 0; } + int rv=htonl(*(uint32_t*)*s); + *s+=4; + return rv; +} +static int64_t osc_parse_i64_(const char **s, const char *e) { + if (*s+8>e) { *s=e; return 0; } + int64_t rv=htonll(*(uint64_t*)*s); + *s+=8; + return rv; +} +static const char *osc_parse_str_(const char **s, const char *e) { + int len=(int)strlen(*s); + const char *rv=*s; + *s=rv+((len+4)&~3); + if (*s>e) *s=e; + return rv; +} +static const char *osc_parse_bin_(const char **s, const char *e, int *len) { + *len=osc_parse_i32_(s,e); + int maxlen=(int)(e-*s); + if (*len>maxlen) *len=maxlen; + if (*len<0) *len=0; + const char *rv=*s; + *s=rv+((*len+3)&~3); + if (*s>e) *s=e; + return rv; +} +static int osc_parse_(osc_message *out, int maxmsg, const char *s, const char *e) { + if (maxmsg<=0 || s>=e) return 0; + if (*(uint64_t*)s==*(uint64_t*)"#bundle\0") { // bundle is #bundle, uint64_t time, uint32_t length, + osc_parse_i64_(&s,e); // @todo: skipped time for now + int msgcount=0; + while (smaxlen) len=maxlen; + if (len<0) len=0; + int n=osc_parse_(out+msgcount, maxmsg-msgcount, s, s+len); + msgcount+=n; + s+=((len+3)&~3); + } + return msgcount; + } + // single message + memset(out,0,sizeof(osc_message)); + out->pattern=osc_parse_str_(&s,e); + if (!out->pattern) + return 0; + out->types=(*s==',')?osc_parse_str_(&s,e):",f"; + if (!out->types) + return 0; + out->types++; + out->data=s; + for (int param=0;param<8;++param) { + switch (out->types[param]) { + default: return 1; // done! + case OSC_CHAR: case OSC_RGBA: // all int32... + case OSC_MIDI: case OSC_INT: out->v[param].i=osc_parse_i32_(&s,e); break; + case OSC_TIME: case OSC_INT64: out->v[param].i=osc_parse_i64_(&s,e); break; + case OSC_STRING: case OSC_SYMBOL: out->v[param].s=osc_parse_str_(&s,e); /*out->v[param].i=strlen(out->v[param].s);*/ break; + case OSC_FLOAT: out->v[param].f=osc_asfloat_(osc_parse_i32_(&s,e)); break; + case OSC_DOUBLE: out->v[param].f=osc_asdouble_(osc_parse_i32_(&s,e)); break; + case OSC_BLOB: {int len=0; out->v[param].s=osc_parse_bin_(&s,e,&len); /*out->v[param].i=len;*/ break; } // @todo: important to signal len in variant somewhere + case OSC_INFINITY: out->v[param].f=INFINITY; break; + case OSC_TRUE: out->v[param].i=1; break; + case OSC_FALSE: case OSC_NIL: out->v[param].i=0; break; +// case OSC_ARRAY: @todo + } + } + return 1; +} + +static struct osc_message *msg = 0; //[OSC_MAX_MESSAGES]; +static int msgpos; + +int osc_listen( const char *mask, const char *port ) { + do_once udp_init(); + + int fd = swrapSocket(SWRAP_UDP,SWRAP_BIND,SWRAP_NOBLOCK,mask,port); + return fd; +} + +int osc_update(int fd, int timeout_ms /*= -1*/) { + do_once udp_init(); + + static char *buf = 0; // [OSC_MAX_BUF]; + static int bufpos; + + if( !buf ) { + buf = CALLOC( 1, OSC_MAX_BUF ); + msg = CALLOC( 1, OSC_MAX_MESSAGES * sizeof(struct osc_message) ); + } + if(fd<0) return 0; + + /* check if something is available */ + msgpos=0; + if( timeout_ms >= 0 ) { + int ret = swrapSelect(fd, timeout_ms / 1000.0); + if (ret <= 0) { // error, or timeout + return 0; + } + } + + for (msgpos=0,bufpos=0;msgpos0;) if (osc_match_(msg[i].pattern, addr)) return &msg[i]; + return 0; +} + +int osc_debug(FILE *fp, const char *buf, int len) { + osc_message m[16]; + int nn = osc_parse_(m, 16, buf, buf+len); + + for( int n = 0; n < nn; ++n ) { + fprintf(fp, "%s [%s]", m[n].pattern, m[n].types); + // @todo #bundle @%lld + + for(int i = 0; m[n].types[i]; ++i) { + char f = m[n].types[i]; + /**/ if (f == 'T' || f == 'F' || f == 'N') fprintf( fp, ",%s", (f=='T'?"True":f=='F'?"False":"Null")); + else if (f == 'h' || f == 't') fprintf( fp, ",%lld", m[n].v[i].i); + else if (f == 'f' || f == 'd') fprintf( fp, ",%f", m[n].v[i].f); + else if (f == 'i' || f == 'c' || f == 'r' || f == 'm' ) fprintf( fp, ",%d", (int)m[n].v[i].i); + else if (f == 's' || f == 'S' ) fprintf( fp, ",%s", m[n].v[i].s); + else if (f == 'b') fprintf( fp, ",%d bytes", (int)m[n].v[i].i); + else fprintf(fp, ",%s", "?"); + } + + fprintf(fp, "%s\n", ""); + } + return 1; +} + + +#ifdef OSCRECV_DEMO + +int main() { + // @mmalex's tests + assert( osc_match_("/[a-c]/?/[abc]/*/fish*/*food/f*/{foo,bar,baz}","/a/b/c//fishfood/monkeyfood/f/bar") ); + assert( !osc_match_("/[a-c]/?/[abc]/*/fish*/*food/f*/{fog,bar,baz}","/a/b/c//fishfood/monkeyfood/f/foo") ); + assert( !osc_match_("/[a-c]/?/[abc]/*/fith*/*food/f*/{foo,bar,baz}","/a/b/c//fishfood/monkeyfood/f/foo") ); + assert( !osc_match_("/[a-c]/?/[abc]/*/fish*/*good/f*/{foo,bar,baz}","/a/b/c//fishfood/monkeyfood/f/foo") ); + assert( !osc_match_("/[a-c]/?/[abc]/*/fish*/*food/g/{foo,bar,baz}","/a/b/c//fishfood/monkeyfood/f/foo") ); + assert( osc_match_("/[fa-cd]/?/[abc]/*/fish*/*food/f*/{foo,bar,baz}","/d/b/c//fishfood/monkeyfood/f/bar") ); + + // Julien Pommier's oscpkt tests + assert( !osc_match_("//bar", "bar")); +// assert( osc_match_("//bar", "/bar")); +// assert( osc_match_("//bar", "/foo/plop/bar")); +// assert( osc_match_("/foo//", "/foo/plop/df/")); +// assert( osc_match_("/foo///////bar", "/foo/plop/baz/bar")); + assert( osc_match_("*", "bar")); + assert( osc_match_("/foo/*", "/foo/bar")); +// assert( osc_match_("/{bar,fo}/b[aA]r", "/fo/bar")); +// assert( !osc_match_("/{bar,fo}/b[aA]r", "/foo/bar")); +// assert( osc_match_("/fo{bar,}/ba[e-t]", "/fo/bar")); + assert( !osc_match_("/fo{bar,}/ba[t-z]", "/fo/bar")); +// assert( osc_match_("/f{,ioio,bar}o/?a[!a]", "/fo/bar")); + assert( osc_match_("/foo/bar", "/foo/bar")); + assert( osc_match_("/f*o/bar", "/foo/bar")); + assert( osc_match_("/fo*o/bar", "/foo/bar")); +// assert( osc_match_("/*//bar", "/foo/bar")); + assert( osc_match_("/*/bar", "/foo/bar")); + assert( osc_match_("/*o/bar", "/foo/bar")); + assert( osc_match_("/*/*/*/*a***/*/*/*/*/", "/foo/bar/foo/barrrr/foo/bar/foo/barrrr/")); + assert( !osc_match_("/*/*/*/**/*/*/*/*/q", "/foo/bar/foo/barrrr/foo/bar/foo/barrrr/p")); + assert( osc_match_("[-]", "-")); +// assert( osc_match_("[a-]", "a")); +// assert( osc_match_("[a-]", "-")); + + int fd = osc_listen( "127.0.0.1", "9000" ); + if( fd ) for(;;) { + static unsigned char counter = 0; + printf("\r127.0.0.1:9000 %c", "\\|/-"[ counter = (counter+1) & 3 ]); + + Sleep(100); + osc_update(fd); + + const osc_message *begin; + for( int it = 0, end = osc_list(&begin); it < end; ++it ) { + const osc_message *msg = begin + it; + printf("> %s [%s]\n", msg->pattern, msg->types); + } + } +} + +#endif diff --git a/tools/editor/labs.osc/oscsend.c b/tools/editor/labs.osc/oscsend.c new file mode 100644 index 0000000..ede9bf6 --- /dev/null +++ b/tools/editor/labs.osc/oscsend.c @@ -0,0 +1,28 @@ +#include "fwk.h" +#include "oscsend.h" +#include "oscedit.h" +int main(int argc, char **argv) { + if( argc > 1 ) { + char *address = argv[1]; + char *arg = 0; strcatf(&arg, "%s", ""); for(int i = 2; i < argc; ++i) strcatf(&arg, "%s", argv[i]); + + char *message = 0; + if( !arg[0] ) message = osc_pack_va(address, ""); + else if( strchr("\'\"`", arg[0]) ) message = osc_pack_va(address, "s", (arg[strlen(arg)-1] = '\0', arg+1) ); + else if( !strcmp(arg, "true") || !strcmp(arg, "false") ) message = osc_pack_va(address, arg[0] == 't' ? "T" : "F"); + else if( strchr(".+-0123456789", arg[0]) && strpbrk(arg, ".fe") ) message = osc_pack_va(address, "f", atof(arg)); + else if( strchr( "+-0123456789", arg[0]) && strspn(arg+1, "0123456789") == strlen(arg)-1 ) message = osc_pack_va(address, "i", atoi(arg)); + else message = osc_pack_va(address, "s", arg ); + hexdump( message+4, *(int*)message ); + + int socket = osc_open("127.0.0.1", OSC_EDIT_PORT); + printf("%d bytes sent\n", osc_send( socket, message+4, *(int*)message )); + } else { + printf("%s /osc/address/ [argtype]\n", argv[0]); + printf("argtypes: [false][true][int][float][string]\n\n","\t"); + printf("example: %s /player/time 5.5\n", argv[0]); + printf("example: %s /player/health 100\n", argv[0]); + printf("example: %s /player/active true\n", argv[0]); + printf("example: %s /player/fullname \"john doe\"\n", argv[0]); + } +} diff --git a/tools/editor/labs.osc/oscsend.h b/tools/editor/labs.osc/oscsend.h new file mode 100644 index 0000000..7fcaeb8 --- /dev/null +++ b/tools/editor/labs.osc/oscsend.h @@ -0,0 +1,121 @@ +#pragma once +#define osc_open udp_open +#define osc_send udp_send +#define osc_close udp_close + +// rgb_formats: +// - 888 (RGB 3bytes/pixel), +// - 332 (RGB 1byte/pixel), +// - 242 (RGB 1byte/pixel), +// - 7755 (YCoCg 3bytes/2pixels) + +API void osc_send_fb( void *pixels, int w, int h, int comp, int rgb_format ); + + +// usage: +// void* rgb = render_capture(3); +// int rgbmode = 332; // 7755,242,332,888 safest+slowest +// osc_send_fb(rgb, window_width(), window_height(), 3, rgbmode); // 242 crash); 7755 green); + +//viewport_clear(false, true); +//ddraw_frame(NULL); +//ddraw_axis(10); + + +#pragma once +#include "oscpack.h" + +void osc_send_fb( void *pixels, int w, int h, int comp, int rgb_format ) { + static int s, *init = 0; + if( !init ) { init = &s; s = osc_open("127.0.0.1", "9000"); } + if( s <= 0 ) return; + + if( !pixels ) return; + if( (w * h * comp) <= 0 ) return; + + unsigned char* line_a = (unsigned char *)pixels; + unsigned char* line_b = (unsigned char *)pixels + (0 + (h - 1) * w) * comp; + unsigned char* line; + + static unsigned char activity = 0; + static unsigned num_sent_packets = 0; + + static char *oscbuf = 0; + if( !oscbuf ) oscbuf = (char*)MALLOC( 8192 * 4 + 64 ); // 8K RGBA32 max + some room for osc headers + + bool sending = 0; + int stride = w * comp; + + line = line_b + stride; + if( rgb_format == 888 ) for( int y = 0; y < h; ++y ) { line -= stride; + int osclen = osc_pack( oscbuf, "/render/", "iiiib", w,h,rgb_format, y, w*3,line); + bool sent = osc_send( s, oscbuf + 4, osclen - 4 ); + num_sent_packets += sent; sending |= sent; + } + if( rgb_format == 332 ) for( int y = 0; y < h; ++y ) { line -= stride; + for( int x = 0; x < w; ++x ) { + unsigned char r = line[x*3+0], g = line[x*3+1], b = line[x*3+2]; + line[x] = (( r >> 5 ) << 5) | (( g >> 5 ) << 2) | (( b >> 6 ) << 0); + } + int osclen = osc_pack( oscbuf, "/render/", "iiiib", w,h,rgb_format, y, w,line); + bool sent = osc_send( s, oscbuf + 4, osclen - 4 ); + num_sent_packets += sent; sending |= sent; + } + if( rgb_format == 242 ) for( int y = 0; y < h; ++y ) { line -= stride; + for( int x = 0; x < w; ++x ) { + unsigned char r = line[x*3+0], g = line[x*3+1], b = line[x*3+2]; + line[x] = (( r >> 6 ) << 6) | (( g >> 4 ) << 2) | (( b >> 6 ) << 0); + } + int osclen = osc_pack( oscbuf, "/render/", "iiiib", w,h,rgb_format, y, w,line); + bool sent = osc_send( s, oscbuf + 4, osclen - 4 ); + num_sent_packets += sent; sending |= sent; + } + if( rgb_format == 7755 ) for( int y = 0; y < h; ++y ) { line -= stride; + /**/ if( (w % 2) == 0 ) {} + else if( (w % 2) == 1 ) w -= 1; + assert( (w % 2) == 0 ); + uint8_t *out = line; + for( int x = 0, i = 0; i < w/2; ++i, ++x ) { + + // (/2>>1 /4>>2 /8>>3 /16>>4 /32>>5 /64>>6 /128>>7) + + // Y Y CoCg (7 7 5 5) : 3bytes == 2px !!wow!! + int32_t r,g,b,y0,y1,co0,co1,cg0,cg1; + r = line[x*6+0], g = line[x*6+1], b = line[x*6+2]; + y0 = r/4 + g/2 + b/4 + 1; // +1 to avoid some ycocg2rgb overflows + co0 = r/2 - b/2; + cg0 = g/2 - r/4 - b/4; + r = line[x*6+3], g = line[x*6+4], b = line[x*6+5]; + y1 = r/4 + g/2 + b/4 + 1; // +1 to avoid some ycocg2rgb overflows + co1 = r/2 - b/2; + cg1 = g/2 - r/4 - b/4; + int32_t co = (co0+co1)/2 + 128, cg = (cg0+cg1)/2 + 128; + // 7 7 5 5 + uint8_t y0_7 = y0 / 2, y1_7 = y1 / 2, co_5 = co / 8, cg_5 = cg / 8; + uint32_t pack24 = (y0_7<<17)|(y1_7<<10)|(co_5<<5)|(cg_5<<0); + *out++ = ( pack24 >> 16 ) & 255; + *out++ = ( pack24 >> 8 ) & 255; + *out++ = ( pack24 >> 0 ) & 255; + + // other packed options + // YCoCg (4 4 4) : 12bits == 1px + // YCoCg (2 3 3) : 1byte == 1px + // Y CoCg (8 4 4) : 2bytes == 1px + // Y Y CoCg (5 5 3 3) : 2bytes == 2px (meh) + // Y Y CoCg (8 8 4 4) : 3bytes == 2px + // Y Y CoCg (7 7 5 5) : 3bytes == 2px (wow!) + // Y Y Y CoCg (8 8 8 4 4) : 4bytes == 3px + // Y Y Y CoCg (7 7 7 5 6) : 4bytes == 3px + // Y Y Y CoCg (6 6 6 7 7) : 4bytes == 3px + // Y Y Y Y CoCg (7 7 7 7 6 6) : 5bytes == 4px + // Y Y Y Y CoCg (6 6 6 6 8 8) : 5bytes == 4px + } + int osclen = osc_pack( oscbuf, "/render/", "iiiib", w,h,rgb_format, y, w*2,line); + bool sent = osc_send( s, oscbuf + 4, osclen - 4 ); + num_sent_packets += sent; sending |= sent; + } + + activity += sending; + // printf("\r%c netsend %d", "\\|/-"[ activity % 4 ], num_sent_packets); + // stbi_write_png("out.png", w, h, 3, pixels, w * 3); +} diff --git a/tools/editor/labs.vm/ecs.c b/tools/editor/labs.vm/ecs.c new file mode 100644 index 0000000..e404065 --- /dev/null +++ b/tools/editor/labs.vm/ecs.c @@ -0,0 +1,425 @@ +// fast simple ecs +// - rlyeh, public domain +// +// features: +// - mostly heap allocation free +// - 2^64 systems max +// - 2^64 entities max +// +// cons: +// - 64 components max +// - memory requirements may be suboptimal. ie, using unions for the components: all components will equally size like the largest component. +// +// @note: +// - best perf when compiled with `/DNDEBUG /openmp /arch:AVX2 /Os /Ox /Gw /GL /MT` +// - runs 86M ops/sec on my old laptop: updates 50K entities out of 100K in 0.58ms/frame. 1000 frames in 0.57s +// +// @todo: +// - thread-safe +// - world/context management +// - dont use arrays: sequential access is killing performances when doing huge amount of entities (10M). worthy? +// compromise: use islands. 10K entities per island seems reasonable & best perf. the lower the better. +// we could parallelize islands as well (openmp? threads?). +// - file format spec: +// ; ecs data file format (.ini) +// [entity] +// components = mesh aabb +// position = 0 0 0 ; common +// rotation = 0 0 0 ; common +// scale = 2 2 2 ; common +// aabb.static = 1 +// aabb.size = 3 3 3 +// aabb.offset = 0 0 0 +// mesh.model = cube +// mesh.texture = wood_03 +// mesh.texture.tiling = 0.5 0.5 +// - file format parser: +// for each_map_sorted_ptr(ini("ecs.ini"), char *, k, char *, v) +// printf("'%s'='%s'\n", *k, *v += strspn(*v, " ")); +// + +/* api */ + +#ifdef _OPENMP +# ifdef _MSC_VER +# define parallel __pragma(omp parallel for) +# define has_parallel 1 +# else // __GNUC__ +# define parallel _Pragma("omp parallel for") // C99 +# define has_parallel 1 +# endif +#else +# define parallel +# define has_parallel 0 +#endif + +void ecs_max_components(int max_components); + +#define ecs_dump_world(fp, ...) ecs_dump_world(fp,ecs_mask64(__VA_ARGS__,-1)) + +#define ecs_add_entity(...) ecs_add_entity(ecs_mask64(__VA_ARGS__,-1)) + +union component_t* ecs_get_component( int eid, int cid ); +union component_t** ecs_get_components( int eid ); + +#define ecs_has_component(eid,...) ecs_has_component(eid,ecs_mask64(__VA_ARGS__,-1)) +#define ecs_add_component(eid,...) ecs_add_component(eid,ecs_mask64(__VA_ARGS__,-1)) +#define ecs_del_component(eid,...) ecs_del_component(eid,ecs_mask64(__VA_ARGS__,-1)) +#define ecs_use_component(eid,...) ecs_use_component(eid,ecs_mask64(__VA_ARGS__,-1)) +#define ecs_off_component(eid,...) ecs_off_component(eid,ecs_mask64(__VA_ARGS__,-1)) +#define each_ecs_component(obj, ...) \ + ( uint64_t sys_ = ecs_mask64(__VA_ARGS__,-1), ent_ = 0; ent_ < world.ne; ++ent_ ) \ + for( component_t **obj = sys_ == (sys_ & world.entities[ent_ * 2 + 0]) ? ecs_get_components(ent_) : 0; obj ; obj = 0 ) + +#if has_parallel +#undef each_ecs_component +#define each_ecs_component(obj, ...) \ + ( ent_ = (sys_ = ecs_mask64(__VA_ARGS__,-1), 0); ent_ < world.ne; ++ent_ ) \ + for( component_t **obj = sys_ == (sys_ & world.entities[ent_ * 2 + 0]) ? ecs_get_components(ent_) : 0; obj ; obj = 0 ) +static int64_t ent_; +static uint64_t sys_; +#endif + +/* internals */ + +typedef union component_t { + struct dummy { int dummy; }; + +#ifdef COMPONENT_HEADER +#include COMPONENT_HEADER +#endif +#ifdef COMPONENT_DATAS + COMPONENT_DATAS +#endif +#if defined ECS_DEMO || defined ECS_BENCH + struct position { float x,y,z; }; // c1 + struct velocity { float vx,vy,vz; }; // c2 + struct color { float r,g,b; }; // c3 + struct health { float health; }; // c4 + char *name; // c5 +#endif + +} component_t; + +struct world_t { + int ne, nc; // ne: number of entities, nc: number of components (stride) (cN) + array(uint64_t) entities; // vtable entities (2 entries/entity) [ e1(cflags,offset) e2(cflags,offset) .. eN(cflags,offset) ] + array(component_t) components; // instanced components (nc entries/entity) [ e1(c1,c2..) e2(c1,c2..) .. ] +}; + +uint64_t ecs_mask64( unsigned id1, ... ); +int (ecs_add_entity)( uint64_t component_mask ); +bool (ecs_has_component)( int eid, uint64_t flags ); +bool (ecs_add_component)( int eid, uint64_t flags ); +bool (ecs_del_component)( int eid, uint64_t flags ); +bool (ecs_use_component)( int eid, uint64_t flags ); +bool (ecs_off_component)( int eid, uint64_t flags ); +void (ecs_dump_world)( FILE *fp, uint64_t component_mask ); + +extern struct world_t world; + +// impl + +// static +struct world_t world = {0}; + +uint64_t ecs_mask64(unsigned id1, ... ) { + uint64_t flags = 0; + + // update flags and entities of components + va_list ap; + va_start(ap, id1); + for( uint64_t id = id1; id != ((unsigned)-1); id = va_arg(ap, unsigned) ) { + flags |= 1ull << id; + } + va_end(ap); + + return flags; +} + +int (ecs_add_entity)( uint64_t component_mask ) { + component_t c = {0}; + + // add mask+offset into entities + array_push(world.entities, component_mask); + array_push(world.entities, array_count(world.components)); + + // add components into entity + for (uint64_t cid = 0; cid < world.nc; ++cid) { + if( (1ull << cid) & component_mask ) { + array_push( world.components, c ); + } + } + + int eid = world.ne; + return world.ne++; +} + +void ecs_max_components(int max_comps) { + world.nc = max_comps; +} + +component_t* ecs_get_component( int eid, int target_cid ) { + uint64_t sys = world.entities[ eid * 2 + 0 ]; + uint64_t off = world.entities[ eid * 2 + 1 ]; + for( uint64_t cid = 0; cid < target_cid; ++cid ) { + off += !!((1ull << cid) & sys); + } + bool has_cid = !!((1ull << target_cid) & sys); + return (component_t*)(has_cid * (uintptr_t)(&world.components[ off ])); +} + +component_t** ecs_get_components( int eid ) { + static __thread component_t* local[8][64] = {0}; + static __thread int counter = 0; counter = (counter + 1) % 8; + + uint64_t sys = world.entities[ eid * 2 + 0 ]; + uint64_t off = world.entities[ eid * 2 + 1 ]; + for( uint64_t cid = 0, idx = 0; cid < world.nc; ++cid ) { + bool has_cid = !!((1ull << cid) & sys); + local[counter][ cid ] = (component_t*)(has_cid * (uintptr_t)(&world.components[ off ] + idx)); + idx += has_cid; + } + + return local[counter]; +} + +bool (ecs_has_component)( int eid, uint64_t flags ) { + uint64_t sys = world.entities[ eid * 2 + 0 ]; + return flags == (sys & flags); +} +bool (ecs_add_component)( int eid, uint64_t flags ) { + uint64_t sys = world.entities[ eid * 2 + 0 ]; + uint64_t off = world.entities[ eid * 2 + 1 ]; + + int eid2 = (ecs_add_entity)( sys | flags ); + uint64_t sys2 = world.entities[ eid2 * 2 + 0 ]; + uint64_t off2 = world.entities[ eid2 * 2 + 1 ]; + + for( uint64_t cid = 0, idx = 0, idx2 = 0; cid < world.nc; ++cid) { + if( (1ull << cid) & sys ) { + if( (1ull << cid) & sys2 ) { + memcpy( &world.components[ off2 ] + idx2, &world.components[ off ] + idx, sizeof(component_t)); + } + } + if( (1ull << cid) & sys ) ++idx; + if( (1ull << cid) & sys2 ) ++idx2; + } + + world.entities[ eid * 2 + 0 ] = sys2; + world.entities[ eid * 2 + 1 ] = off2; + + world.entities[ eid2 * 2 + 0 ] = 0; + world.entities[ eid2 * 2 + 1 ] = 0; + + return true; +} +bool (ecs_del_component)( int eid, uint64_t flags ) { // clr_component? + uint64_t sys = world.entities[ eid * 2 + 0 ]; + uint64_t off = world.entities[ eid * 2 + 1 ]; + for( uint64_t cid = 0, idx = 0; cid < world.nc; ++cid) { + if( (1ull << cid) & flags ) { + memset( &world.components[ off ] + idx, 0, sizeof(component_t)); + } + if( (1ull << cid) & sys ) ++idx; + } + world.entities[ eid * 2 + 0 ] &= ~flags; + return true; +} +bool (ecs_use_component)( int eid, uint64_t flags ) { + world.entities[ eid * 2 + 0 ] |= flags; + return true; +} +bool (ecs_off_component)( int eid, uint64_t flags ) { + world.entities[ eid * 2 + 0 ] &= ~flags; + return true; +} + +void (ecs_dump_world)( FILE *fp, uint64_t sys_mask ) { + if( sys_mask ) for( int eid = 0; eid < world.ne; ++eid ) { + uint64_t sys = world.entities[ eid * 2 + 0 ]; + uint64_t off = world.entities[ eid * 2 + 1 ]; + if( sys_mask != (sys & sys_mask)) continue; + fprintf(fp, "eid:%d sys:%#x ", eid, (unsigned)sys); + for( uint64_t cid = 0, idx = 0; cid < world.nc; ++cid ) { + if( (1ull << cid) & sys ) { + fprintf(fp, "cid:%d,%p+%d ", (int)cid, &world.components[ off ], (int)idx ); + ++idx; + } + } + fputc('\n', fp); + } +} + + +#ifdef ECS_BENCH +#include +#include + +int main(int argc, char **argv) { + int player; + + /* entities to spawn */ + #ifndef ECS_N + const int ECS_N = argc > 1 ? atoi(argv[1]) : 100000; + #endif + + /* frames to benchmark */ + #ifndef ECS_F + const int ECS_F = argc > 2 ? atoi(argv[2]) : 1000; + #endif + + // declare components: c1, c2, ... + enum { POSITION, VELOCITY, COLOR, HEALTH, INPUT, NAME, TOTAL }; + ecs_max_components(TOTAL); + + // spawn entities + { + double start = time_ss(); + + player = ecs_add_entity(NAME, POSITION, VELOCITY, HEALTH, INPUT); + ecs_get_component(player, VELOCITY)->vx = 1; + ecs_get_component(player, VELOCITY)->vy = 2; + + for (int i = 0; i < ECS_N; ++i) { + switch (i & 3) { + break; case 0:; /* static enemy */ + int enemy0 = ecs_add_entity(NAME, POSITION, COLOR, HEALTH); + break; case 1:; /* dynamic enemy */ + int enemy1 = ecs_add_entity(NAME, POSITION, COLOR, HEALTH, VELOCITY); + break; case 2:; /* static light */ + int light0 = ecs_add_entity(NAME, POSITION, COLOR); + break; case 3:; /* dynamic light */ + int light1 = ecs_add_entity(NAME, POSITION, COLOR, VELOCITY); + } + } + + double end = time_ss(); + double t = (end - start); + + int T = 1 * ECS_N; + printf("%17s: %d frame(s) * %d num_entities = %d total ops, in %.3fs => %.3fM ops/s, %.2fms/frame\n", + "spawn benchmark", 1, ECS_N, T, t, (T / 1000000.0) / t, (t * 1000 / 1) ); + } + + // process & benchmark + { + double start = time_ss(); + + for( int frame = 0; frame < ECS_F; ++frame ) { + parallel + for each_ecs_component(obj, POSITION, VELOCITY) { + component_t *p = obj[POSITION]; + component_t *v = obj[VELOCITY]; + p->x += v->vx; + p->y += v->vy; + p->z += v->vz; + } + } + + double end = time_ss(); + double t = (end - start); + + // stats + int num_iterated_entities = ECS_N; + int num_processed_entities = 0; + for each_ecs_component(obj, POSITION, VELOCITY) { + ++num_processed_entities; + } + + int T = ECS_F * num_processed_entities; + printf("%17s: %d frame(s) * %d num_entities = %d total ops, in %.3fs => %.3fM ops/s, %.2fms/frame\n", + "process benchmark", ECS_F, num_processed_entities, T, t, (T / 1000000.0) / t, (t * 1000 / ECS_F) ); + } + + {component_t *p = ecs_get_component(player, POSITION); + printf("eid:%d (position: %f,%f,%f)\n", player, p->x, p->y, p->z );} + + assert( ecs_get_component(player, POSITION)->x == (ECS_F * 1)); + assert( ecs_get_component(player, POSITION)->y == (ECS_F * 2)); + + assert( ~puts("Ok") ); +} +#endif + +#ifdef ECS_DEMO +#include +#include + +int main(int argc, char **argv) { + // declare components: c1, c2, ... + enum { POSITION, VELOCITY, COLOR, HEALTH, INPUT, TOTAL }; + ecs_max_components(TOTAL); + + // spawn entities + int player = ecs_add_entity(POSITION, VELOCITY, HEALTH, INPUT); + ecs_get_component(player, VELOCITY)->vx = 1; + ecs_get_component(player, VELOCITY)->vy = 2; + + // some more + int enemy0, enemy1, light0, light1; + for (int i = 0; i < 10; ++i) { + if( 0 == (i&3)) /* static enemy */ enemy0 = ecs_add_entity(POSITION, COLOR, HEALTH); + if( 1 == (i&3)) /* dynamic enemy */ enemy1 = ecs_add_entity(POSITION, COLOR, HEALTH, VELOCITY); + if( 2 == (i&3)) /* static light */ light0 = ecs_add_entity(POSITION, COLOR); + if( 3 == (i&3)) /* dynamic light */ light1 = ecs_add_entity(POSITION, COLOR, VELOCITY), ecs_get_component(light1, VELOCITY)->vy = 2; + } + + { + component_t *p = ecs_get_component(player, POSITION); + printf("eid:%d (position: %f,%f,%f)\n", player, p->x, p->y, p->z ); + p = ecs_get_component(player, VELOCITY); + printf("eid:%d (velocity: %f,%f)\n", player, p->vx, p->vy ); + + p = ecs_get_component(light1, POSITION); + printf("eid:%d (position: %f,%f,%f)\n", light1, p->x, p->y, p->z ); + p = ecs_get_component(light1, VELOCITY); + printf("eid:%d (velocity: %f,%f)\n", light1, p->vx, p->vy ); + } + + + // simulate system processing + int frames = 1000; + for( int frame = 0; frame < frames; ++frame ) { + for each_ecs_component(obj, POSITION, VELOCITY) { + component_t *p = obj[POSITION]; + component_t *v = obj[VELOCITY]; + p->x += v->vx; + p->y += v->vy; + p->z += v->vz; + } + } + + // verify (should display only player entity) + ecs_dump_world(stdout, INPUT); + + { + component_t *p = ecs_get_component(player, POSITION); + printf("eid:%d (position: %f,%f,%f)\n", player, p->x, p->y, p->z ); + p = ecs_get_component(light1, POSITION); + printf("eid:%d (position: %f,%f,%f)\n", light1, p->x, p->y, p->z ); + } + + assert( ecs_get_component(player, POSITION)->x == (frames * 1)); + assert( ecs_get_component(player, POSITION)->y == (frames * 2)); + + assert( ecs_has_component(player, POSITION) ); + assert(!ecs_has_component(player, COLOR) ); + + assert( ecs_add_component(player, COLOR) ); + assert( ecs_has_component(player, COLOR) ); + + assert( ecs_off_component(player, POSITION) ); + assert(!ecs_has_component(player, POSITION) ); + assert( ecs_get_component(player, POSITION) == NULL); + assert( ecs_use_component(player, POSITION) ); + assert( ecs_has_component(player, POSITION) ); + assert( ecs_get_component(player, POSITION)->x == (frames * 1)); + + assert( ecs_del_component(player, POSITION) ); + assert(!ecs_has_component(player, POSITION) ); + assert( ecs_get_component(player, POSITION) == NULL ); + + assert( ~puts("Ok") ); +} +#endif diff --git a/tools/editor/labs.vm/hybrid-p2p.md b/tools/editor/labs.vm/hybrid-p2p.md new file mode 100644 index 0000000..761b5e6 --- /dev/null +++ b/tools/editor/labs.vm/hybrid-p2p.md @@ -0,0 +1,25 @@ +# Fair hybrid P2P model (rlyeh, public domain) + +## Abstract + +In gamedev, the problem with p2p is that decision-making falls to individuals, and they can unbalance the balance in their very own favor. For this reason, the client/server model is usually preferred, with the servers (and $maintenance$) being handled by the company that hosts the game. + +## Theory + +Following hybrid p2p system would be one in which a game is made up of many clusters of clients that play different games simultaneously, but in which the peers that act as referees actually belong to a different game, in such a way that a peer cannot Referee their own game. Basically if a peer is in a game-A with A-peers, it can only arbitrate a game-B with B-peers, all this while peer is also playing their game with other A peers. + +In addition, in order for peer A to arbitrate and initiate a game-B of B-peers, all nodes of B must send the source code of the server (with the rules of the game) to peer A for further validation. Once the source code is validated (sources must match for all the involved nodes), server-A will process the traffic of game-B, but note that server does not care about the rules of B, neither what the traffic of B looks like, and in fact, server-A does not know or care what peers in game-B are doing... what peer A does know as a player, however, is everything about game-A, whose refereers are in fact random external players to A set. + +## Appendix and notes + +``` +game A game B +[ player12 player16* player45 server33* player08 ] [ player49 player09 player33* server16* ] +``` + +- 16 is a player in A, and a refeerer in B. 33 is a player in B, and a refeerer in A. +- 33 agreed to arbitrate game A after checking data & code consensus for players { 12, 16, 45, 08 }. +- 16 agreed to arbitrate game B after checking data & code consensus for players { 49, 09, 33 }. +- traffic could be encrypted. public key could be a deterministic hash of all code+datas involved. +- peers could use an anonymous karma score system: positive for peers completing game sessions, negative for those peers cheating and/or with communication issues. + diff --git a/tools/editor/labs.vm/netproto.md b/tools/editor/labs.vm/netproto.md new file mode 100644 index 0000000..59554dd --- /dev/null +++ b/tools/editor/labs.vm/netproto.md @@ -0,0 +1,92 @@ +// minimalist network proto/scheme that resembles a .ini file. +// - rlyeh, public domain + +## features + +```json +types: + floating + integer + string + +signed: + yes/no + +sizes: + 1,8,12,15,16,24,32,64,... + +containers: + lists + maps (can be simulated by using two contiguous lists of keys and values) + +blocks: + enum + union (can be extended) + struct (can be extended) + +options: + zigzag, decay (double->half), packed, default, + +messaging: + numbered fields + optional + required + repeated + extensions +``` + +## binary format + +```ini +[cellsize in bytes:8] +[num key-values:cellsize] + [key id:cellsize][value id:cellsize] + [...] +[num types:cellsize] + [type class:cellsize][num items:cellsize][item1][item2][...] + [...] // would be nice to put strings at the very end, because of their length-variable nature +``` + +## text format + +```ini +// namespace +[[Demo]] + +// free-standing options +package = "tutorial" +version = 123 +pi = 3.14159 + +[PhoneType] // enumeration: all members are uppercase + no need for explicit values. +HOME +WORK +OTHER = HOME +MOBILE + +[PhoneNumber] // struct: variables never share same field number (1!=2) +1: number = 0 // .field 1, .id number, .value 0, .type Integer +2: phonetype = OTHER? // .field 2, .id phonetype, .value OTHER, .type PhoneType, .optional + +[Name] // union: some variables may share a very same field number (1==1) +1: name = "" // .field 1, .id name, .value "", .type String +1: nickname = "" // .field 1, .id nickname, .value "", .type String + +[Profile] // struct +1: name = Name... // .field 1, .id name, .type Name, .repeat +2: icon = "guest.png"? // .field 2, .id icon, .type String, .value = "guest.png", .optional + +[Contact] // struct +1: person = Profile +2: card_id = 0 bits:15 // .default = 0, .bits = 15 +3: email = ""? // email is an optional string located in the third message field that defaults to an empy string +4: phone = PhoneType... // .repeat +5: calls = 0... bits:64 packed // .default = 0, .repeat, .bits = 64, .packed +10..max: // user-defined, reserved extensions + +[ContactExt <- Contact] // struct that extends from Contact +1: phone_ext_number = 0? + +[AddressBook] // struct +1: contact = ContactExt... +``` diff --git a/tools/editor/plugins/.gitkeep b/tools/editor/plugins/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tools/editor/windows.ini b/tools/editor/windows.ini new file mode 100644 index 0000000..14f0d74 --- /dev/null +++ b/tools/editor/windows.ini @@ -0,0 +1,12 @@ +[Outliner] +x=0.000000 +y=0.045390 +w=0.250208 +h=0.953120 +visible=1 +[Properties] +x=0.780381 +y=0.045390 +w=0.219619 +h=0.953154 +visible=1 diff --git a/tools/lua/LICENSE b/tools/lua/LICENSE new file mode 100644 index 0000000..b71e184 --- /dev/null +++ b/tools/lua/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Dmitry Ivanov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/tools/lua/README.md b/tools/lua/README.md new file mode 100644 index 0000000..e7f27ee --- /dev/null +++ b/tools/lua/README.md @@ -0,0 +1,55 @@ +# premake-ninja + +[Premake](https://github.com/premake/premake-core) extension to support [Ninja](https://github.com/martine/ninja), because it's awesome. [![Build Status](https://travis-ci.org/jimon/premake-ninja.svg?branch=master)](https://travis-ci.org/jimon/premake-ninja) [![Build status](https://ci.appveyor.com/api/projects/status/3d2vot6y72nfo24y/branch/master?svg=true)](https://ci.appveyor.com/project/jimon/premake-ninja/branch/master) + +### Implementation + +For each project - configuration pair we create separate .ninja file. For solution we create build.ninja file which imports other .ninja files with subninja command. + +Build.ninja file sets phony targets for configuration names so you can build them from command line. And default target is a first configuration name in your project (usually default). + +### TODO + +- Resources are not supported +- Makefile not supported +- Bundles of any sort are not supported +- Clear methods are not supported +- C# not supported +- D not supported + +### Tested on + +- msvc / win + - ConsoleApp works + - WindowedApp works + - StaticLib works + - SharedLib works + - win64 not tested +- clang / win + - not tested +- clang / linux + - not tested +- clang / osx + - ConsoleApp works + - WindowedApp works + - StaticLib works + - SharedLib works + - x86_64 tested, x86 not tested +- gcc / win + - ConsoleApp works + - WindowedApp works + - StaticLib works + - SharedLib works + - x86_64 tested, x86 not tested +- gcc / linux (tested on Ubuntu 15.04) + - ConsoleApp works + - WindowedApp works + - StaticLib works + - SharedLib works + - PCH works + - Custom build rules works + - x86_64 tested, x86 not tested + +### Extra Tests + +Part of integration tests of several generators in https://github.com/Jarod42/premake-sample-projects diff --git a/tools/lua/_manifest.lua b/tools/lua/_manifest.lua new file mode 100644 index 0000000..31fb223 --- /dev/null +++ b/tools/lua/_manifest.lua @@ -0,0 +1,4 @@ +return { + "_preload.lua", + "ninja.lua", +} diff --git a/tools/lua/_preload.lua b/tools/lua/_preload.lua new file mode 100644 index 0000000..2d71806 --- /dev/null +++ b/tools/lua/_preload.lua @@ -0,0 +1,62 @@ +-- +-- Name: premake-ninja/_preload.lua +-- Purpose: Define the ninja action. +-- Author: Dmitry Ivanov +-- Created: 2015/07/04 +-- Copyright: (c) 2015 Dmitry Ivanov +-- + + local p = premake + + newaction + { + -- Metadata for the command line and help system + trigger = "ninja", + shortname = "ninja", + description = "Ninja is a small build system with a focus on speed", + + -- The capabilities of this action + valid_kinds = {"ConsoleApp", "WindowedApp", "SharedLib", "StaticLib", "None"}, -- Not supported: Makefile, Packaging, SharedItems, Utility + valid_languages = {"C", "C++"}, + valid_tools = {cc = { "gcc", "clang", "msc" }}, + + toolset = "gcc", + + -- Workspace and project generation logic + onWorkspace = function(wks) + p.eol("\r\n") + p.indent(" ") + p.escaper(p.modules.ninja.esc) + p.generate(wks, "build.ninja", p.modules.ninja.generateWorkspace) + end, + onProject = function(prj) + p.eol("\r\n") + p.indent(" ") + p.escaper(p.modules.ninja.esc) + p.modules.ninja.generateProject(prj) + end, + onBranch = function(prj) + p.eol("\r\n") + p.indent(" ") + p.escaper(p.modules.ninja.esc) + p.modules.ninja.generateProject(prj) + end, + onCleanSolution = function(sln) + -- TODO + end, + onCleanProject = function(prj) + -- TODO + end, + onCleanTarget = function(prj) + -- TODO + end, + } + + +-- +-- Decide when the full module should be loaded. +-- + + return function(cfg) + return (_ACTION == "ninja") + end diff --git a/tools/lua/appveyor.yml b/tools/lua/appveyor.yml new file mode 100644 index 0000000..738ea66 --- /dev/null +++ b/tools/lua/appveyor.yml @@ -0,0 +1,23 @@ +version: 0.1.{build} +environment: + global: + PYTHON: "C:\\Python35" +install: + - set PATH=%cd%/.bins;%PYTHON%;%PYTHON%/Scripts;%PATH% + - python --version + - mkdir -p .bins + - cd .bins + - powershell "(New-Object Net.WebClient).DownloadFile('https://github.com/premake/premake-core/releases/download/v5.0.0-alpha6/premake-5.0.0-alpha6-windows.zip','premake-5.0.0-alpha6-windows.zip')" + - powershell "(new-object -com shell.application).namespace($pwd.Path).CopyHere((new-object -com shell.application).namespace(\"$pwd\premake-5.0.0-alpha6-windows.zip\").Items(),16)" + - powershell "(New-Object Net.WebClient).DownloadFile('https://github.com/martine/ninja/releases/download/v1.6.0/ninja-win.zip','ninja.zip')" + - powershell "(new-object -com shell.application).namespace($pwd.Path).CopyHere((new-object -com shell.application).namespace(\"$pwd\ninja.zip\").Items(),16)" + - premake5 --version + - ninja --version + - cd .. +build: off +test_script: + - call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 + - cd tests + - python run_tests.py + - cd .. + diff --git a/tools/lua/ninja.lua b/tools/lua/ninja.lua new file mode 100644 index 0000000..171e6e1 --- /dev/null +++ b/tools/lua/ninja.lua @@ -0,0 +1,725 @@ +-- +-- Name: premake-ninja/ninja.lua +-- Purpose: Define the ninja action. +-- Author: Dmitry Ivanov +-- Created: 2015/07/04 +-- Copyright: (c) 2015 Dmitry Ivanov +-- + +local p = premake +local tree = p.tree +local project = p.project +local config = p.config +local fileconfig = p.fileconfig + +premake.modules.ninja = {} +local ninja = p.modules.ninja + +local function get_key(cfg) + if cfg.platform then + return cfg.project.name .. "_" .. cfg.buildcfg .. "_" .. cfg.platform + else + return cfg.project.name .. "_" .. cfg.buildcfg + end +end + +local build_cache = {} + +local function add_build(cfg, output, extra_outputs, command, args) + local cached = build_cache[output] + if cached ~= nil then + if extra_outputs == cached.extra_outputs + and command == cached.command + and table.equals(args or {}, cached.args or {}) + then + -- custom_command rule is identical for each configuration (contrary to other rules) + -- So we can compare extra parameter + if string.startswith(cached.command, "custom_command") then + p.w("# INFO: Rule ignored, same as " .. cached.cfg_key) + else + local cfg_key = get_key(cfg) + p.warn(cached.cfg_key .. " and " .. cfg_key .. " both generate (differently?) " .. output .. ". Ignoring " .. cfg_key) + p.w("# WARNING: Rule ignored, using the one from " .. cached.cfg_key) + end + else + local cfg_key = get_key(cfg) + p.warn(cached.cfg_key .. " and " .. cfg_key .. " both generate differently " .. output .. ". Ignoring " .. cfg_key) + p.w("# ERROR: Rule ignored, using the one from " .. cached.cfg_key) + end + p.w("# build " .. output .. ": " .. command) + for i, arg in ipairs(args or {}) do + p.w("# " .. arg) + end + return + end + p.w("build " .. output .. ": " .. command) + for i, arg in ipairs(args or {}) do + p.w(" " .. arg) + end + build_cache[output] = { + cfg_key = get_key(cfg), + extra_outputs = extra_outputs, + command = command, + args = args + } +end + +function ninja.esc(value) + value = value:gsub("%$", "$$") -- TODO maybe there is better way + value = value:gsub(":", "$:") + value = value:gsub("\n", "$\n") + value = value:gsub(" ", "$ ") + return value +end + +function ninja.quote(value) + value = value:gsub("\\", "\\\\") + value = value:gsub("'", "\\'") + value = value:gsub("\"", "\\\"") + + return "\"" .. value .. "\"" +end + +-- in some cases we write file names in rule commands directly +-- so we need to propely escape them +function ninja.shesc(value) + if type(value) == "table" then + local result = {} + local n = #value + for i = 1, n do + table.insert(result, ninja.shesc(value[i])) + end + return result + end + + if value:find(" ") then + return ninja.quote(value) + end + return value +end + +-- generate solution that will call ninja for projects +function ninja.generateWorkspace(wks) + local oldGetDefaultSeparator = path.getDefaultSeparator + path.getDefaultSeparator = function() return "/" end + + p.w("# solution build file") + p.w("# generated with premake ninja") + p.w("") + + p.w("# build projects") + local cfgs = {} -- key is concatenated name or variant name, value is string of outputs names + local key = "" + local cfg_first = nil + local cfg_first_lib = nil + + for prj in p.workspace.eachproject(wks) do + if p.action.supports(prj.kind) and prj.kind ~= p.NONE then + for cfg in p.project.eachconfig(prj) do + key = prj.name .. "_" .. cfg.buildcfg + + if cfg.platform ~= nil then key = key .. "_" .. cfg.platform end + + if not cfgs[cfg.buildcfg] then cfgs[cfg.buildcfg] = "" end + cfgs[cfg.buildcfg] = cfgs[cfg.buildcfg] .. key .. " " + + -- set first configuration name + if (cfg_first == nil) and (cfg.kind == p.CONSOLEAPP or cfg.kind == p.WINDOWEDAPP) then + cfg_first = key + end + if (cfg_first_lib == nil) and (cfg.kind == p.STATICLIB or cfg.kind == p.SHAREDLIB) then + cfg_first_lib = key + end + + -- include other ninja file + p.w("subninja " .. p.esc(ninja.projectCfgFilename(cfg, true))) + end + end + end + + if cfg_first == nil then cfg_first = cfg_first_lib end + + p.w("") + + p.w("# targets") + for cfg, outputs in pairs(cfgs) do + p.w("build " .. p.esc(cfg) .. ": phony " .. outputs) + end + p.w("") + + p.w("# default target") + p.w("default " .. p.esc(cfg_first)) + p.w("") + + path.getDefaultSeparator = oldGetDefaultSeparator +end + +function ninja.list(value) + if #value > 0 then + return " " .. table.concat(value, " ") + else + return "" + end +end + +local function shouldcompileasc(filecfg) + if filecfg.compileas and filecfg.compileas ~= "Default" then + return p.languages.isc(filecfg.compileas) + end + return path.iscfile(filecfg.abspath) +end + +local function shouldcompileascpp(filecfg) + if filecfg.compileas and filecfg.compileas ~= "Default" then + return p.languages.iscpp(filecfg.compileas) + end + return path.iscppfile(filecfg.abspath) +end + +local function getDefaultToolsetFromOs() + local system_name = os.target() + + if system_name == "windows" then + return "msc" + elseif system_name == "macosx" then + return "clang" + elseif system_name == "linux" then + return "gcc" + else + p.warnOnce("unknown_system", "no toolchain set and unknown system " .. system_name .. " so assuming toolchain is gcc") + return "gcc" + end +end + +local function getToolsetExecutables(cfg, toolset, toolset_name) + local cc = "" + local cxx = "" + local ar = "" + local link = "" + local rc = "" + + if toolset_name == "msc" then + -- TODO premake doesn't set tools names for msc, do we want to fix it ? + cc = "cl" + cxx = "cl" + ar = "lib" + link = "cl" + rc = "rc" + elseif toolset_name == "clang" or toolset_name == "gcc" then + if not cfg.gccprefix then cfg.gccprefix = "" end + cc = toolset.gettoolname(cfg, "cc") + cxx = toolset.gettoolname(cfg, "cxx") + ar = toolset.gettoolname(cfg, "ar") + link = toolset.gettoolname(cfg, iif(cfg.language == "C", "cc", "cxx")) + else + p.error("unknown toolchain " .. toolset_name) + end + return cc, cxx, ar, link, rc +end + +local function getFileDependencies(cfg) + local dependencies = {} + if #cfg.prebuildcommands > 0 or cfg.prebuildmessage then + dependencies = {"prebuild_" .. get_key(cfg)} + end + for i = 1, #cfg.dependson do + table.insert(dependencies, cfg.dependson[i] .. "_" .. cfg.buildcfg) + end + return dependencies +end + +local function getcflags(toolset, cfg, filecfg) + local buildopt = ninja.list(filecfg.buildoptions) + local cppflags = ninja.list(toolset.getcppflags(filecfg)) + local cflags = ninja.list(toolset.getcflags(filecfg)) + local defines = ninja.list(table.join(toolset.getdefines(filecfg.defines), toolset.getundefines(filecfg.undefines))) + local includes = ninja.list(toolset.getincludedirs(cfg, filecfg.includedirs, filecfg.externalincludedirs)) + local forceincludes = ninja.list(toolset.getforceincludes(cfg)) + + return buildopt .. cppflags .. cflags .. defines .. includes .. forceincludes +end + +local function getcxxflags(toolset, cfg, filecfg) + local buildopt = ninja.list(filecfg.buildoptions) + local cppflags = ninja.list(toolset.getcppflags(filecfg)) + local cxxflags = ninja.list(toolset.getcxxflags(filecfg)) + local defines = ninja.list(table.join(toolset.getdefines(filecfg.defines), toolset.getundefines(filecfg.undefines))) + local includes = ninja.list(toolset.getincludedirs(cfg, filecfg.includedirs, filecfg.externalincludedirs)) + local forceincludes = ninja.list(toolset.getforceincludes(cfg)) + return buildopt .. cppflags .. cxxflags .. defines .. includes .. forceincludes +end + +local function getldflags(toolset, cfg) + local ldflags = ninja.list(table.join(toolset.getLibraryDirectories(cfg), toolset.getldflags(cfg), cfg.linkoptions)) + + -- experimental feature, change install_name of shared libs + --if (toolset_name == "clang") and (cfg.kind == p.SHAREDLIB) and ninja.endsWith(cfg.buildtarget.name, ".dylib") then + -- ldflags = ldflags .. " -install_name " .. cfg.buildtarget.name + --end + return ldflags +end + +local function prebuild_rule(cfg) + if #cfg.prebuildcommands > 0 or cfg.prebuildmessage then + local commands = {} + if cfg.prebuildmessage then + commands = {os.translateCommandsAndPaths("{ECHO} " .. cfg.prebuildmessage, cfg.project.basedir, cfg.project.location)} + end + commands = table.join(commands, os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)) + if (#commands > 1) then + commands = 'sh -c ' .. ninja.quote(table.implode(commands,"","",";")) + else + commands = commands[1] + end + p.w("rule run_prebuild") + p.w(" command = " .. p.esc(commands)) + p.w(" description = prebuild") + p.w("") + end +end + +local function postbuild_rule(cfg) + if #cfg.postbuildcommands > 0 or cfg.postbuildmessage then + local commands = {} + if cfg.postbuildmessage then + commands = {os.translateCommandsAndPaths("{ECHO} " .. cfg.postbuildmessage, cfg.project.basedir, cfg.project.location)} + end + commands = table.join(commands, os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location)) + if (#commands > 1) then + commands = 'sh -c ' .. ninja.quote(table.implode(commands,"","",";")) + else + commands = commands[1] + end + p.w("rule run_postbuild") + p.w(" command = " .. p.esc(commands)) + p.w(" description = postbuild") + p.w("") + end +end + +local function compilation_rules(cfg, toolset, toolset_name, pch) + ---------------------------------------------------- figure out toolset executables + local cc, cxx, ar, link, rc = getToolsetExecutables(cfg, toolset, toolset_name) + + local all_cflags = getcflags(toolset, cfg, cfg) + local all_cxxflags = getcxxflags(toolset, cfg, cfg) + local all_ldflags = getldflags(toolset, cfg) + + if toolset_name == "msc" then + -- for some reason Visual Studio add this libraries as "defaults" and premake doesn't tell us this + local default_msvc_libs = " kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib" + + p.w("rule cc") + p.w(" command = " .. cc .. all_cflags .. " /nologo /showIncludes -c $in /Fo$out") + p.w(" description = cc $out") + p.w(" deps = msvc") + p.w("") + p.w("rule cxx") + p.w(" command = " .. cxx .. all_cxxflags .. " /nologo /showIncludes -c $in /Fo$out") + p.w(" description = cxx $out") + p.w(" deps = msvc") + p.w("") + p.w("rule cc_flags") + p.w(" command = " .. cc .. " $CFLAGS" .. " /nologo /showIncludes -c $in /Fo$out") + p.w(" description = cc $out") + p.w(" deps = msvc") + p.w("") + p.w("rule cxx_flags") + p.w(" command = " .. cxx .. " $CXXFLAGS" .. " /nologo /showIncludes -c $in /Fo$out") + p.w(" description = cxx $out") + p.w(" deps = msvc") + p.w("") + p.w("rule rc") + p.w(" command = " .. rc .. " /nologo /fo$out $in") + p.w(" description = rc $out") + p.w("") + if cfg.kind == p.STATICLIB then + p.w("rule ar") + p.w(" command = " .. ar .. " $in /nologo -OUT:$out") + p.w(" description = ar $out") + p.w("") + else + p.w("rule link") + p.w(" command = " .. link .. " $in" .. ninja.list(ninja.shesc(toolset.getlinks(cfg, true))) .. default_msvc_libs .. " /link" .. all_ldflags .. " /nologo /out:$out") + p.w(" description = link $out") + p.w("") + end + elseif toolset_name == "clang" then + local force_include_pch = "" + if pch then + force_include_pch = " -include " .. p.esc(pch.placeholder) + p.w("rule build_pch") + p.w(" command = " .. iif(cfg.language == "C", cc .. all_cflags .. " -x c-header", cxx .. all_cxxflags .. " -x c++-header") .. " -H -MMD -MF $out.d -c -o $out $in") + p.w(" description = build_pch $out") + p.w(" depfile = $out.d") + p.w(" deps = gcc") + end + p.w("rule cc") + p.w(" command = " .. cc .. all_cflags .. force_include_pch .. " -x c -MMD -MF $out.d -c -o $out $in") + p.w(" description = cc $out") + p.w(" depfile = $out.d") + p.w(" deps = gcc") + p.w("") + p.w("rule cxx") + p.w(" command = " .. cxx .. all_cxxflags .. force_include_pch .. " -x c++ -MMD -MF $out.d -c -o $out $in") + p.w(" description = cxx $out") + p.w(" depfile = $out.d") + p.w(" deps = gcc") + p.w("") + p.w("rule cc_flags") + p.w(" command = " .. cc .. " $CFLAGS".. force_include_pch .. " -x c -MMD -MF $out.d -c -o $out $in") + p.w(" description = cc $out") + p.w(" depfile = $out.d") + p.w(" deps = gcc") + p.w("") + p.w("rule cxx_flags") + p.w(" command = " .. cxx .. " $CXXFLAGS" .. force_include_pch .. " -x c++ -MMD -MF $out.d -c -o $out $in") + p.w(" description = cxx $out") + p.w(" deps = msvc") + p.w("") + if cfg.kind == p.STATICLIB then + p.w("rule ar") + p.w(" command = " .. ar .. " rcs $out $in") + p.w(" description = ar $out") + p.w("") + else + p.w("rule link") + p.w(" command = " .. link .. " -o $out $in" .. ninja.list(ninja.shesc(toolset.getlinks(cfg, true))) .. all_ldflags) + p.w(" description = link $out") + p.w("") + end + elseif toolset_name == "gcc" then + local force_include_pch = "" + if pch then + force_include_pch = " -include " .. p.esc(pch.placeholder) + p.w("rule build_pch") + p.w(" command = " .. iif(cfg.language == "C", cc .. all_cflags .. " -x c-header", cxx .. all_cxxflags .. " -x c++-header") .. " -H -MMD -MF $out.d -c -o $out $in") + p.w(" description = build_pch $out") + p.w(" depfile = $out.d") + p.w(" deps = gcc") + end + p.w("rule cc") + p.w(" command = " .. cc .. all_cflags .. force_include_pch .. " -x c -MMD -MF $out.d -c -o $out $in") + p.w(" description = cc $out") + p.w(" depfile = $out.d") + p.w(" deps = gcc") + p.w("") + p.w("rule cxx") + p.w(" command = " .. cxx .. all_cxxflags .. force_include_pch .. " -x c++ -MMD -MF $out.d -c -o $out $in") + p.w(" description = cxx $out") + p.w(" depfile = $out.d") + p.w(" deps = gcc") + p.w("") + p.w("rule cc_flags") + p.w(" command = " .. cc .. " $CFLAGS".. force_include_pch .. " -x c -MMD -MF $out.d -c -o $out $in") + p.w(" description = cc $out") + p.w(" depfile = $out.d") + p.w(" deps = gcc") + p.w("") + p.w("rule cxx_flags") + p.w(" command = " .. cxx .. " $CXXFLAGS" .. force_include_pch .. " -x c++ -MMD -MF $out.d -c -o $out $in") + p.w(" description = cxx $out") + p.w(" deps = msvc") + p.w("") + if cfg.kind == p.STATICLIB then + p.w("rule ar") + p.w(" command = " .. ar .. " rcs $out $in") + p.w(" description = ar $out") + p.w("") + else + p.w("rule link") + p.w(" command = " .. link .. " -o $out $in" .. ninja.list(ninja.shesc(toolset.getlinks(cfg, true))) .. all_ldflags) + p.w(" description = link $out") + p.w("") + end + end +end + +local function custom_command_rule() + p.w("rule custom_command") + p.w(" command = $CUSTOM_COMMAND") + p.w(" description = $CUSTOM_DESCRIPTION") + p.w("") +end + +local function collect_generated_files(prj, cfg) + local generated_files = {} + tree.traverse(project.getsourcetree(prj), { + onleaf = function(node, depth) + function append_to_generated_files(filecfg) + local output = project.getrelative(prj, filecfg.buildoutputs[1]) + table.insert(generated_files, p.esc(output)) + end + local filecfg = fileconfig.getconfig(node, cfg) + local rule = p.global.getRuleForFile(node.name, prj.rules) + if fileconfig.hasCustomBuildRule(filecfg) then + append_to_generated_files(filecfg) + elseif rule then + local environ = table.shallowcopy(filecfg.environ) + + if rule.propertydefinition then + p.rule.prepareEnvironment(rule, environ, cfg) + p.rule.prepareEnvironment(rule, environ, filecfg) + end + local rulecfg = p.context.extent(rule, environ) + append_to_generated_files(rulecfg) + end + end, + }, false, 1) + return generated_files +end + +local function pch_build(cfg, pch) + local pch_dependency = "" + if pch then + pch_dependency = " | " .. pch.gch + add_build(cfg, p.esc(pch.gch), "", "build_pch " .. p.esc(pch.input)) + end + return pch_dependency +end + +local function custom_command_build(prj, cfg, filecfg, filename, file_dependencies) + local output = project.getrelative(prj, filecfg.buildoutputs[1]) + local inputs = "" + if #filecfg.buildinputs > 0 then + inputs = table.implode(filecfg.buildinputs," ","","") + end + + local commands = {} + if filecfg.buildmessage then + commands = {os.translateCommandsAndPaths("{ECHO} " .. filecfg.buildmessage, prj.basedir, prj.location)} + end + commands = table.join(commands, os.translateCommandsAndPaths(filecfg.buildcommands, prj.basedir, prj.location)) + if (#commands > 1) then + commands = 'sh -c ' .. ninja.quote(table.implode(commands,"","",";")) + else + commands = commands[1] + end + + add_build(cfg, p.esc(output), "", "custom_command | " .. p.esc(filename) .. inputs .. iif(#file_dependencies > 0, "||" .. ninja.list(file_dependencies), ""), + {"CUSTOM_COMMAND = " .. commands, "CUSTOM_DESCRIPTION = custom build " .. p.esc(output)}) +end + +local function compile_file_build(cfg, filecfg, toolset, pch_dependency, regular_file_dependencies, objfiles) + local obj_dir = project.getrelative(cfg.workspace, cfg.objdir) + local has_custom_settings = fileconfig.hasFileSettings(filecfg) + + if shouldcompileasc(filecfg) then + local objfilename = obj_dir .. "/" .. filecfg.objname .. iif(toolset_name == "msc", ".obj", ".o") + objfiles[#objfiles + 1] = objfilename + local cflags = {} + if has_custom_settings then + cflags = {"CFLAGS = " .. getcflags(toolset, cfg, filecfg)} + end + add_build(cfg, p.esc(objfilename), "", iif(has_custom_settings, "cc_flags ", "cc ") .. p.esc(filecfg.relpath) .. pch_dependency .. regular_file_dependencies, cflags) + elseif shouldcompileascpp(filecfg) then + local objfilename = obj_dir .. "/" .. filecfg.objname .. iif(toolset_name == "msc", ".obj", ".o") + objfiles[#objfiles + 1] = objfilename + local cxxflags = {} + if has_custom_settings then + cxxflags = {"CXXFLAGS = " .. getcxxflags(toolset, cfg, filecfg)} + end + add_build(cfg, p.esc(objfilename), "", iif(has_custom_settings, "cxx_flags ", "cxx ") .. p.esc(filecfg.relpath) .. pch_dependency .. regular_file_dependencies, cxxflags) + elseif path.isresourcefile(filecfg.abspath) then + local objfilename = obj_dir .. "/" .. filecfg.name .. ".res" + objfiles[#objfiles + 1] = objfilename + add_build(cfg, p.esc(objfilename), "", "rc " .. p.esc(filecfg.relpath)) + end +end + +local function files_build(prj, cfg, toolset, toolset_name, pch_dependency, regular_file_dependencies, file_dependencies) + local objfiles = {} + tree.traverse(project.getsourcetree(prj), { + onleaf = function(node, depth) + local filecfg = fileconfig.getconfig(node, cfg) + local rule = p.global.getRuleForFile(node.name, prj.rules) + if fileconfig.hasCustomBuildRule(filecfg) then + custom_command_build(prj, cfg, filecfg, node.relpath, file_dependencies) + elseif rule then + local environ = table.shallowcopy(filecfg.environ) + + if rule.propertydefinition then + p.rule.prepareEnvironment(rule, environ, cfg) + p.rule.prepareEnvironment(rule, environ, filecfg) + end + local rulecfg = p.context.extent(rule, environ) + custom_command_build(prj, cfg, rulecfg, node.relpath, file_dependencies) + else + compile_file_build(cfg, filecfg, toolset, pch_dependency, regular_file_dependencies, objfiles) + end + end, + }, false, 1) + p.w("") + + return objfiles +end + +local function generated_files_build(cfg, generated_files, key) + local final_dependency = "" + if #generated_files > 0 then + p.w("# generated files") + add_build(cfg, "generated_files_" .. key, "", "phony" .. ninja.list(generated_files)) + final_dependency = " || generated_files_" .. key + end + return final_dependency +end + +-- generate project + config build file +function ninja.generateProjectCfg(cfg) + local oldGetDefaultSeparator = path.getDefaultSeparator + path.getDefaultSeparator = function() return "/" end + + local prj = cfg.project + local key = prj.name .. "_" .. cfg.buildcfg + -- TODO why premake doesn't provide default name always ? + local toolset_name = _OPTIONS.cc or cfg.toolset or ninja.getDefaultToolsetFromOs() + local toolset = p.tools[toolset_name] + + p.w("# project build file") + p.w("# generated with premake ninja") + p.w("") + + -- premake-ninja relies on scoped rules + -- and they were added in ninja v1.6 + p.w("ninja_required_version = 1.6") + p.w("") + + ---------------------------------------------------- figure out settings + local pch = nil + if toolset_name ~= "msc" then + pch = p.tools.gcc.getpch(cfg) + if pch then + pch = { + input = pch, + placeholder = project.getrelative(cfg.workspace, path.join(cfg.objdir, path.getname(pch))), + gch = project.getrelative(cfg.workspace, path.join(cfg.objdir, path.getname(pch) .. ".gch")) + } + end + end + + ---------------------------------------------------- write rules + p.w("# core rules for " .. cfg.name) + prebuild_rule(cfg) + postbuild_rule(cfg) + compilation_rules(cfg, toolset, toolset_name, pch) + custom_command_rule() + + ---------------------------------------------------- build all files + p.w("# build files") + + local pch_dependency = pch_build(cfg, pch) + + local generated_files = collect_generated_files(prj, cfg) + local file_dependencies = getFileDependencies(cfg) + local regular_file_dependencies = "" + if #generated_files > 0 then + regular_file_dependencies = " || generated_files_" .. key .. ninja.list(file_dependencies) + elseif #file_dependencies > 0 then + regular_file_dependencies = " ||" .. ninja.list(file_dependencies) + end + + local obj_dir = project.getrelative(cfg.workspace, cfg.objdir) + local objfiles = files_build(prj, cfg, toolset, toolset_name, pch_dependency, regular_file_dependencies, file_dependencies) + local final_dependency = generated_files_build(cfg, generated_files, key) + + ---------------------------------------------------- build final target + if #cfg.prebuildcommands > 0 or cfg.prebuildmessage then + p.w("# prebuild") + add_build(cfg, "prebuild_" .. get_key(cfg), "", "run_prebuild") + end + if #cfg.postbuildcommands > 0 or cfg.postbuildmessage then + p.w("# postbuild") + add_build(cfg, "postbuild_" .. get_key(cfg), "", "run_postbuild | " .. ninja.outputFilename(cfg)) + end + + -- we don't pass getlinks(cfg) through dependencies + -- because system libraries are often not in PATH so ninja can't find them + local libs = ninja.list(p.esc(config.getlinks(cfg, "siblings", "fullpath"))) + if cfg.kind == p.STATICLIB then + p.w("# link static lib") + add_build(cfg, p.esc(ninja.outputFilename(cfg)), "", "ar " .. table.concat(p.esc(objfiles), " ") .. libs .. final_dependency) + + elseif cfg.kind == p.SHAREDLIB then + local output = ninja.outputFilename(cfg) + p.w("# link shared lib") + + local extra_output = "" + if ninja.endsWith(output, ".dll") then + extra_output = " | " .. p.esc(ninja.noext(output, ".dll")) .. ".lib" .. " " .. p.esc(ninja.noext(output, ".dll")) .. ".exp" + elseif ninja.endsWith(output, ".so") then + extra_output = " | " .. p.esc(ninja.noext(output, ".so")) .. ".a" + elseif ninja.endsWith(output, ".dylib") then + -- in case of .dylib there are no corresponding .a file + else + p.error("unknown type of shared lib '" .. output .. "', so no idea what to do, sorry") + end + + add_build(cfg, p.esc(output), extra_output, "link " .. table.concat(p.esc(objfiles), " ") .. libs .. final_dependency) + + elseif (cfg.kind == p.CONSOLEAPP) or (cfg.kind == p.WINDOWEDAPP) then + p.w("# link executable") + add_build(cfg, p.esc(ninja.outputFilename(cfg)), "", "link " .. table.concat(p.esc(objfiles), " ") .. libs .. final_dependency) + + else + p.error("ninja action doesn't support this kind of target " .. cfg.kind) + end + + p.w("") + if #cfg.postbuildcommands > 0 or cfg.postbuildmessage then + add_build(cfg, key, "", "phony postbuild_" .. get_key(cfg)) + else + add_build(cfg, key, "", "phony " .. ninja.outputFilename(cfg)) + end + p.w("") + + path.getDefaultSeparator = oldGetDefaultSeparator +end + +-- return name of output binary relative to build folder +function ninja.outputFilename(cfg) + return project.getrelative(cfg.workspace, cfg.buildtarget.directory) .. "/" .. cfg.buildtarget.name +end + +-- return name of build file for configuration +function ninja.projectCfgFilename(cfg, relative) + if relative ~= nil then + relative = project.getrelative(cfg.workspace, cfg.location) .. "/" + else + relative = "" + end + + local ninjapath = relative .. "build_" .. cfg.project.name .. "_" .. cfg.buildcfg + + if cfg.platform ~= nil then ninjapath = ninjapath .. "_" .. cfg.platform end + + return ninjapath .. ".ninja" +end + +-- check if string starts with string +function ninja.startsWith(str, starts) + return str:sub(0, starts:len()) == starts +end + +-- check if string ends with string +function ninja.endsWith(str, ends) + return str:sub(-ends:len()) == ends +end + +-- removes extension from string +function ninja.noext(str, ext) + return str:sub(0, str:len() - ext:len()) +end + +-- generate all build files for every project configuration +function ninja.generateProject(prj) + if not p.action.supports(prj.kind) or prj.kind == p.NONE then + return + end + for cfg in project.eachconfig(prj) do + p.generate(cfg, ninja.projectCfgFilename(cfg), ninja.generateProjectCfg) + end +end + +include("_preload.lua") + +return ninja diff --git a/tools/ninja.exe b/tools/ninja.exe new file mode 100644 index 0000000..e4fafa0 Binary files /dev/null and b/tools/ninja.exe differ diff --git a/tools/ninja.linux b/tools/ninja.linux new file mode 100644 index 0000000..189832f Binary files /dev/null and b/tools/ninja.linux differ diff --git a/tools/ninja.osx b/tools/ninja.osx new file mode 100644 index 0000000..d26afc2 Binary files /dev/null and b/tools/ninja.osx differ diff --git a/tools/premake5.exe b/tools/premake5.exe new file mode 100644 index 0000000..1a637aa Binary files /dev/null and b/tools/premake5.exe differ diff --git a/tools/premake5.linux b/tools/premake5.linux new file mode 100644 index 0000000..f385c8c Binary files /dev/null and b/tools/premake5.linux differ diff --git a/tools/premake5.lua b/tools/premake5.lua new file mode 100644 index 0000000..8c44d65 --- /dev/null +++ b/tools/premake5.lua @@ -0,0 +1,112 @@ +require "ninja" + +-- workspace + +workspace "project" + configurations { "devel", "debug", "release" } + location "../_project" + targetdir "../_%{cfg.buildcfg}" + configuration "windows" + platforms { "x64" } + buildoptions "/W1" +-- entrypoint "main" + -- buildoptions { "/utf-8" } "/W3", "/wd4996", "/wd4244", "/wd4305", "/wd4267"} -- "/EHsc-"} + -- links { "user32", "gdi32" } + -- flags { "/MT" } + configuration "linux" + links { "pthread", "X11", "m", "dl" } + filter "configurations:debug" + symbols "On" + optimize "Off" + filter "configurations:devel" + symbols "On" + optimize "On" + filter "configurations:release" + defines {"NDEBUG"} + symbols "Off" + optimize "On" + +-- dlls + +project "engine" + language "C" + kind "SharedLib" + files {"../engine/fwk.c"} + includedirs {"../engine/"} + defines {"API=EXPORT"} + +-- exes + +project "editor" + language "C" + kind "ConsoleApp" -- "WindowedApp" + links {"engine"} defines {"API=IMPORT"} + files { + "../tools/editor/**.", + "../tools/editor/**.h*", + "../tools/editor/editor.c", + } + includedirs { + "../tools/editor/", + "../engine/", + } + +project "editor2" + language "C" + kind "ConsoleApp" + files { + "../tools/editor/**.", + "../tools/editor/**.h*", + "../tools/editor/editor2.c", + } + includedirs { + "../tools/editor/", + "../engine/", + } + +-- demos + +function demos(...) + for _, name in ipairs({...}) do + project (name) + -- defaults() + uuid (os.uuid(name)) + language "C" + kind "consoleApp" -- "WindowedApp" + includedirs {"../engine/"} + links {"engine"} defines {"API=IMPORT"} -- kind "SharedLib" links {"engine"} + includedirs {"../engine/", "../tools/editor/"} + files { + "../demos/*"..name.."*.h*", + "../demos/*"..name.."*.c*", + "../demos/*"..name.."*.inl", + } + end +end + +demos( + "00-hello","01-sprite","02-ddraw","03-character","04-control", + "04-scene","05-network","06-pbr" +) + +-- games + +function games(...) + for _, name in ipairs({...}) do + project ("game-" .. name) + uuid (os.uuid("game-" .. name)) + language "C++" + kind "consoleApp" + includedirs {"../engine/"} + links {"engine"} defines {"API=IMPORT"} -- kind "SharedLib" links {"engine"} + includedirs {"../engine/", "../tools/editor/"} + files { + "../games/"..name.."/**.h*", + "../games/"..name.."/**.c*", + "../games/"..name.."/**.inl", + } + -- defaults() + end +end + +-- games("untitled") diff --git a/tools/premake5.osx b/tools/premake5.osx new file mode 100644 index 0000000..04d2d82 Binary files /dev/null and b/tools/premake5.osx differ