From dd31c9b90df510d5642e79c736ef44fbbc99337a Mon Sep 17 00:00:00 2001 From: Daniel Santos <47725160+DanielSant0s@users.noreply.github.com> Date: Sun, 30 Jun 2024 14:22:55 -0700 Subject: [PATCH] V3 (#53) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Optimize Pads sample even more * Rework pads module to be a class * Add fast OBJ loader * Change custom OBJ parser to Fast OBJ loader * Add per-vertex color support using mtl * Make bounding box a fixed array * Implement support for multiple renderes and offline buffer allocation * Renderer: Assign to white if no material * Add multitexturing support (up to 10 textures per model) * Add a new render sample * Update modules descriptions on README * Add font render class * Fix compilation (#43) * Emergency fix * [makefile] migrate from bin2s to bin2c (#42) * [makefile] migrate from bin2s to bin2c * Update .gitignore * Update embed.make * Change some socket funcs --------- Co-authored-by: Matías Israelson <57065102+israpps@users.noreply.github.com> * Use stat to get dir entry size * Improve displayFunc functionality * Add fast math functions * Update compilation.yml * Add vector basics * Add second controller support * Add more pad types support * Add pad button events * general path fixes * feat: rework our 3D renderer to work under VU1 (#45) * Add initial samples for VU1 * Add initial VU1 support * Add batch logic to VU1 code * Add workaround for waiting VU1 * Add printf again * Add flush at the code start * refactor: separate functions * Change to use OpenVCL * feat: add initial support for multiple mpgs * Use VCL macros * Add VU0 routines for lights * change mpg name * Add initial pipeline thing * Do some general cleaning * feat: diffuse and ambient lights * perf: add normal math on VU1 * use dynamic batches * feat: add no texture pipeline * remove unused members * feat: multitexturing on VU1 * clean temporary functions * add linear filter support * improve texture code readability * improve bounding box calculation * add multiple pipeline support * add only color pipeline * put obj in a class * Add non-textured pipelines * Update render.js * change to not use image objects on render * expose vertex access * Add pipeline descriptors * Add Render docs * add screen docs * add pads docs * Update README.md * Improve 3D camera * optimize obj memory usage * add optimize, size and bpp to image class * add new image docs * change render objects to hold js textures * add get texture for render objects * Update README.md * optimize grayscale png loading * expose vertex quantity * Update README.md * Add palette and pixels changing on Image * add alpha support for models * Update README.md * fix file manager * remove athena cli * fix orbit camera * add new camera functions * change addvector to vu0 * add vector3 lib * expose bounding boxes * implement some 3d collision * clean old unused code * remove texture quantity limits for RenderObject * remove fish model * remove getFreeMemory * remove leftover memory stuff * save some kb with static functions * remove vector leftovers * Improve script post-run * change some error types * change athena error font * dont preload fonts at start * quantize dash icons * optimize dash memory usage * Add athena_restart for restart the sound without needed to reload, Add a workaround when sound end to just pause (to avoid stop working) (#48) * Testing * Try to never end wav reprodution (just pause) * Implement restart on ath_sound, for restart a sound without reloading * Fix build * Fix build and rename function * Implement athena_restart for ogg, fix arg count for athena_restart * Introduze 'athena_get_position', 'athena_set_position', rename 'athena_duration' for 'athena_get_duration' (#49) * Introduze 'athena_get_position', 'athena_set_position', rename 'athena_duration' for 'athena_get_duration' * Update Music Player, Now uses the new functions * Use resume instead of play, and only only go forward or backwards if sound is playing * feat: add specular for light data * fix: update for new GCC * fix: process exceptions from callbacks * feat: strip redundant functions/consts --------- Co-authored-by: Matías Israelson <57065102+israpps@users.noreply.github.com> Co-authored-by: KreitinnSoftware <80591934+KreitinnSoftware@users.noreply.github.com> --- .github/workflows/compilation.yml | 2 - .gitignore | 3 +- Makefile | 54 +- README.md | 143 +- bin/3dcollision.js | 97 + bin/app_filemgr.js | 57 +- bin/app_store.js | 44 +- bin/async_request.js | 30 + bin/cell.js | 75 +- bin/cell_icon.png | Bin 7249 -> 4453 bytes bin/cursor/pointer.png | Bin 2704 -> 0 bytes bin/frontend.js | 8 + bin/lfm_icon.png | Bin 4468 -> 2348 bytes bin/main.js | 38 +- bin/no_icon.png | Bin 3324 -> 2409 bytes bin/pads.js | 66 +- bin/pads_icon.png | Bin 4595 -> 3107 bytes bin/playground.js | 28 + bin/pong.js | 12 +- bin/render.js | 147 +- bin/render/Boombox.mtl | 13 + bin/render/Boombox.obj | 3203 ++++++++++++++++++ bin/render/Boombox_BaseColor.png | Bin 0 -> 83101 bytes bin/render/Car.mtl | 82 + bin/render/Car.obj | 4713 +++++++++++++++++++++++++++ bin/render/cubes.mtl | 32 + bin/render/cubes.obj | 132 + bin/render/tex1.png | Bin 0 -> 11668 bytes bin/render/tex2.png | Bin 0 -> 22867 bytes bin/render_icon.png | Bin 3393 -> 2271 bytes bin/request.js | 27 +- bin/sap_icon.png | Bin 3089 -> 2423 bytes bin/sound.js | 85 +- bin/task.js | 2 +- bin/terminal.js | 288 -- bin/text | 1 - bin/usr/bin/apm.js | 41 - bin/usr/bin/cat.js | 1 - bin/usr/bin/cd.js | 11 - bin/usr/bin/clear.js | 1 - bin/usr/bin/exit.js | 1 - bin/usr/bin/ls.js | 3 - bin/usr/bin/mv.js | 1 - bin/usr/bin/neofetch.js | 39 - embed.make | 60 +- src/.DS_Store | Bin 8196 -> 0 bytes src/ath_archive.c | 3 + src/ath_cli.c | 101 - src/ath_env.c | 93 +- src/ath_env.h | 13 +- src/ath_font.c | 123 +- src/ath_image.c | 95 +- src/ath_network.c | 19 +- src/ath_pads.c | 345 +- src/ath_physics.c | 287 ++ src/ath_render.c | 670 +++- src/ath_screen.c | 24 +- src/ath_socket.c | 15 +- src/ath_sound.c | 27 +- src/ath_system.c | 198 +- src/ath_vector.c | 643 ++++ src/athena_math.c | 251 ++ src/calc_3d.c | 692 +++- src/draw_3D.vcl | 139 + src/draw_3D.vsm | 75 + src/draw_3D_colors.vcl | 143 + src/draw_3D_colors.vsm | 63 + src/draw_3D_colors_notex.vcl | 124 + src/draw_3D_colors_notex.vsm | 59 + src/draw_3D_lights.vcl | 200 ++ src/draw_3D_lights.vsm | 123 + src/draw_3D_lights_notex.vcl | 181 + src/draw_3D_lights_notex.vsm | 114 + src/draw_3D_notex.vcl | 120 + src/draw_3D_notex.vsm | 50 + src/ee_tools.c | 4 +- src/fast_obj/.gitignore | 2 + src/fast_obj/CMakeLists.txt | 18 + src/fast_obj/LICENSE | 21 + src/fast_obj/README.md | 24 + src/fast_obj/fast_obj.c | 28 + src/fast_obj/fast_obj.h | 1526 +++++++++ src/fast_obj/test/test.cpp | 181 + src/fast_obj/test/tiny_obj_loader.h | 2926 +++++++++++++++++ src/fntsys.c | 2 +- src/graphics.c | 113 +- src/include/athena_math.h | 13 + src/include/graphics.h | 30 +- src/include/network.h | 7 +- src/include/render.h | 103 +- src/include/sound.h | 3 + src/include/system.h | 5 - src/main.c | 116 +- src/network.c | 8 +- src/pad.c | 29 +- src/quickjs/quickjs-libc.c | 260 +- src/quickjs/quickjs-libc.h | 34 +- src/quickjs/quickjs.c | 27 +- src/quickjs/quickjs.h | 4 + src/render.c | 1687 ++++++---- src/sioprintf.c | 1 + src/sound.c | 111 +- src/system.c | 136 - src/taskman.c | 2 +- src/vcl_sml.i | 974 ++++++ src/vif.c | 75 + src/vif.h | 110 + 107 files changed, 20869 insertions(+), 2241 deletions(-) create mode 100644 bin/3dcollision.js create mode 100644 bin/async_request.js delete mode 100644 bin/cursor/pointer.png create mode 100644 bin/frontend.js create mode 100644 bin/playground.js create mode 100644 bin/render/Boombox.mtl create mode 100644 bin/render/Boombox.obj create mode 100644 bin/render/Boombox_BaseColor.png create mode 100644 bin/render/Car.mtl create mode 100644 bin/render/Car.obj create mode 100644 bin/render/cubes.mtl create mode 100644 bin/render/cubes.obj create mode 100644 bin/render/tex1.png create mode 100644 bin/render/tex2.png delete mode 100644 bin/terminal.js delete mode 100644 bin/text delete mode 100644 bin/usr/bin/apm.js delete mode 100644 bin/usr/bin/cat.js delete mode 100644 bin/usr/bin/cd.js delete mode 100644 bin/usr/bin/clear.js delete mode 100644 bin/usr/bin/exit.js delete mode 100644 bin/usr/bin/ls.js delete mode 100644 bin/usr/bin/mv.js delete mode 100644 bin/usr/bin/neofetch.js delete mode 100644 src/.DS_Store delete mode 100644 src/ath_cli.c create mode 100644 src/ath_physics.c create mode 100644 src/ath_vector.c create mode 100644 src/athena_math.c create mode 100644 src/draw_3D.vcl create mode 100644 src/draw_3D.vsm create mode 100644 src/draw_3D_colors.vcl create mode 100644 src/draw_3D_colors.vsm create mode 100644 src/draw_3D_colors_notex.vcl create mode 100644 src/draw_3D_colors_notex.vsm create mode 100644 src/draw_3D_lights.vcl create mode 100644 src/draw_3D_lights.vsm create mode 100644 src/draw_3D_lights_notex.vcl create mode 100644 src/draw_3D_lights_notex.vsm create mode 100644 src/draw_3D_notex.vcl create mode 100644 src/draw_3D_notex.vsm create mode 100644 src/fast_obj/.gitignore create mode 100644 src/fast_obj/CMakeLists.txt create mode 100644 src/fast_obj/LICENSE create mode 100644 src/fast_obj/README.md create mode 100644 src/fast_obj/fast_obj.c create mode 100644 src/fast_obj/fast_obj.h create mode 100644 src/fast_obj/test/test.cpp create mode 100644 src/fast_obj/test/tiny_obj_loader.h create mode 100644 src/include/athena_math.h create mode 100644 src/vcl_sml.i create mode 100644 src/vif.c create mode 100644 src/vif.h diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index ff235bc..0ce3bfa 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -31,8 +31,6 @@ jobs: - name: Compile project run: | make clean - make CLI=1 - make clean make - name: Get short SHA diff --git a/.gitignore b/.gitignore index 9a799c4..ef99dea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ src/main.js.c .vscode/settings.json obj/ -asm/ \ No newline at end of file +embed/ +bin/brew_data.json diff --git a/Makefile b/Makefile index 22009f1..77062d1 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ EE_BIN_PKD = athena_pkd EE_SRC_DIR = src/ EE_OBJS_DIR = obj/ -EE_ASM_DIR = asm/ +EE_ASM_DIR = embed/ RESET_IOP ?= 1 DEBUG ?= 0 @@ -49,13 +49,13 @@ KEYBOARD ?= 1 MOUSE ?= 1 CAMERA ?= 0 -EE_LIBS = -L$(PS2SDK)/ports/lib -L$(PS2DEV)/gsKit/lib/ -Lmodules/ds34bt/ee/ -Lmodules/ds34usb/ee/ -lmc -lpad -laudsrv -lpatches -ldebug -lmath3d -ljpeg -lfreetype -lgskit_toolkit -lgskit -ldmakit -lpng -lz -lds34bt -lds34usb -lnetman -lps2ip -lcurl -lwolfssl -lkbd -lmouse -lvorbisfile -lvorbis -logg -llzma -lzip -lfileXio -lelf-loader-nocolour +EE_LIBS = -L$(PS2SDK)/ports/lib -L$(PS2DEV)/gsKit/lib/ -Lmodules/ds34bt/ee/ -Lmodules/ds34usb/ee/ -lmc -lpad -laudsrv -lpatches -ldebug -lmath3d -ljpeg -lfreetype -lgskit_toolkit -lgskit -ldmakit -lpng -lz -lds34bt -lds34usb -lnetman -lps2ip -lcurl -lwolfssl -lkbd -lmouse -lvorbisfile -lvorbis -logg -llzma -lzip -lfileXio -lelf-loader-nocolour -lerl EE_INCS += -I$(PS2DEV)/gsKit/include -I$(PS2SDK)/ports/include -I$(PS2SDK)/ports/include/freetype2 -I$(PS2SDK)/ports/include/zlib EE_INCS += -Imodules/ds34bt/ee -Imodules/ds34usb/ee -EE_CFLAGS += -Wno-sign-compare -fno-strict-aliasing -fno-exceptions -DCONFIG_VERSION=\"$(shell cat VERSION)\" -D__TM_GMTOFF=tm_gmtoff -DPATH_MAX=256 -DPS2 +EE_CFLAGS += -Wno-sign-compare -fno-strict-aliasing -fno-exceptions -fpermissive -DCONFIG_VERSION=\"$(shell cat VERSION)\" -D__TM_GMTOFF=tm_gmtoff -DPATH_MAX=256 -DPS2 ifeq ($(RESET_IOP),1) EE_CFLAGS += -DRESET_IOP endif @@ -64,16 +64,19 @@ ifeq ($(DEBUG),1) EE_CFLAGS += -DDEBUG endif -BIN2S = $(PS2SDK)/bin/bin2s +BIN2S = $(PS2SDK)/bin/bin2c +EE_DVP = dvp-as +EE_VCL = vcl +EE_VCLPP = vclpp EXT_LIBS = modules/ds34usb/ee/libds34usb.a modules/ds34bt/ee/libds34bt.a JS_CORE = quickjs/cutils.o quickjs/libbf.o quickjs/libregexp.o quickjs/libunicode.o \ quickjs/realpath.o quickjs/quickjs.o quickjs/quickjs-libc.o -APP_CORE = main.o memory.o ee_tools.o module_system.o taskman.o pad.o system.o strUtils.o +APP_CORE = main.o vif.o draw_3D_colors.o draw_3D_colors_notex.o draw_3D.o draw_3D_notex.o draw_3D_lights.o draw_3D_lights_notex.o athena_math.o memory.o ee_tools.o module_system.o taskman.o pad.o system.o strUtils.o -ATHENA_MODULES = ath_env.o ath_pads.o ath_system.o ath_archive.o ath_timer.o ath_task.o +ATHENA_MODULES = ath_env.o ath_physics.o ath_vector.o ath_pads.o ath_system.o ath_archive.o ath_timer.o ath_task.o IOP_MODULES = iomanx.o filexio.o sio2man.o mcman.o mcserv.o padman.o \ usbd.o bdm.o bdmfs_fatfs.o usbmass_bd.o cdfs.o ds34bt.o \ @@ -82,17 +85,9 @@ IOP_MODULES = iomanx.o filexio.o sio2man.o mcman.o mcserv.o padman.o \ EMBEDDED_ASSETS = quicksand_regular.o -ifeq ($(CLI),1) - EE_BIN := $(EE_BIN)_cli - EE_BIN_PKD := $(EE_BIN_PKD)_cli - EE_CFLAGS += -DATHENA_CLI - ATHENA_MODULES += ath_cli.o - GRAPHICS = 0 -endif - ifeq ($(GRAPHICS),1) EE_CFLAGS += -DATHENA_GRAPHICS - APP_CORE += graphics.o atlas.o fntsys.o render.o calc_3d.o + APP_CORE += graphics.o atlas.o fntsys.o render.o calc_3d.o fast_obj/fast_obj.o ATHENA_MODULES += ath_color.o ath_font.o ath_render.o ath_screen.o ath_image.o ath_imagelist.o ath_shape.o endif @@ -154,11 +149,13 @@ all: $(EXT_LIBS) $(EE_BIN) $(EE_ASM_DIR) $(EE_OBJS_DIR) echo "Building $(EE_BIN)..." $(EE_STRIP) $(EE_BIN) - echo "Compressing $(EE_BIN_PKD)...\n" - ps2-packer $(EE_BIN) $(EE_BIN_PKD) > /dev/null +# echo "Compressing $(EE_BIN_PKD)...\n" +# ps2-packer $(EE_BIN) $(EE_BIN_PKD) > /dev/null mv $(EE_BIN) bin/ - mv $(EE_BIN_PKD) bin/ +# mv $(EE_BIN_PKD) bin/ + +#mpgs: src/draw_3D.vsm src/draw_3D_notex.vsm src/draw_3D_colors.vsm src/draw_3D_colors_notex.vsm src/draw_3D_lights.vsm src/draw_3D_lights_notex.vsm debug: $(EXT_LIBS) $(EE_BIN) echo "Building $(EE_BIN) with debug symbols..." @@ -192,7 +189,22 @@ $(EE_OBJS_DIR)%.o: $(EE_SRC_DIR)%.c | $(EE_OBJS_DIR) $(DIR_GUARD) $(EE_CC) $(EE_CFLAGS) $(EE_INCS) -c $< -o $@ -$(EE_OBJS_DIR)%.o: $(EE_ASM_DIR)%.s | $(EE_OBJS_DIR) - @echo AS - $< +$(EE_OBJS_DIR)%.o: $(EE_SRC_DIR)%.vsm | $(EE_OBJS_DIR) + @echo DVP - $< + $(DIR_GUARD) + $(EE_DVP) $< -o $@ + +$(EE_SRC_DIR)%.vcl: $(EE_SRC_DIR)%.vclpp | $(EE_SRC_DIR) + @echo VCLPP - $< $(DIR_GUARD) - $(EE_AS) $(EE_ASFLAGS) $< -o $@ + $(EE_VCLPP) $< $@.vcl + +$(EE_SRC_DIR)%.vsm: $(EE_SRC_DIR)%.vcl | $(EE_SRC_DIR) + @echo VCL - $< + $(DIR_GUARD) + $(EE_VCL) -Isrc -g -o$@ $< + +$(EE_OBJS_DIR)%.o: $(EE_ASM_DIR)%.c | $(EE_OBJS_DIR) + @echo BIN2C - $< + $(DIR_GUARD) + $(EE_CC) $(EE_CFLAGS) $(EE_INCS) -c $< -o $@ diff --git a/README.md b/README.md index ad89905..8dd9cd9 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ AthenaEnv is a project that seeks to facilitate and at the same time brings a co * Image: Image drawing. * ImageList: Load and manage multiple images while your code is running, multithreaded loading! * Draw: Shape drawing, triangles, circles etc. -* Render: Basic 3D support. +* Render: Basic 3D support powered by a VU1 renderer. * Screen: The entire screen of your project (2D and 3D), being able to change the resolution, enable or disable parameters. * Font: Functions that control the texts that appear on the screen, loading texts, drawing and unloading from memory. * Pads: Above being able to draw and everything else, A human interface is important. Supports rumble and pressure sensitivity. @@ -177,6 +177,7 @@ The std module provides wrappers to the libc stdlib.h and stdio.h and a few othe * std.evalScript(str, options = undefined) - Evaluate the string str as a script (global eval). options is an optional object containing the following optional properties: • std.backtrace_barrier - Boolean (default = false). If true, error backtraces do not list the stack frames below the evalScript. * std.loadScript(filename) - Evaluate the file filename as a script (global eval). +* let hasfile = std.exists(filename) - Returns a bool that determines whether the file exists or not. * let fstr = std.loadFile(filename) - Load the file filename and return it as a string assuming UTF-8 encoding. Return null in case of I/O error. * let file = std.open(filename, flags, errorObj = undefined) - Open a file (wrapper to the libc fopen()). Return the FILE object or null in case of I/O error. If errorObj is not undefined, set its errno property to the error code or to 0 if no error occured. * std.fdopen(fd, flags, errorObj = undefined) - Open a file from a file handle (wrapper to the libc fdopen()). Return the FILE object or null in case of I/O error. If errorObj is not undefined, set its errno property to the error code or to 0 if no error occured. @@ -330,11 +331,17 @@ Properties: * endx, endy - End of the area that will be drawn from the image, the default value is the original image size. * angle - Define image rotation angle, default value is 0.0. * color - Define image tinting, default value is Color.new(255, 255, 255, 128). -* filter - Choose between **LINEAR** or **NEAREST**, default value is NEAREST. +* filter - Choose between **LINEAR** or **NEAREST**, default value is NEAREST. +* size - Returns image real size occupied in memory. +* bpp - Returns image bits per-pixel qantity. +* delayed - If true, your texture was loaded in RAM, else, VRAM. +* pixels - The image pixel ArrayBuffer. +* palette - If is a palette image, it has a palette ArrayBuffer right here. Methods: * draw(x, y) - Draw loaded image onscreen(call it every frame). Example: image.draw(15.0, 100.0); +* optimize() - If your image has 24 bits per-pixel (aka RGB), you can use this to make it 16 bits per-pixel, saving some memory! * ready() - Returns true if an asynchronous image was successfully loaded in memory. ```js var loaded = image.ready(); @@ -375,27 +382,63 @@ canvas.psmz = Z16S; Screen.setMode(canvas); ``` -* Render.init(aspect) - Initializes rendering routines. *default aspect is 4/3, widescreen is 16/9 -* var model = Render.loadOBJ(path, *texture*) - Load simple obj 3d data files. MTL not supported yet. Actually it only supports a single texture that you have to load using Image class and pass as a second argument if you want to use it. -* Render.drawOBJ(model, pos_x, pos_y, pos_z, rot_x, rot_y, rot_z) - Draws the loaded OBJ on the screen. -* Render.freeOBJ(model) - Frees the model from memory. +* Render.setView(aspect, *fov*) - Initializes rendering routines. *default aspect is 4/3, widescreen is 16/9. FOV isn't mandatory, default: 0.2 +* Render.vertex(x, y, z, n1, n2, n3, s, t, r, g, b, a) - Returns a vertex to build a 3D mesh. It should be used to create vertex arrays. + • x, y, z - Vertex position on 3D world. + • n1, n2, n3 - Vertex normal. + • s, t - Vertex texture coordinates. + • r, g, b, a - Vertex color. + +### RenderObject module + +Construction: +```js +var model = new RenderObject(mesh, *texture*) +/* Load simple WaveFront OBJ files or vertex arrays. +MTL is supported on OBJs (including per-vertex colors and multi-texturing). +If you don't have a MTL file but you want to bind a texture on it, +just pass the image as a second argument if you want to use it. */ +``` +Methods: + +* draw(pos_x, pos_y, pos_z, rot_x, rot_y, rot_z) - Draws the object on screen. +* drawBounds(pos_x, pos_y, pos_z, rot_x, rot_y, rot_z) - Draws object bounding box. +* getTexture(id) - Gets the nth texture object from the model. +* setTexture(id, texture, *range*) - Changes or sets the nth texture on models. +* getPipeline() - Returns the current rendering pipeline loaded for the model. +* setPipeline(pipeline) - Sets the current pipeline for the model. Available pipelines: + • Render.PL_NO_LIGHTS_COLORS - Colors and lights disabled. + • Render.PL_NO_LIGHTS_COLORS_TEX - Colors, lights and textures disabled. + • Render.PL_NO_LIGHTS - Lights disabled, colors still working. + • Render.PL_NO_LIGHTS_TEX - Textures and lights disabled, colors still working. + • Render.PL_DEFAULT - Default for textured models. Lights and colors enabled. + • Render.PL_DEFAULT_NO_TEX - Default for non-textured models. Lights and colors enabled. + +Properties: + +* vertices - A Render.vertex array that can be modified and read. +* size - Vertex quantity. + **Camera** * Camera.position(x, y, z) * Camera.rotation(x, y, z) **Lights** -* Lights.create(count) -* Lights.set(light, dir_x, dir_y, dir_z, r, g, b, type) - • Avaiable light types: AMBIENT, DIRECTIONAL +You have 4 lights to use in 3D scenes, use set to configure them. +* Lights.set(id, attribute, x, y, z) + • Avaiable light attributes: Lights.DIRECTION, Lights.AMBIENT, Lights.DIFFUSE + ### Screen module -* Screen.clear(*color*) - Clears screen with the specified color. If you don't specify any argument, it will use black as default. -* Screen.flip() - Run the render queue and jump to the next frame, i.e.: Updates your screen. -* var freevram = Screen.getFreeVRAM() - Returns the total of free Video Memory. -* Screen.setVSync(bool) - Toggles VSync, which makes the framerate stable in 15, 30, 60(depending on the mode) on screen. -* Screen.setFrameCounter(bool) - Toggles frame counting and FPS collecting. -* Screen.waitVblankStart() - Waits for a vertical sync. +* Screen.display(func) - Makes the specified function behave like a main loop, when you don't need to clear or flip the screen because it's done automatically. +* Screen.clearColor(*color*) - Sets a constant clear color for Screen.display function. +* Screen.clear(*color*) - Clears screen with the specified color. If you don't specify any argument, it will use black as default. +* Screen.flip() - Run the render queue and jump to the next frame, i.e.: Updates your screen. +* var freevram = Screen.getFreeVRAM() - Returns the total of free Video Memory. +* Screen.setVSync(bool) - Toggles VSync, which makes the framerate stable in 15, 30, 60(depending on the mode) on screen. +* Screen.setFrameCounter(bool) - Toggles frame counting and FPS collecting. +* Screen.waitVblankStart() - Waits for a vertical sync. * var fps = Screen.getFPS(frame_interval) - Get Frames per second measure within the specified frame_interval in msec. Dependant on Screen.setFrameCounter(true) to work. * const canvas = Screen.getMode() - Get actual video mode parameters. Returns an object. • canvas.mode - Available modes: NTSC, DTV_480p, PAL, DTV_576p, DTV_720p, DTV_1080i. @@ -427,27 +470,11 @@ Properties: * scale - Proportional scale, default: 1.0f Methods: -* print(x, y, text) - Draw text on screen(call it every frame). Example: font.print(10.0, 10.0, "Hello world!)); +* print(x, y, text) - Draw text on screen(call it every frame). Example: font.print(10.0, 10.0, "Hello world!); * getTextSize(text) - Returns text absolute size in pixels (width, height). Example: const size = font.getTextSize("Hello world!"); ### Pads module -* var pad = Pads.get(*port*) - Returns a pad object containing the following properties: - • pad.btns - Buttons - • pad.lx - Left analog horizontal position (left = -127, default = 0, right = 128) - • pad.ly - Left analog vertical position (up = -127, default = 0, down = 128) - • pad.rx - Right analog horizontal position (left = -127, default = 0, right = 128) - • pad.ry - Right analog vertical position (up = -127, default = 0, down = 128) - - ![analog_graph](https://user-images.githubusercontent.com/47725160/154816009-99d7e5da-badf-409b-9a3b-3618fd372f09.png) - -* var type = Pads.getType(*port*) - Gets gamepad type in the specified port. - • Pads.DIGITAL - • Pads.ANALOG - • Pads.DUALSHOCK -* var press = Pads.getPressure(*port*, button) - Get button pressure level. -* Pads.rumble(port, big, small) - Rumble your gamepad. -* var ret = Pads.check(pad, button) - Check if the button was pressed on the specified pad. * Buttons list: • Pads.SELECT • Pads.START @@ -465,6 +492,38 @@ Methods: • Pads.R2 • Pads.L3 • Pads.R3 + +* var pad = Pads.get(*port*) - Returns a pad object: +Properties: + • pad.btns - Button state on the current check. + • pad.old_btns = Button state on the last check. + • pad.lx - Left analog horizontal position (left = -127, default = 0, right = 128). + • pad.ly - Left analog vertical position (up = -127, default = 0, down = 128). + • pad.rx - Right analog horizontal position (left = -127, default = 0, right = 128). + • pad.ry - Right analog vertical position (up = -127, default = 0, down = 128). + + ![analog_graph](https://user-images.githubusercontent.com/47725160/154816009-99d7e5da-badf-409b-9a3b-3618fd372f09.png) + +Methods: + • update() - Updates all pads pressed and stick positions data. + • pressed(button) - Checks if a button is being pressed (continuously). + • justPressed(button) - Checks if a button was pressed only once. + • setEventHandler() - Sets the pad object to listen events defined by Pads.newEvent, so it doesn't need to be updated. + +* let event_id = Pads.newEvent(button, kind, function) - Creates an asynchronous pad event, returns the event id. Remember to set the pad object event handler first! +* Pad event kinds: + • Pads.PRESSED + • Pads.JUST_PRESSED + • Pads.NON_PRESSED +* Pads.deleteEvent(event_id) - Deletes the event created by Pads.newEvent. +* let type = Pads.getType(*port*) - Gets gamepad type in the specified port. +* Pad Types: + • Pads.DIGITAL + • Pads.ANALOG + • Pads.DUALSHOCK + +* let press = Pads.getPressure(*port*, button) - Get button pressure level. +* Pads.rumble(port, big, small) - Rumble your gamepad. ### Keyboard module * Keyboard.init() - Initialize keyboard routines. @@ -489,35 +548,15 @@ Methods: ### System module -* var fd = System.openFile(path, type) -* Types list: - • System.FREAD - • System.FWRITE - • System.FCREATE - • System.FRDWR -* var buffer = System.readFile(file, size) -* System.writeFile(fd, data, size) -* System.closeFile(fd) -* System.seekFile(fd, pos, type) -* Types list: - • System.SET - • System.CUR - • System.END -* var size = System.sizeFile(fd) -* System.doesFileExist(path) -* System.CurrentDirectory(path) *if path given, it sets the current dir, else it gets the current dir * var listdir = System.listDir(*path*) • listdir[index].name - return file name on indicated index(string) • listdir[index].size - return file size on indicated index(integer) • listdir[index].directory - return if indicated index is a file or a directory(bool) -* System.createDirectory(path) * System.removeDirectory(path) -* System.removeFile(path) * System.copyFile(source, dest) * System.moveFile(source, dest) * System.rename(source, dest) * System.sleep(sec) -* var freemem = System.getFreeMemory() * System.exitToBrowser() * System.setDarkMode(value) * var temps = System.getTemperature() // It only works with SCPH-500XX and later models. diff --git a/bin/3dcollision.js b/bin/3dcollision.js new file mode 100644 index 0000000..c4c3fb3 --- /dev/null +++ b/bin/3dcollision.js @@ -0,0 +1,97 @@ +// {"name": "3D collision", "author": "Daniel Santos", "version": "04072023", "icon": "render_icon.png", "file": "3dcollision.js"} + +let fntcpy = new Font("default"); +fntcpy.scale = (0.4f); + +const canvas = Screen.getMode(); + +canvas.zbuffering = true; +canvas.psmz = Z16S; + +Screen.setMode(canvas); + +Render.setView(4/3); + +Screen.setFrameCounter(true); +Screen.setVSync(false); + +// Change your root folder to "render" so we can work with file path magic :p +os.chdir("render"); + +let dragontex = new Image("dragon.png"); +let dragonmesh = new RenderObject("dragon.obj", dragontex); +let dragonbounds = Physics.createBox(4, 4, 4); + +let car = new RenderObject("Car.obj"); +let carbounds = Physics.createBox(2, 2, 2); + +Camera.position(0.0f, 0.0f, 40.0f); +Camera.type(Camera.LOOKAT); + +Lights.set(0, Lights.DIRECTION, 0.0, 1.0, -1.0); +Lights.set(0, Lights.DIFFUSE, 0.8, 0.8, 0.8); + +Lights.set(1, Lights.DIRECTION, 0.0, 1.0, 1.0); +Lights.set(1, Lights.DIFFUSE, 0.4, 0.0, 0.8); + +let pad = Pads.get(); + +let lx = null; +let ly = null; +let rx = null; +let ry = null; + +let savedlx = 0.0f; +let savedly = 0.0f; +let savedrx = 0.0f; +let savedry = 0.0f; + +const gray = Color.new(40, 40, 40, 128); + +let bbox = false; + +let collision = undefined; + +while(true){ + Screen.clear(gray); + Camera.update(); + pad.update(); + + lx = ((pad.lx > 25 || pad.lx < -25)? pad.lx : 0) / 1024.0f; + ly = ((pad.ly > 25 || pad.ly < -25)? pad.ly : 0) / 1024.0f; + savedlx = savedlx - lx; + savedly = savedly - ly; + + rx = ((pad.rx > 25 || pad.rx < -25)? pad.rx : 0) / 10240.0f; + ry = ((pad.ry > 25 || pad.ry < -25)? pad.ry : 0) / 10240.0f; + savedrx = savedrx - rx; + savedry = savedry - ry; + + if(pad.justPressed(Pads.TRIANGLE)) { + System.loadELF(System.boot_path + "/athena.elf"); + } + + if(pad.justPressed(Pads.SQUARE)) { + bbox ^= 1; + } + + collision = Physics.boxBoxCollide(dragonbounds, savedrx, savedry, 25.0f, carbounds, 0.0f, 0.0f, 25.0f); + + Camera.target(savedrx, savedry, 25.0f); + + car.draw(savedrx, savedry, 25.0f, 3.14f, 0.0f, 0.0f); + if (collision) { + car.drawBounds(savedrx, savedry, 25.0f, 3.14f, 0.0f, 0.0f, Color.new(0, 255, 0)); + } else { + car.drawBounds(savedrx, savedry, 25.0f, 3.14f, 0.0f, 0.0f, Color.new(255, 0, 0)); + } + + dragonmesh.draw(0.0f, 0.0f, 25.0f, 3.14f, 0.0f, 0.0f); + + dragonmesh.drawBounds(0.0f, 0.0f, 25.0f, 3.14f, 0.0f, 0.0f, Color.new(128, 0, 255)); + + fntcpy.print(10, 10, Screen.getFPS(360) + " FPS | " + `RAM: ${Math.floor(System.getMemoryStats().used / 1048576)}MB` + + ` | Collision: ${JSON.stringify(collision)}`); + + Screen.flip(); +} \ No newline at end of file diff --git a/bin/app_filemgr.js b/bin/app_filemgr.js index ca2433b..61a2b43 100644 --- a/bin/app_filemgr.js +++ b/bin/app_filemgr.js @@ -1,8 +1,8 @@ // {"name": "File manager", "author": "Daniel Santos", "version": "04072023", "icon": "lfm_icon.png", "file": "app_filemgr.js"} -var font = new Font(); +var font = new Font("default"); -let boot_path = System.currentDir(); +font.scale = 0.55f; function getStringSize(string, scale){ return string.length * (scale*15); @@ -17,8 +17,8 @@ function range(end) { }; function printCentered(x, y, scale, string){ - font.scale = scale; - font.print(x-(getStringSize(string, scale)/2), y, string); + //font.scale = scale; + font.print(x-(getStringSize(string, scale)/2), y-12, string); }; @@ -29,10 +29,10 @@ function process_list_commands(control_var, list){ if (control_var < 0){ control_var = list.length-1; } - if (Pads.check(pad, Pads.DOWN) && !Pads.check(oldpad, Pads.DOWN)){ + if (pad.justPressed(Pads.DOWN)){ control_var++; } - if (Pads.check(pad, Pads.UP) && !Pads.check(oldpad, Pads.UP)){ + if (pad.justPressed(Pads.UP)){ control_var--; } return control_var; @@ -61,16 +61,16 @@ function process_matrix_commands(x_var, y_var, x_limit, y_limit){ x_var = x_limit-1; y_var = y_limit-1; } - if (Pads.check(pad, Pads.RIGHT) && !Pads.check(oldpad, Pads.RIGHT)){ + if (pad.justPressed(Pads.RIGHT)){ x_var++; } - if (Pads.check(pad, Pads.LEFT) && !Pads.check(oldpad, Pads.LEFT)){ + if (pad.justPressed(Pads.LEFT)){ x_var--; } - if (Pads.check(pad, Pads.DOWN) && !Pads.check(oldpad, Pads.DOWN)){ + if (pad.justPressed(Pads.DOWN)){ y_var++; } - if (Pads.check(pad, Pads.UP) && !Pads.check(oldpad, Pads.UP)){ + if (pad.justPressed(Pads.UP)){ y_var--; } return [x_var, y_var]; @@ -166,7 +166,7 @@ file_manager.process = function() { } } - if ((Pads.check(pad, Pads.TRIANGLE) && !Pads.check(oldpad, Pads.TRIANGLE))){ + if ((pad.justPressed(Pads.TRIANGLE))){ var idxof = path.lastIndexOf("/"); if(path[idxof-1] != ":" && idxof != -1){ path = path.slice(0, idxof); @@ -179,7 +179,7 @@ file_manager.process = function() { } } - if ((Pads.check(pad, Pads.CROSS) && !Pads.check(oldpad, Pads.CROSS))){ + if ((pad.justPressed(Pads.CROSS))){ if(file_manager.data[3] == 1){ if(file[file_manager.data[0]].dir){ if(path == ""){ @@ -190,8 +190,7 @@ file_manager.process = function() { file = System.listDir(path); } else if(file[file_manager.data[0]].name.endsWith(".js")){ - System.currentDir(path + "/"); - std.loadScript(file[file_manager.data[0]].name); + System.loadELF(System.boot_path + "/athena.elf", [path + "/" + file[file_manager.data[0]].name]); } else if(file[file_manager.data[0]].name.endsWith(".elf") || file[file_manager.data[0]].name.endsWith(".ELF")){ System.loadELF(path + "/"+ file[file_manager.data[0]].name); } else if(file[file_manager.data[0]].name.endsWith(".zip")){ @@ -247,7 +246,7 @@ file_manager.process = function() { } } - if (Pads.check(pad, Pads.R1) && !Pads.check(oldpad, Pads.R1)){ + if (pad.justPressed(Pads.R1)){ file_manager.data[2] ^= 1; file_manager.data[3] ^= 1; } @@ -261,45 +260,43 @@ let render_filelist = function() { Draw.rect(file_manager.gfx.x, file_manager.gfx.y+(20), file_manager.gfx.w, 20, Color.new(64, 64, 64, 64)); printCentered(file_manager.gfx.x+(file_manager.gfx.w/2), file_manager.gfx.y+(3+(20)), 0.55f, path); for (var i = 0; i < file.length; i++) { - if(i+file_manager.comp < 21 && i+file_manager.comp >= 0){ - font.scale = 0.55f; - font.print(file_manager.gfx.x+10, file_manager.gfx.y+(3+(20*(i+2+file_manager.comp))), + if(i+file_manager.comp < 20 && i+file_manager.comp >= 0){ + //font.scale = 0.55f; + font.print(file_manager.gfx.x+10, file_manager.gfx.y+(3+(19*(i+2+file_manager.comp)))-12, file[i].name); if(!file[i].dir){ - font.print(file_manager.gfx.x+200, file_manager.gfx.y+(3+(20*(i+2+file_manager.comp))), + font.print(file_manager.gfx.x+200, file_manager.gfx.y+(3+(19*(i+2+file_manager.comp)))-12, String(file[i].size)); } } }; if(file_manager.data[2] == 1) { - font.scale = 0.55f; + //font.scale = 0.55f; Draw.rect(file_manager.gfx.x+file_manager.gfx.w-75, file_manager.gfx.y+40, 75, 20*5, Color.new(0, 0, 0, 100)); Draw.rect(file_manager.gfx.x+file_manager.gfx.w-75, file_manager.gfx.y+20+20*(file_manager.data[1]+1), 75, 20, Color.new(64, 0, 128, 64)); - font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(1))), "Copy"); - font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(2))), "Move"); - font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(3))), "Paste"); - font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(4))), "Rename"); - font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(5))), "Delete"); + font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(1)))-12, "Copy"); + font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(2)))-12, "Move"); + font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(3)))-12, "Paste"); + font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(4)))-12, "Rename"); + font.print(file_manager.gfx.x+file_manager.gfx.w-75+5, file_manager.gfx.y+(23+(20*(5)))-12, "Delete"); }; }; file_manager.gfx.add(render_filelist); -var oldpad = Pads.get(); var pad = Pads.get(); while(true){ - oldpad = pad; - pad = Pads.get(); + pad.update(); Screen.clear(Color.new(0, 0, 0)); file_manager.run(); - if(Pads.check(pad, Pads.CIRCLE) && !Pads.check(oldpad, Pads.CIRCLE)) { + if(pad.justPressed(Pads.CIRCLE)) { break; } Screen.flip(); }; -System.loadELF(boot_path + "athena_pkd.elf"); \ No newline at end of file +System.loadELF(System.boot_path + "/athena_pkd.elf"); \ No newline at end of file diff --git a/bin/app_store.js b/bin/app_store.js index 945f08b..3d9b303 100644 --- a/bin/app_store.js +++ b/bin/app_store.js @@ -1,20 +1,8 @@ // {"name": "App store", "author": "Daniel Santos", "version": "04102023", "file": "app_store.js"} -function decodeUTF16LE(binaryStr) { - var cp = []; - for( var i = 0; i < binaryStr.length; i+=2) { - cp.push( - binaryStr.charCodeAt(i) | - ( binaryStr.charCodeAt(i+1) << 8 ) - ); - } - - return String.fromCharCode.apply( String, cp ); -} - function load_app_db(fname) { let list_file = std.open(fname, "r"); - let app_list = JSON.parse(decodeUTF16LE(list_file.getline())); + let app_list = JSON.parse(list_file.getline()); list_file.close(); list_file = null; @@ -65,12 +53,7 @@ let app_list = null; const buttons = [{inc:Pads.DOWN, dec:Pads.UP}, {inc:Pads.RIGHT, dec:Pads.LEFT}, {inc:Pads.R2, dec:Pads.L2}]; -function pressed(pad, button) { - return (Pads.check(pad[0], button) && !Pads.check(pad[1], button)); -} - - -let pad = [undefined, undefined]; +let pad = Pads.get(); const FADE_IN = 0; const FADE_OUT = 1; @@ -131,10 +114,6 @@ class UI { Screen.clear(0x80101010); } - pressed(pad, button) { - return (Pads.check(pad[0], button) && !Pads.check(pad[1], button)); - } - } let ui = new UI(); @@ -162,11 +141,11 @@ class Menu { } process_input(pad) { - if(pressed(pad, buttons[this.pad_mode].dec)) { + if(pad.justPressed(buttons[this.pad_mode].dec)) { this.num--; } - if(pressed(pad, buttons[this.pad_mode].inc)) { + if(pad.justPressed(buttons[this.pad_mode].inc)) { this.num++; } @@ -318,8 +297,7 @@ const UPDATED = 2; let update_state = NOT_UPDATED; while(true) { - pad[1] = pad[0]; - pad[0] = Pads.get(); + pad.update(); switch (app_state) { case LOADING: @@ -347,7 +325,7 @@ while(true) { update_state = UPDATING; transfering = true; } else if (update_state == UPDATING) { - if(req.ready(5)) { + if(req.ready(5000)) { transfering = false; update_state = UPDATED; } @@ -389,11 +367,11 @@ while(true) { break; } - if(pressed(pad, Pads.CROSS)) { + if(pad.justPressed(Pads.CROSS)) { app_state = DLING_MENU; } - if(pressed(pad, Pads.TRIANGLE)) { + if(pad.justPressed(Pads.TRIANGLE)) { terminate = true; } @@ -412,17 +390,17 @@ while(true) { switch (dl_state) { case DETAILS: - if(pressed(pad, Pads.CROSS)) { + if(pad.justPressed(Pads.CROSS)) { dling_text += "Downloading package...\n"; dling_text += "This is an alpha version, it will take a long time\n"; dl_state++; } - if(pressed(pad, Pads.TRIANGLE)) { + if(pad.justPressed(Pads.TRIANGLE)) { app_state = MAIN_MENU; } break; case TODOWNLOAD: - req.asyncDownload(app_list[explore_menu.num].link, System.boot_path + app_list[explore_menu.num].fname); + req.asyncDownload(app_list[explore_menu.num].link, System.boot_path + "/" + app_list[explore_menu.num].fname); transfering = true; dl_state++; break; diff --git a/bin/async_request.js b/bin/async_request.js new file mode 100644 index 0000000..9e16a3e --- /dev/null +++ b/bin/async_request.js @@ -0,0 +1,30 @@ +// {"name": "Async Network", "author": "Daniel Santos", "version": "04072023", "file": "async_request.js"} + +IOP.reset(); + +IOP.loadDefaultModule(IOP.network); + +Network.init(); + +Screen.log(JSON.stringify(Network.getConfig())); + +let req = new Request(); +req.followlocation = true; +req.headers = ["upgrade-insecure-requests: 1", + "sec-fetch-dest: document", + "sec-fetch-mode: navigate"]; + +req.asyncGet("https://raw.githubusercontent.com/DanielSant0s/brewstore-db/main/brew_data.json"); + +while(!req.ready(2)) { + console.log("Waiting... " + req.getAsyncSize() + " bytes transfered"); + System.sleep(1); +} + +let result = req.getAsyncData(); + +console.log(result); + +Network.deinit(); + +System.sleep(300); \ No newline at end of file diff --git a/bin/cell.js b/bin/cell.js index 148bd12..d03c9d0 100644 --- a/bin/cell.js +++ b/bin/cell.js @@ -1,6 +1,9 @@ // {"name": "Cell.io", "author": "Daniel Santos", "version": "04072023", "icon": "cell_icon.png", "file": "cell.js"} -var font = new Font("fonts/LEMONMILK-Regular.otf"); +Screen.setFrameCounter(true); +Screen.setVSync(false); + +let font = new Font("fonts/LEMONMILK-Regular.otf"); font.scale = (2); const MAIN_MENU = 0; @@ -8,15 +11,15 @@ const RUN_GAME = 1; const PAUSE_MENU = 2; const GAME_OVER = 3; -var game_state = MAIN_MENU; +let game_state = MAIN_MENU; -var running = true; +let running = true; -var player = {x:0, y:0, r:25, color:Color.new(128, 100, 255)}; +let player = {x:0, y:0, r:25, color:Color.new(128, 100, 255)}; -var camera = {x:-320, y:-224}; +let camera = {x:-320, y:-224}; -var enemies = []; +let enemies = []; function World2Screen(rect, camera){ return [rect.x-camera.x, rect.y-camera.y] @@ -28,9 +31,9 @@ function drawCell(coords, radius, color, hollow){ } function circleCircleColl(c1, c2) { - var distX = c1.x - c2.x; - var distY = c1.y - c2.y; - var dist = Math.sqrtf( (distX*distX) + (distY*distY) ); + let distX = c1.x - c2.x; + let distY = c1.y - c2.y; + let dist = Math.sqrtf( (distX*distX) + (distY*distY) ); if (dist <= c1.r + c2.r) { return true; @@ -44,34 +47,32 @@ function randint(min, max) { return Math.floorf(Math.random() * (max - min + 1)) + min; } -var main_menu_ptr = 0; +let main_menu_ptr = 0; -var oldpad = Pads.get(); -var pad = oldpad; +let pad = Pads.get(); const transparent = Color.new(255, 255, 255, 40); const purple = Color.new(64, 0, 128); -while(running){ - Screen.clear(purple); +Screen.clearColor(purple); - oldpad = pad; - pad = Pads.get(); +pad.setEventHandler(); +Screen.display(() => { if(game_state == MAIN_MENU){ - if(Pads.check(pad, Pads.UP) && !Pads.check(oldpad, Pads.UP)){ + if(pad.justPressed(Pads.UP)){ if(main_menu_ptr > 0){ main_menu_ptr--; } } - if(Pads.check(pad, Pads.DOWN) && !Pads.check(oldpad, Pads.DOWN)){ + if(pad.justPressed(Pads.DOWN)){ if(main_menu_ptr < 2){ main_menu_ptr++; } } - if(Pads.check(pad, Pads.CROSS) && !Pads.check(oldpad, Pads.CROSS)){ + if(pad.justPressed(Pads.CROSS)){ switch(main_menu_ptr){ case 0: game_state = RUN_GAME; @@ -84,15 +85,15 @@ while(running){ camera.y = -224; - enemies_qt = randint(8, 15); + let enemies_qt = randint(8, 15); while(enemies_qt > enemies.length){ - var color = Color.new(randint(0, 255), randint(0, 255), randint(0, 255)); - var enemy = {color:color, x:randint(0, 640), y:randint(0, 448), r:randint(5, 75)}; + let color = Color.new(randint(0, 255), randint(0, 255), randint(0, 255)); + let enemy = {color:color, x:randint(0, 640), y:randint(0, 448), r:randint(5, 75)}; - var found_collision = false; + let found_collision = false; - for(var j = 0; j < enemies.length; j++){ + for(let j = 0; j < enemies.length; j++){ if (circleCircleColl(enemy, enemies[j])) { found_collision = true; } @@ -123,9 +124,9 @@ while(running){ } else if(game_state == RUN_GAME){ - var s_lines = null; - var e_lines = null; - for(var i = -1500; i < 1501; i+=50){ + let s_lines = null; + let e_lines = null; + for(let i = -1500; i < 1501; i+=50){ s_lines = World2Screen({x:i, y:-1500}, camera); if(s_lines[0] > 0 && s_lines[0] < 640) { @@ -140,25 +141,25 @@ while(running){ } } - if(Pads.check(pad, Pads.LEFT) && player.x > -1500){ + if(pad.pressed(Pads.LEFT) && player.x > -1500){ player.x-=4; camera.x-=4; } - if(Pads.check(pad, Pads.RIGHT) && player.x < 1500){ + if(pad.pressed(Pads.RIGHT) && player.x < 1500){ player.x+=4; camera.x+=4; } - if(Pads.check(pad, Pads.UP) && player.y > -1500){ + if(pad.pressed(Pads.UP) && player.y > -1500){ player.y-=4; camera.y-=4; } - if(Pads.check(pad, Pads.DOWN) && player.y < 1500){ + if(pad.pressed(Pads.DOWN) && player.y < 1500){ player.y+=4; camera.y+=4; } - for(var i = 0; i < enemies.length; i++){ - var enemy_coords = World2Screen(enemies[i], camera); + for(let i = 0; i < enemies.length; i++){ + let enemy_coords = World2Screen(enemies[i], camera); if((enemy_coords[0]+enemies[i].r) > 0 && (enemy_coords[0]-enemies[i].r) < 640 && (enemy_coords[1]+enemies[i].r) > 0 && (enemy_coords[1]-enemies[i].r) < 448 ){ drawCell(enemy_coords, enemies[i].r, enemies[i].color, false); @@ -188,14 +189,10 @@ while(running){ font.color = Color.new(128, 128, 128); font.print(150, 200, ((!enemies.length)? "VICTORY!" : "GAME OVER")); - if(Pads.check(pad, Pads.CROSS) && !Pads.check(oldpad, Pads.CROSS)){ + if(pad.justPressed(Pads.CROSS)){ game_state = MAIN_MENU; enemies = []; } } - - Screen.flip(); -} - -font = null; \ No newline at end of file +}); diff --git a/bin/cell_icon.png b/bin/cell_icon.png index 6d5777ebe99d839502f38f7fd363c7a4730356d2..dc5cb39aefe8bfd620b8e089381e021f21d40591 100644 GIT binary patch delta 2743 zcmbuB`9ISS1Hd=tn9zujYi`z@U-!K^<`~inxkBz^kx{um=t~y4&z1WKg(2sZJ986C z%55Qq9N$oQzOU!?dj5jv{d)iW{`ozrP7_V#W2OgzKnF`{$MvdZjA>%r00?~zd&3A5 zh&})RI1ETl`8Ul?xNeC9&Bdm7v5Out?AXzYP33vC8peY?yPF$VK_C`JQ$y5^2Xj9h zK9d>|tg=At5Fc0Mx$S&<;KT=(WM-F`7zM z&X4$bM(NL=E-Ng2%*(%e78Qgi<@YDTkh6MD3ykR>zd`wx1JKNDGxS$?#<6F4LP?f> z(t_Au2d+}R@N}V#!pd$|^;0+BsK2p*sr98dXRXZH%U`cJ*xap2O9c0bM-E&yCU7p7 zI(MspQr!2OlW^9#AE$KhW}9s_(AxH6Jew+)5Z>{NVyJh31QERY zevN7R`$<NWu*V@#ikd+;AoCg$+ss}5Z(PM zV&IkgH#ffsjU7*Wfkn;>lVqqA7wbQJ#VFYqiW?I*M z9&UHAqYgdBw23 zrhX{`;Aa1jR>t17ITXlwn)nF4xMz@vUgJq4|l&?p;p3;7m4_3D?496%B zhG<_Shv49jD(J~3Z@M1U!L=^ba^`~D3RSKOV&rf3Wq0>)BOB%kmIGI{K@Wx&$^lRU zzgxNTLzf?vN2Vz>;K zd1}_N*CTWk6(xsz@S#Kp5!yE3CJLAfdf#bg44a=TDCZLQI8DCzw99nZhD*;d&~^i+ zX;wPVsBzyQngasUqLYr6)WPmLSX<}*o|6#g=Zbb@?&`8AV6ej~&?Op%P2Yz^lkT}h z%8~FF$aOIo-WB8TFd@HmO~vj62;xHdqDqW0YeGgHUs4VeVarT%dn&$y4(K^!WZqP< zc{o4w^5S3a(u*%VC1j^zE`Ir~FgTZa6OYgDv5jG-*$xzTuq8nnT^?sP@*FCYTyS_Z zO{Qh6EKYo-&UcOd_<4SsukwVYNn(UWbvjzx$Oo6m_|hB@<1P0uD0r9Tsa7*F!{lfg z;a*j?6Th07ICz}dFylwh3vi;^?|oGbwf5t*zm%RB%I8Tp|8Sep=v#%!-wpXU^@thj z)Mr-OCI-mQmx2QA^;N4D6o~=oPNR&P+^+JBYTt)13dcuQFaWrW+U94wg?qHW z$)AsyzM0wUf8pGD6#}-P%WvLv6xptqT9(X6NLBr<$yH zwjBGcNX*5?2&X@cf(=8Tud7&QX^JZycudWWX++le3y=-mz`om6@TDfC{3(aI>ehNX zrA>~_+QN?iW=gp?4p64d?OQSZ7!*ic;Sk~n(J z0>nV{u26@;m(4sezP-0ko>6)d+k5|*ama>IC-VEO;(=}DDwmxj%&Qfwr?EQyhBE~A zNRO?)0>ApfHLcIlu`Vp9f+B2RrOo z9#Fg}N_=qO%a0pUOaJ6f%^nW+_DVewZV{qCppv$wLx`7Jk0qJ33z9WfYPU2n<^#@T zpPKce)eyvZ$~y4{zJ9rAq1}by)G~wIf;edl-+|34W~Epzb_^_qu|fmwdZL23})+y%YKry1wK4JZ<+lb89j8@=j=OMfV~z^0fE~ zC&+oJ01=#!UQi2{Tt96q%_awgq^cu}r584D9*$-Tnkg_uMtFii`xdvPm>PhZHsLBO zOh6Hvn^57Ez?TiqA7}*)LE)YDPc_9WfECX_kHVWC{ zZpwJFs$eDpk#hfoh34YU*OZi$X47UK&0Jd%%};B8l`qR@W7Gt`d7PotG7g3nl&*~q z35udlS1&%!pZ)pc%mjA6q{cWlQNMT^gt~M7X&m`H%N#}5xtq}W({9A|jCNQ1Eqqn$ f?SDP9UDYd^+3Ip}$YT8WBSEG{mWH)xm)QRRr!qv) delta 5579 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 diff --git a/bin/cursor/pointer.png b/bin/cursor/pointer.png deleted file mode 100644 index dc90d0d0e4b313c91f761d6e8dacbe2396929865..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2704 zcmV;B3UBp^P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 { + +}); \ No newline at end of file diff --git a/bin/lfm_icon.png b/bin/lfm_icon.png index a751fff84ceb04edcd913904e7cdd58cc4fd2ef7..4f2486abd581f3d20451b516af9cef40ea6d79b1 100644 GIT binary patch delta 640 zcmeyOv_@!xq$)E51H+dG6Mq6JmUKs7M+SzC{oH>NS%G}U;vjb?hIQv;UT)4{T*6k* zz**oCSgyth2K-H8u6ej~{#X?CI|AUb}Yf?c28>Ja};A$dL~pJ}g|gaM!L~|NsBr zy?gie?c4wS`Lloj{+BObUc7km=5$d%phfi(B|(0{3{T%Tb?xGp`+V2tUNHNwIePg= zE^d2w>tCc-V=B~)opm~|*+9Vc?gxW_WI47Zg(v^*jWb;{#8l+0%PxC; z&xt%EXl&+DED7@d0lB3sLjLXS7TO=M@8s9ylmg~^FPFENt+3z1DHq}(`TF$|o(Oxh z>-E=*Wi~L*Tqcmmvudr|w&ULnHn{{mcUvd=tL=S2{`2CQeqlYqQzlszSZ5vm_xX_- z>o1$$vN>#C_h)T&h?a^@$uH1(k*-i#;jLEAR@U?QK)iTD=6~a5-K_1OEqt5V8P&EZ z7tQQD)O_HsM1;}l;wR1v-fS_Bed*Y+@mgepSR70JrBinod{Zo#d*t3#ho6E8dsa@F z^D>}5{RC6|;Rx}PBV~<8@3F}2-~4tr%a2=ae`1+l{#swzz`&xwz{ugiz$Ad3`QtXz zv9EirV}ioWmN6XWzA!DUjicTA>$Gz#Gv-~NKH;ZJ(Dvg;B>Jr_Oa1EQxIP!GT<}%; z^A@2)IgHFa_u02`hsFsRXPjs5eRs2UTHVemi?WK>uP|HZ+{VBN3|SCps6WGcfbqks SqYbaNgZQ4VelF{r5}E+cb0};8 delta 2758 zcmaJ@c{CLM7XQwIL2B%4nL=i)gBAv3vP2RxgBshAWlBj%qcAffONwMq8B$p@6n;qz zvTyB)3SlT~St83ky>s3_@4WNwfA@3mx#!+LKA-z36 z?`Yw>34!2Iq3)6HYB2tl&>KuqbB_;vSuF&)VQVe48srU~(T`TC($8WtUxW5uEo}@) z(CdZJD=ktg8>dB3p~tO~^^zmt?cAfW>~co`5#+Ks@;PEs5Va<36@km_c?mfn(3B3T zIO?EISk~2_kS0m_JoHON%aF-rHT{QUst?rQ&Qci#Tq$)LS7kz4a!gt!z2G5uZ~9q) zKMEPcLcRrOp1$&C>4Fwm^pXKmp+3>e&j{&-NOmQY__oZ*I4CUbm; zX?H;e)04R!2IG;UGqZQYt}^hj=6VCbX|>ys#T{_HJPg!LbY!TVV3ShDx2Z;zac zp`lAhcQ+|?x<7A>%dIE6y5x(OzD({rsFKo&mIBQ>7S(&;-pZe&3(Kzedc24UJ`LxBwMOo)^_nz6= z*#O5=C$7cD@`xl-D!Pr{tjN%GsYWuWFh)XeM@QJ)=Ej=inViny;ox46c>%(vN34Zf=*}_rE-o!agpGHmRmH@_C`w33bnBXGW%z&C zI+H&AlZM~Hf5^}85t&H5f8fA@;+lXjl{r7Arr!Vh^((Swa7GMKFMzaxaayiRn+WM@ ztdU!Xf*0+5Nz~K;OZf-|{lH=8JoDW2bg8QReABX$k|zana&r4*tgWr-_>4M=XplA$ zpuBwridQE3a))jwFG+#+uCBKztm%r@m?8#)>AWF-u=g6Y@xEmeX|?q70{2)~6a($# z;$pZ5xvp?cso|$%H>9w%R6~gLJxS5zaZgXr-OLz0ArP*5jYbvx^{0Bn1|*9I3lz=Q-Zi`==Ua!rAB*veND(M6aW0Rn zX=vbGw@1;IexwZy4*JW6y>PFps=By7(j1$;ibJZ}=B%uJeiod@CoE;tE(!=; zDX9gz*2M2jNXrHrZ zZDN$8v&s5x_MLI5R|R(L6$6TZvib1Xou^cLGP(HJ;SPk?HDz0qkmp|-BsTKd?7*8{ z=fhvUcdzHGn6+_rupbvoYw4E>hZ_nla+|G4-Fi$h;1;Etfv;deljH(KU zVmnkku{-p9ocIBE6i278-=lEFfk|Eq2b4frHQ_Sy39E2axBAuY?TmWQ{iFp!j)WUE8Fd+y6=k`R;e0Ob?>|xu1Jy^P8 z*{kuO)8*|iJ(r&aPuMdDdQs-E%%tPZ)#nDE-jK%XILO|e5oer^rpx3BkDhnK$LCKq z@Q&Pv3N!*vdP7u%f(XO0hkj5D+6JXuT(}X%xdm2iM{ql#mQaf7!XshkeZW52|F;e( zHSF^e@SA`$t4YTp`*(pyS&CZlYJN@;Hx|Y$gJ4IC?|nkCRe45Ktp&$bKP^ zt%!$LLl|63ex@d1t==-DSc9f8+Eb$MO8)NYbNV5ZbbF4#VVcps`b-$IcXafp64iG_ z6u1nqVHj0azYp{C^Hjzii%TsE3}I=la0vt>FE6jLuC6YrLPXf5uy8)Q@Fa;;V65=G zw)RibAvqUfH-s}g(Ad~Wn0T;#+qOlI=b`7W4$f@aQzLVQ+{BiLOoF-HXLWd~as~VL z?F*Nq2LrYy*>=|M3}W0L<53aqR=^bDD;a$K`gO(IM|O`|6u{@(mML|gg6_f8j(+aE zF0DP_KqNMZ2ZO~&7c~VHb8^;aj%H|``!P95J6w{esidTI$}Sgwu8G(ptw~ file.name).filter(str => str.endsWi let metadata = JSON.parse(metadata_str); - if (System.doesFileExist(metadata.icon)) { + if (std.exists(metadata.icon)) { metadata.icon = new Image(metadata.icon); } else { metadata.icon = no_icon; @@ -47,8 +57,7 @@ let app_table = System.listDir().map(file => file.name).filter(str => str.endsWi let menu_ptr = 0; -let new_pad = Pads.get(); -let old_pad = new_pad; +let pad = Pads.get(); let old_kbd_char = 0; let kbd_char = 0; @@ -61,9 +70,10 @@ const VK_RETURN = 10; var ee_info = System.getCPUInfo(); +let mem = undefined; + os.setInterval(() => { - old_pad = new_pad; - new_pad = Pads.get(); + pad.update(); old_kbd_char = kbd_char; kbd_char = Keyboard.get(); @@ -72,28 +82,28 @@ os.setInterval(() => { bg.draw(0, 0); - font_bold.print(15, 5, "Athena dash alpha v0.1"); + font_bold.print(15, 5, "Athena dash"); - font.print(15, 420, `Temp: ${System.getTemperature() === undefined? "NaN" : System.getTemperature()} C | RAM Usage: ${Math.floor(System.getMemoryStats().used / 1048576)}MB / ${Math.floor(ee_info.RAMSize / 1048576)}MB`); + mem = System.getMemoryStats(); - if(Pads.check(new_pad, Pads.UP) && !Pads.check(old_pad, Pads.UP) || old_kbd_char == VK_OLD_UP && kbd_char == VK_NEW_UP) { + font.print(15, 420, `Temp: ${System.getTemperature() === undefined? "NaN" : System.getTemperature()} C | RAM Usage: ${Math.floor(mem.used / 1024)}KB / ${Math.floor(ee_info.RAMSize / 1024)}KB`); + + if(pad.justPressed(Pads.UP) || old_kbd_char == VK_OLD_UP && kbd_char == VK_NEW_UP) { app_table.unshift(app_table.pop()); } - if(Pads.check(new_pad, Pads.DOWN) && !Pads.check(old_pad, Pads.DOWN) || old_kbd_char == VK_OLD_DOWN && kbd_char == VK_NEW_DOWN){ + if(pad.justPressed(Pads.DOWN) || old_kbd_char == VK_OLD_DOWN && kbd_char == VK_NEW_DOWN){ app_table.push(app_table.shift()); } - if(Pads.check(new_pad, Pads.CROSS) && !Pads.check(old_pad, Pads.CROSS) || kbd_char == VK_RETURN){ + if(pad.justPressed(Pads.CROSS) || kbd_char == VK_RETURN){ let bin = "athena.elf"; if ("bin" in app_table[0]) { bin = app_table[0].bin; } - - - System.loadELF(System.boot_path + bin, [app_table[0].file, ]); // Doing this to reset all the stuff + System.loadELF(System.boot_path + "/" + bin, [app_table[0].file, ]); // Doing this to reset all the stuff } font_medium.print(210, 125, app_table[0].name); @@ -102,6 +112,6 @@ os.setInterval(() => { for(let i = 1; i < (app_table.length < 10? app_table.length : 10); i++) { font.print(210, 125+(23*i), app_table[i].name); } - + Screen.flip(); }, 0); \ No newline at end of file diff --git a/bin/no_icon.png b/bin/no_icon.png index 95f2a746506cfb8983c9c76993cdbcbcb4240dce..084935af2f49922a4931f690217c339c6c9d68da 100644 GIT binary patch delta 703 zcmew(`BG?tgc=Jo0|SGhK+O*z#ggvm>&U>cv7h@-A}f&3SRCZ;#IWw1%*)Lgj7!++ z88{0(B8wRq^pruEv0|xx8BmolFz$+-7+y0!nI0FmR-s%;yaCLBA--ptv}*{_NDPa3I#cgpDQQZTqTvU4{j#exGuT{&j`}(y}TXFL&ezH9UFH zVt2EEeR&sO)G_s$f8#DOF>bq)J!Pkkkz#|u2Xz-U#tSN1YdINmtuClDY-o&XV7B`i z7H7VYL1|Uw_5}^;g5vD&n3GNy^_VwIx;AB^mB59MB8kE)PR(1jX%!2DhnJ9$Lp_5g zXVWbPrW5NFQWi24t@|1(%A&BnN1&D2BX;5`1{Mp^nXU>AnUj+k8PX3O_2W>MxR99r z_bH>*W+yJYLyWtwzB%x?%5v$k^jixV{DM1w+V7ZM_ym=VD#R# z1fa+6r7G$&<_aIv1W0<)nm#oy{A#vje`_3=b8NF*FqzPT-XbG>Nv zNqSt|*ebiWf~+`toCX5%yJtdn&L%`W+ZxeWX8}ab2fyMhDZXi=DD4vJ&nQ@Bz{$bZ zlQl3sG@Gb(Xv4Gj%0gdbiL)1%AoSw)#Agt!_Iq*5OIMY0=LnK&ZlJysw>(te*eQxb zFg~l_uWD?ypPLbQk06k;pDgm%`g76$KU9dl3?RV@pb3aDa0mR}?qq4e&9fBk;2cU;@HyF@@}o%V zH)i(haXpjh#2Nqww z8Z`&WYP3yP0_srpF+dY5%6@j><>|176AdwUDv1j5tYeTh;Ily8V6pdBN58PzMv|a< zt3A7|QW+joHm+330HPFsT!7xx z99zf^g{p2JX%@pW3ebcnFb^dKv<_t{jt}k9 zQ|N*os59A2b8Fev&=QFwNZ-iruE?J59oJAl2)UmFWd{8ToW~2`4H!e>nF9r3`kl7B zo~~qbA&w>E1y4ugB;ctLao|^h)9exuwoKqa3zF&#o3824eO*}}4ARx8M?7XcZtWZ4 zBiSDyw95>7AOmyx7O4)RT0q6RJ1o;d!*LQddKP*o%Ob7r) z8tjLDywPO3+YhS%KME9hWuV^<`mY8(2>iT5|8NAvdHl(!V(RiZDa$n^!~Nyb=`vfm z#IFxhL>w!PoPNJ_)xRc3Z}&{GGAgr}Ak&b#3HpFy+^FZk$4k!NdxVbE%CG0$O=d_u z6@rR|>1#rhhm=$@la=7AJ#RiPw`XkWaqnvMEumDP`vWfQ)S65&KNwHj(B!zgx=?J ztoIG0?fn-;%jopJh_kF)>)niD^txUBHXQoxYq6A$rVYKDXHo?jvxps}Aoy+dpfE>A zy4rS#xd)<6<$l;|?Z z+HgEBEoIcC3gSm4?~c8@zHB>~@awuCuUY7AlE1Xx=zgnJ+Pga&BS)?$m0`d|Vr2S& z1ig}uXzhQRyRwfVVH!BTj5HMLnN1Kz^svpd7eYYNI`8}l^?^{E@ge#fxVvpF diff --git a/bin/pads.js b/bin/pads.js index 54b0fed..acb0228 100644 --- a/bin/pads.js +++ b/bin/pads.js @@ -2,11 +2,11 @@ const canvas = Screen.getMode(); -canvas.psm = CT16S; -canvas.zbuffering = false; -canvas.double_buffering = false; +//canvas.psm = CT24; +//canvas.zbuffering = false; +//canvas.double_buffering = false; -Screen.setMode(canvas); +//Screen.setMode(canvas); Screen.setVSync(false); Screen.setFrameCounter(true); @@ -37,14 +37,10 @@ const l3 = new Image("pads/l3.png", VRAM); const r3 = new Image("pads/r3.png", VRAM); var rumble = false; -var new_pad = Pads.get(); -var old_pad = new_pad; +var pad = Pads.get(0); -os.setInterval(() => { - Screen.clear(); - - old_pad = new_pad; - new_pad = Pads.get(); +Screen.display(() => { + pad.update(); font.print(220, 25, "\nAthena project: Controls demo\n"); font.print(100, 370, "\nTips:\n"); @@ -52,67 +48,65 @@ os.setInterval(() => { font.print(100, 405, "\nButtons transparency varies with the pressure applied to them.\n"); font.print(100, 420, "\nPress Start+R3+Pad-Up to exit this demo.\n"); - pad_select.color = (Pads.check(new_pad, Pads.SELECT)? opaque : translucent); + pad_select.color = ((pad.btns & Pads.SELECT)? opaque : translucent); pad_select.draw(260.0f, 190.0f); - start.color = (Pads.check(new_pad, Pads.START)? opaque : translucent); + start.color = ((pad.btns & Pads.START)? opaque : translucent); start.draw(380.0f, 190.0f); - up.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.UP)? Pads.getPressure(Pads.UP) : 60)); + up.color = Color.new(128, 128, 128, ((pad.btns & Pads.UP)? Pads.getPressure(Pads.UP) : 60)); up.draw(120.0f, 155.0f); - down.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.DOWN)? Pads.getPressure(Pads.DOWN) : 60)); + down.color = Color.new(128, 128, 128, ((pad.btns & Pads.DOWN)? Pads.getPressure(Pads.DOWN) : 60)); down.draw(120.0f, 225.0f); - left.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.LEFT)? Pads.getPressure(Pads.LEFT) : 60)); + left.color = Color.new(128, 128, 128, ((pad.btns & Pads.LEFT)? Pads.getPressure(Pads.LEFT) : 60)); left.draw(85.0f, 190.0f); - right.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.RIGHT)? Pads.getPressure(Pads.RIGHT) : 60)); + right.color = Color.new(128, 128, 128, ((pad.btns & Pads.RIGHT)? Pads.getPressure(Pads.RIGHT) : 60)); right.draw(155.0f, 190.0f); - triangle.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.TRIANGLE)? Pads.getPressure(Pads.TRIANGLE) : 60)); + triangle.color = Color.new(128, 128, 128, ((pad.btns & Pads.TRIANGLE)? Pads.getPressure(Pads.TRIANGLE) : 60)); triangle.draw(520.0f, 155.0f); - cross.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.CROSS)? Pads.getPressure(Pads.CROSS) : 60)); + cross.color = Color.new(128, 128, 128, ((pad.btns & Pads.CROSS)? Pads.getPressure(Pads.CROSS) : 60)); cross.draw(520.0f, 225.0f); - square.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.SQUARE)? Pads.getPressure(Pads.SQUARE) : 60)); + square.color = Color.new(128, 128, 128, ((pad.btns & Pads.SQUARE)? Pads.getPressure(Pads.SQUARE) : 60)); square.draw(485.0f, 190.0f); - circle.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.CIRCLE)? Pads.getPressure(Pads.CIRCLE) : 60)); + circle.color = Color.new(128, 128, 128, ((pad.btns & Pads.CIRCLE)? Pads.getPressure(Pads.CIRCLE) : 60)); circle.draw(555.0f, 190.0f); - l1.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.L1)? Pads.getPressure(Pads.L1) : 60)); + l1.color = Color.new(128, 128, 128, ((pad.btns & Pads.L1)? Pads.getPressure(Pads.L1) : 60)); l1.draw(102.0f, 100.0f); - r1.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.R1)? Pads.getPressure(Pads.R1) : 60)); + r1.color = Color.new(128, 128, 128, ((pad.btns & Pads.R1)? Pads.getPressure(Pads.R1) : 60)); r1.draw(502.0f, 100.0f); - l2.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.L2)? Pads.getPressure(Pads.L2) : 60)); + l2.color = Color.new(128, 128, 128, ((pad.btns & Pads.L2)? Pads.getPressure(Pads.L2) : 60)); l2.draw(137.0f, 100.0f); - r2.color = Color.new(128, 128, 128, (Pads.check(new_pad, Pads.R2)? Pads.getPressure(Pads.R2) : 60)); + r2.color = Color.new(128, 128, 128, ((pad.btns & Pads.R2)? Pads.getPressure(Pads.R2) : 60)); r2.draw(537.0f, 100.0f); - l3.color = (Pads.check(new_pad, Pads.L3)? opaque : translucent); - l3.draw(new_pad.lx/4.0f+242.0f, new_pad.ly/4.0f+300.0f); + l3.color = ((pad.btns & Pads.L3)? opaque : translucent); + l3.draw(pad.lx/4.0f+242.0f, pad.ly/4.0f+300.0f); - r3.color = (Pads.check(new_pad, Pads.R3)? opaque : translucent); - r3.draw(new_pad.rx/4.0f+402.0f, new_pad.ry/4.0f+300.0f); + r3.color = ((pad.btns & Pads.R3)? opaque : translucent); + r3.draw(pad.rx/4.0f+402.0f, pad.ry/4.0f+300.0f); Draw.rect(220.0f, 280.0f, 75, 75, dark_gray); Draw.rect(380.0f, 280.0f, 75, 75, dark_gray); - if((Pads.check(new_pad, Pads.R2) && Pads.check(new_pad, Pads.L2)) && !(Pads.check(old_pad, Pads.R2) && Pads.check(old_pad, Pads.L2))){ - rumble? Pads.rumble(0 ,0) : Pads.rumble(1 ,255); - rumble ^= 1; + if(((pad.btns & Pads.R2) && (pad.btns & Pads.L2)) && !((pad.old_btns & Pads.R2) && !(pad.old_btns & Pads.L2))){ + rumble? Pads.rumble(0 ,0) : Pads.rumble(1 ,255); + rumble ^= 1; } - if(Pads.check(new_pad, Pads.START) && Pads.check(new_pad, Pads.R3) && Pads.check(new_pad, Pads.UP)){ + if((pad.btns & Pads.START) && (pad.btns & Pads.R3) && (pad.btns & Pads.UP)){ System.loadELF(System.boot_path + "athena_pkd.elf"); } font.print(10, 10, Screen.getFPS(360) + " FPS"); - - Screen.flip(); -}, 0); +}); diff --git a/bin/pads_icon.png b/bin/pads_icon.png index 92acbc8636240cb03f20b6674c2d1ac4e504868c..5c6fd4a458891bee542cea9c5d6d7761fb1388a1 100644 GIT binary patch delta 1386 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 { + console.log(`DPad Left button was just pressed`); +}); + +Pads.newEvent(Pads.RIGHT, Pads.JUST_PRESSED, () => { + console.log(`DPad Right button was just pressed`); +}); diff --git a/bin/pong.js b/bin/pong.js index 36fa921..9d67054 100644 --- a/bin/pong.js +++ b/bin/pong.js @@ -145,8 +145,7 @@ const player = new Paddle(); const ball = new Ball(); ball.randomDirection(); -let new_pad = Pads.get(); -let old_pad = new_pad; +let pad = Pads.get(); let paused = true; let game_over = false; @@ -167,12 +166,11 @@ const pause = { }; while(true) { - old_pad = new_pad; - new_pad = Pads.get(); + pad.update(); Screen.clear(dark_gray); - if(Pads.check(new_pad, Pads.START) && !Pads.check(old_pad, Pads.START)) { + if(pad.justPressed(Pads.START)) { paused = (!paused); if(game_over) { ball.reset(); @@ -186,11 +184,11 @@ while(true) { } if(!paused) { - if(Pads.check(new_pad, Pads.LEFT) || new_pad.rx < -50) { + if(pad.pressed(Pads.LEFT) || pad.rx < -50) { player.moveLeft(); } - if(Pads.check(new_pad, Pads.RIGHT) || new_pad.rx > 50){ + if(pad.pressed(Pads.RIGHT) || pad.rx > 50){ player.moveRight(); } diff --git a/bin/render.js b/bin/render.js index ba87987..bb48831 100644 --- a/bin/render.js +++ b/bin/render.js @@ -1,7 +1,16 @@ // {"name": "Render demo", "author": "Daniel Santos", "version": "04072023", "icon": "render_icon.png", "file": "render.js"} -var fntcpy = new Font(); +const pipelines = [ + "NO_LIGHTS_COLORS", + "NO_LIGHTS_COLORS_TEX", + "NO_LIGHTS", + "NO_LIGHTS_TEX", + "DEFAULT", + "DEFAULT_NO_TEX" +]; + +let fntcpy = new Font("default"); fntcpy.scale = (0.4f); Screen.setFrameCounter(true); @@ -14,49 +23,94 @@ canvas.psmz = Z16S; Screen.setMode(canvas); -Render.init(4/3); +Render.setView(4/3); -var dragontex = new Image("render/dragon.png"); -dragontex.filter = LINEAR; -var dragonmesh = Render.loadOBJ("render/dragon.obj", dragontex); +// Change your root folder to "render" so we can work with file path magic :p +os.chdir("render"); -var monkeytex = new Image("render/monkey.png"); -monkeytex.filter = LINEAR; -var monkeymesh = Render.loadOBJ("render/monkey.obj", monkeytex); +const vertList = [ + Render.vertex(0.5f, 0.5f, 0.5f, // Position - X Y Z + 1.0f, 1.0f, 1.0f, // Normal - N1 N2 N3 + 0.0f, 0.0f, // Texture coordinate - S T + 1.0f, 0.0f, 0.0f, 1.0f), // Color - R G B A -var model = [dragonmesh, monkeymesh]; + Render.vertex(0.5f, 0.8f, 0.5f, + 1.0f, 1.0f, 1.0f, + 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 1.0f), + + Render.vertex(0.8f, 0.8f, 0.5f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 1.0f), +]; + +let listtest = new RenderObject(vertList); + +let dragontex = new Image("dragon.png"); +let dragonmesh = new RenderObject("dragon.obj", dragontex); + +let monkeytex = new Image("monkey.png"); +let monkeymesh = new RenderObject("monkey.obj", monkeytex); +monkeymesh.setPipeline(Render.PL_NO_LIGHTS_COLORS); + +let car = new RenderObject("Car.obj"); + +let car_vertices = car.vertices; + +car_vertices.forEach(vertex => { + if (vertex.r > 0.45f && vertex.g < 0.2f && vertex.b < 0.2f) { + vertex.r = 0.4f; + vertex.g = 0.0f; + vertex.b = 0.8f; + } +}); + +car.vertices = car_vertices; + +let mill = new RenderObject("cubes.obj"); + +let boombox = new RenderObject("Boombox.obj"); +let boomboxtex = boombox.getTexture(0); +boomboxtex.filter = LINEAR; + +let model = [dragonmesh, monkeymesh, car, boombox, mill, listtest]; Camera.position(0.0f, 0.0f, 50.0f); Camera.rotation(0.0f, 0.0f, 0.0f); -Lights.create(1); +Lights.set(0, Lights.DIRECTION, 0.0, 1.0, -1.0); +Lights.set(0, Lights.DIFFUSE, 0.8, 0.8, 0.8); -//Lights.set(1, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, AMBIENT); -//Lights.set(2, 1.0, 0.0, -1.0, 1.0, 1.0, 1.0, DIRECTIONAL); -Lights.set(1, 0.0, 1.0, -1.0, 0.9, 0.5, 0.5, DIRECTIONAL); -//Lights.set(4, -1.0, -1.0, -1.0, 0.5, 0.5, 0.5, DIRECTIONAL); +Lights.set(1, Lights.DIRECTION, 0.0, 1.0, 1.0); +Lights.set(1, Lights.DIFFUSE, 0.4, 0.0, 0.8); -var pad = null; -var oldpad = null; -var modeltodisplay = 0; -var lx = null; -var ly = null; -var rx = null; -var ry = null; +let pad = Pads.get(); +let modeltodisplay = 0; +let lx = null; +let ly = null; +let rx = null; +let ry = null; -var savedlx = 0.0f; -var savedly = 180.0f; -var savedrx = 50.0f; -var savedry = 0.0f; +let savedlx = 0.0f; +let savedly = 180.0f; +let savedrx = 100.0f; +let savedry = 0.0f; -var free_mem = 0; -var free_vram = Screen.getFreeVRAM(); +let ee_info = System.getCPUInfo(); + +let free_mem = `RAM: ${Math.floor(System.getMemoryStats().used / 1048576)}MB / ${Math.floor(ee_info.RAMSize / 1048576)}MB`; +let free_vram = Screen.getFreeVRAM(); + +const gray = Color.new(40, 40, 40, 128); + +let bbox = false; while(true){ - Screen.clear(Color.new(40, 40, 40, 128)); + Screen.clear(gray); + Camera.update(); + pad.update(); - oldpad = pad; - pad = Pads.get(); lx = ((pad.lx > 25 || pad.lx < -25)? pad.lx : 0) / 1024.0f; ly = ((pad.ly > 25 || pad.ly < -25)? pad.ly : 0) / 1024.0f; savedlx = savedlx - lx; @@ -69,17 +123,38 @@ while(true){ Camera.position(0.0f, savedry, savedrx); - if((Pads.check(pad, Pads.LEFT) && !Pads.check(oldpad, Pads.LEFT)) || (Pads.check(pad, Pads.RIGHT) && !Pads.check(oldpad, Pads.RIGHT))){ - modeltodisplay ^= 1 + if(pad.justPressed(Pads.LEFT) && modeltodisplay > 0){ + modeltodisplay -= 1; + } + + if(pad.justPressed(Pads.RIGHT) && modeltodisplay < model.length-1){ + modeltodisplay += 1; + } + + if(pad.justPressed(Pads.UP) && model[modeltodisplay].getPipeline() > 0){ + model[modeltodisplay].setPipeline(model[modeltodisplay].getPipeline()-1); + } + + if(pad.justPressed(Pads.DOWN) && model[modeltodisplay].getPipeline() < 5){ + model[modeltodisplay].setPipeline(model[modeltodisplay].getPipeline()+1); + } + + if(pad.justPressed(Pads.TRIANGLE)) { + System.loadELF(System.boot_path + "/athena.elf"); } - if(Pads.check(pad, Pads.TRIANGLE) && !Pads.check(oldpad, Pads.TRIANGLE)) { - break; + if(pad.justPressed(Pads.SQUARE)) { + bbox ^= 1; } - Render.drawOBJ(model[modeltodisplay], 0.0f, 0.0f, 30.0f, savedly, savedlx, 0.0f); + model[modeltodisplay].draw(0.0f, 0.0f, 30.0f, savedly, savedlx, 0.0f); + + if(bbox) { + model[modeltodisplay].drawBounds(0.0f, 0.0f, 30.0f, savedly, savedlx, 0.0f, Color.new(128, 0, 255)); + } - fntcpy.print(10, 10, Screen.getFPS(360) + " FPS | Free RAM: " + free_mem + "KB | Free VRAM: " + free_vram + "KB"); + fntcpy.print(10, 10, Screen.getFPS(360) + " FPS | " + free_mem + " | Free VRAM: " + free_vram + "KB"); + fntcpy.print(10, 25, model[modeltodisplay].size + " Vertices | " + "Pipeline: " + pipelines[model[modeltodisplay].getPipeline()]); Screen.flip(); } \ No newline at end of file diff --git a/bin/render/Boombox.mtl b/bin/render/Boombox.mtl new file mode 100644 index 0000000..4e5eecc --- /dev/null +++ b/bin/render/Boombox.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Boombox_mat +Ns 0.000000 +Ka 1.000000 1.000000 1.000000 +Kd 1.000000 1.000000 1.000000 +Ks 0.000000 0.000000 0.000000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 1 +map_Kd Boombox_BaseColor.png diff --git a/bin/render/Boombox.obj b/bin/render/Boombox.obj new file mode 100644 index 0000000..4bdd370 --- /dev/null +++ b/bin/render/Boombox.obj @@ -0,0 +1,3203 @@ +# Blender v3.6.0 OBJ File: '' +# www.blender.org +mtllib Boombox.mtl +o Boombox +v -0.426094 0.661963 0.309047 +v -0.462655 0.646819 0.312243 +v -0.462655 0.337200 0.312243 +v -0.426094 0.322056 0.309047 +v -0.666444 0.902313 0.309047 +v -0.681589 0.865752 0.312243 +v -1.006351 0.902313 0.309047 +v -0.991207 0.865752 0.312243 +v -1.246702 0.661963 0.309047 +v -1.210140 0.646819 0.312243 +v -1.246702 0.322056 0.309047 +v -1.210140 0.337200 0.312243 +v -1.006351 0.081706 0.309047 +v -0.991207 0.118267 0.312243 +v -0.666444 0.081706 0.309047 +v -0.681588 0.118267 0.312243 +v -0.395292 0.674721 0.266485 +v -0.653686 0.933115 0.266485 +v -1.019110 0.933115 0.266485 +v -1.277504 0.674721 0.266485 +v -1.277504 0.309297 0.266485 +v -1.019110 0.050903 0.266485 +v -0.653686 0.050903 0.266485 +v -0.395292 0.309297 0.266485 +v -0.690027 0.845378 0.295874 +v -0.483029 0.638380 0.295874 +v -0.982768 0.845378 0.295874 +v -1.189767 0.638380 0.295874 +v -1.189767 0.345639 0.295874 +v -0.982768 0.138640 0.295874 +v -0.690027 0.138640 0.295874 +v -0.483029 0.345639 0.295874 +v -0.730224 0.748336 0.345523 +v -0.580071 0.598183 0.345523 +v -0.942572 0.748336 0.345523 +v -1.092725 0.598183 0.345523 +v -1.092725 0.385835 0.345523 +v -0.942572 0.235682 0.345523 +v -0.730224 0.235682 0.345523 +v -0.580071 0.385835 0.345523 +v -0.836398 0.492009 0.382547 +v -0.987182 1.099718 0.304157 +v -0.997832 1.095306 0.307353 +v -0.997832 1.005114 0.307353 +v -0.987182 1.000703 0.304157 +v -1.057196 1.169732 0.304157 +v -1.061607 1.159081 0.307353 +v -1.156211 1.169732 0.304157 +v -1.151799 1.159081 0.307353 +v -1.226225 1.099718 0.304157 +v -1.215574 1.095306 0.307353 +v -1.226225 1.000703 0.304157 +v -1.215574 1.005114 0.307353 +v -1.156211 0.930689 0.304157 +v -1.151799 0.941339 0.307353 +v -1.057196 0.930689 0.304157 +v -1.061607 0.941339 0.307353 +v -0.978209 1.103434 0.261594 +v -1.053479 1.178704 0.261594 +v -1.159927 1.178704 0.261594 +v -1.235197 1.103434 0.261594 +v -1.235197 0.996986 0.261594 +v -1.159927 0.921716 0.261594 +v -1.053479 0.921716 0.261594 +v -0.978209 0.996986 0.261594 +v -1.064065 1.153147 0.295272 +v -1.003767 1.092848 0.295272 +v -1.149341 1.153147 0.295272 +v -1.209640 1.092848 0.295272 +v -1.209640 1.007573 0.295272 +v -1.149341 0.947274 0.295272 +v -1.064065 0.947274 0.295272 +v -1.003767 1.007573 0.295272 +v -1.075775 1.124878 0.316997 +v -1.032035 1.081139 0.316997 +v -1.137632 1.124878 0.316997 +v -1.181371 1.081139 0.316997 +v -1.181371 1.019282 0.316997 +v -1.137632 0.975542 0.316997 +v -1.075775 0.975542 0.316997 +v -1.032035 1.019282 0.316997 +v -1.106703 1.050210 0.332579 +v -1.211118 1.273658 0.284967 +v -1.039056 1.273658 0.284967 +v -1.039056 1.273658 0.308177 +v -1.211118 1.273658 0.308177 +v -1.211118 1.478615 0.308177 +v -1.039056 1.478615 0.308177 +v -1.039056 1.478615 0.284967 +v -1.211118 1.478615 0.284967 +v -1.058486 1.290666 0.308177 +v -1.191688 1.290666 0.308177 +v -1.058486 1.461607 0.308177 +v -1.191688 1.461607 0.308177 +v -1.125087 1.308950 0.291590 +v -1.125087 1.443323 0.291590 +v -1.177440 1.309059 0.286529 +v -1.147032 1.309059 0.345837 +v -1.147032 1.444271 0.345837 +v -1.177440 1.444271 0.286529 +v -1.097128 1.309059 0.343969 +v -1.097128 1.444271 0.343969 +v -1.070452 1.309059 0.283506 +v -1.070452 1.444271 0.283506 +v 0.364419 1.041380 0.270267 +v 0.343388 1.041380 0.289813 +v 0.343388 0.786454 0.289813 +v 1.291926 1.058489 0.270267 +v 0.364488 1.058489 0.270267 +v 0.376108 1.041380 0.289813 +v 1.315718 1.041380 0.289813 +v 1.308067 1.057448 0.202745 +v 1.315718 1.534981 -0.289813 +v 1.315718 1.534981 0.289813 +v 1.315718 1.072627 0.289813 +v 0.364419 0.068235 0.270267 +v 0.376108 0.051836 0.289813 +v 1.315718 0.000000 0.289813 +v 1.315718 -0.000000 -0.289813 +v -1.315718 1.041380 0.289813 +v -1.308067 1.057448 0.202745 +v -1.315718 0.000000 0.289813 +v -0.376108 0.051836 0.289813 +v -0.343388 0.786454 0.289813 +v -0.364419 1.041380 0.270267 +v -0.364419 0.068235 0.270267 +v -0.343388 0.096570 0.289813 +v -1.315718 1.534981 -0.289813 +v -1.116088 1.534981 -0.289813 +v -1.116088 1.416450 -0.289813 +v 0.104302 0.944253 0.273660 +v 0.104302 0.845266 0.273660 +v 0.319608 0.845266 0.273660 +v 0.319608 0.944253 0.273660 +v -1.291926 1.058489 0.270267 +v -0.376108 1.041380 0.289813 +v -0.364488 1.058489 0.270267 +v -0.343388 1.041380 0.289813 +v -1.315718 1.072627 0.289813 +v -1.315718 1.534981 0.289813 +v -1.001684 1.452647 0.275071 +v -1.001684 1.300824 0.275071 +v 1.001684 1.300824 0.275071 +v 1.001684 1.452647 0.275071 +v -1.315718 -0.000000 -0.289813 +v -1.116088 1.534981 0.013677 +v 0.343388 0.096570 0.289813 +v 0.098271 0.947109 0.289813 +v 0.325639 0.947109 0.289813 +v 0.098271 0.842410 0.289813 +v 0.325639 0.842410 0.289813 +v -1.045505 1.534981 0.013677 +v 1.033957 1.534981 0.013677 +v -0.993566 1.483619 -0.237871 +v -0.993566 1.483619 -0.145933 +v 0.982017 1.483619 -0.145933 +v 0.982017 1.483619 -0.237871 +v -1.045505 1.416450 -0.289813 +v -1.045505 1.534981 -0.289813 +v 1.033957 1.534981 -0.289813 +v 1.033957 1.416450 -0.289813 +v -1.051033 1.390316 0.013677 +v -1.110560 1.390316 0.013677 +v 1.106174 1.534981 0.013677 +v 1.033957 1.416450 0.013677 +v 1.106174 1.416450 0.013677 +v 1.106174 1.416450 -0.289813 +v 1.106174 1.534981 -0.289813 +v -1.006404 1.534981 -0.141955 +v -1.006404 1.534981 -0.241850 +v 0.994855 1.534981 -0.141955 +v 0.994855 1.534981 -0.241850 +v -1.023813 1.278627 0.289813 +v -1.023813 1.474843 0.289813 +v 1.023813 1.278627 0.289813 +v 1.023813 1.474843 0.289813 +v -0.274317 0.880628 0.289813 +v -0.248023 0.880628 0.289813 +v -0.248023 0.880628 0.310428 +v -0.274317 0.880628 0.310428 +v -0.248023 0.934412 0.310428 +v -0.274317 0.934412 0.310428 +v -0.248023 0.934412 0.289813 +v -0.274317 0.934412 0.289813 +v -0.202994 0.899861 0.289813 +v -0.176699 0.899861 0.289813 +v -0.176699 0.899861 0.310428 +v -0.202994 0.899861 0.310428 +v -0.176699 0.953646 0.310428 +v -0.202994 0.953646 0.310428 +v -0.176699 0.953646 0.289813 +v -0.202994 0.953646 0.289813 +v -0.125260 0.877422 0.289813 +v -0.098965 0.877422 0.289813 +v -0.098965 0.877422 0.310428 +v -0.125260 0.877422 0.310428 +v -0.098965 0.931207 0.310428 +v -0.125260 0.931207 0.310428 +v -0.098965 0.931207 0.289813 +v -0.125260 0.931207 0.289813 +v -0.049929 0.916690 0.289813 +v -0.023635 0.916690 0.289813 +v -0.023635 0.916690 0.310428 +v -0.049929 0.916690 0.310428 +v -0.023635 0.970475 0.310428 +v -0.049929 0.970475 0.310428 +v -0.023635 0.970475 0.289813 +v -0.049929 0.970475 0.289813 +v 0.023798 0.931115 0.289813 +v 0.050093 0.931115 0.289813 +v 0.050093 0.931115 0.310428 +v 0.023798 0.931115 0.310428 +v 0.050093 0.984900 0.310428 +v 0.023798 0.984900 0.310428 +v 0.050093 0.984900 0.289813 +v 0.023798 0.984900 0.289813 +v 0.256520 0.872874 0.271180 +v 0.305506 0.872874 0.271180 +v 0.305506 0.872874 0.309806 +v 0.256520 0.872874 0.309806 +v 0.305506 0.906993 0.309806 +v 0.256520 0.906993 0.309806 +v 0.305506 0.906993 0.271180 +v 0.256520 0.906993 0.271180 +v 0.121094 1.105327 0.289813 +v 0.190720 1.105327 0.289813 +v 0.190720 1.105327 0.302106 +v 0.121094 1.105327 0.302106 +v 0.130074 1.117428 0.295710 +v 0.181740 1.117428 0.295710 +v 0.181740 1.208487 0.295710 +v 0.130074 1.208487 0.295710 +v 0.121094 1.220589 0.302106 +v 0.190720 1.220589 0.302106 +v 0.190720 1.220589 0.289813 +v 0.121094 1.220589 0.289813 +v 0.182503 1.116084 0.302106 +v 0.129311 1.116084 0.302106 +v 0.182503 1.209832 0.302106 +v 0.129311 1.209832 0.302106 +v -0.004644 1.105327 0.289813 +v 0.064981 1.105327 0.289813 +v 0.064981 1.105327 0.302106 +v -0.004644 1.105327 0.302106 +v 0.004335 1.117429 0.295710 +v 0.056002 1.117429 0.295710 +v 0.056002 1.208487 0.295710 +v 0.004335 1.208487 0.295710 +v -0.004644 1.220589 0.302106 +v 0.064981 1.220589 0.302106 +v 0.064981 1.220589 0.289813 +v -0.004644 1.220589 0.289813 +v 0.056765 1.116084 0.302106 +v 0.003572 1.116084 0.302106 +v 0.056765 1.209832 0.302106 +v 0.003572 1.209832 0.302106 +v 0.245966 1.105327 0.289813 +v 0.315591 1.105327 0.289813 +v 0.315591 1.105327 0.302106 +v 0.245966 1.105327 0.302106 +v 0.254945 1.117428 0.295710 +v 0.306612 1.117428 0.295710 +v 0.306612 1.208487 0.295710 +v 0.254945 1.208487 0.295710 +v 0.245966 1.220589 0.302106 +v 0.315591 1.220589 0.302106 +v 0.315591 1.220589 0.289813 +v 0.245966 1.220589 0.289813 +v 0.307375 1.116084 0.302106 +v 0.254182 1.116084 0.302106 +v 0.307375 1.209832 0.302106 +v 0.254182 1.209832 0.302106 +v 0.373439 1.105327 0.289813 +v 0.443064 1.105327 0.289813 +v 0.443064 1.105327 0.302106 +v 0.373439 1.105327 0.302106 +v 0.382418 1.117428 0.295710 +v 0.434085 1.117428 0.295710 +v 0.434085 1.208487 0.295710 +v 0.382418 1.208487 0.295710 +v 0.373439 1.220589 0.302106 +v 0.443064 1.220589 0.302106 +v 0.443064 1.220589 0.289813 +v 0.373439 1.220589 0.289813 +v 0.434848 1.116084 0.302106 +v 0.381655 1.116084 0.302106 +v 0.434848 1.209832 0.302106 +v 0.381655 1.209832 0.302106 +v 0.499177 1.105327 0.289813 +v 0.568802 1.105327 0.289813 +v 0.568802 1.105327 0.302106 +v 0.499177 1.105327 0.302106 +v 0.508156 1.117428 0.295710 +v 0.559823 1.117428 0.295710 +v 0.559823 1.208487 0.295710 +v 0.508156 1.208487 0.295710 +v 0.499177 1.220589 0.302106 +v 0.568802 1.220589 0.302106 +v 0.568802 1.220589 0.289813 +v 0.499177 1.220589 0.289813 +v 0.560586 1.116084 0.302106 +v 0.507393 1.116084 0.302106 +v 0.560586 1.209832 0.302106 +v 0.507393 1.209832 0.302106 +v 0.131489 1.145006 0.315996 +v 0.180475 1.145006 0.315996 +v 0.171353 1.155424 0.367194 +v 0.140611 1.155424 0.367194 +v 0.171353 1.168707 0.367194 +v 0.140611 1.168707 0.367194 +v 0.180475 1.179125 0.315996 +v 0.131489 1.179125 0.315996 +v 0.142232 1.152489 0.312587 +v 0.169731 1.152489 0.312587 +v 0.169731 1.171642 0.312587 +v 0.142232 1.171642 0.312587 +v 0.142232 1.152489 0.284256 +v 0.169731 1.152489 0.284256 +v 0.169731 1.171642 0.284256 +v 0.142232 1.171642 0.284256 +v 0.256360 1.132694 0.305131 +v 0.305346 1.132694 0.305131 +v 0.296224 1.119314 0.355636 +v 0.265482 1.119314 0.355636 +v 0.296224 1.131217 0.361530 +v 0.265482 1.131217 0.361530 +v 0.305346 1.163270 0.320270 +v 0.256360 1.163270 0.320270 +v 0.267103 1.140913 0.305396 +v 0.294603 1.140913 0.305396 +v 0.294603 1.158077 0.313894 +v 0.267103 1.158077 0.313894 +v 0.267103 1.153483 0.280006 +v 0.294603 1.153483 0.280006 +v 0.294603 1.170648 0.288505 +v 0.267103 1.170648 0.288505 +v 0.005750 1.132080 0.304239 +v 0.054736 1.132080 0.304239 +v 0.045614 1.117188 0.354319 +v 0.014872 1.117188 0.354319 +v 0.045614 1.128909 0.360568 +v 0.014872 1.128909 0.360568 +v 0.054736 1.162188 0.320290 +v 0.005750 1.162188 0.320290 +v 0.016494 1.140287 0.304751 +v 0.043993 1.140287 0.304751 +v 0.043993 1.157188 0.313761 +v 0.016494 1.157188 0.313761 +v 0.016494 1.153615 0.279750 +v 0.043993 1.153615 0.279750 +v 0.043993 1.170516 0.288761 +v 0.016494 1.170516 0.288761 +v 0.383833 1.152646 0.319037 +v 0.432819 1.152646 0.319037 +v 0.423697 1.174400 0.366540 +v 0.392955 1.174400 0.366540 +v 0.423697 1.187337 0.363529 +v 0.392955 1.187337 0.363529 +v 0.432819 1.185877 0.311302 +v 0.383833 1.185877 0.311302 +v 0.394576 1.159161 0.314020 +v 0.422075 1.159161 0.314020 +v 0.422075 1.177816 0.309678 +v 0.394576 1.177816 0.309678 +v 0.394576 1.152739 0.286427 +v 0.422075 1.152739 0.286427 +v 0.422075 1.171393 0.282084 +v 0.394576 1.171393 0.282084 +v 0.509571 1.133926 0.306764 +v 0.558557 1.133926 0.306764 +v 0.549435 1.123432 0.357947 +v 0.518693 1.123432 0.357947 +v 0.549435 1.135651 0.363155 +v 0.518693 1.135651 0.363155 +v 0.558557 1.165312 0.320144 +v 0.509571 1.165312 0.320144 +v 0.520315 1.142146 0.306562 +v 0.547814 1.142146 0.306562 +v 0.547814 1.159765 0.314073 +v 0.520315 1.159765 0.314073 +v 0.520315 1.153256 0.280500 +v 0.547814 1.153256 0.280500 +v 0.547814 1.170875 0.288011 +v 0.520315 1.170875 0.288011 +v 1.177440 1.309059 0.286529 +v 1.177440 1.444271 0.286529 +v 1.147032 1.444271 0.345837 +v 1.147032 1.309059 0.345837 +v 1.097128 1.444271 0.343969 +v 1.097128 1.309059 0.343969 +v 1.070452 1.444271 0.283506 +v 1.070452 1.309059 0.283506 +v 1.211118 1.273658 0.284967 +v 1.211118 1.273658 0.308177 +v 1.039056 1.273658 0.308177 +v 1.039056 1.273658 0.284967 +v 1.211118 1.478615 0.308177 +v 1.211118 1.478615 0.284967 +v 1.039056 1.478615 0.284967 +v 1.039056 1.478615 0.308177 +v 1.191688 1.290666 0.308177 +v 1.058486 1.290666 0.308177 +v 1.058486 1.461607 0.308177 +v 1.191688 1.461607 0.308177 +v 1.125087 1.308950 0.291590 +v 1.125087 1.443323 0.291590 +v 0.987182 1.099718 0.304157 +v 0.987182 1.000703 0.304157 +v 0.997832 1.005114 0.307353 +v 0.997832 1.095306 0.307353 +v 1.061607 1.159081 0.307353 +v 1.057196 1.169732 0.304157 +v 1.151799 1.159081 0.307353 +v 1.156211 1.169732 0.304157 +v 1.215574 1.095306 0.307353 +v 1.226225 1.099718 0.304157 +v 1.215574 1.005114 0.307353 +v 1.226225 1.000703 0.304157 +v 1.151799 0.941339 0.307353 +v 1.156211 0.930689 0.304157 +v 1.061607 0.941339 0.307353 +v 1.057196 0.930689 0.304157 +v 0.978209 1.103434 0.261594 +v 1.053479 1.178704 0.261594 +v 1.159927 1.178704 0.261594 +v 1.235197 1.103434 0.261594 +v 1.235197 0.996986 0.261594 +v 1.159927 0.921716 0.261594 +v 1.053479 0.921716 0.261594 +v 0.978209 0.996986 0.261594 +v 1.003767 1.092848 0.295272 +v 1.064065 1.153147 0.295272 +v 1.149341 1.153147 0.295272 +v 1.209640 1.092848 0.295272 +v 1.209640 1.007573 0.295272 +v 1.149341 0.947274 0.295272 +v 1.064065 0.947274 0.295272 +v 1.003767 1.007573 0.295272 +v 1.032035 1.081139 0.316997 +v 1.075775 1.124878 0.316997 +v 1.137632 1.124878 0.316997 +v 1.181371 1.081139 0.316997 +v 1.181371 1.019282 0.316997 +v 1.137632 0.975542 0.316997 +v 1.075775 0.975542 0.316997 +v 1.032035 1.019282 0.316997 +v 1.106703 1.050210 0.332579 +v 0.426094 0.661963 0.309047 +v 0.426094 0.322056 0.309047 +v 0.462655 0.337200 0.312243 +v 0.462655 0.646819 0.312243 +v 0.681589 0.865752 0.312243 +v 0.666444 0.902313 0.309047 +v 0.991207 0.865752 0.312243 +v 1.006351 0.902313 0.309047 +v 1.210140 0.646819 0.312243 +v 1.246702 0.661963 0.309047 +v 1.210140 0.337200 0.312243 +v 1.246702 0.322056 0.309047 +v 0.991207 0.118267 0.312243 +v 1.006351 0.081706 0.309047 +v 0.681588 0.118267 0.312243 +v 0.666444 0.081706 0.309047 +v 0.395292 0.674721 0.266485 +v 0.653686 0.933115 0.266485 +v 1.019110 0.933115 0.266485 +v 1.277504 0.674721 0.266485 +v 1.277504 0.309297 0.266485 +v 1.019110 0.050903 0.266485 +v 0.653686 0.050903 0.266485 +v 0.395292 0.309297 0.266485 +v 0.483029 0.638380 0.295874 +v 0.690027 0.845378 0.295874 +v 0.982768 0.845378 0.295874 +v 1.189767 0.638380 0.295874 +v 1.189767 0.345639 0.295874 +v 0.982768 0.138640 0.295874 +v 0.690027 0.138640 0.295874 +v 0.483029 0.345639 0.295874 +v 0.580071 0.598183 0.345523 +v 0.730224 0.748336 0.345523 +v 0.942572 0.748336 0.345523 +v 1.092725 0.598183 0.345523 +v 1.092725 0.385835 0.345523 +v 0.942572 0.235682 0.345523 +v 0.730224 0.235682 0.345523 +v 0.580071 0.385835 0.345523 +v 0.836398 0.492009 0.382547 +v 0.876067 1.209488 0.273119 +v 0.826762 1.258792 0.273119 +v 0.826608 1.258420 0.346181 +v 0.875695 1.209333 0.346181 +v 0.757034 1.258792 0.273119 +v 0.757188 1.258420 0.346181 +v 0.707729 1.209488 0.273119 +v 0.708102 1.209333 0.346181 +v 0.707729 1.139760 0.273119 +v 0.708102 1.139914 0.346181 +v 0.757034 1.090455 0.273119 +v 0.757188 1.090827 0.346181 +v 0.826762 1.090455 0.273119 +v 0.826608 1.090827 0.346181 +v 0.876067 1.139760 0.273119 +v 0.875695 1.139914 0.346181 +v 0.791898 1.174624 0.346181 +v -0.143088 1.183798 0.273119 +v -0.170383 1.211093 0.273119 +v -0.170468 1.210887 0.346181 +v -0.143294 1.183713 0.346181 +v -0.208984 1.211093 0.273119 +v -0.208898 1.210887 0.346181 +v -0.236278 1.183798 0.273119 +v -0.236072 1.183713 0.346181 +v -0.236278 1.145198 0.273119 +v -0.236072 1.145283 0.346181 +v -0.208984 1.117903 0.273119 +v -0.208898 1.118109 0.346181 +v -0.170383 1.117903 0.273119 +v -0.170468 1.118109 0.346181 +v -0.143088 1.145198 0.273119 +v -0.143294 1.145283 0.346181 +v -0.189683 1.164498 0.346181 +v -0.338302 1.175140 0.273849 +v -0.360439 1.197276 0.273849 +v -0.360508 1.197109 0.345983 +v -0.338470 1.175071 0.345983 +v -0.391745 1.197276 0.273849 +v -0.391675 1.197109 0.345983 +v -0.413881 1.175140 0.273849 +v -0.413714 1.175071 0.345983 +v -0.413881 1.143834 0.273849 +v -0.413714 1.143903 0.345983 +v -0.391745 1.121698 0.273849 +v -0.391675 1.121865 0.345983 +v -0.360439 1.121698 0.273849 +v -0.360508 1.121865 0.345983 +v -0.338302 1.143834 0.273849 +v -0.338470 1.143903 0.345983 +v -0.376092 1.159487 0.345983 +v -0.794434 1.171730 0.304157 +v -0.803496 1.167976 0.307353 +v -0.803496 1.148957 0.307353 +v -0.794434 1.145204 0.304157 +v -0.813191 1.190486 0.304157 +v -0.816944 1.181424 0.307353 +v -0.839716 1.190486 0.304157 +v -0.835963 1.181424 0.307353 +v -0.858473 1.171730 0.304157 +v -0.849411 1.167976 0.307353 +v -0.858473 1.145204 0.304157 +v -0.849411 1.148957 0.307353 +v -0.839716 1.126448 0.304157 +v -0.835963 1.135509 0.307353 +v -0.813191 1.126448 0.304157 +v -0.816944 1.135509 0.307353 +v -0.792031 1.172725 0.261594 +v -0.812195 1.192890 0.261594 +v -0.840712 1.192890 0.261594 +v -0.860876 1.172725 0.261594 +v -0.860876 1.144208 0.261594 +v -0.840712 1.124044 0.261594 +v -0.812195 1.124044 0.261594 +v -0.792031 1.144208 0.261594 +v -0.818183 1.178434 0.292960 +v -0.806487 1.166738 0.292960 +v -0.834724 1.178434 0.292960 +v -0.846421 1.166738 0.292960 +v -0.846421 1.150196 0.292960 +v -0.834724 1.138500 0.292960 +v -0.818183 1.138500 0.292960 +v -0.806487 1.150196 0.292960 +v -0.826454 1.158467 0.290649 +v -0.252252 0.165094 0.286787 +v -0.214824 0.165094 0.286787 +v -0.214824 0.165094 0.325413 +v -0.252252 0.165094 0.325413 +v -0.214824 0.188876 0.325413 +v -0.252252 0.188876 0.325413 +v -0.252252 0.241031 0.305707 +v -0.214824 0.241031 0.305707 +v -0.214824 0.241031 0.286787 +v -0.252252 0.241031 0.286787 +v -0.162136 0.165094 0.286787 +v -0.124709 0.165094 0.286787 +v -0.124708 0.165094 0.325413 +v -0.162136 0.165094 0.325413 +v -0.124708 0.188876 0.325413 +v -0.162136 0.188876 0.325413 +v -0.162136 0.241031 0.305707 +v -0.124708 0.241031 0.305707 +v -0.124709 0.241031 0.286787 +v -0.162136 0.241031 0.286787 +v -0.072021 0.165094 0.286787 +v -0.034593 0.165094 0.286787 +v -0.034593 0.165094 0.325413 +v -0.072021 0.165094 0.325413 +v -0.034593 0.188876 0.325413 +v -0.072021 0.188876 0.325413 +v -0.072021 0.241031 0.305707 +v -0.034593 0.241031 0.305707 +v -0.034593 0.241031 0.286787 +v -0.072021 0.241031 0.286787 +v 0.252252 0.165094 0.286787 +v 0.252252 0.165094 0.325413 +v 0.214824 0.165094 0.325413 +v 0.214824 0.165094 0.286787 +v 0.252252 0.188876 0.325413 +v 0.214824 0.188876 0.325413 +v 0.252252 0.241031 0.305707 +v 0.252252 0.241031 0.286787 +v 0.214824 0.241031 0.286787 +v 0.214824 0.241031 0.305707 +v 0.162136 0.165094 0.286787 +v 0.162136 0.165094 0.325413 +v 0.124708 0.165094 0.325413 +v 0.124709 0.165094 0.286787 +v 0.162136 0.188876 0.325413 +v 0.124708 0.188876 0.325413 +v 0.162136 0.241031 0.305707 +v 0.162136 0.241031 0.286787 +v 0.124709 0.241031 0.286787 +v 0.124708 0.241031 0.305707 +v 0.072021 0.165094 0.286787 +v 0.072021 0.165094 0.325413 +v 0.034593 0.165094 0.325413 +v 0.034593 0.165094 0.286787 +v 0.072021 0.188876 0.325413 +v 0.034593 0.188876 0.325413 +v 0.072021 0.241031 0.305707 +v 0.072021 0.241031 0.286787 +v 0.034593 0.241031 0.286787 +v 0.034593 0.241031 0.305707 +v 1.044138 1.736537 -0.118738 +v 1.044138 1.397352 -0.117785 +v 1.044138 1.395765 0.005102 +v 1.044138 1.736537 0.006055 +v 1.097894 1.395765 0.005102 +v 1.097894 1.842957 0.006055 +v 1.097894 1.397352 -0.117785 +v 1.097894 1.842957 -0.118738 +v -1.053426 1.736537 0.006055 +v -1.053426 1.736537 -0.118738 +v -1.107182 1.842957 0.006055 +v -1.107182 1.842957 -0.118738 +v -1.053426 1.395765 0.005102 +v -1.053426 1.397352 -0.117785 +v -1.107182 1.395765 0.005102 +v -1.107182 1.397352 -0.117785 +v 0.960600 1.516305 -0.213345 +v 0.898482 1.516305 -0.213345 +v 0.898482 1.523244 -0.184476 +v 0.960600 1.523244 -0.184476 +v 0.898482 1.494375 -0.177538 +v 0.960600 1.494375 -0.177538 +v 0.898482 1.487436 -0.206407 +v 0.960600 1.487436 -0.206407 +v 0.059050 1.523244 -0.184476 +v 0.059050 1.494375 -0.177538 +v 0.763985 1.494375 -0.177538 +v 0.763985 1.523244 -0.184476 +v 0.763985 1.516305 -0.213345 +v 0.059050 1.516305 -0.213345 +v 0.763985 1.487436 -0.206407 +v 0.059050 1.487436 -0.206407 +v 0.821489 1.510989 -0.204665 +v 0.821489 1.514563 -0.189793 +v 0.821489 1.496117 -0.201090 +v 0.821489 1.499691 -0.186218 +v 0.887226 1.510989 -0.204665 +v 0.887226 1.514563 -0.189793 +v 0.887226 1.496117 -0.201090 +v 0.887226 1.499691 -0.186218 +v 0.075645 1.483332 -0.219973 +v 0.075645 1.506781 -0.219973 +v 0.075645 1.506781 -0.168942 +v 0.075645 1.483332 -0.168942 +v 0.024614 1.506781 -0.168942 +v 0.024614 1.483332 -0.168942 +v 0.024614 1.506781 -0.219973 +v 0.024614 1.483332 -0.219973 +v 0.065899 1.525752 -0.210227 +v 0.065899 1.525752 -0.178688 +v 0.034360 1.525752 -0.178688 +v 0.034360 1.525752 -0.210227 +v -0.960600 1.516305 -0.213345 +v -0.960600 1.523244 -0.184476 +v -0.898482 1.523244 -0.184476 +v -0.898482 1.516305 -0.213345 +v -0.960600 1.494375 -0.177538 +v -0.898482 1.494375 -0.177538 +v -0.960600 1.487436 -0.206407 +v -0.898482 1.487436 -0.206407 +v -0.059050 1.523244 -0.184476 +v -0.763985 1.523244 -0.184476 +v -0.763985 1.494375 -0.177538 +v -0.059050 1.494375 -0.177538 +v -0.763985 1.516305 -0.213345 +v -0.059050 1.516305 -0.213345 +v -0.763985 1.487436 -0.206407 +v -0.059050 1.487436 -0.206407 +v -0.821489 1.510989 -0.204665 +v -0.821489 1.514563 -0.189793 +v -0.821489 1.496117 -0.201090 +v -0.821489 1.499691 -0.186218 +v -0.887226 1.514563 -0.189793 +v -0.887226 1.510989 -0.204665 +v -0.887226 1.496117 -0.201090 +v -0.887226 1.499691 -0.186218 +v -0.075645 1.483332 -0.219973 +v -0.075645 1.483332 -0.168942 +v -0.075645 1.506781 -0.168942 +v -0.075645 1.506781 -0.219973 +v -0.024614 1.483332 -0.168942 +v -0.024614 1.506781 -0.168942 +v -0.024614 1.483332 -0.219973 +v -0.024614 1.506781 -0.219973 +v -0.065899 1.525752 -0.178688 +v -0.065899 1.525752 -0.210227 +v -0.034360 1.525752 -0.178688 +v -0.034360 1.525752 -0.210227 +vt 0.808616 0.296899 +vt 0.732501 0.288953 +vt 0.811862 0.288966 +vt 0.867969 0.345092 +vt 0.860064 0.348406 +vt 0.867956 0.424453 +vt 0.860023 0.421207 +vt 0.808516 0.472655 +vt 0.811830 0.480560 +vt 0.735715 0.472613 +vt 0.676362 0.424421 +vt 0.684267 0.421106 +vt 0.684308 0.348306 +vt 0.676375 0.345060 +vt 0.735815 0.296858 +vt 0.816436 0.277714 +vt 0.879160 0.340370 +vt 0.879208 0.429027 +vt 0.816552 0.491751 +vt 0.732469 0.480547 +vt 0.727895 0.491799 +vt 0.665123 0.340486 +vt 0.665171 0.429143 +vt 0.727779 0.277762 +vt 0.854530 0.350709 +vt 0.806330 0.302441 +vt 0.854481 0.418921 +vt 0.806213 0.467120 +vt 0.689801 0.418804 +vt 0.738000 0.467072 +vt 0.738118 0.302392 +vt 0.689850 0.350591 +vt 0.796797 0.325071 +vt 0.831787 0.359970 +vt 0.831851 0.409388 +vt 0.796952 0.444377 +vt 0.747534 0.444441 +vt 0.712480 0.360124 +vt 0.712544 0.409543 +vt 0.747379 0.325135 +vt 0.772165 0.384756 +vt 0.721333 0.197768 +vt 0.697781 0.195438 +vt 0.722300 0.195503 +vt 0.737306 0.213804 +vt 0.739592 0.212886 +vt 0.737262 0.236438 +vt 0.739527 0.237404 +vt 0.721226 0.252411 +vt 0.697626 0.254632 +vt 0.698592 0.252366 +vt 0.680334 0.237249 +vt 0.682619 0.236330 +vt 0.680398 0.212730 +vt 0.682664 0.213696 +vt 0.698700 0.197724 +vt 0.748251 0.209019 +vt 0.725689 0.186646 +vt 0.748384 0.240793 +vt 0.726011 0.263355 +vt 0.722144 0.254696 +vt 0.694237 0.263489 +vt 0.671675 0.241115 +vt 0.671541 0.209341 +vt 0.693915 0.186779 +vt 0.720229 0.200481 +vt 0.734607 0.214942 +vt 0.734549 0.235333 +vt 0.699697 0.249653 +vt 0.720088 0.249711 +vt 0.685319 0.235193 +vt 0.685377 0.214801 +vt 0.699837 0.200423 +vt 0.717050 0.208027 +vt 0.727023 0.218030 +vt 0.717000 0.242128 +vt 0.727003 0.232155 +vt 0.702875 0.242107 +vt 0.692923 0.217980 +vt 0.692902 0.232105 +vt 0.702925 0.208006 +vt 0.709963 0.225067 +vt 0.867211 0.202048 +vt 0.827247 0.207254 +vt 0.827227 0.202041 +vt 0.867189 0.255011 +vt 0.827225 0.260217 +vt 0.827218 0.255000 +vt 0.872479 0.254943 +vt 0.867219 0.207265 +vt 0.872489 0.207325 +vt 0.821957 0.207322 +vt 0.821947 0.254940 +vt 0.831524 0.211011 +vt 0.862912 0.251254 +vt 0.862859 0.210984 +vt 0.831578 0.251281 +vt 0.847223 0.216007 +vt 0.847213 0.246259 +vt 0.781019 0.215066 +vt 0.766313 0.246845 +vt 0.766307 0.214980 +vt 0.791872 0.214925 +vt 0.781022 0.246913 +vt 0.806552 0.215019 +vt 0.791862 0.246755 +vt 0.773871 0.201046 +vt 0.773864 0.260733 +vt 0.523105 0.875308 +vt 0.518525 0.874872 +vt 0.516496 0.816451 +vt 0.735776 0.880781 +vt 0.527605 0.875033 +vt 0.740317 0.874998 +vt 0.759428 0.880143 +vt 0.740496 0.993817 +vt 0.740495 0.886187 +vt 0.524091 0.880671 +vt 0.522165 0.654261 +vt 0.526282 0.649754 +vt 0.740700 0.634904 +vt 0.873229 0.634871 +vt 0.141936 0.875032 +vt 0.122923 0.880228 +vt 0.140438 0.635268 +vt 0.354876 0.650345 +vt 0.365346 0.817257 +vt 0.358999 0.654936 +vt 0.364645 0.661946 +vt 0.606716 0.357536 +vt 0.561124 0.357478 +vt 0.561161 0.330406 +vt 0.761616 0.591266 +vt 0.784495 0.541917 +vt 0.784495 0.591264 +vt 0.146484 0.880747 +vt 0.354644 0.874781 +vt 0.358170 0.880605 +vt 0.363739 0.874725 +vt 0.141772 0.886094 +vt 0.141818 0.993569 +vt 0.213413 0.938538 +vt 0.668908 0.972691 +vt 0.213398 0.972522 +vt 0.008987 0.635318 +vt 0.139990 0.502887 +vt 0.606551 0.489921 +vt 0.561034 0.426798 +vt 0.359158 0.874876 +vt 0.516549 0.661311 +vt 0.463123 0.853363 +vt 0.462769 0.829383 +vt 0.512878 0.829252 +vt 0.513847 0.853386 +vt 0.788091 0.592782 +vt 0.788091 0.540402 +vt 0.760692 0.595020 +vt 0.785421 0.595014 +vt 0.758020 0.592782 +vt 0.761616 0.541920 +vt 0.785418 0.538162 +vt 0.760689 0.538171 +vt 0.009627 0.991653 +vt 0.873700 0.991894 +vt 0.544914 0.426772 +vt 0.647920 0.031060 +vt 0.624886 0.482139 +vt 0.626924 0.030965 +vt 0.545008 0.357456 +vt 0.070143 0.329751 +vt 0.545038 0.330378 +vt 0.607177 0.006985 +vt 0.006234 0.006183 +vt 0.914861 0.890683 +vt 0.947436 0.906530 +vt 0.914375 0.906796 +vt 0.946588 0.823377 +vt 0.947846 0.892943 +vt 0.961440 0.892944 +vt 0.962707 0.823378 +vt 0.994425 0.890688 +vt 0.053516 0.426114 +vt 0.070011 0.426133 +vt 0.005574 0.489123 +vt 0.908673 0.962532 +vt 0.977982 0.946036 +vt 0.977983 0.962529 +vt 0.053654 0.329736 +vt 0.005767 0.356752 +vt 0.053613 0.356786 +vt 0.908674 0.989602 +vt 0.977984 0.989599 +vt 0.908673 0.918970 +vt 0.892176 0.946036 +vt 0.892182 0.918966 +vt 0.977981 0.918966 +vt 0.908670 0.946040 +vt 0.648884 0.018973 +vt 0.626070 0.018870 +vt 0.657634 0.485219 +vt 0.659698 0.028181 +vt 0.646737 0.494328 +vt 0.645882 0.482233 +vt 0.615173 0.027980 +vt 0.613108 0.485018 +vt 0.208271 0.978418 +vt 0.673996 0.932760 +vt 0.208291 0.932639 +vt 0.674038 0.978598 +vt 0.668877 0.938664 +vt 0.074473 0.563238 +vt 0.080478 0.558530 +vt 0.080478 0.563238 +vt 0.074473 0.558530 +vt 0.080478 0.546247 +vt 0.074473 0.546247 +vt 0.080478 0.541539 +vt 0.069765 0.546247 +vt 0.069765 0.558530 +vt 0.085186 0.558530 +vt 0.085186 0.546247 +vt 0.074473 0.563238 +vt 0.080478 0.558530 +vt 0.080478 0.563238 +vt 0.074473 0.558530 +vt 0.080478 0.546247 +vt 0.074473 0.546247 +vt 0.080478 0.541539 +vt 0.069765 0.546247 +vt 0.069765 0.558530 +vt 0.085186 0.558530 +vt 0.085186 0.546247 +vt 0.074473 0.563238 +vt 0.080478 0.558530 +vt 0.080478 0.563238 +vt 0.074473 0.558530 +vt 0.080478 0.546247 +vt 0.074473 0.546247 +vt 0.080478 0.541539 +vt 0.069765 0.546247 +vt 0.069765 0.558530 +vt 0.085186 0.558530 +vt 0.085186 0.546247 +vt 0.074473 0.563238 +vt 0.080478 0.558530 +vt 0.080478 0.563238 +vt 0.074473 0.558530 +vt 0.080478 0.546247 +vt 0.074473 0.546247 +vt 0.080478 0.541539 +vt 0.069765 0.546247 +vt 0.069765 0.558530 +vt 0.085186 0.558530 +vt 0.085186 0.546247 +vt 0.074473 0.563238 +vt 0.080478 0.558530 +vt 0.080478 0.563238 +vt 0.074473 0.558530 +vt 0.080478 0.546247 +vt 0.074473 0.546247 +vt 0.080478 0.541539 +vt 0.069765 0.546247 +vt 0.069765 0.558530 +vt 0.085186 0.558530 +vt 0.085186 0.546247 +vt 0.046342 0.555877 +vt 0.037467 0.544697 +vt 0.046339 0.544659 +vt 0.037465 0.555844 +vt 0.029758 0.544692 +vt 0.029756 0.555839 +vt 0.020882 0.544659 +vt 0.029707 0.564711 +vt 0.037517 0.564706 +vt 0.037517 0.535826 +vt 0.029707 0.535831 +vt 0.721895 0.134082 +vt 0.704318 0.136805 +vt 0.704407 0.133931 +vt 0.718725 0.140471 +vt 0.707473 0.160914 +vt 0.707290 0.140564 +vt 0.721783 0.164377 +vt 0.704538 0.167437 +vt 0.704456 0.164564 +vt 0.724648 0.164421 +vt 0.721820 0.136946 +vt 0.724695 0.137001 +vt 0.701443 0.136869 +vt 0.701591 0.164610 +vt 0.719898 0.139501 +vt 0.706230 0.139433 +vt 0.719908 0.161869 +vt 0.706334 0.161940 +vt 0.718851 0.160729 +vt 0.751382 0.134082 +vt 0.733805 0.136805 +vt 0.733894 0.133931 +vt 0.748212 0.140471 +vt 0.736961 0.160914 +vt 0.736778 0.140564 +vt 0.751270 0.164377 +vt 0.734025 0.167437 +vt 0.733944 0.164564 +vt 0.754135 0.164421 +vt 0.751308 0.136946 +vt 0.754183 0.137001 +vt 0.730931 0.136869 +vt 0.731078 0.164610 +vt 0.735717 0.139433 +vt 0.749396 0.161869 +vt 0.749385 0.139501 +vt 0.735822 0.161940 +vt 0.748339 0.160729 +vt 0.781853 0.134082 +vt 0.764276 0.136805 +vt 0.764365 0.133931 +vt 0.778683 0.140471 +vt 0.767431 0.160914 +vt 0.767249 0.140564 +vt 0.781741 0.164377 +vt 0.764496 0.167437 +vt 0.764415 0.164564 +vt 0.784606 0.164421 +vt 0.781778 0.136946 +vt 0.784653 0.137001 +vt 0.761402 0.136869 +vt 0.761549 0.164610 +vt 0.766188 0.139433 +vt 0.779866 0.161869 +vt 0.779856 0.139501 +vt 0.766293 0.161940 +vt 0.778809 0.160729 +vt 0.811832 0.134082 +vt 0.794255 0.136805 +vt 0.794344 0.133931 +vt 0.808662 0.140471 +vt 0.797410 0.160914 +vt 0.797228 0.140564 +vt 0.811720 0.164377 +vt 0.794475 0.167437 +vt 0.794394 0.164564 +vt 0.814585 0.164421 +vt 0.811758 0.136946 +vt 0.814632 0.137001 +vt 0.791381 0.136869 +vt 0.791528 0.164610 +vt 0.796167 0.139433 +vt 0.809845 0.161869 +vt 0.809835 0.139501 +vt 0.796272 0.161940 +vt 0.808788 0.160729 +vt 0.697321 0.134082 +vt 0.679745 0.136805 +vt 0.679834 0.133931 +vt 0.694152 0.140471 +vt 0.682900 0.160914 +vt 0.682717 0.140564 +vt 0.697209 0.164377 +vt 0.679964 0.167437 +vt 0.679883 0.164564 +vt 0.700075 0.164421 +vt 0.697247 0.136946 +vt 0.700122 0.137001 +vt 0.676870 0.136869 +vt 0.677018 0.164610 +vt 0.695325 0.139501 +vt 0.681657 0.139433 +vt 0.695335 0.161869 +vt 0.681761 0.161940 +vt 0.694278 0.160729 +vt 0.698188 0.124506 +vt 0.683540 0.121211 +vt 0.693379 0.114248 +vt 0.673559 0.119344 +vt 0.681298 0.119105 +vt 0.680168 0.121961 +vt 0.674691 0.116488 +vt 0.687631 0.108849 +vt 0.677097 0.104678 +vt 0.671618 0.116503 +vt 0.669209 0.104663 +vt 0.690535 0.101513 +vt 0.681676 0.099866 +vt 0.680003 0.097343 +vt 0.680045 0.103984 +vt 0.685957 0.106325 +vt 0.687588 0.102208 +vt 0.718489 0.082914 +vt 0.724948 0.089240 +vt 0.718391 0.089183 +vt 0.718497 0.072154 +vt 0.724867 0.078462 +vt 0.718473 0.078484 +vt 0.724850 0.082870 +vt 0.718316 0.067718 +vt 0.724843 0.072064 +vt 0.731607 0.125981 +vt 0.716959 0.122686 +vt 0.726798 0.115722 +vt 0.706978 0.120818 +vt 0.714718 0.120579 +vt 0.713588 0.123436 +vt 0.708110 0.117963 +vt 0.721050 0.110324 +vt 0.710516 0.106152 +vt 0.705037 0.117977 +vt 0.702628 0.106138 +vt 0.723954 0.102988 +vt 0.715095 0.101341 +vt 0.713422 0.098817 +vt 0.713464 0.105459 +vt 0.719376 0.107800 +vt 0.721007 0.103682 +vt 0.728220 0.082914 +vt 0.734679 0.089240 +vt 0.728122 0.089183 +vt 0.728228 0.072154 +vt 0.734598 0.078462 +vt 0.728204 0.078484 +vt 0.734581 0.082870 +vt 0.728047 0.067718 +vt 0.734574 0.072064 +vt 0.762078 0.124998 +vt 0.747430 0.121703 +vt 0.757268 0.114739 +vt 0.737449 0.119835 +vt 0.745188 0.119596 +vt 0.744058 0.122453 +vt 0.738581 0.116980 +vt 0.751521 0.109341 +vt 0.740987 0.105169 +vt 0.735508 0.116994 +vt 0.733099 0.105155 +vt 0.743893 0.097834 +vt 0.751478 0.102699 +vt 0.745565 0.100358 +vt 0.749847 0.106817 +vt 0.754425 0.102005 +vt 0.743935 0.104476 +vt 0.738172 0.082914 +vt 0.744632 0.089240 +vt 0.738075 0.089183 +vt 0.738181 0.072154 +vt 0.744550 0.078462 +vt 0.738156 0.078484 +vt 0.744533 0.082870 +vt 0.738000 0.067718 +vt 0.744526 0.072064 +vt 0.796480 0.126964 +vt 0.781832 0.123669 +vt 0.791671 0.116705 +vt 0.771851 0.121801 +vt 0.779590 0.121562 +vt 0.778461 0.124419 +vt 0.772983 0.118946 +vt 0.785923 0.111307 +vt 0.775389 0.107135 +vt 0.769910 0.118960 +vt 0.767501 0.107121 +vt 0.788827 0.103971 +vt 0.779968 0.102324 +vt 0.778295 0.099800 +vt 0.778337 0.106441 +vt 0.784249 0.108783 +vt 0.785880 0.104665 +vt 0.748125 0.082914 +vt 0.754584 0.089240 +vt 0.748027 0.089183 +vt 0.748133 0.072154 +vt 0.754502 0.078462 +vt 0.748109 0.078484 +vt 0.754486 0.082870 +vt 0.754659 0.067815 +vt 0.754478 0.072064 +vt 0.698188 0.087155 +vt 0.683540 0.083860 +vt 0.693379 0.076897 +vt 0.673559 0.081993 +vt 0.681298 0.081754 +vt 0.680168 0.084610 +vt 0.674691 0.079138 +vt 0.687631 0.071498 +vt 0.669209 0.067312 +vt 0.671618 0.079152 +vt 0.680003 0.059992 +vt 0.687588 0.064857 +vt 0.681676 0.062515 +vt 0.677097 0.067327 +vt 0.685957 0.068974 +vt 0.690535 0.064162 +vt 0.680045 0.066633 +vt 0.709201 0.082914 +vt 0.715660 0.089240 +vt 0.709103 0.089183 +vt 0.709209 0.072154 +vt 0.715578 0.078462 +vt 0.709185 0.078484 +vt 0.715562 0.082870 +vt 0.709028 0.067718 +vt 0.715554 0.072064 +vt 0.766313 0.246845 +vt 0.781019 0.215066 +vt 0.766307 0.214980 +vt 0.781022 0.246913 +vt 0.791872 0.214925 +vt 0.791862 0.246755 +vt 0.806552 0.215019 +vt 0.773871 0.201046 +vt 0.773864 0.260733 +vt 0.799057 0.260549 +vt 0.827247 0.207254 +vt 0.867211 0.202048 +vt 0.827227 0.202041 +vt 0.827225 0.260217 +vt 0.867189 0.255011 +vt 0.827218 0.255000 +vt 0.867219 0.207265 +vt 0.872479 0.254943 +vt 0.872489 0.207325 +vt 0.821957 0.207322 +vt 0.821947 0.254940 +vt 0.831524 0.211011 +vt 0.862912 0.251254 +vt 0.831578 0.251281 +vt 0.847223 0.216007 +vt 0.862859 0.210984 +vt 0.847213 0.246259 +vt 0.697781 0.195438 +vt 0.721333 0.197768 +vt 0.722300 0.195503 +vt 0.737306 0.213804 +vt 0.737262 0.236438 +vt 0.739592 0.212886 +vt 0.721226 0.252411 +vt 0.739527 0.237404 +vt 0.697626 0.254632 +vt 0.722144 0.254696 +vt 0.698592 0.252366 +vt 0.680334 0.237249 +vt 0.682619 0.236330 +vt 0.680398 0.212730 +vt 0.682664 0.213696 +vt 0.748251 0.209019 +vt 0.725689 0.186646 +vt 0.748384 0.240793 +vt 0.726011 0.263355 +vt 0.694237 0.263489 +vt 0.671675 0.241115 +vt 0.671541 0.209341 +vt 0.693915 0.186779 +vt 0.720229 0.200481 +vt 0.734607 0.214942 +vt 0.734549 0.235333 +vt 0.699697 0.249653 +vt 0.685319 0.235193 +vt 0.685377 0.214801 +vt 0.698700 0.197724 +vt 0.699837 0.200423 +vt 0.717050 0.208027 +vt 0.727023 0.218030 +vt 0.717000 0.242128 +vt 0.720088 0.249711 +vt 0.702875 0.242107 +vt 0.692923 0.217980 +vt 0.702925 0.208006 +vt 0.709963 0.225067 +vt 0.727003 0.232155 +vt 0.692902 0.232105 +vt 0.732501 0.288953 +vt 0.808616 0.296899 +vt 0.811862 0.288966 +vt 0.867969 0.345092 +vt 0.860064 0.348406 +vt 0.867956 0.424453 +vt 0.808516 0.472655 +vt 0.860023 0.421207 +vt 0.735715 0.472613 +vt 0.811830 0.480560 +vt 0.676362 0.424421 +vt 0.732469 0.480547 +vt 0.684308 0.348306 +vt 0.684267 0.421106 +vt 0.735815 0.296858 +vt 0.676375 0.345060 +vt 0.816436 0.277714 +vt 0.879160 0.340370 +vt 0.879208 0.429027 +vt 0.816552 0.491751 +vt 0.727895 0.491799 +vt 0.665171 0.429143 +vt 0.665123 0.340486 +vt 0.727779 0.277762 +vt 0.854530 0.350709 +vt 0.854481 0.418921 +vt 0.806213 0.467120 +vt 0.689801 0.418804 +vt 0.738118 0.302392 +vt 0.796797 0.325071 +vt 0.806330 0.302441 +vt 0.831787 0.359970 +vt 0.831851 0.409388 +vt 0.796952 0.444377 +vt 0.738000 0.467072 +vt 0.747534 0.444441 +vt 0.712480 0.360124 +vt 0.689850 0.350591 +vt 0.747379 0.325135 +vt 0.772165 0.384756 +vt 0.712544 0.409543 +vt 0.936516 0.662150 +vt 0.925781 0.687081 +vt 0.925904 0.662154 +vt 0.936009 0.712224 +vt 0.936393 0.687189 +vt 0.935364 0.737254 +vt 0.925399 0.712006 +vt 0.934457 0.762276 +vt 0.924757 0.736925 +vt 0.933290 0.787287 +vt 0.923854 0.761837 +vt 0.931862 0.812285 +vt 0.922692 0.786737 +vt 0.935978 0.612077 +vt 0.925766 0.637227 +vt 0.925368 0.612302 +vt 0.936377 0.637113 +vt 0.977643 0.804880 +vt 0.964969 0.788768 +vt 0.985505 0.790961 +vt 0.962419 0.809326 +vt 0.948522 0.801634 +vt 0.944319 0.786473 +vt 0.951722 0.772693 +vt 0.966795 0.768110 +vt 0.980837 0.775588 +vt 0.950938 0.668491 +vt 0.942916 0.649956 +vt 0.950812 0.649862 +vt 0.942974 0.687050 +vt 0.943042 0.668503 +vt 0.950869 0.687120 +vt 0.942712 0.705595 +vt 0.950150 0.724371 +vt 0.950607 0.705748 +vt 0.949498 0.742989 +vt 0.942257 0.724137 +vt 0.948653 0.761600 +vt 0.941609 0.742672 +vt 0.949976 0.612613 +vt 0.942597 0.631412 +vt 0.942085 0.612872 +vt 0.950491 0.631235 +vt 0.941463 0.602922 +vt 0.933325 0.595013 +vt 0.944754 0.594708 +vt 0.933437 0.606480 +vt 0.925246 0.603281 +vt 0.921826 0.595271 +vt 0.924875 0.587166 +vt 0.932809 0.583544 +vt 0.941063 0.586615 +vt 0.964622 0.663896 +vt 0.957245 0.647398 +vt 0.964309 0.647229 +vt 0.957697 0.680588 +vt 0.957558 0.663992 +vt 0.964762 0.680566 +vt 0.957663 0.697184 +vt 0.964520 0.713904 +vt 0.964728 0.697235 +vt 0.964138 0.730570 +vt 0.957456 0.713779 +vt 0.963582 0.747231 +vt 0.957075 0.730371 +vt 0.963160 0.613909 +vt 0.956760 0.630809 +vt 0.956101 0.614226 +vt 0.963821 0.630566 +vt 0.966387 0.601458 +vt 0.960697 0.594224 +vt 0.969917 0.595209 +vt 0.959552 0.603454 +vt 0.953312 0.600001 +vt 0.951425 0.593194 +vt 0.954749 0.587007 +vt 0.961516 0.584950 +vt 0.967821 0.588307 +vt 0.083073 0.606228 +vt 0.085384 0.597759 +vt 0.086301 0.607240 +vt 0.078615 0.611492 +vt 0.080139 0.614453 +vt 0.071760 0.612102 +vt 0.063539 0.609168 +vt 0.066540 0.607646 +vt 0.062718 0.599784 +vt 0.065953 0.600839 +vt 0.068810 0.592661 +vt 0.070357 0.595637 +vt 0.077139 0.595011 +vt 0.078104 0.591775 +vt 0.082418 0.599366 +vt 0.082116 0.618296 +vt 0.090224 0.608244 +vt 0.069261 0.619104 +vt 0.059844 0.610814 +vt 0.070723 0.615325 +vt 0.059071 0.598567 +vt 0.079084 0.587837 +vt 0.066805 0.589143 +vt 0.088848 0.595654 +vt 0.081426 0.605736 +vt 0.077820 0.609949 +vt 0.068075 0.606884 +vt 0.072308 0.610472 +vt 0.067570 0.601360 +vt 0.071164 0.597151 +vt 0.080920 0.600201 +vt 0.076664 0.596661 +vt 0.074512 0.603574 +vt 0.033491 0.587284 +vt 0.024749 0.596278 +vt 0.024943 0.587279 +vt 0.033676 0.596281 +vt 0.025277 0.601474 +vt 0.033565 0.613963 +vt 0.024829 0.618331 +vt 0.024851 0.613959 +vt 0.033145 0.601476 +vt 0.037809 0.615254 +vt 0.015524 0.598443 +vt 0.020610 0.615249 +vt 0.042899 0.598447 +vt 0.033490 0.587284 +vt 0.024749 0.596278 +vt 0.024943 0.587279 +vt 0.033675 0.596281 +vt 0.025277 0.601474 +vt 0.024851 0.613959 +vt 0.033582 0.618337 +vt 0.024829 0.618331 +vt 0.033145 0.601476 +vt 0.037809 0.615255 +vt 0.033565 0.613963 +vt 0.015524 0.598443 +vt 0.020610 0.615249 +vt 0.042899 0.598447 +vt 0.033490 0.587284 +vt 0.024749 0.596278 +vt 0.024943 0.587279 +vt 0.033675 0.596281 +vt 0.025277 0.601474 +vt 0.033565 0.613963 +vt 0.024829 0.618331 +vt 0.024851 0.613959 +vt 0.033145 0.601476 +vt 0.037809 0.615255 +vt 0.015524 0.598443 +vt 0.020610 0.615249 +vt 0.042899 0.598447 +vt 0.024749 0.596278 +vt 0.033490 0.587284 +vt 0.024943 0.587279 +vt 0.025277 0.601474 +vt 0.033675 0.596281 +vt 0.024829 0.618331 +vt 0.033565 0.613963 +vt 0.024851 0.613959 +vt 0.033145 0.601476 +vt 0.037809 0.615255 +vt 0.042899 0.598447 +vt 0.015524 0.598443 +vt 0.020610 0.615249 +vt 0.024749 0.596278 +vt 0.033490 0.587284 +vt 0.024943 0.587279 +vt 0.025277 0.601474 +vt 0.033675 0.596281 +vt 0.024851 0.613959 +vt 0.033582 0.618337 +vt 0.033565 0.613963 +vt 0.033145 0.601476 +vt 0.037809 0.615255 +vt 0.042899 0.598447 +vt 0.015524 0.598443 +vt 0.020610 0.615249 +vt 0.024749 0.596278 +vt 0.033490 0.587284 +vt 0.024943 0.587279 +vt 0.025277 0.601474 +vt 0.033675 0.596281 +vt 0.024829 0.618331 +vt 0.033565 0.613963 +vt 0.024851 0.613959 +vt 0.033145 0.601476 +vt 0.037809 0.615255 +vt 0.042899 0.598447 +vt 0.015524 0.598443 +vt 0.020610 0.615249 +vt 0.809981 0.556064 +vt 0.887363 0.527565 +vt 0.887444 0.556064 +vt 0.887330 0.609014 +vt 0.809559 0.596405 +vt 0.911687 0.596841 +vt 0.809982 0.568341 +vt 0.911748 0.568341 +vt 0.964552 0.077029 +vt 0.993051 0.556065 +vt 0.964551 0.556064 +vt 0.940248 0.064752 +vt 0.940247 0.568341 +vt 0.911748 0.064752 +vt 0.887444 0.077029 +vt 0.887365 0.105529 +vt 0.809982 0.077031 +vt 0.887330 0.024079 +vt 0.809559 0.036690 +vt 0.809506 0.024414 +vt 0.911686 0.036252 +vt 0.809982 0.064754 +vt 0.902374 0.847089 +vt 0.895490 0.854195 +vt 0.895713 0.847089 +vt 0.894022 0.897717 +vt 0.899243 0.908492 +vt 0.889531 0.900967 +vt 0.894125 0.868140 +vt 0.883819 0.876961 +vt 0.889208 0.866040 +vt 0.888926 0.878236 +vt 0.883461 0.889739 +vt 0.888782 0.888740 +vt 0.897316 0.894063 +vt 0.902991 0.904130 +vt 0.897536 0.873143 +vt 0.893967 0.879556 +vt 0.893744 0.886968 +vt 0.900942 0.887480 +vt 0.902374 0.847089 +vt 0.895490 0.854195 +vt 0.902597 0.854196 +vt 0.899243 0.908492 +vt 0.894022 0.897717 +vt 0.889531 0.900967 +vt 0.883819 0.876961 +vt 0.894125 0.868140 +vt 0.889208 0.866040 +vt 0.883461 0.889739 +vt 0.888926 0.878236 +vt 0.888782 0.888740 +vt 0.902991 0.904130 +vt 0.897316 0.894063 +vt 0.897536 0.873143 +vt 0.893967 0.879556 +vt 0.893744 0.886968 +vt 0.900942 0.887480 +vt 0.867209 0.260224 +vt 0.806559 0.246817 +vt 0.799054 0.201333 +vt 0.799057 0.260549 +vt 0.740987 0.502522 +vt 0.758019 0.540402 +vt 0.070105 0.356815 +vt 0.919581 0.821533 +vt 0.989715 0.821538 +vt 0.623923 0.494225 +vt 0.074473 0.541539 +vt 0.074473 0.541539 +vt 0.074473 0.541539 +vt 0.074473 0.541539 +vt 0.074473 0.541539 +vt 0.020884 0.555877 +vt 0.721801 0.167245 +vt 0.751288 0.167245 +vt 0.781759 0.167245 +vt 0.811738 0.167245 +vt 0.697228 0.167245 +vt 0.686577 0.127636 +vt 0.725023 0.067815 +vt 0.719996 0.129110 +vt 0.734754 0.067815 +vt 0.750467 0.128128 +vt 0.744707 0.067815 +vt 0.784869 0.130093 +vt 0.747952 0.067718 +vt 0.686577 0.090285 +vt 0.715735 0.067815 +vt 0.806559 0.246817 +vt 0.799054 0.201333 +vt 0.867209 0.260224 +vt 0.921270 0.811624 +vt 0.940767 0.761200 +vt 0.956522 0.746959 +vt 0.033582 0.618337 +vt 0.033582 0.618337 +vt 0.033582 0.618337 +vt 0.024829 0.618331 +vt 0.033582 0.618337 +vt 0.809540 0.528001 +vt 0.809507 0.608682 +vt 0.993052 0.077029 +vt 0.809541 0.105094 +vt 0.902597 0.854196 +vt 0.903482 0.898762 +vt 0.901280 0.880111 +vt 0.895713 0.847089 +vt 0.903482 0.898762 +vt 0.901280 0.880111 +vt 0.536032 0.391216 +vt 0.536064 0.368400 +vt 0.078991 0.390601 +vt 0.079019 0.367780 +vt 0.889719 0.832222 +vt 0.889719 0.847089 +vt 0.895936 0.832222 +vt 0.902152 0.832222 +vt 0.908368 0.847089 +vt 0.883503 0.832222 +vt 0.883503 0.847089 +vt 0.902374 0.631317 +vt 0.895936 0.800033 +vt 0.895713 0.631317 +vt 0.889719 0.631317 +vt 0.889719 0.800033 +vt 0.883503 0.631317 +vt 0.883503 0.800033 +vt 0.908368 0.800033 +vt 0.902152 0.800033 +vt 0.896770 0.813795 +vt 0.890554 0.813795 +vt 0.885845 0.813795 +vt 0.907696 0.813795 +vt 0.902986 0.813795 +vt 0.896770 0.829528 +vt 0.885845 0.829528 +vt 0.907696 0.829528 +vt 0.908368 0.832222 +vt 0.890554 0.829528 +vt 0.902986 0.829528 +vt 0.889719 0.832222 +vt 0.889719 0.847089 +vt 0.895936 0.832222 +vt 0.908368 0.847089 +vt 0.902152 0.832222 +vt 0.883503 0.832222 +vt 0.883503 0.847089 +vt 0.895936 0.800033 +vt 0.902374 0.631317 +vt 0.895713 0.631317 +vt 0.889719 0.631317 +vt 0.889719 0.800033 +vt 0.883503 0.631317 +vt 0.883503 0.800033 +vt 0.908368 0.800033 +vt 0.902152 0.800033 +vt 0.896770 0.813795 +vt 0.890554 0.813795 +vt 0.885845 0.813795 +vt 0.907696 0.813795 +vt 0.902986 0.813795 +vt 0.896770 0.829528 +vt 0.890554 0.829528 +vt 0.885845 0.829528 +vt 0.907696 0.829528 +vt 0.902986 0.829528 +vt 0.908367 0.631317 +vt 0.908368 0.832222 +vt 0.908367 0.631317 +vn 0.0871 0.0000 0.9962 +vn 0.0616 0.0616 0.9962 +vn 0.0000 0.0871 0.9962 +vn -0.0616 0.0616 0.9962 +vn -0.0871 0.0000 0.9962 +vn -0.0616 -0.0616 0.9962 +vn 0.0000 -0.0871 0.9962 +vn 0.0616 -0.0616 0.9962 +vn 0.5728 0.5728 0.5863 +vn 0.0000 0.8101 0.5863 +vn -0.5728 0.5728 0.5863 +vn -0.8101 0.0000 0.5863 +vn -0.5728 -0.5728 0.5863 +vn 0.0000 -0.8101 0.5863 +vn 0.5728 -0.5728 0.5863 +vn 0.8101 0.0000 0.5863 +vn -0.4429 -0.4429 0.7796 +vn 0.0000 -0.6263 0.7796 +vn 0.4429 -0.4429 0.7796 +vn 0.6263 -0.0000 0.7795 +vn 0.4429 0.4429 0.7796 +vn 0.0000 0.6263 0.7795 +vn -0.4429 0.4429 0.7796 +vn -0.6263 0.0000 0.7796 +vn 0.3221 0.3221 0.8903 +vn 0.0000 0.4555 0.8902 +vn -0.3221 0.3221 0.8902 +vn -0.4555 0.0000 0.8902 +vn -0.3221 -0.3221 0.8902 +vn 0.0000 -0.4555 0.8902 +vn 0.3221 -0.3221 0.8903 +vn 0.4555 0.0000 0.8902 +vn 0.1011 0.1011 0.9897 +vn 0.0000 0.1430 0.9897 +vn -0.1011 0.1011 0.9897 +vn -0.1430 0.0000 0.9897 +vn -0.1011 -0.1011 0.9897 +vn 0.0000 -0.1430 0.9897 +vn 0.1011 -0.1011 0.9897 +vn 0.1430 0.0000 0.9897 +vn 0.2874 0.0000 0.9578 +vn 0.2032 0.2032 0.9578 +vn 0.0000 0.2874 0.9578 +vn -0.2032 0.2032 0.9578 +vn -0.2874 0.0000 0.9578 +vn -0.2032 -0.2032 0.9578 +vn 0.0000 -0.2874 0.9578 +vn 0.2032 -0.2032 0.9578 +vn 0.6919 0.6919 0.2063 +vn 0.0000 0.9785 0.2063 +vn -0.6919 0.6919 0.2063 +vn -0.9785 0.0000 0.2063 +vn -0.6919 -0.6919 0.2063 +vn 0.0000 -0.9785 0.2063 +vn 0.6919 -0.6919 0.2063 +vn 0.9785 0.0000 0.2063 +vn -0.6347 -0.6347 0.4409 +vn 0.0000 -0.8976 0.4409 +vn 0.6347 -0.6347 0.4409 +vn 0.8976 0.0000 0.4409 +vn 0.6347 0.6347 0.4409 +vn 0.0000 0.8975 0.4409 +vn -0.6347 0.6347 0.4409 +vn -0.8975 0.0000 0.4409 +vn 0.4309 0.4309 0.7929 +vn 0.0000 0.6094 0.7929 +vn -0.4309 0.4309 0.7929 +vn -0.6094 0.0000 0.7929 +vn -0.4309 -0.4309 0.7929 +vn 0.0000 -0.6094 0.7929 +vn 0.4309 -0.4309 0.7929 +vn 0.6094 0.0000 0.7929 +vn 0.1444 0.1445 0.9789 +vn 0.0000 0.2043 0.9789 +vn -0.1445 0.1444 0.9789 +vn -0.2043 0.0000 0.9789 +vn -0.1445 -0.1444 0.9789 +vn 0.0000 -0.2043 0.9789 +vn 0.1445 -0.1444 0.9789 +vn 0.2043 0.0000 0.9789 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 0.6719 0.7406 +vn -0.2417 0.0000 0.9704 +vn 0.0000 -0.6719 0.7406 +vn 0.2417 0.0000 0.9704 +vn -0.8899 -0.0000 0.4562 +vn 0.0374 0.0000 0.9993 +vn 0.9149 0.0000 0.4037 +vn 0.6808 0.0000 0.7325 +vn 0.0000 0.7525 0.6586 +vn 0.5186 0.8478 0.1109 +vn 0.9962 -0.0000 -0.0875 +vn 0.4342 -0.8931 0.1176 +vn 0.6808 -0.0027 0.7325 +vn -0.8582 0.0000 0.5132 +vn -0.8582 0.0035 0.5132 +vn 1.0000 0.0072 0.0000 +vn -0.9962 0.0000 -0.0875 +vn -0.6808 0.0000 0.7325 +vn 0.0000 0.0000 -1.0000 +vn -0.5186 0.8478 0.1109 +vn 0.0000 -0.8103 0.5861 +vn -0.4342 -0.8931 0.1176 +vn -1.0000 0.0072 0.0000 +vn -0.6808 -0.0027 0.7325 +vn 0.8582 0.0000 0.5132 +vn 0.8582 0.0035 0.5132 +vn 0.0000 0.7661 0.6427 +vn 0.0000 -0.5678 0.8231 +vn 0.0000 -0.9847 0.1741 +vn 0.9368 0.0000 0.3498 +vn 0.0000 0.9847 0.1741 +vn -0.9368 0.0000 0.3498 +vn -0.9999 -0.0160 0.0000 +vn -0.9999 -0.0000 0.0155 +vn 0.9999 0.0000 0.0155 +vn -0.9992 0.0382 -0.0149 +vn 0.0000 0.9963 0.0858 +vn 0.9992 0.0382 -0.0149 +vn 0.9702 0.2425 0.0000 +vn 0.0000 0.0772 -0.9970 +vn -0.9702 0.2425 0.0000 +vn 0.0000 0.0772 0.9970 +vn 0.5544 -0.0000 0.8322 +vn 0.0000 0.5532 0.8330 +vn -0.5544 0.0000 0.8322 +vn 0.0000 -0.5533 0.8330 +vn 0.0000 0.9786 0.2056 +vn -0.9930 0.0000 0.1185 +vn 0.0000 -0.9786 0.2058 +vn 0.9930 0.0000 0.1185 +vn 0.0000 0.9786 0.2058 +vn 0.0000 -0.9799 0.1994 +vn 0.0000 0.9799 0.1994 +vn 0.9845 0.0000 0.1754 +vn -0.9845 0.0000 0.1754 +vn 0.0000 -0.4146 -0.9100 +vn 0.0000 0.4146 -0.9100 +vn -0.3025 0.0000 -0.9532 +vn 0.3024 0.0000 -0.9532 +vn 0.0000 -0.9667 -0.2561 +vn 0.0000 -0.4437 0.8962 +vn 0.0000 0.7897 0.6135 +vn 0.9845 -0.0778 0.1572 +vn -0.9845 -0.0778 0.1572 +vn 0.0000 0.0322 -0.9995 +vn 0.0000 0.7754 -0.6315 +vn -0.3025 0.4229 -0.8542 +vn 0.3025 0.4229 -0.8542 +vn 0.0000 -0.8962 -0.4437 +vn 0.0000 0.8962 0.4437 +vn 0.0000 -0.9585 -0.2850 +vn 0.0000 -0.4705 0.8824 +vn 0.0000 0.7709 0.6369 +vn 0.9845 -0.0825 0.1548 +vn -0.9845 -0.0825 0.1548 +vn 0.0000 0.0623 -0.9981 +vn 0.0000 0.7939 -0.6080 +vn -0.3024 0.4484 -0.8411 +vn 0.3025 0.4484 -0.8411 +vn 0.0000 -0.8824 -0.4704 +vn 0.0000 0.8824 0.4704 +vn 0.0000 -0.9092 0.4164 +vn 0.0000 0.2267 0.9740 +vn 0.0000 0.9996 -0.0279 +vn 0.9845 0.0398 0.1708 +vn -0.9845 0.0398 0.1708 +vn 0.0000 -0.6101 -0.7923 +vn 0.0000 0.1975 -0.9803 +vn -0.3025 -0.2161 -0.9283 +vn 0.3024 -0.2161 -0.9283 +vn 0.0000 -0.9740 0.2267 +vn 0.0000 0.9740 -0.2267 +vn 0.0000 -0.9796 -0.2008 +vn 0.0000 -0.3921 0.9199 +vn 0.0000 0.8232 0.5677 +vn 0.9845 -0.0688 0.1614 +vn -0.9845 -0.0688 0.1614 +vn 0.0000 -0.0246 -0.9997 +vn 0.0000 0.7382 -0.6745 +vn -0.3025 0.3738 -0.8768 +vn 0.3025 0.3738 -0.8768 +vn 0.0000 -0.9199 -0.3921 +vn 0.0000 0.9199 0.3921 +vn 0.8899 0.0000 0.4562 +vn -0.0374 0.0000 0.9993 +vn -0.9149 0.0000 0.4037 +vn -0.8976 0.0000 0.4409 +vn 0.8975 0.0000 0.4409 +vn 0.6093 0.0000 0.7929 +vn -0.1444 0.1445 0.9789 +vn 0.1445 0.1444 0.9789 +vn 0.4429 -0.4429 0.7795 +vn -0.6263 -0.0000 0.7795 +vn 0.6263 0.0000 0.7796 +vn -0.3221 0.3221 0.8903 +vn 0.3221 0.3221 0.8902 +vn 0.3221 -0.3221 0.8902 +vn 0.7071 0.7071 0.0051 +vn 0.0000 1.0000 0.0051 +vn -0.7071 0.7071 0.0051 +vn -1.0000 0.0000 0.0051 +vn -0.7071 -0.7071 0.0051 +vn 0.0000 -1.0000 0.0051 +vn 0.7071 -0.7071 0.0051 +vn 1.0000 0.0000 0.0051 +vn 0.7071 0.7071 0.0028 +vn 0.0000 1.0000 0.0028 +vn -0.7071 0.7071 0.0028 +vn -1.0000 0.0000 0.0028 +vn -0.7071 -0.7071 0.0028 +vn 0.0000 -1.0000 0.0028 +vn 0.7071 -0.7071 0.0028 +vn 1.0000 0.0000 0.0028 +vn 0.7071 0.7071 0.0023 +vn 0.0000 1.0000 0.0023 +vn -0.7071 0.7071 0.0023 +vn -1.0000 0.0000 0.0023 +vn -0.7071 -0.7071 0.0023 +vn 0.0000 -1.0000 0.0023 +vn 0.7071 -0.7071 0.0023 +vn 1.0000 0.0000 0.0023 +vn 0.3326 0.0000 0.9431 +vn 0.2352 0.2352 0.9431 +vn 0.0000 0.3326 0.9431 +vn -0.2352 0.2352 0.9431 +vn -0.3326 0.0000 0.9431 +vn -0.2352 -0.2352 0.9431 +vn 0.0000 -0.3326 0.9431 +vn 0.2352 -0.2352 0.9431 +vn 0.7060 0.7060 0.0564 +vn 0.0000 0.9984 0.0564 +vn -0.7060 0.7060 0.0564 +vn -0.9984 0.0000 0.0564 +vn -0.7060 -0.7060 0.0564 +vn 0.0000 -0.9984 0.0564 +vn 0.7060 -0.7060 0.0564 +vn 0.9984 0.0000 0.0564 +vn -0.6923 -0.6923 0.2034 +vn 0.0000 -0.9791 0.2034 +vn 0.6923 -0.6923 0.2034 +vn 0.9791 0.0000 0.2034 +vn 0.6923 0.6924 0.2034 +vn 0.0000 0.9791 0.2035 +vn -0.6923 0.6923 0.2035 +vn -0.9791 0.0000 0.2035 +vn -0.0813 -0.0813 0.9934 +vn 0.0000 -0.1150 0.9934 +vn 0.0813 -0.0813 0.9934 +vn 0.1150 0.0000 0.9934 +vn 0.0813 0.0813 0.9934 +vn 0.0000 0.1150 0.9934 +vn -0.0813 0.0813 0.9934 +vn -0.1150 0.0000 0.9934 +vn 0.0000 0.3534 0.9355 +vn 1.0000 -0.0000 -0.0001 +vn 0.0042 -0.0021 1.0000 +vn 0.0042 -0.0021 -1.0000 +vn 0.0000 -0.0028 1.0000 +vn -0.0042 -0.0021 -1.0000 +vn 0.8895 0.4570 0.0000 +vn 0.0000 0.4570 0.8895 +vn -0.8895 0.4570 0.0000 +vn 0.0000 0.4570 -0.8895 +vn -0.4429 -0.4429 0.7795 +vn -0.6346 0.6346 0.4410 +vn 0.9999 -0.0160 0.0000 +vn 0.0000 -0.5678 0.8232 +vn 0.0000 -0.8102 0.5861 +vn 0.0000 0.7940 -0.6080 +vn -0.3025 0.4484 -0.8411 +vn 0.3025 -0.2161 -0.9283 +vn 0.0000 -0.9199 -0.3922 +vn 0.0000 0.9199 0.3922 +vn 0.6346 0.6346 0.4410 +vn -0.3221 -0.3221 0.8903 +vn 0.6923 0.6923 0.2035 +vn -1.0000 -0.0000 -0.0001 +vn 0.0000 -0.0028 -1.0000 +vn -0.0042 -0.0021 1.0000 +vn -0.0544 0.9709 -0.2333 +vn 0.0000 0.9723 -0.2337 +vn -0.0542 0.2334 0.9709 +vn 0.0000 0.2337 0.9723 +vn -0.0544 -0.9709 0.2333 +vn 0.0000 -0.9723 0.2337 +vn -0.0542 -0.2334 -0.9709 +vn 0.0000 -0.2337 -0.9723 +vn 0.0066 0.2337 0.9723 +vn 0.0066 0.9723 -0.2337 +vn 0.0065 -0.2337 -0.9723 +vn 0.0067 -0.2337 -0.9723 +vn 0.0066 -0.9723 0.2337 +vn 0.0725 0.9698 -0.2331 +vn 0.0725 -0.2331 -0.9697 +vn 0.0725 -0.9698 0.2331 +vn 0.0725 0.2331 0.9697 +vn -0.1590 0.9599 -0.2307 +vn -0.0549 0.9708 -0.2334 +vn -0.1554 -0.2309 -0.9605 +vn -0.0550 -0.2333 -0.9708 +vn -0.1590 -0.9599 0.2307 +vn -0.0549 -0.9708 0.2333 +vn -0.1554 0.2309 0.9605 +vn -0.0550 0.2333 0.9708 +vn -0.1570 0.9603 -0.2308 +vn -0.1611 -0.2307 -0.9596 +vn -0.1570 -0.9603 0.2308 +vn -0.1611 0.2307 0.9596 +vn 0.0544 0.9709 -0.2334 +vn 0.0542 0.2333 0.9709 +vn 0.0544 -0.9709 0.2333 +vn 0.0542 -0.2333 -0.9709 +vn -0.0066 0.2337 0.9723 +vn -0.0066 0.9723 -0.2337 +vn -0.0065 -0.2337 -0.9723 +vn -0.0067 -0.2337 -0.9723 +vn -0.0066 -0.9723 0.2337 +vn -0.0725 0.9698 -0.2331 +vn -0.0725 -0.2331 -0.9697 +vn -0.0725 -0.9698 0.2331 +vn -0.0725 0.2331 0.9697 +vn 0.1590 0.9599 -0.2308 +vn 0.1570 0.9603 -0.2308 +vn 0.0550 -0.2333 -0.9708 +vn 0.1554 -0.2309 -0.9605 +vn 0.1590 -0.9599 0.2307 +vn 0.1570 -0.9602 0.2308 +vn 0.0550 0.2333 0.9708 +vn 0.1554 0.2309 0.9605 +vn 0.1611 -0.2307 -0.9596 +vn 0.1611 0.2307 0.9596 +vn 0.0549 0.9708 -0.2334 +vn 0.0549 -0.9708 0.2333 +usemtl Boombox_mat +s off +f 2/1/1 4/2/1 1/3/1 +f 2/1/2 5/4/2 6/5/2 +f 6/5/3 7/6/3 8/7/3 +f 7/6/4 10/8/4 8/7/4 +f 9/9/5 12/10/5 10/8/5 +f 12/10/6 13/11/6 14/12/6 +f 13/11/7 16/13/7 14/12/7 +f 15/14/8 3/15/8 16/13/8 +f 17/16/9 5/4/9 1/3/9 +f 18/17/10 7/6/10 5/4/10 +f 19/18/11 9/9/11 7/6/11 +f 20/19/12 11/20/12 9/9/12 +f 21/21/13 13/11/13 11/20/13 +f 23/22/14 13/11/14 22/23/14 +f 24/24/15 15/14/15 23/22/15 +f 17/16/16 4/2/16 24/24/16 +f 2/1/17 25/25/17 26/26/17 +f 6/5/18 27/27/18 25/25/18 +f 10/8/19 27/27/19 8/7/19 +f 12/10/20 28/28/20 10/8/20 +f 12/10/21 30/29/21 29/30/21 +f 16/13/22 30/29/22 14/12/22 +f 16/13/23 32/31/23 31/32/23 +f 2/1/24 32/31/24 3/15/24 +f 25/25/25 34/33/25 26/26/25 +f 27/27/26 33/34/26 25/25/26 +f 28/28/27 35/35/27 27/27/27 +f 29/30/28 36/36/28 28/28/28 +f 30/29/29 37/37/29 29/30/29 +f 30/29/30 39/38/30 38/39/30 +f 31/32/31 40/40/31 39/38/31 +f 32/31/32 34/33/32 40/40/32 +f 34/33/33 33/34/33 41/41/33 +f 33/34/34 35/35/34 41/41/34 +f 35/35/35 36/36/35 41/41/35 +f 36/36/36 37/37/36 41/41/36 +f 37/37/37 38/39/37 41/41/37 +f 38/39/38 39/38/38 41/41/38 +f 39/38/39 40/40/39 41/41/39 +f 40/40/40 34/33/40 41/41/40 +f 43/42/41 45/43/41 42/44/41 +f 42/44/42 47/45/42 43/42/42 +f 46/46/43 49/47/43 47/45/43 +f 48/48/44 51/49/44 49/47/44 +f 51/49/45 52/50/45 53/51/45 +f 53/51/46 54/52/46 55/53/46 +f 55/53/47 56/54/47 57/55/47 +f 57/55/48 45/43/48 44/56/48 +f 59/57/49 42/44/49 58/58/49 +f 60/59/50 46/46/50 59/57/50 +f 61/60/51 48/48/51 60/59/51 +f 61/60/52 52/50/52 50/61/52 +f 62/62/53 54/52/53 52/50/53 +f 63/63/54 56/54/54 54/52/54 +f 64/64/55 45/43/55 56/54/55 +f 58/58/56 45/43/56 65/65/56 +f 47/45/57 67/66/57 43/42/57 +f 49/47/58 66/67/58 47/45/58 +f 51/49/59 68/68/59 49/47/59 +f 51/49/60 70/69/60 69/70/60 +f 53/51/61 71/71/61 70/69/61 +f 57/55/62 71/71/62 55/53/62 +f 44/56/63 72/72/63 57/55/63 +f 43/42/64 73/73/64 44/56/64 +f 66/67/65 75/74/65 67/66/65 +f 68/68/66 74/75/66 66/67/66 +f 68/68/67 77/76/67 76/77/67 +f 70/69/68 77/76/68 69/70/68 +f 71/71/69 78/78/69 70/69/69 +f 71/71/70 80/79/70 79/80/70 +f 72/72/71 81/81/71 80/79/71 +f 67/66/72 81/81/72 73/73/72 +f 75/74/73 74/75/73 82/82/73 +f 74/75/74 76/77/74 82/82/74 +f 76/77/75 77/76/75 82/82/75 +f 77/76/76 78/78/76 82/82/76 +f 78/78/77 79/80/77 82/82/77 +f 79/80/78 80/79/78 82/82/78 +f 80/79/79 81/81/79 82/82/79 +f 81/81/80 75/74/80 82/82/80 +f 84/83/81 86/84/81 83/85/81 +f 88/86/82 90/87/82 87/88/82 +f 89/89/83 85/90/83 84/91/83 +f 83/92/84 87/88/84 90/93/84 +f 85/90/85 92/94/85 86/84/85 +f 85/90/85 93/95/85 91/96/85 +f 87/88/85 93/95/85 88/86/85 +f 86/84/85 94/97/85 87/88/85 +f 92/94/86 91/96/86 95/98/86 +f 93/95/87 95/98/87 91/96/87 +f 93/95/88 94/97/88 96/99/88 +f 92/94/89 96/99/89 94/97/89 +f 98/100/90 100/101/90 97/102/90 +f 101/103/91 99/104/91 98/100/91 +f 103/105/92 102/106/92 101/103/92 +f 97/107/81 101/103/81 98/100/81 +f 102/106/82 100/108/82 99/104/82 +f 105/109/93 106/110/93 107/111/93 +f 108/112/94 110/113/94 111/114/94 +f 112/115/95 108/112/95 111/114/95 +f 112/115/96 114/116/96 115/117/96 +f 112/115/97 115/117/97 108/112/97 +f 106/110/98 105/109/98 109/118/98 +f 110/113/99 116/119/99 117/120/99 +f 105/109/100 110/113/100 109/118/100 +f 118/121/101 119/122/101 112/115/101 +f 118/121/96 112/115/96 111/114/96 +f 117/120/85 111/114/85 110/113/85 +f 120/123/102 121/124/102 122/125/102 +f 122/125/85 117/120/85 123/126/85 +f 124/127/103 126/128/103 127/129/103 +f 128/130/104 129/131/104 130/132/104 +f 132/133/85 134/134/85 131/135/85 +f 135/136/94 136/137/94 137/138/94 +f 121/124/105 120/123/105 135/136/105 +f 138/139/94 109/118/94 137/138/94 +f 121/124/102 139/140/102 140/141/102 +f 142/142/85 144/143/85 141/144/85 +f 137/138/106 139/140/106 135/136/106 +f 121/124/107 135/136/107 139/140/107 +f 122/125/108 121/124/108 145/145/108 +f 145/146/81 118/121/81 122/125/81 +f 128/130/82 140/147/82 146/148/82 +f 138/139/109 137/138/109 125/149/109 +f 136/137/110 126/128/110 125/149/110 +f 125/149/111 137/138/111 136/137/111 +f 123/126/112 116/119/112 126/128/112 +f 120/123/85 123/126/85 136/137/85 +f 126/128/113 147/150/113 127/129/113 +f 137/138/106 115/117/106 139/140/106 +f 115/117/106 109/118/106 108/112/106 +f 127/129/85 107/111/85 124/127/85 +f 148/151/85 106/110/85 138/139/85 +f 124/127/85 148/151/85 138/139/85 +f 150/152/85 107/111/85 151/153/85 +f 107/111/85 149/154/85 151/153/85 +f 148/155/114 134/134/114 149/156/114 +f 150/157/115 131/135/115 148/158/115 +f 150/159/116 133/160/116 132/133/116 +f 149/161/117 133/160/117 151/162/117 +f 124/127/103 138/139/103 125/149/103 +f 107/111/93 116/119/93 105/109/93 +f 128/163/118 121/124/118 140/141/118 +f 121/124/119 128/163/119 145/145/119 +f 113/164/120 112/115/120 119/122/120 +f 146/148/82 140/147/82 152/165/82 +f 155/166/82 157/167/82 154/168/82 +f 128/130/82 146/148/82 129/131/82 +f 159/169/104 161/170/104 158/171/104 +f 145/172/104 161/170/104 119/173/104 +f 145/172/104 130/132/104 158/171/104 +f 145/172/104 128/130/104 130/132/104 +f 152/174/104 163/175/104 146/176/104 +f 152/174/121 158/177/121 162/178/121 +f 158/177/122 163/179/122 162/178/122 +f 130/180/123 146/181/123 163/179/123 +f 164/182/82 153/183/82 114/184/82 +f 165/185/82 167/186/82 161/187/82 +f 119/173/104 161/170/104 167/188/104 +f 113/189/82 164/182/82 114/184/82 +f 168/190/82 164/182/82 113/189/82 +f 167/188/104 168/190/104 113/189/104 +f 119/173/104 167/188/104 113/189/104 +f 153/191/83 161/187/83 160/192/83 +f 164/193/104 165/194/104 153/195/104 +f 168/196/84 166/197/84 164/193/84 +f 169/198/124 154/168/124 170/199/124 +f 171/200/125 155/166/125 169/201/125 +f 171/202/126 157/167/126 156/203/126 +f 170/204/127 157/167/127 172/205/127 +f 174/206/128 142/142/128 141/144/128 +f 175/207/129 142/142/129 173/208/129 +f 176/209/130 143/210/130 175/207/130 +f 174/206/131 144/143/131 176/209/131 +f 178/211/81 180/212/81 177/213/81 +f 179/214/85 182/215/85 180/212/85 +f 181/216/82 184/217/82 182/215/82 +f 183/218/83 179/214/83 178/219/83 +f 177/220/84 182/215/84 184/221/84 +f 186/222/81 188/223/81 185/224/81 +f 187/225/85 190/226/85 188/223/85 +f 189/227/82 192/228/82 190/226/82 +f 191/229/83 187/225/83 186/230/83 +f 185/231/84 190/226/84 192/232/84 +f 194/233/81 196/234/81 193/235/81 +f 195/236/85 198/237/85 196/234/85 +f 197/238/82 200/239/82 198/237/82 +f 199/240/83 195/236/83 194/241/83 +f 193/242/84 198/237/84 200/243/84 +f 202/244/81 204/245/81 201/246/81 +f 203/247/85 206/248/85 204/245/85 +f 205/249/82 208/250/82 206/248/82 +f 207/251/83 203/247/83 202/252/83 +f 201/253/84 206/248/84 208/254/84 +f 210/255/81 212/256/81 209/257/81 +f 211/258/85 214/259/85 212/256/85 +f 213/260/82 216/261/82 214/259/82 +f 215/262/83 211/258/83 210/263/83 +f 209/264/84 214/259/84 216/265/84 +f 218/266/81 220/267/81 217/268/81 +f 219/269/85 222/270/85 220/267/85 +f 221/271/82 224/272/82 222/270/82 +f 223/273/83 219/269/83 218/274/83 +f 217/275/84 222/270/84 224/276/84 +f 226/277/81 228/278/81 225/279/81 +f 230/280/85 232/281/85 229/282/85 +f 234/283/82 236/284/82 233/285/82 +f 235/286/83 227/287/83 226/288/83 +f 225/289/84 233/285/84 236/290/84 +f 228/278/85 237/291/85 238/292/85 +f 227/287/85 239/293/85 237/291/85 +f 233/285/85 239/293/85 234/283/85 +f 228/278/85 240/294/85 233/285/85 +f 237/291/132 229/282/132 238/292/132 +f 237/291/133 231/295/133 230/280/133 +f 240/294/134 231/295/134 239/293/134 +f 238/292/135 232/281/135 240/294/135 +f 242/296/81 244/297/81 241/298/81 +f 246/299/85 248/300/85 245/301/85 +f 250/302/82 252/303/82 249/304/82 +f 251/305/83 243/306/83 242/307/83 +f 241/308/84 249/304/84 252/309/84 +f 243/306/85 254/310/85 244/297/85 +f 243/306/85 255/311/85 253/312/85 +f 249/304/85 255/311/85 250/302/85 +f 244/297/85 256/313/85 249/304/85 +f 253/312/136 245/301/136 254/310/136 +f 255/311/133 246/299/133 253/312/133 +f 256/313/134 247/314/134 255/311/134 +f 256/313/135 245/301/135 248/300/135 +f 258/315/81 260/316/81 257/317/81 +f 262/318/85 264/319/85 261/320/85 +f 266/321/82 268/322/82 265/323/82 +f 267/324/83 259/325/83 258/326/83 +f 257/327/84 265/323/84 268/328/84 +f 259/325/85 270/329/85 260/316/85 +f 259/325/85 271/330/85 269/331/85 +f 266/321/85 272/332/85 271/330/85 +f 260/316/85 272/332/85 265/323/85 +f 269/331/132 261/320/132 270/329/132 +f 269/331/133 263/333/133 262/318/133 +f 272/332/134 263/333/134 271/330/134 +f 270/329/135 264/319/135 272/332/135 +f 274/334/81 276/335/81 273/336/81 +f 278/337/85 280/338/85 277/339/85 +f 282/340/82 284/341/82 281/342/82 +f 283/343/83 275/344/83 274/345/83 +f 273/346/84 281/342/84 284/347/84 +f 275/344/85 286/348/85 276/335/85 +f 275/344/85 287/349/85 285/350/85 +f 281/342/85 287/349/85 282/340/85 +f 276/335/85 288/351/85 281/342/85 +f 285/350/132 277/339/132 286/348/132 +f 285/350/133 279/352/133 278/337/133 +f 288/351/134 279/352/134 287/349/134 +f 286/348/135 280/338/135 288/351/135 +f 290/353/81 292/354/81 289/355/81 +f 294/356/85 296/357/85 293/358/85 +f 298/359/82 300/360/82 297/361/82 +f 299/362/83 291/363/83 290/364/83 +f 289/365/84 297/361/84 300/366/84 +f 292/354/85 301/367/85 302/368/85 +f 291/363/85 303/369/85 301/367/85 +f 297/361/85 303/369/85 298/359/85 +f 292/354/85 304/370/85 297/361/85 +f 301/367/132 293/358/132 302/368/132 +f 301/367/133 295/371/133 294/356/133 +f 304/370/134 295/371/134 303/369/134 +f 302/368/135 296/357/135 304/370/135 +f 306/372/137 308/373/137 305/374/137 +f 307/375/85 310/376/85 308/377/85 +f 309/378/138 312/379/138 310/376/138 +f 311/380/139 307/381/139 306/382/139 +f 312/379/140 308/373/140 310/376/140 +f 305/383/141 314/384/141 306/385/141 +f 312/379/142 315/386/142 316/387/142 +f 312/379/143 313/388/143 305/383/143 +f 306/385/144 315/386/144 311/380/144 +f 313/389/81 318/390/81 314/391/81 +f 315/392/82 320/393/82 316/394/82 +f 316/394/84 317/395/84 313/389/84 +f 314/396/83 319/397/83 315/392/83 +f 322/398/145 324/399/145 321/400/145 +f 323/401/146 326/402/146 324/403/146 +f 325/404/147 328/405/147 326/402/147 +f 327/406/148 323/407/148 322/408/148 +f 328/405/149 324/399/149 326/402/149 +f 321/409/150 330/410/150 322/411/150 +f 328/405/151 331/412/151 332/413/151 +f 328/405/152 329/414/152 321/409/152 +f 327/406/153 330/410/153 331/412/153 +f 329/415/154 334/416/154 330/417/154 +f 331/418/155 336/419/155 332/420/155 +f 329/415/84 336/419/84 333/421/84 +f 330/422/83 335/423/83 331/418/83 +f 338/424/156 340/425/156 337/426/156 +f 339/427/157 342/428/157 340/429/157 +f 341/430/158 344/431/158 342/428/158 +f 343/432/159 339/433/159 338/434/159 +f 344/431/160 340/425/160 342/428/160 +f 338/435/161 345/436/161 346/437/161 +f 343/432/162 348/438/162 344/431/162 +f 337/439/163 348/438/163 345/436/163 +f 338/435/164 347/440/164 343/432/164 +f 345/441/165 350/442/165 346/443/165 +f 347/444/166 352/445/166 348/446/166 +f 345/441/84 352/445/84 349/447/84 +f 346/448/83 351/449/83 347/444/83 +f 354/450/167 356/451/167 353/452/167 +f 355/453/168 358/454/168 356/455/168 +f 357/456/169 360/457/169 358/454/169 +f 359/458/170 355/459/170 354/460/170 +f 360/457/171 356/451/171 358/454/171 +f 353/461/172 362/462/172 354/463/172 +f 360/457/173 363/464/173 364/465/173 +f 353/461/174 364/465/174 361/466/174 +f 354/463/175 363/464/175 359/458/175 +f 361/467/176 366/468/176 362/469/176 +f 363/470/177 368/471/177 364/472/177 +f 364/472/84 365/473/84 361/467/84 +f 363/470/83 366/474/83 367/475/83 +f 370/476/178 372/477/178 369/478/178 +f 371/479/179 374/480/179 372/481/179 +f 373/482/180 376/483/180 374/480/180 +f 370/484/181 373/482/181 371/485/181 +f 369/478/182 374/480/182 376/483/182 +f 370/486/183 377/487/183 378/488/183 +f 375/489/184 380/490/184 376/483/184 +f 369/491/185 380/490/185 377/487/185 +f 370/486/186 379/492/186 375/489/186 +f 377/493/187 382/494/187 378/495/187 +f 379/496/188 384/497/188 380/498/188 +f 377/493/84 384/497/84 381/499/84 +f 378/500/83 383/501/83 379/496/83 +f 386/502/189 388/503/189 385/504/189 +f 387/505/190 390/506/190 388/503/190 +f 389/507/191 392/508/191 390/506/191 +f 390/506/81 385/509/81 388/503/81 +f 389/507/82 386/510/82 391/511/82 +f 394/512/81 396/513/81 393/514/81 +f 398/515/82 400/516/82 397/517/82 +f 395/518/84 399/519/84 396/520/84 +f 397/517/83 393/521/83 398/522/83 +f 401/523/85 395/518/85 394/512/85 +f 395/518/85 403/524/85 400/516/85 +f 403/524/85 397/517/85 400/516/85 +f 404/525/85 394/512/85 397/517/85 +f 401/523/86 405/526/86 402/527/86 +f 405/526/89 403/524/89 402/527/89 +f 403/524/88 406/528/88 404/525/88 +f 406/528/87 401/523/87 404/525/87 +f 408/529/45 410/530/45 407/531/45 +f 411/532/44 407/531/44 410/530/44 +f 413/533/43 412/534/43 411/532/43 +f 415/535/42 414/536/42 413/533/42 +f 415/535/41 418/537/41 416/538/41 +f 417/539/48 420/540/48 418/537/48 +f 419/541/47 422/542/47 420/540/47 +f 421/543/46 408/529/46 422/542/46 +f 407/531/51 424/544/51 423/545/51 +f 412/534/50 425/546/50 424/544/50 +f 414/536/49 426/547/49 425/546/49 +f 426/547/56 418/537/56 427/548/56 +f 427/548/55 420/540/55 428/549/55 +f 428/549/54 422/542/54 429/550/54 +f 429/550/53 408/529/53 430/551/53 +f 408/529/52 423/545/52 430/551/52 +f 431/552/59 411/532/59 410/530/59 +f 432/553/58 413/533/58 411/532/58 +f 433/554/57 415/535/57 413/533/57 +f 415/535/192 435/555/192 417/539/192 +f 417/539/63 436/556/63 419/541/63 +f 436/556/62 421/543/62 419/541/62 +f 437/557/61 409/558/61 421/543/61 +f 438/559/193 410/530/193 409/558/193 +f 439/560/67 432/553/67 431/552/67 +f 440/561/66 433/554/66 432/553/66 +f 433/554/65 442/562/65 434/563/65 +f 442/562/194 435/555/194 434/563/194 +f 443/564/71 436/556/71 435/555/71 +f 436/556/70 445/565/70 437/557/70 +f 437/557/69 446/566/69 438/559/69 +f 446/566/68 431/552/68 438/559/68 +f 439/560/195 447/567/195 440/561/195 +f 440/561/74 447/567/74 441/568/74 +f 441/568/196 447/567/196 442/562/196 +f 442/562/80 447/567/80 443/564/80 +f 443/564/79 447/567/79 444/569/79 +f 444/569/78 447/567/78 445/565/78 +f 445/565/77 447/567/77 446/566/77 +f 446/566/76 447/567/76 439/560/76 +f 449/570/5 451/571/5 448/572/5 +f 451/571/4 453/573/4 448/572/4 +f 452/574/3 455/575/3 453/573/3 +f 456/576/2 455/575/2 454/577/2 +f 458/578/1 457/579/1 456/576/1 +f 458/578/8 461/580/8 459/581/8 +f 462/582/7 461/580/7 460/583/7 +f 450/584/6 463/585/6 462/582/6 +f 464/586/11 453/573/11 465/587/11 +f 465/587/10 455/575/10 466/588/10 +f 466/588/9 457/579/9 467/589/9 +f 467/589/16 459/581/16 468/590/16 +f 468/590/15 461/580/15 469/591/15 +f 461/580/14 470/592/14 469/591/14 +f 463/585/13 471/593/13 470/592/13 +f 449/570/12 464/586/12 471/593/12 +f 451/571/197 473/594/197 452/574/197 +f 452/574/18 474/595/18 454/577/18 +f 474/595/17 456/576/17 454/577/17 +f 475/596/198 458/578/198 456/576/198 +f 458/578/23 477/597/23 460/583/23 +f 477/597/22 462/582/22 460/583/22 +f 462/582/21 479/598/21 450/584/21 +f 479/598/199 451/571/199 450/584/199 +f 480/599/200 473/594/200 472/600/200 +f 481/601/26 474/595/26 473/594/26 +f 482/602/201 475/596/201 474/595/201 +f 483/603/32 476/604/32 475/596/32 +f 484/605/202 477/597/202 476/604/202 +f 477/597/30 486/606/30 478/607/30 +f 478/607/29 487/608/29 479/598/29 +f 479/598/28 480/599/28 472/600/28 +f 480/599/35 488/609/35 481/601/35 +f 481/601/34 488/609/34 482/602/34 +f 482/602/33 488/609/33 483/603/33 +f 483/603/40 488/609/40 484/605/40 +f 484/605/39 488/609/39 485/610/39 +f 485/610/38 488/609/38 486/606/38 +f 486/606/37 488/609/37 487/608/37 +f 487/608/36 488/609/36 480/599/36 +f 489/611/203 491/612/203 492/613/203 +f 493/614/204 491/612/204 490/615/204 +f 495/616/205 494/617/205 493/614/205 +f 497/618/206 496/619/206 495/616/206 +f 499/620/207 498/621/207 497/618/207 +f 501/622/208 500/623/208 499/620/208 +f 501/624/209 504/625/209 502/626/209 +f 503/627/210 492/613/210 504/625/210 +f 491/628/85 505/629/85 492/630/85 +f 494/631/85 505/629/85 491/628/85 +f 496/632/85 505/629/85 494/631/85 +f 498/633/85 505/629/85 496/632/85 +f 500/634/85 505/629/85 498/633/85 +f 502/635/85 505/629/85 500/634/85 +f 504/636/85 505/629/85 502/635/85 +f 492/630/85 505/629/85 504/636/85 +f 507/637/211 509/638/211 506/639/211 +f 507/637/212 511/640/212 508/641/212 +f 510/642/213 513/643/213 511/640/213 +f 514/644/214 513/643/214 512/645/214 +f 516/646/215 515/647/215 514/644/215 +f 518/648/216 517/649/216 516/646/216 +f 518/650/217 521/651/217 519/652/217 +f 506/639/218 521/651/218 520/653/218 +f 508/654/85 522/655/85 509/656/85 +f 511/657/85 522/655/85 508/654/85 +f 513/658/85 522/655/85 511/657/85 +f 515/659/85 522/655/85 513/658/85 +f 517/660/85 522/655/85 515/659/85 +f 519/661/85 522/655/85 517/660/85 +f 521/662/85 522/655/85 519/661/85 +f 509/656/85 522/655/85 521/662/85 +f 524/663/219 526/664/219 523/665/219 +f 524/663/220 528/666/220 525/667/220 +f 527/668/221 530/669/221 528/666/221 +f 531/670/222 530/669/222 529/671/222 +f 533/672/223 532/673/223 531/670/223 +f 535/674/224 534/675/224 533/672/224 +f 535/676/225 538/677/225 536/678/225 +f 523/665/226 538/677/226 537/679/226 +f 525/680/85 539/681/85 526/682/85 +f 528/683/85 539/681/85 525/680/85 +f 530/684/85 539/681/85 528/683/85 +f 532/685/85 539/681/85 530/684/85 +f 534/686/85 539/681/85 532/685/85 +f 536/687/85 539/681/85 534/686/85 +f 538/688/85 539/681/85 536/687/85 +f 526/682/85 539/681/85 538/688/85 +f 541/689/227 543/690/227 540/691/227 +f 540/691/228 545/692/228 541/689/228 +f 544/693/229 547/694/229 545/692/229 +f 547/694/230 548/695/230 549/696/230 +f 549/696/231 550/697/231 551/698/231 +f 551/698/232 552/699/232 553/700/232 +f 552/699/233 555/701/233 553/700/233 +f 554/702/234 542/703/234 555/701/234 +f 557/704/235 540/691/235 556/705/235 +f 558/706/236 544/693/236 557/704/236 +f 559/707/237 546/708/237 558/706/237 +f 559/707/238 550/697/238 548/695/238 +f 560/709/239 552/699/239 550/697/239 +f 562/710/240 552/699/240 561/711/240 +f 563/712/241 554/702/241 562/710/241 +f 556/705/242 543/690/242 563/712/242 +f 545/692/243 565/713/243 541/689/243 +f 547/694/244 564/714/244 545/692/244 +f 547/694/245 567/715/245 566/716/245 +f 549/696/246 568/717/246 567/715/246 +f 551/698/247 569/718/247 568/717/247 +f 555/701/248 569/718/248 553/700/248 +f 555/701/249 571/719/249 570/720/249 +f 541/689/250 571/719/250 542/703/250 +f 565/713/251 564/714/251 572/721/251 +f 564/714/252 566/716/252 572/721/252 +f 566/716/253 567/715/253 572/721/253 +f 567/715/254 568/717/254 572/721/254 +f 568/717/255 569/718/255 572/721/255 +f 569/718/256 570/720/256 572/721/256 +f 570/720/257 571/719/257 572/721/257 +f 571/719/258 565/713/258 572/721/258 +f 574/722/81 576/723/81 573/724/81 +f 575/725/85 578/726/85 576/723/85 +f 580/727/82 582/728/82 579/729/82 +f 577/730/83 581/731/83 580/727/83 +f 578/726/84 573/732/84 576/723/84 +f 577/730/259 579/729/259 578/726/259 +f 582/733/84 578/726/84 579/729/84 +f 574/734/83 577/730/83 575/725/83 +f 584/735/81 586/736/81 583/737/81 +f 585/738/85 588/739/85 586/736/85 +f 589/740/82 591/741/82 592/742/82 +f 587/743/260 591/744/260 590/745/260 +f 588/739/84 583/746/84 586/736/84 +f 587/743/259 589/740/259 588/739/259 +f 592/747/84 588/739/84 589/740/84 +f 584/748/83 587/743/83 585/738/83 +f 594/749/81 596/750/81 593/751/81 +f 595/752/85 598/753/85 596/750/85 +f 600/754/82 602/755/82 599/756/82 +f 597/757/83 601/758/83 600/754/83 +f 598/753/84 593/759/84 596/750/84 +f 597/757/259 599/756/259 598/753/259 +f 602/760/84 598/753/84 599/756/84 +f 594/761/83 597/757/83 595/752/83 +f 604/762/81 606/763/81 603/764/81 +f 607/765/85 605/766/85 604/762/85 +f 610/767/82 612/768/82 609/769/82 +f 608/770/84 611/771/84 606/772/84 +f 607/765/83 604/762/83 603/773/83 +f 609/769/259 608/770/259 607/765/259 +f 610/774/83 607/765/83 603/773/83 +f 606/772/84 605/766/84 608/770/84 +f 614/775/81 616/776/81 613/777/81 +f 617/778/85 615/779/85 614/775/85 +f 619/780/82 621/781/82 622/782/82 +f 618/783/84 621/784/84 616/785/84 +f 617/778/83 614/775/83 613/786/83 +f 619/780/259 618/783/259 617/778/259 +f 620/787/83 617/778/83 613/786/83 +f 616/785/84 615/779/84 618/783/84 +f 624/788/81 626/789/81 623/790/81 +f 627/791/85 625/792/85 624/788/85 +f 630/793/82 632/794/82 629/795/82 +f 628/796/84 631/797/84 626/798/84 +f 627/791/83 624/788/83 623/799/83 +f 629/795/259 628/796/259 627/791/259 +f 630/800/83 627/791/83 623/799/83 +f 626/798/84 625/792/84 628/796/84 +f 634/801/84 636/802/84 633/803/84 +f 636/804/261 637/805/261 638/806/261 +f 638/806/83 639/807/83 640/808/83 +f 639/807/262 633/803/262 640/808/262 +f 641/809/81 633/810/81 636/811/81 +f 643/812/85 636/811/85 638/813/85 +f 644/814/82 638/813/82 640/808/82 +f 642/815/104 640/808/104 633/803/104 +f 641/816/83 646/817/83 642/815/83 +f 641/818/263 647/819/263 645/820/263 +f 643/821/84 648/822/84 647/819/84 +f 642/815/264 648/822/264 644/814/264 +f 654/823/83 649/824/83 652/825/83 +f 674/826/83 676/827/83 673/828/83 +f 675/829/85 678/830/85 676/831/85 +f 677/832/84 680/833/84 678/830/84 +f 679/834/104 673/828/104 680/833/104 +f 681/835/265 675/836/265 674/826/265 +f 682/837/266 677/832/266 675/829/266 +f 683/838/267 679/834/267 677/832/267 +f 684/839/268 674/826/268 679/834/268 +f 683/838/82 681/840/82 684/839/82 +f 689/841/84 685/842/84 691/843/84 +f 710/844/84 712/845/84 709/846/84 +f 713/847/85 711/848/85 710/849/85 +f 715/850/83 714/851/83 713/847/83 +f 709/846/104 716/852/104 715/850/104 +f 711/853/267 718/854/267 712/845/267 +f 714/851/266 717/855/266 711/848/266 +f 716/852/265 719/856/265 714/851/265 +f 712/845/268 720/857/268 716/852/268 +f 718/858/82 719/856/82 720/857/82 +f 2/1/1 3/15/1 4/2/1 +f 2/1/2 1/3/2 5/4/2 +f 6/5/3 5/4/3 7/6/3 +f 7/6/4 9/9/4 10/8/4 +f 9/9/5 11/20/5 12/10/5 +f 12/10/6 11/20/6 13/11/6 +f 13/11/7 15/14/7 16/13/7 +f 15/14/8 4/2/8 3/15/8 +f 17/16/9 18/17/9 5/4/9 +f 18/17/10 19/18/10 7/6/10 +f 19/18/11 20/19/11 9/9/11 +f 20/19/12 21/21/12 11/20/12 +f 21/21/13 22/23/13 13/11/13 +f 23/22/14 15/14/14 13/11/14 +f 24/24/15 4/2/15 15/14/15 +f 17/16/16 1/3/16 4/2/16 +f 2/1/269 6/5/269 25/25/269 +f 6/5/18 8/7/18 27/27/18 +f 10/8/19 28/28/19 27/27/19 +f 12/10/20 29/30/20 28/28/20 +f 12/10/21 14/12/21 30/29/21 +f 16/13/22 31/32/22 30/29/22 +f 16/13/23 3/15/23 32/31/23 +f 2/1/24 26/26/24 32/31/24 +f 25/25/25 33/34/25 34/33/25 +f 27/27/26 35/35/26 33/34/26 +f 28/28/200 36/36/200 35/35/200 +f 29/30/28 37/37/28 36/36/28 +f 30/29/29 38/39/29 37/37/29 +f 30/29/30 31/32/30 39/38/30 +f 31/32/202 32/31/202 40/40/202 +f 32/31/32 26/26/32 34/33/32 +f 43/42/41 44/56/41 45/43/41 +f 42/44/42 46/46/42 47/45/42 +f 46/46/43 48/48/43 49/47/43 +f 48/48/44 50/61/44 51/49/44 +f 51/49/45 50/61/45 52/50/45 +f 53/51/46 52/50/46 54/52/46 +f 55/53/47 54/52/47 56/54/47 +f 57/55/48 56/54/48 45/43/48 +f 59/57/49 46/46/49 42/44/49 +f 60/59/50 48/48/50 46/46/50 +f 61/60/51 50/61/51 48/48/51 +f 61/60/52 62/62/52 52/50/52 +f 62/62/53 63/63/53 54/52/53 +f 63/63/54 64/64/54 56/54/54 +f 64/64/55 65/65/55 45/43/55 +f 58/58/56 42/44/56 45/43/56 +f 47/45/57 66/67/57 67/66/57 +f 49/47/58 68/68/58 66/67/58 +f 51/49/59 69/70/59 68/68/59 +f 51/49/60 53/51/60 70/69/60 +f 53/51/61 55/53/61 71/71/61 +f 57/55/62 72/72/62 71/71/62 +f 44/56/270 73/73/270 72/72/270 +f 43/42/64 67/66/64 73/73/64 +f 66/67/65 74/75/65 75/74/65 +f 68/68/66 76/77/66 74/75/66 +f 68/68/67 69/70/67 77/76/67 +f 70/69/68 78/78/68 77/76/68 +f 71/71/69 79/80/69 78/78/69 +f 71/71/70 72/72/70 80/79/70 +f 72/72/71 73/73/71 81/81/71 +f 67/66/72 75/74/72 81/81/72 +f 84/83/81 85/90/81 86/84/81 +f 88/86/82 89/859/82 90/87/82 +f 89/89/83 88/86/83 85/90/83 +f 83/92/84 86/84/84 87/88/84 +f 85/90/85 91/96/85 92/94/85 +f 85/90/85 88/86/85 93/95/85 +f 87/88/85 94/97/85 93/95/85 +f 86/84/85 92/94/85 94/97/85 +f 93/95/87 96/99/87 95/98/87 +f 92/94/89 95/98/89 96/99/89 +f 98/100/90 99/104/90 100/101/90 +f 101/103/91 102/106/91 99/104/91 +f 103/105/92 104/860/92 102/106/92 +f 97/107/81 103/861/81 101/103/81 +f 102/106/82 104/862/82 100/108/82 +f 108/112/94 109/118/94 110/113/94 +f 112/115/271 113/164/271 114/116/271 +f 110/113/99 105/109/99 116/119/99 +f 117/120/85 118/121/85 111/114/85 +f 122/125/85 118/121/85 117/120/85 +f 124/127/103 125/149/103 126/128/103 +f 132/133/85 133/160/85 134/134/85 +f 135/136/94 120/123/94 136/137/94 +f 138/139/94 106/110/94 109/118/94 +f 142/142/85 143/210/85 144/143/85 +f 145/146/81 119/863/81 118/121/81 +f 136/137/110 123/126/110 126/128/110 +f 123/126/112 117/120/112 116/119/112 +f 120/123/85 122/125/85 123/126/85 +f 126/128/272 116/119/272 147/150/272 +f 137/138/273 109/118/273 115/117/273 +f 127/129/85 147/150/85 107/111/85 +f 148/151/85 149/154/85 106/110/85 +f 124/127/85 150/152/85 148/151/85 +f 150/152/85 124/127/85 107/111/85 +f 107/111/85 106/110/85 149/154/85 +f 148/155/114 131/135/114 134/134/114 +f 150/157/115 132/133/115 131/135/115 +f 150/159/116 151/864/116 133/160/116 +f 149/161/117 134/134/117 133/160/117 +f 107/111/93 147/150/93 116/119/93 +f 155/166/82 156/203/82 157/167/82 +f 159/169/104 160/865/104 161/170/104 +f 145/172/104 158/171/104 161/170/104 +f 152/174/104 162/178/104 163/175/104 +f 152/174/84 159/866/84 158/177/84 +f 158/177/122 130/180/122 163/179/122 +f 130/180/83 129/867/83 146/181/83 +f 165/185/82 166/197/82 167/186/82 +f 153/191/83 165/185/83 161/187/83 +f 164/193/104 166/197/104 165/194/104 +f 168/196/84 167/186/84 166/197/84 +f 169/198/124 155/166/124 154/168/124 +f 171/200/125 156/203/125 155/166/125 +f 171/202/126 172/868/126 157/167/126 +f 170/204/127 154/168/127 157/167/127 +f 174/206/128 173/208/128 142/142/128 +f 175/207/129 143/210/129 142/142/129 +f 176/209/130 144/143/130 143/210/130 +f 174/206/131 141/144/131 144/143/131 +f 178/211/81 179/214/81 180/212/81 +f 179/214/85 181/216/85 182/215/85 +f 181/216/82 183/869/82 184/217/82 +f 183/218/83 181/216/83 179/214/83 +f 177/220/84 180/212/84 182/215/84 +f 186/222/81 187/225/81 188/223/81 +f 187/225/85 189/227/85 190/226/85 +f 189/227/82 191/870/82 192/228/82 +f 191/229/83 189/227/83 187/225/83 +f 185/231/84 188/223/84 190/226/84 +f 194/233/81 195/236/81 196/234/81 +f 195/236/85 197/238/85 198/237/85 +f 197/238/82 199/871/82 200/239/82 +f 199/240/83 197/238/83 195/236/83 +f 193/242/84 196/234/84 198/237/84 +f 202/244/81 203/247/81 204/245/81 +f 203/247/85 205/249/85 206/248/85 +f 205/249/82 207/872/82 208/250/82 +f 207/251/83 205/249/83 203/247/83 +f 201/253/84 204/245/84 206/248/84 +f 210/255/81 211/258/81 212/256/81 +f 211/258/85 213/260/85 214/259/85 +f 213/260/82 215/873/82 216/261/82 +f 215/262/83 213/260/83 211/258/83 +f 209/264/84 212/256/84 214/259/84 +f 218/266/81 219/269/81 220/267/81 +f 219/269/85 221/271/85 222/270/85 +f 221/271/82 223/874/82 224/272/82 +f 223/273/83 221/271/83 219/269/83 +f 217/275/84 220/267/84 222/270/84 +f 226/277/81 227/287/81 228/278/81 +f 230/280/85 231/295/85 232/281/85 +f 234/283/82 235/875/82 236/284/82 +f 235/286/83 234/283/83 227/287/83 +f 225/289/84 228/278/84 233/285/84 +f 228/278/85 227/287/85 237/291/85 +f 227/287/85 234/283/85 239/293/85 +f 233/285/85 240/294/85 239/293/85 +f 228/278/85 238/292/85 240/294/85 +f 237/291/132 230/280/132 229/282/132 +f 237/291/133 239/293/133 231/295/133 +f 240/294/134 232/281/134 231/295/134 +f 238/292/135 229/282/135 232/281/135 +f 242/296/81 243/306/81 244/297/81 +f 246/299/85 247/314/85 248/300/85 +f 250/302/82 251/876/82 252/303/82 +f 251/305/83 250/302/83 243/306/83 +f 241/308/84 244/297/84 249/304/84 +f 243/306/85 253/312/85 254/310/85 +f 243/306/85 250/302/85 255/311/85 +f 249/304/85 256/313/85 255/311/85 +f 244/297/85 254/310/85 256/313/85 +f 253/312/136 246/299/136 245/301/136 +f 255/311/133 247/314/133 246/299/133 +f 256/313/134 248/300/134 247/314/134 +f 256/313/135 254/310/135 245/301/135 +f 258/315/81 259/325/81 260/316/81 +f 262/318/85 263/333/85 264/319/85 +f 266/321/82 267/877/82 268/322/82 +f 267/324/83 266/321/83 259/325/83 +f 257/327/84 260/316/84 265/323/84 +f 259/325/85 269/331/85 270/329/85 +f 259/325/85 266/321/85 271/330/85 +f 266/321/85 265/323/85 272/332/85 +f 260/316/85 270/329/85 272/332/85 +f 269/331/132 262/318/132 261/320/132 +f 269/331/133 271/330/133 263/333/133 +f 272/332/134 264/319/134 263/333/134 +f 270/329/135 261/320/135 264/319/135 +f 274/334/81 275/344/81 276/335/81 +f 278/337/85 279/352/85 280/338/85 +f 282/340/82 283/878/82 284/341/82 +f 283/343/83 282/340/83 275/344/83 +f 273/346/84 276/335/84 281/342/84 +f 275/344/85 285/350/85 286/348/85 +f 275/344/85 282/340/85 287/349/85 +f 281/342/85 288/351/85 287/349/85 +f 276/335/85 286/348/85 288/351/85 +f 285/350/132 278/337/132 277/339/132 +f 285/350/133 287/349/133 279/352/133 +f 288/351/134 280/338/134 279/352/134 +f 286/348/135 277/339/135 280/338/135 +f 290/353/81 291/363/81 292/354/81 +f 294/356/85 295/371/85 296/357/85 +f 298/359/82 299/879/82 300/360/82 +f 299/362/83 298/359/83 291/363/83 +f 289/365/84 292/354/84 297/361/84 +f 292/354/85 291/363/85 301/367/85 +f 291/363/85 298/359/85 303/369/85 +f 297/361/85 304/370/85 303/369/85 +f 292/354/85 302/368/85 304/370/85 +f 301/367/132 294/356/132 293/358/132 +f 301/367/133 303/369/133 295/371/133 +f 304/370/134 296/357/134 295/371/134 +f 302/368/135 293/358/135 296/357/135 +f 306/372/137 307/880/137 308/373/137 +f 307/375/85 309/378/85 310/376/85 +f 309/378/138 311/380/138 312/379/138 +f 311/380/139 309/378/139 307/381/139 +f 312/379/140 305/374/140 308/373/140 +f 305/383/141 313/388/141 314/384/141 +f 312/379/142 311/380/142 315/386/142 +f 312/379/143 316/387/143 313/388/143 +f 306/385/144 314/384/144 315/386/144 +f 313/389/81 317/395/81 318/390/81 +f 315/392/82 319/397/82 320/393/82 +f 316/394/84 320/393/84 317/395/84 +f 314/396/83 318/881/83 319/397/83 +f 322/398/145 323/882/145 324/399/145 +f 323/401/146 325/404/146 326/402/146 +f 325/404/147 327/406/147 328/405/147 +f 327/406/148 325/404/148 323/407/148 +f 328/405/149 321/400/149 324/399/149 +f 321/409/150 329/414/150 330/410/150 +f 328/405/151 327/406/151 331/412/151 +f 328/405/152 332/413/152 329/414/152 +f 327/406/153 322/411/153 330/410/153 +f 329/415/154 333/421/154 334/416/154 +f 331/418/155 335/423/155 336/419/155 +f 329/415/84 332/420/84 336/419/84 +f 330/422/83 334/883/83 335/423/83 +f 338/424/156 339/884/156 340/425/156 +f 339/427/157 341/430/157 342/428/157 +f 341/430/158 343/432/158 344/431/158 +f 343/432/159 341/430/159 339/433/159 +f 344/431/160 337/426/160 340/425/160 +f 338/435/161 337/439/161 345/436/161 +f 343/432/274 347/440/274 348/438/274 +f 337/439/275 344/431/275 348/438/275 +f 338/435/164 346/437/164 347/440/164 +f 345/441/165 349/447/165 350/442/165 +f 347/444/166 351/449/166 352/445/166 +f 345/441/84 348/446/84 352/445/84 +f 346/448/83 350/885/83 351/449/83 +f 354/450/167 355/886/167 356/451/167 +f 355/453/168 357/456/168 358/454/168 +f 357/456/169 359/458/169 360/457/169 +f 359/458/170 357/456/170 355/459/170 +f 360/457/171 353/452/171 356/451/171 +f 353/461/172 361/466/172 362/462/172 +f 360/457/173 359/458/173 363/464/173 +f 353/461/174 360/457/174 364/465/174 +f 354/463/276 362/462/276 363/464/276 +f 361/467/176 365/473/176 366/468/176 +f 363/470/177 367/475/177 368/471/177 +f 364/472/84 368/471/84 365/473/84 +f 363/470/83 362/887/83 366/474/83 +f 370/476/178 371/888/178 372/477/178 +f 371/479/179 373/482/179 374/480/179 +f 373/482/180 375/489/180 376/483/180 +f 370/484/181 375/489/181 373/482/181 +f 369/478/182 372/477/182 374/480/182 +f 370/486/183 369/491/183 377/487/183 +f 375/489/184 379/492/184 380/490/184 +f 369/491/185 376/483/185 380/490/185 +f 370/486/186 378/488/186 379/492/186 +f 377/493/277 381/499/277 382/494/277 +f 379/496/278 383/501/278 384/497/278 +f 377/493/84 380/498/84 384/497/84 +f 378/500/83 382/889/83 383/501/83 +f 386/502/189 387/505/189 388/503/189 +f 387/505/190 389/507/190 390/506/190 +f 389/507/191 391/890/191 392/508/191 +f 390/506/81 392/891/81 385/509/81 +f 389/507/82 387/505/82 386/510/82 +f 394/512/81 395/518/81 396/513/81 +f 398/515/82 399/892/82 400/516/82 +f 395/518/84 400/516/84 399/519/84 +f 397/517/83 394/512/83 393/521/83 +f 401/523/85 402/527/85 395/518/85 +f 395/518/85 402/527/85 403/524/85 +f 403/524/85 404/525/85 397/517/85 +f 404/525/85 401/523/85 394/512/85 +f 405/526/89 406/528/89 403/524/89 +f 406/528/87 405/526/87 401/523/87 +f 408/529/45 409/558/45 410/530/45 +f 411/532/44 412/534/44 407/531/44 +f 413/533/43 414/536/43 412/534/43 +f 415/535/42 416/538/42 414/536/42 +f 415/535/41 417/539/41 418/537/41 +f 417/539/48 419/541/48 420/540/48 +f 419/541/47 421/543/47 422/542/47 +f 421/543/46 409/558/46 408/529/46 +f 407/531/51 412/534/51 424/544/51 +f 412/534/50 414/536/50 425/546/50 +f 414/536/49 416/538/49 426/547/49 +f 426/547/56 416/538/56 418/537/56 +f 427/548/55 418/537/55 420/540/55 +f 428/549/54 420/540/54 422/542/54 +f 429/550/53 422/542/53 408/529/53 +f 408/529/52 407/531/52 423/545/52 +f 431/552/59 432/553/59 411/532/59 +f 432/553/58 433/554/58 413/533/58 +f 433/554/57 434/563/57 415/535/57 +f 415/535/192 434/563/192 435/555/192 +f 417/539/63 435/555/63 436/556/63 +f 436/556/62 437/557/62 421/543/62 +f 437/557/279 438/559/279 409/558/279 +f 438/559/193 431/552/193 410/530/193 +f 439/560/67 440/561/67 432/553/67 +f 440/561/66 441/568/66 433/554/66 +f 433/554/65 441/568/65 442/562/65 +f 442/562/72 443/564/72 435/555/72 +f 443/564/71 444/569/71 436/556/71 +f 436/556/70 444/569/70 445/565/70 +f 437/557/69 445/565/69 446/566/69 +f 446/566/68 439/560/68 431/552/68 +f 449/570/5 450/584/5 451/571/5 +f 451/571/4 452/574/4 453/573/4 +f 452/574/3 454/577/3 455/575/3 +f 456/576/2 457/579/2 455/575/2 +f 458/578/1 459/581/1 457/579/1 +f 458/578/8 460/583/8 461/580/8 +f 462/582/7 463/585/7 461/580/7 +f 450/584/6 449/570/6 463/585/6 +f 464/586/11 448/572/11 453/573/11 +f 465/587/10 453/573/10 455/575/10 +f 466/588/9 455/575/9 457/579/9 +f 467/589/16 457/579/16 459/581/16 +f 468/590/15 459/581/15 461/580/15 +f 461/580/14 463/585/14 470/592/14 +f 463/585/13 449/570/13 471/593/13 +f 449/570/12 448/572/12 464/586/12 +f 451/571/19 472/600/19 473/594/19 +f 452/574/18 473/594/18 474/595/18 +f 474/595/17 475/596/17 456/576/17 +f 475/596/198 476/604/198 458/578/198 +f 458/578/23 476/604/23 477/597/23 +f 477/597/22 478/607/22 462/582/22 +f 462/582/21 478/607/21 479/598/21 +f 479/598/199 472/600/199 451/571/199 +f 480/599/200 481/601/200 473/594/200 +f 481/601/26 482/602/26 474/595/26 +f 482/602/25 483/603/25 475/596/25 +f 483/603/32 484/605/32 476/604/32 +f 484/605/202 485/610/202 477/597/202 +f 477/597/30 485/610/30 486/606/30 +f 478/607/280 486/606/280 487/608/280 +f 479/598/28 487/608/28 480/599/28 +f 489/611/203 490/615/203 491/612/203 +f 493/614/204 494/617/204 491/612/204 +f 495/616/205 496/619/205 494/617/205 +f 497/618/206 498/621/206 496/619/206 +f 499/620/207 500/623/207 498/621/207 +f 501/622/208 502/893/208 500/623/208 +f 501/624/209 503/627/209 504/625/209 +f 503/627/210 489/611/210 492/613/210 +f 507/637/211 508/641/211 509/638/211 +f 507/637/212 510/642/212 511/640/212 +f 510/642/213 512/645/213 513/643/213 +f 514/644/214 515/647/214 513/643/214 +f 516/646/215 517/649/215 515/647/215 +f 518/648/216 519/894/216 517/649/216 +f 518/650/217 520/653/217 521/651/217 +f 506/639/218 509/638/218 521/651/218 +f 524/663/219 525/667/219 526/664/219 +f 524/663/220 527/668/220 528/666/220 +f 527/668/221 529/671/221 530/669/221 +f 531/670/222 532/673/222 530/669/222 +f 533/672/223 534/675/223 532/673/223 +f 535/674/224 536/895/224 534/675/224 +f 535/676/225 537/679/225 538/677/225 +f 523/665/226 526/664/226 538/677/226 +f 541/689/227 542/703/227 543/690/227 +f 540/691/228 544/693/228 545/692/228 +f 544/693/229 546/708/229 547/694/229 +f 547/694/230 546/708/230 548/695/230 +f 549/696/231 548/695/231 550/697/231 +f 551/698/232 550/697/232 552/699/232 +f 552/699/233 554/702/233 555/701/233 +f 554/702/234 543/690/234 542/703/234 +f 557/704/235 544/693/235 540/691/235 +f 558/706/236 546/708/236 544/693/236 +f 559/707/237 548/695/237 546/708/237 +f 559/707/238 560/709/238 550/697/238 +f 560/709/239 561/711/239 552/699/239 +f 562/710/240 554/702/240 552/699/240 +f 563/712/241 543/690/241 554/702/241 +f 556/705/242 540/691/242 543/690/242 +f 545/692/243 564/714/243 565/713/243 +f 547/694/244 566/716/244 564/714/244 +f 547/694/245 549/696/245 567/715/245 +f 549/696/246 551/698/246 568/717/246 +f 551/698/281 553/700/281 569/718/281 +f 555/701/248 570/720/248 569/718/248 +f 555/701/249 542/703/249 571/719/249 +f 541/689/250 565/713/250 571/719/250 +f 574/722/81 575/725/81 576/723/81 +f 575/725/85 577/730/85 578/726/85 +f 580/727/82 581/896/82 582/728/82 +f 577/730/83 574/734/83 581/731/83 +f 577/730/259 580/727/259 579/729/259 +f 582/733/84 573/732/84 578/726/84 +f 584/735/81 585/738/81 586/736/81 +f 585/738/85 587/743/85 588/739/85 +f 589/740/82 590/745/82 591/741/82 +f 587/743/83 584/748/83 591/744/83 +f 587/743/259 590/745/259 589/740/259 +f 592/747/84 583/746/84 588/739/84 +f 594/749/81 595/752/81 596/750/81 +f 595/752/85 597/757/85 598/753/85 +f 600/754/82 601/897/82 602/755/82 +f 597/757/83 594/761/83 601/758/83 +f 597/757/259 600/754/259 599/756/259 +f 602/760/84 593/759/84 598/753/84 +f 604/762/81 605/766/81 606/763/81 +f 607/765/85 608/770/85 605/766/85 +f 610/767/82 611/898/82 612/768/82 +f 608/770/84 612/768/84 611/771/84 +f 609/769/259 612/768/259 608/770/259 +f 610/774/83 609/769/83 607/765/83 +f 614/775/81 615/779/81 616/776/81 +f 617/778/85 618/783/85 615/779/85 +f 619/780/82 620/899/82 621/781/82 +f 618/783/282 622/782/282 621/784/282 +f 619/780/259 622/782/259 618/783/259 +f 620/787/83 619/780/83 617/778/83 +f 624/788/81 625/792/81 626/789/81 +f 627/791/85 628/796/85 625/792/85 +f 630/793/82 631/900/82 632/794/82 +f 628/796/84 632/794/84 631/797/84 +f 629/795/259 632/794/259 628/796/259 +f 630/800/83 629/795/83 627/791/83 +f 634/801/84 635/901/84 636/802/84 +f 636/804/263 635/902/263 637/805/263 +f 638/806/83 637/805/83 639/807/83 +f 639/807/283 634/801/283 633/803/283 +f 641/809/81 642/903/81 633/810/81 +f 643/812/85 641/809/85 636/811/85 +f 644/814/82 643/812/82 638/813/82 +f 642/815/104 644/814/104 640/808/104 +f 641/816/83 645/904/83 646/817/83 +f 641/818/284 643/821/284 647/819/284 +f 643/821/84 644/814/84 648/822/84 +f 642/815/283 646/817/283 648/822/283 +f 654/823/83 656/905/83 649/824/83 +f 674/826/83 675/836/83 676/827/83 +f 675/829/85 677/832/85 678/830/85 +f 677/832/84 679/834/84 680/833/84 +f 679/834/104 674/826/104 673/828/104 +f 681/835/265 682/906/265 675/836/265 +f 682/837/266 683/838/266 677/832/266 +f 683/838/267 684/839/267 679/834/267 +f 684/839/268 681/835/268 674/826/268 +f 683/838/82 682/907/82 681/840/82 +f 689/841/84 686/908/84 685/842/84 +f 710/844/84 711/853/84 712/845/84 +f 713/847/85 714/851/85 711/848/85 +f 715/850/83 716/852/83 714/851/83 +f 709/846/104 712/845/104 716/852/104 +f 711/853/267 717/909/267 718/854/267 +f 714/851/266 719/856/266 717/855/266 +f 716/852/265 720/857/265 719/856/265 +f 712/845/268 718/854/268 720/857/268 +f 718/858/82 717/910/82 719/856/82 +s 1 +f 140/147/82 153/183/82 152/165/82 +f 159/169/82 169/911/82 170/912/82 +f 152/165/82 171/913/82 169/911/82 +f 160/865/82 171/913/82 153/183/82 +f 159/169/82 172/914/82 160/865/82 +f 140/141/85 173/208/85 174/206/85 +f 115/117/85 173/208/85 139/140/85 +f 114/116/85 175/207/85 115/117/85 +f 140/141/85 176/209/85 114/116/85 +f 650/915/285 652/825/286 649/916/286 +f 651/917/287 654/823/288 652/825/288 +f 653/918/289 656/919/290 654/823/290 +f 655/920/291 649/916/292 656/921/292 +f 658/922/288 660/923/293 657/924/288 +f 662/925/286 660/923/294 661/926/294 +f 664/927/292 661/926/295 663/928/296 +f 658/922/290 663/929/297 659/930/297 +f 661/926/294 666/931/298 665/932/298 +f 667/933/299 661/926/295 665/932/299 +f 659/930/297 667/934/300 668/935/300 +f 666/931/301 659/930/293 668/935/301 +f 650/915/285 670/936/302 651/917/303 +f 671/937/304 650/915/305 655/920/291 +f 653/918/289 671/938/306 655/939/307 +f 670/936/308 653/918/309 651/917/287 +f 665/932/298 670/936/302 669/940/310 +f 667/933/299 669/940/311 671/937/304 +f 668/935/300 671/938/306 672/941/312 +f 666/931/301 672/941/313 670/936/308 +f 686/908/286 688/942/314 685/943/286 +f 689/841/288 687/944/315 686/908/288 +f 691/945/290 690/946/316 689/841/290 +f 685/943/292 692/947/317 691/948/292 +f 694/949/318 696/950/288 693/951/288 +f 694/949/319 698/952/286 697/953/319 +f 697/953/320 700/954/292 699/955/321 +f 699/956/322 696/950/290 695/957/322 +f 702/958/323 697/953/319 701/959/323 +f 703/960/324 697/953/320 699/955/321 +f 703/961/325 695/957/322 704/962/325 +f 702/958/326 695/957/318 694/949/318 +f 688/942/314 705/963/327 706/964/328 +f 688/942/329 707/965/330 692/947/317 +f 690/946/316 707/966/331 708/967/332 +f 690/946/333 705/963/334 687/944/315 +f 705/963/327 701/959/323 706/964/328 +f 706/964/335 703/960/324 707/965/330 +f 707/966/331 704/962/325 708/967/332 +f 708/967/336 702/958/326 705/963/334 +f 140/147/82 114/184/82 153/183/82 +f 159/169/82 152/165/82 169/911/82 +f 152/165/82 153/183/82 171/913/82 +f 160/865/82 172/914/82 171/913/82 +f 159/169/82 170/912/82 172/914/82 +f 140/141/85 139/140/85 173/208/85 +f 115/117/85 175/207/85 173/208/85 +f 114/116/85 176/209/85 175/207/85 +f 140/141/85 174/206/85 176/209/85 +f 650/915/285 651/917/303 652/825/286 +f 651/917/287 653/918/309 654/823/288 +f 653/918/289 655/939/307 656/919/290 +f 655/920/291 650/915/305 649/916/292 +f 658/922/288 659/930/293 660/923/293 +f 662/925/286 657/924/286 660/923/294 +f 664/927/292 662/925/292 661/926/295 +f 658/922/290 664/968/290 663/929/297 +f 661/926/294 660/923/294 666/931/298 +f 667/933/299 663/928/296 661/926/295 +f 659/930/297 663/929/297 667/934/300 +f 666/931/301 660/923/293 659/930/293 +f 650/915/285 669/940/310 670/936/302 +f 671/937/304 669/940/311 650/915/305 +f 653/918/289 672/941/312 671/938/306 +f 670/936/308 672/941/313 653/918/309 +f 665/932/298 666/931/298 670/936/302 +f 667/933/299 665/932/299 669/940/311 +f 668/935/300 667/934/300 671/938/306 +f 666/931/301 668/935/301 672/941/313 +f 686/908/286 687/944/337 688/942/314 +f 689/841/288 690/946/333 687/944/315 +f 691/945/290 692/969/338 690/946/316 +f 685/943/292 688/942/329 692/947/317 +f 694/949/318 695/957/318 696/950/288 +f 694/949/319 693/951/286 698/952/286 +f 697/953/320 698/952/292 700/954/292 +f 699/956/322 700/970/290 696/950/290 +f 702/958/323 694/949/319 697/953/319 +f 703/960/324 701/959/324 697/953/320 +f 703/961/325 699/956/322 695/957/322 +f 702/958/326 704/962/326 695/957/318 +f 688/942/314 687/944/337 705/963/327 +f 688/942/329 706/964/335 707/965/330 +f 690/946/316 692/969/338 707/966/331 +f 690/946/333 708/967/336 705/963/334 +f 705/963/327 702/958/323 701/959/323 +f 706/964/335 701/959/324 703/960/324 +f 707/966/331 703/961/325 704/962/325 +f 708/967/336 704/962/326 702/958/326 diff --git a/bin/render/Boombox_BaseColor.png b/bin/render/Boombox_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..301a250bb028de496d34423c9c6de9549a13d542 GIT binary patch literal 83101 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 z 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 literal 0 HcmV?d00001 diff --git a/bin/render/Car.mtl b/bin/render/Car.mtl new file mode 100644 index 0000000..6788d27 --- /dev/null +++ b/bin/render/Car.mtl @@ -0,0 +1,82 @@ +# Blender MTL File: 'None' +# Material Count: 8 + +newmtl Black +Ns 96.078421 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.000000 0.000000 +Ks 0.000000 0.000000 0.000000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 1 + +newmtl Body +Ns 96.078421 +Ka 1.000000 1.000000 1.000000 +Kd 1.000000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Bottom +Ns 96.078421 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.000000 0.000000 +Ks 0.000000 0.000000 0.000000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 1 + +newmtl Bumpers +Ns 96.078421 +Ka 1.000000 1.000000 1.000000 +Kd 0.058589 0.058589 0.058589 +Ks 0.000000 0.000000 0.000000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 1 + +newmtl Lights +Ns 96.078421 +Ka 1.000000 1.000000 1.000000 +Kd 1.000000 1.000000 1.000000 +Ks 0.000000 0.000000 0.000000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 1 + +newmtl Tires +Ns 96.078421 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Wheels +Ns 96.078421 +Ka 1.000000 1.000000 1.000000 +Kd 0.492142 0.492142 0.492142 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Window +Ns 96.078421 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.000000 0.000000 +Ks 1.000000 1.000000 1.000000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/bin/render/Car.obj b/bin/render/Car.obj new file mode 100644 index 0000000..83d1c2a --- /dev/null +++ b/bin/render/Car.obj @@ -0,0 +1,4713 @@ +# Blender v3.6.0 OBJ File: '' +# www.blender.org +mtllib Car.mtl +o Car_Cube +v 0.007443 1.064997 -2.124808 +v 0.491660 1.049997 -2.124808 +v 0.975877 0.999997 -2.124808 +v 1.007443 0.974997 -2.111530 +v 0.007443 0.976237 -2.122556 +v -0.670682 1.524997 -0.499808 +v -0.961307 0.999997 -0.374808 +v -0.946775 1.025622 -0.334777 +v -0.685213 1.498122 -0.447277 +v -0.476932 1.049997 -1.612308 +v -0.961307 0.999997 -1.537308 +v -0.912869 1.014147 -1.528589 +v -0.452713 1.061647 -1.603589 +v 0.685568 1.499997 -1.188558 +v 0.976193 0.999997 -1.537308 +v 0.927756 1.014147 -1.528589 +v 0.651662 1.489147 -1.197277 +v 0.685568 1.524997 -0.499808 +v 0.976193 0.999997 -0.374808 +v 0.961662 1.025622 -0.427339 +v 0.700100 1.498122 -0.539839 +v -0.992557 0.924997 2.025192 +v -0.992557 0.662497 2.125192 +v -0.962295 0.949997 2.075192 +v -0.961307 0.999997 0.787692 +v -0.992557 0.974997 -0.374808 +v 0.685568 1.499997 0.188942 +v 0.976193 0.999997 0.787692 +v 0.961662 1.025622 0.711473 +v 0.700100 1.475622 0.172598 +v -0.685213 1.498122 -0.539839 +v -0.946775 1.025622 -0.427339 +v 0.007443 1.064997 -1.637308 +v 0.007443 1.075897 -1.628589 +v 1.007443 0.974997 -0.959183 +v 1.007443 0.974997 -0.374808 +v 1.007443 0.662497 -0.374808 +v 1.007443 0.762497 -1.021996 +v 1.007443 0.937497 1.425192 +v 1.007443 0.924997 2.025192 +v 1.007443 0.662497 2.125192 +v 1.007443 0.762497 1.491989 +v -0.992557 0.946872 0.975192 +v -0.992557 0.974997 -0.374808 +v -0.992557 0.662497 -0.374808 +v -0.992557 0.762497 1.081832 +v -0.992557 0.974997 -1.587308 +v -0.992557 0.974997 -2.111530 +v -0.992557 0.662497 -2.174808 +v -0.992557 0.762497 -1.524496 +v -0.953354 0.679826 2.102824 +v -0.927631 0.924201 2.060324 +v 0.977347 0.949997 2.075192 +v -0.492557 0.662495 2.225192 +v -0.528354 0.679825 2.187824 +v -0.892557 0.662497 -2.174808 +v -0.992557 0.610414 -2.174808 +v 1.007443 0.610414 -0.374808 +v 1.007443 0.452080 -0.374808 +v 1.007443 0.452080 -0.859183 +v 1.007443 0.762497 1.081832 +v 1.007443 0.452080 0.876754 +v -0.477426 0.999997 2.125192 +v -0.515493 0.931701 2.102824 +v 0.492395 0.999997 2.125192 +v 0.530460 0.931701 2.102824 +v 0.942668 0.924201 2.060324 +v 0.507443 0.662495 2.225192 +v 0.968251 0.679826 2.102824 +v 0.543251 0.679825 2.187824 +v 1.007443 0.974997 -1.587308 +v 1.007443 0.946872 0.975192 +v -0.960193 0.999997 -2.124808 +v -0.670682 1.499997 0.188942 +v -0.685213 1.475622 0.172598 +v -0.946775 1.025622 0.711473 +v -0.670682 1.499997 -1.188558 +v -0.946775 1.025622 -1.473589 +v -0.685213 1.475622 -1.159714 +v -0.946775 1.025622 -1.015855 +v -0.946775 1.025622 -1.212027 +v 0.700100 1.498122 -0.447277 +v 0.961662 1.025622 -0.334777 +v 0.700100 1.475622 -1.159714 +v 0.961662 1.025622 -1.473589 +v 1.007443 0.610414 -2.174808 +v 0.907443 0.662497 -2.174808 +v 1.007443 0.662497 -2.174808 +v -0.992557 0.610414 -0.374808 +v -0.992557 0.452080 -0.374808 +v -0.992557 0.452080 0.876754 +v -0.992557 0.937497 1.425192 +v -0.992557 0.762497 1.491989 +v -0.992557 0.974997 -0.959183 +v -0.992557 0.762497 -1.021996 +v 1.007443 0.762497 -1.524496 +v -0.685213 1.481247 -1.004745 +v -0.685213 1.485466 -0.888519 +v 0.491818 1.049997 -1.612308 +v 0.467600 1.061647 -1.603589 +v -0.636775 1.489147 -1.197277 +v -0.476375 1.049997 -2.124808 +v 0.007443 0.662497 -2.174808 +v -0.992557 0.452080 -0.859183 +v 1.007443 0.452080 -1.687308 +v 1.007443 0.452080 -2.174808 +v -0.992557 0.452080 -2.174808 +v -0.992557 0.452080 -1.687308 +v 0.491818 1.049997 0.837692 +v 0.346506 1.574997 -0.499808 +v 0.346506 1.549997 -1.263558 +v 0.007443 1.564997 -1.288558 +v 0.007443 1.589997 -0.499808 +v 0.007443 1.564997 0.238942 +v -0.331619 1.549997 0.238942 +v -0.314666 1.536497 0.253911 +v 0.007443 1.551497 0.253911 +v -0.636775 1.488997 0.203911 +v -0.476932 1.049997 0.837692 +v 0.007443 0.999997 2.140192 +v 0.007443 1.064997 0.837692 +v -0.331619 1.574997 -0.499808 +v 0.346506 1.549997 0.238942 +v 0.329553 1.536497 0.253911 +v 0.651662 1.488997 0.203911 +v 0.927756 1.013997 0.772723 +v 0.329553 1.536647 -1.272277 +v 0.007443 1.550897 -1.297277 +v -0.912869 1.013997 0.772723 +v 0.467600 1.061497 0.822723 +v 0.007443 1.076497 0.822723 +v -0.452713 1.061497 0.822723 +v -0.331619 1.549997 -1.263558 +v -0.314666 1.536647 -1.272277 +v 0.007443 0.999997 2.140192 +v 0.007443 0.662494 2.240192 +v 0.507443 0.662495 2.225192 +v 0.492395 0.999997 2.125192 +v -0.477426 0.999997 2.125192 +v -0.492557 0.662495 2.225192 +v -0.946775 1.025622 -0.427339 +v -0.946775 1.025622 -1.015855 +v -0.945468 1.027928 0.704613 +v -0.945468 1.027928 -0.331174 +v 0.957117 1.033636 -0.322257 +v 0.957117 1.033636 0.687636 +v -0.946775 1.025622 -1.212027 +v -0.946775 1.025622 -1.473589 +v -0.945468 1.027928 -0.432067 +v -0.945468 1.027928 -1.467854 +v 0.957117 1.033636 -1.453661 +v 0.957117 1.033636 -0.443769 +v -0.604564 1.478839 -1.205559 +v -0.866854 1.027589 -1.520307 +v 0.881740 1.027589 -1.520307 +v 0.619451 1.478839 -1.205559 +v -0.429705 1.072714 -1.595307 +v 0.444592 1.072714 -1.595307 +v 0.007443 1.086252 -1.620306 +v -0.298561 1.523822 0.267131 +v 0.007443 1.538072 0.267131 +v 0.444592 1.072572 0.807503 +v 0.007443 1.086822 0.807503 +v 0.881740 1.027447 0.760003 +v -0.429705 1.072572 0.807503 +v -0.866854 1.027447 0.760003 +v 0.007443 1.537502 -1.305560 +v -0.298561 1.523964 -1.280560 +v 0.313447 1.523964 -1.280560 +v -0.604564 1.478697 0.219631 +v 0.313447 1.523822 0.267131 +v 0.619451 1.478697 0.219631 +v -0.686521 1.495703 -0.442549 +v -0.685213 1.475622 -1.159714 +v -0.685213 1.498122 -0.539839 +v -0.685213 1.481247 -1.004745 +v -0.686521 1.495703 -0.543442 +v -0.686521 1.473428 -1.157118 +v -0.686521 1.473428 0.171127 +v 0.704644 1.489717 -0.430848 +v 0.704644 1.467999 -1.150693 +v 0.704644 1.489717 -0.552359 +v 0.704644 1.467999 0.167487 +v -0.685213 1.485466 -0.888519 +v -0.686521 1.483173 -0.888635 +v -1.042557 0.427080 0.876754 +v -1.042557 0.737497 1.081832 +v -1.042557 0.737497 1.491989 +v 1.057443 0.427080 -0.859183 +v 1.057443 0.737497 -1.021996 +v 1.057443 0.737497 -1.524496 +v 1.057443 0.427080 -1.687308 +v 1.057443 0.737497 1.081832 +v 1.057443 0.427080 0.876754 +v 1.057443 0.737497 1.491989 +v -1.042557 0.450362 0.938278 +v -1.042557 0.714216 1.112594 +v -1.042557 0.714216 1.461227 +v -0.967557 0.299997 0.876754 +v 1.057443 0.714216 1.112594 +v 1.057443 0.450362 0.938278 +v 1.057443 0.714216 1.461227 +v 0.982443 0.299997 0.876754 +v 1.057443 0.721970 -1.486808 +v 1.057443 0.442607 -1.625199 +v 1.057443 0.442607 -0.921292 +v 1.057443 0.721970 -1.059683 +v 0.982443 0.299997 -1.687308 +v 0.982443 0.299997 -0.859183 +v -1.042557 0.427080 -0.859183 +v -1.042557 0.450362 -0.921292 +v -0.967557 0.299997 -0.859183 +v -0.967557 0.299997 -2.174808 +v -0.967557 0.299997 -1.687308 +v 1.007443 0.452080 1.697067 +v 1.007443 0.402080 2.125192 +v 0.982443 0.299997 2.125192 +v 0.982443 0.299997 1.697067 +v 0.982443 0.299997 -0.374808 +v -0.967557 0.299997 -0.374808 +v 0.007443 0.662494 2.240192 +v 0.007443 0.646869 2.375192 +v 0.457460 0.646870 2.360192 +v 0.952443 0.356765 -2.274808 +v 0.982443 0.299997 -2.174808 +v -0.937557 0.356765 -2.274808 +v 0.007443 0.356765 -2.274808 +v 0.007443 0.299997 -2.174808 +v -0.870057 0.299997 -2.174808 +v 0.907476 0.646872 2.260192 +v 0.007443 0.299997 2.225192 +v 0.494943 0.299997 2.225192 +v 0.457460 0.365622 2.360192 +v 0.007443 0.365622 2.375192 +v 0.884943 0.299997 -2.174808 +v -0.967557 0.299997 2.125192 +v -0.480057 0.299997 2.225192 +v -0.442573 0.365622 2.360192 +v -0.892590 0.365622 2.260192 +v 0.907476 0.412497 2.275192 +v 0.457460 0.412496 2.375192 +v 0.907476 0.365622 2.260192 +v 0.007443 0.412495 2.390192 +v -0.442573 0.412496 2.375192 +v -0.892590 0.412497 2.275192 +v 1.007443 0.610414 2.125192 +v 0.907476 0.599997 2.275192 +v -0.892590 0.646872 2.260192 +v -0.442573 0.646870 2.360192 +v -0.992557 0.402080 2.125192 +v -0.992557 0.610414 2.125192 +v -0.892590 0.599997 2.275192 +v -0.442573 0.599995 2.375192 +v 0.007443 0.599993 2.390192 +v 0.457460 0.599995 2.375192 +v -0.937557 0.630622 -2.274808 +v 0.952443 0.630622 -2.274808 +v 0.007443 0.630622 -2.274808 +v -0.992557 0.452080 1.697067 +v -0.967557 0.299997 1.697067 +v -1.042557 0.737497 -1.021996 +v -1.042557 0.737497 -1.524496 +v 1.057443 0.427080 1.697067 +v -1.042557 0.427080 1.697067 +v -1.042557 0.427080 -1.687308 +v -1.042557 0.450362 1.635544 +v 1.057443 0.450362 1.635543 +v -1.042557 0.450362 -1.625199 +v -1.042557 0.714216 -1.486808 +v -1.042557 0.714216 -1.059683 +v 0.787443 0.299997 -1.687308 +v -0.772557 0.299997 -1.687308 +v 0.007443 0.299997 -1.687308 +v -0.480057 0.299997 0.975192 +v -0.480057 0.299997 -0.374808 +v 0.007443 0.299997 -0.374808 +v 0.007443 0.299997 0.975192 +v 0.494943 0.299997 0.975192 +v 0.494943 0.299997 -0.374808 +v -0.480057 0.299997 1.575192 +v 0.007443 0.299997 1.575192 +v 0.494943 0.299997 1.575192 +v -0.699432 0.299997 -0.859183 +v 0.007443 0.299997 -0.859183 +v 0.714318 0.299997 -0.859183 +v -0.942557 0.399997 0.885192 +v -0.742557 0.399997 0.885192 +v -0.742557 0.321961 0.892878 +v -0.942557 0.321961 0.892878 +v -0.742557 0.246923 0.915640 +v -0.942557 0.246923 0.915640 +v -0.742557 0.177769 0.952604 +v -0.942557 0.177769 0.952604 +v -0.742557 0.117154 1.002349 +v -0.942557 0.117154 1.002349 +v -0.742557 0.067409 1.062963 +v -0.942557 0.067409 1.062963 +v -0.742557 0.030445 1.132118 +v -0.942557 0.030445 1.132118 +v -0.742557 0.007683 1.207155 +v -0.942557 0.007683 1.207155 +v -0.742557 -0.000003 1.285192 +v -0.942557 -0.000003 1.285192 +v -0.742557 0.007683 1.363228 +v -0.942557 0.007683 1.363228 +v -0.742557 0.030445 1.438265 +v -0.942557 0.030445 1.438265 +v -0.742557 0.067409 1.507420 +v -0.942557 0.067409 1.507420 +v -0.742557 0.117154 1.568034 +v -0.942557 0.117154 1.568034 +v -0.742557 0.177769 1.617779 +v -0.942557 0.177769 1.617779 +v -0.742557 0.246923 1.654743 +v -0.942557 0.246923 1.654743 +v -0.742557 0.321961 1.677506 +v -0.942557 0.321961 1.677506 +v -0.742557 0.399997 1.685192 +v -0.942557 0.399997 1.685192 +v -0.742557 0.478033 1.677506 +v -0.942557 0.478033 1.677506 +v -0.742557 0.553070 1.654743 +v -0.942557 0.553070 1.654743 +v -0.742557 0.622225 1.617779 +v -0.942557 0.622225 1.617779 +v -0.742557 0.682840 1.568034 +v -0.942557 0.682840 1.568034 +v -0.742557 0.732585 1.507420 +v -0.942557 0.732585 1.507420 +v -0.742557 0.769549 1.438265 +v -0.942557 0.769549 1.438265 +v -0.742557 0.792311 1.363227 +v -0.942557 0.792311 1.363227 +v -0.742557 0.799997 1.285191 +v -0.942557 0.799997 1.285191 +v -0.742557 0.792311 1.207155 +v -0.942557 0.792311 1.207155 +v -0.742557 0.769548 1.132118 +v -0.942557 0.769548 1.132118 +v -0.742557 0.732584 1.062963 +v -0.942557 0.732584 1.062963 +v -0.742557 0.682839 1.002349 +v -0.942557 0.682839 1.002349 +v -0.742557 0.622224 0.952604 +v -0.942557 0.622224 0.952604 +v -0.742557 0.553070 0.915640 +v -0.942557 0.553070 0.915640 +v -0.700057 0.751071 1.430611 +v -0.700057 0.772695 1.359326 +v -0.742557 0.478032 0.892877 +v -0.942557 0.478032 0.892877 +v -0.985057 0.399997 0.905192 +v -0.985057 0.474131 0.912493 +v -0.700057 0.188880 1.601150 +v -0.700057 0.131296 1.553892 +v -0.700057 0.198471 1.486717 +v -0.700057 0.241659 1.522161 +v -0.985057 0.751071 1.139771 +v -0.985057 0.715955 1.074075 +v -0.985057 0.636965 1.126854 +v -0.985057 0.663302 1.176126 +v -0.985057 0.048923 1.139772 +v -0.985057 0.084038 1.074075 +v -0.700057 0.474130 0.912493 +v -0.700057 0.399997 0.905192 +v -0.985057 0.325863 1.657890 +v -0.985057 0.254577 1.636266 +v -0.700057 0.084038 1.074075 +v -0.700057 0.048923 1.139772 +v -0.985057 0.779997 1.285191 +v -0.985057 0.772695 1.359326 +v -0.700057 0.254577 1.636266 +v -0.700057 0.325862 1.657890 +v -0.700057 0.779997 1.285191 +v -0.985057 0.027298 1.211057 +v -0.985057 0.399997 1.665192 +v -0.700057 0.027298 1.211057 +v -0.985057 0.772695 1.211057 +v -0.700057 0.399997 1.665192 +v -0.700057 0.772695 1.211057 +v -0.985057 0.019997 1.285192 +v -0.985057 0.474131 1.657890 +v -0.700057 0.019997 1.285192 +v -0.700057 0.474131 1.657890 +v -0.700057 0.751071 1.139771 +v -0.985057 0.027298 1.359326 +v -0.985057 0.545417 1.636266 +v -0.700057 0.027298 1.359326 +v -0.985057 0.325862 0.912493 +v -0.700057 0.545417 1.636266 +v -0.700057 0.325862 0.912493 +v -0.700057 0.715955 1.074075 +v -0.985057 0.048923 1.430611 +v -0.985057 0.611114 1.601150 +v -0.700057 0.048923 1.430611 +v -0.985057 0.668697 1.016491 +v -0.700057 0.611114 1.601150 +v -0.985057 0.254577 0.934117 +v -0.700057 0.668697 1.016491 +v -0.985057 0.084038 1.496308 +v -0.700057 0.254577 0.934117 +v -0.985057 0.668697 1.553892 +v -0.700057 0.084038 1.496308 +v -0.985057 0.611113 0.969233 +v -0.700057 0.668697 1.553892 +v -0.985057 0.188880 0.969233 +v -0.700057 0.611113 0.969233 +v -0.985057 0.131296 1.553892 +v -0.700057 0.188880 0.969233 +v -0.985057 0.715955 1.496308 +v -0.985057 0.545416 0.934117 +v -0.700057 0.715955 1.496308 +v -0.985057 0.131296 1.016491 +v -0.700057 0.545416 0.934117 +v -0.985057 0.188880 1.601150 +v -0.700057 0.131296 1.016491 +v -0.985057 0.751071 1.430611 +v -0.700057 0.455598 1.564715 +v -0.700057 0.399997 1.570192 +v -0.714307 0.399997 1.541692 +v -0.714307 0.450038 1.536763 +v -0.985057 0.120473 1.229591 +v -0.985057 0.136691 1.176127 +v -0.985057 0.290932 1.548497 +v -0.985057 0.241659 1.522161 +v -0.985057 0.636966 1.443529 +v -0.985057 0.601522 1.486717 +v -0.985057 0.344396 1.005668 +v -0.985057 0.399997 1.000192 +v -0.985057 0.601522 1.083666 +v -0.985057 0.114997 1.285192 +v -0.985057 0.344396 1.564715 +v -0.985057 0.663303 1.394256 +v -0.985057 0.290932 1.021886 +v -0.985057 0.558334 1.048223 +v -0.985057 0.120473 1.340792 +v -0.985057 0.399997 1.570192 +v -0.985057 0.679521 1.340792 +v -0.985057 0.241659 1.048223 +v -0.985057 0.509061 1.021886 +v -0.985057 0.136691 1.394256 +v -0.985057 0.455598 1.564715 +v -0.985057 0.684997 1.285191 +v -0.985057 0.198471 1.083666 +v -0.985057 0.455597 1.005668 +v -0.985057 0.163028 1.443529 +v -0.985057 0.509062 1.548497 +v -0.985057 0.679520 1.229591 +v -0.985057 0.163028 1.126854 +v -0.985057 0.198471 1.486717 +v -0.985057 0.558334 1.522161 +v -0.970807 0.301838 1.048217 +v -0.970807 0.349956 1.033620 +v -0.700057 0.558334 1.522161 +v -0.700057 0.601522 1.486717 +v -0.700057 0.663302 1.176126 +v -0.700057 0.636965 1.126854 +v -0.700057 0.136691 1.176127 +v -0.700057 0.120473 1.229591 +v -0.700057 0.290932 1.548497 +v -0.700057 0.636966 1.443529 +v -0.700057 0.399997 1.000192 +v -0.700057 0.344396 1.005668 +v -0.700057 0.601522 1.083666 +v -0.700057 0.114997 1.285192 +v -0.700057 0.344396 1.564715 +v -0.700057 0.663302 1.394256 +v -0.700057 0.290932 1.021886 +v -0.700057 0.558334 1.048223 +v -0.700057 0.120473 1.340792 +v -0.700057 0.679520 1.340792 +v -0.700057 0.241659 1.048223 +v -0.700057 0.509061 1.021886 +v -0.700057 0.136691 1.394256 +v -0.700057 0.684997 1.285191 +v -0.700057 0.198471 1.083666 +v -0.700057 0.455597 1.005668 +v -0.700057 0.163028 1.443529 +v -0.700057 0.509062 1.548497 +v -0.700057 0.679520 1.229591 +v -0.700057 0.163028 1.126854 +v -0.970807 0.542500 1.071919 +v -0.970807 0.581369 1.103818 +v -0.970807 0.257493 1.071920 +v -0.714307 0.498155 1.522167 +v -0.970807 0.498155 1.048216 +v -0.970807 0.218624 1.103819 +v -0.714307 0.542501 1.498464 +v -0.970807 0.450037 1.033620 +v -0.970807 0.186725 1.142688 +v -0.714307 0.581370 1.466564 +v -0.970807 0.399997 1.028692 +v -0.970807 0.163022 1.187033 +v -0.714307 0.613269 1.427695 +v -0.970807 0.148425 1.235151 +v -0.714307 0.636972 1.383350 +v -0.970807 0.143497 1.285192 +v -0.714307 0.651568 1.335232 +v -0.970807 0.148425 1.335232 +v -0.714307 0.656497 1.285191 +v -0.970807 0.163022 1.383350 +v -0.714307 0.651568 1.235151 +v -0.970807 0.186725 1.427695 +v -0.714307 0.636972 1.187033 +v -0.970807 0.218624 1.466564 +v -0.714307 0.613268 1.142688 +v -0.714307 0.399997 1.028692 +v -0.714307 0.349956 1.033620 +v -0.970807 0.257493 1.498464 +v -0.714307 0.581369 1.103818 +v -0.714307 0.301838 1.048217 +v -0.970807 0.301839 1.522167 +v -0.714307 0.542500 1.071919 +v -0.714307 0.257493 1.071920 +v -0.970807 0.349956 1.536763 +v -0.714307 0.498155 1.048216 +v -0.714307 0.218624 1.103819 +v -0.970807 0.399997 1.541692 +v -0.714307 0.450037 1.033620 +v -0.714307 0.186725 1.142688 +v -0.970807 0.450038 1.536763 +v -0.714307 0.163022 1.187033 +v -0.970807 0.498155 1.522167 +v -0.714307 0.148425 1.235151 +v -0.970807 0.542501 1.498464 +v -0.714307 0.143497 1.285192 +v -0.970807 0.581370 1.466564 +v -0.714307 0.148425 1.335232 +v -0.970807 0.613269 1.427695 +v -0.714307 0.163022 1.383350 +v -0.970807 0.636972 1.383350 +v -0.714307 0.186725 1.427695 +v -0.970807 0.651568 1.335232 +v -0.714307 0.218624 1.466564 +v -0.970807 0.656497 1.285191 +v -0.714307 0.257493 1.498464 +v -0.970807 0.651568 1.235151 +v -0.714307 0.301838 1.522167 +v -0.970807 0.636972 1.187033 +v -0.714307 0.349956 1.536763 +v -0.970807 0.613268 1.142688 +v -0.942557 0.399997 -1.674808 +v -0.742557 0.399997 -1.674808 +v -0.742557 0.321961 -1.667122 +v -0.942557 0.321961 -1.667122 +v -0.742557 0.246923 -1.644360 +v -0.942557 0.246923 -1.644360 +v -0.742557 0.177769 -1.607396 +v -0.942557 0.177769 -1.607396 +v -0.742557 0.117154 -1.557651 +v -0.942557 0.117154 -1.557651 +v -0.742557 0.067409 -1.497036 +v -0.942557 0.067409 -1.497036 +v -0.742557 0.030445 -1.427882 +v -0.942557 0.030445 -1.427882 +v -0.742557 0.007683 -1.352844 +v -0.942557 0.007683 -1.352844 +v -0.742557 -0.000003 -1.274808 +v -0.942557 -0.000003 -1.274808 +v -0.742557 0.007683 -1.196772 +v -0.942557 0.007683 -1.196772 +v -0.742557 0.030445 -1.121735 +v -0.942557 0.030445 -1.121735 +v -0.742557 0.067409 -1.052580 +v -0.942557 0.067409 -1.052580 +v -0.742557 0.117154 -0.991966 +v -0.942557 0.117154 -0.991966 +v -0.742557 0.177769 -0.942220 +v -0.942557 0.177769 -0.942220 +v -0.742557 0.246923 -0.905256 +v -0.942557 0.246923 -0.905256 +v -0.742557 0.321961 -0.882494 +v -0.942557 0.321961 -0.882494 +v -0.742557 0.399997 -0.874808 +v -0.942557 0.399997 -0.874808 +v -0.742557 0.478033 -0.882494 +v -0.942557 0.478033 -0.882494 +v -0.742557 0.553070 -0.905257 +v -0.942557 0.553070 -0.905257 +v -0.742557 0.622225 -0.942221 +v -0.942557 0.622225 -0.942221 +v -0.742557 0.682840 -0.991966 +v -0.942557 0.682840 -0.991966 +v -0.742557 0.732585 -1.052580 +v -0.942557 0.732585 -1.052580 +v -0.742557 0.769549 -1.121735 +v -0.942557 0.769549 -1.121735 +v -0.742557 0.792311 -1.196773 +v -0.942557 0.792311 -1.196773 +v -0.742557 0.799997 -1.274809 +v -0.942557 0.799997 -1.274809 +v -0.742557 0.792311 -1.352845 +v -0.942557 0.792311 -1.352845 +v -0.742557 0.769548 -1.427882 +v -0.942557 0.769548 -1.427882 +v -0.742557 0.732584 -1.497037 +v -0.942557 0.732584 -1.497037 +v -0.742557 0.682839 -1.557651 +v -0.942557 0.682839 -1.557651 +v -0.742557 0.622224 -1.607396 +v -0.942557 0.622224 -1.607396 +v -0.742557 0.553070 -1.644360 +v -0.942557 0.553070 -1.644360 +v -0.700057 0.751071 -1.129389 +v -0.700057 0.772695 -1.200674 +v -0.742557 0.478032 -1.667122 +v -0.942557 0.478032 -1.667122 +v -0.985057 0.399997 -1.654808 +v -0.985057 0.474131 -1.647507 +v -0.700057 0.188880 -0.958850 +v -0.700057 0.131296 -1.006108 +v -0.700057 0.198471 -1.073283 +v -0.700057 0.241659 -1.037839 +v -0.985057 0.751071 -1.420228 +v -0.985057 0.715955 -1.485925 +v -0.985057 0.636965 -1.433146 +v -0.985057 0.663302 -1.383873 +v -0.985057 0.048923 -1.420228 +v -0.985057 0.084038 -1.485925 +v -0.700057 0.474130 -1.647507 +v -0.700057 0.399997 -1.654808 +v -0.985057 0.325863 -0.902110 +v -0.985057 0.254577 -0.923734 +v -0.700057 0.084038 -1.485925 +v -0.700057 0.048923 -1.420228 +v -0.985057 0.779997 -1.274809 +v -0.985057 0.772695 -1.200674 +v -0.700057 0.254577 -0.923734 +v -0.700057 0.325862 -0.902110 +v -0.700057 0.779997 -1.274809 +v -0.985057 0.027298 -1.348943 +v -0.985057 0.399997 -0.894808 +v -0.700057 0.027298 -1.348943 +v -0.985057 0.772695 -1.348943 +v -0.700057 0.399997 -0.894808 +v -0.700057 0.772695 -1.348943 +v -0.985057 0.019997 -1.274808 +v -0.985057 0.474131 -0.902110 +v -0.700057 0.019997 -1.274808 +v -0.700057 0.474131 -0.902110 +v -0.700057 0.751071 -1.420228 +v -0.985057 0.027298 -1.200674 +v -0.985057 0.545417 -0.923734 +v -0.700057 0.027298 -1.200674 +v -0.985057 0.325862 -1.647507 +v -0.700057 0.545417 -0.923734 +v -0.700057 0.325862 -1.647507 +v -0.700057 0.715955 -1.485925 +v -0.985057 0.048923 -1.129389 +v -0.985057 0.611114 -0.958850 +v -0.700057 0.048923 -1.129389 +v -0.985057 0.668697 -1.543509 +v -0.700057 0.611114 -0.958850 +v -0.985057 0.254577 -1.625882 +v -0.700057 0.668697 -1.543509 +v -0.985057 0.084038 -1.063692 +v -0.700057 0.254577 -1.625882 +v -0.985057 0.668697 -1.006108 +v -0.700057 0.084038 -1.063692 +v -0.985057 0.611113 -1.590767 +v -0.700057 0.668697 -1.006108 +v -0.985057 0.188880 -1.590767 +v -0.700057 0.611113 -1.590767 +v -0.985057 0.131296 -1.006108 +v -0.700057 0.188880 -1.590767 +v -0.985057 0.715955 -1.063692 +v -0.985057 0.545416 -1.625883 +v -0.700057 0.715955 -1.063692 +v -0.985057 0.131296 -1.543509 +v -0.700057 0.545416 -1.625883 +v -0.985057 0.188880 -0.958850 +v -0.700057 0.131296 -1.543509 +v -0.985057 0.751071 -1.129389 +v -0.700057 0.455598 -0.995285 +v -0.700057 0.399997 -0.989808 +v -0.714307 0.399997 -1.018308 +v -0.714307 0.450038 -1.023237 +v -0.985057 0.120473 -1.330409 +v -0.985057 0.136691 -1.383873 +v -0.985057 0.290932 -1.011503 +v -0.985057 0.241659 -1.037839 +v -0.985057 0.636966 -1.116471 +v -0.985057 0.601522 -1.073283 +v -0.985057 0.344396 -1.554332 +v -0.985057 0.399997 -1.559808 +v -0.985057 0.601522 -1.476334 +v -0.985057 0.114997 -1.274808 +v -0.985057 0.344396 -0.995284 +v -0.985057 0.663303 -1.165744 +v -0.985057 0.290932 -1.538114 +v -0.985057 0.558334 -1.511777 +v -0.985057 0.120473 -1.219208 +v -0.985057 0.399997 -0.989808 +v -0.985057 0.679521 -1.219208 +v -0.985057 0.241659 -1.511777 +v -0.985057 0.509061 -1.538114 +v -0.985057 0.136691 -1.165744 +v -0.985057 0.455598 -0.995285 +v -0.985057 0.684997 -1.274809 +v -0.985057 0.198471 -1.476334 +v -0.985057 0.455597 -1.554332 +v -0.985057 0.163028 -1.116471 +v -0.985057 0.509062 -1.011503 +v -0.985057 0.679520 -1.330409 +v -0.985057 0.163028 -1.433146 +v -0.985057 0.198471 -1.073283 +v -0.985057 0.558334 -1.037839 +v -0.970807 0.301838 -1.511783 +v -0.970807 0.349956 -1.526380 +v -0.700057 0.558334 -1.037839 +v -0.700057 0.601522 -1.073283 +v -0.700057 0.663302 -1.383873 +v -0.700057 0.636965 -1.433146 +v -0.700057 0.136691 -1.383873 +v -0.700057 0.120473 -1.330409 +v -0.700057 0.290932 -1.011503 +v -0.700057 0.636966 -1.116471 +v -0.700057 0.399997 -1.559808 +v -0.700057 0.344396 -1.554332 +v -0.700057 0.601522 -1.476334 +v -0.700057 0.114997 -1.274808 +v -0.700057 0.344396 -0.995284 +v -0.700057 0.663302 -1.165744 +v -0.700057 0.290932 -1.538114 +v -0.700057 0.558334 -1.511777 +v -0.700057 0.120473 -1.219208 +v -0.700057 0.679520 -1.219208 +v -0.700057 0.241659 -1.511777 +v -0.700057 0.509061 -1.538114 +v -0.700057 0.136691 -1.165744 +v -0.700057 0.684997 -1.274809 +v -0.700057 0.198471 -1.476334 +v -0.700057 0.455597 -1.554332 +v -0.700057 0.163028 -1.116471 +v -0.700057 0.509062 -1.011503 +v -0.700057 0.679520 -1.330409 +v -0.700057 0.163028 -1.433146 +v -0.970807 0.542500 -1.488081 +v -0.970807 0.581369 -1.456182 +v -0.970807 0.257493 -1.488080 +v -0.714307 0.498155 -1.037833 +v -0.970807 0.498155 -1.511784 +v -0.970807 0.218624 -1.456181 +v -0.714307 0.542501 -1.061536 +v -0.970807 0.450037 -1.526380 +v -0.970807 0.186725 -1.417312 +v -0.714307 0.581370 -1.093436 +v -0.970807 0.399997 -1.531308 +v -0.970807 0.163022 -1.372967 +v -0.714307 0.613269 -1.132305 +v -0.970807 0.148425 -1.324849 +v -0.714307 0.636972 -1.176650 +v -0.970807 0.143497 -1.274808 +v -0.714307 0.651568 -1.224768 +v -0.970807 0.148425 -1.224768 +v -0.714307 0.656497 -1.274809 +v -0.970807 0.163022 -1.176650 +v -0.714307 0.651568 -1.324849 +v -0.970807 0.186725 -1.132305 +v -0.714307 0.636972 -1.372967 +v -0.970807 0.218624 -1.093436 +v -0.714307 0.613268 -1.417312 +v -0.714307 0.399997 -1.531308 +v -0.714307 0.349956 -1.526380 +v -0.970807 0.257493 -1.061536 +v -0.714307 0.581369 -1.456182 +v -0.714307 0.301838 -1.511783 +v -0.970807 0.301839 -1.037833 +v -0.714307 0.542500 -1.488081 +v -0.714307 0.257493 -1.488080 +v -0.970807 0.349956 -1.023237 +v -0.714307 0.498155 -1.511784 +v -0.714307 0.218624 -1.456181 +v -0.970807 0.399997 -1.018308 +v -0.714307 0.450037 -1.526380 +v -0.714307 0.186725 -1.417312 +v -0.970807 0.450038 -1.023237 +v -0.714307 0.163022 -1.372967 +v -0.970807 0.498155 -1.037833 +v -0.714307 0.148425 -1.324849 +v -0.970807 0.542501 -1.061536 +v -0.714307 0.143497 -1.274808 +v -0.970807 0.581370 -1.093436 +v -0.714307 0.148425 -1.224768 +v -0.970807 0.613269 -1.132305 +v -0.714307 0.163022 -1.176650 +v -0.970807 0.636972 -1.176650 +v -0.714307 0.186725 -1.132305 +v -0.970807 0.651568 -1.224768 +v -0.714307 0.218624 -1.093436 +v -0.970807 0.656497 -1.274809 +v -0.714307 0.257493 -1.061536 +v -0.970807 0.651568 -1.324849 +v -0.714307 0.301838 -1.037833 +v -0.970807 0.636972 -1.372967 +v -0.714307 0.349956 -1.023237 +v -0.970807 0.613268 -1.417312 +v 0.757443 0.399997 -1.674808 +v 0.957443 0.399997 -1.674808 +v 0.957443 0.321961 -1.667122 +v 0.757443 0.321961 -1.667122 +v 0.957443 0.246923 -1.644360 +v 0.757443 0.246923 -1.644360 +v 0.957443 0.177769 -1.607396 +v 0.757443 0.177769 -1.607396 +v 0.957443 0.117154 -1.557651 +v 0.757443 0.117154 -1.557651 +v 0.957443 0.067409 -1.497036 +v 0.757443 0.067409 -1.497036 +v 0.957443 0.030445 -1.427882 +v 0.757443 0.030445 -1.427882 +v 0.957443 0.007683 -1.352844 +v 0.757443 0.007683 -1.352844 +v 0.957443 -0.000003 -1.274808 +v 0.757443 -0.000003 -1.274808 +v 0.957443 0.007683 -1.196772 +v 0.757443 0.007683 -1.196772 +v 0.957443 0.030445 -1.121735 +v 0.757443 0.030445 -1.121735 +v 0.957443 0.067409 -1.052580 +v 0.757443 0.067409 -1.052580 +v 0.957443 0.117154 -0.991966 +v 0.757443 0.117154 -0.991966 +v 0.957443 0.177769 -0.942220 +v 0.757443 0.177769 -0.942220 +v 0.957443 0.246923 -0.905256 +v 0.757443 0.246923 -0.905256 +v 0.957443 0.321961 -0.882494 +v 0.757443 0.321961 -0.882494 +v 0.957443 0.399997 -0.874808 +v 0.757443 0.399997 -0.874808 +v 0.957443 0.478033 -0.882494 +v 0.757443 0.478033 -0.882494 +v 0.957443 0.553070 -0.905257 +v 0.757443 0.553070 -0.905257 +v 0.957443 0.622225 -0.942221 +v 0.757443 0.622225 -0.942221 +v 0.957443 0.682840 -0.991966 +v 0.757443 0.682840 -0.991966 +v 0.957443 0.732585 -1.052580 +v 0.757443 0.732585 -1.052580 +v 0.957443 0.769549 -1.121735 +v 0.757443 0.769549 -1.121735 +v 0.957443 0.792311 -1.196773 +v 0.757443 0.792311 -1.196773 +v 0.957443 0.799997 -1.274809 +v 0.757443 0.799997 -1.274809 +v 0.957443 0.792311 -1.352845 +v 0.757443 0.792311 -1.352845 +v 0.957443 0.769548 -1.427882 +v 0.757443 0.769548 -1.427882 +v 0.957443 0.732584 -1.497037 +v 0.757443 0.732584 -1.497037 +v 0.957443 0.682839 -1.557651 +v 0.757443 0.682839 -1.557651 +v 0.957443 0.622224 -1.607396 +v 0.757443 0.622224 -1.607396 +v 0.957443 0.553070 -1.644360 +v 0.757443 0.553070 -1.644360 +v 0.999943 0.751071 -1.129389 +v 0.999943 0.772695 -1.200674 +v 0.957443 0.478032 -1.667122 +v 0.757443 0.478032 -1.667122 +v 0.714943 0.399997 -1.654808 +v 0.714943 0.474131 -1.647507 +v 0.999943 0.188880 -0.958850 +v 0.999943 0.131296 -1.006108 +v 0.999943 0.198471 -1.073283 +v 0.999943 0.241659 -1.037839 +v 0.714943 0.751071 -1.420228 +v 0.714943 0.715955 -1.485925 +v 0.714943 0.636965 -1.433146 +v 0.714943 0.663302 -1.383873 +v 0.714943 0.048923 -1.420228 +v 0.714943 0.084038 -1.485925 +v 0.999943 0.474130 -1.647507 +v 0.999943 0.399997 -1.654808 +v 0.714943 0.325863 -0.902110 +v 0.714943 0.254577 -0.923734 +v 0.999943 0.084038 -1.485925 +v 0.999943 0.048923 -1.420228 +v 0.714943 0.779997 -1.274809 +v 0.714943 0.772695 -1.200674 +v 0.999943 0.254577 -0.923734 +v 0.999943 0.325862 -0.902110 +v 0.999943 0.779997 -1.274809 +v 0.714943 0.027298 -1.348943 +v 0.714943 0.399997 -0.894808 +v 0.999943 0.027298 -1.348943 +v 0.714943 0.772695 -1.348943 +v 0.999943 0.399997 -0.894808 +v 0.999943 0.772695 -1.348943 +v 0.714943 0.019997 -1.274808 +v 0.714943 0.474131 -0.902110 +v 0.999943 0.019997 -1.274808 +v 0.999943 0.474131 -0.902110 +v 0.999943 0.751071 -1.420228 +v 0.714943 0.027298 -1.200674 +v 0.714943 0.545417 -0.923734 +v 0.999943 0.027298 -1.200674 +v 0.714943 0.325862 -1.647507 +v 0.999943 0.545417 -0.923734 +v 0.999943 0.325862 -1.647507 +v 0.999943 0.715955 -1.485925 +v 0.714943 0.048923 -1.129389 +v 0.714943 0.611114 -0.958850 +v 0.999943 0.048923 -1.129389 +v 0.714943 0.668697 -1.543509 +v 0.999943 0.611114 -0.958850 +v 0.714943 0.254577 -1.625882 +v 0.999943 0.668697 -1.543509 +v 0.714943 0.084038 -1.063692 +v 0.999943 0.254577 -1.625882 +v 0.714943 0.668697 -1.006108 +v 0.999943 0.084038 -1.063692 +v 0.714943 0.611113 -1.590767 +v 0.999943 0.668697 -1.006108 +v 0.714943 0.188880 -1.590767 +v 0.999943 0.611113 -1.590767 +v 0.714943 0.131296 -1.006108 +v 0.999943 0.188880 -1.590767 +v 0.714943 0.715955 -1.063692 +v 0.714943 0.545416 -1.625883 +v 0.999943 0.715955 -1.063692 +v 0.714943 0.131296 -1.543509 +v 0.999943 0.545416 -1.625883 +v 0.714943 0.188880 -0.958850 +v 0.999943 0.131296 -1.543509 +v 0.714943 0.751071 -1.129389 +v 0.999943 0.455598 -0.995285 +v 0.999943 0.399997 -0.989808 +v 0.985693 0.399997 -1.018308 +v 0.985693 0.450038 -1.023237 +v 0.714943 0.120473 -1.330409 +v 0.714943 0.136691 -1.383873 +v 0.714943 0.290932 -1.011503 +v 0.714943 0.241659 -1.037839 +v 0.714943 0.636966 -1.116471 +v 0.714943 0.601522 -1.073283 +v 0.714943 0.344396 -1.554332 +v 0.714943 0.399997 -1.559808 +v 0.714943 0.601522 -1.476334 +v 0.714943 0.114997 -1.274808 +v 0.714943 0.344396 -0.995284 +v 0.714943 0.663303 -1.165744 +v 0.714943 0.290932 -1.538114 +v 0.714943 0.558334 -1.511777 +v 0.714943 0.120473 -1.219208 +v 0.714943 0.399997 -0.989808 +v 0.714943 0.679521 -1.219208 +v 0.714943 0.241659 -1.511777 +v 0.714943 0.509061 -1.538114 +v 0.714943 0.136691 -1.165744 +v 0.714943 0.455598 -0.995285 +v 0.714943 0.684997 -1.274809 +v 0.714943 0.198471 -1.476334 +v 0.714943 0.455597 -1.554332 +v 0.714943 0.163028 -1.116471 +v 0.714943 0.509062 -1.011503 +v 0.714943 0.679520 -1.330409 +v 0.714943 0.163028 -1.433146 +v 0.714943 0.198471 -1.073283 +v 0.714943 0.558334 -1.037839 +v 0.729193 0.301838 -1.511783 +v 0.729193 0.349956 -1.526380 +v 0.999943 0.558334 -1.037839 +v 0.999943 0.601522 -1.073283 +v 0.999943 0.663302 -1.383873 +v 0.999943 0.636965 -1.433146 +v 0.999943 0.136691 -1.383873 +v 0.999943 0.120473 -1.330409 +v 0.999943 0.290932 -1.011503 +v 0.999943 0.636966 -1.116471 +v 0.999943 0.399997 -1.559808 +v 0.999943 0.344396 -1.554332 +v 0.999943 0.601522 -1.476334 +v 0.999943 0.114997 -1.274808 +v 0.999943 0.344396 -0.995284 +v 0.999943 0.663302 -1.165744 +v 0.999943 0.290932 -1.538114 +v 0.999943 0.558334 -1.511777 +v 0.999943 0.120473 -1.219208 +v 0.999943 0.679520 -1.219208 +v 0.999943 0.241659 -1.511777 +v 0.999943 0.509061 -1.538114 +v 0.999943 0.136691 -1.165744 +v 0.999943 0.684997 -1.274809 +v 0.999943 0.198471 -1.476334 +v 0.999943 0.455597 -1.554332 +v 0.999943 0.163028 -1.116471 +v 0.999943 0.509062 -1.011503 +v 0.999943 0.679520 -1.330409 +v 0.999943 0.163028 -1.433146 +v 0.729193 0.542500 -1.488081 +v 0.729193 0.581369 -1.456182 +v 0.729193 0.257493 -1.488080 +v 0.985693 0.498155 -1.037833 +v 0.729193 0.498155 -1.511784 +v 0.729193 0.218624 -1.456181 +v 0.985693 0.542501 -1.061536 +v 0.729193 0.450037 -1.526380 +v 0.729193 0.186725 -1.417312 +v 0.985693 0.581370 -1.093436 +v 0.729193 0.399997 -1.531308 +v 0.729193 0.163022 -1.372967 +v 0.985693 0.613269 -1.132305 +v 0.729193 0.148425 -1.324849 +v 0.985693 0.636972 -1.176650 +v 0.729193 0.143497 -1.274808 +v 0.985693 0.651568 -1.224768 +v 0.729193 0.148425 -1.224768 +v 0.985693 0.656497 -1.274809 +v 0.729193 0.163022 -1.176650 +v 0.985693 0.651568 -1.324849 +v 0.729193 0.186725 -1.132305 +v 0.985693 0.636972 -1.372967 +v 0.729193 0.218624 -1.093436 +v 0.985693 0.613268 -1.417312 +v 0.985693 0.399997 -1.531308 +v 0.985693 0.349956 -1.526380 +v 0.729193 0.257493 -1.061536 +v 0.985693 0.581369 -1.456182 +v 0.985693 0.301838 -1.511783 +v 0.729193 0.301839 -1.037833 +v 0.985693 0.542500 -1.488081 +v 0.985693 0.257493 -1.488080 +v 0.729193 0.349956 -1.023237 +v 0.985693 0.498155 -1.511784 +v 0.985693 0.218624 -1.456181 +v 0.729193 0.399997 -1.018308 +v 0.985693 0.450037 -1.526380 +v 0.985693 0.186725 -1.417312 +v 0.729193 0.450038 -1.023237 +v 0.985693 0.163022 -1.372967 +v 0.729193 0.498155 -1.037833 +v 0.985693 0.148425 -1.324849 +v 0.729193 0.542501 -1.061536 +v 0.985693 0.143497 -1.274808 +v 0.729193 0.581370 -1.093436 +v 0.985693 0.148425 -1.224768 +v 0.729193 0.613269 -1.132305 +v 0.985693 0.163022 -1.176650 +v 0.729193 0.636972 -1.176650 +v 0.985693 0.186725 -1.132305 +v 0.729193 0.651568 -1.224768 +v 0.985693 0.218624 -1.093436 +v 0.729193 0.656497 -1.274809 +v 0.985693 0.257493 -1.061536 +v 0.729193 0.651568 -1.324849 +v 0.985693 0.301838 -1.037833 +v 0.729193 0.636972 -1.372967 +v 0.985693 0.349956 -1.023237 +v 0.729193 0.613268 -1.417312 +v 0.757443 0.399997 0.885192 +v 0.957443 0.399997 0.885192 +v 0.957443 0.321961 0.892878 +v 0.757443 0.321961 0.892878 +v 0.957443 0.246923 0.915640 +v 0.757443 0.246923 0.915640 +v 0.957443 0.177769 0.952604 +v 0.757443 0.177769 0.952604 +v 0.957443 0.117154 1.002349 +v 0.757443 0.117154 1.002349 +v 0.957443 0.067409 1.062963 +v 0.757443 0.067409 1.062963 +v 0.957443 0.030445 1.132118 +v 0.757443 0.030445 1.132118 +v 0.957443 0.007683 1.207155 +v 0.757443 0.007683 1.207155 +v 0.957443 -0.000003 1.285192 +v 0.757443 -0.000003 1.285192 +v 0.957443 0.007683 1.363228 +v 0.757443 0.007683 1.363228 +v 0.957443 0.030445 1.438265 +v 0.757443 0.030445 1.438265 +v 0.957443 0.067409 1.507420 +v 0.757443 0.067409 1.507420 +v 0.957443 0.117154 1.568034 +v 0.757443 0.117154 1.568034 +v 0.957443 0.177769 1.617779 +v 0.757443 0.177769 1.617779 +v 0.957443 0.246923 1.654743 +v 0.757443 0.246923 1.654743 +v 0.957443 0.321961 1.677506 +v 0.757443 0.321961 1.677506 +v 0.957443 0.399997 1.685192 +v 0.757443 0.399997 1.685192 +v 0.957443 0.478033 1.677506 +v 0.757443 0.478033 1.677506 +v 0.957443 0.553070 1.654743 +v 0.757443 0.553070 1.654743 +v 0.957443 0.622225 1.617779 +v 0.757443 0.622225 1.617779 +v 0.957443 0.682840 1.568034 +v 0.757443 0.682840 1.568034 +v 0.957443 0.732585 1.507420 +v 0.757443 0.732585 1.507420 +v 0.957443 0.769549 1.438265 +v 0.757443 0.769549 1.438265 +v 0.957443 0.792311 1.363227 +v 0.757443 0.792311 1.363227 +v 0.957443 0.799997 1.285191 +v 0.757443 0.799997 1.285191 +v 0.957443 0.792311 1.207155 +v 0.757443 0.792311 1.207155 +v 0.957443 0.769548 1.132118 +v 0.757443 0.769548 1.132118 +v 0.957443 0.732584 1.062963 +v 0.757443 0.732584 1.062963 +v 0.957443 0.682839 1.002349 +v 0.757443 0.682839 1.002349 +v 0.957443 0.622224 0.952604 +v 0.757443 0.622224 0.952604 +v 0.957443 0.553070 0.915640 +v 0.757443 0.553070 0.915640 +v 0.999943 0.751071 1.430611 +v 0.999943 0.772695 1.359326 +v 0.957443 0.478032 0.892877 +v 0.757443 0.478032 0.892877 +v 0.714943 0.399997 0.905192 +v 0.714943 0.474131 0.912493 +v 0.999943 0.188880 1.601150 +v 0.999943 0.131296 1.553892 +v 0.999943 0.198471 1.486717 +v 0.999943 0.241659 1.522161 +v 0.714943 0.751071 1.139771 +v 0.714943 0.715955 1.074075 +v 0.714943 0.636965 1.126854 +v 0.714943 0.663302 1.176126 +v 0.714943 0.048923 1.139772 +v 0.714943 0.084038 1.074075 +v 0.999943 0.474130 0.912493 +v 0.999943 0.399997 0.905192 +v 0.714943 0.325863 1.657890 +v 0.714943 0.254577 1.636266 +v 0.999943 0.084038 1.074075 +v 0.999943 0.048923 1.139772 +v 0.714943 0.779997 1.285191 +v 0.714943 0.772695 1.359326 +v 0.999943 0.254577 1.636266 +v 0.999943 0.325862 1.657890 +v 0.999943 0.779997 1.285191 +v 0.714943 0.027298 1.211057 +v 0.714943 0.399997 1.665192 +v 0.999943 0.027298 1.211057 +v 0.714943 0.772695 1.211057 +v 0.999943 0.399997 1.665192 +v 0.999943 0.772695 1.211057 +v 0.714943 0.019997 1.285192 +v 0.714943 0.474131 1.657890 +v 0.999943 0.019997 1.285192 +v 0.999943 0.474131 1.657890 +v 0.999943 0.751071 1.139771 +v 0.714943 0.027298 1.359326 +v 0.714943 0.545417 1.636266 +v 0.999943 0.027298 1.359326 +v 0.714943 0.325862 0.912493 +v 0.999943 0.545417 1.636266 +v 0.999943 0.325862 0.912493 +v 0.999943 0.715955 1.074075 +v 0.714943 0.048923 1.430611 +v 0.714943 0.611114 1.601150 +v 0.999943 0.048923 1.430611 +v 0.714943 0.668697 1.016491 +v 0.999943 0.611114 1.601150 +v 0.714943 0.254577 0.934117 +v 0.999943 0.668697 1.016491 +v 0.714943 0.084038 1.496308 +v 0.999943 0.254577 0.934117 +v 0.714943 0.668697 1.553892 +v 0.999943 0.084038 1.496308 +v 0.714943 0.611113 0.969233 +v 0.999943 0.668697 1.553892 +v 0.714943 0.188880 0.969233 +v 0.999943 0.611113 0.969233 +v 0.714943 0.131296 1.553892 +v 0.999943 0.188880 0.969233 +v 0.714943 0.715955 1.496308 +v 0.714943 0.545416 0.934117 +v 0.999943 0.715955 1.496308 +v 0.714943 0.131296 1.016491 +v 0.999943 0.545416 0.934117 +v 0.714943 0.188880 1.601150 +v 0.999943 0.131296 1.016491 +v 0.714943 0.751071 1.430611 +v 0.999943 0.455598 1.564715 +v 0.999943 0.399997 1.570192 +v 0.985693 0.399997 1.541692 +v 0.985693 0.450038 1.536763 +v 0.714943 0.120473 1.229591 +v 0.714943 0.136691 1.176127 +v 0.714943 0.290932 1.548497 +v 0.714943 0.241659 1.522161 +v 0.714943 0.636966 1.443529 +v 0.714943 0.601522 1.486717 +v 0.714943 0.344396 1.005668 +v 0.714943 0.399997 1.000192 +v 0.714943 0.601522 1.083666 +v 0.714943 0.114997 1.285192 +v 0.714943 0.344396 1.564715 +v 0.714943 0.663303 1.394256 +v 0.714943 0.290932 1.021886 +v 0.714943 0.558334 1.048223 +v 0.714943 0.120473 1.340792 +v 0.714943 0.399997 1.570192 +v 0.714943 0.679521 1.340792 +v 0.714943 0.241659 1.048223 +v 0.714943 0.509061 1.021886 +v 0.714943 0.136691 1.394256 +v 0.714943 0.455598 1.564715 +v 0.714943 0.684997 1.285191 +v 0.714943 0.198471 1.083666 +v 0.714943 0.455597 1.005668 +v 0.714943 0.163028 1.443529 +v 0.714943 0.509062 1.548497 +v 0.714943 0.679520 1.229591 +v 0.714943 0.163028 1.126854 +v 0.714943 0.198471 1.486717 +v 0.714943 0.558334 1.522161 +v 0.729193 0.301838 1.048217 +v 0.729193 0.349956 1.033620 +v 0.999943 0.558334 1.522161 +v 0.999943 0.601522 1.486717 +v 0.999943 0.663302 1.176126 +v 0.999943 0.636965 1.126854 +v 0.999943 0.136691 1.176127 +v 0.999943 0.120473 1.229591 +v 0.999943 0.290932 1.548497 +v 0.999943 0.636966 1.443529 +v 0.999943 0.399997 1.000192 +v 0.999943 0.344396 1.005668 +v 0.999943 0.601522 1.083666 +v 0.999943 0.114997 1.285192 +v 0.999943 0.344396 1.564715 +v 0.999943 0.663302 1.394256 +v 0.999943 0.290932 1.021886 +v 0.999943 0.558334 1.048223 +v 0.999943 0.120473 1.340792 +v 0.999943 0.679520 1.340792 +v 0.999943 0.241659 1.048223 +v 0.999943 0.509061 1.021886 +v 0.999943 0.136691 1.394256 +v 0.999943 0.684997 1.285191 +v 0.999943 0.198471 1.083666 +v 0.999943 0.455597 1.005668 +v 0.999943 0.163028 1.443529 +v 0.999943 0.509062 1.548497 +v 0.999943 0.679520 1.229591 +v 0.999943 0.163028 1.126854 +v 0.729193 0.542500 1.071919 +v 0.729193 0.581369 1.103818 +v 0.729193 0.257493 1.071920 +v 0.985693 0.498155 1.522167 +v 0.729193 0.498155 1.048216 +v 0.729193 0.218624 1.103819 +v 0.985693 0.542501 1.498464 +v 0.729193 0.450037 1.033620 +v 0.729193 0.186725 1.142688 +v 0.985693 0.581370 1.466564 +v 0.729193 0.399997 1.028692 +v 0.729193 0.163022 1.187033 +v 0.985693 0.613269 1.427695 +v 0.729193 0.148425 1.235151 +v 0.985693 0.636972 1.383350 +v 0.729193 0.143497 1.285192 +v 0.985693 0.651568 1.335232 +v 0.729193 0.148425 1.335232 +v 0.985693 0.656497 1.285191 +v 0.729193 0.163022 1.383350 +v 0.985693 0.651568 1.235151 +v 0.729193 0.186725 1.427695 +v 0.985693 0.636972 1.187033 +v 0.729193 0.218624 1.466564 +v 0.985693 0.613268 1.142688 +v 0.985693 0.399997 1.028692 +v 0.985693 0.349956 1.033620 +v 0.729193 0.257493 1.498464 +v 0.985693 0.581369 1.103818 +v 0.985693 0.301838 1.048217 +v 0.729193 0.301839 1.522167 +v 0.985693 0.542500 1.071919 +v 0.985693 0.257493 1.071920 +v 0.729193 0.349956 1.536763 +v 0.985693 0.498155 1.048216 +v 0.985693 0.218624 1.103819 +v 0.729193 0.399997 1.541692 +v 0.985693 0.450037 1.033620 +v 0.985693 0.186725 1.142688 +v 0.729193 0.450038 1.536763 +v 0.985693 0.163022 1.187033 +v 0.729193 0.498155 1.522167 +v 0.985693 0.148425 1.235151 +v 0.729193 0.542501 1.498464 +v 0.985693 0.143497 1.285192 +v 0.729193 0.581370 1.466564 +v 0.985693 0.148425 1.335232 +v 0.729193 0.613269 1.427695 +v 0.985693 0.163022 1.383350 +v 0.729193 0.636972 1.383350 +v 0.985693 0.186725 1.427695 +v 0.729193 0.651568 1.335232 +v 0.985693 0.218624 1.466564 +v 0.729193 0.656497 1.285191 +v 0.985693 0.257493 1.498464 +v 0.729193 0.651568 1.235151 +v 0.985693 0.301838 1.522167 +v 0.729193 0.636972 1.187033 +v 0.985693 0.349956 1.536763 +v 0.729193 0.613268 1.142688 +v -0.970807 0.399997 1.028692 +v -0.970807 0.349956 1.033620 +v -0.970807 0.301839 1.048217 +v -0.970807 0.257493 1.071920 +v -0.970807 0.218624 1.103819 +v -0.970807 0.186725 1.142688 +v -0.970807 0.163022 1.187033 +v -0.970807 0.148425 1.235151 +v -0.970807 0.143497 1.285192 +v -0.970807 0.148425 1.335232 +v -0.970807 0.163022 1.383350 +v -0.970807 0.186725 1.427695 +v -0.970807 0.218624 1.466564 +v -0.970807 0.257493 1.498464 +v -0.970807 0.301839 1.522167 +v -0.970807 0.349956 1.536763 +v -0.970807 0.399997 1.541692 +v -0.970807 0.450037 1.536763 +v -0.970807 0.498155 1.522167 +v -0.970807 0.542501 1.498464 +v -0.970807 0.581370 1.466564 +v -0.970807 0.613269 1.427695 +v -0.970807 0.636972 1.383350 +v -0.970807 0.651568 1.335232 +v -0.970807 0.656497 1.285191 +v -0.970807 0.651568 1.235151 +v -0.970807 0.636972 1.187033 +v -0.970807 0.613268 1.142688 +v -0.970807 0.581369 1.103818 +v -0.970807 0.542500 1.071919 +v -0.970807 0.498155 1.048216 +v -0.970807 0.450037 1.033620 +v -0.714307 0.349956 1.033620 +v -0.714307 0.399997 1.028692 +v -0.714307 0.450037 1.033620 +v -0.714307 0.498155 1.048216 +v -0.714307 0.542500 1.071919 +v -0.714307 0.581369 1.103818 +v -0.714307 0.613268 1.142688 +v -0.714307 0.636972 1.187033 +v -0.714307 0.651568 1.235151 +v -0.714307 0.656497 1.285191 +v -0.714307 0.651568 1.335232 +v -0.714307 0.636972 1.383350 +v -0.714307 0.613269 1.427695 +v -0.714307 0.581370 1.466564 +v -0.714307 0.542501 1.498464 +v -0.714307 0.498155 1.522167 +v -0.714307 0.450037 1.536763 +v -0.714307 0.399997 1.541692 +v -0.714307 0.349956 1.536763 +v -0.714307 0.301838 1.522167 +v -0.714307 0.257493 1.498464 +v -0.714307 0.218624 1.466564 +v -0.714307 0.186725 1.427695 +v -0.714307 0.163022 1.383350 +v -0.714307 0.148425 1.335232 +v -0.714307 0.143497 1.285192 +v -0.714307 0.148425 1.235151 +v -0.714307 0.163022 1.187033 +v -0.714307 0.186725 1.142688 +v -0.714307 0.218624 1.103819 +v -0.714307 0.257493 1.071920 +v -0.714307 0.301838 1.048217 +v -0.970807 0.399997 -1.531308 +v -0.970807 0.349956 -1.526380 +v -0.970807 0.301839 -1.511783 +v -0.970807 0.257493 -1.488080 +v -0.970807 0.218624 -1.456181 +v -0.970807 0.186725 -1.417312 +v -0.970807 0.163022 -1.372967 +v -0.970807 0.148425 -1.324849 +v -0.970807 0.143497 -1.274808 +v -0.970807 0.148425 -1.224768 +v -0.970807 0.163022 -1.176650 +v -0.970807 0.186725 -1.132305 +v -0.970807 0.218624 -1.093436 +v -0.970807 0.257493 -1.061536 +v -0.970807 0.301839 -1.037833 +v -0.970807 0.349956 -1.023237 +v -0.970807 0.399997 -1.018308 +v -0.970807 0.450037 -1.023237 +v -0.970807 0.498155 -1.037833 +v -0.970807 0.542501 -1.061536 +v -0.970807 0.581370 -1.093436 +v -0.970807 0.613269 -1.132305 +v -0.970807 0.636972 -1.176650 +v -0.970807 0.651568 -1.224768 +v -0.970807 0.656497 -1.274809 +v -0.970807 0.651568 -1.324849 +v -0.970807 0.636972 -1.372967 +v -0.970807 0.613268 -1.417312 +v -0.970807 0.581369 -1.456182 +v -0.970807 0.542500 -1.488081 +v -0.970807 0.498155 -1.511784 +v -0.970807 0.450037 -1.526380 +v -0.714307 0.349956 -1.526380 +v -0.714307 0.399997 -1.531308 +v -0.714307 0.450037 -1.526380 +v -0.714307 0.498155 -1.511784 +v -0.714307 0.542500 -1.488081 +v -0.714307 0.581369 -1.456182 +v -0.714307 0.613268 -1.417312 +v -0.714307 0.636972 -1.372967 +v -0.714307 0.651568 -1.324849 +v -0.714307 0.656497 -1.274809 +v -0.714307 0.651568 -1.224768 +v -0.714307 0.636972 -1.176650 +v -0.714307 0.613269 -1.132305 +v -0.714307 0.581370 -1.093436 +v -0.714307 0.542501 -1.061536 +v -0.714307 0.498155 -1.037833 +v -0.714307 0.450037 -1.023237 +v -0.714307 0.399997 -1.018308 +v -0.714307 0.349956 -1.023237 +v -0.714307 0.301838 -1.037833 +v -0.714307 0.257493 -1.061536 +v -0.714307 0.218624 -1.093436 +v -0.714307 0.186725 -1.132305 +v -0.714307 0.163022 -1.176650 +v -0.714307 0.148425 -1.224768 +v -0.714307 0.143497 -1.274808 +v -0.714307 0.148425 -1.324849 +v -0.714307 0.163022 -1.372967 +v -0.714307 0.186725 -1.417312 +v -0.714307 0.218624 -1.456181 +v -0.714307 0.257493 -1.488080 +v -0.714307 0.301838 -1.511783 +v 0.729193 0.399997 -1.531308 +v 0.729193 0.349956 -1.526380 +v 0.729193 0.301839 -1.511783 +v 0.729193 0.257493 -1.488080 +v 0.729193 0.218624 -1.456181 +v 0.729193 0.186725 -1.417312 +v 0.729193 0.163022 -1.372967 +v 0.729193 0.148425 -1.324849 +v 0.729193 0.143497 -1.274808 +v 0.729193 0.148425 -1.224768 +v 0.729193 0.163022 -1.176650 +v 0.729193 0.186725 -1.132305 +v 0.729193 0.218624 -1.093436 +v 0.729193 0.257493 -1.061536 +v 0.729193 0.301839 -1.037833 +v 0.729193 0.349956 -1.023237 +v 0.729193 0.399997 -1.018308 +v 0.729193 0.450037 -1.023237 +v 0.729193 0.498155 -1.037833 +v 0.729193 0.542501 -1.061536 +v 0.729193 0.581370 -1.093436 +v 0.729193 0.613269 -1.132305 +v 0.729193 0.636972 -1.176650 +v 0.729193 0.651568 -1.224768 +v 0.729193 0.656497 -1.274809 +v 0.729193 0.651568 -1.324849 +v 0.729193 0.636972 -1.372967 +v 0.729193 0.613268 -1.417312 +v 0.729193 0.581369 -1.456182 +v 0.729193 0.542500 -1.488081 +v 0.729193 0.498155 -1.511784 +v 0.729193 0.450037 -1.526380 +v 0.985693 0.349956 -1.526380 +v 0.985693 0.399997 -1.531308 +v 0.985693 0.450037 -1.526380 +v 0.985693 0.498155 -1.511784 +v 0.985693 0.542500 -1.488081 +v 0.985693 0.581369 -1.456182 +v 0.985693 0.613268 -1.417312 +v 0.985693 0.636972 -1.372967 +v 0.985693 0.651568 -1.324849 +v 0.985693 0.656497 -1.274809 +v 0.985693 0.651568 -1.224768 +v 0.985693 0.636972 -1.176650 +v 0.985693 0.613269 -1.132305 +v 0.985693 0.581370 -1.093436 +v 0.985693 0.542501 -1.061536 +v 0.985693 0.498155 -1.037833 +v 0.985693 0.450037 -1.023237 +v 0.985693 0.399997 -1.018308 +v 0.985693 0.349956 -1.023237 +v 0.985693 0.301838 -1.037833 +v 0.985693 0.257493 -1.061536 +v 0.985693 0.218624 -1.093436 +v 0.985693 0.186725 -1.132305 +v 0.985693 0.163022 -1.176650 +v 0.985693 0.148425 -1.224768 +v 0.985693 0.143497 -1.274808 +v 0.985693 0.148425 -1.324849 +v 0.985693 0.163022 -1.372967 +v 0.985693 0.186725 -1.417312 +v 0.985693 0.218624 -1.456181 +v 0.985693 0.257493 -1.488080 +v 0.985693 0.301838 -1.511783 +v 0.729193 0.399997 1.028692 +v 0.729193 0.349956 1.033620 +v 0.729193 0.301839 1.048217 +v 0.729193 0.257493 1.071920 +v 0.729193 0.218624 1.103819 +v 0.729193 0.186725 1.142688 +v 0.729193 0.163022 1.187033 +v 0.729193 0.148425 1.235151 +v 0.729193 0.143497 1.285192 +v 0.729193 0.148425 1.335232 +v 0.729193 0.163022 1.383350 +v 0.729193 0.186725 1.427695 +v 0.729193 0.218624 1.466564 +v 0.729193 0.257493 1.498464 +v 0.729193 0.301839 1.522167 +v 0.729193 0.349956 1.536763 +v 0.729193 0.399997 1.541692 +v 0.729193 0.450037 1.536763 +v 0.729193 0.498155 1.522167 +v 0.729193 0.542501 1.498464 +v 0.729193 0.581370 1.466564 +v 0.729193 0.613269 1.427695 +v 0.729193 0.636972 1.383350 +v 0.729193 0.651568 1.335232 +v 0.729193 0.656497 1.285191 +v 0.729193 0.651568 1.235151 +v 0.729193 0.636972 1.187033 +v 0.729193 0.613268 1.142688 +v 0.729193 0.581369 1.103818 +v 0.729193 0.542500 1.071919 +v 0.729193 0.498155 1.048216 +v 0.729193 0.450037 1.033620 +v 0.985693 0.349956 1.033620 +v 0.985693 0.399997 1.028692 +v 0.985693 0.450037 1.033620 +v 0.985693 0.498155 1.048216 +v 0.985693 0.542500 1.071919 +v 0.985693 0.581369 1.103818 +v 0.985693 0.613268 1.142688 +v 0.985693 0.636972 1.187033 +v 0.985693 0.651568 1.235151 +v 0.985693 0.656497 1.285191 +v 0.985693 0.651568 1.335232 +v 0.985693 0.636972 1.383350 +v 0.985693 0.613269 1.427695 +v 0.985693 0.581370 1.466564 +v 0.985693 0.542501 1.498464 +v 0.985693 0.498155 1.522167 +v 0.985693 0.450037 1.536763 +v 0.985693 0.399997 1.541692 +v 0.985693 0.349956 1.536763 +v 0.985693 0.301838 1.522167 +v 0.985693 0.257493 1.498464 +v 0.985693 0.218624 1.466564 +v 0.985693 0.186725 1.427695 +v 0.985693 0.163022 1.383350 +v 0.985693 0.148425 1.335232 +v 0.985693 0.143497 1.285192 +v 0.985693 0.148425 1.235151 +v 0.985693 0.163022 1.187033 +v 0.985693 0.186725 1.142688 +v 0.985693 0.218624 1.103819 +v 0.985693 0.257493 1.071920 +v 0.985693 0.301838 1.048217 +vn 0.0009 -0.1173 -0.9931 +vn -0.8742 0.4855 0.0066 +vn -0.1696 0.8333 -0.5262 +vn 0.0233 0.5810 -0.8136 +vn 0.8755 0.4831 -0.0065 +vn -0.8784 0.1701 0.4466 +vn -0.7707 0.6371 0.0129 +vn 0.8609 0.5087 0.0069 +vn -0.8755 0.4831 -0.0065 +vn -0.0602 0.6503 -0.7573 +vn 1.0000 0.0000 0.0000 +vn -1.0000 -0.0000 0.0000 +vn 0.4572 0.1054 0.8831 +vn 0.8795 0.1694 0.4447 +vn -0.0990 0.8631 0.4951 +vn 0.0000 0.0000 -1.0000 +vn -0.7057 0.2425 0.6657 +vn 0.0723 -0.3648 0.9283 +vn 0.0724 -0.3648 0.9283 +vn 0.0990 0.8631 0.4952 +vn -0.0723 -0.3647 0.9283 +vn -0.0724 -0.3647 0.9283 +vn -0.4572 0.1057 0.8830 +vn 0.7056 0.2423 0.6659 +vn 0.6247 0.7809 -0.0000 +vn 0.6209 0.7839 -0.0003 +vn 0.7711 0.6366 0.0129 +vn -0.6225 0.7827 -0.0002 +vn -0.8641 0.5029 0.0183 +vn -0.8610 0.5086 0.0069 +vn -0.8699 0.4933 0.0000 +vn -0.8625 0.5061 -0.0068 +vn 0.8641 0.5029 0.0183 +vn 0.8742 0.4855 0.0065 +vn 0.8699 0.4933 0.0000 +vn 0.8625 0.5061 -0.0068 +vn 0.8680 0.4962 -0.0180 +vn -0.8680 0.4962 -0.0180 +vn 0.1696 0.8332 -0.5263 +vn -0.0233 0.5810 -0.8136 +vn 0.0602 0.6503 -0.7573 +vn -0.0009 -0.1174 -0.9931 +vn -0.0055 0.1814 -0.9834 +vn 0.0055 0.1814 -0.9834 +vn 0.0009 -0.1174 -0.9931 +vn -0.1696 0.8332 -0.5262 +vn -0.0990 0.8631 0.4952 +vn -0.0723 -0.3648 0.9283 +vn -0.0724 -0.3648 0.9283 +vn -0.4572 0.1057 0.8831 +vn 0.7711 0.6365 0.0129 +vn -0.6225 0.7826 -0.0002 +vn -0.8609 0.5086 0.0069 +vn 0.1696 0.8333 -0.5262 +vn -0.0009 -0.1173 -0.9931 +vn 0.3896 0.6057 0.6938 +vn 0.0903 0.9630 0.2539 +vn 0.2288 0.8641 0.4483 +vn 0.1863 0.7699 -0.6103 +vn -0.0000 1.0000 0.0010 +vn 0.0969 0.9953 0.0006 +vn -0.0000 0.9260 0.3776 +vn -0.1213 0.6496 0.7505 +vn 0.0000 0.7179 0.6961 +vn -0.1386 0.8799 0.4545 +vn -0.1246 0.6765 0.7258 +vn 0.0515 0.6719 -0.7388 +vn 0.0000 0.9124 -0.4093 +vn 0.1147 0.9493 -0.2927 +vn -0.0515 0.6719 -0.7389 +vn -0.0000 0.6724 -0.7401 +vn -0.2288 0.8641 0.4483 +vn -0.0000 0.9416 0.3368 +vn -0.0903 0.9630 0.2539 +vn -0.0969 0.9953 0.0006 +vn -0.4799 0.8342 0.2718 +vn 0.1386 0.8799 0.4545 +vn 0.4799 0.8342 0.2718 +vn 0.2703 0.6466 -0.7133 +vn 0.5082 0.8498 -0.1397 +vn 0.1213 0.6496 0.7505 +vn -0.0000 0.9990 0.0444 +vn -0.3890 0.6058 0.6940 +vn -0.5576 0.8190 0.1351 +vn -0.2707 0.6449 -0.7147 +vn -0.1147 0.9493 -0.2927 +vn 0.0869 0.8406 0.5346 +vn 0.5579 0.8188 0.1351 +vn 0.0000 0.8505 -0.5260 +vn 0.1718 0.3354 -0.9263 +vn 0.0000 0.4963 -0.8681 +vn -0.0869 0.8406 0.5346 +vn 0.0823 0.8532 0.5150 +vn -0.0000 0.8155 0.5787 +vn -0.0823 0.8532 0.5150 +vn 0.1246 0.6765 0.7258 +vn -0.4685 0.7982 -0.3787 +vn -0.5635 0.8261 -0.0005 +vn 0.4685 0.7982 -0.3787 +vn 0.5635 0.8261 -0.0005 +vn -0.1863 0.7699 -0.6103 +vn 0.1376 0.3895 -0.9107 +vn -0.1376 0.3895 -0.9107 +vn -0.1718 0.3354 -0.9263 +vn -0.0000 0.2849 0.9586 +vn 0.0293 0.2844 0.9583 +vn -0.0000 0.2848 0.9586 +vn -0.0291 0.2847 0.9582 +vn -0.5070 0.8505 -0.1399 +vn 0.0292 0.2846 0.9582 +vn -0.0291 0.2846 0.9582 +vn 0.9571 1.0336 -0.4438 +vn 3.5631 0.7046 1.4897 +vn 0.1726 3.5631 0.9571 +vn -0.8700 0.4930 0.0000 +vn 3.3126 -0.6865 1.4957 +vn 0.1675 3.3126 0.9571 +vn 1.4957 -0.5434 nan +vn -0.8698 0.4933 -0.0000 +vn -0.8698 0.4935 0.0000 +vn 1.0727 -1.5953 3725669300901539823484928.0000 +vn 0.0074 1.0863 -1.6203 +vn 67404402845334701604864.0000 0.4446 1.0726 +vn 0.8698 0.4933 0.0000 +vn -0.1731 0.8376 -0.5181 +vn 0.1731 0.8376 -0.5181 +vn 0.0623 0.6516 -0.7560 +vn -0.0623 0.6515 -0.7560 +vn -0.7426 0.4780 -1.6671 +vn 19941021249234820537566363648.0000 -0.9426 0.1778 +vn -0.9589 1858415744.0000 -0.7426 +vn -0.8700 0.4931 0.0000 +vn 77148872.0000 -0.9851 0.6370 +vn -1.6548 13618049967703234188083724288.0000 -0.9851 +vn 0.7160 -1.4859 830119958784673122064135866023936.0000 +vn -0.8696 0.4937 0.0000 +vn -0.8697 0.4936 0.0000 +vn -0.8698 0.4934 -0.0000 +vn 97986696112638874665133015040.0000 -0.9851 0.2417 +vn -1.5908 883779855820950118251987206144.0000 -0.9851 +vn 0.0489 -1.1294 28147.1738 +vn -0.0873 0.7266 0.6815 +vn -0.0000 0.7357 0.6773 +vn 0.0806 0.8140 0.5752 +vn 0.0000 0.7989 0.6014 +vn 0.1154 0.8338 0.5399 +vn -0.1154 0.8338 0.5399 +vn -0.1377 0.5109 -0.8486 +vn 0.1377 0.5109 -0.8486 +vn 0.0000 0.5384 -0.8427 +vn 0.1943 0.4245 -0.8843 +vn -0.1943 0.4245 -0.8843 +vn -0.1561 0.7011 0.6958 +vn 0.1561 0.7011 0.6958 +vn 0.0872 0.7266 0.6815 +vn -0.0805 0.8140 0.5752 +vn -0.8742 0.4855 0.0065 +vn 0.0000 0.0000 1.0000 +vn -0.8755 0.4831 -0.0064 +vn -0.8624 0.5061 -0.0063 +vn -0.8625 0.5061 -0.0065 +vn -0.8642 0.5029 0.0183 +vn -0.8609 0.5088 0.0070 +vn 0.8742 0.4855 0.0066 +vn 0.8642 0.5029 0.0183 +vn 0.8610 0.5086 0.0068 +vn 0.8681 0.4961 -0.0180 +vn -0.8681 0.4961 -0.0180 +vn -0.8742 0.4855 0.0064 +vn -0.8625 0.5060 -0.0064 +vn -0.8625 0.5061 -0.0067 +vn -0.8609 0.5087 0.0069 +vn 0.8755 0.4831 -0.0066 +vn -0.8707 0.4918 0.0040 +vn -0.8647 0.5022 0.0105 +vn -0.8686 0.4953 0.0115 +vn 0.8707 0.4918 0.0040 +vn 0.8647 0.5022 0.0105 +vn 0.8658 0.5004 0.0038 +vn -0.8718 0.4898 -0.0039 +vn -0.8708 0.4915 -0.0112 +vn -0.8686 0.4953 -0.0122 +vn 0.8718 0.4898 -0.0038 +vn 0.8669 0.4984 -0.0109 +vn 0.8708 0.4915 -0.0112 +vn 0.0000 0.6171 -0.7869 +vn 0.1231 0.6500 -0.7499 +vn -0.1231 0.6500 -0.7499 +vn 0.1294 0.7164 -0.6856 +vn -0.8658 0.5004 0.0038 +vn 0.8686 0.4953 0.0115 +vn -0.8669 0.4984 -0.0109 +vn -0.8668 0.4986 -0.0038 +vn 0.8668 0.4986 -0.0038 +vn -0.1294 0.7164 -0.6856 +vn -0.2657 0.5314 -0.8044 +vn -0.4472 0.8944 -0.0000 +vn 0.2262 0.4524 0.8626 +vn 0.2262 0.4524 -0.8626 +vn 0.4472 0.8944 0.0000 +vn 0.2657 0.5314 -0.8044 +vn -0.8457 -0.4991 0.1889 +vn 0.8457 -0.4991 0.1889 +vn 0.8543 -0.5042 0.1260 +vn 0.8543 -0.5042 -0.1260 +vn -0.8460 -0.4993 -0.1872 +vn -0.9712 -0.0625 -0.2298 +vn -0.4592 -0.8852 -0.0749 +vn -0.7994 0.1263 -0.5874 +vn 0.9541 -0.1046 0.2808 +vn 0.4524 -0.8891 0.0698 +vn 0.8218 0.1474 0.5503 +vn 0.9967 -0.0814 -0.0000 +vn 0.4606 -0.8840 0.0798 +vn 0.7994 0.1263 0.5873 +vn -0.9967 -0.0814 0.0000 +vn -0.4588 -0.8854 -0.0750 +vn -0.8201 0.1520 -0.5517 +vn 0.0000 0.9933 0.1159 +vn 0.0818 0.7543 0.6514 +vn 0.1995 0.9232 0.3284 +vn 0.4459 -0.4201 -0.7904 +vn 0.9712 -0.0625 -0.2298 +vn 0.6153 -0.7471 -0.2514 +vn 0.0000 -0.5032 -0.8642 +vn 0.0000 -0.9669 -0.2553 +vn -0.4459 -0.4201 -0.7904 +vn 0.4231 0.6620 0.6187 +vn 0.7952 0.3543 0.4920 +vn -0.0000 -0.9767 0.2145 +vn 0.0908 -0.6414 0.7618 +vn 0.0000 -0.6582 0.7528 +vn -0.0342 -0.9689 0.2449 +vn -0.4521 -0.5446 0.7064 +vn -0.6271 -0.7330 0.2635 +vn -0.6153 -0.7471 -0.2514 +vn 0.5444 -0.1298 0.8287 +vn 0.4521 -0.5445 0.7065 +vn 0.1232 -0.1518 0.9807 +vn -0.5444 -0.1299 0.8287 +vn -0.0907 -0.6414 0.7618 +vn -0.1232 -0.1518 0.9807 +vn -0.0000 -0.1537 0.9881 +vn 0.0342 -0.9689 0.2449 +vn 0.9546 0.0238 0.2968 +vn 0.5430 0.1063 0.8330 +vn -0.1995 0.9232 0.3284 +vn -0.4231 0.6620 0.6187 +vn -0.0818 0.7544 0.6513 +vn -0.9541 -0.1046 0.2808 +vn 0.0000 0.7666 0.6421 +vn -0.9546 0.0238 0.2968 +vn -0.7952 0.3542 0.4921 +vn -0.5430 0.1063 0.8330 +vn 0.6271 -0.7330 0.2635 +vn -0.1232 0.1518 0.9807 +vn -0.0000 0.1537 0.9881 +vn 0.1232 0.1518 0.9807 +vn -0.4515 0.5373 -0.7124 +vn -0.9189 0.1649 -0.3584 +vn 0.0935 0.6220 -0.7774 +vn 0.0000 0.5900 -0.8074 +vn -0.0000 0.6611 -0.7503 +vn 0.4515 0.5373 -0.7124 +vn -0.0935 0.6220 -0.7774 +vn 0.9189 0.1649 -0.3584 +vn -0.4524 -0.8891 0.0698 +vn 0.8201 0.1520 -0.5517 +vn 0.6472 -0.7623 0.0000 +vn -0.9163 0.3625 0.1702 +vn 0.7994 0.1263 -0.5873 +vn -0.7994 0.1263 0.5874 +vn -0.6472 -0.7623 -0.0000 +vn 0.9163 0.3625 0.1702 +vn -0.7456 0.5352 0.3971 +vn -0.7621 -0.1214 0.6359 +vn -0.9223 0.3429 -0.1782 +vn -0.9223 0.3429 0.1782 +vn 0.7580 0.5485 0.3528 +vn -0.8218 0.1475 0.5503 +vn -0.7580 0.5485 0.3528 +vn -0.7660 -0.0929 0.6361 +vn -0.7456 0.5352 -0.3971 +vn -0.9894 -0.1360 -0.0515 +vn -0.7660 -0.0929 -0.6361 +vn 0.9894 -0.1360 -0.0515 +vn 0.7660 -0.0929 0.6361 +vn 0.7660 -0.0929 -0.6361 +vn 0.4588 -0.8854 -0.0750 +vn 0.7510 -0.1062 -0.6517 +vn 0.4606 -0.8840 -0.0798 +vn 0.7510 -0.1062 0.6517 +vn -0.7621 -0.1214 -0.6359 +vn -0.9887 -0.1403 0.0526 +vn -0.9887 -0.1403 -0.0526 +vn -0.4592 -0.8852 0.0750 +vn 0.1477 0.2568 0.9551 +vn -0.1478 0.2569 0.9551 +vn 0.1478 0.2568 0.9551 +vn 0.0000 -1.0000 0.0000 +vn 0.2153 -0.0000 -0.9765 +vn -0.2153 -0.1905 -0.9578 +vn -0.2153 -0.0000 -0.9765 +vn 0.2153 -0.1905 -0.9578 +vn -0.2153 -0.3737 -0.9022 +vn 0.2153 -0.3737 -0.9022 +vn -0.2153 -0.5425 -0.8120 +vn 0.2153 -0.5425 -0.8120 +vn -0.2153 -0.6905 -0.6905 +vn 0.2153 -0.6905 -0.6905 +vn -0.2153 -0.8120 -0.5425 +vn 0.2153 -0.8120 -0.5425 +vn -0.2153 -0.9022 -0.3737 +vn 0.2153 -0.9022 -0.3737 +vn -0.2153 -0.9578 -0.1905 +vn 0.2153 -0.9578 -0.1905 +vn -0.2153 -0.9765 0.0000 +vn 0.2153 -0.9765 0.0000 +vn -0.2153 -0.9578 0.1905 +vn 0.2153 -0.9578 0.1905 +vn -0.2153 -0.9022 0.3737 +vn 0.2153 -0.9022 0.3737 +vn -0.2153 -0.8120 0.5425 +vn 0.2153 -0.8120 0.5425 +vn -0.2153 -0.6905 0.6905 +vn 0.2153 -0.6905 0.6905 +vn -0.2153 -0.5425 0.8120 +vn 0.2153 -0.5425 0.8120 +vn -0.2153 -0.3737 0.9022 +vn 0.2153 -0.3737 0.9022 +vn -0.2153 -0.1905 0.9578 +vn 0.2153 -0.1905 0.9578 +vn -0.2153 0.0000 0.9765 +vn 0.2153 0.0000 0.9765 +vn -0.2153 0.1905 0.9578 +vn 0.2153 0.1905 0.9578 +vn -0.2153 0.3737 0.9022 +vn 0.2153 0.3737 0.9022 +vn -0.2153 0.5425 0.8120 +vn 0.2153 0.5425 0.8120 +vn -0.2153 0.6905 0.6905 +vn 0.2153 0.6905 0.6905 +vn -0.2153 0.8120 0.5425 +vn 0.2153 0.8120 0.5425 +vn -0.2153 0.9022 0.3737 +vn 0.2153 0.9022 0.3737 +vn -0.2153 0.9578 0.1905 +vn 0.2153 0.9578 0.1905 +vn -0.2153 0.9765 0.0000 +vn 0.2153 0.9765 -0.0000 +vn -0.2153 0.9578 -0.1905 +vn 0.2153 0.9578 -0.1905 +vn -0.2153 0.9022 -0.3737 +vn 0.2153 0.9022 -0.3737 +vn -0.2153 0.8120 -0.5425 +vn 0.2153 0.8120 -0.5425 +vn -0.2153 0.6905 -0.6905 +vn 0.2153 0.6905 -0.6905 +vn -0.2153 0.5425 -0.8120 +vn 0.2153 0.5425 -0.8120 +vn -0.2153 0.3737 -0.9022 +vn 0.8293 0.5481 0.1090 +vn 0.2153 0.3737 -0.9022 +vn -0.2153 0.1905 -0.9578 +vn 0.2153 0.1905 -0.9578 +vn -0.8293 -0.0000 -0.5588 +vn -0.8293 0.1090 -0.5481 +vn 0.8293 -0.3952 0.3952 +vn 0.9764 0.1200 -0.1796 +vn 0.8293 -0.3105 0.4647 +vn -0.8293 0.4647 -0.3105 +vn -0.9764 -0.1996 0.0827 +vn -0.8293 0.5163 -0.2139 +vn -0.8293 -0.4647 -0.3105 +vn 0.8293 0.1090 -0.5481 +vn 0.8293 -0.0000 -0.5588 +vn -0.8293 -0.2139 0.5163 +vn 0.8293 -0.4647 -0.3105 +vn 0.8293 -0.5163 -0.2139 +vn -0.8293 0.5589 0.0000 +vn -0.8293 0.5481 0.1090 +vn 0.8293 -0.1090 0.5481 +vn 0.8293 0.5589 -0.0000 +vn -0.8293 -0.5163 -0.2139 +vn -0.8293 -0.1090 0.5481 +vn 0.8293 -0.5481 -0.1090 +vn 0.8293 0.0000 0.5589 +vn 0.8293 0.5481 -0.1090 +vn -0.8293 -0.5481 -0.1090 +vn -0.8293 0.1090 0.5481 +vn -0.8293 0.0000 0.5589 +vn 0.8293 -0.5588 0.0000 +vn -0.8293 0.5481 -0.1090 +vn 0.8293 0.1090 0.5481 +vn 0.8293 0.5163 -0.2139 +vn -0.8293 -0.5481 0.1090 +vn -0.8293 -0.5588 0.0000 +vn 0.8293 -0.5481 0.1090 +vn 0.8293 0.2139 0.5163 +vn 0.8293 -0.1090 -0.5481 +vn 0.8293 0.4647 -0.3105 +vn -0.8293 -0.5163 0.2139 +vn -0.8293 0.2139 0.5163 +vn 0.8293 -0.5163 0.2139 +vn -0.8293 0.3952 -0.3952 +vn 0.8293 0.3105 0.4647 +vn -0.8293 -0.2139 -0.5163 +vn -0.8293 -0.1090 -0.5481 +vn 0.8293 0.3952 -0.3952 +vn -0.8293 -0.4647 0.3105 +vn 0.8293 -0.2139 -0.5163 +vn -0.8293 0.3952 0.3952 +vn -0.8293 0.3105 0.4647 +vn 0.8293 -0.4647 0.3105 +vn -0.8293 0.3105 -0.4647 +vn 0.8293 0.3952 0.3952 +vn -0.8293 -0.3105 -0.4647 +vn 0.8293 0.3105 -0.4647 +vn -0.8293 -0.3952 0.3952 +vn 0.8293 -0.3105 -0.4647 +vn -0.8293 0.4647 0.3105 +vn 0.8293 0.4647 0.3105 +vn 0.8293 0.2139 -0.5163 +vn -0.8293 -0.3105 0.4647 +vn 0.8293 -0.3952 -0.3952 +vn -0.8293 0.2139 -0.5163 +vn 0.8293 0.5163 0.2139 +vn -0.8293 -0.3952 -0.3952 +vn -0.8293 0.5163 0.2139 +vn 0.8293 -0.2139 0.5163 +vn 0.9764 -0.0422 -0.2119 +vn 0.8944 -0.0000 -0.4472 +vn 0.8944 -0.0872 -0.4386 +vn -0.9764 0.2119 0.0422 +vn -0.9764 0.1996 0.0827 +vn -0.9764 0.1200 -0.1796 +vn -0.9764 -0.1797 -0.1200 +vn -0.9764 -0.1528 -0.1528 +vn -0.9764 0.0422 0.2119 +vn -0.9764 -0.0000 0.2161 +vn -0.9764 -0.1797 0.1200 +vn -0.9764 0.0422 -0.2119 +vn -0.9764 0.0827 -0.1996 +vn -0.9764 0.0827 0.1996 +vn -0.9764 -0.1528 0.1528 +vn -0.9764 0.2119 -0.0422 +vn -0.9764 0.2161 -0.0000 +vn -0.9764 0.0000 -0.2161 +vn -0.9764 -0.1996 -0.0827 +vn -0.9764 -0.0827 0.1996 +vn -0.9764 -0.1200 0.1797 +vn -0.9764 -0.2119 -0.0422 +vn -0.9764 0.1528 0.1528 +vn -0.9764 0.1200 0.1797 +vn -0.9764 0.1797 -0.1200 +vn -0.9764 0.1996 -0.0827 +vn -0.9764 -0.0422 -0.2119 +vn -0.9764 -0.2161 0.0000 +vn -0.9764 0.1797 0.1200 +vn -0.9764 -0.0422 0.2119 +vn -0.9764 0.1528 -0.1528 +vn -0.9764 -0.1200 -0.1797 +vn -0.9764 -0.0827 -0.1996 +vn -0.9764 -0.2119 0.0422 +vn -0.8944 0.0872 0.4386 +vn 0.9764 -0.1200 -0.1797 +vn 0.9764 -0.1528 -0.1528 +vn 0.9764 -0.1996 0.0827 +vn 0.9764 -0.1797 0.1200 +vn 0.9764 0.2119 0.0422 +vn 0.9764 0.0827 -0.1996 +vn 0.9764 -0.1797 -0.1200 +vn 0.9764 0.0422 0.2119 +vn 0.9764 -0.1528 0.1528 +vn 0.9764 0.2161 -0.0000 +vn 0.9764 0.0422 -0.2119 +vn 0.9764 -0.1996 -0.0827 +vn 0.9764 0.0827 0.1996 +vn 0.9764 -0.1200 0.1797 +vn 0.9764 0.2119 -0.0422 +vn 0.9764 -0.0000 -0.2161 +vn 0.9764 -0.2119 -0.0422 +vn 0.9764 0.1200 0.1797 +vn 0.9764 -0.0827 0.1996 +vn 0.9764 0.1996 -0.0827 +vn 0.9764 -0.2161 0.0000 +vn 0.9764 0.1528 0.1528 +vn 0.9764 -0.0422 0.2119 +vn 0.9764 0.1797 -0.1200 +vn 0.9764 -0.0827 -0.1996 +vn 0.9764 -0.2119 0.0422 +vn 0.9764 0.1797 0.1200 +vn 0.9764 -0.0000 0.2161 +vn 0.9764 0.1528 -0.1528 +vn 0.9764 0.1996 0.0827 +vn -0.8944 -0.3162 0.3162 +vn -0.8944 0.1711 0.4132 +vn 0.8944 -0.1711 -0.4132 +vn -0.8944 -0.2485 0.3719 +vn -0.8944 0.3162 0.3162 +vn -0.8944 0.2485 0.3718 +vn 0.8944 -0.2485 -0.3718 +vn -0.8944 -0.1711 0.4132 +vn 0.8944 -0.3162 -0.3162 +vn -0.8944 -0.0872 0.4386 +vn -0.8944 0.4132 0.1711 +vn -0.8944 0.3718 0.2485 +vn 0.8944 -0.3718 -0.2485 +vn -0.8944 0.4386 0.0872 +vn 0.8944 -0.4132 -0.1711 +vn 0.8944 -0.4386 -0.0872 +vn -0.8944 0.4386 -0.0872 +vn -0.8944 0.4472 -0.0000 +vn 0.8944 -0.4472 0.0000 +vn 0.8944 -0.4386 0.0872 +vn -0.8944 0.4132 -0.1711 +vn 0.8944 -0.4132 0.1711 +vn -0.8944 0.3162 -0.3162 +vn -0.8944 0.3718 -0.2485 +vn 0.8944 -0.3718 0.2485 +vn 0.8944 0.0872 0.4386 +vn -0.8944 0.2485 -0.3718 +vn 0.8944 -0.3162 0.3162 +vn 0.8944 0.1711 0.4132 +vn 0.8944 -0.2485 0.3719 +vn 0.8944 0.2485 0.3718 +vn -0.8944 0.1711 -0.4132 +vn 0.8944 -0.1711 0.4132 +vn 0.8944 0.3162 0.3162 +vn -0.8944 -0.0000 -0.4472 +vn -0.8944 0.0872 -0.4386 +vn 0.8944 -0.0872 0.4386 +vn 0.8944 0.3718 0.2485 +vn 0.8944 -0.0000 0.4472 +vn 0.8944 0.4132 0.1711 +vn -0.8944 -0.1711 -0.4132 +vn -0.8944 -0.0872 -0.4386 +vn 0.8944 0.4386 0.0872 +vn -0.8944 -0.2485 -0.3718 +vn 0.8944 0.4472 -0.0000 +vn 0.8944 0.4386 -0.0872 +vn -0.8944 -0.3162 -0.3162 +vn 0.8944 0.4132 -0.1711 +vn -0.8944 -0.4132 -0.1711 +vn -0.8944 -0.3718 -0.2485 +vn 0.8944 0.3718 -0.2485 +vn -0.8944 -0.4386 -0.0872 +vn 0.8944 0.3162 -0.3162 +vn -0.8944 -0.4472 0.0000 +vn 0.8944 0.2485 -0.3718 +vn 0.8944 0.1711 -0.4132 +vn -0.8944 -0.4132 0.1711 +vn -0.8944 -0.4386 0.0872 +vn 0.8944 0.0872 -0.4386 +vn -0.8944 -0.3718 0.2485 +vn -0.8944 -0.0000 0.4472 +vn -0.8293 0.0000 -0.5589 +vn 0.9764 0.1200 -0.1797 +vn 0.8293 -0.0000 -0.5589 +vn -0.9764 0.1200 -0.1797 +vn 0.8944 0.0873 -0.4386 +usemtl Body +s off +f 2//1 4//1 5//1 +f 7//2 9//2 6//2 +f 10//3 12//3 13//3 +f 14//4 16//4 17//4 +f 18//5 20//5 21//5 +f 22//6 23//6 24//6 +f 25//7 7//7 26//7 +f 27//8 29//8 30//8 +f 6//9 32//9 7//9 +f 33//10 13//10 34//10 +f 35//11 37//11 38//11 +f 40//11 42//11 39//11 +f 43//12 45//12 46//12 +f 48//12 50//12 47//12 +f 24//13 51//13 52//13 +f 53//14 41//14 40//14 +f 23//15 55//15 51//15 +f 49//16 56//16 57//16 +f 58//11 60//11 37//11 +f 62//11 58//11 37//11 +f 54//17 64//17 55//17 +f 53//18 66//19 67//18 +f 41//20 70//20 68//20 +f 24//21 64//22 63//21 +f 53//23 69//23 41//23 +f 68//24 66//24 65//24 +f 35//25 71//25 15//25 +f 15//26 4//26 3//26 +f 39//27 72//27 28//27 +f 11//28 73//28 48//28 +f 74//29 9//29 75//29 +f 74//30 76//30 25//30 +f 7//31 76//31 8//31 +f 77//32 78//32 79//32 +f 7//31 32//31 80//31 +f 27//33 82//33 18//33 +f 19//34 82//34 83//34 +f 19//35 29//35 28//35 +f 14//36 85//36 15//36 +f 14//37 21//37 84//37 +f 86//16 87//16 88//16 +f 89//12 91//12 45//12 +f 22//12 93//12 23//12 +f 39//11 61//11 72//11 +f 72//11 37//11 36//11 +f 92//12 46//12 93//12 +f 47//12 95//12 94//12 +f 4//11 96//11 88//11 +f 98//38 31//38 6//38 +f 94//12 45//12 44//12 +f 19//35 85//35 20//35 +f 71//11 38//11 96//11 +f 99//39 16//39 15//39 +f 77//40 12//40 11//40 +f 33//41 100//41 99//41 +f 48//42 73//42 102//42 +f 5//43 103//43 56//43 +f 87//44 103//44 5//44 +f 104//12 89//12 45//12 +f 105//11 86//11 88//11 +f 49//12 57//12 108//12 +f 5//45 1//45 2//45 +f 2//45 3//45 4//45 +f 7//2 8//2 9//2 +f 10//46 11//46 12//46 +f 14//4 15//4 16//4 +f 18//5 19//5 20//5 +f 26//7 22//7 25//7 +f 22//7 24//7 25//7 +f 27//8 28//8 29//8 +f 6//9 31//9 32//9 +f 33//10 10//10 13//10 +f 35//11 36//11 37//11 +f 40//11 41//11 42//11 +f 43//12 44//12 45//12 +f 48//12 49//12 50//12 +f 24//13 23//13 51//13 +f 23//47 54//47 55//47 +f 58//11 59//11 60//11 +f 60//11 38//11 37//11 +f 37//11 61//11 62//11 +f 62//11 59//11 58//11 +f 54//17 63//17 64//17 +f 53//18 65//18 66//18 +f 41//20 69//20 70//20 +f 24//48 52//48 64//49 +f 53//50 67//50 69//50 +f 68//24 70//24 66//24 +f 71//25 4//25 15//25 +f 15//25 19//25 35//25 +f 19//25 36//25 35//25 +f 72//27 36//27 28//27 +f 36//51 19//51 28//51 +f 28//51 53//51 39//51 +f 53//27 40//27 39//27 +f 48//28 26//28 11//28 +f 26//52 7//52 11//52 +f 74//29 6//29 9//29 +f 74//53 75//53 76//53 +f 7//31 25//31 76//31 +f 77//32 11//32 78//32 +f 78//31 11//31 81//31 +f 11//31 7//31 80//31 +f 81//31 11//31 80//31 +f 27//33 30//33 82//33 +f 19//34 18//34 82//34 +f 19//35 83//35 29//35 +f 14//36 84//36 85//36 +f 14//37 18//37 21//37 +f 89//12 90//12 91//12 +f 91//12 46//12 45//12 +f 22//12 92//12 93//12 +f 39//11 42//11 61//11 +f 72//11 61//11 37//11 +f 92//12 43//12 46//12 +f 47//12 50//12 95//12 +f 4//11 71//11 96//11 +f 6//38 77//38 98//38 +f 77//38 79//38 97//38 +f 98//38 77//38 97//38 +f 94//12 95//12 45//12 +f 19//35 15//35 85//35 +f 71//11 35//11 38//11 +f 99//54 100//54 16//54 +f 77//40 101//40 12//40 +f 33//41 34//41 100//41 +f 1//42 5//42 102//42 +f 5//55 48//55 102//55 +f 56//43 49//43 48//43 +f 48//43 5//43 56//43 +f 5//44 4//44 87//44 +f 4//44 88//44 87//44 +f 45//12 95//12 104//12 +f 104//12 90//12 89//12 +f 88//11 96//11 105//11 +f 105//11 106//11 86//11 +f 108//12 50//12 49//12 +f 57//12 107//12 108//12 +s 1 +f 53//56 109//57 65//58 +f 111//59 113//60 110//61 +f 114//62 116//63 117//64 +f 115//65 118//66 116//63 +f 2//67 33//68 99//69 +f 102//70 33//68 1//71 +f 63//72 121//73 119//74 +f 122//75 74//76 115//65 +f 110//61 114//62 123//77 +f 27//78 110//61 123//77 +f 3//79 99//69 15//80 +f 114//62 124//81 123//77 +f 65//58 121//73 120//82 +f 114//62 122//75 115//65 +f 24//83 119//74 25//84 +f 73//85 10//86 102//70 +f 27//78 126//87 28//88 +f 112//89 127//90 128//91 +f 74//76 129//92 118//66 +f 121//73 130//93 131//94 +f 121//73 132//95 119//74 +f 109//57 126//87 130//93 +f 119//74 129//92 25//84 +f 123//77 125//96 27//78 +f 122//75 77//97 6//98 +f 14//99 110//61 18//100 +f 113//60 133//101 122//75 +f 111//59 17//102 127//90 +f 133//101 101//103 77//97 +f 112//89 134//104 133//101 +f 136//105 138//106 135//107 +f 139//108 136//105 135//107 +f 53//56 28//88 109//57 +f 111//59 112//89 113//60 +f 114//62 115//65 116//63 +f 115//65 74//76 118//66 +f 2//67 1//71 33//68 +f 102//70 10//86 33//68 +f 63//72 120//82 121//73 +f 122//75 6//98 74//76 +f 110//61 113//60 114//62 +f 27//78 18//100 110//61 +f 3//79 2//67 99//69 +f 114//62 117//64 124//81 +f 65//58 109//57 121//73 +f 114//62 113//60 122//75 +f 24//83 63//72 119//74 +f 73//85 11//109 10//86 +f 27//78 125//96 126//87 +f 112//89 111//59 127//90 +f 74//76 25//84 129//92 +f 121//73 109//57 130//93 +f 121//73 131//94 132//95 +f 109//57 28//88 126//87 +f 119//74 132//95 129//92 +f 123//77 124//81 125//96 +f 122//75 133//101 77//97 +f 14//99 111//59 110//61 +f 113//60 112//89 133//101 +f 111//59 14//99 17//102 +f 133//101 134//104 101//103 +f 112//89 128//91 134//104 +f 136//105 137//110 138//106 +f 139//108 140//111 136//105 +usemtl Black +s off +f 32//112 142//113 80//114 +f 8//115 143//115 144//115 +f 83//35 146//35 29//35 +f 81//116 148//117 78//118 +f 142//119 149//119 150//120 +f 80//121 147//122 81//123 +f 20//124 151//124 152//124 +f 101//40 154//40 12//40 +f 17//4 155//4 156//4 +f 13//125 154//125 157//125 +f 100//126 155//126 16//126 +f 34//127 158//127 100//127 +f 34//128 157//128 159//128 +f 32//129 141//130 142//131 +f 8//132 76//132 143//132 +f 83//124 145//124 146//124 +f 81//133 147//134 148//135 +f 150//136 148//137 147//137 +f 142//138 141//138 149//138 +f 150//137 147//137 142//137 +f 80//139 142//140 147//141 +f 20//35 85//35 151//35 +f 101//40 153//40 154//40 +f 17//4 16//4 155//4 +f 13//125 12//125 154//125 +f 100//126 158//126 155//126 +f 34//127 159//127 158//127 +f 34//128 13//128 157//128 +s 1 +f 117//64 160//142 161//143 +f 131//94 162//144 163//145 +f 130//93 164//146 162//144 +f 132//95 166//147 129//92 +f 128//91 168//148 134//104 +f 128//91 169//149 167//150 +f 127//90 156//151 169//149 +f 134//104 153//152 101//103 +f 116//63 170//153 160//142 +f 124//81 172//154 125//96 +f 117//64 171//155 124//81 +f 118//66 166//147 170//153 +f 125//96 164//146 126//87 +f 131//94 165//156 132//95 +f 117//64 116//63 160//142 +f 131//94 130//93 162//144 +f 130//93 126//87 164//146 +f 132//95 165//156 166//147 +f 128//91 167//150 168//148 +f 128//91 127//90 169//149 +f 127//90 17//102 156//151 +f 134//104 168//148 153//152 +f 116//63 118//66 170//153 +f 124//81 171//155 172//154 +f 117//64 161//143 171//155 +f 118//66 129//92 166//147 +f 125//96 172//154 164//146 +f 131//94 163//145 165//156 +usemtl Window +s off +f 8//157 173//157 9//157 +f 78//158 174//158 79//158 +f 31//158 141//158 32//158 +f 79//158 176//158 97//158 +f 175//159 149//159 141//159 +f 174//160 150//161 178//160 +f 75//162 173//162 179//162 +f 75//163 143//163 76//163 +f 83//164 180//164 145//164 +f 84//36 151//36 85//36 +f 21//5 152//5 182//5 +f 30//165 180//165 82//165 +f 30//166 146//166 183//166 +f 97//158 184//158 98//158 +f 84//167 182//167 181//167 +f 98//158 175//158 31//158 +f 184//38 177//38 175//38 +f 176//168 178//168 185//168 +f 8//169 144//169 173//169 +f 78//158 148//158 174//158 +f 31//158 175//158 141//158 +f 79//158 174//158 176//158 +f 175//9 177//9 149//9 +f 174//170 148//171 150//171 +f 75//29 9//29 173//29 +f 75//172 179//172 143//172 +f 83//164 82//164 180//164 +f 84//36 181//36 151//36 +f 21//173 20//173 152//173 +f 30//165 183//165 180//165 +f 30//166 29//166 146//166 +f 97//158 176//158 184//158 +f 84//167 21//167 182//167 +f 98//158 184//158 175//158 +f 184//168 185//168 177//168 +f 185//168 184//168 176//168 +f 176//38 174//38 178//38 +s 1 +f 144//174 179//175 173//176 +f 145//177 183//178 146//179 +f 149//180 177//181 185//182 +f 152//183 181//184 182//185 +f 159//186 169//149 158//187 +f 157//188 153//152 168//148 +f 158//187 156//151 155//189 +f 159//186 168//148 167//150 +f 162//144 172//154 171//155 +f 163//145 160//142 165//156 +f 163//145 171//155 161//143 +f 165//156 170//153 166//147 +f 144//174 143//190 179//175 +f 145//177 180//191 183//178 +f 178//192 150//193 185//182 +f 150//193 149//180 185//182 +f 152//183 151//194 181//184 +f 159//186 167//150 169//149 +f 157//188 154//195 153//152 +f 158//187 169//149 156//151 +f 159//186 157//188 168//148 +f 162//144 164//146 172//154 +f 163//145 161//143 160//142 +f 163//145 162//144 171//155 +f 165//156 160//142 170//153 +usemtl Bumpers +s off +f 91//196 187//196 46//196 +f 46//197 188//197 93//197 +f 60//198 190//198 38//198 +f 105//199 191//199 192//199 +f 38//200 191//200 96//200 +f 62//201 193//201 194//201 +f 42//200 193//200 61//200 +f 187//12 196//12 197//12 +f 187//12 198//12 188//12 +f 186//202 199//202 196//202 +f 193//11 201//11 194//11 +f 195//11 200//11 193//11 +f 194//203 201//203 203//203 +f 191//11 205//11 192//11 +f 190//11 206//11 207//11 +f 190//11 204//11 191//11 +f 192//204 205//204 208//204 +f 189//205 209//205 206//205 +f 210//206 211//206 212//206 +f 91//196 186//196 187//196 +f 46//197 187//197 188//197 +f 60//198 189//198 190//198 +f 105//199 96//199 191//199 +f 38//200 190//200 191//200 +f 62//201 61//201 193//201 +f 42//200 195//200 193//200 +f 187//12 186//12 196//12 +f 187//12 197//12 198//12 +f 193//11 200//11 201//11 +f 195//11 202//11 200//11 +f 191//11 204//11 205//11 +f 190//11 189//11 206//11 +f 190//11 207//11 204//11 +s 1 +f 107//207 214//208 108//209 +f 216//210 218//211 215//212 +f 59//213 209//214 60//215 +f 90//216 199//217 91//218 +f 221//219 223//220 68//221 +f 224//222 106//223 225//224 +f 227//225 229//226 226//227 +f 68//221 230//228 41//229 +f 231//230 233//231 234//232 +f 227//225 235//226 228//226 +f 237//233 239//234 236//235 +f 213//236 226//227 229//226 +f 240//237 233//231 242//238 +f 241//239 234//232 233//231 +f 245//240 238//241 244//242 +f 244//242 234//232 243//243 +f 232//244 242//238 233//231 +f 246//245 230//228 247//246 +f 54//247 248//248 249//249 +f 231//230 238//241 237//233 +f 250//250 239//234 245//240 +f 221//219 249//249 222//251 +f 251//252 248//248 23//253 +f 250//250 252//254 251//252 +f 216//210 242//238 217//255 +f 216//210 247//246 240//237 +f 222//251 253//256 254//257 +f 254//257 244//242 243//243 +f 249//249 252//254 253//256 +f 253//256 245//240 244//242 +f 222//251 255//258 223//220 +f 254//257 241//239 255//258 +f 223//220 247//246 230//228 +f 255//258 240//237 247//246 +f 256//259 107//207 57//260 +f 87//261 258//262 103//263 +f 257//264 227//225 258//262 +f 56//265 258//262 256//259 +f 258//262 226//227 256//259 +f 87//261 86//266 257//264 +f 257//264 106//223 224//222 +f 57//260 56//265 256//259 +f 225//224 235//226 224//222 +f 250//250 260//267 236//235 +f 62//268 219//269 59//213 +f 23//253 93//270 251//252 +f 105//271 225//224 106//223 +f 104//272 220//273 90//216 +f 226//227 213//236 107//207 +f 246//245 215//212 42//274 +f 104//272 261//275 210//276 +f 50//277 261//275 95//278 +f 215//212 195//279 42//274 +f 259//280 188//281 264//282 +f 108//209 262//283 50//277 +f 188//281 266//284 264//282 +f 199//217 186//285 91//218 +f 259//280 264//282 260//267 +f 264//282 266//284 260//267 +f 195//279 267//286 202//11 +f 218//211 263//287 215//212 +f 263//287 218//211 267//286 +f 42//274 41//229 246//245 +f 251//252 259//280 250//250 +f 62//268 194//288 203//289 +f 105//271 192//290 208//291 +f 209//214 189//292 60//215 +f 214//208 265//293 108//209 +f 262//283 268//294 269//12 +f 261//275 269//12 270//12 +f 261//275 211//295 210//276 +f 265//293 214//208 268//294 +f 104//272 210//276 212//296 +f 107//207 213//236 214//208 +f 216//210 217//255 218//211 +f 59//213 219//269 209//214 +f 90//216 220//273 199//217 +f 221//219 222//251 223//220 +f 227//225 228//226 229//226 +f 68//221 223//220 230//228 +f 231//230 232//244 233//231 +f 227//225 224//222 235//226 +f 237//233 238//241 239//234 +f 240//237 241//239 233//231 +f 241//239 243//243 234//232 +f 245//240 239//234 238//241 +f 244//242 238//241 234//232 +f 232//244 217//255 242//238 +f 246//245 41//229 230//228 +f 54//247 23//253 248//248 +f 231//230 234//232 238//241 +f 250//250 236//235 239//234 +f 221//219 54//247 249//249 +f 251//252 252//254 248//248 +f 250//250 245//240 252//254 +f 216//210 240//237 242//238 +f 216//210 246//245 247//246 +f 222//251 249//249 253//256 +f 254//257 253//256 244//242 +f 249//249 248//248 252//254 +f 253//256 252//254 245//240 +f 222//251 254//257 255//258 +f 254//257 243//243 241//239 +f 223//220 255//258 247//246 +f 255//258 241//239 240//237 +f 256//259 226//227 107//207 +f 87//261 257//264 258//262 +f 257//264 224//222 227//225 +f 56//265 103//263 258//262 +f 258//262 227//225 226//227 +f 257//264 86//266 106//223 +f 250//250 259//280 260//267 +f 62//268 203//289 219//269 +f 105//271 208//291 225//224 +f 104//272 212//296 220//273 +f 246//245 216//210 215//212 +f 104//272 95//278 261//275 +f 50//277 262//283 261//275 +f 215//212 263//287 195//279 +f 259//280 93//270 188//281 +f 108//209 265//293 262//283 +f 188//281 198//12 266//284 +f 195//279 263//287 267//286 +f 251//252 93//270 259//280 +f 262//283 265//293 268//294 +f 261//275 262//283 269//12 +f 261//275 270//12 211//295 +usemtl Lights +s off +f 70//297 67//297 66//297 +f 52//298 55//298 64//298 +f 70//299 69//299 67//299 +f 52//298 51//298 55//298 +usemtl Bottom +f 235//300 208//300 271//300 +f 272//300 228//300 273//300 +f 275//300 277//300 274//300 +f 279//300 203//300 278//300 +f 276//300 278//300 277//300 +f 228//300 271//300 273//300 +f 199//300 275//300 274//300 +f 214//300 229//300 272//300 +f 260//300 237//300 236//300 +f 281//300 232//300 231//300 +f 232//300 218//300 217//300 +f 280//300 231//300 237//300 +f 199//300 280//300 260//300 +f 277//300 282//300 281//300 +f 282//300 203//300 218//300 +f 274//300 281//300 280//300 +f 212//300 272//300 283//300 +f 273//300 285//300 284//300 +f 283//300 273//300 284//300 +f 271//300 209//300 285//300 +f 285//300 219//300 279//300 +f 275//300 284//300 276//300 +f 284//300 279//300 276//300 +f 220//300 283//300 275//300 +f 235//300 225//300 208//300 +f 272//300 229//300 228//300 +f 275//300 276//300 277//300 +f 279//300 219//300 203//300 +f 276//300 279//300 278//300 +f 228//300 235//300 271//300 +f 199//300 220//300 275//300 +f 214//300 213//300 229//300 +f 260//300 280//300 237//300 +f 281//300 282//300 232//300 +f 232//300 282//300 218//300 +f 280//300 281//300 231//300 +f 199//300 274//300 280//300 +f 277//300 278//300 282//300 +f 282//300 278//300 203//300 +f 274//300 277//300 281//300 +f 212//300 214//300 272//300 +f 273//300 271//300 285//300 +f 283//300 272//300 273//300 +f 271//300 208//300 209//300 +f 285//300 209//300 219//300 +f 275//300 283//300 284//300 +f 284//300 285//300 279//300 +f 220//300 212//300 283//300 +usemtl Tires +s 1 +f 287//301 289//302 286//303 +f 288//304 291//305 289//302 +f 290//306 293//307 291//305 +f 292//308 295//309 293//307 +f 294//310 297//311 295//309 +f 296//312 299//313 297//311 +f 298//314 301//315 299//313 +f 300//316 303//317 301//315 +f 302//318 305//319 303//317 +f 304//320 307//321 305//319 +f 306//322 309//323 307//321 +f 308//324 311//325 309//323 +f 310//326 313//327 311//325 +f 312//328 315//329 313//327 +f 314//330 317//331 315//329 +f 316//332 319//333 317//331 +f 318//334 321//335 319//333 +f 320//336 323//337 321//335 +f 322//338 325//339 323//337 +f 324//340 327//341 325//339 +f 326//342 329//343 327//341 +f 328//344 331//345 329//343 +f 330//346 333//347 331//345 +f 332//348 335//349 333//347 +f 334//350 337//351 335//349 +f 336//352 339//353 337//351 +f 338//354 341//355 339//353 +f 340//356 343//357 341//355 +f 342//358 345//359 343//357 +f 344//360 347//361 345//359 +f 330//346 349//362 332//348 +f 346//363 351//364 347//361 +f 350//365 286//303 351//364 +f 351//364 352//366 353//367 +f 355//368 357//369 354//370 +f 359//371 361//372 358//373 +f 299//313 363//374 297//311 +f 287//301 364//375 365//376 +f 317//331 367//377 315//329 +f 298//314 368//378 369//379 +f 333//347 370//380 371//381 +f 314//330 373//382 316//332 +f 332//348 374//383 334//350 +f 301//315 362//384 299//313 +f 319//333 366//385 317//331 +f 300//316 369//379 377//386 +f 337//351 370//380 335//349 +f 316//332 379//387 318//334 +f 334//350 380//388 336//352 +f 303//317 375//389 301//315 +f 319//333 382//390 376//391 +f 302//318 377//386 383//392 +f 339//353 378//393 337//351 +f 318//334 384//394 320//336 +f 338//354 380//388 385//395 +f 303//317 386//396 381//397 +f 323//337 382//390 321//335 +f 302//318 388//398 304//320 +f 289//302 352//366 286//303 +f 339//353 359//371 358//373 +f 322//338 384//394 390//399 +f 288//304 365//376 391//400 +f 338//354 392//401 340//356 +f 305//319 393//402 386//396 +f 325//339 387//403 323//337 +f 304//320 395//404 306//322 +f 341//355 396//405 359//371 +f 324//340 390//399 397//406 +f 289//302 398//407 389//408 +f 340//356 399//409 342//358 +f 307//321 400//410 393//402 +f 288//304 401//411 290//306 +f 325//339 402//412 394//413 +f 306//322 403//414 308//324 +f 343//357 404//415 396//405 +f 324//340 405//416 326//342 +f 291//305 406//417 398//407 +f 342//358 407//418 344//360 +f 309//323 408//419 400//410 +f 290//306 409//420 292//308 +f 327//341 410//421 402//412 +f 308//324 355//368 310//326 +f 347//361 404//415 345//359 +f 326//342 412//422 328//344 +f 295//309 406//417 293//307 +f 346//363 407//418 414//423 +f 311//325 415//424 408//419 +f 294//310 409//420 416//425 +f 331//345 410//421 329//343 +f 310//326 354//370 312//328 +f 347//361 353//367 411//426 +f 330//346 412//422 348//427 +f 297//311 413//428 295//309 +f 350//365 414//423 364//375 +f 315//329 415//424 313//327 +f 296//312 416//425 368//378 +f 331//345 371//381 417//429 +f 314//330 354//370 372//430 +f 418//431 420//432 421//433 +f 362//384 422//434 423//435 +f 367//377 425//436 415//424 +f 402//412 426//437 427//438 +f 352//366 428//439 429//440 +f 396//405 360//441 359//371 +f 381//397 422//434 375//389 +f 367//377 432//442 424//443 +f 417//429 426//437 410//421 +f 389//408 434//444 428//439 +f 404//415 430//445 396//405 +f 381//397 436//446 431//447 +f 366//385 437//448 432//442 +f 371//381 433//449 417//429 +f 406//417 434//444 398//407 +f 404//415 440//450 435//451 +f 393//402 436//446 386//396 +f 382//390 437//448 376//391 +f 370//380 438//452 371//381 +f 406//417 444//453 439//454 +f 353//367 440//450 411//426 +f 393//402 446//455 441//456 +f 387//403 442//457 382//390 +f 378//393 443//458 370//380 +f 413//428 449//459 444//453 +f 352//366 445//460 353//367 +f 400//410 450//461 446//455 +f 387//403 451//462 447//463 +f 378//393 361//372 448//464 +f 362//384 449//459 363//374 +f 408//419 425//436 450//461 +f 402//412 451//462 394//413 +f 434//444 453//465 428//439 +f 405//416 454//466 455//467 +f 392//401 456//468 457//469 +f 369//379 459//470 377//386 +f 372//430 357//369 460//471 +f 405//416 461//472 412//422 +f 365//376 463//473 391//400 +f 399//409 457//469 464//474 +f 383//392 459//470 465//475 +f 373//382 460//471 466//476 +f 348//427 461//472 467//477 +f 391//400 468//478 401//411 +f 407//418 464//474 469//479 +f 383//392 470//480 388//398 +f 373//382 419//481 379//387 +f 349//362 467//477 471//482 +f 409//420 468//478 472//483 +f 407//418 473//484 414//423 +f 395//404 470//480 474//485 +f 384//394 419//481 418//431 +f 349//362 475//486 374//383 +f 409//420 476//487 416//425 +f 414//423 477//488 364//375 +f 395//404 478//489 403//414 +f 390//399 418//431 479//490 +f 380//388 475//486 480//491 +f 416//425 481//492 368//378 +f 365//376 477//488 462//493 +f 403//414 356//494 355//368 +f 390//399 454//466 397//406 +f 380//388 456//468 385//395 +f 369//379 481//492 458//495 +f 435//451 483//496 430//445 +f 439//454 452//497 434//444 +f 418//431 485//498 479//490 +f 440//450 482//499 435//451 +f 439//454 487//500 484//501 +f 479//490 488//502 454//466 +f 445//460 486//503 440//450 +f 449//459 487//500 444//453 +f 455//467 488//502 491//504 +f 429//440 489//505 445//460 +f 449//459 493//506 490//507 +f 461//472 491//504 494//508 +f 423//435 495//509 493//506 +f 467//477 494//508 496//510 +f 431//447 495//509 422//434 +f 467//477 498//511 471//482 +f 431//447 499//512 497//513 +f 471//482 500//514 475//486 +f 441//456 499//512 436//446 +f 480//491 500//514 502//515 +f 446//455 501//516 441//456 +f 480//491 504//517 456//468 +f 446//455 505//518 503//519 +f 456//468 506//520 457//469 +f 462//493 508//521 463//473 +f 450//461 509//522 505//518 +f 457//469 510//523 464//474 +f 468//478 508//521 511//524 +f 424//443 509//522 425//436 +f 469//479 510//523 513//525 +f 472//483 511//524 514//526 +f 432//442 512//527 424//443 +f 473//484 513//525 516//528 +f 472//483 517//529 476//487 +f 432//442 518//530 515//531 +f 477//488 516//528 519//532 +f 481//492 517//529 520//533 +f 442//457 518//530 437//448 +f 462//493 519//532 507//534 +f 481//492 522//535 458//495 +f 442//457 523//536 521//537 +f 458//495 524//538 459//470 +f 447//463 525//539 523//536 +f 465//475 524//538 526//540 +f 427//438 525//539 451//462 +f 465//475 528//541 470//480 +f 426//437 527//542 427//438 +f 474//485 528//541 530//543 +f 426//437 531//544 529//545 +f 478//489 530//543 532//546 +f 433//449 533//547 531//544 +f 478//489 534//548 356//494 +f 438//452 535//549 533//547 +f 356//494 536//550 357//369 +f 448//464 535//549 443//458 +f 460//471 536//550 538//551 +f 448//464 539//552 537//553 +f 466//476 538//551 540//554 +f 361//372 541//555 539//552 +f 429//440 453//465 492//556 +f 466//476 420//432 419//481 +f 360//441 483//496 541//555 +f 543//301 545//302 542//303 +f 544//304 547//305 545//302 +f 546//306 549//307 547//305 +f 548//308 551//309 549//307 +f 550//310 553//311 551//309 +f 552//312 555//313 553//311 +f 554//314 557//315 555//313 +f 556//316 559//317 557//315 +f 558//318 561//319 559//317 +f 560//320 563//321 561//319 +f 562//322 565//323 563//321 +f 564//324 567//325 565//323 +f 566//326 569//327 567//325 +f 568//328 571//329 569//327 +f 570//330 573//331 571//329 +f 572//332 575//333 573//331 +f 574//334 577//335 575//333 +f 576//336 579//337 577//335 +f 578//338 581//339 579//337 +f 580//340 583//341 581//339 +f 582//342 585//343 583//341 +f 584//344 587//345 585//343 +f 586//346 589//347 587//345 +f 588//348 591//349 589//347 +f 590//350 593//351 591//349 +f 592//352 595//353 593//351 +f 594//354 597//355 595//353 +f 596//356 599//357 597//355 +f 598//358 601//359 599//357 +f 600//360 603//361 601//359 +f 586//346 605//362 588//348 +f 602//363 607//364 603//361 +f 606//365 542//303 607//364 +f 607//364 608//557 609//367 +f 611//368 613//558 610//370 +f 615//371 617//372 614//373 +f 553//311 618//384 619//374 +f 543//301 620//375 621//559 +f 573//331 623//377 571//329 +f 552//312 625//379 554//314 +f 589//347 626//380 627//381 +f 570//330 629//382 572//332 +f 588//348 630//383 590//350 +f 555//313 631//389 618//384 +f 575//333 622//385 573//331 +f 554//314 633//386 556//316 +f 593//351 626//380 591//349 +f 572//332 635//387 574//334 +f 592//352 630//383 636//388 +f 557//315 637//397 631//389 +f 575//333 638//390 632//391 +f 556//316 639//392 558//318 +f 593//351 614//373 634//393 +f 574//334 640//394 576//336 +f 592//352 641//395 594//354 +f 559//317 642//396 637//397 +f 579//337 638//390 577//335 +f 558//318 644//398 560//320 +f 545//302 608//557 542//303 +f 595//353 615//371 614//373 +f 578//338 640//394 646//399 +f 544//304 621//559 647//400 +f 594//354 648//401 596//356 +f 561//319 649//402 642//396 +f 581//339 643//403 579//337 +f 560//320 651//404 562//322 +f 597//355 652//405 615//371 +f 580//340 646//399 653//406 +f 547//305 645//408 545//302 +f 596//356 655//409 598//358 +f 563//321 656//410 649//402 +f 546//306 647//400 657//411 +f 581//339 658//412 650//413 +f 562//322 659//414 564//324 +f 599//357 660//415 652//405 +f 580//340 661//416 582//342 +f 547//305 662//417 654//407 +f 598//358 663//418 600//360 +f 565//323 664//419 656//410 +f 546//306 665//420 548//308 +f 583//341 666//421 658//412 +f 564//324 611//368 566//326 +f 603//361 660//415 601//359 +f 582//342 668//422 584//344 +f 551//309 662//417 549//307 +f 602//363 663//418 670//423 +f 567//325 671//424 664//419 +f 550//310 665//420 672//425 +f 587//345 666//421 585//343 +f 566//326 610//370 568//328 +f 607//364 667//426 603//361 +f 586//346 668//422 604//427 +f 553//311 669//428 551//309 +f 606//365 670//423 620//375 +f 569//327 623//377 671//424 +f 552//312 672//425 624//378 +f 587//345 627//381 673//429 +f 568//328 628//430 570//330 +f 674//431 676//432 677//433 +f 618//384 678//434 679//435 +f 623//377 681//560 671//424 +f 658//412 682//437 683//438 +f 608//557 684//439 685//440 +f 615//371 686//445 616//441 +f 637//397 678//434 631//389 +f 623//377 688//442 680//443 +f 673//429 682//437 666//421 +f 654//407 684//439 645//408 +f 660//415 686//445 652//405 +f 637//397 692//446 687//447 +f 622//385 693//448 688//442 +f 627//381 689//449 673//429 +f 654//407 695//454 690//444 +f 660//415 696//450 691//451 +f 649//402 692//446 642//396 +f 638//390 693//448 632//391 +f 626//380 694//452 627//381 +f 669//428 695//454 662//417 +f 609//367 696//450 667//426 +f 649//402 702//455 697//456 +f 643//403 698//457 638//390 +f 634//393 699//458 626//380 +f 619//374 700//453 669//428 +f 608//557 701//460 609//367 +f 656//410 706//461 702//455 +f 643//403 707//462 703//463 +f 634//393 617//372 704//464 +f 618//384 705//459 619//374 +f 664//419 681//560 706//461 +f 658//412 707//462 650//413 +f 690//444 709//465 684//439 +f 661//416 710//466 711//467 +f 648//401 712//468 713//469 +f 625//379 715//470 633//386 +f 628//430 613//558 716//471 +f 661//416 717//472 668//422 +f 621//559 719//473 647//400 +f 648//401 720//474 655//409 +f 639//392 715//470 721//475 +f 628//430 722//476 629//382 +f 604//427 717//472 723//477 +f 657//411 719//473 724//478 +f 663//418 720//474 725//479 +f 639//392 726//480 644//398 +f 635//387 722//476 675//481 +f 605//362 723//477 727//482 +f 657//411 728//483 665//420 +f 663//418 729//484 670//423 +f 651//404 726//480 730//485 +f 640//394 675//481 674//431 +f 605//362 731//486 630//383 +f 665//420 732//487 672//425 +f 670//423 733//488 620//375 +f 651//404 734//489 659//414 +f 646//399 674//431 735//490 +f 636//388 731//486 736//491 +f 624//378 732//487 737//492 +f 621//559 733//488 718//493 +f 659//414 612//494 611//368 +f 646//399 710//466 653//406 +f 636//388 712//468 641//395 +f 625//379 737//492 714//495 +f 691//451 739//496 686//445 +f 695//454 708//497 690//444 +f 674//431 741//498 735//490 +f 696//450 738//499 691//451 +f 695//454 743//500 740//501 +f 735//490 744//502 710//466 +f 696//450 745//505 742//503 +f 705//459 743//500 700//453 +f 711//467 744//502 747//504 +f 685//440 745//505 701//460 +f 705//459 749//506 746//507 +f 717//472 747//504 750//508 +f 679//435 751//509 749//506 +f 723//477 750//508 752//510 +f 687//447 751//509 678//434 +f 727//482 752//510 754//511 +f 687//447 755//512 753//513 +f 727//482 756//514 731//486 +f 697//456 755//512 692//446 +f 736//491 756//514 758//515 +f 702//455 757//516 697//456 +f 736//491 760//517 712//468 +f 702//455 761//518 759//519 +f 713//469 760//517 762//520 +f 718//493 764//521 719//473 +f 706//461 765//522 761//518 +f 720//474 762//520 766//523 +f 724//478 764//521 767//524 +f 680//443 765//522 681//560 +f 725//479 766//523 769//525 +f 728//483 767//524 770//526 +f 688//442 768//527 680//443 +f 729//484 769//525 772//528 +f 728//483 773//529 732//487 +f 688//442 774//530 771//531 +f 729//484 775//532 733//488 +f 737//492 773//529 776//533 +f 698//457 774//530 693//448 +f 718//493 775//532 763//534 +f 737//492 778//535 714//495 +f 698//457 779//536 777//537 +f 714//495 780//538 715//470 +f 703//463 781//539 779//536 +f 721//475 780//538 782//540 +f 683//438 781//539 707//462 +f 721//475 784//541 726//480 +f 682//437 783//542 683//438 +f 730//485 784//541 786//543 +f 682//437 787//544 785//545 +f 734//489 786//543 788//546 +f 689//449 789//547 787//544 +f 734//489 790//548 612//494 +f 694//452 791//549 789//547 +f 612//494 792//550 613//558 +f 704//464 791//549 699//458 +f 716//471 792//550 794//551 +f 704//464 795//552 793//553 +f 716//471 796//554 722//476 +f 616//441 795//552 617//372 +f 685//440 709//465 748//556 +f 722//476 676//432 675//481 +f 686//445 797//555 616//441 +f 799//301 801//302 798//303 +f 800//304 803//305 801//302 +f 802//306 805//307 803//305 +f 804//308 807//309 805//307 +f 806//310 809//311 807//309 +f 808//312 811//313 809//311 +f 810//314 813//315 811//313 +f 812//316 815//317 813//315 +f 814//318 817//319 815//317 +f 816//320 819//321 817//319 +f 818//322 821//323 819//321 +f 820//324 823//325 821//323 +f 822//326 825//327 823//325 +f 824//328 827//329 825//327 +f 826//330 829//331 827//329 +f 828//332 831//333 829//331 +f 830//334 833//335 831//333 +f 832//336 835//337 833//335 +f 834//338 837//339 835//337 +f 836//340 839//341 837//339 +f 838//342 841//343 839//341 +f 840//344 843//345 841//343 +f 842//346 845//347 843//345 +f 844//348 847//349 845//347 +f 846//350 849//351 847//349 +f 848//352 851//353 849//351 +f 850//354 853//355 851//353 +f 852//356 855//357 853//355 +f 854//358 857//359 855//357 +f 856//360 859//361 857//359 +f 842//346 861//362 844//348 +f 858//363 863//364 859//361 +f 862//365 798//303 863//364 +f 863//364 864//557 865//367 +f 867//368 869//558 866//370 +f 871//371 873//372 870//373 +f 809//311 874//384 875//374 +f 799//301 876//375 877//559 +f 829//331 879//377 827//329 +f 808//312 881//379 810//314 +f 845//347 882//380 883//381 +f 826//330 885//382 828//332 +f 844//348 886//383 846//350 +f 811//313 887//389 874//384 +f 831//333 878//385 829//331 +f 810//314 889//386 812//316 +f 849//351 882//380 847//349 +f 828//332 891//387 830//334 +f 848//352 886//383 892//388 +f 813//315 893//397 887//389 +f 831//333 894//390 888//391 +f 812//316 895//392 814//318 +f 849//351 870//373 890//393 +f 830//334 896//394 832//336 +f 848//352 897//395 850//354 +f 815//317 898//396 893//397 +f 835//337 894//390 833//335 +f 814//318 900//398 816//320 +f 801//302 864//557 798//303 +f 851//353 871//371 870//373 +f 834//338 896//394 902//399 +f 800//304 877//559 903//400 +f 850//354 904//401 852//356 +f 817//319 905//402 898//396 +f 837//339 899//403 835//337 +f 816//320 907//404 818//322 +f 853//355 908//405 871//371 +f 836//340 902//399 909//406 +f 803//305 901//408 801//302 +f 852//356 911//409 854//358 +f 819//321 912//410 905//402 +f 802//306 903//400 913//411 +f 837//339 914//412 906//413 +f 818//322 915//414 820//324 +f 855//357 916//415 908//405 +f 836//340 917//416 838//342 +f 803//305 918//417 910//407 +f 854//358 919//418 856//360 +f 821//323 920//419 912//410 +f 802//306 921//420 804//308 +f 839//341 922//421 914//412 +f 820//324 867//368 822//326 +f 859//361 916//415 857//359 +f 838//342 924//422 840//344 +f 807//309 918//417 805//307 +f 858//363 919//418 926//423 +f 823//325 927//424 920//419 +f 806//310 921//420 928//425 +f 843//345 922//421 841//343 +f 822//326 866//370 824//328 +f 863//364 923//426 859//361 +f 842//346 924//422 860//427 +f 809//311 925//428 807//309 +f 862//365 926//423 876//375 +f 825//327 879//377 927//424 +f 808//312 928//425 880//378 +f 843//345 883//381 929//429 +f 824//328 884//430 826//330 +f 930//431 932//432 933//433 +f 874//384 934//434 935//435 +f 879//377 937//560 927//424 +f 914//412 938//437 939//438 +f 864//557 940//439 941//440 +f 871//371 942//445 872//441 +f 893//397 934//434 887//389 +f 879//377 944//442 936//443 +f 929//429 938//437 922//421 +f 910//407 940//439 901//408 +f 916//415 942//445 908//405 +f 893//397 948//446 943//447 +f 878//385 949//448 944//442 +f 883//381 945//449 929//429 +f 910//407 951//454 946//444 +f 916//415 952//450 947//451 +f 905//402 948//446 898//396 +f 894//390 949//448 888//391 +f 882//380 950//452 883//381 +f 925//428 951//454 918//417 +f 865//367 952//450 923//426 +f 905//402 958//455 953//456 +f 899//403 954//457 894//390 +f 890//393 955//458 882//380 +f 875//374 956//453 925//428 +f 864//557 957//460 865//367 +f 912//410 962//461 958//455 +f 899//403 963//462 959//463 +f 890//393 873//372 960//464 +f 874//384 961//459 875//374 +f 920//419 937//560 962//461 +f 914//412 963//462 906//413 +f 946//444 965//465 940//439 +f 917//416 966//466 967//467 +f 904//401 968//468 969//469 +f 881//379 971//470 889//386 +f 884//430 869//558 972//471 +f 917//416 973//472 924//422 +f 877//559 975//473 903//400 +f 904//401 976//474 911//409 +f 895//392 971//470 977//475 +f 884//430 978//476 885//382 +f 860//427 973//472 979//477 +f 913//411 975//473 980//478 +f 919//418 976//474 981//479 +f 895//392 982//480 900//398 +f 891//387 978//476 931//481 +f 861//362 979//477 983//482 +f 913//411 984//483 921//420 +f 919//418 985//484 926//423 +f 907//404 982//480 986//485 +f 896//394 931//481 930//431 +f 861//362 987//486 886//383 +f 921//420 988//487 928//425 +f 926//423 989//488 876//375 +f 907//404 990//489 915//414 +f 902//399 930//431 991//490 +f 892//388 987//486 992//491 +f 880//378 988//487 993//492 +f 877//559 989//488 974//493 +f 915//414 868//494 867//368 +f 902//399 966//466 909//406 +f 892//388 968//468 897//395 +f 881//379 993//492 970//495 +f 947//451 995//496 942//445 +f 951//454 964//497 946//444 +f 930//431 997//498 991//490 +f 952//450 994//499 947//451 +f 951//454 999//500 996//501 +f 991//490 1000//502 966//466 +f 952//450 1001//505 998//503 +f 961//459 999//500 956//453 +f 967//467 1000//502 1003//504 +f 941//440 1001//505 957//460 +f 961//459 1005//506 1002//507 +f 973//472 1003//504 1006//508 +f 935//435 1007//509 1005//506 +f 979//477 1006//508 1008//510 +f 943//447 1007//509 934//434 +f 983//482 1008//510 1010//511 +f 943//447 1011//512 1009//513 +f 983//482 1012//514 987//486 +f 953//456 1011//512 948//446 +f 992//491 1012//514 1014//515 +f 958//455 1013//516 953//456 +f 992//491 1016//517 968//468 +f 958//455 1017//518 1015//519 +f 969//469 1016//517 1018//520 +f 974//493 1020//521 975//473 +f 962//461 1021//522 1017//518 +f 976//474 1018//520 1022//523 +f 980//478 1020//521 1023//524 +f 936//443 1021//522 937//560 +f 981//479 1022//523 1025//525 +f 984//483 1023//524 1026//526 +f 944//442 1024//527 936//443 +f 985//484 1025//525 1028//528 +f 984//483 1029//529 988//487 +f 944//442 1030//530 1027//531 +f 985//484 1031//532 989//488 +f 993//492 1029//529 1032//533 +f 954//457 1030//530 949//448 +f 974//493 1031//532 1019//534 +f 993//492 1034//535 970//495 +f 954//457 1035//536 1033//537 +f 970//495 1036//538 971//470 +f 959//463 1037//539 1035//536 +f 977//475 1036//538 1038//540 +f 939//438 1037//539 963//462 +f 977//475 1040//541 982//480 +f 938//437 1039//542 939//438 +f 986//485 1040//541 1042//543 +f 938//437 1043//544 1041//545 +f 990//489 1042//543 1044//546 +f 945//449 1045//547 1043//544 +f 990//489 1046//548 868//494 +f 950//452 1047//549 1045//547 +f 868//494 1048//550 869//558 +f 960//464 1047//549 955//458 +f 972//471 1048//550 1050//551 +f 960//464 1051//552 1049//553 +f 972//471 1052//561 978//476 +f 872//441 1051//552 873//372 +f 941//440 965//465 1004//556 +f 978//476 932//432 931//481 +f 942//445 1053//555 872//441 +f 1055//301 1057//302 1054//303 +f 1056//304 1059//305 1057//302 +f 1058//306 1061//307 1059//305 +f 1060//308 1063//309 1061//307 +f 1062//310 1065//311 1063//309 +f 1064//312 1067//313 1065//311 +f 1066//314 1069//315 1067//313 +f 1068//316 1071//317 1069//315 +f 1070//318 1073//319 1071//317 +f 1072//320 1075//321 1073//319 +f 1074//322 1077//323 1075//321 +f 1076//324 1079//325 1077//323 +f 1078//326 1081//327 1079//325 +f 1080//328 1083//329 1081//327 +f 1082//330 1085//331 1083//329 +f 1084//332 1087//333 1085//331 +f 1086//334 1089//335 1087//333 +f 1088//336 1091//337 1089//335 +f 1090//338 1093//339 1091//337 +f 1092//340 1095//341 1093//339 +f 1094//342 1097//343 1095//341 +f 1096//344 1099//345 1097//343 +f 1098//346 1101//347 1099//345 +f 1100//348 1103//349 1101//347 +f 1102//350 1105//351 1103//349 +f 1104//352 1107//353 1105//351 +f 1106//354 1109//355 1107//353 +f 1108//356 1111//357 1109//355 +f 1110//358 1113//359 1111//357 +f 1112//360 1115//361 1113//359 +f 1098//346 1117//362 1100//348 +f 1114//363 1119//364 1115//361 +f 1118//365 1054//303 1119//364 +f 1119//364 1120//366 1121//367 +f 1123//368 1125//369 1122//370 +f 1127//371 1129//372 1126//373 +f 1067//313 1131//374 1065//311 +f 1055//301 1132//375 1133//376 +f 1085//331 1135//377 1083//329 +f 1066//314 1136//378 1137//379 +f 1101//347 1138//380 1139//381 +f 1082//330 1141//382 1084//332 +f 1100//348 1142//383 1102//350 +f 1069//315 1130//384 1067//313 +f 1087//333 1134//385 1085//331 +f 1068//316 1137//379 1145//386 +f 1105//351 1138//380 1103//349 +f 1084//332 1147//387 1086//334 +f 1104//352 1142//383 1148//388 +f 1071//317 1143//389 1069//315 +f 1087//333 1150//390 1144//391 +f 1070//318 1145//386 1151//392 +f 1107//353 1146//393 1105//351 +f 1086//334 1152//394 1088//336 +f 1106//354 1148//388 1153//395 +f 1071//317 1154//396 1149//397 +f 1091//337 1150//390 1089//335 +f 1070//318 1156//398 1072//320 +f 1057//302 1120//366 1054//303 +f 1107//353 1127//371 1126//373 +f 1090//338 1152//394 1158//399 +f 1056//304 1133//376 1159//400 +f 1106//354 1160//401 1108//356 +f 1073//319 1161//402 1154//396 +f 1093//339 1155//403 1091//337 +f 1072//320 1163//404 1074//322 +f 1109//355 1164//405 1127//371 +f 1092//340 1158//399 1165//406 +f 1057//302 1166//407 1157//408 +f 1108//356 1167//409 1110//358 +f 1075//321 1168//410 1161//402 +f 1056//304 1169//411 1058//306 +f 1093//339 1170//412 1162//413 +f 1074//322 1171//414 1076//324 +f 1111//357 1172//415 1164//405 +f 1092//340 1173//416 1094//342 +f 1059//305 1174//417 1166//407 +f 1110//358 1175//418 1112//360 +f 1077//323 1176//419 1168//410 +f 1058//306 1177//420 1060//308 +f 1095//341 1178//421 1170//412 +f 1076//324 1123//368 1078//326 +f 1115//361 1172//415 1113//359 +f 1094//342 1180//422 1096//344 +f 1063//309 1174//417 1061//307 +f 1114//363 1175//418 1182//423 +f 1079//325 1183//424 1176//419 +f 1062//310 1177//420 1184//425 +f 1099//345 1178//421 1097//343 +f 1078//326 1122//370 1080//328 +f 1115//361 1121//367 1179//426 +f 1098//346 1180//422 1116//427 +f 1065//311 1181//428 1063//309 +f 1118//365 1182//423 1132//375 +f 1083//329 1183//424 1081//327 +f 1064//312 1184//425 1136//378 +f 1099//345 1139//381 1185//429 +f 1082//330 1122//370 1140//430 +f 1186//431 1188//432 1189//433 +f 1130//384 1190//434 1191//435 +f 1135//377 1193//436 1183//424 +f 1170//412 1194//437 1195//438 +f 1120//366 1196//439 1197//440 +f 1164//405 1128//441 1127//371 +f 1149//397 1190//434 1143//389 +f 1135//377 1200//442 1192//443 +f 1185//429 1194//437 1178//421 +f 1157//408 1202//444 1196//439 +f 1172//415 1198//445 1164//405 +f 1149//397 1204//446 1199//447 +f 1134//385 1205//448 1200//442 +f 1139//381 1201//449 1185//429 +f 1174//417 1202//444 1166//407 +f 1172//415 1208//450 1203//451 +f 1161//402 1204//446 1154//396 +f 1150//390 1205//448 1144//391 +f 1138//380 1206//452 1139//381 +f 1174//417 1212//453 1207//454 +f 1121//367 1208//450 1179//426 +f 1161//402 1214//455 1209//456 +f 1155//403 1210//457 1150//390 +f 1146//393 1211//458 1138//380 +f 1181//428 1217//459 1212//453 +f 1120//366 1213//460 1121//367 +f 1168//410 1218//461 1214//455 +f 1155//403 1219//462 1215//463 +f 1146//393 1129//372 1216//464 +f 1130//384 1217//459 1131//374 +f 1176//419 1193//436 1218//461 +f 1170//412 1219//462 1162//413 +f 1202//444 1221//465 1196//439 +f 1173//416 1222//466 1223//467 +f 1160//401 1224//468 1225//469 +f 1137//379 1227//470 1145//386 +f 1140//430 1125//369 1228//471 +f 1173//416 1229//472 1180//422 +f 1133//376 1231//473 1159//400 +f 1167//409 1225//469 1232//474 +f 1151//392 1227//470 1233//475 +f 1141//382 1228//471 1234//476 +f 1116//427 1229//472 1235//477 +f 1159//400 1236//478 1169//411 +f 1175//418 1232//474 1237//479 +f 1151//392 1238//480 1156//398 +f 1141//382 1187//481 1147//387 +f 1117//362 1235//477 1239//482 +f 1177//420 1236//478 1240//483 +f 1175//418 1241//484 1182//423 +f 1163//404 1238//480 1242//485 +f 1152//394 1187//481 1186//431 +f 1117//362 1243//486 1142//383 +f 1177//420 1244//487 1184//425 +f 1182//423 1245//488 1132//375 +f 1163//404 1246//489 1171//414 +f 1158//399 1186//431 1247//490 +f 1148//388 1243//486 1248//491 +f 1184//425 1249//492 1136//378 +f 1133//376 1245//488 1230//493 +f 1171//414 1124//494 1123//368 +f 1158//399 1222//466 1165//406 +f 1148//388 1224//468 1153//395 +f 1137//379 1249//492 1226//495 +f 1203//451 1251//496 1198//445 +f 1207//454 1220//497 1202//444 +f 1186//431 1253//498 1247//490 +f 1208//450 1250//499 1203//451 +f 1207//454 1255//500 1252//501 +f 1247//490 1256//502 1222//466 +f 1213//460 1254//503 1208//450 +f 1217//459 1255//500 1212//453 +f 1223//467 1256//502 1259//504 +f 1197//440 1257//505 1213//460 +f 1217//459 1261//506 1258//507 +f 1229//472 1259//504 1262//508 +f 1191//435 1263//509 1261//506 +f 1235//477 1262//508 1264//510 +f 1199//447 1263//509 1190//434 +f 1235//477 1266//511 1239//482 +f 1199//447 1267//512 1265//513 +f 1239//482 1268//514 1243//486 +f 1209//456 1267//512 1204//446 +f 1248//491 1268//514 1270//515 +f 1214//455 1269//516 1209//456 +f 1248//491 1272//517 1224//468 +f 1214//455 1273//518 1271//519 +f 1224//468 1274//520 1225//469 +f 1230//493 1276//521 1231//473 +f 1218//461 1277//522 1273//518 +f 1225//469 1278//523 1232//474 +f 1236//478 1276//521 1279//524 +f 1192//443 1277//522 1193//436 +f 1237//479 1278//523 1281//525 +f 1240//483 1279//524 1282//526 +f 1200//442 1280//527 1192//443 +f 1241//484 1281//525 1284//528 +f 1240//483 1285//529 1244//487 +f 1200//442 1286//530 1283//531 +f 1245//488 1284//528 1287//532 +f 1249//492 1285//529 1288//533 +f 1210//457 1286//530 1205//448 +f 1230//493 1287//532 1275//534 +f 1249//492 1290//535 1226//495 +f 1210//457 1291//536 1289//537 +f 1226//495 1292//538 1227//470 +f 1215//463 1293//539 1291//536 +f 1233//475 1292//538 1294//540 +f 1195//438 1293//539 1219//462 +f 1233//475 1296//541 1238//480 +f 1194//437 1295//542 1195//438 +f 1242//485 1296//541 1298//543 +f 1194//437 1299//544 1297//545 +f 1246//489 1298//543 1300//546 +f 1201//449 1301//547 1299//544 +f 1246//489 1302//548 1124//494 +f 1206//452 1303//549 1301//547 +f 1124//494 1304//550 1125//369 +f 1216//464 1303//549 1211//458 +f 1228//471 1304//550 1306//551 +f 1216//464 1307//552 1305//553 +f 1234//476 1306//551 1308//554 +f 1129//372 1309//555 1307//552 +f 1197//440 1221//465 1260//556 +f 1234//476 1188//432 1187//481 +f 1128//441 1251//496 1309//555 +f 287//301 288//304 289//302 +f 288//304 290//306 291//305 +f 290//306 292//308 293//307 +f 292//308 294//310 295//309 +f 294//310 296//312 297//311 +f 296//312 298//314 299//313 +f 298//314 300//316 301//315 +f 300//316 302//318 303//317 +f 302//318 304//320 305//319 +f 304//320 306//322 307//321 +f 306//322 308//324 309//323 +f 308//324 310//326 311//325 +f 310//326 312//328 313//327 +f 312//328 314//330 315//329 +f 314//330 316//332 317//331 +f 316//332 318//334 319//333 +f 318//334 320//336 321//335 +f 320//336 322//338 323//337 +f 322//338 324//340 325//339 +f 324//340 326//342 327//341 +f 326//342 328//344 329//343 +f 328//344 330//346 331//345 +f 330//346 332//348 333//347 +f 332//348 334//350 335//349 +f 334//350 336//352 337//351 +f 336//352 338//354 339//353 +f 338//354 340//356 341//355 +f 340//356 342//358 343//357 +f 342//358 344//360 345//359 +f 344//360 346//363 347//361 +f 330//346 348//427 349//362 +f 346//363 350//365 351//364 +f 350//365 287//301 286//303 +f 351//364 286//303 352//366 +f 355//368 356//494 357//369 +f 359//371 360//441 361//372 +f 299//313 362//384 363//374 +f 287//301 350//365 364//375 +f 317//331 366//385 367//377 +f 298//314 296//312 368//378 +f 333//347 335//349 370//380 +f 314//330 372//430 373//382 +f 332//348 349//362 374//383 +f 301//315 375//389 362//384 +f 319//333 376//391 366//385 +f 300//316 298//314 369//379 +f 337//351 378//393 370//380 +f 316//332 373//382 379//387 +f 334//350 374//383 380//388 +f 303//317 381//397 375//389 +f 319//333 321//335 382//390 +f 302//318 300//316 377//386 +f 339//353 358//373 378//393 +f 318//334 379//387 384//394 +f 338//354 336//352 380//388 +f 303//317 305//319 386//396 +f 323//337 387//403 382//390 +f 302//318 383//392 388//398 +f 289//302 389//408 352//366 +f 339//353 341//355 359//371 +f 322//338 320//336 384//394 +f 288//304 287//301 365//376 +f 338//354 385//395 392//401 +f 305//319 307//321 393//402 +f 325//339 394//413 387//403 +f 304//320 388//398 395//404 +f 341//355 343//357 396//405 +f 324//340 322//338 390//399 +f 289//302 291//305 398//407 +f 340//356 392//401 399//409 +f 307//321 309//323 400//410 +f 288//304 391//400 401//411 +f 325//339 327//341 402//412 +f 306//322 395//404 403//414 +f 343//357 345//359 404//415 +f 324//340 397//406 405//416 +f 291//305 293//307 406//417 +f 342//358 399//409 407//418 +f 309//323 311//325 408//419 +f 290//306 401//411 409//420 +f 327//341 329//343 410//421 +f 308//324 403//414 355//368 +f 347//361 411//426 404//415 +f 326//342 405//416 412//422 +f 295//309 413//428 406//417 +f 346//363 344//360 407//418 +f 311//325 313//327 415//424 +f 294//310 292//308 409//420 +f 331//345 417//429 410//421 +f 310//326 355//368 354//370 +f 347//361 351//364 353//367 +f 330//346 328//344 412//422 +f 297//311 363//374 413//428 +f 350//365 346//363 414//423 +f 315//329 367//377 415//424 +f 296//312 294//310 416//425 +f 331//345 333//347 371//381 +f 314//330 312//328 354//370 +f 418//431 419//481 420//432 +f 362//384 375//389 422//434 +f 367//377 424//443 425//436 +f 402//412 410//421 426//437 +f 352//366 389//408 428//439 +f 396//405 430//445 360//441 +f 381//397 431//447 422//434 +f 367//377 366//385 432//442 +f 417//429 433//449 426//437 +f 389//408 398//407 434//444 +f 404//415 435//451 430//445 +f 381//397 386//396 436//446 +f 366//385 376//391 437//448 +f 371//381 438//452 433//449 +f 406//417 439//454 434//444 +f 404//415 411//426 440//450 +f 393//402 441//456 436//446 +f 382//390 442//457 437//448 +f 370//380 443//458 438//452 +f 406//417 413//428 444//453 +f 353//367 445//460 440//450 +f 393//402 400//410 446//455 +f 387//403 447//463 442//457 +f 378//393 448//464 443//458 +f 413//428 363//374 449//459 +f 352//366 429//440 445//460 +f 400//410 408//419 450//461 +f 387//403 394//413 451//462 +f 378//393 358//373 361//372 +f 362//384 423//435 449//459 +f 408//419 415//424 425//436 +f 402//412 427//438 451//462 +f 434//444 452//497 453//465 +f 405//416 397//406 454//466 +f 392//401 385//395 456//468 +f 369//379 458//495 459//470 +f 372//430 354//370 357//369 +f 405//416 455//467 461//472 +f 365//376 462//493 463//473 +f 399//409 392//401 457//469 +f 383//392 377//386 459//470 +f 373//382 372//430 460//471 +f 348//427 412//422 461//472 +f 391//400 463//473 468//478 +f 407//418 399//409 464//474 +f 383//392 465//475 470//480 +f 373//382 466//476 419//481 +f 349//362 348//427 467//477 +f 409//420 401//411 468//478 +f 407//418 469//479 473//484 +f 395//404 388//398 470//480 +f 384//394 379//387 419//481 +f 349//362 471//482 475//486 +f 409//420 472//483 476//487 +f 414//423 473//484 477//488 +f 395//404 474//485 478//489 +f 390//399 384//394 418//431 +f 380//388 374//383 475//486 +f 416//425 476//487 481//492 +f 365//376 364//375 477//488 +f 403//414 478//489 356//494 +f 390//399 479//490 454//466 +f 380//388 480//491 456//468 +f 369//379 368//378 481//492 +f 435//451 482//499 483//496 +f 439//454 484//501 452//497 +f 418//431 421//433 485//498 +f 440//450 486//503 482//499 +f 439//454 444//453 487//500 +f 479//490 485//498 488//502 +f 445//460 489//505 486//503 +f 449//459 490//507 487//500 +f 455//467 454//466 488//502 +f 429//440 492//556 489//505 +f 449//459 423//435 493//506 +f 461//472 455//467 491//504 +f 423//435 422//434 495//509 +f 467//477 461//472 494//508 +f 431//447 497//513 495//509 +f 467//477 496//510 498//511 +f 431//447 436//446 499//512 +f 471//482 498//511 500//514 +f 441//456 501//516 499//512 +f 480//491 475//486 500//514 +f 446//455 503//519 501//516 +f 480//491 502//515 504//517 +f 446//455 450//461 505//518 +f 456//468 504//517 506//520 +f 462//493 507//534 508//521 +f 450//461 425//436 509//522 +f 457//469 506//520 510//523 +f 468//478 463//473 508//521 +f 424//443 512//527 509//522 +f 469//479 464//474 510//523 +f 472//483 468//478 511//524 +f 432//442 515//531 512//527 +f 473//484 469//479 513//525 +f 472//483 514//526 517//529 +f 432//442 437//448 518//530 +f 477//488 473//484 516//528 +f 481//492 476//487 517//529 +f 442//457 521//537 518//530 +f 462//493 477//488 519//532 +f 481//492 520//533 522//535 +f 442//457 447//463 523//536 +f 458//495 522//535 524//538 +f 447//463 451//462 525//539 +f 465//475 459//470 524//538 +f 427//438 527//542 525//539 +f 465//475 526//540 528//541 +f 426//437 529//545 527//542 +f 474//485 470//480 528//541 +f 426//437 433//449 531//544 +f 478//489 474//485 530//543 +f 433//449 438//452 533//547 +f 478//489 532//546 534//548 +f 438//452 443//458 535//549 +f 356//494 534//548 536//550 +f 448//464 537//553 535//549 +f 460//471 357//369 536//550 +f 448//464 361//372 539//552 +f 466//476 460//471 538//551 +f 361//372 360//441 541//555 +f 429//440 428//439 453//465 +f 466//476 540//554 420//432 +f 360//441 430//445 483//496 +f 543//301 544//304 545//302 +f 544//304 546//306 547//305 +f 546//306 548//308 549//307 +f 548//308 550//310 551//309 +f 550//310 552//312 553//311 +f 552//312 554//314 555//313 +f 554//314 556//316 557//315 +f 556//316 558//318 559//317 +f 558//318 560//320 561//319 +f 560//320 562//322 563//321 +f 562//322 564//324 565//323 +f 564//324 566//326 567//325 +f 566//326 568//328 569//327 +f 568//328 570//330 571//329 +f 570//330 572//332 573//331 +f 572//332 574//334 575//333 +f 574//334 576//336 577//335 +f 576//336 578//338 579//337 +f 578//338 580//340 581//339 +f 580//340 582//342 583//341 +f 582//342 584//344 585//343 +f 584//344 586//346 587//345 +f 586//346 588//348 589//347 +f 588//348 590//350 591//349 +f 590//350 592//352 593//351 +f 592//352 594//354 595//353 +f 594//354 596//356 597//355 +f 596//356 598//358 599//357 +f 598//358 600//360 601//359 +f 600//360 602//363 603//361 +f 586//346 604//427 605//362 +f 602//363 606//365 607//364 +f 606//365 543//301 542//303 +f 607//364 542//303 608//557 +f 611//368 612//494 613//558 +f 615//371 616//441 617//372 +f 553//311 555//313 618//384 +f 543//301 606//365 620//375 +f 573//331 622//385 623//377 +f 552//312 624//378 625//379 +f 589//347 591//349 626//380 +f 570//330 628//430 629//382 +f 588//348 605//362 630//383 +f 555//313 557//315 631//389 +f 575//333 632//391 622//385 +f 554//314 625//379 633//386 +f 593//351 634//393 626//380 +f 572//332 629//382 635//387 +f 592//352 590//350 630//383 +f 557//315 559//317 637//397 +f 575//333 577//335 638//390 +f 556//316 633//386 639//392 +f 593//351 595//353 614//373 +f 574//334 635//387 640//394 +f 592//352 636//388 641//395 +f 559//317 561//319 642//396 +f 579//337 643//403 638//390 +f 558//318 639//392 644//398 +f 545//302 645//408 608//557 +f 595//353 597//355 615//371 +f 578//338 576//336 640//394 +f 544//304 543//301 621//559 +f 594//354 641//395 648//401 +f 561//319 563//321 649//402 +f 581//339 650//413 643//403 +f 560//320 644//398 651//404 +f 597//355 599//357 652//405 +f 580//340 578//338 646//399 +f 547//305 654//407 645//408 +f 596//356 648//401 655//409 +f 563//321 565//323 656//410 +f 546//306 544//304 647//400 +f 581//339 583//341 658//412 +f 562//322 651//404 659//414 +f 599//357 601//359 660//415 +f 580//340 653//406 661//416 +f 547//305 549//307 662//417 +f 598//358 655//409 663//418 +f 565//323 567//325 664//419 +f 546//306 657//411 665//420 +f 583//341 585//343 666//421 +f 564//324 659//414 611//368 +f 603//361 667//426 660//415 +f 582//342 661//416 668//422 +f 551//309 669//428 662//417 +f 602//363 600//360 663//418 +f 567//325 569//327 671//424 +f 550//310 548//308 665//420 +f 587//345 673//429 666//421 +f 566//326 611//368 610//370 +f 607//364 609//367 667//426 +f 586//346 584//344 668//422 +f 553//311 619//374 669//428 +f 606//365 602//363 670//423 +f 569//327 571//329 623//377 +f 552//312 550//310 672//425 +f 587//345 589//347 627//381 +f 568//328 610//370 628//430 +f 674//431 675//481 676//432 +f 618//384 631//389 678//434 +f 623//377 680//443 681//560 +f 658//412 666//421 682//437 +f 608//557 645//408 684//439 +f 615//371 652//405 686//445 +f 637//397 687//447 678//434 +f 623//377 622//385 688//442 +f 673//429 689//449 682//437 +f 654//407 690//444 684//439 +f 660//415 691//451 686//445 +f 637//397 642//396 692//446 +f 622//385 632//391 693//448 +f 627//381 694//452 689//449 +f 654//407 662//417 695//454 +f 660//415 667//426 696//450 +f 649//402 697//456 692//446 +f 638//390 698//457 693//448 +f 626//380 699//458 694//452 +f 669//428 700//453 695//454 +f 609//367 701//460 696//450 +f 649//402 656//410 702//455 +f 643//403 703//463 698//457 +f 634//393 704//464 699//458 +f 619//374 705//459 700//453 +f 608//557 685//440 701//460 +f 656//410 664//419 706//461 +f 643//403 650//413 707//462 +f 634//393 614//373 617//372 +f 618//384 679//435 705//459 +f 664//419 671//424 681//560 +f 658//412 683//438 707//462 +f 690//444 708//497 709//465 +f 661//416 653//406 710//466 +f 648//401 641//395 712//468 +f 625//379 714//495 715//470 +f 628//430 610//370 613//558 +f 661//416 711//467 717//472 +f 621//559 718//493 719//473 +f 648//401 713//469 720//474 +f 639//392 633//386 715//470 +f 628//430 716//471 722//476 +f 604//427 668//422 717//472 +f 657//411 647//400 719//473 +f 663//418 655//409 720//474 +f 639//392 721//475 726//480 +f 635//387 629//382 722//476 +f 605//362 604//427 723//477 +f 657//411 724//478 728//483 +f 663//418 725//479 729//484 +f 651//404 644//398 726//480 +f 640//394 635//387 675//481 +f 605//362 727//482 731//486 +f 665//420 728//483 732//487 +f 670//423 729//484 733//488 +f 651//404 730//485 734//489 +f 646//399 640//394 674//431 +f 636//388 630//383 731//486 +f 624//378 672//425 732//487 +f 621//559 620//375 733//488 +f 659//414 734//489 612//494 +f 646//399 735//490 710//466 +f 636//388 736//491 712//468 +f 625//379 624//378 737//492 +f 691//451 738//499 739//496 +f 695//454 740//501 708//497 +f 674//431 677//433 741//498 +f 696//450 742//503 738//499 +f 695//454 700//453 743//500 +f 735//490 741//498 744//502 +f 696//450 701//460 745//505 +f 705//459 746//507 743//500 +f 711//467 710//466 744//502 +f 685//440 748//556 745//505 +f 705//459 679//435 749//506 +f 717//472 711//467 747//504 +f 679//435 678//434 751//509 +f 723//477 717//472 750//508 +f 687//447 753//513 751//509 +f 727//482 723//477 752//510 +f 687//447 692//446 755//512 +f 727//482 754//511 756//514 +f 697//456 757//516 755//512 +f 736//491 731//486 756//514 +f 702//455 759//519 757//516 +f 736//491 758//515 760//517 +f 702//455 706//461 761//518 +f 713//469 712//468 760//517 +f 718//493 763//534 764//521 +f 706//461 681//560 765//522 +f 720//474 713//469 762//520 +f 724//478 719//473 764//521 +f 680//443 768//527 765//522 +f 725//479 720//474 766//523 +f 728//483 724//478 767//524 +f 688//442 771//531 768//527 +f 729//484 725//479 769//525 +f 728//483 770//526 773//529 +f 688//442 693//448 774//530 +f 729//484 772//528 775//532 +f 737//492 732//487 773//529 +f 698//457 777//537 774//530 +f 718//493 733//488 775//532 +f 737//492 776//533 778//535 +f 698//457 703//463 779//536 +f 714//495 778//535 780//538 +f 703//463 707//462 781//539 +f 721//475 715//470 780//538 +f 683//438 783//542 781//539 +f 721//475 782//540 784//541 +f 682//437 785//545 783//542 +f 730//485 726//480 784//541 +f 682//437 689//449 787//544 +f 734//489 730//485 786//543 +f 689//449 694//452 789//547 +f 734//489 788//546 790//548 +f 694//452 699//458 791//549 +f 612//494 790//548 792//550 +f 704//464 793//553 791//549 +f 716//471 613//558 792//550 +f 704//464 617//372 795//552 +f 716//471 794//551 796//554 +f 616//441 797//555 795//552 +f 685//440 684//439 709//465 +f 722//476 796//554 676//432 +f 686//445 739//496 797//555 +f 799//301 800//304 801//302 +f 800//304 802//306 803//305 +f 802//306 804//308 805//307 +f 804//308 806//310 807//309 +f 806//310 808//312 809//311 +f 808//312 810//314 811//313 +f 810//314 812//316 813//315 +f 812//316 814//318 815//317 +f 814//318 816//320 817//319 +f 816//320 818//322 819//321 +f 818//322 820//324 821//323 +f 820//324 822//326 823//325 +f 822//326 824//328 825//327 +f 824//328 826//330 827//329 +f 826//330 828//332 829//331 +f 828//332 830//334 831//333 +f 830//334 832//336 833//335 +f 832//336 834//338 835//337 +f 834//338 836//340 837//339 +f 836//340 838//342 839//341 +f 838//342 840//344 841//343 +f 840//344 842//346 843//345 +f 842//346 844//348 845//347 +f 844//348 846//350 847//349 +f 846//350 848//352 849//351 +f 848//352 850//354 851//353 +f 850//354 852//356 853//355 +f 852//356 854//358 855//357 +f 854//358 856//360 857//359 +f 856//360 858//363 859//361 +f 842//346 860//427 861//362 +f 858//363 862//365 863//364 +f 862//365 799//301 798//303 +f 863//364 798//303 864//557 +f 867//368 868//494 869//558 +f 871//371 872//441 873//372 +f 809//311 811//313 874//384 +f 799//301 862//365 876//375 +f 829//331 878//385 879//377 +f 808//312 880//378 881//379 +f 845//347 847//349 882//380 +f 826//330 884//430 885//382 +f 844//348 861//362 886//383 +f 811//313 813//315 887//389 +f 831//333 888//391 878//385 +f 810//314 881//379 889//386 +f 849//351 890//393 882//380 +f 828//332 885//382 891//387 +f 848//352 846//350 886//383 +f 813//315 815//317 893//397 +f 831//333 833//335 894//390 +f 812//316 889//386 895//392 +f 849//351 851//353 870//373 +f 830//334 891//387 896//394 +f 848//352 892//388 897//395 +f 815//317 817//319 898//396 +f 835//337 899//403 894//390 +f 814//318 895//392 900//398 +f 801//302 901//408 864//557 +f 851//353 853//355 871//371 +f 834//338 832//336 896//394 +f 800//304 799//301 877//559 +f 850//354 897//395 904//401 +f 817//319 819//321 905//402 +f 837//339 906//413 899//403 +f 816//320 900//398 907//404 +f 853//355 855//357 908//405 +f 836//340 834//338 902//399 +f 803//305 910//407 901//408 +f 852//356 904//401 911//409 +f 819//321 821//323 912//410 +f 802//306 800//304 903//400 +f 837//339 839//341 914//412 +f 818//322 907//404 915//414 +f 855//357 857//359 916//415 +f 836//340 909//406 917//416 +f 803//305 805//307 918//417 +f 854//358 911//409 919//418 +f 821//323 823//325 920//419 +f 802//306 913//411 921//420 +f 839//341 841//343 922//421 +f 820//324 915//414 867//368 +f 859//361 923//426 916//415 +f 838//342 917//416 924//422 +f 807//309 925//428 918//417 +f 858//363 856//360 919//418 +f 823//325 825//327 927//424 +f 806//310 804//308 921//420 +f 843//345 929//429 922//421 +f 822//326 867//368 866//370 +f 863//364 865//367 923//426 +f 842//346 840//344 924//422 +f 809//311 875//374 925//428 +f 862//365 858//363 926//423 +f 825//327 827//329 879//377 +f 808//312 806//310 928//425 +f 843//345 845//347 883//381 +f 824//328 866//370 884//430 +f 930//431 931//481 932//432 +f 874//384 887//389 934//434 +f 879//377 936//443 937//560 +f 914//412 922//421 938//437 +f 864//557 901//408 940//439 +f 871//371 908//405 942//445 +f 893//397 943//447 934//434 +f 879//377 878//385 944//442 +f 929//429 945//449 938//437 +f 910//407 946//444 940//439 +f 916//415 947//451 942//445 +f 893//397 898//396 948//446 +f 878//385 888//391 949//448 +f 883//381 950//452 945//449 +f 910//407 918//417 951//454 +f 916//415 923//426 952//450 +f 905//402 953//456 948//446 +f 894//390 954//457 949//448 +f 882//380 955//458 950//452 +f 925//428 956//453 951//454 +f 865//367 957//460 952//450 +f 905//402 912//410 958//455 +f 899//403 959//463 954//457 +f 890//393 960//464 955//458 +f 875//374 961//459 956//453 +f 864//557 941//440 957//460 +f 912//410 920//419 962//461 +f 899//403 906//413 963//462 +f 890//393 870//373 873//372 +f 874//384 935//435 961//459 +f 920//419 927//424 937//560 +f 914//412 939//438 963//462 +f 946//444 964//497 965//465 +f 917//416 909//406 966//466 +f 904//401 897//395 968//468 +f 881//379 970//495 971//470 +f 884//430 866//370 869//558 +f 917//416 967//467 973//472 +f 877//559 974//493 975//473 +f 904//401 969//469 976//474 +f 895//392 889//386 971//470 +f 884//430 972//471 978//476 +f 860//427 924//422 973//472 +f 913//411 903//400 975//473 +f 919//418 911//409 976//474 +f 895//392 977//475 982//480 +f 891//387 885//382 978//476 +f 861//362 860//427 979//477 +f 913//411 980//478 984//483 +f 919//418 981//479 985//484 +f 907//404 900//398 982//480 +f 896//394 891//387 931//481 +f 861//362 983//482 987//486 +f 921//420 984//483 988//487 +f 926//423 985//484 989//488 +f 907//404 986//485 990//489 +f 902//399 896//394 930//431 +f 892//388 886//383 987//486 +f 880//378 928//425 988//487 +f 877//559 876//375 989//488 +f 915//414 990//489 868//494 +f 902//399 991//490 966//466 +f 892//388 992//491 968//468 +f 881//379 880//378 993//492 +f 947//451 994//499 995//496 +f 951//454 996//501 964//497 +f 930//431 933//433 997//498 +f 952//450 998//503 994//499 +f 951//454 956//453 999//500 +f 991//490 997//498 1000//502 +f 952//450 957//460 1001//505 +f 961//459 1002//507 999//500 +f 967//467 966//466 1000//502 +f 941//440 1004//556 1001//505 +f 961//459 935//435 1005//506 +f 973//472 967//467 1003//504 +f 935//435 934//434 1007//509 +f 979//477 973//472 1006//508 +f 943//447 1009//513 1007//509 +f 983//482 979//477 1008//510 +f 943//447 948//446 1011//512 +f 983//482 1010//511 1012//514 +f 953//456 1013//516 1011//512 +f 992//491 987//486 1012//514 +f 958//455 1015//519 1013//516 +f 992//491 1014//515 1016//517 +f 958//455 962//461 1017//518 +f 969//469 968//468 1016//517 +f 974//493 1019//534 1020//521 +f 962//461 937//560 1021//522 +f 976//474 969//469 1018//520 +f 980//478 975//473 1020//521 +f 936//443 1024//527 1021//522 +f 981//479 976//474 1022//523 +f 984//483 980//478 1023//524 +f 944//442 1027//531 1024//527 +f 985//484 981//479 1025//525 +f 984//483 1026//526 1029//529 +f 944//442 949//448 1030//530 +f 985//484 1028//528 1031//532 +f 993//492 988//487 1029//529 +f 954//457 1033//537 1030//530 +f 974//493 989//488 1031//532 +f 993//492 1032//533 1034//535 +f 954//457 959//463 1035//536 +f 970//495 1034//535 1036//538 +f 959//463 963//462 1037//539 +f 977//475 971//470 1036//538 +f 939//438 1039//542 1037//539 +f 977//475 1038//540 1040//541 +f 938//437 1041//545 1039//542 +f 986//485 982//480 1040//541 +f 938//437 945//449 1043//544 +f 990//489 986//485 1042//543 +f 945//449 950//452 1045//547 +f 990//489 1044//546 1046//548 +f 950//452 955//458 1047//549 +f 868//494 1046//548 1048//550 +f 960//464 1049//553 1047//549 +f 972//471 869//558 1048//550 +f 960//464 873//372 1051//552 +f 972//471 1050//551 1052//561 +f 872//441 1053//555 1051//552 +f 941//440 940//439 965//465 +f 978//476 1052//561 932//432 +f 942//445 995//496 1053//555 +f 1055//301 1056//304 1057//302 +f 1056//304 1058//306 1059//305 +f 1058//306 1060//308 1061//307 +f 1060//308 1062//310 1063//309 +f 1062//310 1064//312 1065//311 +f 1064//312 1066//314 1067//313 +f 1066//314 1068//316 1069//315 +f 1068//316 1070//318 1071//317 +f 1070//318 1072//320 1073//319 +f 1072//320 1074//322 1075//321 +f 1074//322 1076//324 1077//323 +f 1076//324 1078//326 1079//325 +f 1078//326 1080//328 1081//327 +f 1080//328 1082//330 1083//329 +f 1082//330 1084//332 1085//331 +f 1084//332 1086//334 1087//333 +f 1086//334 1088//336 1089//335 +f 1088//336 1090//338 1091//337 +f 1090//338 1092//340 1093//339 +f 1092//340 1094//342 1095//341 +f 1094//342 1096//344 1097//343 +f 1096//344 1098//346 1099//345 +f 1098//346 1100//348 1101//347 +f 1100//348 1102//350 1103//349 +f 1102//350 1104//352 1105//351 +f 1104//352 1106//354 1107//353 +f 1106//354 1108//356 1109//355 +f 1108//356 1110//358 1111//357 +f 1110//358 1112//360 1113//359 +f 1112//360 1114//363 1115//361 +f 1098//346 1116//427 1117//362 +f 1114//363 1118//365 1119//364 +f 1118//365 1055//301 1054//303 +f 1119//364 1054//303 1120//366 +f 1123//368 1124//494 1125//369 +f 1127//371 1128//441 1129//372 +f 1067//313 1130//384 1131//374 +f 1055//301 1118//365 1132//375 +f 1085//331 1134//385 1135//377 +f 1066//314 1064//312 1136//378 +f 1101//347 1103//349 1138//380 +f 1082//330 1140//430 1141//382 +f 1100//348 1117//362 1142//383 +f 1069//315 1143//389 1130//384 +f 1087//333 1144//391 1134//385 +f 1068//316 1066//314 1137//379 +f 1105//351 1146//393 1138//380 +f 1084//332 1141//382 1147//387 +f 1104//352 1102//350 1142//383 +f 1071//317 1149//397 1143//389 +f 1087//333 1089//335 1150//390 +f 1070//318 1068//316 1145//386 +f 1107//353 1126//373 1146//393 +f 1086//334 1147//387 1152//394 +f 1106//354 1104//352 1148//388 +f 1071//317 1073//319 1154//396 +f 1091//337 1155//403 1150//390 +f 1070//318 1151//392 1156//398 +f 1057//302 1157//408 1120//366 +f 1107//353 1109//355 1127//371 +f 1090//338 1088//336 1152//394 +f 1056//304 1055//301 1133//376 +f 1106//354 1153//395 1160//401 +f 1073//319 1075//321 1161//402 +f 1093//339 1162//413 1155//403 +f 1072//320 1156//398 1163//404 +f 1109//355 1111//357 1164//405 +f 1092//340 1090//338 1158//399 +f 1057//302 1059//305 1166//407 +f 1108//356 1160//401 1167//409 +f 1075//321 1077//323 1168//410 +f 1056//304 1159//400 1169//411 +f 1093//339 1095//341 1170//412 +f 1074//322 1163//404 1171//414 +f 1111//357 1113//359 1172//415 +f 1092//340 1165//406 1173//416 +f 1059//305 1061//307 1174//417 +f 1110//358 1167//409 1175//418 +f 1077//323 1079//325 1176//419 +f 1058//306 1169//411 1177//420 +f 1095//341 1097//343 1178//421 +f 1076//324 1171//414 1123//368 +f 1115//361 1179//426 1172//415 +f 1094//342 1173//416 1180//422 +f 1063//309 1181//428 1174//417 +f 1114//363 1112//360 1175//418 +f 1079//325 1081//327 1183//424 +f 1062//310 1060//308 1177//420 +f 1099//345 1185//429 1178//421 +f 1078//326 1123//368 1122//370 +f 1115//361 1119//364 1121//367 +f 1098//346 1096//344 1180//422 +f 1065//311 1131//374 1181//428 +f 1118//365 1114//363 1182//423 +f 1083//329 1135//377 1183//424 +f 1064//312 1062//310 1184//425 +f 1099//345 1101//347 1139//381 +f 1082//330 1080//328 1122//370 +f 1186//431 1187//481 1188//432 +f 1130//384 1143//389 1190//434 +f 1135//377 1192//443 1193//436 +f 1170//412 1178//421 1194//437 +f 1120//366 1157//408 1196//439 +f 1164//405 1198//445 1128//441 +f 1149//397 1199//447 1190//434 +f 1135//377 1134//385 1200//442 +f 1185//429 1201//449 1194//437 +f 1157//408 1166//407 1202//444 +f 1172//415 1203//451 1198//445 +f 1149//397 1154//396 1204//446 +f 1134//385 1144//391 1205//448 +f 1139//381 1206//452 1201//449 +f 1174//417 1207//454 1202//444 +f 1172//415 1179//426 1208//450 +f 1161//402 1209//456 1204//446 +f 1150//390 1210//457 1205//448 +f 1138//380 1211//458 1206//452 +f 1174//417 1181//428 1212//453 +f 1121//367 1213//460 1208//450 +f 1161//402 1168//410 1214//455 +f 1155//403 1215//463 1210//457 +f 1146//393 1216//464 1211//458 +f 1181//428 1131//374 1217//459 +f 1120//366 1197//440 1213//460 +f 1168//410 1176//419 1218//461 +f 1155//403 1162//413 1219//462 +f 1146//393 1126//373 1129//372 +f 1130//384 1191//435 1217//459 +f 1176//419 1183//424 1193//436 +f 1170//412 1195//438 1219//462 +f 1202//444 1220//497 1221//465 +f 1173//416 1165//406 1222//466 +f 1160//401 1153//395 1224//468 +f 1137//379 1226//495 1227//470 +f 1140//430 1122//370 1125//369 +f 1173//416 1223//467 1229//472 +f 1133//376 1230//493 1231//473 +f 1167//409 1160//401 1225//469 +f 1151//392 1145//386 1227//470 +f 1141//382 1140//430 1228//471 +f 1116//427 1180//422 1229//472 +f 1159//400 1231//473 1236//478 +f 1175//418 1167//409 1232//474 +f 1151//392 1233//475 1238//480 +f 1141//382 1234//476 1187//481 +f 1117//362 1116//427 1235//477 +f 1177//420 1169//411 1236//478 +f 1175//418 1237//479 1241//484 +f 1163//404 1156//398 1238//480 +f 1152//394 1147//387 1187//481 +f 1117//362 1239//482 1243//486 +f 1177//420 1240//483 1244//487 +f 1182//423 1241//484 1245//488 +f 1163//404 1242//485 1246//489 +f 1158//399 1152//394 1186//431 +f 1148//388 1142//383 1243//486 +f 1184//425 1244//487 1249//492 +f 1133//376 1132//375 1245//488 +f 1171//414 1246//489 1124//494 +f 1158//399 1247//490 1222//466 +f 1148//388 1248//491 1224//468 +f 1137//379 1136//378 1249//492 +f 1203//451 1250//499 1251//496 +f 1207//454 1252//501 1220//497 +f 1186//431 1189//433 1253//498 +f 1208//450 1254//503 1250//499 +f 1207//454 1212//453 1255//500 +f 1247//490 1253//498 1256//502 +f 1213//460 1257//505 1254//503 +f 1217//459 1258//507 1255//500 +f 1223//467 1222//466 1256//502 +f 1197//440 1260//556 1257//505 +f 1217//459 1191//435 1261//506 +f 1229//472 1223//467 1259//504 +f 1191//435 1190//434 1263//509 +f 1235//477 1229//472 1262//508 +f 1199//447 1265//513 1263//509 +f 1235//477 1264//510 1266//511 +f 1199//447 1204//446 1267//512 +f 1239//482 1266//511 1268//514 +f 1209//456 1269//516 1267//512 +f 1248//491 1243//486 1268//514 +f 1214//455 1271//519 1269//516 +f 1248//491 1270//515 1272//517 +f 1214//455 1218//461 1273//518 +f 1224//468 1272//517 1274//520 +f 1230//493 1275//534 1276//521 +f 1218//461 1193//436 1277//522 +f 1225//469 1274//520 1278//523 +f 1236//478 1231//473 1276//521 +f 1192//443 1280//527 1277//522 +f 1237//479 1232//474 1278//523 +f 1240//483 1236//478 1279//524 +f 1200//442 1283//531 1280//527 +f 1241//484 1237//479 1281//525 +f 1240//483 1282//526 1285//529 +f 1200//442 1205//448 1286//530 +f 1245//488 1241//484 1284//528 +f 1249//492 1244//487 1285//529 +f 1210//457 1289//537 1286//530 +f 1230//493 1245//488 1287//532 +f 1249//492 1288//533 1290//535 +f 1210//457 1215//463 1291//536 +f 1226//495 1290//535 1292//538 +f 1215//463 1219//462 1293//539 +f 1233//475 1227//470 1292//538 +f 1195//438 1295//542 1293//539 +f 1233//475 1294//540 1296//541 +f 1194//437 1297//545 1295//542 +f 1242//485 1238//480 1296//541 +f 1194//437 1201//449 1299//544 +f 1246//489 1242//485 1298//543 +f 1201//449 1206//452 1301//547 +f 1246//489 1300//546 1302//548 +f 1206//452 1211//458 1303//549 +f 1124//494 1302//548 1304//550 +f 1216//464 1305//553 1303//549 +f 1228//471 1125//369 1304//550 +f 1216//464 1129//372 1307//552 +f 1234//476 1228//471 1306//551 +f 1129//372 1128//441 1309//555 +f 1197//440 1196//439 1221//465 +f 1234//476 1308//554 1188//432 +f 1128//441 1198//445 1251//496 +usemtl Wheels +s off +f 1337//12 1341//12 1325//12 +f 1357//11 1365//11 1373//11 +f 1401//12 1405//12 1389//12 +f 1421//11 1429//11 1437//11 +f 1465//12 1469//12 1453//12 +f 1485//11 1493//11 1501//11 +f 1529//12 1533//12 1517//12 +f 1549//11 1557//11 1565//11 +f 1341//12 1310//12 1311//12 +f 1311//12 1312//12 1341//12 +f 1312//12 1313//12 1341//12 +f 1313//12 1314//12 1315//12 +f 1315//12 1316//12 1313//12 +f 1316//12 1317//12 1313//12 +f 1317//12 1318//12 1321//12 +f 1318//12 1319//12 1321//12 +f 1319//12 1320//12 1321//12 +f 1321//12 1322//12 1325//12 +f 1322//12 1323//12 1325//12 +f 1323//12 1324//12 1325//12 +f 1325//12 1326//12 1327//12 +f 1327//12 1328//12 1329//12 +f 1329//12 1330//12 1333//12 +f 1330//12 1331//12 1333//12 +f 1331//12 1332//12 1333//12 +f 1333//12 1334//12 1335//12 +f 1335//12 1336//12 1337//12 +f 1337//12 1338//12 1339//12 +f 1339//12 1340//12 1341//12 +f 1325//12 1327//12 1333//12 +f 1327//12 1329//12 1333//12 +f 1333//12 1335//12 1337//12 +f 1337//12 1339//12 1341//12 +f 1341//12 1313//12 1325//12 +f 1313//12 1317//12 1325//12 +f 1317//12 1321//12 1325//12 +f 1333//12 1337//12 1325//12 +f 1373//11 1342//11 1343//11 +f 1343//11 1344//11 1345//11 +f 1345//11 1346//11 1347//11 +f 1347//11 1348//11 1345//11 +f 1348//11 1349//11 1345//11 +f 1349//11 1350//11 1351//11 +f 1351//11 1352//11 1349//11 +f 1352//11 1353//11 1349//11 +f 1353//11 1354//11 1355//11 +f 1355//11 1356//11 1357//11 +f 1357//11 1358//11 1361//11 +f 1358//11 1359//11 1361//11 +f 1359//11 1360//11 1361//11 +f 1361//11 1362//11 1363//11 +f 1363//11 1364//11 1365//11 +f 1365//11 1366//11 1367//11 +f 1367//11 1368//11 1369//11 +f 1369//11 1370//11 1373//11 +f 1370//11 1371//11 1373//11 +f 1371//11 1372//11 1373//11 +f 1373//11 1343//11 1349//11 +f 1343//11 1345//11 1349//11 +f 1353//11 1355//11 1349//11 +f 1355//11 1357//11 1349//11 +f 1361//11 1363//11 1357//11 +f 1363//11 1365//11 1357//11 +f 1365//11 1367//11 1373//11 +f 1367//11 1369//11 1373//11 +f 1373//11 1349//11 1357//11 +f 1405//12 1374//12 1375//12 +f 1375//12 1376//12 1405//12 +f 1376//12 1377//12 1405//12 +f 1377//12 1378//12 1379//12 +f 1379//12 1380//12 1377//12 +f 1380//12 1381//12 1377//12 +f 1381//12 1382//12 1385//12 +f 1382//12 1383//12 1385//12 +f 1383//12 1384//12 1385//12 +f 1385//12 1386//12 1389//12 +f 1386//12 1387//12 1389//12 +f 1387//12 1388//12 1389//12 +f 1389//12 1390//12 1391//12 +f 1391//12 1392//12 1393//12 +f 1393//12 1394//12 1397//12 +f 1394//12 1395//12 1397//12 +f 1395//12 1396//12 1397//12 +f 1397//12 1398//12 1399//12 +f 1399//12 1400//12 1401//12 +f 1401//12 1402//12 1403//12 +f 1403//12 1404//12 1405//12 +f 1389//12 1391//12 1397//12 +f 1391//12 1393//12 1397//12 +f 1397//12 1399//12 1401//12 +f 1401//12 1403//12 1405//12 +f 1405//12 1377//12 1381//12 +f 1381//12 1385//12 1405//12 +f 1385//12 1389//12 1405//12 +f 1397//12 1401//12 1389//12 +f 1437//11 1406//11 1407//11 +f 1407//11 1408//11 1409//11 +f 1409//11 1410//11 1411//11 +f 1411//11 1412//11 1409//11 +f 1412//11 1413//11 1409//11 +f 1413//11 1414//11 1415//11 +f 1415//11 1416//11 1413//11 +f 1416//11 1417//11 1413//11 +f 1417//11 1418//11 1419//11 +f 1419//11 1420//11 1421//11 +f 1421//11 1422//11 1425//11 +f 1422//11 1423//11 1425//11 +f 1423//11 1424//11 1425//11 +f 1425//11 1426//11 1427//11 +f 1427//11 1428//11 1429//11 +f 1429//11 1430//11 1431//11 +f 1431//11 1432//11 1433//11 +f 1433//11 1434//11 1437//11 +f 1434//11 1435//11 1437//11 +f 1435//11 1436//11 1437//11 +f 1437//11 1407//11 1413//11 +f 1407//11 1409//11 1413//11 +f 1417//11 1419//11 1413//11 +f 1419//11 1421//11 1413//11 +f 1425//11 1427//11 1421//11 +f 1427//11 1429//11 1421//11 +f 1429//11 1431//11 1437//11 +f 1431//11 1433//11 1437//11 +f 1437//11 1413//11 1421//11 +f 1469//12 1438//12 1439//12 +f 1439//12 1440//12 1469//12 +f 1440//12 1441//12 1469//12 +f 1441//12 1442//12 1443//12 +f 1443//12 1444//12 1441//12 +f 1444//12 1445//12 1441//12 +f 1445//12 1446//12 1449//12 +f 1446//12 1447//12 1449//12 +f 1447//12 1448//12 1449//12 +f 1449//12 1450//12 1453//12 +f 1450//12 1451//12 1453//12 +f 1451//12 1452//12 1453//12 +f 1453//12 1454//12 1455//12 +f 1455//12 1456//12 1457//12 +f 1457//12 1458//12 1461//12 +f 1458//12 1459//12 1461//12 +f 1459//12 1460//12 1461//12 +f 1461//12 1462//12 1463//12 +f 1463//12 1464//12 1465//12 +f 1465//12 1466//12 1467//12 +f 1467//12 1468//12 1469//12 +f 1453//12 1455//12 1461//12 +f 1455//12 1457//12 1461//12 +f 1461//12 1463//12 1465//12 +f 1465//12 1467//12 1469//12 +f 1469//12 1441//12 1445//12 +f 1445//12 1449//12 1469//12 +f 1449//12 1453//12 1469//12 +f 1461//12 1465//12 1453//12 +f 1501//11 1470//11 1471//11 +f 1471//11 1472//11 1473//11 +f 1473//11 1474//11 1475//11 +f 1475//11 1476//11 1473//11 +f 1476//11 1477//11 1473//11 +f 1477//11 1478//11 1479//11 +f 1479//11 1480//11 1477//11 +f 1480//11 1481//11 1477//11 +f 1481//11 1482//11 1483//11 +f 1483//11 1484//11 1485//11 +f 1485//11 1486//11 1489//11 +f 1486//11 1487//11 1489//11 +f 1487//11 1488//11 1489//11 +f 1489//11 1490//11 1491//11 +f 1491//11 1492//11 1493//11 +f 1493//11 1494//11 1495//11 +f 1495//11 1496//11 1497//11 +f 1497//11 1498//11 1499//11 +f 1499//11 1500//11 1501//11 +f 1501//11 1471//11 1477//11 +f 1471//11 1473//11 1477//11 +f 1481//11 1483//11 1477//11 +f 1483//11 1485//11 1477//11 +f 1489//11 1491//11 1485//11 +f 1491//11 1493//11 1485//11 +f 1493//11 1495//11 1497//11 +f 1497//11 1499//11 1493//11 +f 1499//11 1501//11 1493//11 +f 1501//11 1477//11 1485//11 +f 1533//12 1502//12 1503//12 +f 1503//12 1504//12 1533//12 +f 1504//12 1505//12 1533//12 +f 1505//12 1506//12 1507//12 +f 1507//12 1508//12 1505//12 +f 1508//12 1509//12 1505//12 +f 1509//12 1510//12 1513//12 +f 1510//12 1511//12 1513//12 +f 1511//12 1512//12 1513//12 +f 1513//12 1514//12 1517//12 +f 1514//12 1515//12 1517//12 +f 1515//12 1516//12 1517//12 +f 1517//12 1518//12 1519//12 +f 1519//12 1520//12 1521//12 +f 1521//12 1522//12 1525//12 +f 1522//12 1523//12 1525//12 +f 1523//12 1524//12 1525//12 +f 1525//12 1526//12 1527//12 +f 1527//12 1528//12 1529//12 +f 1529//12 1530//12 1531//12 +f 1531//12 1532//12 1533//12 +f 1517//12 1519//12 1525//12 +f 1519//12 1521//12 1525//12 +f 1525//12 1527//12 1529//12 +f 1529//12 1531//12 1533//12 +f 1533//12 1505//12 1517//12 +f 1505//12 1509//12 1517//12 +f 1509//12 1513//12 1517//12 +f 1525//12 1529//12 1517//12 +f 1565//11 1534//11 1535//11 +f 1535//11 1536//11 1537//11 +f 1537//11 1538//11 1539//11 +f 1539//11 1540//11 1537//11 +f 1540//11 1541//11 1537//11 +f 1541//11 1542//11 1543//11 +f 1543//11 1544//11 1541//11 +f 1544//11 1545//11 1541//11 +f 1545//11 1546//11 1547//11 +f 1547//11 1548//11 1549//11 +f 1549//11 1550//11 1553//11 +f 1550//11 1551//11 1553//11 +f 1551//11 1552//11 1553//11 +f 1553//11 1554//11 1555//11 +f 1555//11 1556//11 1557//11 +f 1557//11 1558//11 1559//11 +f 1559//11 1560//11 1561//11 +f 1561//11 1562//11 1563//11 +f 1563//11 1564//11 1565//11 +f 1565//11 1535//11 1541//11 +f 1535//11 1537//11 1541//11 +f 1545//11 1547//11 1541//11 +f 1547//11 1549//11 1541//11 +f 1553//11 1555//11 1549//11 +f 1555//11 1557//11 1549//11 +f 1557//11 1559//11 1561//11 +f 1561//11 1563//11 1557//11 +f 1563//11 1565//11 1557//11 +f 1565//11 1541//11 1549//11 diff --git a/bin/render/cubes.mtl b/bin/render/cubes.mtl new file mode 100644 index 0000000..c1c63d1 --- /dev/null +++ b/bin/render/cubes.mtl @@ -0,0 +1,32 @@ +# Blender 3.6.0 MTL File: 'None' +# www.blender.org + +newmtl t0.001 +Ns 250.000000 +Ka 1.000000 1.000000 1.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 +map_Kd tex1.png + +newmtl t1.001 +Ns 250.000000 +Ka 1.000000 1.000000 1.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 +map_Kd tex2.png + +newmtl t1.002 +Ns 250.000000 +Ka 1.000000 1.000000 1.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 +map_Kd monkey.png diff --git a/bin/render/cubes.obj b/bin/render/cubes.obj new file mode 100644 index 0000000..1a5cb31 --- /dev/null +++ b/bin/render/cubes.obj @@ -0,0 +1,132 @@ +# Blender 3.6.0 +# www.blender.org +mtllib cubes.mtl +o Cube.002 +v -1.000000 1.000000 -2.836600 +v 1.000000 1.000000 -0.836600 +v 1.000000 1.000000 -2.836600 +v -1.000000 -1.000000 -0.836600 +v 1.000000 -1.000000 -0.836600 +v -1.000000 1.000000 -0.836600 +v -1.000000 -1.000000 -2.836600 +v 1.000000 -1.000000 -2.836600 +vn -0.0000 1.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -1.0000 -0.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vt 0.875000 0.500000 +vt 0.625000 0.750000 +vt 0.625000 0.500000 +vt 0.375000 1.000000 +vt 0.375000 0.750000 +vt 0.625000 0.000000 +vt 0.375000 0.250000 +vt 0.375000 0.000000 +vt 0.375000 0.500000 +vt 0.125000 0.750000 +vt 0.125000 0.500000 +vt 0.625000 0.250000 +vt 0.875000 0.750000 +vt 0.625000 1.000000 +s 1 +usemtl t0.001 +f 1/1/1 2/2/1 3/3/1 +f 2/2/2 4/4/2 5/5/2 +f 6/6/3 7/7/3 4/8/3 +f 8/9/4 4/10/4 7/11/4 +f 3/3/5 5/5/5 8/9/5 +f 1/12/6 8/9/6 7/7/6 +f 1/1/1 6/13/1 2/2/1 +f 2/2/2 6/14/2 4/4/2 +f 6/6/3 1/12/3 7/7/3 +f 8/9/4 5/5/4 4/10/4 +f 3/3/5 2/2/5 5/5/5 +f 1/12/6 3/3/6 8/9/6 +o Cube.003 +v -1.000000 1.000000 -0.441804 +v 1.000000 1.000000 1.558196 +v 1.000000 1.000000 -0.441804 +v -1.000000 -1.000000 1.558196 +v 1.000000 -1.000000 1.558196 +v -1.000000 1.000000 1.558196 +v -1.000000 -1.000000 -0.441804 +v 1.000000 -1.000000 -0.441804 +vn -0.0000 1.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -1.0000 -0.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vt 0.875000 0.500000 +vt 0.625000 0.750000 +vt 0.625000 0.500000 +vt 0.375000 1.000000 +vt 0.375000 0.750000 +vt 0.625000 0.000000 +vt 0.375000 0.250000 +vt 0.375000 0.000000 +vt 0.375000 0.500000 +vt 0.125000 0.750000 +vt 0.125000 0.500000 +vt 0.625000 0.250000 +vt 0.875000 0.750000 +vt 0.625000 1.000000 +s 1 +usemtl t1.001 +f 9/15/7 10/16/7 11/17/7 +f 10/16/8 12/18/8 13/19/8 +f 14/20/9 15/21/9 12/22/9 +f 16/23/10 12/24/10 15/25/10 +f 11/17/11 13/19/11 16/23/11 +f 9/26/12 16/23/12 15/21/12 +f 9/15/7 14/27/7 10/16/7 +f 10/16/8 14/28/8 12/18/8 +f 14/20/9 9/26/9 15/21/9 +f 16/23/10 13/19/10 12/24/10 +f 11/17/11 10/16/11 13/19/11 +f 9/26/12 11/17/12 16/23/12 +o Cube.001 +v 2.300650 1.000000 -0.441804 +v 4.300650 1.000000 1.558196 +v 4.300650 1.000000 -0.441804 +v 2.300650 -1.000000 1.558196 +v 4.300650 -1.000000 1.558196 +v 2.300650 1.000000 1.558196 +v 2.300650 -1.000000 -0.441804 +v 4.300650 -1.000000 -0.441804 +vn -0.0000 1.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -1.0000 -0.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vt 0.875000 0.500000 +vt 0.625000 0.750000 +vt 0.625000 0.500000 +vt 0.375000 1.000000 +vt 0.375000 0.750000 +vt 0.625000 0.000000 +vt 0.375000 0.250000 +vt 0.375000 0.000000 +vt 0.375000 0.500000 +vt 0.125000 0.750000 +vt 0.125000 0.500000 +vt 0.625000 0.250000 +vt 0.875000 0.750000 +vt 0.625000 1.000000 +s 1 +usemtl t1.002 +f 17/29/13 18/30/13 19/31/13 +f 18/30/14 20/32/14 21/33/14 +f 22/34/15 23/35/15 20/36/15 +f 24/37/16 20/38/16 23/39/16 +f 19/31/17 21/33/17 24/37/17 +f 17/40/18 24/37/18 23/35/18 +f 17/29/13 22/41/13 18/30/13 +f 18/30/14 22/42/14 20/32/14 +f 22/34/15 17/40/15 23/35/15 +f 24/37/16 21/33/16 20/38/16 +f 19/31/17 18/30/17 21/33/17 +f 17/40/18 19/31/18 24/37/18 diff --git a/bin/render/tex1.png b/bin/render/tex1.png new file mode 100644 index 0000000000000000000000000000000000000000..831e0cbeff28d7e8a0b185d9701d9c60a4113bdb GIT binary patch literal 11668 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 eh3|jAnf-t3TLxA3ltEem0000K literal 0 HcmV?d00001 diff --git a/bin/render/tex2.png b/bin/render/tex2.png new file mode 100644 index 0000000000000000000000000000000000000000..5c3c3bdb2fc6e27735c520fdb561d8af747f6fec GIT binary patch literal 22867 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 delta 2120 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 VLxbOQ3NVE+c)I$ztaD0e0svwrLNfpW delta 1387 zcmah}X;4#V6#d@IO9BZrAt*(Guta1Dgdj=aug7kx77tt$xEZaqeiXvs3<#OdUihDwbC+_KfTE}#z9&X*-NQ# zpj8w&&q-BW?(Ml@k2`u7y0RuS&!@efBbWIcoIF2uq%`fp^_k4cePCtad|Rm%I4A>> zJn$(5E`N-~z)o-k?l0+6l!8JUD`hAc?b=z6FQKG^5U>Hei4|k63+mG(tNwGJox^n! zk|8=g0B6ASkyDN}N8udB0c@LQB@eHJbW|N_Jz@~n-|K?Dbe}m~zFa{30purK8jF=Y zyT5o`Ws8#B^i}p=L2;Bmim)^Iu@BB}%T0tWV~&&26<84#i+S>xBA_cxEe6476b;WW z_A{E#m~afCl1)d7z{2-gNa!G#q;ihnRIvc2q5!c1kRIpX6it0X)mmr?eD4cjzaA!` zicEEKc9{tG5FbNsgu>C2t!=sraF?hNWQ=5!gakSv8OLIDn$z9BbG^+%-vI+!mEbl0 zZ}d;SwD~%iwW=EGNXmnAfW|LFKFJ9DBASwp%WZi44$(Bq+U!wBNVi;PAhyf0%s$I* z70~&IbR~XEdN`u}oWEM4ox`G9d{0N~=Xxf>!vSl)g7RRDfx$eMXxG;ERVWDrUM;L5 zYnDMVj-S5h|I~u`DAY^3i$_#`t27b0Xv3O%Mv{4Qu%F0r(@MaL7e+r`$>tfdW5FmQ zGI?=_mf1apU5D){r~+k-L?6X|O)SviQg+W53MT1 z8P~=ou$e}Hd{?jB!>c8K2U{zhm|%Mh*-6`JfPFy-JktiCc9h%Nq;BMZ(n7^xc$ZuH zfSEa2STl5AY@Jm)EmKPjzuc9(Suz0RXbQQ-;XawRne2Sq`|w)fpk1!qSaqfR%|t~xamPq~;WcBGKPxpt+vb>`U?xfo z8~!z(vH7I|2dBK@KPIRO0F#QJ0P@>vly?@PDdQ z=shqn4>C+dYjbCB`P)kROlKFw;;vu~`Rbcvw(%RDN$V*(Nav8H=qs}rcQK~#F8Vn> z*>QH*qcXgKHvi;p-h`~y_0eLT4Y+3vvz>ppICp5toje8_pLGsF)wHFj?>s|A$K4ym zphh1mkqF#|4}Zok;7rZ*bcn$~0Kjg!uc4z&2w3~PLlsAOmDlrOuWNk@K+T7QLVW|5@Z*j3doAZ?mD_l>Vuf=Ed|#-1}%h8Vvq7| zjbSsSYnHX85NnlTqj+sfA}yjf+rZprqtkoO$lDL(15k|c=zbz<^q+9>Tm file.name).filter(str => str.endsWith(".ogg") || str.endsWith(".wav") ); +console.log(JSON.stringify(System.listDir("/sounds"))); + const sounds = Array(); sound_table.forEach(name => { @@ -19,14 +21,17 @@ sound_table.forEach(name => { let cur_sound = 0; -var track = Sound.load("sounds/" + sounds[0].file); -var duration = Sound.duration(track); +let track = Sound.load("sounds/" + sounds[0].file); + +let duration = Sound.getDuration(track); + +let position = Sound.getPosition(track); + Sound.play(track); -Timer.reset(timer); let repeat = false; -let pad = 0; -let oldpad = 0; + +let pad = Pads.get(); const icons = {play:new Image("amp/play.png"), pause:new Image("amp/pause.png"), next:new Image("amp/next.png"), back:new Image("amp/back.png")}; icons.play.color = purple; @@ -47,79 +52,95 @@ icons.back.height /= 3; let playing = true; -var cur_duration = 0.0f; +let cur_duration = 0.0f; let text_size = null; console.log(JSON.stringify(Tasks.get())); +function msToSecondMin(ms) { + let minutes = Math.floor(ms / 60000) + let seconds = Math.round(ms / 1000) + + seconds -= minutes * 60 + + if (seconds < 10) { + return minutes + ":0" + seconds + } else { + return minutes + ":" + seconds + } +} + while(true){ - Screen.clear(Color.new(32, 32, 32)); - oldpad = pad; - pad = Pads.get(); - if(Pads.check(pad, Pads.START) && !Pads.check(oldpad, Pads.START)) { + Screen.clear(gray); + + pad.update(); + + if(pad.justPressed(Pads.START)) { if(Sound.isPlaying()) { Sound.pause(track); - Timer.pause(timer); playing = false; } else { Sound.resume(track); - Timer.resume(timer); playing = true; } } - if(Pads.check(pad, Pads.LEFT) && !Pads.check(oldpad, Pads.LEFT)) { + if(pad.justPressed(Pads.L1)) { cur_duration = 0; Sound.pause(track); sounds.unshift(sounds.pop()); let new_track = Sound.load("sounds/" + sounds[0].file); - duration = Sound.duration(new_track); - Timer.reset(timer); + duration = Sound.getDuration(new_track); Sound.play(new_track); let old_track = track; track = new_track; Sound.free(old_track); } - if(Pads.check(pad, Pads.RIGHT) && !Pads.check(oldpad, Pads.RIGHT)) { + if(pad.justPressed(Pads.R1)) { cur_duration = 0; Sound.pause(track); sounds.push(sounds.shift()); let new_track = Sound.load("sounds/" + sounds[0].file); - duration = Sound.duration(new_track); - Timer.reset(timer); + duration = Sound.getDuration(new_track); Sound.play(new_track); let old_track = track; track = new_track; Sound.free(old_track); } - if(Pads.check(pad, Pads.TRIANGLE) && !Pads.check(oldpad, Pads.TRIANGLE)) { - break; + position = Sound.getPosition(track); + + if(pad.justPressed(Pads.RIGHT) && Sound.isPlaying()) { + Sound.setPosition(track, Sound.getPosition(track) + 5000); + } else if(pad.justPressed(Pads.LEFT) && Sound.isPlaying()) { + Sound.setPosition(track, Sound.getPosition(track) - 5000); } - if(playing) { + if(pad.justPressed(Pads.TRIANGLE)) { + Sound.restart(track); + } + + if(Sound.isPlaying()) { icons.pause.draw(320 - icons.play.width/2, 224 - icons.play.height/2 + 100); } else { icons.play.draw(320 - icons.play.width/2, 224 - icons.play.height/2 + 100); } - if (cur_duration < 1) { - cur_duration = (Math.floor(Timer.getTime(timer))/duration); - } + cur_duration = position/duration; + + segoe_ui.print(320 - sounds[0].size.width/2, 180 - sounds[0].size.height/2, sounds[0].name); - segoe_ui.print(320 - sounds[0].size.width/2, 224 - sounds[0].size.height/2, sounds[0].name); + segoe_ui.print(260, 220, msToSecondMin(Sound.getPosition(track)) + "/" + msToSecondMin(duration)); Draw.rect(120, 260, 400, 8, purple); - Draw.rect(120, 260, 400*cur_duration, 8, Color.new(127, 0, 255)); + Draw.rect(120, 260, 400 * cur_duration, 8, Color.new(127, 0, 255)); + Draw.circle(120 + 400 * cur_duration, 264, 8, Color.new(127, 0, 255)) icons.next.draw(320 - icons.next.width/2 + 150, 224 - icons.next.height/2 + 100); icons.back.draw(320 - icons.back.width/2 - 150, 224 - icons.back.height/2 + 100); - + Screen.flip(); } - -Timer.destroy(timer); -Sound.deinit(); diff --git a/bin/task.js b/bin/task.js index 8eb82e3..b758e2b 100644 --- a/bin/task.js +++ b/bin/task.js @@ -1,6 +1,6 @@ // {"name": "BUGGED", "author": "Daniel Santos", "version": "05112023","file": "task.js"} -Tasks.new(function() { +Tasks.new(() => { for(let i = 0; i < 10000; i++) { Screen.log("Hello, world!"); } diff --git a/bin/terminal.js b/bin/terminal.js deleted file mode 100644 index e850b0c..0000000 --- a/bin/terminal.js +++ /dev/null @@ -1,288 +0,0 @@ -// {"name": "Terminal", "author": "Daniel Santos", "version": "04102023", "file": "terminal.js", "bin": "athena_cli.elf"} - -function resetCommandLine(str) { - for (let i = str.length; i > 0; i--) { - Console.setCoords(Console.getX()-1, Console.getY()); - Console.print(""); - } -} - - -function printPrompt(user, device, cur_path) { - Console.setFontColor(0xFF0080); - Console.print(`${user}@${device}:${cur_path}$ `); - Console.setFontColor(0xFFFFFFFF); -} - -class CommandLineInterface { - constructor() { - this.str = ""; - this.ptr = 0; - this.history = {cmds:[], ptr:0, backup:false}; - } - - handleArrowRight() { - Console.setCursorColor(0); - Console.print(""); - - this.ptr++; - - resetCommandLine(this.str); - - Console.print(this.str.slice(0, this.ptr)); - let x_bak = Console.getX(); - Console.print(this.str.slice(this.ptr, this.str.length)); - let x_cur_bak = Console.getX(); - Console.setCursorColor(0xFFFFFF); - Console.setCoords(x_bak, Console.getY()); - Console.print(""); - Console.setCursor(false); - Console.setCoords(x_cur_bak, Console.getY()); - Console.print(""); - Console.setCursor(true); - } - - handleArrowLeft() { - Console.setCursorColor(0); - Console.print(""); - - this.ptr--; - - for (let i = this.str.length; i > this.ptr; i--) { - Console.setCoords(Console.getX() - 1, Console.getY()); - Console.print(""); - } - - let x_bak = Console.getX(); - Console.print(this.str.slice(this.ptr, this.str.length)); - let x_cur_bak = Console.getX(); - Console.setCursorColor(0xFFFFFF); - Console.setCoords(x_bak, Console.getY()); - Console.print(""); - Console.setCursor(false); - Console.setCoords(x_cur_bak, Console.getY()); - Console.print(""); - Console.setCursor(true); - } - - handleArrowUp() { - if (this.str != "" && this.history.ptr == this.history.cmds.length) { - this.history.cmds.push(this.str); - } - - this.history.ptr--; - - if (this.history.ptr < this.history.cmds.length) { - Console.setCursorColor(0); - Console.print(""); - resetCommandLine(this.str); - Console.setCursorColor(0xFFFFFF); - Console.print(this.history.cmds[this.history.ptr]); - this.str = this.history.cmds[this.history.ptr]; - } - } - - handleArrowDown() { - this.history.ptr++; - - if (this.history.ptr < this.history.cmds.length) { - Console.setCursorColor(0); - Console.print(""); - resetCommandLine(this.str); - Console.setCursorColor(0xFFFFFF); - Console.print(this.history.cmds[this.history.ptr]); - this.str = this.history.cmds[this.history.ptr]; - } else if (!this.history.backup) { - Console.setCursorColor(0); - Console.print(""); - resetCommandLine(this.str); - Console.setCursorColor(0xFFFFFF); - Console.print(""); - this.str = ""; - } - } - - handleBackspace() { - if (this.str.length > 0) { - Console.setCursorColor(0); - Console.print(""); - - resetCommandLine(this.str); - - Console.setCursorColor(0xFFFFFF); - - Console.setCursor(false); - Console.print(this.str.slice(0, this.ptr-1)); - let x_bak = Console.getX(); - Console.print(this.str.slice(this.ptr, this.str.length)); - let x_cur_bak = Console.getX(); - Console.setCursor(true); - Console.setCoords(x_bak, Console.getY()); - Console.print(""); - Console.setCursor(false); - Console.setCoords(x_cur_bak, Console.getY()); - Console.print(""); - Console.setCursor(true); - - this.str = this.str.slice(0, this.ptr-1) + this.str.slice(this.ptr, this.str.length); - - this.ptr--; - } - } - - handleReturn() { - resetCommandLine(this.str); - Console.print(this.str); - - Console.setCursor(false); - Console.print(" \n"); - } - - putChar(ch) { - resetCommandLine(this.str); - - let c = String.fromCharCode(ch); - - Console.setCursor(false); - Console.print(this.str.slice(0, this.ptr)); - Console.print(c); - let x_bak = Console.getX(); - Console.print(this.str.slice(this.ptr, this.str.length)); - let x_cur_bak = Console.getX(); - Console.setCursor(true); - Console.setCoords(x_bak, Console.getY()); - Console.print(""); - Console.setCursor(false); - Console.setCoords(x_cur_bak, Console.getY()); - Console.print(""); - Console.setCursor(true); - - this.str = this.str.slice(0, this.ptr) + c + this.str.slice(this.ptr, this.str.length); - - this.ptr++; - } - - resetParameters() { - Console.setCursor(true); - this.history.backup = false; - this.ptr = 0; - this.str = ""; - } -}; - -IOP.reset(); -IOP.loadDefaultModule(IOP.hdd); -IOP.loadDefaultModule(IOP.cdfs); -IOP.loadDefaultModule(IOP.memcard); -IOP.loadDefaultModule(IOP.usb_mass); -IOP.loadDefaultModule(IOP.pads); -IOP.loadDefaultModule(IOP.network); -IOP.loadDefaultModule(IOP.keyboard); - -Network.init(); -Keyboard.init(); - -Keyboard.setBlockingMode(1); - -globalThis.user = "user"; -globalThis.device = "ps2"; - -const VK_ACTION = 27; -const VK_RIGHT = 41; -const VK_LEFT = 42; -const VK_DOWN = 43; -const VK_UP = 44; -const BACKSPACE = 7; -const RETURN = 10; - -let old_char = 0; -let cur_char = 0; - -let cmds = System.listDir("usr/bin").map(file => file.name.replace(".js", "")); -let cmd_found = false; - -let cur_path = null; - -const cli = new CommandLineInterface(); - -while(true) { - cur_path = System.currentDir(); - printPrompt(user, device, cur_path); - - while(cur_char != RETURN) { - old_char = cur_char; - cur_char = Keyboard.get(); - - if (cur_char == VK_RIGHT && old_char == VK_ACTION && cli.ptr < cli.str.length) { - cli.handleArrowRight(); - - } else if (cur_char == VK_LEFT && old_char == VK_ACTION && cli.ptr > 0) { - cli.handleArrowLeft(); - - } else if (cur_char == VK_UP && old_char == VK_ACTION && cli.history.ptr > 0) { - cli.handleArrowUp(); - - } else if (cur_char == VK_DOWN && old_char == VK_ACTION && cli.history.ptr < cli.history.cmds.length) { - cli.handleArrowDown(); - - } else if (cur_char == BACKSPACE){ - cli.handleBackspace(); - - } else if(cur_char == RETURN) { - cli.handleReturn(); - } else if(cur_char != VK_ACTION && old_char != VK_ACTION){ - cli.putChar(cur_char); - } - } - - if (Console.getY() > 27) { - Console.clear(); - } - - - cli.history.cmds.push(cli.str); - cli.history.ptr = cli.history.cmds.length; - let command = cli.str.replace("\n", "").split(" "); - - if(command[0].slice(0, 2) != "./") { - cmds.forEach(cmd => { - globalThis.args = []; - if(cmd == command[0]) { - cmd_found = true; - command.shift(); - args = command; - std.loadScript(System.boot_path + "usr/bin/" + cmd + ".js"); - args = undefined; - } - }); - } else if (command[0].endsWith(".elf") || command[0].endsWith(".ELF")) { - if (command.length < 2) { - System.loadELF(cur_path + command[0].slice(2, command[0].length)); - } else { - let fst_cmd = command.shift(); - System.loadELF(cur_path + fst_cmd.slice(2, fst_cmd.length), command); - } - - } else if (command[0].endsWith(".irx") || command[0].endsWith(".IRX")) { - IOP.loadModule(cur_path + command[0].slice(2, command[0].length)); - - } else if (command[0].endsWith(".js")) { - if (command.length < 2) { - std.loadScript(command[0].slice(2, command[0].length)); - } else { - let fst_cmd = command.shift(); - globalThis.args = command; - std.loadScript(fst_cmd.slice(2, fst_cmd.length)); - } - } - - - if(!cmd_found && command[0] != "") { - Console.print(`${command[0]}: command not found\n`); - } - - cli.resetParameters(); - cmd_found = false; - cur_char = 0; -} - diff --git a/bin/text b/bin/text deleted file mode 100644 index 5e4f256..0000000 --- a/bin/text +++ /dev/null @@ -1 +0,0 @@ -test1234 \ No newline at end of file diff --git a/bin/usr/bin/apm.js b/bin/usr/bin/apm.js deleted file mode 100644 index 8c0cfae..0000000 --- a/bin/usr/bin/apm.js +++ /dev/null @@ -1,41 +0,0 @@ -if(args !== undefined) { - - var load_pkg_db = (fname) => { - return JSON.parse(loadFile(fname)); - } - - var req = new Request(); - req.followlocation = true; - req.useragent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/37.0.2062.94 Chrome/37.0.2062.94 Safari/537.36"; - req.headers = ["upgrade-insecure-requests: 0", - "sec-fetch-dest: document", - "sec-fetch-mode: navigate", - "sec-fetch-user: ?1", - "sec-fetch-site: same-origin", - "sec-ch-ua-mobile: ?0", - "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", - 'sec-ch-ua-platform: ^\^"Linux^\^"', - "sec-ch-ua-mobile: ?0", - ]; - - if (args[0] == "update") { - Console.print("Updating package lists...\n"); - req.download("https://raw.githubusercontent.com/DanielSant0s/AthenaPM-db/main/packages.json", "packages.json"); - - while(!req.ready(5)) { - console.log("Waiting!\n"); - } - - let pkgs = load_pkg_db("packages.json"); - - pkgs.forEach(pkg => { - Console.print(`${pkg.name}\n`); - }); - - } else if (args[0] == "install") { - - } - - req = null; -} - diff --git a/bin/usr/bin/cat.js b/bin/usr/bin/cat.js deleted file mode 100644 index 7c087de..0000000 --- a/bin/usr/bin/cat.js +++ /dev/null @@ -1 +0,0 @@ -Console.print(std.loadFile(args[0])); \ No newline at end of file diff --git a/bin/usr/bin/cd.js b/bin/usr/bin/cd.js deleted file mode 100644 index 53e6bed..0000000 --- a/bin/usr/bin/cd.js +++ /dev/null @@ -1,11 +0,0 @@ - -if (!args[0]){ - System.currentDir(System.boot_path) -} else{ - if (System.doesFileExist(args[0])){ - System.currentDir(args[0] + "/") - }else{ - Console.print("cd: File or Directory Not Found: " + args[0] + "\n") - } - -} \ No newline at end of file diff --git a/bin/usr/bin/clear.js b/bin/usr/bin/clear.js deleted file mode 100644 index bd84578..0000000 --- a/bin/usr/bin/clear.js +++ /dev/null @@ -1 +0,0 @@ -Console.clear(); \ No newline at end of file diff --git a/bin/usr/bin/exit.js b/bin/usr/bin/exit.js deleted file mode 100644 index 4cb5ec5..0000000 --- a/bin/usr/bin/exit.js +++ /dev/null @@ -1 +0,0 @@ -System.loadELF(System.boot_path + "athena_pkd.elf"); diff --git a/bin/usr/bin/ls.js b/bin/usr/bin/ls.js deleted file mode 100644 index 857a3a8..0000000 --- a/bin/usr/bin/ls.js +++ /dev/null @@ -1,3 +0,0 @@ -var dir = System.listDir().map(item => item.name).join(" "); -Console.print(dir + "\n"); -dir = undefined; \ No newline at end of file diff --git a/bin/usr/bin/mv.js b/bin/usr/bin/mv.js deleted file mode 100644 index 1a04109..0000000 --- a/bin/usr/bin/mv.js +++ /dev/null @@ -1 +0,0 @@ -System.moveFile(args[0], args[1]); \ No newline at end of file diff --git a/bin/usr/bin/neofetch.js b/bin/usr/bin/neofetch.js deleted file mode 100644 index 5a7d35d..0000000 --- a/bin/usr/bin/neofetch.js +++ /dev/null @@ -1,39 +0,0 @@ -var ee_info = System.getCPUInfo(); -var gs_info = System.getGPUInfo(); - -console.log(JSON.stringify(ee_info)); - -var logo = -` - - #@@@@@@@@@@@@@@@* ${user}@${device} - /@@@&@@@@@@@@@@@@@# *@* ------------ - /@@@@@@@@@@@. @@@@@@@ OS: AthenaEnv v2 - @@ #@@@@@@& ,@@, @@@@@@ Kernel: PS2DEV Kernel - /@@,*@@@@ .@@@/ (@@@@@@@ Uptime: null - . @@@* *@@@@@@@@ Packages: 4 (dpkg) - @@@@@@ #@@@@@@@@@@@@@@@@ Shell: owlsh 0.1 - @@@@@( *@@@@@@@@@@@@@@@@@@ Terminal: TermOwl - .@@@@@@@@@@@@@@@ @@@@@@@@@@@. CPU: MIPS R5900 rev. ${ee_info.revision} @ ${Math.floor(ee_info.CPUClock * 0.000001)}MHz - @@@(@@@@@@@@& @@@@@@@@@@@@@@ GPU: Graphics Synthesizer id ${gs_info.id} rev. ${gs_info.revision} - .@@@ @@@@ *@@@@@@@@@@@@@@@. Memory: ${Math.floor(System.getMemoryStats().used / 1048576)}MiB / ${Math.floor(ee_info.RAMSize / 1048576)}MiB - .@@@ /@@@@@@@@@@@@@@@@. - @@@, @@@@@@@@@@@@@@@@@ - @@@ /@@@@@@@@@@@@@@@@@ - @@@. /@@@@@@@@@@@@@@@@. - @@@ @@@@@@@@@@@@@@@@ - &@@, @@@@@@@@@@@@@@ - %@@& (@@@@@@@@@@@ - *@@@@@*. @@@@@@@@ - *@@@. #@@@ %@# - ,@@@ /@@& - #@@@,@@@(@@@( @@%&@@@@@@@@* - / % # .( -` - -//Console.setFontColor(0xFFFF0080); -Console.print(logo); - -logo = null; -ee_info = null; -gs_info = null; diff --git a/embed.make b/embed.make index 83356eb..8606fa2 100644 --- a/embed.make +++ b/embed.make @@ -1,59 +1,59 @@ #-------------------- Embedded IOP Modules ------------------------# -$(EE_ASM_DIR)iomanx.s: $(PS2SDK)/iop/irx/iomanX.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)iomanx.c: $(PS2SDK)/iop/irx/iomanX.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ iomanX_irx -$(EE_ASM_DIR)filexio.s: $(PS2SDK)/iop/irx/fileXio.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)filexio.c: $(PS2SDK)/iop/irx/fileXio.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ fileXio_irx -$(EE_ASM_DIR)sio2man.s: $(PS2SDK)/iop/irx/sio2man.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)sio2man.c: $(PS2SDK)/iop/irx/sio2man.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ sio2man_irx -$(EE_ASM_DIR)mcman.s: $(PS2SDK)/iop/irx/mcman.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)mcman.c: $(PS2SDK)/iop/irx/mcman.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ mcman_irx -$(EE_ASM_DIR)mcserv.s: $(PS2SDK)/iop/irx/mcserv.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)mcserv.c: $(PS2SDK)/iop/irx/mcserv.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ mcserv_irx -$(EE_ASM_DIR)padman.s: $(PS2SDK)/iop/irx/padman.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)padman.c: $(PS2SDK)/iop/irx/padman.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ padman_irx -$(EE_ASM_DIR)mtapman.s: $(PS2SDK)/iop/irx/mtapman.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)mtapman.c: $(PS2SDK)/iop/irx/mtapman.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ mtapman_irx -$(EE_ASM_DIR)libsd.s: $(PS2SDK)/iop/irx/libsd.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)libsd.c: $(PS2SDK)/iop/irx/libsd.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ libsd_irx -$(EE_ASM_DIR)usbd.s: $(PS2SDK)/iop/irx/usbd.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)usbd.c: $(PS2SDK)/iop/irx/usbd.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ usbd_irx -$(EE_ASM_DIR)audsrv.s: $(PS2SDK)/iop/irx/audsrv.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)audsrv.c: $(PS2SDK)/iop/irx/audsrv.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ audsrv_irx -$(EE_ASM_DIR)bdm.s: $(PS2SDK)/iop/irx/bdm.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)bdm.c: $(PS2SDK)/iop/irx/bdm.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ bdm_irx -$(EE_ASM_DIR)bdmfs_fatfs.s: $(PS2SDK)/iop/irx/bdmfs_fatfs.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)bdmfs_fatfs.c: $(PS2SDK)/iop/irx/bdmfs_fatfs.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ bdmfs_fatfs_irx -$(EE_ASM_DIR)usbmass_bd.s: $(PS2SDK)/iop/irx/usbmass_bd.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)usbmass_bd.c: $(PS2SDK)/iop/irx/usbmass_bd.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ usbmass_bd_irx -$(EE_ASM_DIR)ps2dev9.s: $(PS2SDK)/iop/irx/ps2dev9.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ps2dev9.c: $(PS2SDK)/iop/irx/ps2dev9.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ps2dev9_irx -$(EE_ASM_DIR)ps2atad.s: $(PS2SDK)/iop/irx/ps2atad.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ps2atad.c: $(PS2SDK)/iop/irx/ps2atad.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ps2atad_irx -$(EE_ASM_DIR)ps2hdd.s: $(PS2SDK)/iop/irx/ps2hdd.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ps2hdd.c: $(PS2SDK)/iop/irx/ps2hdd.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ps2hdd_irx -$(EE_ASM_DIR)ps2fs.s: $(PS2SDK)/iop/irx/ps2fs.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ps2fs.c: $(PS2SDK)/iop/irx/ps2fs.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ps2fs_irx -$(EE_ASM_DIR)cdfs.s: $(PS2SDK)/iop/irx/cdfs.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)cdfs.c: $(PS2SDK)/iop/irx/cdfs.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ cdfs_irx -$(EE_ASM_DIR)poweroff.s: $(PS2SDK)/iop/irx/poweroff.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)poweroff.c: $(PS2SDK)/iop/irx/poweroff.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ poweroff_irx modules/ds34bt/ee/libds34bt.a: modules/ds34bt/ee @@ -62,7 +62,7 @@ modules/ds34bt/ee/libds34bt.a: modules/ds34bt/ee modules/ds34bt/iop/ds34bt.irx: modules/ds34bt/iop $(MAKE) -C $< -$(EE_ASM_DIR)ds34bt.s: modules/ds34bt/iop/ds34bt.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ds34bt.c: modules/ds34bt/iop/ds34bt.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ds34bt_irx modules/ds34usb/ee/libds34usb.a: modules/ds34usb/ee @@ -71,35 +71,35 @@ modules/ds34usb/ee/libds34usb.a: modules/ds34usb/ee modules/ds34usb/iop/ds34usb.irx: modules/ds34usb/iop $(MAKE) -C $< -$(EE_ASM_DIR)ds34usb.s: modules/ds34usb/iop/ds34usb.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ds34usb.c: modules/ds34usb/iop/ds34usb.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ds34usb_irx modules/freeram/freeram.irx: modules/freeram $(MAKE) -C $< -$(EE_ASM_DIR)freeram.s: modules/freeram/freeram.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)freeram.c: modules/freeram/freeram.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ freeram_irx -$(EE_ASM_DIR)NETMAN.s: $(PS2SDK)/iop/irx/netman.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)NETMAN.c: $(PS2SDK)/iop/irx/netman.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ NETMAN_irx -$(EE_ASM_DIR)SMAP.s: $(PS2SDK)/iop/irx/smap.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)SMAP.c: $(PS2SDK)/iop/irx/smap.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ SMAP_irx -$(EE_ASM_DIR)ps2ips.s: $(PS2SDK)/iop/irx/ps2ips.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ps2ips.c: $(PS2SDK)/iop/irx/ps2ips.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ps2ips_irx -$(EE_ASM_DIR)ps2kbd.s: $(PS2SDK)/iop/irx/ps2kbd.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ps2kbd.c: $(PS2SDK)/iop/irx/ps2kbd.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ps2kbd_irx -$(EE_ASM_DIR)ps2mouse.s: $(PS2SDK)/iop/irx/ps2mouse.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ps2mouse.c: $(PS2SDK)/iop/irx/ps2mouse.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ps2mouse_irx -$(EE_ASM_DIR)ps2cam.s: $(PS2SDK)/iop/irx/ps2cam.irx | $(EE_ASM_DIR) +$(EE_ASM_DIR)ps2cam.c: $(PS2SDK)/iop/irx/ps2cam.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ps2cam_irx #--------------------- Embedded text fonts ------------------------# -$(EE_ASM_DIR)quicksand_regular.s: assets/fonts/Quicksand-Regular.ttf | $(EE_ASM_DIR) - $(BIN2S) $< $@ quicksand_regular \ No newline at end of file +$(EE_ASM_DIR)quicksand_regular.c: assets/fonts/Quicksand-Regular.ttf | $(EE_ASM_DIR) + $(BIN2S) $< $@ quicksand_regular diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index f518d0b5ad0fa629651534cb81d95a3aa7d06dc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeI1!EVz)5Qb+E+}{2fmi5}x8lwz z{4=|}zYiHY+uYf;wer(} zii4RRS778m;l%AJ2a(+7W6i#--|cpvN$f6$ME1o^%8x5BMqO3Car2l6@5g- z-&b7!QLzYPnD02auWgLY2I~85j8nW-sE}n8#eD5W>1K0A7tqgXHlX_1Q|-2qI#o$$ zR1d}6n)lQXTXltK4SkY#Am?#hq4>?C*v;?~W1JvfG(%#JzUDz2?F!Kfp = zip_open(path_buf, 0, &err)) == NULL) { zip_error_to_str(buf, sizeof(buf), err, errno); @@ -295,6 +296,7 @@ static JSValue athena_untar(JSContext *ctx, JSValue this_val, int argc, JSValueC } getcwd(tar_path, 512); + strcat(tar_path, "/"); untar(fp, tar_path); fclose(fp); @@ -334,6 +336,7 @@ static JSValue athena_extractall(JSContext *ctx, JSValue this_val, int argc, JSV if (zip_stat_index(arc->fp, i, 0, &sb) == 0) { if (sb.name[len - 1] == '/') { strcpy(outbuff, boot_path); + strcat(outbuff, "/"); strcat(outbuff, sb.name); mkdir(outbuff, 0755); } else { diff --git a/src/ath_cli.c b/src/ath_cli.c deleted file mode 100644 index 5d0ab0a..0000000 --- a/src/ath_cli.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "ath_env.h" - -static JSValue athena_print(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - const char* text = JS_ToCString(ctx, argv[0]); - scr_printf(text); - JS_FreeCString(ctx, text); - return JS_UNDEFINED; -} - -static JSValue athena_putchar(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - int x, y, ch; - uint32_t color; - - JS_ToInt32(ctx, &x, argv[0]); - JS_ToInt32(ctx, &y, argv[1]); - JS_ToUint32(ctx, &color, argv[2]); - JS_ToInt32(ctx, &ch, argv[3]); - scr_putchar(x, y, color, ch); - return JS_UNDEFINED; -} - -static JSValue athena_setxy(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - int x, y; - JS_ToInt32(ctx, &x, argv[0]); - JS_ToInt32(ctx, &y, argv[1]); - scr_setXY(x, y); - return JS_UNDEFINED; -} - -static JSValue athena_clear(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - scr_clear(); - return JS_UNDEFINED; -} - -static JSValue athena_setbgcolor(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - uint32_t color; - JS_ToUint32(ctx, &color, argv[0]); - scr_setbgcolor(color); - return JS_UNDEFINED; -} - -static JSValue athena_setfontcolor(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - uint32_t color; - JS_ToUint32(ctx, &color, argv[0]); - scr_setfontcolor(color); - return JS_UNDEFINED; -} - -static JSValue athena_setcursorcolor(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - uint32_t color; - JS_ToUint32(ctx, &color, argv[0]); - scr_setcursorcolor(color); - return JS_UNDEFINED; -} - -static JSValue athena_setcursor(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - scr_setCursor(JS_ToBool(ctx, argv[0])); - return JS_UNDEFINED; -} - -static JSValue athena_getx(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - return JS_NewInt32(ctx, scr_getX()); -} - -static JSValue athena_gety(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - return JS_NewInt32(ctx, scr_getY()); -} - -/*static JSValue athena_getcursor(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - return JS_NewBool(ctx, scr_getCursor()); -}*/ - -static const JSCFunctionListEntry console_funcs[] = { - JS_CFUNC_DEF("print", 1, athena_print), - JS_CFUNC_DEF("putChar", 4, athena_putchar), - JS_CFUNC_DEF("setCoords", 2, athena_setxy), - JS_CFUNC_DEF("clear", 0, athena_clear), - JS_CFUNC_DEF("setBGColor", 1, athena_setbgcolor), - JS_CFUNC_DEF("setFontColor", 1, athena_setfontcolor), - JS_CFUNC_DEF("setCursorColor", 1, athena_setcursorcolor), - JS_CFUNC_DEF("setCursor", 1, athena_setcursor), - JS_CFUNC_DEF("getX", 0, athena_getx), - JS_CFUNC_DEF("getY", 0, athena_gety), - //JS_CFUNC_DEF("getCursor", 0, athena_getcursor), -}; - -static int console_init(JSContext *ctx, JSModuleDef *m) -{ - return JS_SetModuleExportList(ctx, m, console_funcs, countof(console_funcs)); -} - -JSModuleDef *athena_console_init(JSContext* ctx){ - return athena_push_module(ctx, console_init, console_funcs, countof(console_funcs), "Console"); -} \ No newline at end of file diff --git a/src/ath_env.c b/src/ath_env.c index 9fe064a..5cddeef 100644 --- a/src/ath_env.c +++ b/src/ath_env.c @@ -10,6 +10,8 @@ #define TRUE 1 +#define JSFILE_NOTFOUND -5656 + JSModuleDef *athena_push_module(JSContext* ctx, JSModuleInitFunc *func, const JSCFunctionListEntry *func_list, int len, const char* module_name){ JSModuleDef *m; m = JS_NewCModule(ctx, module_name, func); @@ -154,6 +156,7 @@ static int qjs_handle_fh(JSContext *ctx, FILE *f, const char *filename, const ch "import * as Image from 'Image';\n" "import * as ImageList from 'ImageList';\n" "import * as Render from 'Render';\n" + "import * as RenderObject from 'RenderObject';\n" "import * as Lights from 'Lights';\n" "import * as Camera from 'Camera';\n" "globalThis.Color = Color;\n" @@ -187,6 +190,7 @@ static int qjs_handle_fh(JSContext *ctx, FILE *f, const char *filename, const ch "globalThis.ImageList = ImageList.ImageList;\n" "globalThis.Render = Render;\n" + "globalThis.RenderObject = RenderObject.RenderObject;\n" "globalThis.Lights = Lights;\n" "globalThis.AMBIENT = Lights.AMBIENT;\n" @@ -194,12 +198,6 @@ static int qjs_handle_fh(JSContext *ctx, FILE *f, const char *filename, const ch "globalThis.Camera = Camera;\n" - #else - - #ifdef ATHENA_CLI - "import * as Console from 'Console';\n" - "globalThis.Console = Console;\n" - #endif #endif "import * as std from 'std';\n" @@ -210,6 +208,9 @@ static int qjs_handle_fh(JSContext *ctx, FILE *f, const char *filename, const ch "import * as System from 'System';\n" "import * as IOP from 'IOP';\n" "import * as Archive from 'Archive';\n" + "import * as Vector2 from 'Vector2';\n" + "import * as Vector3 from 'Vector3';\n" + "import * as Physics from 'Physics';\n" "globalThis.std = std;\n" "globalThis.os = os;\n" @@ -218,7 +219,11 @@ static int qjs_handle_fh(JSContext *ctx, FILE *f, const char *filename, const ch "globalThis.Pads = Pads;\n" "globalThis.System = System;\n" "globalThis.Archive = Archive;\n" - "globalThis.IOP = IOP;\n"; + "globalThis.IOP = IOP;\n" + "globalThis.Vector2 = Vector2;\n" + "globalThis.Vector3 = Vector3;\n" + "globalThis.Physics = Physics;\n"; + rc = qjs_eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); if (rc != 0) { return retval; } @@ -233,17 +238,12 @@ static int qjs_handle_fh(JSContext *ctx, FILE *f, const char *filename, const ch static int qjs_handle_file(JSContext *ctx, const char *filename, const char *bytecode_filename) { FILE *f = NULL; int retval; - char fnbuf[256]; - - snprintf(fnbuf, sizeof(fnbuf), "%s", filename); - - fnbuf[sizeof(fnbuf) - 1] = (char) 0; - f = fopen(fnbuf, "r"); + f = fopen(filename, "r"); if (!f) { - fprintf(stderr, "failed to open source file: %s\n", filename); - fflush(stderr); - return -1; + //fprintf(stderr, "failed to open source file: %s\n", filename); + //fflush(stderr); + return JSFILE_NOTFOUND; } retval = qjs_handle_fh(ctx, f, filename, bytecode_filename); @@ -267,6 +267,8 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt) athena_timer_init(ctx); athena_task_init(ctx); athena_pads_init(ctx); + athena_vector_init(ctx); + athena_physics_init(ctx); #ifdef ATHENA_AUDIO athena_sound_init(ctx); @@ -280,10 +282,6 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt) athena_shape_init(ctx); athena_screen_init(ctx); athena_render_init(ctx); - #else - #ifdef ATHENA_CLI - athena_console_init(ctx); - #endif #endif #ifdef ATHENA_KEYBOARD @@ -315,39 +313,62 @@ const char* runScript(const char* script, bool isBuffer) size_t memoryLimit = (GetMemorySize() - get_used_memory()) >> 1; dbgprintf("\nStarting AthenaEnv...\n"); - JSRuntime *rt = JS_NewRuntime(); if (!rt) { return "Runtime creation"; } + JSRuntime *rt = JS_NewRuntime(); if (!rt) { return "AthenaError: Runtime creation"; } js_std_set_worker_new_context_func(JS_NewCustomContext); js_std_init_handlers(rt); JS_SetMemoryLimit(rt, memoryLimit); - JS_SetGCThreshold(rt, memoryLimit >> 3); + JS_SetGCThreshold(rt, memoryLimit - 4194304); - JSContext *ctx = JS_NewCustomContext(rt); if (!ctx) { return "Context creation"; } + JSContext *ctx = JS_NewCustomContext(rt); if (!ctx) { return "AthenaError: Context creation"; } JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); int s = qjs_handle_file(ctx, script, NULL); - js_std_loop(ctx); - if (s < 0) { - JSValue exception_val = JS_GetException(ctx); - const char* exception = JS_ToCString(ctx, exception_val); - JSValue stack_val = JS_GetPropertyStr(ctx, exception_val, "stack"); - const char* stack = JS_ToCString(ctx, stack_val); - JS_FreeValue(ctx, exception_val); - JS_FreeValue(ctx, stack_val); + if (s == JSFILE_NOTFOUND) { + sprintf(error_buf, "AthenaError: Fail when opening %s\n" + "\nTip: If you are on PCSX2, check Host filesystem!\n", script); + } else { + JSValue exception_val = JS_GetException(ctx); + const char* exception = JS_ToCString(ctx, exception_val); + JSValue stack_val = JS_GetPropertyStr(ctx, exception_val, "stack"); + const char* stack = JS_ToCString(ctx, stack_val); + JS_FreeValue(ctx, exception_val); + JS_FreeValue(ctx, stack_val); + + strcpy(error_buf, exception); + strcat(error_buf, "\n"); + strcat(error_buf, stack); + } - strcpy(error_buf, exception); - strcat(error_buf, "\n"); - strcat(error_buf, stack); - js_std_free_handlers(rt); JS_FreeContext(ctx); JS_FreeRuntime(rt); - printf("%s\n", error_buf); return error_buf; + } else { + s = js_std_loop(ctx); + + if (s < 0) { + JSValue exception_val = JS_GetException(ctx); + const char* exception = JS_ToCString(ctx, exception_val); + JSValue stack_val = JS_GetPropertyStr(ctx, exception_val, "stack"); + const char* stack = JS_ToCString(ctx, stack_val); + JS_FreeValue(ctx, exception_val); + JS_FreeValue(ctx, stack_val); + + strcpy(error_buf, exception); + strcat(error_buf, "\n"); + strcat(error_buf, stack); + + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + return error_buf; + } } js_std_free_handlers(rt); diff --git a/src/ath_env.h b/src/ath_env.h index 6ea222e..c2f09cc 100644 --- a/src/ath_env.h +++ b/src/ath_env.h @@ -1,3 +1,6 @@ +#ifndef ATH_ENV_H +#define ATH_ENV_H + #include #include #include @@ -58,6 +61,9 @@ JSModuleDef *athena_timer_init(JSContext* ctx); JSModuleDef *athena_task_init(JSContext* ctx); JSModuleDef *athena_pads_init(JSContext* ctx); +JSModuleDef *athena_vector_init(JSContext *ctx); +JSModuleDef *athena_physics_init(JSContext *ctx); + #ifdef ATHENA_GRAPHICS JSModuleDef *athena_render_init(JSContext* ctx); JSModuleDef *athena_screen_init(JSContext* ctx); @@ -66,10 +72,6 @@ JSModuleDef *athena_shape_init(JSContext* ctx); JSModuleDef *athena_font_init(JSContext* ctx); JSModuleDef *athena_image_init(JSContext* ctx); JSModuleDef *athena_imagelist_init(JSContext* ctx); -#else -#ifdef ATHENA_CLI -JSModuleDef *athena_console_init(JSContext* ctx); -#endif #endif #ifdef ATHENA_NETWORK @@ -95,3 +97,6 @@ JSModuleDef *athena_sound_init(JSContext* ctx); JSModuleDef *athena_camera_init(JSContext* ctx); #endif + + +#endif \ No newline at end of file diff --git a/src/ath_font.c b/src/ath_font.c index f01a358..e156ff6 100644 --- a/src/ath_font.c +++ b/src/ath_font.c @@ -9,9 +9,20 @@ #include "include/fntsys.h" #include "ath_env.h" -const uint32_t osdsys_font = 0; -const uint32_t image_font = 1; -const uint32_t truetype_font = 2; +enum { + osdsys_font, + image_font, + truetype_font +} FontTypes; + +bool osdsysfnt_loaded = false; +bool truetypefnt_loaded = false; + +enum { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT +} FontAlign; typedef struct { uint32_t type; @@ -21,15 +32,28 @@ typedef struct { float scale; } JSFontData; +typedef struct { + JSFontData* font_data; + const char* text; + uint8_t align; + Coords size; +} JSFontRenderData; + static JSClassID js_font_class_id; +static JSClassID js_fontrender_class_id; static void athena_font_dtor(JSRuntime *rt, JSValue val){ JSFontData *font = JS_GetOpaque(val, js_font_class_id); - if (font->type == 1) { + if (font->type == image_font) { unloadFont(font->data); - } else { + } else if (font->type == truetype_font) { fntRelease(font->id); + } else { + if (osdsysfnt_loaded) { + unloadFontM(); + osdsysfnt_loaded = false; + } } js_free_rt(rt, font); @@ -51,6 +75,11 @@ static JSValue athena_font_ctor(JSContext *ctx, JSValueConst new_target, int arg if (strcmp(path, "default") == 0) { font->id = 0; font->type = truetype_font; + if (!truetypefnt_loaded) { + fntLoadDefault(NULL); + truetypefnt_loaded = true; + } + } else { dbgprintf("%s\n", path); font->id = fntLoadFile(path); @@ -65,6 +94,10 @@ static JSValue athena_font_ctor(JSContext *ctx, JSValueConst new_target, int arg JS_FreeCString(ctx, path); } else { + if (!osdsysfnt_loaded) { + loadFontM(); + osdsysfnt_loaded = true; + } font->type = osdsys_font; } @@ -94,6 +127,7 @@ static JSValue athena_font_print(JSContext *ctx, JSValue this_val, int argc, JSV JS_ToFloat32(ctx, &x, argv[0]); JS_ToFloat32(ctx, &y, argv[1]); + const char* text = JS_ToCString(ctx, argv[2]); if (font->type == 1){ @@ -176,19 +210,94 @@ static JSValue athena_font_set_color(JSContext *ctx, JSValueConst this_val, JSVa return JS_UNDEFINED; } +static JSValue athena_render_font(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv){ + JSFontRenderData* render_data; + JSValue obj = JS_UNDEFINED; + JSValue proto; + + obj = JS_NewObjectClass(ctx, js_fontrender_class_id); + if (!JS_IsException(obj)) { + render_data = js_mallocz(ctx, sizeof(*render_data)); + if (!render_data) + return JS_EXCEPTION; + + render_data->font_data = JS_GetOpaque2(ctx, this_val, js_font_class_id); + render_data->text = JS_ToCString(ctx, argv[0]); + + if (argc == 2) { + JS_ToUint32(ctx, &render_data->align, argv[1]); + } + + if (render_data->font_data->type == 2) { + render_data->size = fntGetTextSize(render_data->font_data->id, render_data->text); + } else if (render_data->font_data->type == 0) { + render_data->size.width = strlen(render_data->text) * render_data->font_data->scale * 0.68f * 26.0f; + render_data->size.height = 26.0f * render_data->font_data->scale; + } + + JS_SetOpaque(obj, render_data); + return obj; + } + + js_free(ctx, render_data); + return JS_EXCEPTION; +} + +static JSValue athena_fontrender_print(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { + float x, y; + + JSFontRenderData *render_data = JS_GetOpaque2(ctx, this_val, js_fontrender_class_id); + + JS_ToFloat32(ctx, &x, argv[0]); + JS_ToFloat32(ctx, &y, argv[1]); + + if (render_data->font_data->type == 1){ + printFontText(render_data->font_data->data, render_data->text, x, y, render_data->font_data->scale, render_data->font_data->color); + } else if (render_data->font_data->type == 0){ + printFontMText(render_data->text, x, y, render_data->font_data->scale, render_data->font_data->color); + } else { + fntRenderString(render_data->font_data->id, x, y, 0, 0, 0, render_data->text, render_data->font_data->color); + } + + return JS_UNDEFINED; +} + +static JSClassDef js_fontrender_class = { + "FontRender", + //.finalizer = js_std_file_finalizer, +}; + +static const JSCFunctionListEntry js_fontrender_proto_funcs[] = { + JS_CFUNC_DEF("print", 2, athena_fontrender_print), +}; + static JSClassDef js_font_class = { "Font", .finalizer = athena_font_dtor, }; static const JSCFunctionListEntry js_font_proto_funcs[] = { - //JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0), JS_CGETSET_DEF("scale", athena_font_get_scale, athena_font_set_scale), JS_CGETSET_DEF("color", athena_font_get_color, athena_font_set_color), JS_CFUNC_DEF("print", 3, athena_font_print), + JS_CFUNC_DEF("render", 3, athena_render_font), JS_CFUNC_DEF("getTextSize", 1, athena_font_gettextsize), }; +static void js_renderfont_init(JSContext *ctx) +{ + JSValue proto; + + /* the class ID is created once */ + JS_NewClassID(&js_fontrender_class_id); + /* the class is created once per runtime */ + JS_NewClass(JS_GetRuntime(ctx), js_fontrender_class_id, &js_fontrender_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_fontrender_proto_funcs, + countof(js_fontrender_proto_funcs)); + JS_SetClassProto(ctx, js_fontrender_class_id, proto); +} + static int font_init(JSContext *ctx, JSModuleDef *m) { JSValue font_proto, font_class; @@ -206,6 +315,8 @@ static int font_init(JSContext *ctx, JSModuleDef *m) { JS_SetModuleExport(ctx, m, "Font", font_class); + js_renderfont_init(ctx); + return 0; } diff --git a/src/ath_image.c b/src/ath_image.c index 4371c7b..87e199f 100644 --- a/src/ath_image.c +++ b/src/ath_image.c @@ -13,7 +13,7 @@ JSClassID get_img_class_id(){ return js_image_class_id; } -int append_img(JSImageData* img, JSImgList* list) +static int append_img(JSImageData* img, JSImgList* list) { JSImageData** aux = malloc((list->size+1)*sizeof(JSImageData*)); @@ -125,6 +125,22 @@ static JSValue athena_image_draw(JSContext *ctx, JSValue this_val, int argc, JSV return JS_UNDEFINED; } +static JSValue athena_image_optimize(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + JSImageData *image = JS_GetOpaque2(ctx, this_val, js_image_class_id); + + switch(image->tex.PSM) { + case GS_PSM_CT24: + gsKit_texture_to_psm16(&(image->tex)); + return JS_NewBool(ctx, true); + break; + default: + return JS_NewBool(ctx, false); + } + + return JS_UNDEFINED; +} + + static JSValue js_image_get(JSContext *ctx, JSValueConst this_val, int magic) { JSValue val = JS_UNDEFINED; @@ -204,22 +220,79 @@ static JSValue athena_image_get_uint(JSContext *ctx, JSValueConst this_val, int if (!s) return JS_EXCEPTION; - return JS_NewUint32(ctx, (magic == 0? s->color : s->tex.Filter)); + switch (magic) { + case 0: + return JS_NewUint32(ctx, s->color); + case 1: + return JS_NewUint32(ctx, s->tex.Filter); + case 2: + return JS_NewUint32(ctx, gsKit_texture_size_ee(s->tex.Width, s->tex.Height, s->tex.PSM)); + case 3: + switch (s->tex.PSM) { + case GS_PSM_T4: + return JS_NewUint32(ctx, 4); + case GS_PSM_T8: + return JS_NewUint32(ctx, 8); + case GS_PSM_CT16: + case GS_PSM_CT16S: + return JS_NewUint32(ctx, 16); + case GS_PSM_CT24: + return JS_NewUint32(ctx, 24); + case GS_PSM_CT32: + return JS_NewUint32(ctx, 32); + } + case 4: + return JS_NewBool(ctx, s->delayed); + case 5: + return JS_NewArrayBuffer(ctx, s->tex.Mem, gsKit_texture_size_ee(s->tex.Width, s->tex.Height, s->tex.PSM), NULL, NULL, 1); + case 6: + if (s->tex.PSM == GS_PSM_T4) { + return JS_NewArrayBuffer(ctx, s->tex.Clut, gsKit_texture_size_ee(8, 2, GS_PSM_CT32), NULL, NULL, 1); + } else if (s->tex.PSM == GS_PSM_T8) { + return JS_NewArrayBuffer(ctx, s->tex.Clut, gsKit_texture_size_ee(16, 16, GS_PSM_CT32), NULL, NULL, 1); + } + + } + + return JS_UNDEFINED; } static JSValue athena_image_set_uint(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) { JSImageData *s = JS_GetOpaque2(ctx, this_val, js_image_class_id); - uint32_t value; + uint32_t value, arr_size; if (!s) return JS_EXCEPTION; if (JS_ToUint32(ctx, &value, val)) return JS_EXCEPTION; - - if(magic == 0){ - s->color = value; - } else { - s->tex.Filter = value; + + switch (magic) { + case 0: + s->color = value; + break; + case 1: + s->tex.Filter = value; + break; + case 5: + if (s->tex.Delayed) { + void *pixels = JS_GetArrayBuffer(ctx, &arr_size, val); + if (pixels != s->tex.Mem) { + free(s->tex.Mem); + s->tex.Mem = pixels; + } + } + break; + case 6: + if (s->tex.Clut && s->tex.Delayed) { + void *palette = JS_GetArrayBuffer(ctx, &arr_size, val); + if (palette != s->tex.Clut) { + free(s->tex.Clut); + s->tex.Clut = palette; + } + } + break; + + } return JS_UNDEFINED; @@ -233,6 +306,7 @@ static JSClassDef js_image_class = { static const JSCFunctionListEntry js_image_proto_funcs[] = { JS_CFUNC_DEF("draw", 2, athena_image_draw), JS_CFUNC_DEF("ready", 0, athena_image_isloaded), + JS_CFUNC_DEF("optimize", 0, athena_image_optimize), JS_CGETSET_MAGIC_DEF("width", js_image_get, js_image_set, 0), JS_CGETSET_MAGIC_DEF("height", js_image_get, js_image_set, 1), JS_CGETSET_MAGIC_DEF("startx", js_image_get, js_image_set, 2), @@ -242,6 +316,11 @@ static const JSCFunctionListEntry js_image_proto_funcs[] = { JS_CGETSET_MAGIC_DEF("angle", js_image_get, js_image_set, 6), JS_CGETSET_MAGIC_DEF("color", athena_image_get_uint, athena_image_set_uint, 0), JS_CGETSET_MAGIC_DEF("filter", athena_image_get_uint, athena_image_set_uint, 1), + JS_CGETSET_MAGIC_DEF("size", athena_image_get_uint, athena_image_set_uint, 2), + JS_CGETSET_MAGIC_DEF("bpp", athena_image_get_uint, athena_image_set_uint, 3), + JS_CGETSET_MAGIC_DEF("delayed", athena_image_get_uint, athena_image_set_uint, 4), + JS_CGETSET_MAGIC_DEF("pixels", athena_image_get_uint, athena_image_set_uint, 5), + JS_CGETSET_MAGIC_DEF("palette", athena_image_get_uint, athena_image_set_uint, 6), }; static int image_init(JSContext *ctx, JSModuleDef *m) { diff --git a/src/ath_network.c b/src/ath_network.c index 957ab9c..7a5ad9e 100644 --- a/src/ath_network.c +++ b/src/ath_network.c @@ -62,10 +62,10 @@ static JSValue athena_nw_get_config(JSContext *ctx, JSValue this_val, int argc, if (ps2ip_getconfig("sm0", &ip_info) >= 0) { obj = JS_NewObject(ctx); - JS_DefinePropertyValueStr(ctx, obj, "ip", JS_NewString(ctx, inet_ntoa(ip_info.ipaddr)), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "netmask", JS_NewString(ctx, inet_ntoa(ip_info.netmask)), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "gateway", JS_NewString(ctx, inet_ntoa(ip_info.gw)), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "dns", JS_NewString(ctx, inet_ntoa(*dns_getserver(0))), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ip", JS_NewString(ctx, ip4addr_ntoa(&ip_info.ipaddr)), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "netmask", JS_NewString(ctx, ip4addr_ntoa(&ip_info.netmask)), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "gateway", JS_NewString(ctx, ip4addr_ntoa(&ip_info.gw)), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "dns", JS_NewString(ctx, ip4addr_ntoa(dns_getserver(0))), JS_PROP_C_W_E); } else { obj = JS_ThrowInternalError(ctx, "Unable to read network info.\n"); } @@ -86,12 +86,12 @@ static JSValue athena_nw_deinit(JSContext *ctx, JSValue this_val, int argc, JSVa static JSValue athena_nw_gethostbyname(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { const char* host = JS_ToCString(ctx, argv[0]); - struct hostent *host_address = lwip_gethostbyname(host); + struct hostent *host_address = gethostbyname(host); if (host_address == NULL) return JS_ThrowInternalError(ctx, "Unable to resolve address.\n"); - return JS_NewString(ctx, inet_ntoa(*(struct in_addr*)host_address->h_addr)); + return JS_NewString(ctx, ip4addr_ntoa((struct in_addr*)host_address->h_addr)); } static void athena_nw_dtor(JSRuntime *rt, JSValue val) @@ -343,7 +343,7 @@ static JSValue athena_nw_requests_async_get(JSContext *ctx, JSValue this_val, in s->chunk.size = 0; /* no data at this point */ s->chunk.transferring = false; - s->tid = create_task("Requests: Get", requestThread, 4096*10, 16); + s->tid = create_task("Requests: Get", requestThread, 1638400, 16); init_task(s->tid, (void*)s); return JS_UNDEFINED; @@ -353,13 +353,16 @@ static JSValue athena_nw_requests_async_get(JSContext *ctx, JSValue this_val, in static JSValue athena_nw_requests_ready(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { JSRequestData* s = JS_GetOpaque2(ctx, this_val, js_request_class_id); - int timeout = 999999999, transfer_timeout = 60; + int timeout = 999999999, transfer_timeout = 3000; if (argc > 0) { JS_ToInt32(ctx, &timeout, argv[0]); if(argc > 1) { JS_ToInt32(ctx, &transfer_timeout, argv[1]); } + + + if ((clock() - s->chunk.timer) / 1000 > timeout && s->chunk.transferring) { s->ready = true; } else if ((clock() - s->chunk.timer) / 1000 > transfer_timeout && !s->chunk.transferring && s->chunk.timer > 0) { diff --git a/src/ath_pads.c b/src/ath_pads.c index 4e0e28f..fef92ef 100644 --- a/src/ath_pads.c +++ b/src/ath_pads.c @@ -4,6 +4,8 @@ #include #include +static JSClassID js_pads_class_id; + static JSValue athena_gettype(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ if (argc != 0 && argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); int port = 0; @@ -15,11 +17,53 @@ static JSValue athena_gettype(JSContext *ctx, JSValue this_val, int argc, JSValu return JS_NewInt32(ctx, mode); } -static JSValue athena_getpad(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ +static JSValue athena_new_pad_event(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + int buttons, flavour; + JS_ToInt32(ctx, &buttons, argv[0]); + JS_ToInt32(ctx, &flavour, argv[1]); + int id = js_new_input_event(buttons, JS_DupValue(ctx, argv[2]), flavour); + return JS_NewInt32(ctx, id); +} + +static JSValue athena_delete_pad_event(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + int id; + JS_ToInt32(ctx, &id, argv[0]); + js_delete_input_event(id); + return JS_UNDEFINED; +} + +static JSValue athena_getstate(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + if (argc != 0 && argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); + int port = 0; + if (argc == 1){ + JS_ToInt32(ctx, &port, argv[0]); + if (port > 1) return JS_ThrowSyntaxError(ctx, "wrong port number."); + } + int mode = padGetState(port, 0); + return JS_NewInt32(ctx, mode); +} + +static bool pad1_initialized = false; + +static JSValue athena_getpad(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv){ int port = 0; + JSPads* pad; + JSValue obj = JS_UNDEFINED; + JSValue proto; + + obj = JS_NewObjectClass(ctx, js_pads_class_id); + if (JS_IsException(obj)) + goto fail; + pad = js_mallocz(ctx, sizeof(*pad)); + if (!pad) + return JS_EXCEPTION; if (argc == 1){ JS_ToInt32(ctx, &port, argv[0]); + if (port == 1 && !pad1_initialized) { + initializePad(1, 0); + pad1_initialized = true; + } if (port > 1) return JS_ThrowSyntaxError(ctx, "wrong port number."); } @@ -51,25 +95,118 @@ static JSValue athena_getpad(JSContext *ctx, JSValue this_val, int argc, JSValue } } - JSValue obj = JS_NewObject(ctx); - JS_DefinePropertyValueStr(ctx, obj, "btns", JS_NewUint32(ctx, paddata), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "lx", JS_NewInt32(ctx, buttons.ljoy_h-127), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ly", JS_NewInt32(ctx, buttons.ljoy_v-127), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "rx", JS_NewInt32(ctx, buttons.rjoy_h-127), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ry", JS_NewInt32(ctx, buttons.rjoy_v-127), JS_PROP_C_W_E); + pad->btns = paddata; + pad->lx = buttons.ljoy_h-127; + pad->ly = buttons.ljoy_v-127; + pad->rx = buttons.rjoy_h-127; + pad->ry = buttons.rjoy_v-127; + pad->port = port; - return obj; + JS_SetOpaque(obj, pad); + return obj; + + fail: + js_free(ctx, pad); + return JS_EXCEPTION; +} + +void js_pads_update(JSPads *pad) { + struct padButtonStatus buttons; + u32 paddata = 0; + int ret; + + int state = padGetState(pad->port, 0); + + if ((state == PAD_STATE_STABLE) || (state == PAD_STATE_FINDCTP1)) { + // pad is connected. Read pad button information. + ret = padRead(pad->port, 0, &buttons); // port, slot, buttons + if (ret != 0) { + paddata = 0xffff ^ buttons.btns; + } + } + + if (ds34bt_get_status(pad->port) & DS34BT_STATE_RUNNING) { + ret = ds34bt_get_data(pad->port, (u8 *)&buttons.btns); + if (ret != 0) { + paddata |= 0xffff ^ buttons.btns; + } + } + + if (ds34usb_get_status(pad->port) & DS34USB_STATE_RUNNING) { + ret = ds34usb_get_data(pad->port, (u8 *)&buttons.btns); + if (ret != 0) { + paddata |= 0xffff ^ buttons.btns; + } + } + + pad->old_btns = pad->btns; + pad->old_lx = pad->lx; + pad->old_ly = pad->ly; + pad->old_rx = pad->rx; + pad->old_ry = pad->ry; + + pad->btns = paddata; + pad->lx = buttons.ljoy_h-127; + pad->ly = buttons.ljoy_v-127; + pad->rx = buttons.rjoy_h-127; + pad->ry = buttons.rjoy_v-127; +} + +static JSValue athena_update(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + JSPads *pad = JS_GetOpaque2(ctx, this_val, js_pads_class_id); + + struct padButtonStatus buttons; + u32 paddata = 0; + int ret; + + int state = padGetState(pad->port, 0); + + if ((state == PAD_STATE_STABLE) || (state == PAD_STATE_FINDCTP1)) { + // pad is connected. Read pad button information. + ret = padRead(pad->port, 0, &buttons); // port, slot, buttons + if (ret != 0) { + paddata = 0xffff ^ buttons.btns; + } + } + + if (ds34bt_get_status(pad->port) & DS34BT_STATE_RUNNING) { + ret = ds34bt_get_data(pad->port, (u8 *)&buttons.btns); + if (ret != 0) { + paddata |= 0xffff ^ buttons.btns; + } + } + + if (ds34usb_get_status(pad->port) & DS34USB_STATE_RUNNING) { + ret = ds34usb_get_data(pad->port, (u8 *)&buttons.btns); + if (ret != 0) { + paddata |= 0xffff ^ buttons.btns; + } + } + + pad->old_btns = pad->btns; + pad->old_lx = pad->lx; + pad->old_ly = pad->ly; + pad->old_rx = pad->rx; + pad->old_ry = pad->ry; + + pad->btns = paddata; + pad->lx = buttons.ljoy_h-127; + pad->ly = buttons.ljoy_v-127; + pad->rx = buttons.rjoy_h-127; + pad->ry = buttons.rjoy_v-127; + + return JS_UNDEFINED; } static JSValue athena_getpressure(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ if (argc != 1 && argc != 2) return JS_ThrowSyntaxError(ctx, "wrong number of arguments."); int port = 0; - int button; + u32 button; if (argc == 2) { JS_ToInt32(ctx, &port, argv[0]); - JS_ToInt32(ctx, &button, argv[1]); + JS_ToUint32(ctx, &button, argv[1]); } else { - JS_ToInt32(ctx, &button, argv[0]); + JS_ToUint32(ctx, &button, argv[0]); } struct padButtonStatus pad; @@ -149,17 +286,28 @@ static JSValue athena_rumble(JSContext *ctx, JSValue this_val, int argc, JSValue } static JSValue athena_check(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - int pad, button; - JSValue val; + int button; + JSPads *pad = JS_GetOpaque2(ctx, this_val, js_pads_class_id); + + JS_ToInt32(ctx, &button, argv[0]); + + return JS_NewBool(ctx, (pad->btns & button)); +} - val = JS_GetPropertyStr(ctx, argv[0], "btns"); - JS_ToUint32(ctx, &pad, val); +static JSValue athena_justpressed(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + int button; + JSPads *pad = JS_GetOpaque2(ctx, this_val, js_pads_class_id); - JS_ToInt32(ctx, &button, argv[1]); + JS_ToInt32(ctx, &button, argv[0]); - JS_FreeValue(ctx, val); + return JS_NewBool(ctx, (pad->btns & button) && !(pad->old_btns & button)); +} - return JS_NewBool(ctx, (pad & button)); +static JSValue athena_seteventhandler(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + int button; + JSPads *pad = JS_GetOpaque2(ctx, this_val, js_pads_class_id); + js_set_input_event_handler(pad); + return JS_UNDEFINED; } static JSValue athena_set_led(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ @@ -185,13 +333,112 @@ static JSValue athena_set_led(JSContext *ctx, JSValue this_val, int argc, JSValu return JS_UNDEFINED; } +static JSValue js_pad_get_prop(JSContext *ctx, JSValueConst this_val, int magic) +{ + JSValue val = JS_UNDEFINED; + JSPads *pad = JS_GetOpaque2(ctx, this_val, js_pads_class_id); + + if (!pad){ + return JS_EXCEPTION; + } + + switch(magic) { + case 0: + val = JS_NewUint32(ctx, pad->btns); + break; + case 1: + val = JS_NewInt32(ctx, pad->lx); + break; + case 2: + val = JS_NewInt32(ctx, pad->ly); + break; + case 3: + val = JS_NewInt32(ctx, pad->rx); + break; + case 4: + val = JS_NewInt32(ctx, pad->ry); + break; + case 5: + val = JS_NewUint32(ctx, pad->old_btns); + break; + case 6: + val = JS_NewInt32(ctx, pad->old_lx); + break; + case 7: + val = JS_NewInt32(ctx, pad->old_ly); + break; + case 8: + val = JS_NewInt32(ctx, pad->old_rx); + break; + case 9: + val = JS_NewInt32(ctx, pad->old_ry); + break; + } + + return val; + +} + +static JSValue js_pad_set_prop(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ + JSPads *pad = JS_GetOpaque2(ctx, this_val, js_pads_class_id); + u32 v; + + if (!pad || JS_ToUint32(ctx, &v, val)){ + return JS_EXCEPTION; + } + + switch(magic) { + case 0: + pad->btns = v; + break; + case 1: + pad->lx = v; + break; + case 2: + pad->ly = v; + break; + case 3: + pad->rx = v; + break; + case 4: + pad->ry = v; + break; + case 5: + pad->old_btns = v; + break; + case 6: + pad->old_lx = v; + break; + case 7: + pad->old_ly = v; + break; + case 8: + pad->old_rx = v; + break; + case 9: + pad->old_ry = v; + break; + } + + return JS_UNDEFINED; +} + static const JSCFunctionListEntry module_funcs[] = { JS_CFUNC_DEF("get", 1, athena_getpad), JS_CFUNC_DEF("getType", 1, athena_gettype), + JS_CFUNC_DEF("getState", 1, athena_getstate), JS_CFUNC_DEF("getPressure", 2, athena_getpressure), JS_CFUNC_DEF("rumble", 3, athena_rumble), JS_CFUNC_DEF("setLED", 4, athena_set_led), - JS_CFUNC_DEF("check", 2, athena_check), + + JS_CFUNC_DEF("newEvent", 3, athena_new_pad_event), + JS_CFUNC_DEF("deleteEvent", 1, athena_delete_pad_event), + + JS_PROP_INT32_DEF("PRESSED", PRESSED_EVENT, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("JUST_PRESSED", JUSTPRESSED_EVENT, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("NONPRESSED", NONPRESSED_EVENT, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SELECT", PAD_SELECT, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("START", PAD_START, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("UP", PAD_UP, JS_PROP_CONFIGURABLE ), @@ -208,17 +455,65 @@ static const JSCFunctionListEntry module_funcs[] = { JS_PROP_INT32_DEF("R2", PAD_R2, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("L3", PAD_L3, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("R3", PAD_R3, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("DIGITAL", PAD_TYPE_DIGITAL, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("ANALOG", PAD_TYPE_ANALOG, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("DUALSHOCK", PAD_TYPE_DUALSHOCK, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_NEJICON", PAD_TYPE_NEJICON, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_KONAMIGUN", PAD_TYPE_KONAMIGUN, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_DIGITAL", PAD_TYPE_DIGITAL, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_ANALOG", PAD_TYPE_ANALOG, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_NAMCOGUN", PAD_TYPE_NAMCOGUN, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_DUALSHOCK", PAD_TYPE_DUALSHOCK, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_JOGCON", PAD_TYPE_JOGCON, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_EX_TSURICON", PAD_TYPE_EX_TSURICON, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("TYPE_EX_JOGCON", PAD_TYPE_EX_JOGCON, JS_PROP_CONFIGURABLE ), + + JS_PROP_INT32_DEF("STATE_DISCONN", PAD_STATE_DISCONN, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("STATE_FINDPAD", PAD_STATE_FINDPAD, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("STATE_FINDCTP1", PAD_STATE_FINDCTP1, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("STATE_EXECCMD", PAD_STATE_EXECCMD, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("STATE_STABLE", PAD_STATE_STABLE, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("STATE_ERROR", PAD_STATE_ERROR, JS_PROP_CONFIGURABLE ), }; -static int module_init(JSContext *ctx, JSModuleDef *m) +static const JSCFunctionListEntry js_pad_proto_funcs[] = { + JS_CFUNC_DEF("update", 0, athena_update), + JS_CFUNC_DEF("pressed", 1, athena_check), + JS_CFUNC_DEF("justPressed", 1, athena_justpressed), + JS_CFUNC_DEF("setEventHandler", 0, athena_seteventhandler), + JS_CGETSET_MAGIC_DEF("btns", js_pad_get_prop, js_pad_set_prop, 0), + JS_CGETSET_MAGIC_DEF("lx", js_pad_get_prop, js_pad_set_prop, 1), + JS_CGETSET_MAGIC_DEF("ly", js_pad_get_prop, js_pad_set_prop, 2), + JS_CGETSET_MAGIC_DEF("rx", js_pad_get_prop, js_pad_set_prop, 3), + JS_CGETSET_MAGIC_DEF("ry", js_pad_get_prop, js_pad_set_prop, 4), + JS_CGETSET_MAGIC_DEF("old_btns", js_pad_get_prop, js_pad_set_prop, 5), + JS_CGETSET_MAGIC_DEF("old_lx", js_pad_get_prop, js_pad_set_prop, 6), + JS_CGETSET_MAGIC_DEF("old_ly", js_pad_get_prop, js_pad_set_prop, 7), + JS_CGETSET_MAGIC_DEF("old_rx", js_pad_get_prop, js_pad_set_prop, 8), + JS_CGETSET_MAGIC_DEF("old_ry", js_pad_get_prop, js_pad_set_prop, 9), +}; + +static JSClassDef js_pads_class = { + "Pad", + //.finalizer = js_std_file_finalizer, +}; + +static int js_pads_init(JSContext *ctx, JSModuleDef *m) { - return JS_SetModuleExportList(ctx, m, module_funcs, countof(module_funcs)); + JSValue proto; + + /* the class ID is created once */ + JS_NewClassID(&js_pads_class_id); + /* the class is created once per runtime */ + JS_NewClass(JS_GetRuntime(ctx), js_pads_class_id, &js_pads_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_pad_proto_funcs, + countof(js_pad_proto_funcs)); + JS_SetClassProto(ctx, js_pads_class_id, proto); + + return JS_SetModuleExportList(ctx, m, module_funcs, + countof(module_funcs)); +; } JSModuleDef *athena_pads_init(JSContext* ctx){ - return athena_push_module(ctx, module_init, module_funcs, countof(module_funcs), "Pads"); + return athena_push_module(ctx, js_pads_init, module_funcs, countof(module_funcs), "Pads"); } diff --git a/src/ath_physics.c b/src/ath_physics.c new file mode 100644 index 0000000..7fc3bf2 --- /dev/null +++ b/src/ath_physics.c @@ -0,0 +1,287 @@ +#include "ath_env.h" +#include +#include + +#include "include/render.h" + +static bool overlap(float min1, float max1, float min2, float max2) { + return max1 >= min2 && max2 >= min1; +} + +static bool boxBoxCollide(VECTOR bbox1[8], VECTOR bbox2[8], float coords1[3], float coords2[3], float collision[3]) { + for (int i = 0; i < 3; ++i) { + float min1 = bbox1[0][i] + coords1[i], max1 = bbox1[0][i] + coords1[i]; + float min2 = bbox2[0][i] + coords2[i], max2 = bbox2[0][i] + coords2[i]; + + for (int j = 1; j < 8; ++j) { + min1 = (bbox1[j][i] + coords1[i] < min1) ? bbox1[j][i] + coords1[i] : min1; + max1 = (bbox1[j][i] + coords1[i] > max1) ? bbox1[j][i] + coords1[i] : max1; + min2 = (bbox2[j][i] + coords2[i] < min2) ? bbox2[j][i] + coords2[i] : min2; + max2 = (bbox2[j][i] + coords2[i] > max2) ? bbox2[j][i] + coords2[i] : max2; + } + + if (!overlap(min1, max1, min2, max2)) + return false; + + float middle1 = (max1 + min1) / 2.0f; + float middle2 = (max2 + min2) / 2.0f; + collision[i] = (middle1 + middle2) / 2.0f; + } + return true; +} + + +static float distanceSquared(float x1, float y1, float z1, float x2, float y2, float z2) { + float dx = x2 - x1; + float dy = y2 - y1; + float dz = z2 - z1; + return dx * dx + dy * dy + dz * dz; +} + +static bool sphereBoxCollide(float sphereCenterX, float sphereCenterY, float sphereCenterZ, float sphereRadius, + VECTOR bbox[8], float coords[3], float collision[3]) { + + float closestX = fminf(fmaxf(sphereCenterX, bbox[0][0] + coords[0]), bbox[1][0] + coords[0]); + float closestY = fminf(fmaxf(sphereCenterY, bbox[0][1] + coords[1]), bbox[2][1] + coords[1]); + float closestZ = fminf(fmaxf(sphereCenterZ, bbox[0][2] + coords[2]), bbox[4][2] + coords[2]); + + + float distanceSq = distanceSquared(sphereCenterX, sphereCenterY, sphereCenterZ, closestX, closestY, closestZ); + bool collides = distanceSq <= (sphereRadius * sphereRadius); + + if (collides) { + + collision[0] = closestX; + collision[1] = closestY; + collision[2] = closestZ; + } + + return collides; +} + + +static bool sphereSphereCollide(float sphere1X, float sphere1Y, float sphere1Z, float sphere1Radius, + float sphere2X, float sphere2Y, float sphere2Z, float sphere2Radius, + float collision[3]) { + + float dx = sphere2X - sphere1X; + float dy = sphere2Y - sphere1Y; + float dz = sphere2Z - sphere1Z; + float distance = sqrtf(dx * dx + dy * dy + dz * dz); + + + if (distance <= (sphere1Radius + sphere2Radius)) { + + float scaleFactor = sphere1Radius / (sphere1Radius + sphere2Radius); + collision[0] = sphere1X + dx * scaleFactor; + collision[1] = sphere1Y + dy * scaleFactor; + collision[2] = sphere1Z + dz * scaleFactor; + return true; + } + return false; +} + +static void createBox(VECTOR bbox[8], float size[3]) { + float halfSize[3] = { size[0] / 2.0f, size[1] / 2.0f, size[2] / 2.0f }; + + bbox[0][0] = -halfSize[0]; + bbox[0][1] = -halfSize[1]; + bbox[0][2] = -halfSize[2]; + + bbox[1][0] = halfSize[0]; + bbox[1][1] = -halfSize[1]; + bbox[1][2] = -halfSize[2]; + + bbox[2][0] = halfSize[0]; + bbox[2][1] = halfSize[1]; + bbox[2][2] = -halfSize[2]; + + bbox[3][0] = -halfSize[0]; + bbox[3][1] = halfSize[1]; + bbox[3][2] = -halfSize[2]; + + bbox[4][0] = -halfSize[0]; + bbox[4][1] = -halfSize[1]; + bbox[4][2] = halfSize[2]; + + bbox[5][0] = halfSize[0]; + bbox[5][1] = -halfSize[1]; + bbox[5][2] = halfSize[2]; + + bbox[6][0] = halfSize[0]; + bbox[6][1] = halfSize[1]; + bbox[6][2] = halfSize[2]; + + bbox[7][0] = -halfSize[0]; + bbox[7][1] = halfSize[1]; + bbox[7][2] = halfSize[2]; +} + +static JSValue js_physics_bbox_collide(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) +{ + VECTOR bbox1[8], bbox2[8]; + float coords1[3], coords2[3], collision[3]; + + JS_ToFloat32(ctx, &coords1[0], argv[1]); + JS_ToFloat32(ctx, &coords1[1], argv[2]); + JS_ToFloat32(ctx, &coords1[2], argv[3]); + + JS_ToFloat32(ctx, &coords2[0], argv[5]); + JS_ToFloat32(ctx, &coords2[1], argv[6]); + JS_ToFloat32(ctx, &coords2[2], argv[7]); + + for (int i = 0; i < 8; i++) { + JSValue vertex1 = JS_GetPropertyUint32(ctx, argv[0], i); + JSValue vertex2 = JS_GetPropertyUint32(ctx, argv[4], i); + + JS_ToFloat32(ctx, &bbox1[i][0], JS_GetPropertyStr(ctx, vertex1, "x")); + JS_ToFloat32(ctx, &bbox1[i][1], JS_GetPropertyStr(ctx, vertex1, "y")); + JS_ToFloat32(ctx, &bbox1[i][2], JS_GetPropertyStr(ctx, vertex1, "z")); + bbox1[i][3] = 1.0f; + + JS_ToFloat32(ctx, &bbox2[i][0], JS_GetPropertyStr(ctx, vertex2, "x")); + JS_ToFloat32(ctx, &bbox2[i][1], JS_GetPropertyStr(ctx, vertex2, "y")); + JS_ToFloat32(ctx, &bbox2[i][2], JS_GetPropertyStr(ctx, vertex2, "z")); + bbox2[i][3] = 1.0f; + } + + if (boxBoxCollide(bbox1, bbox2, coords1, coords2, collision)) { + JSValue obj = JS_NewObject(ctx); + + JS_DefinePropertyValueStr(ctx, obj, "x", JS_NewFloat32(ctx, collision[0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "y", JS_NewFloat32(ctx, collision[1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "z", JS_NewFloat32(ctx, collision[2]), JS_PROP_C_W_E); + + return obj; + } + + return JS_UNDEFINED; +} + +static JSValue js_physics_spherebox_collide(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) +{ + VECTOR bbox1[8]; + float coords1[3], sphereCoords[3], sphereRadius, collision[3]; + + JS_ToFloat32(ctx, &sphereCoords[0], JS_GetPropertyStr(ctx, argv[0], "x")); + JS_ToFloat32(ctx, &sphereCoords[1], JS_GetPropertyStr(ctx, argv[0], "y")); + JS_ToFloat32(ctx, &sphereCoords[2], JS_GetPropertyStr(ctx, argv[0], "z")); + JS_ToFloat32(ctx, &sphereRadius, JS_GetPropertyStr(ctx, argv[0], "r")); + + JS_ToFloat32(ctx, &coords1[0], argv[2]); + JS_ToFloat32(ctx, &coords1[1], argv[3]); + JS_ToFloat32(ctx, &coords1[2], argv[4]); + + for (int i = 0; i < 8; i++) { + JSValue vertex1 = JS_GetPropertyUint32(ctx, argv[1], i); + + JS_ToFloat32(ctx, &bbox1[i][0], JS_GetPropertyStr(ctx, vertex1, "x")); + JS_ToFloat32(ctx, &bbox1[i][1], JS_GetPropertyStr(ctx, vertex1, "y")); + JS_ToFloat32(ctx, &bbox1[i][2], JS_GetPropertyStr(ctx, vertex1, "z")); + bbox1[i][3] = 1.0f; + } + + if (sphereBoxCollide(sphereCoords[0], sphereCoords[1], sphereCoords[2], sphereRadius, bbox1, coords1, collision)) { + JSValue obj = JS_NewObject(ctx); + + JS_DefinePropertyValueStr(ctx, obj, "x", JS_NewFloat32(ctx, collision[0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "y", JS_NewFloat32(ctx, collision[1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "z", JS_NewFloat32(ctx, collision[2]), JS_PROP_C_W_E); + + return obj; + } + + return JS_UNDEFINED; +} + +static JSValue js_physics_spheresphere_collide(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) +{ + float sphere1Coords[3], sphere1Radius, sphere2Coords[3], sphere2Radius, collision[3]; + + JS_ToFloat32(ctx, &sphere1Coords[0], JS_GetPropertyStr(ctx, argv[0], "x")); + JS_ToFloat32(ctx, &sphere1Coords[1], JS_GetPropertyStr(ctx, argv[0], "y")); + JS_ToFloat32(ctx, &sphere1Coords[2], JS_GetPropertyStr(ctx, argv[0], "z")); + JS_ToFloat32(ctx, &sphere1Radius, JS_GetPropertyStr(ctx, argv[0], "r")); + + JS_ToFloat32(ctx, &sphere2Coords[0], JS_GetPropertyStr(ctx, argv[1], "x")); + JS_ToFloat32(ctx, &sphere2Coords[1], JS_GetPropertyStr(ctx, argv[1], "y")); + JS_ToFloat32(ctx, &sphere2Coords[2], JS_GetPropertyStr(ctx, argv[1], "z")); + JS_ToFloat32(ctx, &sphere2Radius, JS_GetPropertyStr(ctx, argv[1], "r")); + + if (sphereSphereCollide(sphere1Coords[0], sphere1Coords[1], sphere1Coords[2], sphere1Radius, + sphere2Coords[0], sphere2Coords[1], sphere2Coords[2], sphere2Radius, collision)) { + + JSValue obj = JS_NewObject(ctx); + + JS_DefinePropertyValueStr(ctx, obj, "x", JS_NewFloat32(ctx, collision[0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "y", JS_NewFloat32(ctx, collision[1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "z", JS_NewFloat32(ctx, collision[2]), JS_PROP_C_W_E); + + return obj; + } + + return JS_UNDEFINED; +} + + +static JSValue js_physics_sphere_create(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) +{ + float coords1[3], sphereRadius; + + JS_ToFloat32(ctx, &coords1[0], argv[0]); + JS_ToFloat32(ctx, &coords1[1], argv[1]); + JS_ToFloat32(ctx, &coords1[2], argv[2]); + JS_ToFloat32(ctx, &sphereRadius, argv[3]); + + JSValue obj = JS_NewObject(ctx); + + JS_DefinePropertyValueStr(ctx, obj, "x", JS_NewFloat32(ctx, coords1[0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "y", JS_NewFloat32(ctx, coords1[1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "z", JS_NewFloat32(ctx, coords1[2]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "r", JS_NewFloat32(ctx, sphereRadius), JS_PROP_C_W_E); + + return obj; +} + +static JSValue js_physics_box_create(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) +{ + float size[3]; + VECTOR bbox[8]; + + JS_ToFloat32(ctx, &size[0], argv[0]); + JS_ToFloat32(ctx, &size[1], argv[1]); + JS_ToFloat32(ctx, &size[2], argv[2]); + + createBox(bbox, size); + + JSValue array = JS_NewArray(ctx); + + for (int i = 0; i < 8; i++) { + JSValue obj = JS_NewObject(ctx); + + JS_DefinePropertyValueStr(ctx, obj, "x", JS_NewFloat32(ctx, bbox[i][0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "y", JS_NewFloat32(ctx, bbox[i][1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "z", JS_NewFloat32(ctx, bbox[i][2]), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, array, i, obj, JS_PROP_C_W_E); + } + + return array; +} + +static const JSCFunctionListEntry js_physics_funcs[] = { + JS_CFUNC_DEF("createBox", 3, js_physics_box_create), + JS_CFUNC_DEF("createSphere", 4, js_physics_sphere_create), + JS_CFUNC_DEF("boxBoxCollide", 8, js_physics_bbox_collide), + JS_CFUNC_DEF("sphereBoxCollide", 5, js_physics_spherebox_collide), + JS_CFUNC_DEF("sphereSphereCollide", 2, js_physics_spheresphere_collide), +}; + +static int js_physics_init(JSContext *ctx, JSModuleDef *m) +{ + return JS_SetModuleExportList(ctx, m, js_physics_funcs, countof(js_physics_funcs)); +} + +JSModuleDef *athena_physics_init(JSContext *ctx) +{ + return athena_push_module(ctx, js_physics_init, js_physics_funcs, countof(js_physics_funcs), "Physics"); +} diff --git a/src/ath_render.c b/src/ath_render.c index 99dc81e..28ca857 100644 --- a/src/ath_render.c +++ b/src/ath_render.c @@ -8,147 +8,97 @@ #include "ath_env.h" static JSValue athena_initrender(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { - if (argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments."); - float aspect; + float aspect, fov = 0.2f, near = 0.1f, far = 2000.0f; JS_ToFloat32(ctx, &aspect, argv[0]); - init3D(aspect); + if (argc > 1) { + JS_ToFloat32(ctx, &fov, argv[1]); + + if (argc > 2) { + JS_ToFloat32(ctx, &near, argv[2]); + JS_ToFloat32(ctx, &far, argv[3]); + } + } + init3D(aspect, fov, near, far); return JS_UNDEFINED; } -static JSValue athena_loadobj(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - JSImageData *image; - model* res_m; +static JSValue athena_newvertex(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { + float x, y, z, s, t, n1, n2, n3, r, g, b, a; - #ifndef SKIP_ERROR_HANDLING - if (argc != 2 && argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - #endif - const char *file_tbo = JS_ToCString(ctx, argv[0]); //Model filename - - // Loading texture - if(argc == 2) { - image = JS_GetOpaque2(ctx, argv[1], get_img_class_id()); - res_m = loadOBJ(file_tbo, &(image->tex)); - } else { - res_m = loadOBJ(file_tbo, NULL); - } + JS_ToFloat32(ctx, &x, argv[0]); + JS_ToFloat32(ctx, &y, argv[1]); + JS_ToFloat32(ctx, &z, argv[2]); - return JS_NewUint32(ctx, res_m); -} + JS_ToFloat32(ctx, &n1, argv[3]); + JS_ToFloat32(ctx, &n2, argv[4]); + JS_ToFloat32(ctx, &n3, argv[5]); + JS_ToFloat32(ctx, &s, argv[6]); + JS_ToFloat32(ctx, &t, argv[7]); -static JSValue athena_freeobj(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { -#ifndef SKIP_ERROR_HANDLING - if (argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); -#endif - - model* m; - JS_ToUint32(ctx, &m, argv[0]); - - free(m->idxList); - m->idxList = NULL; - free(m->positions); - m->positions = NULL; - free(m->colours); - m->colours = NULL; - free(m->normals); - m->normals = NULL; - free(m->texcoords); - m->texcoords = NULL; - free(m->bounding_box); - m->bounding_box = NULL; - - free(m); - m = NULL; + JS_ToFloat32(ctx, &r, argv[8]); + JS_ToFloat32(ctx, &g, argv[9]); + JS_ToFloat32(ctx, &b, argv[10]); + JS_ToFloat32(ctx, &a, argv[11]); - return JS_UNDEFINED; -} -static JSValue athena_drawobj(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 7) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); + JSValue obj = JS_NewObject(ctx); - float pos_x, pos_y, pos_z, rot_x, rot_y, rot_z; - model* m; - JS_ToUint32(ctx, &m, argv[0]); - - JS_ToFloat32(ctx, &pos_x, argv[1]); - JS_ToFloat32(ctx, &pos_y, argv[2]); - JS_ToFloat32(ctx, &pos_z, argv[3]); - JS_ToFloat32(ctx, &rot_x, argv[4]); - JS_ToFloat32(ctx, &rot_y, argv[5]); - JS_ToFloat32(ctx, &rot_z, argv[6]); - - drawOBJ(m, pos_x, pos_y, pos_z, rot_x, rot_y, rot_z); - - return JS_UNDEFINED; -} + JS_DefinePropertyValueStr(ctx, obj, "x", JS_NewFloat32(ctx, x), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "y", JS_NewFloat32(ctx, y), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "z", JS_NewFloat32(ctx, z), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "n1", JS_NewFloat32(ctx, n1), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "n2", JS_NewFloat32(ctx, n2), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "n3", JS_NewFloat32(ctx, n3), JS_PROP_C_W_E); -static JSValue athena_drawbbox(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 8) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); + JS_DefinePropertyValueStr(ctx, obj, "s", JS_NewFloat32(ctx, s), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "t", JS_NewFloat32(ctx, t), JS_PROP_C_W_E); - float pos_x, pos_y, pos_z, rot_x, rot_y, rot_z; - Color color; - model* m; - JS_ToUint32(ctx, &m, argv[0]); - - JS_ToFloat32(ctx, &pos_x, argv[1]); - JS_ToFloat32(ctx, &pos_y, argv[2]); - JS_ToFloat32(ctx, &pos_z, argv[3]); - JS_ToFloat32(ctx, &rot_x, argv[4]); - JS_ToFloat32(ctx, &rot_y, argv[5]); - JS_ToFloat32(ctx, &rot_z, argv[6]); - JS_ToUint32(ctx, &color, argv[7]); - - draw_bbox(m, pos_x, pos_y, pos_z, rot_x, rot_y, rot_z, color); + JS_DefinePropertyValueStr(ctx, obj, "r", JS_NewFloat32(ctx, r), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "g", JS_NewFloat32(ctx, g), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "b", JS_NewFloat32(ctx, b), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "a", JS_NewFloat32(ctx, a), JS_PROP_C_W_E); - return JS_UNDEFINED; + return obj; } static const JSCFunctionListEntry render_funcs[] = { - JS_CFUNC_DEF( "init", 1, athena_initrender), - JS_CFUNC_DEF( "loadOBJ", 2, athena_loadobj ), - JS_CFUNC_DEF( "drawOBJ", 7, athena_drawobj ), - JS_CFUNC_DEF( "drawBbox", 8, athena_drawbbox), - JS_CFUNC_DEF( "freeOBJ", 1, athena_freeobj ), + JS_CFUNC_DEF( "setView", 2, athena_initrender), + JS_CFUNC_DEF( "vertex", 12, athena_newvertex), + + JS_PROP_INT32_DEF("PL_NO_LIGHTS_COLORS", PL_NO_LIGHTS_COLORS, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("PL_NO_LIGHTS_COLORS_TEX", PL_NO_LIGHTS_COLORS_TEX, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("PL_NO_LIGHTS", PL_NO_LIGHTS, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("PL_NO_LIGHTS_TEX", PL_NO_LIGHTS_TEX, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("PL_DEFAULT", PL_DEFAULT, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("PL_DEFAULT_NO_TEX", PL_DEFAULT_NO_TEX, JS_PROP_CONFIGURABLE ), }; +static JSValue athena_setlight(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + if (argc != 5) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); -static JSValue athena_lightnumber(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - - int lightcount; - JS_ToInt32(ctx, &lightcount, argv[0]); - - setLightQuantity(lightcount); - - return JS_UNDEFINED; -} - -static JSValue athena_createlight(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 8) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - - float dir_x, dir_y, dir_z, r, g, b; - int id, type; + float x, y, z; + int id, attr; JS_ToInt32(ctx, &id, argv[0]); - JS_ToFloat32(ctx, &dir_x, argv[1]); - JS_ToFloat32(ctx, &dir_y, argv[2]); - JS_ToFloat32(ctx, &dir_z, argv[3]); - JS_ToFloat32(ctx, &r, argv[4]); - JS_ToFloat32(ctx, &g, argv[5]); - JS_ToFloat32(ctx, &b, argv[6]); - JS_ToInt32(ctx, &type, argv[7]); + JS_ToInt32(ctx, &attr, argv[1]); + JS_ToFloat32(ctx, &x, argv[2]); + JS_ToFloat32(ctx, &y, argv[3]); + JS_ToFloat32(ctx, &z, argv[4]); - createLight(id, dir_x, dir_y, dir_z, type, r, g, b); + + SetLightAttribute(id, x, y, z, attr); return JS_UNDEFINED; } static const JSCFunctionListEntry light_funcs[] = { - JS_CFUNC_DEF( "set", 8, athena_createlight), - JS_CFUNC_DEF( "create", 1, athena_lightnumber), - JS_PROP_INT32_DEF("AMBIENT", LIGHT_AMBIENT, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("DIRECTIONAL", LIGHT_DIRECTIONAL, JS_PROP_CONFIGURABLE ), + JS_CFUNC_DEF( "set", 5, athena_setlight), + JS_PROP_INT32_DEF("AMBIENT", ATHENA_LIGHT_AMBIENT, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("DIFFUSE", ATHENA_LIGHT_DIFFUSE, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SPECULAR", ATHENA_LIGHT_SPECULAR, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("DIRECTION", ATHENA_LIGHT_DIRECTION, JS_PROP_CONFIGURABLE ), }; @@ -177,9 +127,98 @@ static JSValue athena_camrotation(JSContext *ctx, JSValue this_val, int argc, JS return JS_UNDEFINED; } +static JSValue athena_camtarget(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + if (argc != 3) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); + float x, y, z; + JS_ToFloat32(ctx, &x, argv[0]); + JS_ToFloat32(ctx, &y, argv[1]); + JS_ToFloat32(ctx, &z, argv[2]); + + setCameraTarget(x, y, z); + + return JS_UNDEFINED; +} + +static JSValue athena_camorbit(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + float yaw, pitch; + JS_ToFloat32(ctx, &yaw, argv[0]); + JS_ToFloat32(ctx, &pitch, argv[1]); + + orbitCamera(yaw, pitch); + + return JS_UNDEFINED; +} + +static JSValue athena_camturn(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + float yaw, pitch; + JS_ToFloat32(ctx, &yaw, argv[0]); + JS_ToFloat32(ctx, &pitch, argv[1]); + + turnCamera(yaw, pitch); + + return JS_UNDEFINED; +} + +static JSValue athena_campan(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + float x, y; + JS_ToFloat32(ctx, &x, argv[0]); + JS_ToFloat32(ctx, &y, argv[1]); + + panCamera(x, y); + + return JS_UNDEFINED; +} + +static JSValue athena_camdolly(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + float dist; + JS_ToFloat32(ctx, &dist, argv[0]); + + dollyCamera(dist); + + return JS_UNDEFINED; +} + +static JSValue athena_camzoom(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + float dist; + JS_ToFloat32(ctx, &dist, argv[0]); + + zoomCamera(dist); + + return JS_UNDEFINED; +} + +static JSValue athena_camupdate(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + cameraUpdate(); + + return JS_UNDEFINED; +} + +static JSValue athena_camtype(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + eCameraTypes type; + + JS_ToUint32(ctx, &type, argv[0]); + + setCameraType(type); + + return JS_UNDEFINED; +} + static const JSCFunctionListEntry camera_funcs[] = { JS_CFUNC_DEF("position", 3, athena_camposition), JS_CFUNC_DEF("rotation", 3, athena_camrotation), + + JS_CFUNC_DEF("target", 3, athena_camtarget), + JS_CFUNC_DEF("orbit", 2, athena_camorbit), + JS_CFUNC_DEF("turn", 2, athena_camturn), + JS_CFUNC_DEF("dolly", 1, athena_camdolly), + JS_CFUNC_DEF("zoom", 1, athena_camzoom), + JS_CFUNC_DEF("pan", 2, athena_campan), + + JS_CFUNC_DEF("update", 0, athena_camupdate), + JS_CFUNC_DEF("type", 1, athena_camtype), + + JS_PROP_INT32_DEF("DEFAULT", CAMERA_DEFAULT, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("LOOKAT", CAMERA_LOOKAT, JS_PROP_CONFIGURABLE ), }; static int render_init(JSContext *ctx, JSModuleDef *m) @@ -197,7 +236,414 @@ static int camera_init(JSContext *ctx, JSModuleDef *m) return JS_SetModuleExportList(ctx, m, camera_funcs, countof(camera_funcs)); } +static JSClassID js_object_class_id; + +typedef struct { + model m; + JSValue *textures; +} JSRenderObject; + +static void athena_object_dtor(JSRuntime *rt, JSValue val){ + JSRenderObject* ro = JS_GetOpaque(val, js_object_class_id); + + free(ro->m.positions); + free(ro->m.colours); + free(ro->m.normals); + free(ro->m.texcoords); + + for (int i = 0; i < ro->m.tex_count; i++) { + JS_FreeValueRT(rt, ro->textures[i]); + } + + free(ro->m.textures); + free(ro->m.tex_ranges); + free(ro->textures); + + js_free_rt(rt, ro); +} + +static JSClassDef js_object_class = { + "RenderObject", + .finalizer = athena_object_dtor, +}; + +static JSValue athena_drawobject(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + float pos_x, pos_y, pos_z, rot_x, rot_y, rot_z; + + JSRenderObject* ro = JS_GetOpaque2(ctx, this_val, js_object_class_id); + + JS_ToFloat32(ctx, &pos_x, argv[0]); + JS_ToFloat32(ctx, &pos_y, argv[1]); + JS_ToFloat32(ctx, &pos_z, argv[2]); + JS_ToFloat32(ctx, &rot_x, argv[3]); + JS_ToFloat32(ctx, &rot_y, argv[4]); + JS_ToFloat32(ctx, &rot_z, argv[5]); + + ro->m.render(&ro->m, pos_x, pos_y, pos_z, rot_x, rot_y, rot_z); + + return JS_UNDEFINED; +} + +static JSValue athena_drawbbox(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + float pos_x, pos_y, pos_z, rot_x, rot_y, rot_z; + Color color; + + JSRenderObject* ro = JS_GetOpaque2(ctx, this_val, js_object_class_id); + + JS_ToFloat32(ctx, &pos_x, argv[0]); + JS_ToFloat32(ctx, &pos_y, argv[1]); + JS_ToFloat32(ctx, &pos_z, argv[2]); + JS_ToFloat32(ctx, &rot_x, argv[3]); + JS_ToFloat32(ctx, &rot_y, argv[4]); + JS_ToFloat32(ctx, &rot_z, argv[5]); + JS_ToUint32(ctx, &color, argv[6]); + + draw_bbox(&ro->m, pos_x, pos_y, pos_z, rot_x, rot_y, rot_z, color); + + return JS_UNDEFINED; +} + +static JSValue athena_setpipeline(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + int pipeline; + JSRenderObject* ro = JS_GetOpaque2(ctx, this_val, js_object_class_id); + + JS_ToUint32(ctx, &pipeline, argv[0]); + + return JS_NewUint32(ctx, athena_render_set_pipeline(&ro->m, pipeline)); +} + +static JSValue athena_getpipeline(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + int pipeline; + JSRenderObject* ro = JS_GetOpaque2(ctx, this_val, js_object_class_id); + + return JS_NewUint32(ctx, ro->m.pipeline); +} + +static JSValue athena_settexture(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + int tex_idx; + JSRenderObject* ro = JS_GetOpaque2(ctx, this_val, js_object_class_id); + + JS_ToUint32(ctx, &tex_idx, argv[0]); + + if (ro->m.textures[tex_idx]) { + JS_FreeValue(ctx, ro->textures[tex_idx]); + } + + JS_DupValue(ctx, argv[1]); + JSImageData* image = JS_GetOpaque2(ctx, argv[1], get_img_class_id()); + + if (ro->m.tex_count < (tex_idx+1)) { + ro->m.textures = realloc(ro->m.textures, sizeof(GSTEXTURE*)*(ro->m.tex_count+1)); + ro->m.tex_ranges = realloc(ro->m.tex_ranges, sizeof(int)*(ro->m.tex_count+1)); + } + + ro->textures[tex_idx] = argv[1]; + ro->m.textures[tex_idx] = &(image->tex); + + ro->m.tex_count = (ro->m.tex_count < (tex_idx+1)? (tex_idx+1) : ro->m.tex_count); + + if (argc > 2) { + int range; + JS_ToUint32(ctx, &range, argv[2]); + if (range == -1) { + range = ro->m.indexCount; + } + + ro->m.tex_ranges[tex_idx] = range; + } + + return JS_UNDEFINED; +} + +static JSValue athena_gettexture(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + int tex_idx; + JSRenderObject* ro = JS_GetOpaque2(ctx, this_val, js_object_class_id); + + JS_ToUint32(ctx, &tex_idx, argv[0]); + + if (ro->m.tex_count > tex_idx) { + return ro->textures[tex_idx]; + } + + return JS_UNDEFINED; +} + + + +static JSValue js_object_get(JSContext *ctx, JSValueConst this_val, int magic) +{ + JSRenderObject* ro = JS_GetOpaque2(ctx, this_val, js_object_class_id); + if (!ro) + return JS_EXCEPTION; + + if (magic == 0) { + JSValue array = JS_NewArray(ctx); + + for (int i = 0; i < ro->m.indexCount; i++) { + JSValue obj = JS_NewObject(ctx); + + JS_DefinePropertyValueStr(ctx, obj, "x", JS_NewFloat32(ctx, ro->m.positions[i][0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "y", JS_NewFloat32(ctx, ro->m.positions[i][1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "z", JS_NewFloat32(ctx, ro->m.positions[i][2]), JS_PROP_C_W_E); + + JS_DefinePropertyValueStr(ctx, obj, "n1", JS_NewFloat32(ctx, ro->m.normals[i][0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "n2", JS_NewFloat32(ctx, ro->m.normals[i][1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "n3", JS_NewFloat32(ctx, ro->m.normals[i][2]), JS_PROP_C_W_E); + + JS_DefinePropertyValueStr(ctx, obj, "s", JS_NewFloat32(ctx, ro->m.texcoords[i][0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "t", JS_NewFloat32(ctx, ro->m.texcoords[i][1]), JS_PROP_C_W_E); + + JS_DefinePropertyValueStr(ctx, obj, "r", JS_NewFloat32(ctx, ro->m.colours[i][0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "g", JS_NewFloat32(ctx, ro->m.colours[i][1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "b", JS_NewFloat32(ctx, ro->m.colours[i][2]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "a", JS_NewFloat32(ctx, ro->m.colours[i][3]), JS_PROP_C_W_E); + + JS_DefinePropertyValueUint32(ctx, array, i, obj, JS_PROP_C_W_E); + } + + return array; + } else if (magic == 1) { + return JS_NewUint32(ctx, ro->m.indexCount); + } else if (magic == 2) { + JSValue array = JS_NewArray(ctx); + + for (int i = 0; i < 8; i++) { + JSValue obj = JS_NewObject(ctx); + + JS_DefinePropertyValueStr(ctx, obj, "x", JS_NewFloat32(ctx, ro->m.bounding_box[i][0]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "y", JS_NewFloat32(ctx, ro->m.bounding_box[i][1]), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "z", JS_NewFloat32(ctx, ro->m.bounding_box[i][2]), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, array, i, obj, JS_PROP_C_W_E); + } + + return array; + } +} + +static JSValue js_object_set(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ + JSRenderObject* ro = JS_GetOpaque2(ctx, this_val, js_object_class_id); + if (!ro) + return JS_EXCEPTION; + + if (magic == 0) { + free(ro->m.positions); + free(ro->m.colours); + free(ro->m.normals); + free(ro->m.texcoords); + + int length; + JS_ToUint32(ctx, &length, JS_GetPropertyStr(ctx, val, "length")); + + ro->m.indexCount = length; + ro->m.positions = malloc(sizeof(VECTOR)*ro->m.indexCount); + ro->m.normals = malloc(sizeof(VECTOR)*ro->m.indexCount); + ro->m.texcoords = malloc(sizeof(VECTOR)*ro->m.indexCount); + ro->m.colours = malloc(sizeof(VECTOR)*ro->m.indexCount); + + for (int i = 0; i < length; i++) { + JSValue vertex = JS_GetPropertyUint32(ctx, val, i); + + JS_ToFloat32(ctx, &ro->m.positions[i][0], JS_GetPropertyStr(ctx, vertex, "x")); + JS_ToFloat32(ctx, &ro->m.positions[i][1], JS_GetPropertyStr(ctx, vertex, "y")); + JS_ToFloat32(ctx, &ro->m.positions[i][2], JS_GetPropertyStr(ctx, vertex, "z")); + ro->m.positions[i][3] = 1.0f; + + JS_ToFloat32(ctx, &ro->m.normals[i][0], JS_GetPropertyStr(ctx, vertex, "n1")); + JS_ToFloat32(ctx, &ro->m.normals[i][1], JS_GetPropertyStr(ctx, vertex, "n2")); + JS_ToFloat32(ctx, &ro->m.normals[i][2], JS_GetPropertyStr(ctx, vertex, "n3")); + ro->m.normals[i][3] = 1.0f; + + JS_ToFloat32(ctx, &ro->m.texcoords[i][0], JS_GetPropertyStr(ctx, vertex, "s")); + JS_ToFloat32(ctx, &ro->m.texcoords[i][1], JS_GetPropertyStr(ctx, vertex, "t")); + ro->m.texcoords[i][2] = 1.0f; + ro->m.texcoords[i][3] = 1.0f; + + JS_ToFloat32(ctx, &ro->m.colours[i][0], JS_GetPropertyStr(ctx, vertex, "r")); + JS_ToFloat32(ctx, &ro->m.colours[i][1], JS_GetPropertyStr(ctx, vertex, "g")); + JS_ToFloat32(ctx, &ro->m.colours[i][2], JS_GetPropertyStr(ctx, vertex, "b")); + JS_ToFloat32(ctx, &ro->m.colours[i][3], JS_GetPropertyStr(ctx, vertex, "a")); + } + } else if (magic == 2) { + + for (int i = 0; i < 8; i++) { + JSValue vertex = JS_GetPropertyUint32(ctx, val, i); + + JS_ToFloat32(ctx, &ro->m.bounding_box[i][0], JS_GetPropertyStr(ctx, vertex, "x")); + JS_ToFloat32(ctx, &ro->m.bounding_box[i][1], JS_GetPropertyStr(ctx, vertex, "y")); + JS_ToFloat32(ctx, &ro->m.bounding_box[i][2], JS_GetPropertyStr(ctx, vertex, "z")); + ro->m.bounding_box[i][3] = 1.0f; + } + } + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_object_proto_funcs[] = { + JS_CFUNC_DEF("draw", 6, athena_drawobject), + JS_CFUNC_DEF("drawBounds", 7, athena_drawbbox), + JS_CFUNC_DEF("setPipeline", 1, athena_setpipeline ), + JS_CFUNC_DEF("getPipeline", 0, athena_getpipeline ), + JS_CFUNC_DEF("setTexture", 3, athena_settexture ), + JS_CFUNC_DEF("getTexture", 1, athena_gettexture ), + JS_CGETSET_MAGIC_DEF("vertices", js_object_get, js_object_set, 0), + JS_CGETSET_MAGIC_DEF("size", js_object_get, js_object_set, 1), + JS_CGETSET_MAGIC_DEF("bounds", js_object_get, js_object_set, 2), +}; + +static JSValue athena_object_ctor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) { + JSImageData *image; + JSValue obj = JS_UNDEFINED; + JSValue proto; + + JSRenderObject* ro = js_mallocz(ctx, sizeof(JSRenderObject)); + if (!ro) + return JS_EXCEPTION; + + if (JS_IsArray(ctx, argv[0])) { + memset(ro, 0, sizeof(JSRenderObject)); + + int length; + JS_ToUint32(ctx, &length, JS_GetPropertyStr(ctx, argv[0], "length")); + + ro->m.indexCount = length; + ro->m.positions = malloc(sizeof(VECTOR)*ro->m.indexCount); + ro->m.normals = malloc(sizeof(VECTOR)*ro->m.indexCount); + ro->m.texcoords = malloc(sizeof(VECTOR)*ro->m.indexCount); + ro->m.colours = malloc(sizeof(VECTOR)*ro->m.indexCount); + + for (int i = 0; i < ro->m.indexCount; i++) { + JSValue vertex = JS_GetPropertyUint32(ctx, argv[0], i); + + JS_ToFloat32(ctx, &ro->m.positions[i][0], JS_GetPropertyStr(ctx, vertex, "x")); + JS_ToFloat32(ctx, &ro->m.positions[i][1], JS_GetPropertyStr(ctx, vertex, "y")); + JS_ToFloat32(ctx, &ro->m.positions[i][2], JS_GetPropertyStr(ctx, vertex, "z")); + ro->m.positions[i][3] = 1.0f; + + JS_ToFloat32(ctx, &ro->m.normals[i][0], JS_GetPropertyStr(ctx, vertex, "n1")); + JS_ToFloat32(ctx, &ro->m.normals[i][1], JS_GetPropertyStr(ctx, vertex, "n2")); + JS_ToFloat32(ctx, &ro->m.normals[i][2], JS_GetPropertyStr(ctx, vertex, "n3")); + ro->m.normals[i][3] = 1.0f; + + JS_ToFloat32(ctx, &ro->m.texcoords[i][0], JS_GetPropertyStr(ctx, vertex, "s")); + JS_ToFloat32(ctx, &ro->m.texcoords[i][1], JS_GetPropertyStr(ctx, vertex, "t")); + ro->m.texcoords[i][2] = 1.0f; + ro->m.texcoords[i][3] = 1.0f; + + JS_ToFloat32(ctx, &ro->m.colours[i][0], JS_GetPropertyStr(ctx, vertex, "r")); + JS_ToFloat32(ctx, &ro->m.colours[i][1], JS_GetPropertyStr(ctx, vertex, "g")); + JS_ToFloat32(ctx, &ro->m.colours[i][2], JS_GetPropertyStr(ctx, vertex, "b")); + JS_ToFloat32(ctx, &ro->m.colours[i][3], JS_GetPropertyStr(ctx, vertex, "a")); + } + + if(argc > 1) { + JS_DupValue(ctx, argv[1]); + image = JS_GetOpaque2(ctx, argv[1], get_img_class_id()); + + image->tex.Filter = GS_FILTER_LINEAR; + + ro->m.textures = malloc(sizeof(GSTEXTURE*)); + ro->m.tex_ranges = malloc(sizeof(int)); + ro->textures = malloc(sizeof(JSValue)); + + ro->m.textures[0] = &(image->tex); + ro->m.tex_count = 1; + ro->m.tex_ranges[0] = ro->m.indexCount; + + ro->textures[0] = argv[1]; + } + + ro->m.pipeline = athena_render_set_pipeline(&ro->m, PL_DEFAULT); + + goto register_3d_object; + } + + const char *file_tbo = JS_ToCString(ctx, argv[0]); // Model filename + + // Loading texture + if(argc > 1) { + JS_DupValue(ctx, argv[1]); + image = JS_GetOpaque2(ctx, argv[1], get_img_class_id()); + + ro->textures = malloc(sizeof(JSValue)); + + ro->textures[0] = argv[1]; + + image->tex.Filter = GS_FILTER_LINEAR; + + loadOBJ(&ro->m, file_tbo, &(image->tex)); + + } else if (argc > 0) { + loadOBJ(&ro->m, file_tbo, NULL); + + ro->textures = malloc(sizeof(JSValue)*ro->m.tex_count); + + for (int i = 0; i < ro->m.tex_count; i++) { + JSImageData* image; + JSValue img_obj = JS_UNDEFINED; + + image = js_mallocz(ctx, sizeof(*image)); + if (!image) + return JS_EXCEPTION; + + image->delayed = true; + image->tex = *ro->m.textures[i]; + free(ro->m.textures[i]); + ro->m.textures[i] = &(image->tex); + + image->loaded = true; + image->width = image->tex.Width; + image->height = image->tex.Height; + image->endx = image->tex.Width; + image->endy = image->tex.Height; + + image->startx = 0.0f; + image->starty = 0.0f; + image->angle = 0.0f; + image->color = 0x80808080; + + img_obj = JS_NewObjectClass(ctx, get_img_class_id()); + JS_SetOpaque(img_obj, image); + + ro->textures[i] = img_obj; + } + } + +register_3d_object: + proto = JS_GetPropertyStr(ctx, new_target, "prototype"); + obj = JS_NewObjectProtoClass(ctx, proto, js_object_class_id); + JS_FreeValue(ctx, proto); + JS_SetOpaque(obj, ro); + + return obj; +} + +static int js_object_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue object_proto, object_class; + + /* create the Point class */ + JS_NewClassID(&js_object_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_object_class_id, &js_object_class); + + object_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, object_proto, js_object_proto_funcs, countof(js_object_proto_funcs)); + + object_class = JS_NewCFunction2(ctx, athena_object_ctor, "RenderObject", 2, JS_CFUNC_constructor, 0); + /* set proto.constructor and ctor.prototype */ + JS_SetConstructor(ctx, object_class, object_proto); + JS_SetClassProto(ctx, js_object_class_id, object_proto); + + JS_SetModuleExport(ctx, m, "RenderObject", object_class); + return 0; +} + JSModuleDef *athena_render_init(JSContext* ctx){ + JSModuleDef *m; + m = JS_NewCModule(ctx, "RenderObject", js_object_init); + if (!m) + return NULL; + JS_AddModuleExport(ctx, m, "RenderObject"); + athena_push_module(ctx, render_init, render_funcs, countof(render_funcs), "Render"); athena_push_module(ctx, light_init, light_funcs, countof(light_funcs), "Lights"); return athena_push_module(ctx, camera_init, camera_funcs, countof(camera_funcs), "Camera"); diff --git a/src/ath_screen.c b/src/ath_screen.c index a4ba5bd..2b12b3a 100644 --- a/src/ath_screen.c +++ b/src/ath_screen.c @@ -8,6 +8,18 @@ #include "include/fntsys.h" #include "ath_env.h" +static JSValue athena_set_clear_color(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + static Color clear_color = GS_SETREG_RGBAQ(0x00, 0x00, 0x00, 0x80, 0x00); + JS_ToInt32(ctx, &clear_color, argv[0]); + js_set_clear_color((uint64_t)clear_color); + return JS_UNDEFINED; +} + +static JSValue athena_displayfunc(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + js_set_render_loop_func(JS_DupValue(ctx, argv[0])); + return JS_UNDEFINED; +} + static JSValue athena_flip(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ flipScreen(); return JS_UNDEFINED; @@ -128,6 +140,7 @@ static JSValue athena_scrlog(JSContext *ctx, JSValue this_val, int argc, JSValue str_buf = malloc(512); buf_len = 512; memset(str_buf, 0, buf_len); + fntLoadDefault(NULL); } old_len = str_len; @@ -146,8 +159,10 @@ static JSValue athena_scrlog(JSContext *ctx, JSValue this_val, int argc, JSValue clearScreen(GS_SETREG_RGBAQ(0x00, 0x00, 0x00, 0x80, 0x00)); + fntSetCharSize(0, FNTSYS_CHAR_SIZE*64*0.8f, FNTSYS_CHAR_SIZE*64*0.8f); + if (str_len > 0) { - printFontMText(str_buf, 0, 0, 0.5f, 0x80808080); + fntRenderString(0, 0, 0, 0, 640, 448, str_buf, 0x80808080); } flipScreen(); @@ -164,9 +179,6 @@ static JSValue athena_cls(JSContext *ctx, JSValue this_val, int argc, JSValueCon return JS_UNDEFINED; } - - - static const JSCFunctionListEntry module_funcs[] = { JS_CFUNC_DEF("flip", 0, athena_flip), JS_CFUNC_DEF("clear", 1, athena_clear), @@ -179,6 +191,10 @@ static const JSCFunctionListEntry module_funcs[] = { JS_CFUNC_DEF("setMode", 1, athena_setvmode), JS_CFUNC_DEF("log", 1, athena_scrlog), JS_CFUNC_DEF("cls", 0, athena_cls), + + JS_CFUNC_DEF("clearColor", 1, athena_set_clear_color), + JS_CFUNC_DEF("display", 1, athena_displayfunc), + JS_PROP_INT32_DEF("NTSC", GS_MODE_NTSC, JS_PROP_CONFIGURABLE), JS_PROP_INT32_DEF("DTV_480p", GS_MODE_DTV_480P, JS_PROP_CONFIGURABLE), JS_PROP_INT32_DEF("PAL", GS_MODE_PAL, JS_PROP_CONFIGURABLE), diff --git a/src/ath_socket.c b/src/ath_socket.c index 276de36..bfefc11 100644 --- a/src/ath_socket.c +++ b/src/ath_socket.c @@ -1,8 +1,7 @@ #include "ath_env.h" -#include -#include +#include "include/network.h" #include typedef struct { @@ -32,7 +31,7 @@ static JSValue athena_socket_ctor(JSContext *ctx, JSValueConst new_target, int a JS_ToInt32(ctx, &s->sin_family, argv[0]); JS_ToInt32(ctx, &protocol, argv[1]); - s->id = lwip_socket(s->sin_family, protocol, 0); + s->id = socket(s->sin_family, protocol, 0); proto = JS_GetPropertyStr(ctx, new_target, "prototype"); obj = JS_NewObjectProtoClass(ctx, proto, js_socket_class_id); @@ -57,7 +56,7 @@ static JSValue athena_socket_connect(JSContext *ctx, JSValue this_val, int argc, JS_ToInt32(ctx, &sin_port, argv[1]); addr.sin_port = PP_HTONS(sin_port); - int ret = lwip_connect(s->id, (struct sockaddr*)&addr, sizeof(addr)); + int ret = connect(s->id, (struct sockaddr*)&addr, sizeof(addr)); return JS_NewInt32(ctx, ret); } @@ -77,7 +76,7 @@ static JSValue athena_socket_bind(JSContext *ctx, JSValue this_val, int argc, JS JS_ToInt32(ctx, &sin_port, argv[1]); addr.sin_port = PP_HTONS(sin_port); - int ret = lwip_bind(s->id, (struct sockaddr*)&addr, sizeof(addr)); + int ret = bind(s->id, (struct sockaddr*)&addr, sizeof(addr)); return JS_NewInt32(ctx, ret); } @@ -90,7 +89,7 @@ static JSValue athena_socket_send(JSContext *ctx, JSValue this_val, int argc, JS size_t len = 0; const char* buf = JS_ToCStringLen(ctx, &len, argv[0]); - int ret = lwip_send(s->id, buf, len, MSG_DONTWAIT); + int ret = send(s->id, buf, len, MSG_DONTWAIT); return JS_NewInt32(ctx, ret); } @@ -100,7 +99,7 @@ static JSValue athena_socket_listen(JSContext *ctx, JSValue this_val, int argc, JSSocketData* s = JS_GetOpaque2(ctx, this_val, js_socket_class_id); - int ret = lwip_listen(s->id, 0); + int ret = listen(s->id, 0); return JS_NewInt32(ctx, ret); } @@ -115,7 +114,7 @@ static JSValue athena_socket_recv(JSContext *ctx, JSValue this_val, int argc, JS void* buf = js_mallocz(ctx, len); - lwip_recv(s->id, buf, len, MSG_PEEK); + recv(s->id, buf, len, MSG_PEEK); return JS_NewStringLen(ctx, buf, len); } diff --git a/src/ath_sound.c b/src/ath_sound.c index 14bed5e..0ca1f1a 100644 --- a/src/ath_sound.c +++ b/src/ath_sound.c @@ -83,7 +83,7 @@ static JSValue athena_isplaying(JSContext *ctx, JSValue this_val, int argc, JSVa return JS_NewBool(ctx, is_sound_playing()); } -static JSValue athena_duration(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ +static JSValue athena_get_duration(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ Sound* snd; JS_ToUint32(ctx, &snd, argv[0]); @@ -111,6 +111,26 @@ static JSValue athena_resume(JSContext *ctx, JSValue this_val, int argc, JSValue return JS_UNDEFINED; } +static JSValue athena_restart(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + sound_restart(); + return JS_UNDEFINED; +} + +static JSValue athena_setposition(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + Sound* snd; + uint32_t position; + JS_ToUint32(ctx, &snd, argv[0]); + JS_ToUint32(ctx, &position, argv[1]); + sound_set_position(snd, position); + return JS_UNDEFINED; +} + +static JSValue athena_getposition(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ + Sound* snd; + JS_ToUint32(ctx, &snd, argv[0]); + return JS_NewInt32(ctx, sound_get_position(snd)); +} + static const JSCFunctionListEntry module_funcs[] = { JS_CFUNC_DEF("setVolume", 2, athena_setvolume), JS_CFUNC_DEF("load", 1, athena_load), @@ -118,10 +138,13 @@ static const JSCFunctionListEntry module_funcs[] = { JS_CFUNC_DEF("free", 1, athena_free), JS_CFUNC_DEF("deinit", 0, athena_deinit), JS_CFUNC_DEF("isPlaying", 0, athena_isplaying), - JS_CFUNC_DEF("duration", 1, athena_duration), + JS_CFUNC_DEF("getDuration", 1, athena_get_duration), JS_CFUNC_DEF("repeat", 1, athena_repeat), JS_CFUNC_DEF("pause", 1, athena_pause), JS_CFUNC_DEF("resume", 1, athena_resume), + JS_CFUNC_DEF("restart", 0, athena_restart), + JS_CFUNC_DEF("setPosition", 2, athena_setposition), + JS_CFUNC_DEF("getPosition", 1, athena_getposition), }; static int module_init(JSContext *ctx, JSModuleDef *m){ diff --git a/src/ath_system.c b/src/ath_system.c index 33602e9..5545519 100644 --- a/src/ath_system.c +++ b/src/ath_system.c @@ -16,63 +16,6 @@ #define MAX_DIR_FILES 512 -static JSValue athena_getCurrentDirectory(JSContext *ctx) -{ - char path[256]; - getcwd(path, 256); - return JS_NewString(ctx, path); -} - -static JSValue athena_setCurrentDirectory(JSContext *ctx, JSValueConst *argv) -{ - static char temp_path[256]; - const char *path = JS_ToCString(ctx, argv[0]); - if(!path) return JS_ThrowSyntaxError(ctx, "Argument error: System.currentDirectory(file) takes a filename as string as argument."); - - athena_getCurrentDirectory(ctx); - - // let's do what the ps2sdk should do, - // some normalization... :) - // if absolute path (contains [drive]:path/) - if (strchr(path, ':')) - { - strcpy(temp_path,path); - } - else // relative path - { - // remove last directory ? - if(!strncmp(path, "..", 2)) - { - getcwd(temp_path, 256); - if ((temp_path[strlen(temp_path)-1] != ':')) - { - int idx = strlen(temp_path)-1; - do { - idx--; - } while (temp_path[idx] != '/'); - temp_path[idx] = '\0'; - } - - } else { - getcwd(temp_path, 256); - strcat(temp_path,"/"); - strcat(temp_path,path); - } - } - - dbgprintf("changing directory to %s\n",__ps2_normalize_path(temp_path)); - chdir(__ps2_normalize_path(temp_path)); - - return JS_UNDEFINED; -} - -static JSValue athena_curdir(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { - if(argc == 0) return athena_getCurrentDirectory(ctx); - if(argc == 1) return athena_setCurrentDirectory(ctx, argv); - return JS_ThrowSyntaxError(ctx, "Argument error: System.currentDirectory([file]) takes zero or one argument."); -} - - static JSValue athena_dir(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { @@ -81,7 +24,7 @@ static JSValue athena_dir(JSContext *ctx, JSValue this_val, int argc, JSValueCon JSValue arr = JS_NewArray(ctx); const char *temp_path = ""; - char path[255]; + char path[255], tpath[384]; getcwd((char *)path, 256); dbgprintf("current dir %s\n",(char *)path); @@ -111,14 +54,21 @@ static JSValue athena_dir(JSContext *ctx, JSValue this_val, int argc, JSValueCon d = opendir(path); + struct stat statbuf; + if (d) { while ((dir = readdir(d)) != NULL) { + strcpy(tpath, path); + strcat(tpath, "/"); + strcat(tpath, dir->d_name); + stat(tpath, &statbuf); + JSValue obj = JS_NewObject(ctx); JS_DefinePropertyValueStr(ctx, obj, "name", JS_NewString(ctx, dir->d_name), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "size", JS_NewUint32(ctx, dir->d_stat.st_size), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "dir", JS_NewBool(ctx, S_ISDIR(dir->d_stat.st_mode)), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "size", JS_NewUint32(ctx, statbuf.st_size), JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "dir", JS_NewBool(ctx, (dir->d_type == DT_DIR)), JS_PROP_C_W_E); JS_DefinePropertyValueUint32(ctx, arr, i++, obj, JS_PROP_C_W_E); } @@ -128,15 +78,6 @@ static JSValue athena_dir(JSContext *ctx, JSValue this_val, int argc, JSValueCon return arr; } -static JSValue athena_createDir(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) -{ - const char *path = JS_ToCString(ctx, argv[0]); - if(!path) return JS_ThrowSyntaxError(ctx, "Argument error: System.createDirectory(directory) takes a directory name as string as argument."); - mkdir(path, 0777); - - return JS_UNDEFINED; -} - static JSValue athena_removeDir(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { const char *path = JS_ToCString(ctx, argv[0]); @@ -173,15 +114,6 @@ static JSValue athena_movefile(JSContext *ctx, JSValue this_val, int argc, JSVal return JS_UNDEFINED; } -static JSValue athena_removeFile(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) -{ - const char *path = JS_ToCString(ctx, argv[0]); - if(!path) return JS_ThrowSyntaxError(ctx, "Argument error: System.removeFile(filename) takes a filename as string as argument."); - remove(path); - - return JS_UNDEFINED; -} - static JSValue athena_rename(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { const char *oldName = JS_ToCString(ctx, argv[0]); @@ -252,15 +184,6 @@ static JSValue athena_delay(JSContext *ctx, JSValue this_val, int argc, JSValueC return JS_UNDEFINED; } -static JSValue athena_getFreeMemory(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) -{ - if (argc != 0) return JS_ThrowSyntaxError(ctx, "no arguments expected."); - - size_t result = GetFreeSize(); - - return JS_NewUint32(ctx, (uint32_t)(result)); -} - static JSValue athena_exit(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { if (argc != 0) return JS_ThrowSyntaxError(ctx, "System.exitToBrowser"); @@ -303,89 +226,6 @@ static JSValue athena_getmcinfo(JSContext *ctx, JSValue this_val, int argc, JSVa return obj; } -static JSValue athena_openfile(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 2) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - const char *file_tbo = JS_ToCString(ctx, argv[0]); - int type; - JS_ToInt32(ctx, &type, argv[1]); - - int fd = open(file_tbo, type, 0777); - if (fd < 0) return JS_ThrowSyntaxError(ctx, "cannot open requested file."); - - return JS_NewInt32(ctx, fd); -} - - -static JSValue athena_readfile(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 2) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - - int file; - uint32_t size; - - JS_ToInt32(ctx, &file, argv[0]); - JS_ToUint32(ctx, &size, argv[1]); - uint8_t *buffer = (uint8_t*)malloc(size + 1); - int len = read(file,buffer, size); - buffer[len] = 0; - return JS_NewStringLen(ctx, (const char*)buffer, len); -} - - -static JSValue athena_writefile(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - int fd; - uint32_t size, len; - - JS_ToInt32(ctx, &fd, argv[0]); - unsigned char *data = JS_GetArrayBuffer(ctx, &len, argv[1]); - JS_ToUint32(ctx, &size, argv[2]); - write(fd, data, size); - return JS_UNDEFINED; -} - -static JSValue athena_closefile(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - int fd; - JS_ToInt32(ctx, &fd, argv[0]); - close(fd); - return JS_UNDEFINED; -} - -static JSValue athena_seekfile(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 3) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - int fd; - int pos; - uint32_t type; - - JS_ToInt32(ctx, &fd, argv[0]); - JS_ToInt32(ctx, &pos, argv[1]); - JS_ToUint32(ctx, &type, argv[2]); - - lseek(fd, pos, type); - return JS_UNDEFINED; -} - - -static JSValue athena_sizefile(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - int fd; - JS_ToInt32(ctx, &fd, argv[0]); - uint32_t cur_off = lseek(fd, 0, SEEK_CUR); - uint32_t size = lseek(fd, 0, SEEK_END); - lseek(fd, cur_off, SEEK_SET); - return JS_NewUint32(ctx, size); -} - - -static JSValue athena_checkexist(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv){ - if (argc != 1) return JS_ThrowSyntaxError(ctx, "wrong number of arguments"); - const char *file_tbo = JS_ToCString(ctx, argv[0]); - int fd = open(file_tbo, O_RDONLY, 0777); - if (fd < 0) return JS_NewBool(ctx, false); - close(fd); - return JS_NewBool(ctx, true); -} - - static JSValue athena_loadELF(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { @@ -668,26 +508,15 @@ static JSValue athena_stacktrace(JSContext *ctx, JSValue this_val, int argc, JSV } static const JSCFunctionListEntry system_funcs[] = { - JS_CFUNC_DEF( "openFile", 2, athena_openfile), - JS_CFUNC_DEF( "readFile", 2, athena_readfile ), - JS_CFUNC_DEF( "writeFile", 3, athena_writefile ), - JS_CFUNC_DEF( "closeFile", 1, athena_closefile ), - JS_CFUNC_DEF( "seekFile", 3, athena_seekfile ), - JS_CFUNC_DEF( "sizeFile", 1, athena_sizefile ), - JS_CFUNC_DEF( "doesFileExist", 1, athena_checkexist ), - JS_CFUNC_DEF( "currentDir", 1, athena_curdir ), JS_CFUNC_DEF( "listDir", 1, athena_dir ), - JS_CFUNC_DEF( "createDirectory", 1, athena_createDir ), JS_CFUNC_DEF( "removeDirectory", 1, athena_removeDir ), JS_CFUNC_DEF( "moveFile", 2, athena_movefile ), JS_CFUNC_DEF( "copyFile", 2, athena_copyfile ), JS_CFUNC_DEF( "threadCopyFile", 2, athena_copyasync ), JS_CFUNC_DEF( "getFileProgress", 0, athena_getfileprogress ), - JS_CFUNC_DEF( "removeFile", 2, athena_removeFile ), JS_CFUNC_DEF( "rename", 2, athena_rename ), JS_CFUNC_DEF( "sleep", 1, athena_sleep ), JS_CFUNC_DEF( "delay", 0, athena_delay ), - JS_CFUNC_DEF( "getFreeMemory", 0, athena_getFreeMemory ), JS_CFUNC_DEF( "exitToBrowser", 0, athena_exit ), JS_CFUNC_DEF( "getMCInfo", 2, athena_getmcinfo ), JS_CFUNC_DEF( "loadELF", 3, athena_loadELF ), @@ -701,13 +530,6 @@ static const JSCFunctionListEntry system_funcs[] = { JS_CFUNC_DEF( "getTemperature", 0, athena_gettemps ), JS_CFUNC_DEF( "getStackTrace", 1, athena_stacktrace ), JS_PROP_STRING_DEF("boot_path", boot_path, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("FREAD", O_RDONLY, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("FWRITE", O_WRONLY, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("FCREATE", O_CREAT | O_WRONLY, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("FRDWR", O_RDWR, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("SET", SEEK_SET, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("END", SEEK_END, JS_PROP_CONFIGURABLE ), - JS_PROP_INT32_DEF("CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("READ_ONLY", 1, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("SELECT", 2, JS_PROP_CONFIGURABLE ), }; diff --git a/src/ath_vector.c b/src/ath_vector.c new file mode 100644 index 0000000..9e86128 --- /dev/null +++ b/src/ath_vector.c @@ -0,0 +1,643 @@ +#include "ath_env.h" +#include +#include + +#include "include/render.h" + +typedef struct { + float x; + float y; +} Vector2; + +static JSClassID js_vector2_class_id; + +static void js_vector2_finalizer(JSRuntime *rt, JSValue val) +{ + Vector2 *s = JS_GetOpaque(val, js_vector2_class_id); + /* Note: 's' can be NULL in case JS_SetOpaque() was not called */ + js_free_rt(rt, s); +} + +static JSValue js_vector2_ctor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + Vector2 *s; + JSValue obj = JS_UNDEFINED; + + obj = JS_NewObjectClass(ctx, js_vector2_class_id); + if (JS_IsException(obj)) + goto fail; + + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + if (JS_ToFloat32(ctx, &s->x, argv[0])) + goto fail; + if (JS_ToFloat32(ctx, &s->y, argv[1])) + goto fail; + /* using new_target to get the prototype is necessary when the + class is extended. */ + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_vector2_get_xy(JSContext *ctx, JSValueConst this_val, int magic) +{ + Vector2 *s = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + if (!s) + return JS_EXCEPTION; + if (magic == 0) + return JS_NewFloat32(ctx, s->x); + else + return JS_NewFloat32(ctx, s->y); +} + +static JSValue js_vector2_set_xy(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ + Vector2 *s = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + float v; + if (!s) + return JS_EXCEPTION; + if (JS_ToFloat32(ctx, &v, val)) + return JS_EXCEPTION; + if (magic == 0) + s->x = v; + else + s->y = v; + return JS_UNDEFINED; +} + +static JSValue js_vector2_norm(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector2 *s = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + if (!s) + return JS_EXCEPTION; + return JS_NewFloat32(ctx, sqrtf(s->x * s->x + s->y * s->y)); +} + +static JSValue js_vector2_dotproduct(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector2 *v1 = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + Vector2 *v2 = JS_GetOpaque2(ctx, argv[0], js_vector2_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + return JS_NewFloat32(ctx, (v1->x * v2->x + v1->y * v2->y)); +} + +static JSValue js_vector2_crossproduct(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector2 *v1 = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + Vector2 *v2 = JS_GetOpaque2(ctx, argv[0], js_vector2_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + return JS_NewFloat32(ctx, (v1->x * v2->y - v1->y * v2->x)); +} + +static JSValue js_vector2_distance(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector2 *v1 = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + Vector2 *v2 = JS_GetOpaque2(ctx, argv[0], js_vector2_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + return JS_NewFloat32(ctx, sqrtf((v1->x - v2->x) * (v1->x - v2->x) + (v1->y - v2->y) * (v1->y - v2->y))); +} + +static JSValue js_vector2_distancesqr(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector2 *v1 = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + Vector2 *v2 = JS_GetOpaque2(ctx, argv[0], js_vector2_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + return JS_NewFloat32(ctx, ((v1->x - v2->x) * (v1->x - v2->x) + (v1->y - v2->y) * (v1->y - v2->y))); +} + +static JSValue js_vector2_tostring(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char str[32]; + Vector2 *v = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + + if (!v) + return JS_EXCEPTION; + + sprintf(str, "{x:%g, y:%g}", v->x, v->y); + return JS_NewString(ctx, str); +} + +static JSValue js_vector2_add(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector2 *s; + JSValue obj = JS_UNDEFINED; + + Vector2 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector2_class_id); + Vector2 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector2_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector2_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->x + v2->x; + s->y = v1->y + v2->y; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_vector2_sub(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector2 *s; + JSValue obj = JS_UNDEFINED; + + Vector2 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector2_class_id); + Vector2 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector2_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector2_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->x - v2->x; + s->y = v1->y - v2->y; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + + +static JSValue js_vector2_mul(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector2 *s; + JSValue obj = JS_UNDEFINED; + + Vector2 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector2_class_id); + Vector2 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector2_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector2_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->x * v2->x; + s->y = v1->y * v2->y; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + + +static JSValue js_vector2_div(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector2 *s; + JSValue obj = JS_UNDEFINED; + + Vector2 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector2_class_id); + Vector2 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector2_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector2_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->x / v2->x; + s->y = v1->y / v2->y; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + + +static const JSCFunctionListEntry js_vector2_funcs[] = { + JS_CFUNC_DEF("new", 2, js_vector2_ctor), + JS_CFUNC_DEF("add", 2, js_vector2_add), + JS_CFUNC_DEF("sub", 2, js_vector2_sub), + JS_CFUNC_DEF("mul", 2, js_vector2_mul), + JS_CFUNC_DEF("div", 2, js_vector2_div), +}; + +static JSClassDef js_vector2_class = { + "Vector2", + .finalizer = js_vector2_finalizer, +}; + +static const JSCFunctionListEntry js_vector2_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("x", js_vector2_get_xy, js_vector2_set_xy, 0), + JS_CGETSET_MAGIC_DEF("y", js_vector2_get_xy, js_vector2_set_xy, 1), + JS_CFUNC_DEF("norm", 0, js_vector2_norm), + JS_CFUNC_DEF("dot", 1, js_vector2_dotproduct), + JS_CFUNC_DEF("cross", 1, js_vector2_crossproduct), + JS_CFUNC_DEF("dist", 1, js_vector2_distance), + JS_CFUNC_DEF("distsqr", 1, js_vector2_distancesqr), + + JS_CFUNC_DEF("toString", 0, js_vector2_tostring), +}; + +static int js_vector2_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue vector2_proto, vector2_class; + + /* create the Vector2 class */ + JS_NewClassID(&js_vector2_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_vector2_class_id, &js_vector2_class); + + vector2_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, vector2_proto, js_vector2_proto_funcs, countof(js_vector2_proto_funcs)); + + //vector2_class = JS_NewCFunction2(ctx, js_vector2_ctor, "xy", 2, JS_CFUNC_constructor, 0); + /* set proto.constructor and ctor.prototype */ + //JS_SetConstructor(ctx, vector2_class, vector2_proto); + JS_SetClassProto(ctx, js_vector2_class_id, vector2_proto); + + //JS_SetModuleExport(ctx, m, "Vector2", vector2_class); + return JS_SetModuleExportList(ctx, m, js_vector2_funcs, countof(js_vector2_funcs)); +} + +typedef struct { + float x; + float y; + float z; +} Vector3; + +static JSClassID js_vector3_class_id; + +static void js_vector3_finalizer(JSRuntime *rt, JSValue val) +{ + Vector3 *s = JS_GetOpaque(val, js_vector3_class_id); + /* Note: 's' can be NULL in case JS_SetOpaque() was not called */ + js_free_rt(rt, s); +} + +static JSValue js_vector3_ctor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + Vector3 *s; + JSValue obj = JS_UNDEFINED; + + obj = JS_NewObjectClass(ctx, js_vector3_class_id); + if (JS_IsException(obj)) + goto fail; + + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + if (JS_ToFloat32(ctx, &s->x, argv[0])) + goto fail; + if (JS_ToFloat32(ctx, &s->y, argv[1])) + goto fail; + if (JS_ToFloat32(ctx, &s->z, argv[2])) + goto fail; + /* using new_target to get the prototype is necessary when the + class is extended. */ + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + + +static JSValue js_vector3_get_xyz(JSContext *ctx, JSValueConst this_val, int magic) +{ + Vector3 *s = JS_GetOpaque2(ctx, this_val, js_vector3_class_id); + if (!s) + return JS_EXCEPTION; + if (magic == 0) { + return JS_NewFloat32(ctx, s->x); + } else if (magic == 1) { + return JS_NewFloat32(ctx, s->y); + } else { + return JS_NewFloat32(ctx, s->z); + } +} + +static JSValue js_vector3_set_xyz(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ + Vector3 *s = JS_GetOpaque2(ctx, this_val, js_vector2_class_id); + float v; + if (!s) + return JS_EXCEPTION; + if (JS_ToFloat32(ctx, &v, val)) + return JS_EXCEPTION; + if (magic == 0) { + s->x = v; + } else if (magic == 1) { + s->y = v; + } else { + s->z = v; + } + return JS_UNDEFINED; +} + +static JSValue js_vector3_norm(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector3 *v = JS_GetOpaque2(ctx, this_val, js_vector3_class_id); + if (!v) + return JS_EXCEPTION; + return JS_NewFloat32(ctx, sqrtf(v->x * v->x + v->y * v->y + v->z * v->z)); +} + +static JSValue js_vector3_dotproduct(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector3 *v1 = JS_GetOpaque2(ctx, this_val, js_vector3_class_id); + Vector3 *v2 = JS_GetOpaque2(ctx, argv[0], js_vector3_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + return JS_NewFloat32(ctx, (v1->x * v2->x + v1->y * v2->y + v1->z * v2->z)); +} + +static JSValue js_vector3_dist(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector3 *v1 = JS_GetOpaque2(ctx, this_val, js_vector3_class_id); + Vector3 *v2 = JS_GetOpaque2(ctx, argv[0], js_vector3_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + float dx = v2->x - v1->x; + float dy = v2->y - v1->y; + float dz = v2->z - v1->z; + return JS_NewFloat32(ctx, sqrtf(dx * dx + dy * dy + dz * dz)); +} + +static JSValue js_vector3_distsqr(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + Vector3 *v1 = JS_GetOpaque2(ctx, this_val, js_vector3_class_id); + Vector3 *v2 = JS_GetOpaque2(ctx, argv[0], js_vector3_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + float dx = v2->x - v1->x; + float dy = v2->y - v1->y; + float dz = v2->z - v1->z; + return JS_NewFloat32(ctx, (dx * dx + dy * dy + dz * dz)); +} + +static JSValue js_vector3_tostring(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char str[32]; + Vector3 *v = JS_GetOpaque2(ctx, this_val, js_vector3_class_id); + + if (!v) + return JS_EXCEPTION; + + sprintf(str, "{x:%g, y:%g, z:%g}", v->x, v->y, v->z); + return JS_NewString(ctx, str); +} + +static JSValue js_vector3_add(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector3 *s; + JSValue obj = JS_UNDEFINED; + + Vector3 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector3_class_id); + Vector3 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector3_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector3_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->x + v2->x; + s->y = v1->y + v2->y; + s->z = v1->z + v2->z; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_vector3_sub(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector3 *s; + JSValue obj = JS_UNDEFINED; + + Vector3 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector3_class_id); + Vector3 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector3_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector3_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->x - v2->x; + s->y = v1->y - v2->y; + s->z = v1->z - v2->z; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_vector3_mul(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector3 *s; + JSValue obj = JS_UNDEFINED; + + Vector3 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector3_class_id); + Vector3 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector3_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector3_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->x * v2->x; + s->y = v1->y * v2->y; + s->z = v1->z * v2->z; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_vector3_div(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector3 *s; + JSValue obj = JS_UNDEFINED; + + Vector3 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector3_class_id); + Vector3 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector3_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector3_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->x / v2->x; + s->y = v1->y / v2->y; + s->z = v1->z / v2->z; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_vector3_cross(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + Vector3 *s; + JSValue obj = JS_UNDEFINED; + + Vector3 *v1 = JS_GetOpaque2(ctx, argv[0], js_vector3_class_id); + Vector3 *v2 = JS_GetOpaque2(ctx, argv[1], js_vector3_class_id); + if (!v1 || !v2) + return JS_EXCEPTION; + + obj = JS_NewObjectClass(ctx, js_vector3_class_id); + if (JS_IsException(obj)) + goto fail; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + + s->x = v1->y * v2->z - v1->z * v2->y; + s->y = v1->z * v2->x - v1->x * v2->z; + s->z = v1->x * v2->y - v1->y * v2->x; + + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_vector3_funcs[] = { + JS_CFUNC_DEF("new", 3, js_vector3_ctor), + JS_CFUNC_DEF("add", 2, js_vector3_add), + JS_CFUNC_DEF("sub", 2, js_vector3_sub), + JS_CFUNC_DEF("mul", 2, js_vector3_mul), + JS_CFUNC_DEF("div", 2, js_vector3_div), + JS_CFUNC_DEF("cross", 2, js_vector3_cross), +}; + +static JSClassDef js_vector3_class = { + "Vector3", + .finalizer = js_vector3_finalizer, +}; + +static const JSCFunctionListEntry js_vector3_proto_funcs[] = { + JS_CFUNC_DEF("norm", 0, js_vector3_norm), + JS_CFUNC_DEF("dot", 1, js_vector3_dotproduct), + JS_CFUNC_DEF("dist", 1, js_vector3_dist), + JS_CFUNC_DEF("distsqr", 1, js_vector3_distsqr), + JS_CFUNC_DEF("toString", 0, js_vector3_tostring), + JS_CGETSET_MAGIC_DEF("x", js_vector3_get_xyz, js_vector3_set_xyz, 0), + JS_CGETSET_MAGIC_DEF("y", js_vector3_get_xyz, js_vector3_set_xyz, 1), + JS_CGETSET_MAGIC_DEF("z", js_vector3_get_xyz, js_vector3_set_xyz, 2) +}; + +static int js_vector3_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue vector3_proto, vector3_class; + + /* create the Vector3 class */ + JS_NewClassID(&js_vector3_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_vector3_class_id, &js_vector3_class); + + vector3_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, vector3_proto, js_vector3_proto_funcs, countof(js_vector3_proto_funcs)); + + //vector2_class = JS_NewCFunction2(ctx, js_vector3_ctor, "xy", 2, JS_CFUNC_constructor, 0); + /* set proto.constructor and ctor.prototype */ + //JS_SetConstructor(ctx, vector3_class, vector2_proto); + JS_SetClassProto(ctx, js_vector3_class_id, vector3_proto); + + //JS_SetModuleExport(ctx, m, "Vector3", vector3_class); + return JS_SetModuleExportList(ctx, m, js_vector3_funcs, countof(js_vector3_funcs)); +} + + +JSModuleDef *athena_vector_init(JSContext *ctx) +{ + athena_push_module(ctx, js_vector3_init, js_vector3_funcs, countof(js_vector3_funcs), "Vector3"); + return athena_push_module(ctx, js_vector2_init, js_vector2_funcs, countof(js_vector2_funcs), "Vector2"); +} diff --git a/src/athena_math.c b/src/athena_math.c new file mode 100644 index 0000000..af0e292 --- /dev/null +++ b/src/athena_math.c @@ -0,0 +1,251 @@ + +#include +#include +#include + +#include "include/athena_math.h" + +// Took from Tyra + +extern volatile const uint32_t ATHENA_MATH_ATAN_TABLE[9] = { + 0x3f7ffff5, 0xbeaaa61c, 0x3e4c40a6, 0xbe0e6c63, 0x3dc577df, + 0xbd6501c4, 0x3cb31652, 0xbb84d7e7, 0x3f490fdb, +}; + +extern volatile const float ATHENA_MATH_ATAN_TABLE2[8] = { + 0.0f, M_PI_2, M_PI_2, M_PI, -M_PI, -M_PI_2, -M_PI_2, 0.0f, +}; + +float athena_cosf(float x) { + float r; + asm volatile( + "lui $9, 0x3f00 \n\t" + ".set noreorder \n\t" + ".align 3 \n\t" + "abs.s %0, %1 \n\t" + "lui $8, 0xbe22 \n\t" + "mtc1 $9, $f1 \n\t" + "ori $8, $8, 0xf983 \n\t" + "mtc1 $8, $f8 \n\t" + "lui $9, 0x4b00 \n\t" + "mtc1 $9, $f3 \n\t" + "lui $8, 0x3f80 \n\t" + "mtc1 $8, $f2 \n\t" + "mula.s %0, $f8 \n\t" + "msuba.s $f3, $f2 \n\t" + "madda.s $f3, $f2 \n\t" + "lui $8, 0x40c9 \n\t" + "msuba.s %0, $f8 \n\t" + "ori $8, 0x0fdb \n\t" + "msub.s %0, $f1, $f2 \n\t" + "lui $9, 0xc225 \n\t" + "abs.s %0, %0 \n\t" + "lui $10, 0x3e80 \n\t" + "mtc1 $10, $f7 \n\t" + "ori $9, 0x5de1 \n\t" + "sub.s %0, %0, $f7 \n\t" + "lui $10, 0x42a3 \n\t" + "mtc1 $8, $f3 \n\t" + "ori $10, 0x3458 \n\t" + "mtc1 $9, $f4 \n\t" + "lui $8, 0xc299 \n\t" + "mtc1 $10, $f5 \n\t" + "ori $8, 0x2663 \n\t" + "mul.s $f8, %0, %0 \n\t" + "lui $9, 0x421e \n\t" + "mtc1 $8, $f6 \n\t" + "ori $9, 0xd7bb \n\t" + "mtc1 $9, $f7 \n\t" + "nop \n\t" + "mul.s $f1, %0, $f8 \n\t" + "mul.s $f9, $f8, $f8 \n\t" + "mula.s $f3, %0 \n\t" + "mul.s $f2, $f1, $f8 \n\t" + "madda.s $f4, $f1 \n\t" + "mul.s $f1, $f1, $f9 \n\t" + "mul.s %0, $f2, $f9 \n\t" + "madda.s $f5, $f2 \n\t" + "madda.s $f6, $f1 \n\t" + "madd.s %0, $f7, %0 \n\t" + ".set reorder \n\t" + : "=&f"(r) + : "f"(x) + : "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$8", + "$9", "$10"); + return r; +} + +float athena_atan2f(float y, float x) { + float r; + asm volatile( + "mtc1 $0, %0 \n\t" + "abs.s $f1, %1 \n\t" + "abs.s $f2, %2 \n\t" + "c.lt.s %2, %0 \n\t" + "move $9, $0 \n\t" + "bc1f _atan_00 \n\t" + "addiu $9, $9, 4 \n\t" + "_atan_00: \n\t" + "c.eq.s %2, %0 \n\t" + "bc1f _atan_00_1 \n\t" + "c.eq.s %1, %2 \n\t" + "bc1t _atan_06 \n\t" + "c.lt.s %1, %0 \n\t" + "bc1f _atan_00_1 \n\t" + "addiu $9, $0, 3 \n\t" + "_atan_00_1: \n\t" + "mul.s %1, %1, %2 \n\t" + "c.lt.s %1, %0 \n\t" + "bc1f _atan_01 \n\t" + "addiu $9, $9, 2 \n\t" + "c.lt.s $f2, $f1 \n\t" + "bc1f _atan_02 \n\t" + "addiu $9, $9, 1 \n\t" + "b _atan_02 \n\t" + "_atan_01: \n\t" + "c.lt.s $f1, $f2 \n\t" + "bc1f _atan_02 \n\t" + "addiu $9, $9, 1 \n\t" + "_atan_02: \n\t" + "c.lt.s $f1, $f2 \n\t" + "bc1f _atan_03 \n\t" + "mov.s %1, $f2 \n\t" + "mov.s %2, $f1 \n\t" + "b _atan_04 \n\t" + "_atan_03: \n\t" + "mov.s %1, $f1 \n\t" + "mov.s %2, $f2 \n\t" + "_atan_04: \n\t" + "mfc1 $6, %1 \n\t" + "mfc1 $7, %2 \n\t" + "la $8, ATHENA_MATH_ATAN_TABLE \n\t" + "lqc2 $vf4, 0x0($8) \n\t" + "lqc2 $vf5, 0x10($8) \n\t" + "lqc2 $vf6, 0x20($8) \n\t" + "qmtc2 $6, $vf21 \n\t" + "qmtc2 $7, $vf22 \n\t" + "vadd.x $vf23, $vf21, $vf22 \n\t" + "vsub.x $vf22, $vf22, $vf21 \n\t" + "vdiv $Q, $vf22x, $vf23x \n\t" + "vwaitq \n\t" + "vaddq.x $vf21, $vf0, $Q \n\t" + "vmul.x $vf22, $vf21, $vf21 \n\t" + "vmulax.x $ACC, $vf21, $vf4 \n\t" + "vmul.x $vf21, $vf21, $vf22 \n\t" + "vmadday.x $ACC, $vf21, $vf4 \n\t" + "vmul.x $vf21, $vf21, $vf22 \n\t" + "vmaddaz.x $ACC, $vf21, $vf4 \n\t" + "vmul.x $vf21, $vf21, $vf22 \n\t" + "vmaddaw.x $ACC, $vf21, $vf4 \n\t" + "vmul.x $vf21, $vf21, $vf22 \n\t" + "vmaddax.x $ACC, $vf21, $vf5 \n\t" + "vmul.x $vf21, $vf21, $vf22 \n\t" + "vmadday.x $ACC, $vf21, $vf5 \n\t" + "vmul.x $vf21, $vf21, $vf22 \n\t" + "vmaddaz.x $ACC, $vf21, $vf5 \n\t" + "vmul.x $vf21, $vf21, $vf22 \n\t" + "vmaddaw.x $ACC, $vf21, $vf5 \n\t" + "vmaddw.x $vf21, $vf6, $vf0 \n\t" + "qmfc2 $6, $vf21 \n\t" + "mtc1 $6, %0 \n\t" + "andi $8, $9, 1 \n\t" + "sll $9, $9, 2 \n\t" + "la $7, ATHENA_MATH_ATAN_TABLE2 \n\t" + "add $9, $9, $7 \n\t" + "lw $7, 0x0($9) \n\t" + "mtc1 $7, %1 \n\t" + "beq $8, $0, _atan_05 \n\t" + "sub.s %0, %1, %0 \n\t" + "b _atan_06 \n\t" + "_atan_05: \n\t" + "add.s %0, %1, %0 \n\t" + "_atan_06: \n\t" + : "=&f"(r) + : "f"(x), "f"(y) + : "$6", "$7", "$8", "$9", "$f0", "$f1", "$f2"); + + return r; +} + +float athena_randomf(float min, float max) { + float random = ((float)rand()) / (float)RAND_MAX; + float diff = max - min; + float r = random * diff; + return min + r; +} + +int athena_randomi(int min, int max) { + return rand() % (max - min + 1) + min; +} + +float athena_asinf(float x) { + float r; + asm volatile( + "lui $9, 0x3f00 \n\t" + ".set noreorder \n\t" + ".align 3 \n\t" + "abs.s %0, %1 \n\t" + "lui $8, 0xbe22 \n\t" + "mtc1 $9, $f1 \n\t" + "ori $8, $8, 0xf983 \n\t" + "mtc1 $8, $f8 \n\t" + "lui $9, 0x4b00 \n\t" + "mtc1 $9, $f3 \n\t" + "lui $8, 0x3f80 \n\t" + "mtc1 $8, $f2 \n\t" + "mula.s %0, $f8 \n\t" + "msuba.s $f3, $f2 \n\t" + "madda.s $f3, $f2 \n\t" + "lui $8, 0x40c9 \n\t" + "msuba.s %0, $f8 \n\t" + "ori $8, 0x0fdb \n\t" + "msub.s %0, $f1, $f2 \n\t" + "lui $9, 0xc225 \n\t" + "abs.s %0, %0 \n\t" + "lui $10, 0x3e80 \n\t" + "mtc1 $10, $f7 \n\t" + "ori $9, 0x5de1 \n\t" + "sub.s %0, %0, $f7 \n\t" + "lui $10, 0x42a3 \n\t" + "mtc1 $8, $f3 \n\t" + "ori $10, 0x3458 \n\t" + "mtc1 $9, $f4 \n\t" + "lui $8, 0xc299 \n\t" + "mtc1 $10, $f5 \n\t" + "ori $8, 0x2663 \n\t" + "mul.s $f8, %0, %0 \n\t" + "lui $9, 0x421e \n\t" + "mtc1 $8, $f6 \n\t" + "ori $9, 0xd7bb \n\t" + "mtc1 $9, $f7 \n\t" + "nop \n\t" + "mul.s $f1, %0, $f8 \n\t" + "mul.s $f9, $f8, $f8 \n\t" + "mula.s $f3, %0 \n\t" + "mul.s $f2, $f1, $f8 \n\t" + "madda.s $f4, $f1 \n\t" + "mul.s $f1, $f1, $f9 \n\t" + "mul.s %0, $f2, $f9 \n\t" + "madda.s $f5, $f2 \n\t" + "madda.s $f6, $f1 \n\t" + "madd.s %0, $f7, %0 \n\t" + ".set reorder \n\t" + : "=&f"(r) + : "f"(x) + : "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$8", + "$9", "$10"); + return r; +} + +float athena_acosf(float x) { + float y = sqrtf(1.0f - x * x); + float t = athena_atan2f(y, x); + return t; +} + +float athena_sinf(float x) { return athena_cosf(x - M_PI_2); } + +float athena_tanf(float x) { return athena_sinf(x) / athena_cosf(x); } + + + diff --git a/src/calc_3d.c b/src/calc_3d.c index f7028f5..429ef0c 100644 --- a/src/calc_3d.c +++ b/src/calc_3d.c @@ -1,4 +1,5 @@ #include "include/render.h" +#include int clip_bounding_box(MATRIX local_clip, VECTOR *bounding_box) { @@ -122,4 +123,693 @@ int draw_convert_xyz(xyz_t *output, float x, float y, int z, int count, vertex_f // End function. return 0; -} \ No newline at end of file +} + +unsigned int get_max_z(GSGLOBAL* gsGlobal) +{ + + int z; + unsigned int max_z; + + switch(gsGlobal->PSMZ){ + case GS_PSMZ_32: + z = 32; + break; + + case GS_PSMZ_24: + z = 24; + break; + + case GS_PSMZ_16: + case GS_PSMZ_16S: + z = 16; + break; + + default: + return -1; + } + + max_z = 1 << (z - 1); + + // End function. + return max_z; + +} + +/* +int athena_process_xyz_rgbaq(GSPRIMPOINT *output, GSGLOBAL* gsGlobal, int count, color_f_t *colours, vertex_f_t *vertices) +{ + float q = 1.00f; + + int center_x = gsGlobal->Width/2; + int center_y = gsGlobal->Height/2; + unsigned int max_z = get_max_z(gsGlobal); + + for (int i = 0; i < count; i++) + { + // Calculate the Q value. + if (vertices[i].w != 0) + { + q = 1 / vertices[i].w; + } + + output[i].rgbaq.color.r = (int)(colours[i].r * 128.0f); + output[i].rgbaq.color.g = (int)(colours[i].g * 128.0f); + output[i].rgbaq.color.b = (int)(colours[i].b * 128.0f); + output[i].rgbaq.color.a = 0x80; + output[i].rgbaq.color.q = q; + output[i].rgbaq.tag = GS_RGBAQ; + + output[i].xyz2.xyz.x = gsKit_float_to_int_x(gsGlobal, (vertices[i].x + 1.0f) * center_x); + output[i].xyz2.xyz.y = gsKit_float_to_int_y(gsGlobal, (vertices[i].y + 1.0f) * center_y); + output[i].xyz2.xyz.z = (unsigned int)((vertices[i].z + 1.0f) * max_z); + output[i].xyz2.tag = GS_XYZ2; + + } + + // End function. + return 0; + +} + +int athena_process_xyz_rgbaq_st(GSPRIMSTQPOINT *output, GSGLOBAL* gsGlobal, int count, color_f_t *colours, vertex_f_t *vertices, texel_f_t *coords) +{ + float q = 1.00f; + + int center_x = 2048.0f+gsGlobal->Width/2; + int center_y = 2048.0f+gsGlobal->Height/2; + unsigned int max_z = get_max_z(gsGlobal); + + for (int i = 0; i < count; i++) + { + // Calculate the Q value. + if (vertices[i].w != 0) + { + q = 1 / vertices[i].w; + } + + output[i].rgbaq.color.r = (int)(colours[i].r * 128.0f); + output[i].rgbaq.color.g = (int)(colours[i].g * 128.0f); + output[i].rgbaq.color.b = (int)(colours[i].b * 128.0f); + output[i].rgbaq.color.a = 0x80; + output[i].rgbaq.color.q = q; + output[i].rgbaq.tag = GS_RGBAQ; + + output[i].stq.st.s = coords[i].s * q; + output[i].stq.st.t = coords[i].t * q; + output[i].stq.tag = GS_ST; + + output[i].xyz2.xyz.x = (int)(((vertices[i].x + 1.0f) * center_x) * 16.0f) ; + output[i].xyz2.xyz.y = (int)(((vertices[i].y + 1.0f) * center_y) * 16.0f) ; + output[i].xyz2.xyz.z = (unsigned int)((vertices[i].z + 1.0f) * max_z); + output[i].xyz2.tag = GS_XYZ2; + + } + + // End function. + return 0; + +}*/ + +void athena_line_goraud_3d(GSGLOBAL *gsGlobal, float x1, float y1, int iz1, float x2, float y2, int iz2, u64 color1, u64 color2) +{ + u64* p_store; + u64* p_data; + int qsize = 3; + int bsize = 48; + + int center_x = 2048.0f+gsGlobal->Width/2; + int center_y = 2048.0f+gsGlobal->Height/2; + unsigned int max_z = get_max_z(gsGlobal); + + int ix1 = (int)(((x1 + 1.0f) * center_x) * 16.0f); + int iy1 = (int)(((y1 + 1.0f) * center_y) * 16.0f); + + int ix2 = (int)(((x2 + 1.0f) * center_x) * 16.0f); + int iy2 = (int)(((y2 + 1.0f) * center_y) * 16.0f); + + p_store = p_data = gsKit_heap_alloc(gsGlobal, qsize, bsize, GSKIT_GIF_PRIM_LINE); + + if(p_store == gsGlobal->CurQueue->last_tag) + { + *p_data++ = GIF_TAG_LINE_GORAUD(0); + *p_data++ = GIF_TAG_LINE_GORAUD_REGS; + } + + *p_data++ = GS_SETREG_PRIM( GS_PRIM_PRIM_LINE, 1, 0, gsGlobal->PrimFogEnable, + gsGlobal->PrimAlphaEnable, gsGlobal->PrimAAEnable, + 0, gsGlobal->PrimContext, 0) ; + + *p_data++ = color1; + *p_data++ = GS_SETREG_XYZ2( ix1, iy1, (unsigned int)((iz1 + 1.0f) * max_z) ); + + *p_data++ = color2; + *p_data++ = GS_SETREG_XYZ2( ix2, iy2, (unsigned int)((iz2 + 1.0f) * max_z) ); +} + +static inline u32 lzw(u32 val) +{ + u32 res; + __asm__ __volatile__ (" plzcw %0, %1 " : "=r" (res) : "r" (val)); + return(res); +} + +void athena_set_tw_th(const GSTEXTURE *Texture, int *tw, int *th) +{ + *tw = 31 - (lzw(Texture->Width) + 1); + if(Texture->Width > (1<<*tw)) + (*tw)++; + + *th = 31 - (lzw(Texture->Height) + 1); + if(Texture->Height > (1<<*th)) + (*th)++; +} + +void SetRow(MATRIX output, int row, VECTOR vec) { + for (int i = 0; i < 4; i++) { + output[row * 4 + i] = vec[i]; + } +} + +void SetColumn(MATRIX output, int col, VECTOR vec) { + for (int i = 0; i < 4; i++) { + output[i * 4 + col] = vec[i]; + } +} + +float vu0_innerproduct(VECTOR v0, VECTOR v1) +{ + float ret; + + __asm__ __volatile__( + "lqc2 $vf4, 0(%1)\n" + "lqc2 $vf5, 0(%2)\n" + "vmul.xyz $vf5, $vf4, $vf5\n" + "vaddy.x $vf5, $vf5, $vf5\n" + "vaddz.x $vf5, $vf5, $vf5\n" + "qmfc2 $2, $vf5\n" + "mtc1 $2, %0\n" + : "=f" (ret) :"r" (v0) ,"r" (v1) :"$2", "memory" ); + + return ret; +} +/* +void vu0_build_lights(VECTOR *output, int count, VECTOR *normals, LightData* lights) { + float intensity; + + for (int i = 0; i < count; i++) { + output[i][0] = 0.0f; + output[i][1] = 0.0f; + output[i][2] = 0.0f; + output[i][3] = 0.0f; + + for (int j = 0; j < 4; j++) { + output[i][0] += lights->ambient[j][0]; + output[i][1] += lights->ambient[j][1]; + output[i][2] += lights->ambient[j][2]; + output[i][3] = 1.00f; + + intensity = -vu0_innerproduct(normals[i], lights->direction[j]); + // Clamp the minimum intensity. + if (intensity < 0.00f) { intensity = 0.00f; } + + // If the light has intensity... + if (intensity > 0.00f) { + // Add the light value. + output[i][0] += (lights->diffuse[j][0] * intensity); + output[i][1] += (lights->diffuse[j][1] * intensity); + output[i][2] += (lights->diffuse[j][2] * intensity); + output[i][3] = 1.00f; + } + } + } +} + +void vu0_calculate_lights(VECTOR *output, int count, VECTOR *normals, VECTOR *light_direction, VECTOR *light_colour, const int *light_type, int light_count) { + float intensity; + + for (int i = 0; i < count; i++) { + output[i][0] = 0.0f; + output[i][1] = 0.0f; + output[i][2] = 0.0f; + output[i][3] = 0.0f; + for (int j = 0; j < light_count; j++) { + if (light_type[j] == LIGHT_AMBIENT) { + intensity = 1.00f; + + } else if (light_type[j] == LIGHT_DIRECTIONAL) { + intensity = -vu0_innerproduct(normals[i], light_direction[j]); + // Clamp the minimum intensity. + if (intensity < 0.00f) { intensity = 0.00f; } + // Else, this is an invalid light type. + + } else { + intensity = 0.00f; + } + + // If the light has intensity... + if (intensity > 0.00f) { + // Add the light value. + output[i][0] += (light_colour[j][0] * intensity); + output[i][1] += (light_colour[j][1] * intensity); + output[i][2] += (light_colour[j][2] * intensity); + output[i][3] = 1.00f; + } + } + } +}*/ + +void vu0_vector_clamp(VECTOR v0, VECTOR v1, float min, float max) +{ + __asm__ __volatile__( + "mfc1 $8, %2\n" + "mfc1 $9, %3\n" + "lqc2 $vf6, 0(%1)\n" + "qmtc2 $8, $vf4\n" + "qmtc2 $9, $vf5\n" + "vmaxx.xyzw $vf6, $vf6, $vf4\n" + "vminix.xyzw $vf6, $vf6, $vf5\n" + "sqc2 $vf6, 0(%0)\n" + : : "r" (v0) , "r" (v1), "f" (min), "f" (max):"$8","$9","memory"); +} + +/*void vu0_calculate_colours(VECTOR *output, int count, VECTOR *colours, VECTOR *lights) { + for (int i = 0; i < count; i++) { + // Apply the light value to the colour. + __asm__ __volatile__( + "lqc2 $vf4, 0(%1)\n" + "lqc2 $vf5, 0(%2)\n" + "vmul.xyz $vf6, $vf4, $vf5\n" + "sqc2 $vf6, 0(%0)\n" + : :"r" (output[i]) , "r" (colours[i]) ,"r" (lights[i]) : "memory" ); + + vu0_vector_clamp(output[i], output[i], 0.00f, 1.99f); + } + +}*/ + +void UnitMatrix(MATRIX m0) +{ + __asm__ __volatile__( + "vsub.xyzw $vf4,$vf0,$vf0 #vf4.xyzw=0;\n" + "vadd.w $vf4,$vf4,$vf0\n" + "vmr32.xyzw $vf5,$vf4\n" + "vmr32.xyzw $vf6,$vf5\n" + "vmr32.xyzw $vf7,$vf6\n" + "sqc2 $vf4,0x30(%0)\n" + "sqc2 $vf5,0x20(%0)\n" + "sqc2 $vf6,0x10(%0)\n" + "sqc2 $vf7,0x0(%0)\n" + : : "r" (m0) : "memory"); +} + +void OuterProduct(VECTOR v0, VECTOR v1, VECTOR v2) +{ + __asm__ __volatile__ + ( + "lqc2 $vf4,0x0(%1)\n" + "lqc2 $vf5,0x0(%2)\n" + "vopmula.xyz $ACC,$vf4,$vf5\n" + "vopmsub.xyz $vf6,$vf5,$vf4\n" + "vsub.w $vf6,$vf6,$vf6 #vf6.xyz=0;\n" + "sqc2 $vf6,0x0(%0)\n" + : : "r" (v0) , "r" (v1) ,"r" (v2) : "memory"); +} + +void Normalize(VECTOR v0, VECTOR v1) +{ + __asm__ __volatile__( + "lqc2 $vf4,0x0(%1)\n" + "vmul.xyz $vf5,$vf4,$vf4\n" + "vaddy.x $vf5,$vf5,$vf5\n" + "vaddz.x $vf5,$vf5,$vf5\n" + + "vsqrt $Q,$vf5x\n" + "vwaitq\n" + "vaddq.x $vf5x,$vf0x,$Q\n" + "vnop\n" + "vnop\n" + "vdiv $Q,$vf0w,$vf5x\n" + "vsub.xyzw $vf6,$vf0,$vf0 #vf6.xyzw=0;\n" + "vwaitq\n" + + "vmulq.xyz $vf6,$vf4,$Q\n" + "sqc2 $vf6,0x0(%0)\n" + : : "r" (v0) , "r" (v1) : "memory"); +} + +void TransMatrix(MATRIX m0, MATRIX m1, VECTOR tv) +{ + __asm__ __volatile__( + "lqc2 $vf4,0x0(%2)\n" + "lqc2 $vf5,0x30(%1)\n" + + "lq $7,0x0(%1)\n" + "lq $8,0x10(%1)\n" + "lq $9,0x20(%1)\n" + "vadd.xyz $vf5,$vf5,$vf4\n" + "sq $7,0x0(%0)\n" + "sq $8,0x10(%0)\n" + "sq $9,0x20(%0)\n" + "sqc2 $vf5,0x30(%0)\n" + : : "r" (m0) , "r" (m1), "r" (tv):"$7","$8","$9","memory"); +} + +void InversMatrix(MATRIX m0, MATRIX m1) +{ + __asm__ __volatile__( + "lq $8,0x0000(%1)\n" + "lq $9,0x0010(%1)\n" + "lq $10,0x0020(%1)\n" + "lqc2 $vf4,0x0030(%1)\n" + + "vmove.xyzw $vf5,$vf4\n" + "vsub.xyz $vf4,$vf4,$vf4 #vf4.xyz=0;\n" + "vmove.xyzw $vf9,$vf4\n" + "qmfc2 $11,$vf4\n" + + "pextlw $12,$9,$8\n" + "pextuw $13,$9,$8\n" + "pextlw $14,$11,$10\n" + "pextuw $15,$11,$10\n" + "pcpyld $8,$14,$12\n" + "pcpyud $9,$12,$14\n" + "pcpyld $10,$15,$13\n" + + "qmtc2 $8,$vf6\n" + "qmtc2 $9,$vf7\n" + "qmtc2 $10,$vf8\n" + + "vmulax.xyz $ACC, $vf6,$vf5\n" + "vmadday.xyz $ACC, $vf7,$vf5\n" + "vmaddz.xyz $vf4,$vf8,$vf5\n" + "vsub.xyz $vf4,$vf9,$vf4\n" + "\n" + "sq $8,0x0000(%0)\n" + "sq $9,0x0010(%0)\n" + "sq $10,0x0020(%0)\n" + "sqc2 $vf4,0x0030(%0)\n" + : : "r" (m0) , "r" (m1):"$8","$9","$10","$11","$12","$13","$14","$15", "memory"); +} + +void ApplyMatrix(VECTOR v0, MATRIX m0,VECTOR v1) +{ + __asm__ __volatile__( + "lqc2 $vf4,0x0(%1)\n" + "lqc2 $vf5,0x10(%1)\n" + "lqc2 $vf6,0x20(%1)\n" + "lqc2 $vf7,0x30(%1)\n" + "lqc2 $vf8,0x0(%2)\n" + "vmulax.xyzw $ACC, $vf4,$vf8\n" + "vmadday.xyzw $ACC, $vf5,$vf8\n" + "vmaddaz.xyzw $ACC, $vf6,$vf8\n" + "vmaddw.xyzw $vf9,$vf7,$vf8\n" + "sqc2 $vf9,0x0(%0)\n" + : : "r" (v0) , "r" (m0) ,"r" (v1): "memory"); +} + +/*void RotMatrixZ(MATRIX m0, MATRIX m1, float rz) +{ + __asm__ __volatile__( +" mtc1 $0,$f0\n" +" c.olt.s %2,$f0\n" +" li.s $f0,1.57079637050628662109e0 \n" +" bc1f _RotMatrixZ_01\n" +" add.s %2,$f0,%2 #rx=rx+ƒÎ/2\n" +" li $7,1 #cos(rx)=sin(rx+ƒÎ/2)\n" +" j _RotMatrixZ_02\n" +"_RotMatrixZ_01:\n" +" sub.s %2,$f0,%2 #rx=ƒÎ/2-rx\n" +" move $7,$0\n" +"_RotMatrixZ_02:\n" + +" mfc1 $8,%2\n" +" qmtc2 $8,$vf6\n" +" move $6,$31 # ra •Û‘¶ (–{“–‚̓Xƒ^ƒbƒN‚ðŽg‚¤‚ׂ«)\n" + +" jal _ecossin # sin(roll), cos(roll)\n" +" move $31,$6 # ra ‰ñ•œ\n" +" #vf05:0,0,0,0 |x,y,z,w\n" +" vmove.xyzw $vf06,$vf05\n" +" vmove.xyzw $vf07,$vf05\n" +" vmove.xyzw $vf09,$vf00\n" +" vsub.xyz $vf09,$vf09,$vf09 #0,0,0,1 |x,y,z,w\n" +" vmr32.xyzw $vf08,$vf09 #0,0,1,0 |x,y,z,w\n" + +" vsub.zw $vf04,$vf04,$vf04 #vf04.zw=0 s,c,0,0 |x,y,z,w\n" +" vaddx.y $vf06,$vf05,$vf04 #vf06 0,s,0,0 |x,y,z,w\n" +" vaddy.x $vf06,$vf05,$vf04 #vf06 c,s,0,0 |x,y,z,w\n" +" vsubx.x $vf07,$vf05,$vf04 #vf07 -s,0,0,0 |x,y,z,w\n" +" vaddy.y $vf07,$vf05,$vf04 #vf07 -s,c,0,0 |x,y,z,w\n" + +" li $7,4\n" +" _loopRotMatrixZ:\n" +" lqc2 $vf4,0x0(%1)\n" +" vmulax.xyzw $ACC, $vf6,$vf4\n" +" vmadday.xyzw $ACC, $vf7,$vf4\n" +" vmaddaz.xyzw $ACC, $vf8,$vf4\n" +" vmaddw.xyzw $vf5, $vf9,$vf4\n" +" sqc2 $vf5,0x0(%0)\n" +" addi $7,-1\n" +" addi %1,0x10\n" +" addi %0,0x10\n" +" bne $0,$7,_loopRotMatrixZ\n" + : : "r" (m0) , "r" (m1), "f" (rz):"$6","$7","$8","$f0", "memory"); +} + +void RotMatrixX(MATRIX m0, MATRIX m1, float rx) +{ + __asm__ __volatile__( + "mtc1 $0,$f0\n" + "c.olt.s %2,$f0\n" + "li.s $f0,1.57079637050628662109e0 \n" + "bc1f _RotMatrixX_01\n" + "add.s %2,$f0,%2 #rx=rx+ƒÎ/2\n" + "li $7,1 #cos(rx)=sin(rx+ƒÎ/2)\n" + "j _RotMatrixX_02\n" +"_RotMatrixX_01:\n" +" sub.s %2,$f0,%2 #rx=ƒÎ/2-rx\n" +" move $7,$0\n" +"_RotMatrixX_02:\n" + + "mfc1 $8,%2\n" + "qmtc2 $8,$vf6\n" + "move $6,$31 # ra •Û‘¶ (–{“–‚̓Xƒ^ƒbƒN‚ðŽg‚¤‚ׂ«)\n" + "jal _ecossin # sin(roll), cos(roll)\n" + "move $31,$6 # ra ‰ñ•œ\n" + " #vf05:0,0,0,0 |x,y,z,w\n" + "vmove.xyzw $vf06,$vf05\n" + "vmove.xyzw $vf07,$vf05\n" + "vmove.xyzw $vf08,$vf05\n" + "vmove.xyzw $vf09,$vf05\n" + "vaddw.x $vf06,$vf05,$vf00 #vf06 1,0,0,0 |x,y,z,w\n" + "vaddw.w $vf09,$vf05,$vf00 #vf09 0,0,0,1 |x,y,z,w\n" + + "vsub.zw $vf04,$vf04,$vf04 #vf04.zw=0 s,c,0,0 |x,y,z,w\n" + "vaddx.z $vf07,$vf05,$vf04 #vf07.zw=0 0,0,s,0 |x,y,z,w\n" + "vaddy.y $vf07,$vf05,$vf04 #vf07.zw=0 0,c,s,0 |x,y,z,w\n" + "vsubx.y $vf08,$vf05,$vf04 #vf08.zw=0 0,-s,0,0 |x,y,z,w\n" + "vaddy.z $vf08,$vf05,$vf04 #vf08.zw=0 0,-s,c,0 |x,y,z,w\n" + + "li $7,4\n" + "_loopRotMatrixX:\n" + "lqc2 $vf4,0x0(%1)\n" + "vmulax.xyzw $ACC, $vf6,$vf4\n" + "vmadday.xyzw $ACC, $vf7,$vf4\n" + "vmaddaz.xyzw $ACC, $vf8,$vf4\n" + "vmaddw.xyzw $vf5, $vf9,$vf4\n" + "sqc2 $vf5,0x0(%0)\n" + "addi $7,-1\n" + "addi %1,0x10\n" + "addi %0,0x10\n" + "bne $0,$7,_loopRotMatrixX\n" + : : "r" (m0) , "r" (m1), "f" (rx):"$6","$7","$8","$f0","memory"); +} + +void RotMatrixY(MATRIX m0, MATRIX m1, float ry) +{ + __asm__ __volatile__( + "mtc1 $0,$f0\n" + "c.olt.s %2,$f0\n" + "li.s $f0,1.57079637050628662109e0 \n" + "bc1f _RotMatrixY_01\n" + "add.s %2,$f0,%2 #rx=rx+ƒÎ/2\n" + "li $7,1 #cos(rx)=sin(rx+ƒÎ/2)\n" + "j _RotMatrixY_02\n" +"_RotMatrixY_01:\n" + "sub.s %2,$f0,%2 #rx=ƒÎ/2-rx\n" + "move $7,$0\n" +"_RotMatrixY_02:\n" + + "mfc1 $8,%2\n" + "qmtc2 $8,$vf6\n" + "move $6,$31 # ra •Û‘¶ (–{“–‚̓Xƒ^ƒbƒN‚ðŽg‚¤‚ׂ«)\n" + "jal _ecossin # sin(roll), cos(roll)\n" + "move $31,$6 # ra ‰ñ•œ\n" + " #vf05:0,0,0,0 |x,y,z,w\n" + "vmove.xyzw $vf06,$vf05\n" + "vmove.xyzw $vf07,$vf05\n" + "vmove.xyzw $vf08,$vf05\n" + "vmove.xyzw $vf09,$vf05\n" + "vaddw.y $vf07,$vf05,$vf00 #vf07 0,1,0,0 |x,y,z,w\n" + "vaddw.w $vf09,$vf05,$vf00 #vf09 0,0,0,1 |x,y,z,w\n" + + "vsub.zw $vf04,$vf04,$vf04 #vf04.zw=0 s,c,0,0 |x,y,z,w\n" + "vsubx.z $vf06,$vf05,$vf04 #vf06 0,0,-s,0 |x,y,z,w\n" + "vaddy.x $vf06,$vf05,$vf04 #vf06 c,0,-s,0 |x,y,z,w\n" + "vaddx.x $vf08,$vf05,$vf04 #vf08 s,0,0,0 |x,y,z,w\n" + "vaddy.z $vf08,$vf05,$vf04 #vf08 s,0,c,0 |x,y,z,w\n" + + "li $7,4\n" + "_loopRotMatrixY:\n" + "lqc2 $vf4,0x0(%1)\n" + "vmulax.xyzw $ACC, $vf6,$vf4\n" + "vmadday.xyzw $ACC, $vf7,$vf4\n" + "vmaddaz.xyzw $ACC, $vf8,$vf4\n" + "vmaddw.xyzw $vf5, $vf9,$vf4\n" + "sqc2 $vf5,0x0(%0)\n" + "addi $7,-1\n" + "addi %1,0x10\n" + "addi %0,0x10\n" + "bne $0,$7,_loopRotMatrixY\n" + : : "r" (m0) , "r" (m1), "f" (ry):"$6","$7","$8","$f0","memory"); +}*/ + +void SubVector(VECTOR v0, VECTOR v1, VECTOR v2) +{ + __asm__ __volatile__( + "lqc2 $vf4,0x0(%1)\n" + "lqc2 $vf5,0x0(%2)\n" + "vsub.xyzw $vf6,$vf4,$vf5\n" + "sqc2 $vf6,0x0(%0)\n" + : : "r" (v0) , "r" (v1), "r" (v2) : "memory"); +} + +void AddVector(VECTOR v0, VECTOR v1, VECTOR v2) +{ + __asm__ __volatile__( + "lqc2 $vf4,0x0(%1)\n" + "lqc2 $vf5,0x0(%2)\n" + "vadd.xyzw $vf6,$vf4,$vf5\n" + "sqc2 $vf6,0x0(%0)\n" + : : "r" (v0) , "r" (v1), "r" (v2) : "memory"); +} + +float LenVector(VECTOR v) { + return sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); +} + +void SetLenVector(VECTOR v, float newLength) { + Normalize(v, v); + v[0] *= newLength; + v[1] *= newLength; + v[2] *= newLength; +} + +void ScaleVector(VECTOR res, VECTOR v, float size) { + res[0] = v[0] * size; + res[1] = v[1] * size; + res[2] = v[2] * size; + res[3] = 0.0f; +} + +void CameraMatrix(MATRIX m, VECTOR position, VECTOR target, VECTOR up) +{ + MATRIX m0; + VECTOR xd; + + UnitMatrix(m0); + + OuterProduct(xd, up, target); + + VECTOR left; + Normalize(left, xd); + + VECTOR forward; + Normalize(forward, target); + + VECTOR nup; + OuterProduct(nup, forward, left); + + m0[0] = left[0]; + m0[1] = left[1]; + m0[2] = left[2]; + m0[3] = left[3]; + + m0[4] = nup[0]; + m0[5] = nup[1]; + m0[6] = nup[2]; + m0[7] = nup[3]; + + m0[8] = forward[0]; + m0[9] = forward[1]; + m0[10] = forward[2]; + m0[11] = forward[3]; + + m0[12] = position[0]; + m0[13] = position[1]; + m0[14] = position[2]; + m0[15] = 1.0f; + + //TransMatrix(m0, m0, p); + InversMatrix(m, m0); +} + +void RotCameraMatrix(MATRIX m, VECTOR p, VECTOR zd, VECTOR yd, VECTOR rot) +{ + MATRIX work; + VECTOR direction, vertical, position; + UnitMatrix(work); + matrix_rotate(work, work, rot); + //RotMatrixX(work,work,rot[0]); + //RotMatrixY(work,work,rot[1]); + //RotMatrixZ(work,work,rot[2]); + ApplyMatrix(direction, work, zd); + ApplyMatrix(vertical, work, yd); + ApplyMatrix(position, work, p); + CameraMatrix(m, position, direction, vertical); +} + +void LookAtCameraMatrix(MATRIX m, VECTOR position, VECTOR target, VECTOR up) +{ + MATRIX m0; + VECTOR work; + + VECTOR left, forward, nup; + + UnitMatrix(m0); + + SubVector(work, target, position); + Normalize(forward, work); + + OuterProduct(work, up, forward); + Normalize(left, work); + + OuterProduct(nup, forward, left); + + m0[0] = left[0]; + m0[1] = left[1]; + m0[2] = left[2]; + m0[3] = left[3]; + + m0[4] = nup[0]; + m0[5] = nup[1]; + m0[6] = nup[2]; + m0[7] = nup[3]; + + m0[8] = -forward[0]; + m0[9] = -forward[1]; + m0[10] = -forward[2]; + m0[11] = forward[3]; + + m0[12] = position[0]; + m0[13] = position[1]; + m0[14] = position[2]; + m0[15] = 1.0f; + + //TransMatrix(m0, m0, p); + InversMatrix(m, m0); +} + + + diff --git a/src/draw_3D.vcl b/src/draw_3D.vcl new file mode 100644 index 0000000..15c46f5 --- /dev/null +++ b/src/draw_3D.vcl @@ -0,0 +1,139 @@ +; _____ ___ ____ ___ ____ +; ____| | ____| | | |____| +; | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +;----------------------------------------------------------------------- +; (c) 2020 h4570 Sandro SobczyÅ„ski +; Licenced under Academic Free License version 2.0 +; Review ps2sdk README & LICENSE files for further details. +; +; +;--------------------------------------------------------------- +; draw_3D.vcl | +;--------------------------------------------------------------- +; A VU1 microprogram to draw 3D object using XYZ2, RGBAQ and ST| +; This program uses double buffering (xtop) | +; | +; Many thanks to: | +; - Dr Henry Fortuna | +; - Jesper Svennevid, Daniel Collin | +; - Guilherme Lampert | +;--------------------------------------------------------------- + +.syntax new +.name VU1Draw3D +.vu +.init_vf_all +.init_vi_all + +--enter +--endenter + + ;//////////// --- Load data 1 --- ///////////// + ; Updated once per mesh + lq matrixRow0, 0(vi00) ; load view-projection matrix + lq matrixRow1, 1(vi00) + lq matrixRow2, 2(vi00) + lq matrixRow3, 3(vi00) + ;///////////////////////////////////////////// + + fcset 0x000000 ; VCL won't let us use CLIP without first zeroing + ; the clip flags + + ;//////////// --- Load data 2 --- ///////////// + ; Updated dynamically + xtop iBase + + lq.xyz scale, 0(iBase) ; load program params + ; float : X, Y, Z - scale vector that we will use to scale the verts after projecting them. + ; float : W - vert count. + lq gifSetTag, 1(iBase) ; GIF tag - set + lq texGifTag1, 2(iBase) ; GIF tag - texture LOD + lq texGifTag2, 3(iBase) ; GIF tag - texture buffer & CLUT + lq primTag, 4(iBase) ; GIF tag - tell GS how many data we will send + lq rgba, 5(iBase) ; RGBA + ; u32 : R, G, B, A (0-128) + iaddiu vertexData, iBase, 6 ; pointer to vertex data + ilw.w vertCount, 0(iBase) ; load vert count from scale vector + iadd stqData, vertexData, vertCount ; pointer to stq + iadd kickAddress, stqData, vertCount ; pointer for XGKICK + iadd destAddress, stqData, vertCount ; helper pointer for data inserting + ;//////////////////////////////////////////// + + ;/////////// --- Store tags --- ///////////// + sqi gifSetTag, (destAddress++) ; + sqi texGifTag1, (destAddress++) ; texture LOD tag + sqi gifSetTag, (destAddress++) ; + sqi texGifTag2, (destAddress++) ; texture buffer & CLUT tag + sqi primTag, (destAddress++) ; prim + tell gs how many data will be + ;//////////////////////////////////////////// + + ;/////////////// --- Loop --- /////////////// + iadd vertexCounter, iBase, vertCount ; loop vertCount times + vertexLoop: + + ;////////// --- Load loop data --- ////////// + lq vertex, 0(vertexData) ; load xyz + ; float : X, Y, Z + ; any32 : _ = 0 + lq stq, 0(stqData) ; load stq + ; float : S, T + ; any32 : Q = 1 ; 1, because we will mul this by 1/vert[w] and this + ; will be our q for texture perspective correction + ; any32 : _ = 0 + ;//////////////////////////////////////////// + + + ;////////////// --- Vertex --- ////////////// + mul acc, matrixRow0, vertex[x] ; transform each vertex by the matrix + madd acc, matrixRow1, vertex[y] + madd acc, matrixRow2, vertex[z] + madd vertex, matrixRow3, vertex[w] + + clipw.xyz vertex, vertex ; Dr. Fortuna: This instruction checks if the vertex is outside + ; the viewing frustum. If it is, then the appropriate + ; clipping flags are set + fcand VI01, 0x3FFFF ; Bitwise AND the clipping flags with 0x3FFFF, this makes + ; sure that we get the clipping judgement for the last three + ; verts (i.e. that make up the triangle we are about to draw) + iaddiu iADC, VI01, 0x7FFF ; Add 0x7FFF. If any of the clipping flags were set this will + ; cause the triangle not to be drawn (any values above 0x8000 + ; that are stored in the w component of XYZ2 will set the ADC + ; bit, which tells the GS not to perform a drawing kick on this + ; triangle. + + isw.w iADC, 2(destAddress) + + div q, vf00[w], vertex[w] ; perspective divide (1/vert[w]): + mul.xyz vertex, vertex, q + mula.xyz acc, scale, vf00[w] ; scale to GS screen space + madd.xyz vertex, vertex, scale ; multiply and add the scales -> vert = vert * scale + scale + ftoi4.xyz vertex, vertex ; convert vertex to 12:4 fixed point format + ;//////////////////////////////////////////// + + + ;//////////////// --- ST --- //////////////// + mulq modStq, stq, q + ;//////////////////////////////////////////// + + + ;//////////// --- Store data --- //////////// + sq modStq, 0(destAddress) ; STQ + sq rgba, 1(destAddress) ; RGBA ; q is grabbed from stq + sq.xyz vertex, 2(destAddress) ; XYZ2 + ;//////////////////////////////////////////// + + iaddiu vertexData, vertexData, 1 + iaddiu stqData, stqData, 1 + iaddiu destAddress, destAddress, 3 + + iaddi vertexCounter, vertexCounter, -1 ; decrement the loop counter + ibne vertexCounter, iBase, vertexLoop ; and repeat if needed + + ;//////////////////////////////////////////// + + --barrier + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--exit +--endexit diff --git a/src/draw_3D.vsm b/src/draw_3D.vsm new file mode 100644 index 0000000..96081fc --- /dev/null +++ b/src/draw_3D.vsm @@ -0,0 +1,75 @@ +;------------------------- +;------------------------- +;-----VCL CODE------------ +;------------------------- +;------------------------- +; ================================================= +; flowMon::Emit() vcl 1.4beta7 produced this code: + .vu + .align 4 + .global VU1Draw3D_CodeStart + .global VU1Draw3D_CodeEnd +VU1Draw3D_CodeStart: +__v_src_draw_3D_vcl_4: +; _LNOPT_w=[ normal2 ] 23 [23 0] 23 [__v_src_draw_3D_vcl_4] + NOP lq VF01,0(VI00) + NOP xtop VI02 + NOP lq VF02,1(VI00) + NOP lq VF06,1(VI02) + NOP lq VF09,2(VI02) + NOP lq VF08,3(VI02) + NOP lq VF07,4(VI02) + NOP lq VF03,2(VI00) + NOP iaddiu VI03,VI02,0x00000006 + NOP ilw.w VI07,0(VI02) + NOP lq VF04,3(VI00) + NOP fcset 0 + NOP lq.xyz VF05,0(VI02) + NOP iadd VI04,VI03,VI07 + NOP iadd VI06,VI04,VI07 + NOP sqi VF06,(VI06++) + NOP sqi VF09,(VI06++) + NOP sqi VF06,(VI06++) + NOP sqi VF08,(VI06++) + NOP lq VF06,5(VI02) + NOP iadd VI05,VI04,VI07 + NOP sqi VF07,(VI06++) + NOP iadd VI07,VI02,VI07 +vertexLoop: +; _LNOPT_w=[ normal2 ] 21 [31 14] 31 [vertexLoop] + NOP lq VF07,0(VI03) + mulax ACC,VF01,VF07x sq VF06,1(VI06) ; STALL_LATENCY ?3 + madday ACC,VF02,VF07y lq VF08,0(VI04) + maddaz ACC,VF03,VF07z iaddiu VI06,VI06,0x00000003 + maddw VF07,VF04,VF07w NOP + clipw.xyz VF07xyz,VF07w div Q,VF00w,VF07w ; STALL_LATENCY ?3 + NOP NOP + NOP NOP + NOP NOP + NOP NOP + NOP NOP + NOP NOP + mulq.xyz VF07,VF07,Q fcand VI01,262143 + mulaw.xyz ACC,VF05,VF00w iaddiu VI03,VI03,0x00000001 + madd.xyz VF07,VF07,VF05 iaddiu VI04,VI04,0x00000001 ; STALL_LATENCY ?2 + mulq VF08,VF08,Q isubiu VI07,VI07,1 + ftoi4.xyz VF07,VF07 iaddiu VI01,VI01,0x00007fff ; STALL_LATENCY ?2 + NOP isw.w VI01,-1(VI06) + NOP sq VF08,-3(VI06) + NOP ibne VI07,VI02,vertexLoop + NOP sq.xyz VF07,-1(VI06) +; _LNOPT_w=[ normal2 ] 3 [1 0] 3 [__v_src_draw_3D_vcl_7] + NOP xgkick VI05 + NOP[E] NOP + NOP NOP + .align 4 +VU1Draw3D_CodeEnd: +; iCount=47 +; register stats: +; 8 VU User integer +; 10 VU User floating point +;------------------------- +;------------------------- +;------------------------- +;------------------------- +;------------------------- diff --git a/src/draw_3D_colors.vcl b/src/draw_3D_colors.vcl new file mode 100644 index 0000000..d5e9ed3 --- /dev/null +++ b/src/draw_3D_colors.vcl @@ -0,0 +1,143 @@ +; _____ ___ ____ ___ ____ +; ____| | ____| | | |____| +; | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +;----------------------------------------------------------------------- +; (c) 2020 h4570 Sandro SobczyÅ„ski +; Licenced under Academic Free License version 2.0 +; Review ps2sdk README & LICENSE files for further details. +; +; +;--------------------------------------------------------------- +; draw_3D_lights.vcl | +;--------------------------------------------------------------- +; A VU1 microprogram to draw 3D object using XYZ2, RGBAQ and ST| +; This program uses double buffering (xtop) | +; | +; Many thanks to: | +; - Dr Henry Fortuna | +; - Jesper Svennevid, Daniel Collin | +; - Guilherme Lampert | +;--------------------------------------------------------------- + +.syntax new +.name VU1Draw3DColors +.vu +.init_vf_all +.init_vi_all + +.include "vcl_sml.i" + +--enter +--endenter + + ;//////////// --- Load data 1 --- ///////////// + ; Updated once per mesh + MatrixLoad ObjectToScreen, 0, vi00 ; load view-projection matrix + ;///////////////////////////////////////////// + + fcset 0x000000 ; VCL won't let us use CLIP without first zeroing + ; the clip flags + + ;//////////// --- Load data 2 --- ///////////// + ; Updated dynamically + xtop iBase + + lq.xyz scale, 0(iBase) ; load program params + ; float : X, Y, Z - scale vector that we will use to scale the verts after projecting them. + ; float : W - vert count. + lq gifSetTag, 1(iBase) ; GIF tag - set + lq texGifTag1, 2(iBase) ; GIF tag - texture LOD + lq texGifTag2, 3(iBase) ; GIF tag - texture buffer & CLUT + lq primTag, 4(iBase) ; GIF tag - tell GS how many data we will send + lq rgba, 5(iBase) ; RGBA + ; u32 : R, G, B, A (0-128) + iaddiu vertexData, iBase, 6 ; pointer to vertex data + ilw.w vertCount, 0(iBase) ; load vert count from scale vector + iadd stqData, vertexData, vertCount ; pointer to stq + iadd colorData, stqData, vertCount ; pointer to colors + iadd kickAddress, colorData, vertCount ; pointer for XGKICK + iadd destAddress, colorData, vertCount ; helper pointer for data inserting + ;//////////////////////////////////////////// + + ;/////////// --- Store tags --- ///////////// + sqi gifSetTag, (destAddress++) ; + sqi texGifTag1, (destAddress++) ; texture LOD tag + sqi gifSetTag, (destAddress++) ; + sqi texGifTag2, (destAddress++) ; texture buffer & CLUT tag + sqi primTag, (destAddress++) ; prim + tell gs how many data will be + ;//////////////////////////////////////////// + + ;/////////////// --- Loop --- /////////////// + iadd vertexCounter, iBase, vertCount ; loop vertCount times + vertexLoop: + + ;////////// --- Load loop data --- ////////// + lq vertex, 0(vertexData) ; load xyz + ; float : X, Y, Z + ; any32 : _ = 0 + lq stq, 0(stqData) ; load stq + ; float : S, T + ; any32 : Q = 1 ; 1, because we will mul this by 1/vert[w] and this + ; will be our q for texture perspective correction + ; any32 : _ = 0 + lq.xyzw color, 0(colorData) ; load color + ;//////////////////////////////////////////// + + + ;////////////// --- Vertex --- ////////////// + MatrixMultiplyVertex vertex, ObjectToScreen, vertex ; transform each vertex by the matrix + + clipw.xyz vertex, vertex ; Dr. Fortuna: This instruction checks if the vertex is outside + ; the viewing frustum. If it is, then the appropriate + ; clipping flags are set + fcand VI01, 0x3FFFF ; Bitwise AND the clipping flags with 0x3FFFF, this makes + ; sure that we get the clipping judgement for the last three + ; verts (i.e. that make up the triangle we are about to draw) + iaddiu iADC, VI01, 0x7FFF ; Add 0x7FFF. If any of the clipping flags were set this will + ; cause the triangle not to be drawn (any values above 0x8000 + ; that are stored in the w component of XYZ2 will set the ADC + ; bit, which tells the GS not to perform a drawing kick on this + ; triangle. + + isw.w iADC, 2(destAddress) + + div q, vf00[w], vertex[w] ; perspective divide (1/vert[w]): + mul.xyz vertex, vertex, q + mula.xyz acc, scale, vf00[w] ; scale to GS screen space + madd.xyz vertex, vertex, scale ; multiply and add the scales -> vert = vert * scale + scale + ftoi4.xyz vertex, vertex ; convert vertex to 12:4 fixed point format + ;//////////////////////////////////////////// + + + ;//////////////// --- ST --- //////////////// + mulq modStq, stq, q + ;//////////////////////////////////////////// + + ;//////////////// - COLORS - ///////////////// + mul color, color, rgba ; normalize RGBA + ColorFPtoGsRGBAQ intColor, color ; convert to int + ;/////////////////////////////////////////// + + + ;//////////// --- Store data --- //////////// + sq modStq, 0(destAddress) ; STQ + sq intColor, 1(destAddress) ; RGBA ; q is grabbed from stq + sq.xyz vertex, 2(destAddress) ; XYZ2 + ;//////////////////////////////////////////// + + iaddiu vertexData, vertexData, 1 + iaddiu stqData, stqData, 1 + iaddiu colorData, colorData, 1 + iaddiu destAddress, destAddress, 3 + + iaddi vertexCounter, vertexCounter, -1 ; decrement the loop counter + ibne vertexCounter, iBase, vertexLoop ; and repeat if needed + + ;//////////////////////////////////////////// + + --barrier + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--exit +--endexit diff --git a/src/draw_3D_colors.vsm b/src/draw_3D_colors.vsm new file mode 100644 index 0000000..ffc6bcd --- /dev/null +++ b/src/draw_3D_colors.vsm @@ -0,0 +1,63 @@ +; ================================================= +; flowMon::Emit() vcl 1.4beta7 produced this code: + .vu + .align 4 + .global VU1Draw3DColors_CodeStart + .global VU1Draw3DColors_CodeEnd +VU1Draw3DColors_CodeStart: +__v_src_draw_3D_colors_vcl_4: +; _LNOPT_w=[ normal2 ] 24 [24 0] 24 [__v_src_draw_3D_colors_vcl_4] + NOP lq VF01,0(VI00) + NOP xtop VI02 + NOP lq VF02,1(VI00) + NOP lq VF06,1(VI02) + NOP lq VF09,2(VI02) + NOP lq VF08,3(VI02) + NOP lq VF07,4(VI02) + NOP lq VF03,2(VI00) + NOP iaddiu VI03,VI02,0x00000006 + NOP ilw.w VI08,0(VI02) + NOP lq VF04,3(VI00) + NOP fcset 0 + NOP lq.xyz VF05,0(VI02) + NOP iadd VI04,VI03,VI08 + NOP iadd VI05,VI04,VI08 + NOP iadd VI07,VI05,VI08 + NOP sqi VF06,(VI07++) + NOP sqi VF09,(VI07++) + NOP sqi VF06,(VI07++) + NOP sqi VF08,(VI07++) + NOP lq VF06,5(VI02) + NOP iadd VI06,VI05,VI08 + NOP sqi VF07,(VI07++) + NOP iadd VI08,VI02,VI08 +vertexLoop: +; _LNOPT_w=[ vuta1 ] 18 [31 16] 31 [vertexLoop] + NOP lq VF07,0(VI03) + mulax ACC,VF01,VF07x iaddiu VI07,VI07,0x00000003 ; STALL_LATENCY ?3 + madday ACC,VF02,VF07y isubiu VI08,VI08,1 + maddaz ACC,VF03,VF07z lq VF08,0(VI05) + maddw VF07,VF04,VF07w NOP + mul VF08,VF08,VF06 NOP ; STALL_LATENCY ?2 + mulaw.xyz ACC,VF05,VF00w div Q,VF00w,VF07w + ftoi0 VF09,VF08 lq VF08,0(VI04) ; STALL_LATENCY ?2 + mulq.xyz VF09,VF07,Q sq VF09,-2(VI07) ; STALL_LATENCY ?3 + clipw.xyz VF07xyz,VF07w iaddiu VI05,VI05,0x00000001 + mulq VF07,VF08,Q iaddiu VI04,VI04,0x00000001 + madd.xyz VF08,VF09,VF05 iaddiu VI03,VI03,0x00000001 ; STALL_LATENCY ?1 + NOP fcand VI01,262143 + ftoi4.xyz VF08,VF08 iaddiu VI01,VI01,0x00007fff ; STALL_LATENCY ?2 + NOP isw.w VI01,-1(VI07) + NOP sq VF07,-3(VI07) + NOP ibne VI08,VI02,vertexLoop + NOP sq.xyz VF08,-1(VI07) +; _LNOPT_w=[ normal2 ] 3 [1 0] 3 [__v_src_draw_3D_colors_vcl_7] + NOP xgkick VI06 + NOP[E] NOP + NOP NOP + .align 4 +VU1Draw3DColors_CodeEnd: +; iCount=45 +; register stats: +; 9 VU User integer +; 10 VU User floating point diff --git a/src/draw_3D_colors_notex.vcl b/src/draw_3D_colors_notex.vcl new file mode 100644 index 0000000..c2f9788 --- /dev/null +++ b/src/draw_3D_colors_notex.vcl @@ -0,0 +1,124 @@ +; _____ ___ ____ ___ ____ +; ____| | ____| | | |____| +; | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +;----------------------------------------------------------------------- +; (c) 2020 h4570 Sandro SobczyÅ„ski +; Licenced under Academic Free License version 2.0 +; Review ps2sdk README & LICENSE files for further details. +; +; +;--------------------------------------------------------------- +; draw_3D_lights_notex.vcl | +;--------------------------------------------------------------- +; A VU1 microprogram to draw 3D object using XYZ2, RGBAQ and ST| +; This program uses double buffering (xtop) | +; | +; Many thanks to: | +; - Dr Henry Fortuna | +; - Jesper Svennevid, Daniel Collin | +; - Guilherme Lampert | +;--------------------------------------------------------------- + +.syntax new +.name VU1Draw3DColorsNoTex +.vu +.init_vf_all +.init_vi_all + +.include "vcl_sml.i" + +--enter +--endenter + + ;//////////// --- Load data 1 --- ///////////// + ; Updated once per mesh + MatrixLoad ObjectToScreen, 0, vi00 ; load view-projection matrix + ;///////////////////////////////////////////// + + fcset 0x000000 ; VCL won't let us use CLIP without first zeroing + ; the clip flags + + ;//////////// --- Load data 2 --- ///////////// + ; Updated dynamically + xtop iBase + + lq.xyz scale, 0(iBase) ; load program params + ; float : X, Y, Z - scale vector that we will use to scale the verts after projecting them. + ; float : W - vert count. + lq gifSetTag, 1(iBase) ; GIF tag - set + lq primTag, 2(iBase) ; GIF tag - tell GS how many data we will send + lq rgba, 3(iBase) ; RGBA + ; u32 : R, G, B, A (0-128) + iaddiu vertexData, iBase, 4 ; pointer to vertex data + ilw.w vertCount, 0(iBase) ; load vert count from scale vector + iadd colorData, vertexData, vertCount ; pointer to colors + iadd kickAddress, colorData, vertCount ; pointer for XGKICK + iadd destAddress, colorData, vertCount ; helper pointer for data inserting + ;//////////////////////////////////////////// + + ;/////////// --- Store tags --- ///////////// + sqi primTag, (destAddress++) ; prim + tell gs how many data will be + ;//////////////////////////////////////////// + + ;/////////////// --- Loop --- /////////////// + iadd vertexCounter, iBase, vertCount ; loop vertCount times + vertexLoop: + + ;////////// --- Load loop data --- ////////// + lq vertex, 0(vertexData) ; load xyz + ; float : X, Y, Z + ; any32 : _ = 0 + lq.xyzw color, 0(colorData) ; load color + ;//////////////////////////////////////////// + + + ;////////////// --- Vertex --- ////////////// + MatrixMultiplyVertex vertex, ObjectToScreen, vertex ; transform each vertex by the matrix + + clipw.xyz vertex, vertex ; Dr. Fortuna: This instruction checks if the vertex is outside + ; the viewing frustum. If it is, then the appropriate + ; clipping flags are set + fcand VI01, 0x3FFFF ; Bitwise AND the clipping flags with 0x3FFFF, this makes + ; sure that we get the clipping judgement for the last three + ; verts (i.e. that make up the triangle we are about to draw) + iaddiu iADC, VI01, 0x7FFF ; Add 0x7FFF. If any of the clipping flags were set this will + ; cause the triangle not to be drawn (any values above 0x8000 + ; that are stored in the w component of XYZ2 will set the ADC + ; bit, which tells the GS not to perform a drawing kick on this + ; triangle. + + isw.w iADC, 2(destAddress) + + div q, vf00[w], vertex[w] ; perspective divide (1/vert[w]): + mul.xyz vertex, vertex, q + mula.xyz acc, scale, vf00[w] ; scale to GS screen space + madd.xyz vertex, vertex, scale ; multiply and add the scales -> vert = vert * scale + scale + ftoi4.xyz vertex, vertex ; convert vertex to 12:4 fixed point format + ;//////////////////////////////////////////// + + ;//////////////// - COLORS - ///////////////// + mul color, color, rgba ; normalize RGBA + ColorFPtoGsRGBAQ intColor, color ; convert to int + ;/////////////////////////////////////////// + + + ;//////////// --- Store data --- //////////// + sq intColor, 0(destAddress) ; RGBA ; q is grabbed from stq + sq.xyz vertex, 1(destAddress) ; XYZ2 + ;//////////////////////////////////////////// + + iaddiu vertexData, vertexData, 1 + iaddiu colorData, colorData, 1 + iaddiu destAddress, destAddress, 2 + + iaddi vertexCounter, vertexCounter, -1 ; decrement the loop counter + ibne vertexCounter, iBase, vertexLoop ; and repeat if needed + + ;//////////////////////////////////////////// + + --barrier + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--exit +--endexit diff --git a/src/draw_3D_colors_notex.vsm b/src/draw_3D_colors_notex.vsm new file mode 100644 index 0000000..f493eec --- /dev/null +++ b/src/draw_3D_colors_notex.vsm @@ -0,0 +1,59 @@ +; ================================================= +; flowMon::Emit() vcl 1.4beta7 produced this code: + .vu + .align 4 + .global VU1Draw3DColorsNoTex_CodeStart + .global VU1Draw3DColorsNoTex_CodeEnd +VU1Draw3DColorsNoTex_CodeStart: +__v_src_draw_3D_colors_notex_vcl_4: +; _LNOPT_w=[ normal2 ] 16 [16 0] 16 [__v_src_draw_3D_colors_notex_vcl_4] + NOP lq VF01,0(VI00) + NOP xtop VI02 + NOP lq VF02,1(VI00) + NOP lq VF07,2(VI02) + NOP lq VF03,2(VI00) + NOP iaddiu VI03,VI02,0x00000004 + NOP ilw.w VI07,0(VI02) + NOP lq VF04,3(VI00) + NOP fcset 0 + NOP lq.xyz VF05,0(VI02) + NOP iadd VI04,VI03,VI07 + NOP iadd VI06,VI04,VI07 + NOP lq VF06,3(VI02) + NOP iadd VI05,VI04,VI07 + NOP sqi VF07,(VI06++) + NOP iadd VI07,VI02,VI07 +vertexLoop: +; _LNOPT_w=[ vuta1 ] 22 [31 13] 31 [vertexLoop] + NOP lq VF07,0(VI03) + mulax ACC,VF01,VF07x lq VF08,0(VI04) ; STALL_LATENCY ?3 + madday ACC,VF02,VF07y iaddiu VI06,VI06,0x00000002 + maddaz ACC,VF03,VF07z NOP + maddw VF07,VF04,VF07w NOP + mul VF08,VF08,VF06 NOP + clipw.xyz VF07xyz,VF07w div Q,VF00w,VF07w ; STALL_LATENCY ?2 + NOP NOP + NOP NOP + NOP NOP + NOP NOP + NOP NOP + NOP NOP + mulq.xyz VF07,VF07,Q fcand VI01,262143 + mulaw.xyz ACC,VF05,VF00w iaddiu VI04,VI04,0x00000001 + madd.xyz VF07,VF07,VF05 iaddiu VI03,VI03,0x00000001 ; STALL_LATENCY ?2 + ftoi0 VF08,VF08 isubiu VI07,VI07,1 + ftoi4.xyz VF07,VF07 iaddiu VI01,VI01,0x00007fff ; STALL_LATENCY ?2 + NOP isw.w VI01,0(VI06) + NOP sq VF08,-2(VI06) + NOP ibne VI07,VI02,vertexLoop + NOP sq.xyz VF07,-1(VI06) +; _LNOPT_w=[ normal2 ] 3 [1 0] 3 [__v_src_draw_3D_colors_notex_vcl_7] + NOP xgkick VI05 + NOP[E] NOP + NOP NOP + .align 4 +VU1Draw3DColorsNoTex_CodeEnd: +; iCount=41 +; register stats: +; 8 VU User integer +; 9 VU User floating point diff --git a/src/draw_3D_lights.vcl b/src/draw_3D_lights.vcl new file mode 100644 index 0000000..d839317 --- /dev/null +++ b/src/draw_3D_lights.vcl @@ -0,0 +1,200 @@ +; _____ ___ ____ ___ ____ +; ____| | ____| | | |____| +; | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +;----------------------------------------------------------------------- +; (c) 2020 h4570 Sandro SobczyÅ„ski +; Licenced under Academic Free License version 2.0 +; Review ps2sdk README & LICENSE files for further details. +; +; +;--------------------------------------------------------------- +; draw_3D_lights.vcl | +;--------------------------------------------------------------- +; A VU1 microprogram to draw 3D object using XYZ2, RGBAQ and ST| +; This program uses double buffering (xtop) | +; | +; Many thanks to: | +; - Dr Henry Fortuna | +; - Jesper Svennevid, Daniel Collin | +; - Guilherme Lampert | +;--------------------------------------------------------------- + +.syntax new +.name VU1Draw3DLightsColors +.vu +.init_vf_all +.init_vi_all + +.include "vcl_sml.i" + +--enter +--endenter + + ;//////////// --- Load data 1 --- ///////////// + ; Updated once per mesh + MatrixLoad ObjectToScreen, 0, vi00 ; load view-projection matrix + MatrixLoad LocalLight, 4, vi00 ; load local light matrix + ;///////////////////////////////////////////// + + fcset 0x000000 ; VCL won't let us use CLIP without first zeroing + ; the clip flags + + ;//////////// --- Load data 2 --- ///////////// + ; Updated dynamically + xtop iBase + + lq.xyz scale, 0(iBase) ; load program params + ; float : X, Y, Z - scale vector that we will use to scale the verts after projecting them. + ; float : W - vert count. + lq gifSetTag, 1(iBase) ; GIF tag - set + lq texGifTag1, 2(iBase) ; GIF tag - texture LOD + lq texGifTag2, 3(iBase) ; GIF tag - texture buffer & CLUT + lq primTag, 4(iBase) ; GIF tag - tell GS how many data we will send + lq rgba, 5(iBase) ; RGBA + ; u32 : R, G, B, A (0-128) + iaddiu vertexData, iBase, 6 ; pointer to vertex data + ilw.w vertCount, 0(iBase) ; load vert count from scale vector + iadd stqData, vertexData, vertCount ; pointer to stq + iadd colorData, stqData, vertCount ; pointer to colors + iadd normalData, colorData, vertCount ; pointer to colors + iadd lightsData, normalData, vertCount + MatrixLoad LightDirection, 0, lightsData ; load light directions + MatrixLoad LightAmbient, 4, lightsData ; load light ambients + MatrixLoad LightDiffuse, 8, lightsData ; load light diffuses + iaddiu kickAddress, lightsData, 12 ; pointer for XGKICK + iaddiu destAddress, lightsData, 12 ; helper pointer for data inserting + ;//////////////////////////////////////////// + + ;/////////// --- Store tags --- ///////////// + sqi gifSetTag, (destAddress++) ; + sqi texGifTag1, (destAddress++) ; texture LOD tag + sqi gifSetTag, (destAddress++) ; + sqi texGifTag2, (destAddress++) ; texture buffer & CLUT tag + sqi primTag, (destAddress++) ; prim + tell gs how many data will be + ;//////////////////////////////////////////// + + ;/////////////// --- Loop --- /////////////// + iadd vertexCounter, iBase, vertCount ; loop vertCount times + vertexLoop: + + ;////////// --- Load loop data --- ////////// + lq vertex, 0(vertexData) ; load xyz + ; float : X, Y, Z + ; any32 : _ = 0 + lq stq, 0(stqData) ; load stq + ; float : S, T + ; any32 : Q = 1 ; 1, because we will mul this by 1/vert[w] and this + ; will be our q for texture perspective correction + ; any32 : _ = 0 + lq.xyzw color, 0(colorData) ; load color + lq.xyzw normal, 0(normalData) ; load normal + ;//////////////////////////////////////////// + + + ;////////////// --- Vertex --- ////////////// + MatrixMultiplyVertex vertex, ObjectToScreen, vertex ; transform each vertex by the matrix + + clipw.xyz vertex, vertex ; Dr. Fortuna: This instruction checks if the vertex is outside + ; the viewing frustum. If it is, then the appropriate + ; clipping flags are set + fcand VI01, 0x3FFFF ; Bitwise AND the clipping flags with 0x3FFFF, this makes + ; sure that we get the clipping judgement for the last three + ; verts (i.e. that make up the triangle we are about to draw) + iaddiu iADC, VI01, 0x7FFF ; Add 0x7FFF. If any of the clipping flags were set this will + ; cause the triangle not to be drawn (any values above 0x8000 + ; that are stored in the w component of XYZ2 will set the ADC + ; bit, which tells the GS not to perform a drawing kick on this + ; triangle. + + isw.w iADC, 2(destAddress) + + div q, vf00[w], vertex[w] ; perspective divide (1/vert[w]): + mul.xyz vertex, vertex, q + mula.xyz acc, scale, vf00[w] ; scale to GS screen space + madd.xyz vertex, vertex, scale ; multiply and add the scales -> vert = vert * scale + scale + ftoi4.xyz vertex, vertex ; convert vertex to 12:4 fixed point format + ;//////////////////////////////////////////// + + + ;//////////////// --- ST --- //////////////// + mulq modStq, stq, q + ;//////////////////////////////////////////// + + ;//////////////// - NORMALS - ///////////////// + MatrixMultiplyVertex normal, LocalLight, normal ; transform each normal by the matrix + div q, vf00[w], normal[w] ; perspective divide (1/vert[w]): + mul.xyz normal, normal, q + + add light, vf00, vf00 + add light, light, LightAmbient[0] + add light, light, LightAmbient[1] + add light, light, LightAmbient[2] + add light, light, LightAmbient[3] + + add intensity, vf00, vf00 + + loi -1.0 + addi minusOne, vf00, i + + VectorDotProduct intensity, normal, LightDirection[0] + + mul intensity, intensity, minusOne + maxx.xyzw intensity, intensity, vf00 + + mul diffuse, LightDiffuse[0], intensity[x] + add light, light, diffuse + + VectorDotProduct intensity, normal, LightDirection[1] + + mul intensity, intensity, minusOne + maxx.xyzw intensity, intensity, vf00 + + mul diffuse, LightDiffuse[1], intensity[x] + add light, light, diffuse + + VectorDotProduct intensity, normal, LightDirection[2] + + mul intensity, intensity, minusOne + maxx.xyzw intensity, intensity, vf00 + + mul diffuse, LightDiffuse[2], intensity[x] + add light, light, diffuse + + VectorDotProduct intensity, normal, LightDirection[3] + + mul intensity, intensity, minusOne + maxx.xyzw intensity, intensity, vf00 + + mul diffuse, LightDiffuse[3], intensity[x] + add light, light, diffuse + + mul.xyz color, color, light ; color = color * light + VectorClamp color, color 0.0 1.99 + mul color, color, rgba ; normalize RGBA + ColorFPtoGsRGBAQ intColor, color ; convert to int + ;/////////////////////////////////////////// + + + ;//////////// --- Store data --- //////////// + sq modStq, 0(destAddress) ; STQ + sq intColor, 1(destAddress) ; RGBA ; q is grabbed from stq + sq.xyz vertex, 2(destAddress) ; XYZ2 + ;//////////////////////////////////////////// + + iaddiu vertexData, vertexData, 1 + iaddiu stqData, stqData, 1 + iaddiu colorData, colorData, 1 + iaddiu normalData, normalData, 1 + iaddiu destAddress, destAddress, 3 + + iaddi vertexCounter, vertexCounter, -1 ; decrement the loop counter + ibne vertexCounter, iBase, vertexLoop ; and repeat if needed + + ;//////////////////////////////////////////// + + --barrier + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--exit +--endexit diff --git a/src/draw_3D_lights.vsm b/src/draw_3D_lights.vsm new file mode 100644 index 0000000..b8de379 --- /dev/null +++ b/src/draw_3D_lights.vsm @@ -0,0 +1,123 @@ +; ================================================= +; flowMon::Emit() vcl 1.4beta7 produced this code: + .vu + .align 4 + .global VU1Draw3DLightsColors_CodeStart + .global VU1Draw3DLightsColors_CodeEnd +VU1Draw3DLightsColors_CodeStart: +__v_src_draw_3D_lights_vcl_4: +; _LNOPT_w=[ normal2 ] 42 [42 0] 42 [__v_src_draw_3D_lights_vcl_4] + NOP lq VF01,0(VI00) + NOP lq VF02,1(VI00) + NOP lq VF03,2(VI00) + NOP lq VF04,3(VI00) + NOP lq VF05,4(VI00) + NOP xtop VI02 + NOP lq VF06,5(VI00) + NOP lq VF20,1(VI02) + NOP lq VF22,2(VI02) + NOP lq VF21,3(VI02) + NOP lq VF23,4(VI02) + NOP lq VF07,6(VI00) + NOP iaddiu VI03,VI02,0x00000006 + NOP ilw.w VI09,0(VI02) + NOP lq VF08,7(VI00) + NOP fcset 0 + NOP lq.xyz VF09,0(VI02) + NOP iadd VI04,VI03,VI09 + NOP iadd VI05,VI04,VI09 + NOP iadd VI06,VI05,VI09 + NOP iadd VI07,VI06,VI09 + NOP lq VF10,5(VI02) + NOP lq.xyz VF11,0(VI07) + NOP lq.xyz VF12,1(VI07) + NOP lq.xyz VF13,2(VI07) + NOP lq.xyz VF14,3(VI07) + NOP lq.xyz VF15,4(VI07) + NOP lq.xyz VF16,5(VI07) + NOP lq.xyz VF17,6(VI07) + NOP lq.xyz VF18,7(VI07) + NOP lq.xyz VF19,8(VI07) + NOP iaddiu VI08,VI07,0x0000000c + NOP sqi VF20,(VI08++) + NOP sqi VF22,(VI08++) + NOP sqi VF20,(VI08++) + NOP sqi VF21,(VI08++) + NOP lq.xyz VF20,9(VI07) + NOP lq.xyz VF21,10(VI07) + NOP lq.xyz VF22,11(VI07) + NOP iaddiu VI07,VI07,0x0000000c + NOP sqi VF23,(VI08++) + NOP iadd VI09,VI02,VI09 +vertexLoop: +; _LNOPT_w=[ another ] 60 [83 56] 83 [vertexLoop] + max.xyz VF26,VF00,VF00 lq VF23,0(VI06) + mulax ACC,VF05,VF23x lq VF25,0(VI03) ; STALL_LATENCY ?3 + madday ACC,VF06,VF23y loi 0xbf800000 + maddaz ACC,VF07,VF23z iaddiu VI06,VI06,0x00000001 + maddw VF23,VF08,VF23w iaddiu VI05,VI05,0x00000001 + mulax ACC,VF01,VF25x iaddiu VI04,VI04,0x00000001 + add.xyz VF26,VF26,VF15 iaddiu VI08,VI08,0x00000003 + addi.x VF24,VF00,I div Q,VF00w,VF23w ; STALL_LATENCY ?1 + add.xyz VF26,VF26,VF16 loi 0x00000000 ; STALL_LATENCY ?1 + madday ACC,VF02,VF25y isubiu VI09,VI09,1 + maddaz ACC,VF03,VF25z NOP + maddw VF25,VF04,VF25w NOP + add.xyz VF29,VF26,VF17 NOP + mulq.xyz VF26,VF23,Q waitq + mulaw.xyz ACC,VF09,VF00w NOP + clipw.xyz VF25xyz,VF25w NOP + add.xyz VF23,VF29,VF18 NOP + mul.xyz VF29,VF26,VF11 NOP + mul.xyz VF28,VF26,VF12 NOP + mul.xyz VF27,VF26,VF13 fcand VI01,262143 + mul.xyz VF26,VF26,VF14 NOP + addy.x VF29,VF29,VF29y div Q,VF00w,VF25w + addy.x VF30,VF28,VF28y NOP + addy.x VF27,VF27,VF27y NOP + addy.x VF26,VF26,VF26y NOP + addz.x VF29,VF29,VF29z NOP + addz.x VF28,VF30,VF28z NOP + addz.x VF27,VF27,VF27z NOP + mulq.xyz VF25,VF25,Q lq VF30,-1(VI04) + mul.x VF29,VF29,VF24 NOP + addz.x VF26,VF26,VF26z NOP + mul.x VF28,VF28,VF24 NOP + mulq VF30,VF30,Q NOP + maxx.x VF29,VF29,VF00x NOP + mul.x VF27,VF27,VF24 NOP + maxx.x VF28,VF28,VF00x NOP + mul.x VF26,VF26,VF24 NOP + mulx.xyz VF30,VF19,VF29x sq VF30,-3(VI08) + maxx.x VF27,VF27,VF00x NOP + madd.xyz VF29,VF25,VF09 NOP + mulx.xyz VF25,VF20,VF28x NOP + add.xyz VF28,VF23,VF30 NOP + mulx.xyz VF23,VF21,VF27x NOP + ftoi4.xyz VF27,VF29 NOP + maxx.x VF29,VF26,VF00x NOP + add.xyz VF26,VF28,VF25 NOP + NOP sq.xyz VF27,-1(VI08) ; STALL_LATENCY ?1 + mulx.xyz VF25,VF22,VF29x NOP + add.xyz VF23,VF26,VF23 NOP + add.xyz VF25,VF23,VF25 lq VF23,-1(VI05) ; STALL_LATENCY ?3 + addi.x VF26,VF00,I loi 0x3ffeb852 + mul.xyz VF23,VF23,VF25 NOP ; STALL_LATENCY ?2 + addi.x VF25,VF00,I NOP + maxx VF23,VF23,VF26x NOP ; STALL_LATENCY ?2 + minix VF23,VF23,VF25x NOP ; STALL_LATENCY ?3 + mul VF23,VF23,VF10 iaddiu VI03,VI03,0x00000001 ; STALL_LATENCY ?3 + ftoi0 VF23,VF23 iaddiu VI01,VI01,0x00007fff ; STALL_LATENCY ?3 + NOP isw.w VI01,-1(VI08) + NOP ibne VI09,VI02,vertexLoop + NOP sq VF23,-2(VI08) ; STALL_LATENCY ?1 +; _LNOPT_w=[ normal2 ] 3 [1 0] 3 [__v_src_draw_3D_lights_vcl_7] + NOP xgkick VI07 + NOP[E] NOP + NOP NOP + .align 4 +VU1Draw3DLightsColors_CodeEnd: +; iCount=105 +; register stats: +; 10 VU User integer +; 31 VU User floating point diff --git a/src/draw_3D_lights_notex.vcl b/src/draw_3D_lights_notex.vcl new file mode 100644 index 0000000..058009a --- /dev/null +++ b/src/draw_3D_lights_notex.vcl @@ -0,0 +1,181 @@ +; _____ ___ ____ ___ ____ +; ____| | ____| | | |____| +; | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +;----------------------------------------------------------------------- +; (c) 2020 h4570 Sandro SobczyÅ„ski +; Licenced under Academic Free License version 2.0 +; Review ps2sdk README & LICENSE files for further details. +; +; +;--------------------------------------------------------------- +; draw_3D_lights_notex.vcl | +;--------------------------------------------------------------- +; A VU1 microprogram to draw 3D object using XYZ2, RGBAQ and ST| +; This program uses double buffering (xtop) | +; | +; Many thanks to: | +; - Dr Henry Fortuna | +; - Jesper Svennevid, Daniel Collin | +; - Guilherme Lampert | +;--------------------------------------------------------------- + +.syntax new +.name VU1Draw3DLightsColorsNoTex +.vu +.init_vf_all +.init_vi_all + +.include "vcl_sml.i" + +--enter +--endenter + + ;//////////// --- Load data 1 --- ///////////// + ; Updated once per mesh + MatrixLoad ObjectToScreen, 0, vi00 ; load view-projection matrix + MatrixLoad LocalLight, 4, vi00 ; load local light matrix + ;///////////////////////////////////////////// + + fcset 0x000000 ; VCL won't let us use CLIP without first zeroing + ; the clip flags + + ;//////////// --- Load data 2 --- ///////////// + ; Updated dynamically + xtop iBase + + lq.xyz scale, 0(iBase) ; load program params + ; float : X, Y, Z - scale vector that we will use to scale the verts after projecting them. + ; float : W - vert count. + lq gifSetTag, 1(iBase) ; GIF tag - set + lq primTag, 2(iBase) ; GIF tag - tell GS how many data we will send + lq rgba, 3(iBase) ; RGBA + ; u32 : R, G, B, A (0-128) + iaddiu vertexData, iBase, 4 ; pointer to vertex data + ilw.w vertCount, 0(iBase) ; load vert count from scale vector + iadd colorData, vertexData, vertCount ; pointer to colors + iadd normalData, colorData, vertCount ; pointer to colors + iadd lightsData, normalData, vertCount + MatrixLoad LightDirection, 0, lightsData ; load light directions + MatrixLoad LightAmbient, 4, lightsData ; load light ambients + MatrixLoad LightDiffuse, 8, lightsData ; load light diffuses + iaddiu kickAddress, lightsData, 12 ; pointer for XGKICK + iaddiu destAddress, lightsData, 12 ; helper pointer for data inserting + ;//////////////////////////////////////////// + + ;/////////// --- Store tags --- ///////////// + sqi primTag, (destAddress++) ; prim + tell gs how many data will be + ;//////////////////////////////////////////// + + ;/////////////// --- Loop --- /////////////// + iadd vertexCounter, iBase, vertCount ; loop vertCount times + vertexLoop: + + ;////////// --- Load loop data --- ////////// + lq vertex, 0(vertexData) ; load xyz + ; float : X, Y, Z + ; any32 : _ = 0 + lq.xyzw color, 0(colorData) ; load color + lq.xyzw normal, 0(normalData) ; load normal + ;//////////////////////////////////////////// + + + ;////////////// --- Vertex --- ////////////// + MatrixMultiplyVertex vertex, ObjectToScreen, vertex ; transform each vertex by the matrix + + clipw.xyz vertex, vertex ; Dr. Fortuna: This instruction checks if the vertex is outside + ; the viewing frustum. If it is, then the appropriate + ; clipping flags are set + fcand VI01, 0x3FFFF ; Bitwise AND the clipping flags with 0x3FFFF, this makes + ; sure that we get the clipping judgement for the last three + ; verts (i.e. that make up the triangle we are about to draw) + iaddiu iADC, VI01, 0x7FFF ; Add 0x7FFF. If any of the clipping flags were set this will + ; cause the triangle not to be drawn (any values above 0x8000 + ; that are stored in the w component of XYZ2 will set the ADC + ; bit, which tells the GS not to perform a drawing kick on this + ; triangle. + + isw.w iADC, 2(destAddress) + + div q, vf00[w], vertex[w] ; perspective divide (1/vert[w]): + mul.xyz vertex, vertex, q + mula.xyz acc, scale, vf00[w] ; scale to GS screen space + madd.xyz vertex, vertex, scale ; multiply and add the scales -> vert = vert * scale + scale + ftoi4.xyz vertex, vertex ; convert vertex to 12:4 fixed point format + ;//////////////////////////////////////////// + + ;//////////////// - NORMALS - ///////////////// + MatrixMultiplyVertex normal, LocalLight, normal ; transform each normal by the matrix + div q, vf00[w], normal[w] ; perspective divide (1/vert[w]): + mul.xyz normal, normal, q + + add light, vf00, vf00 + add light, light, LightAmbient[0] + add light, light, LightAmbient[1] + add light, light, LightAmbient[2] + add light, light, LightAmbient[3] + + add intensity, vf00, vf00 + + loi -1.0 + addi minusOne, vf00, i + + VectorDotProduct intensity, normal, LightDirection[0] + + mul intensity, intensity, minusOne + maxx.xyzw intensity, intensity, vf00 + + mul diffuse, LightDiffuse[0], intensity[x] + add light, light, diffuse + + VectorDotProduct intensity, normal, LightDirection[1] + + mul intensity, intensity, minusOne + maxx.xyzw intensity, intensity, vf00 + + mul diffuse, LightDiffuse[1], intensity[x] + add light, light, diffuse + + VectorDotProduct intensity, normal, LightDirection[2] + + mul intensity, intensity, minusOne + maxx.xyzw intensity, intensity, vf00 + + mul diffuse, LightDiffuse[2], intensity[x] + add light, light, diffuse + + VectorDotProduct intensity, normal, LightDirection[3] + + mul intensity, intensity, minusOne + maxx.xyzw intensity, intensity, vf00 + + mul diffuse, LightDiffuse[3], intensity[x] + add light, light, diffuse + + mul.xyz color, color, light ; color = color * light + VectorClamp color, color 0.0 1.99 + mul color, color, rgba ; normalize RGBA + ColorFPtoGsRGBAQ intColor, color ; convert to int + ;/////////////////////////////////////////// + + + ;//////////// --- Store data --- //////////// + sq intColor, 0(destAddress) ; RGBA ; q is grabbed from stq + sq.xyz vertex, 1(destAddress) ; XYZ2 + ;//////////////////////////////////////////// + + iaddiu vertexData, vertexData, 1 + iaddiu colorData, colorData, 1 + iaddiu normalData, normalData, 1 + iaddiu destAddress, destAddress, 2 + + iaddi vertexCounter, vertexCounter, -1 ; decrement the loop counter + ibne vertexCounter, iBase, vertexLoop ; and repeat if needed + + ;//////////////////////////////////////////// + + --barrier + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--exit +--endexit diff --git a/src/draw_3D_lights_notex.vsm b/src/draw_3D_lights_notex.vsm new file mode 100644 index 0000000..ab54f2f --- /dev/null +++ b/src/draw_3D_lights_notex.vsm @@ -0,0 +1,114 @@ +; ================================================= +; flowMon::Emit() vcl 1.4beta7 produced this code: + .vu + .align 4 + .global VU1Draw3DLightsColorsNoTex_CodeStart + .global VU1Draw3DLightsColorsNoTex_CodeEnd +VU1Draw3DLightsColorsNoTex_CodeStart: +__v_src_draw_3D_lights_notex_vcl_4: +; _LNOPT_w=[ normal2 ] 34 [34 0] 34 [__v_src_draw_3D_lights_notex_vcl_4] + NOP lq VF01,0(VI00) + NOP lq VF02,1(VI00) + NOP lq VF03,2(VI00) + NOP lq VF04,3(VI00) + NOP lq VF05,4(VI00) + NOP xtop VI02 + NOP lq VF06,5(VI00) + NOP lq VF23,2(VI02) + NOP lq VF07,6(VI00) + NOP iaddiu VI03,VI02,0x00000004 + NOP ilw.w VI08,0(VI02) + NOP lq VF08,7(VI00) + NOP fcset 0 + NOP lq.xyz VF09,0(VI02) + NOP iadd VI04,VI03,VI08 + NOP iadd VI05,VI04,VI08 + NOP iadd VI06,VI05,VI08 + NOP lq VF10,3(VI02) + NOP lq.xyz VF11,0(VI06) + NOP lq.xyz VF12,1(VI06) + NOP lq.xyz VF13,2(VI06) + NOP lq.xyz VF14,3(VI06) + NOP lq.xyz VF15,4(VI06) + NOP lq.xyz VF16,5(VI06) + NOP lq.xyz VF17,6(VI06) + NOP lq.xyz VF18,7(VI06) + NOP lq.xyz VF19,8(VI06) + NOP iaddiu VI07,VI06,0x0000000c + NOP lq.xyz VF20,9(VI06) + NOP lq.xyz VF21,10(VI06) + NOP lq.xyz VF22,11(VI06) + NOP iaddiu VI06,VI06,0x0000000c + NOP sqi VF23,(VI07++) + NOP iadd VI08,VI02,VI08 +vertexLoop: +; _LNOPT_w=[ another ] 59 [83 55] 83 [vertexLoop] + max.xyz VF26,VF00,VF00 lq VF23,0(VI05) + mulax ACC,VF05,VF23x lq VF24,0(VI03) ; STALL_LATENCY ?3 + madday ACC,VF06,VF23y iaddiu VI05,VI05,0x00000001 + maddaz ACC,VF07,VF23z iaddiu VI04,VI04,0x00000001 + maddw VF23,VF08,VF23w iaddiu VI07,VI07,0x00000002 + mulax ACC,VF01,VF24x NOP + add.xyz VF26,VF26,VF15 NOP + madday ACC,VF02,VF24y div Q,VF00w,VF23w ; STALL_LATENCY ?1 + maddaz ACC,VF03,VF24z NOP + add.xyz VF26,VF26,VF16 NOP + add.xyz VF28,VF26,VF17 NOP ; STALL_LATENCY ?3 + mulq.xyz VF26,VF23,Q waitq + maddw VF24,VF04,VF24w loi 0xbf800000 + addi.x VF25,VF00,I loi 0x00000000 + mulaw.xyz ACC,VF09,VF00w NOP + mul.xyz VF29,VF26,VF11 NOP + clipw.xyz VF24xyz,VF24w NOP + add.xyz VF23,VF28,VF18 NOP + mul.xyz VF28,VF26,VF12 NOP + addy.x VF29,VF29,VF29y NOP + mul.xyz VF27,VF26,VF13 fcand VI01,262143 + mul.xyz VF26,VF26,VF14 NOP + addy.x VF28,VF28,VF28y div Q,VF00w,VF24w + addz.x VF29,VF29,VF29z NOP + addy.x VF27,VF27,VF27y NOP + addy.x VF26,VF26,VF26y NOP + addz.x VF28,VF28,VF28z NOP + mul.x VF31,VF29,VF25 NOP + addz.x VF27,VF27,VF27z NOP + mulq.xyz VF24,VF24,Q waitq + mul.x VF29,VF28,VF25 NOP + maxx.x VF28,VF31,VF00x NOP + addz.x VF30,VF26,VF26z NOP + mul.x VF26,VF27,VF25 NOP + maxx.x VF27,VF29,VF00x NOP + mulx.xyz VF29,VF19,VF28x NOP + mul.x VF25,VF30,VF25 NOP + maxx.x VF26,VF26,VF00x NOP + mulx.xyz VF27,VF20,VF27x NOP + add.xyz VF29,VF23,VF29 NOP + madd.xyz VF28,VF24,VF09 NOP + maxx.x VF24,VF25,VF00x NOP + mulx.xyz VF23,VF21,VF26x NOP + add.xyz VF26,VF29,VF27 NOP + mulx.xyz VF24,VF22,VF24x NOP ; STALL_LATENCY ?1 + add.xyz VF23,VF26,VF23 NOP ; STALL_LATENCY ?1 + add.xyz VF24,VF23,VF24 lq VF23,-1(VI04) ; STALL_LATENCY ?3 + addi.x VF26,VF00,I loi 0x3ffeb852 + mul.xyz VF23,VF23,VF24 NOP ; STALL_LATENCY ?2 + addi.x VF24,VF00,I NOP + maxx VF23,VF23,VF26x NOP ; STALL_LATENCY ?2 + minix VF23,VF23,VF24x NOP ; STALL_LATENCY ?3 + mul VF23,VF23,VF10 iaddiu VI03,VI03,0x00000001 ; STALL_LATENCY ?3 + ftoi4.xyz VF28,VF28 isubiu VI08,VI08,1 + ftoi0 VF23,VF23 iaddiu VI01,VI01,0x00007fff ; STALL_LATENCY ?2 + NOP isw.w VI01,0(VI07) + NOP sq.xyz VF28,-1(VI07) + NOP ibne VI08,VI02,vertexLoop + NOP sq VF23,-2(VI07) +; _LNOPT_w=[ normal2 ] 3 [1 0] 3 [__v_src_draw_3D_lights_notex_vcl_7] + NOP xgkick VI06 + NOP[E] NOP + NOP NOP + .align 4 +VU1Draw3DLightsColorsNoTex_CodeEnd: +; iCount=96 +; register stats: +; 9 VU User integer +; 32 VU User floating point diff --git a/src/draw_3D_notex.vcl b/src/draw_3D_notex.vcl new file mode 100644 index 0000000..eab4984 --- /dev/null +++ b/src/draw_3D_notex.vcl @@ -0,0 +1,120 @@ +; _____ ___ ____ ___ ____ +; ____| | ____| | | |____| +; | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +;----------------------------------------------------------------------- +; (c) 2020 h4570 Sandro SobczyÅ„ski +; Licenced under Academic Free License version 2.0 +; Review ps2sdk README & LICENSE files for further details. +; +; +;--------------------------------------------------------------- +; draw_3D_notex.vcl | +;--------------------------------------------------------------- +; A VU1 microprogram to draw 3D object using XYZ2, RGBAQ and ST| +; This program uses double buffering (xtop) | +; | +; Many thanks to: | +; - Dr Henry Fortuna | +; - Jesper Svennevid, Daniel Collin | +; - Guilherme Lampert | +;--------------------------------------------------------------- + +.syntax new +.name VU1Draw3DNoTex +.vu +.init_vf_all +.init_vi_all + +--enter +--endenter + + ;//////////// --- Load data 1 --- ///////////// + ; Updated once per mesh + lq matrixRow0, 0(vi00) ; load view-projection matrix + lq matrixRow1, 1(vi00) + lq matrixRow2, 2(vi00) + lq matrixRow3, 3(vi00) + ;///////////////////////////////////////////// + + fcset 0x000000 ; VCL won't let us use CLIP without first zeroing + ; the clip flags + + ;//////////// --- Load data 2 --- ///////////// + ; Updated dynamically + xtop iBase + + lq.xyz scale, 0(iBase) ; load program params + ; float : X, Y, Z - scale vector that we will use to scale the verts after projecting them. + ; float : W - vert count. + lq gifSetTag, 1(iBase) ; GIF tag - set + lq primTag, 2(iBase) ; GIF tag - tell GS how many data we will send + lq rgba, 3(iBase) ; RGBA + ; u32 : R, G, B, A (0-128) + iaddiu vertexData, iBase, 4 ; pointer to vertex data + ilw.w vertCount, 0(iBase) ; load vert count from scale vector + iadd kickAddress, vertexData, vertCount ; pointer for XGKICK + iadd destAddress, vertexData, vertCount ; helper pointer for data inserting + ;//////////////////////////////////////////// + + ;/////////// --- Store tags --- ///////////// + sqi primTag, (destAddress++) ; prim + tell gs how many data will be + ;//////////////////////////////////////////// + + ;/////////////// --- Loop --- /////////////// + iadd vertexCounter, iBase, vertCount ; loop vertCount times + vertexLoop: + + ;////////// --- Load loop data --- ////////// + lq vertex, 0(vertexData) ; load xyz + ; float : X, Y, Z + ; any32 : _ = 0 + ;//////////////////////////////////////////// + + + ;////////////// --- Vertex --- ////////////// + mul acc, matrixRow0, vertex[x] ; transform each vertex by the matrix + madd acc, matrixRow1, vertex[y] + madd acc, matrixRow2, vertex[z] + madd vertex, matrixRow3, vertex[w] + + clipw.xyz vertex, vertex ; Dr. Fortuna: This instruction checks if the vertex is outside + ; the viewing frustum. If it is, then the appropriate + ; clipping flags are set + fcand VI01, 0x3FFFF ; Bitwise AND the clipping flags with 0x3FFFF, this makes + ; sure that we get the clipping judgement for the last three + ; verts (i.e. that make up the triangle we are about to draw) + iaddiu iADC, VI01, 0x7FFF ; Add 0x7FFF. If any of the clipping flags were set this will + ; cause the triangle not to be drawn (any values above 0x8000 + ; that are stored in the w component of XYZ2 will set the ADC + ; bit, which tells the GS not to perform a drawing kick on this + ; triangle. + + isw.w iADC, 2(destAddress) + + div q, vf00[w], vertex[w] ; perspective divide (1/vert[w]): + mul.xyz vertex, vertex, q + mula.xyz acc, scale, vf00[w] ; scale to GS screen space + madd.xyz vertex, vertex, scale ; multiply and add the scales -> vert = vert * scale + scale + ftoi4.xyz vertex, vertex ; convert vertex to 12:4 fixed point format + ;//////////////////////////////////////////// + + + ;//////////// --- Store data --- //////////// + sq rgba, 0(destAddress) ; RGBA ; q is grabbed from stq + sq.xyz vertex, 1(destAddress) ; XYZ2 + ;//////////////////////////////////////////// + + iaddiu vertexData, vertexData, 1 + iaddiu destAddress, destAddress, 2 + + iaddi vertexCounter, vertexCounter, -1 ; decrement the loop counter + ibne vertexCounter, iBase, vertexLoop ; and repeat if needed + + ;//////////////////////////////////////////// + + --barrier + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--exit +--endexit diff --git a/src/draw_3D_notex.vsm b/src/draw_3D_notex.vsm new file mode 100644 index 0000000..6de455a --- /dev/null +++ b/src/draw_3D_notex.vsm @@ -0,0 +1,50 @@ +; ================================================= +; flowMon::Emit() vcl 1.4beta7 produced this code: + .vu + .align 4 + .global VU1Draw3DNoTex_CodeStart + .global VU1Draw3DNoTex_CodeEnd +VU1Draw3DNoTex_CodeStart: +__v_src_draw_3D_notex_vcl_4: +; _LNOPT_w=[ normal2 ] 15 [15 0] 15 [__v_src_draw_3D_notex_vcl_4] + NOP lq VF01,0(VI00) + NOP xtop VI02 + NOP lq VF02,1(VI00) + NOP lq VF07,2(VI02) + NOP lq VF03,2(VI00) + NOP iaddiu VI03,VI02,0x00000004 + NOP ilw.w VI06,0(VI02) + NOP lq VF04,3(VI00) + NOP fcset 0 + NOP lq.xyz VF05,0(VI02) + NOP iadd VI05,VI03,VI06 + NOP lq VF06,3(VI02) + NOP iadd VI04,VI03,VI06 + NOP sqi VF07,(VI05++) + NOP iadd VI06,VI02,VI06 +vertexLoop: +; _LNOPT_w=[ normal2 ] 14 [31 12] 31 [vertexLoop] + NOP lq VF07,0(VI03) + mulax ACC,VF01,VF07x iaddiu VI03,VI03,0x00000001 ; STALL_LATENCY ?3 + madday ACC,VF02,VF07y iaddiu VI05,VI05,0x00000002 + maddaz ACC,VF03,VF07z isubiu VI06,VI06,1 + maddw VF07,VF04,VF07w NOP + clipw.xyz VF07xyz,VF07w div Q,VF00w,VF07w ; STALL_LATENCY ?3 + mulq.xyz VF07,VF07,Q waitq ; STALL_LATENCY ?6 + mulaw.xyz ACC,VF05,VF00w NOP + madd.xyz VF07,VF07,VF05 sq VF06,-2(VI05) ; STALL_LATENCY ?2 + ftoi4.xyz VF07,VF07 fcand VI01,262143 ; STALL_LATENCY ?3 + NOP iaddiu VI01,VI01,0x00007fff + NOP isw.w VI01,0(VI05) + NOP ibne VI06,VI02,vertexLoop + NOP sq.xyz VF07,-1(VI05) +; _LNOPT_w=[ normal2 ] 3 [1 0] 3 [__v_src_draw_3D_notex_vcl_7] + NOP xgkick VI04 + NOP[E] NOP + NOP NOP + .align 4 +VU1Draw3DNoTex_CodeEnd: +; iCount=32 +; register stats: +; 7 VU User integer +; 8 VU User floating point diff --git a/src/ee_tools.c b/src/ee_tools.c index f83c951..33fe8c8 100644 --- a/src/ee_tools.c +++ b/src/ee_tools.c @@ -3,7 +3,7 @@ void RedirectFunction(void* old, void* new) { u32* function = old; - u32 patch = new; + u32 patch = (u32)new; FlushCache(2); @@ -17,7 +17,7 @@ void RedirectFunction(void* old, void* new) { void RedirectCall(void* old, void* new) { u32* function = old; - u32 patch = new; + u32 patch = (u32)new; FlushCache(2); diff --git a/src/fast_obj/.gitignore b/src/fast_obj/.gitignore new file mode 100644 index 0000000..9ef9604 --- /dev/null +++ b/src/fast_obj/.gitignore @@ -0,0 +1,2 @@ +build + diff --git a/src/fast_obj/CMakeLists.txt b/src/fast_obj/CMakeLists.txt new file mode 100644 index 0000000..b3d47f4 --- /dev/null +++ b/src/fast_obj/CMakeLists.txt @@ -0,0 +1,18 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.0) + +PROJECT(fast_obj) + +OPTION(FAST_OBJ_BUILD_TEST "Build test application" OFF) + +ADD_LIBRARY(fast_obj INTERFACE) +TARGET_INCLUDE_DIRECTORIES(fast_obj INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +ADD_LIBRARY(fast_obj_lib STATIC fast_obj.c) +TARGET_INCLUDE_DIRECTORIES(fast_obj_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + + +IF(${FAST_OBJ_BUILD_TEST}) + ADD_EXECUTABLE(fast_obj_test test/test.cpp) + TARGET_COMPILE_FEATURES(fast_obj_test PRIVATE cxx_std_11) + TARGET_LINK_LIBRARIES(fast_obj_test PRIVATE fast_obj_lib) +ENDIF() diff --git a/src/fast_obj/LICENSE b/src/fast_obj/LICENSE new file mode 100644 index 0000000..579cbb2 --- /dev/null +++ b/src/fast_obj/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 thisistherk + +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/src/fast_obj/README.md b/src/fast_obj/README.md new file mode 100644 index 0000000..e62df79 --- /dev/null +++ b/src/fast_obj/README.md @@ -0,0 +1,24 @@ +# fast_obj + +Because the world needs another OBJ loader. +Single header library, should compile without warnings in both C89 or C++. +Much faster (5-10x) than other libraries tested. + +To use: + + fastObjMesh* mesh = fast_obj_read("path/to/objfile.obj"); + + ...do stuff with mesh... + + fast_obj_destroy(mesh); + +Note that valid indices in the `fastObjMesh::indices` array start from `1`. A dummy position, normal and +texture coordinate are added to the corresponding `fastObjMesh` arrays at element `0` and then an index +of `0` is used to indicate that attribute is not present at the vertex. This means that users can avoid +the need to test for non-present data if required as the vertices will still reference a valid entry in +the mesh arrays. + +A simple test app is provided to compare speed against [tinyobjloader](https://github.com/syoyo/tinyobjloader) and +check output matches. + + diff --git a/src/fast_obj/fast_obj.c b/src/fast_obj/fast_obj.c new file mode 100644 index 0000000..2dc3257 --- /dev/null +++ b/src/fast_obj/fast_obj.c @@ -0,0 +1,28 @@ +/* + * + * MIT License + * + * Copyright (c) 2018 Richard Knight + * + * 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 FAST_OBJ_IMPLEMENTATION +#include "fast_obj.h" diff --git a/src/fast_obj/fast_obj.h b/src/fast_obj/fast_obj.h new file mode 100644 index 0000000..c0cbb95 --- /dev/null +++ b/src/fast_obj/fast_obj.h @@ -0,0 +1,1526 @@ +/* + * fast_obj + * + * Version 1.2 + * + * MIT License + * + * Copyright (c) 2018-2021 Richard Knight + * + * 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. + * + */ + +#ifndef FAST_OBJ_HDR +#define FAST_OBJ_HDR + +#define FAST_OBJ_VERSION_MAJOR 1 +#define FAST_OBJ_VERSION_MINOR 2 +#define FAST_OBJ_VERSION ((FAST_OBJ_VERSION_MAJOR << 8) | FAST_OBJ_VERSION_MINOR) + +#include + + +typedef struct +{ + /* Texture name from .mtl file */ + char* name; + + /* Resolved path to texture */ + char* path; + +} fastObjTexture; + + +typedef struct +{ + /* Material name */ + char* name; + + /* Parameters */ + float Ka[3]; /* Ambient */ + float Kd[3]; /* Diffuse */ + float Ks[3]; /* Specular */ + float Ke[3]; /* Emission */ + float Kt[3]; /* Transmittance */ + float Ns; /* Shininess */ + float Ni; /* Index of refraction */ + float Tf[3]; /* Transmission filter */ + float d; /* Disolve (alpha) */ + int illum; /* Illumination model */ + + /* Texture maps */ + fastObjTexture map_Ka; + fastObjTexture map_Kd; + fastObjTexture map_Ks; + fastObjTexture map_Ke; + fastObjTexture map_Kt; + fastObjTexture map_Ns; + fastObjTexture map_Ni; + fastObjTexture map_d; + fastObjTexture map_bump; + +} fastObjMaterial; + +/* Allows user override to bigger indexable array */ +#ifndef FAST_OBJ_UINT_TYPE +#define FAST_OBJ_UINT_TYPE unsigned int +#endif + +typedef FAST_OBJ_UINT_TYPE fastObjUInt; + +typedef struct +{ + fastObjUInt p; + fastObjUInt t; + fastObjUInt n; + +} fastObjIndex; + + +typedef struct +{ + /* Group name */ + char* name; + + /* Number of faces */ + unsigned int face_count; + + /* First face in fastObjMesh face_* arrays */ + unsigned int face_offset; + + /* First index in fastObjMesh indices array */ + unsigned int index_offset; + +} fastObjGroup; + + +typedef struct +{ + /* Vertex data */ + unsigned int position_count; + float* positions; + + unsigned int texcoord_count; + float* texcoords; + + unsigned int normal_count; + float* normals; + + /* Face data: one element for each face */ + unsigned int face_count; + unsigned int* face_vertices; + unsigned int* face_materials; + + /* Index data: one element for each face vertex */ + unsigned int index_count; + fastObjIndex* indices; + + /* Materials */ + unsigned int material_count; + fastObjMaterial* materials; + + /* Mesh objects ('o' tag in .obj file) */ + unsigned int object_count; + fastObjGroup* objects; + + /* Mesh groups ('g' tag in .obj file) */ + unsigned int group_count; + fastObjGroup* groups; + +} fastObjMesh; + +typedef struct +{ + void* (*file_open)(const char* path, void* user_data); + void (*file_close)(void* file, void* user_data); + size_t (*file_read)(void* file, void* dst, size_t bytes, void* user_data); + unsigned long (*file_size)(void* file, void* user_data); +} fastObjCallbacks; + +#ifdef __cplusplus +extern "C" { +#endif + +fastObjMesh* fast_obj_read(const char* path); +fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbacks* callbacks, void* user_data); +void fast_obj_destroy(fastObjMesh* mesh); + +#ifdef __cplusplus +} +#endif + +#endif + + +#ifdef FAST_OBJ_IMPLEMENTATION + +#include +#include + +#ifndef FAST_OBJ_REALLOC +#define FAST_OBJ_REALLOC realloc +#endif + +#ifndef FAST_OBJ_FREE +#define FAST_OBJ_FREE free +#endif + +#ifdef _WIN32 +#define FAST_OBJ_SEPARATOR '\\' +#define FAST_OBJ_OTHER_SEP '/' +#else +#define FAST_OBJ_SEPARATOR '/' +#define FAST_OBJ_OTHER_SEP '\\' +#endif + + +/* Size of buffer to read into */ +#define BUFFER_SIZE 65536 + +/* Max supported power when parsing float */ +#define MAX_POWER 20 + +typedef struct +{ + /* Final mesh */ + fastObjMesh* mesh; + + /* Current object/group */ + fastObjGroup object; + fastObjGroup group; + + /* Current material index */ + unsigned int material; + + /* Current line in file */ + unsigned int line; + + /* Base path for materials/textures */ + char* base; + +} fastObjData; + + +static const +double POWER_10_POS[MAX_POWER] = +{ + 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, + 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, +}; + +static const +double POWER_10_NEG[MAX_POWER] = +{ + 1.0e0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8, 1.0e-9, + 1.0e-10, 1.0e-11, 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16, 1.0e-17, 1.0e-18, 1.0e-19, +}; + + +static void* memory_realloc(void* ptr, size_t bytes) +{ + return FAST_OBJ_REALLOC(ptr, bytes); +} + + +static +void memory_dealloc(void* ptr) +{ + FAST_OBJ_FREE(ptr); +} + + +#define array_clean(_arr) ((_arr) ? memory_dealloc(_array_header(_arr)), 0 : 0) +#define array_push(_arr, _val) (_array_mgrow(_arr, 1) ? ((_arr)[_array_size(_arr)++] = (_val), _array_size(_arr) - 1) : 0) +#define array_size(_arr) ((_arr) ? _array_size(_arr) : 0) +#define array_capacity(_arr) ((_arr) ? _array_capacity(_arr) : 0) +#define array_empty(_arr) (array_size(_arr) == 0) + +#define _array_header(_arr) ((fastObjUInt*)(_arr)-2) +#define _array_size(_arr) (_array_header(_arr)[0]) +#define _array_capacity(_arr) (_array_header(_arr)[1]) +#define _array_ngrow(_arr, _n) ((_arr) == 0 || (_array_size(_arr) + (_n) >= _array_capacity(_arr))) +#define _array_mgrow(_arr, _n) (_array_ngrow(_arr, _n) ? (_array_grow(_arr, _n) != 0) : 1) +#define _array_grow(_arr, _n) (*((void**)&(_arr)) = array_realloc(_arr, _n, sizeof(*(_arr)))) + + +static void* array_realloc(void* ptr, fastObjUInt n, fastObjUInt b) +{ + fastObjUInt sz = array_size(ptr); + fastObjUInt nsz = sz + n; + fastObjUInt cap = array_capacity(ptr); + fastObjUInt ncap = cap + cap / 2; + fastObjUInt* r; + + if (ncap < nsz) + ncap = nsz; + ncap = (ncap + 15) & ~15u; + + r = (fastObjUInt*)(memory_realloc(ptr ? _array_header(ptr) : 0, (size_t)b * ncap + 2 * sizeof(fastObjUInt))); + if (!r) + return 0; + + r[0] = sz; + r[1] = ncap; + + return (r + 2); +} + + +static +void* file_open(const char* path, void* user_data) +{ + (void)(user_data); + return fopen(path, "rb"); +} + + +static +void file_close(void* file, void* user_data) +{ + FILE* f; + (void)(user_data); + + f = (FILE*)(file); + fclose(f); +} + + +static +size_t file_read(void* file, void* dst, size_t bytes, void* user_data) +{ + FILE* f; + (void)(user_data); + + f = (FILE*)(file); + return fread(dst, 1, bytes, f); +} + + +static +unsigned long file_size(void* file, void* user_data) +{ + FILE* f; + long p; + long n; + (void)(user_data); + + f = (FILE*)(file); + + p = ftell(f); + fseek(f, 0, SEEK_END); + n = ftell(f); + fseek(f, p, SEEK_SET); + + if (n > 0) + return (unsigned long)(n); + else + return 0; +} + + +static +char* string_copy(const char* s, const char* e) +{ + size_t n; + char* p; + + n = (size_t)(e - s); + p = (char*)(memory_realloc(0, n + 1)); + if (p) + { + memcpy(p, s, n); + p[n] = '\0'; + } + + return p; +} + + +static +char* string_substr(const char* s, size_t a, size_t b) +{ + return string_copy(s + a, s + b); +} + + +static +char* string_concat(const char* a, const char* s, const char* e) +{ + size_t an; + size_t sn; + char* p; + + an = a ? strlen(a) : 0; + sn = (size_t)(e - s); + p = (char*)(memory_realloc(0, an + sn + 1)); + if (p) + { + if (a) + memcpy(p, a, an); + memcpy(p + an, s, sn); + p[an + sn] = '\0'; + } + + return p; +} + + +static +int string_equal(const char* a, const char* s, const char* e) +{ + size_t an = strlen(a); + size_t sn = (size_t)(e - s); + + return an == sn && memcmp(a, s, an) == 0; +} + + +static +void string_fix_separators(char* s) +{ + while (*s) + { + if (*s == FAST_OBJ_OTHER_SEP) + *s = FAST_OBJ_SEPARATOR; + s++; + } +} + + +static +int is_whitespace(char c) +{ + return (c == ' ' || c == '\t' || c == '\r'); +} + +static +int is_end_of_name(char c) +{ + return (c == '\t' || c == '\r' || c == '\n'); +} + +static +int is_newline(char c) +{ + return (c == '\n'); +} + + +static +int is_digit(char c) +{ + return (c >= '0' && c <= '9'); +} + + +static +int is_exponent(char c) +{ + return (c == 'e' || c == 'E'); +} + + +static +const char* skip_whitespace(const char* ptr) +{ + while (is_whitespace(*ptr)) + ptr++; + + return ptr; +} + + +static +const char* skip_line(const char* ptr) +{ + while (!is_newline(*ptr++)) + ; + + return ptr; +} + + +static +fastObjGroup object_default(void) +{ + fastObjGroup object; + + object.name = 0; + object.face_count = 0; + object.face_offset = 0; + object.index_offset = 0; + + return object; +} + + +static +void object_clean(fastObjGroup* object) +{ + memory_dealloc(object->name); +} + + +static +void flush_object(fastObjData* data) +{ + /* Add object if not empty */ + if (data->object.face_count > 0) + array_push(data->mesh->objects, data->object); + else + object_clean(&data->object); + + /* Reset for more data */ + data->object = object_default(); + data->object.face_offset = array_size(data->mesh->face_vertices); + data->object.index_offset = array_size(data->mesh->indices); +} + + + +static +fastObjGroup group_default(void) +{ + fastObjGroup group; + + group.name = 0; + group.face_count = 0; + group.face_offset = 0; + group.index_offset = 0; + + return group; +} + + +static +void group_clean(fastObjGroup* group) +{ + memory_dealloc(group->name); +} + + +static +void flush_group(fastObjData* data) +{ + /* Add group if not empty */ + if (data->group.face_count > 0) + array_push(data->mesh->groups, data->group); + else + group_clean(&data->group); + + /* Reset for more data */ + data->group = group_default(); + data->group.face_offset = array_size(data->mesh->face_vertices); + data->group.index_offset = array_size(data->mesh->indices); +} + + +static +const char* parse_int(const char* ptr, int* val) +{ + int sign; + int num; + + + if (*ptr == '-') + { + sign = -1; + ptr++; + } + else + { + sign = +1; + } + + num = 0; + while (is_digit(*ptr)) + num = 10 * num + (*ptr++ - '0'); + + *val = sign * num; + + return ptr; +} + + +static +const char* parse_float(const char* ptr, float* val) +{ + double sign; + double num; + double fra; + double div; + unsigned int eval; + const double* powers; + + + ptr = skip_whitespace(ptr); + + switch (*ptr) + { + case '+': + sign = 1.0; + ptr++; + break; + + case '-': + sign = -1.0; + ptr++; + break; + + default: + sign = 1.0; + break; + } + + + num = 0.0; + while (is_digit(*ptr)) + num = 10.0 * num + (double)(*ptr++ - '0'); + + if (*ptr == '.') + ptr++; + + fra = 0.0; + div = 1.0; + + while (is_digit(*ptr)) + { + fra = 10.0 * fra + (double)(*ptr++ - '0'); + div *= 10.0; + } + + num += fra / div; + + if (is_exponent(*ptr)) + { + ptr++; + + switch (*ptr) + { + case '+': + powers = POWER_10_POS; + ptr++; + break; + + case '-': + powers = POWER_10_NEG; + ptr++; + break; + + default: + powers = POWER_10_POS; + break; + } + + eval = 0; + while (is_digit(*ptr)) + eval = 10 * eval + (*ptr++ - '0'); + + num *= (eval >= MAX_POWER) ? 0.0 : powers[eval]; + } + + *val = (float)(sign * num); + + return ptr; +} + + +static +const char* parse_vertex(fastObjData* data, const char* ptr) +{ + unsigned int ii; + float v; + + + for (ii = 0; ii < 3; ii++) + { + ptr = parse_float(ptr, &v); + array_push(data->mesh->positions, v); + } + + return ptr; +} + + +static +const char* parse_texcoord(fastObjData* data, const char* ptr) +{ + unsigned int ii; + float v; + + + for (ii = 0; ii < 2; ii++) + { + ptr = parse_float(ptr, &v); + array_push(data->mesh->texcoords, v); + } + + return ptr; +} + + +static +const char* parse_normal(fastObjData* data, const char* ptr) +{ + unsigned int ii; + float v; + + + for (ii = 0; ii < 3; ii++) + { + ptr = parse_float(ptr, &v); + array_push(data->mesh->normals, v); + } + + return ptr; +} + + +static +const char* parse_face(fastObjData* data, const char* ptr) +{ + unsigned int count; + fastObjIndex vn; + int v; + int t; + int n; + + + ptr = skip_whitespace(ptr); + + count = 0; + while (!is_newline(*ptr)) + { + v = 0; + t = 0; + n = 0; + + ptr = parse_int(ptr, &v); + if (*ptr == '/') + { + ptr++; + if (*ptr != '/') + ptr = parse_int(ptr, &t); + + if (*ptr == '/') + { + ptr++; + ptr = parse_int(ptr, &n); + } + } + + if (v < 0) + vn.p = (array_size(data->mesh->positions) / 3) - (fastObjUInt)(-v); + else + vn.p = (fastObjUInt)(v); + + if (t < 0) + vn.t = (array_size(data->mesh->texcoords) / 2) - (fastObjUInt)(-t); + else if (t > 0) + vn.t = (fastObjUInt)(t); + else + vn.t = 0; + + if (n < 0) + vn.n = (array_size(data->mesh->normals) / 3) - (fastObjUInt)(-n); + else if (n > 0) + vn.n = (fastObjUInt)(n); + else + vn.n = 0; + + array_push(data->mesh->indices, vn); + count++; + + ptr = skip_whitespace(ptr); + } + + array_push(data->mesh->face_vertices, count); + array_push(data->mesh->face_materials, data->material); + + data->group.face_count++; + data->object.face_count++; + + return ptr; +} + + +static +const char* parse_object(fastObjData* data, const char* ptr) +{ + const char* s; + const char* e; + + + ptr = skip_whitespace(ptr); + + s = ptr; + while (!is_end_of_name(*ptr)) + ptr++; + + e = ptr; + + flush_object(data); + data->object.name = string_copy(s, e); + + return ptr; +} + + +static +const char* parse_group(fastObjData* data, const char* ptr) +{ + const char* s; + const char* e; + + + ptr = skip_whitespace(ptr); + + s = ptr; + while (!is_end_of_name(*ptr)) + ptr++; + + e = ptr; + + flush_group(data); + data->group.name = string_copy(s, e); + + return ptr; +} + + +static +fastObjTexture map_default(void) +{ + fastObjTexture map; + + map.name = 0; + map.path = 0; + + return map; +} + + +static +fastObjMaterial mtl_default(void) +{ + fastObjMaterial mtl; + + mtl.name = 0; + + mtl.Ka[0] = 0.0; + mtl.Ka[1] = 0.0; + mtl.Ka[2] = 0.0; + mtl.Kd[0] = 1.0; + mtl.Kd[1] = 1.0; + mtl.Kd[2] = 1.0; + mtl.Ks[0] = 0.0; + mtl.Ks[1] = 0.0; + mtl.Ks[2] = 0.0; + mtl.Ke[0] = 0.0; + mtl.Ke[1] = 0.0; + mtl.Ke[2] = 0.0; + mtl.Kt[0] = 0.0; + mtl.Kt[1] = 0.0; + mtl.Kt[2] = 0.0; + mtl.Ns = 1.0; + mtl.Ni = 1.0; + mtl.Tf[0] = 1.0; + mtl.Tf[1] = 1.0; + mtl.Tf[2] = 1.0; + mtl.d = 1.0; + mtl.illum = 1; + + mtl.map_Ka = map_default(); + mtl.map_Kd = map_default(); + mtl.map_Ks = map_default(); + mtl.map_Ke = map_default(); + mtl.map_Kt = map_default(); + mtl.map_Ns = map_default(); + mtl.map_Ni = map_default(); + mtl.map_d = map_default(); + mtl.map_bump = map_default(); + + return mtl; +} + + +static +const char* parse_usemtl(fastObjData* data, const char* ptr) +{ + const char* s; + const char* e; + unsigned int idx; + fastObjMaterial* mtl; + + + ptr = skip_whitespace(ptr); + + /* Parse the material name */ + s = ptr; + while (!is_end_of_name(*ptr)) + ptr++; + + e = ptr; + + /* Find an existing material with the same name */ + idx = 0; + while (idx < array_size(data->mesh->materials)) + { + mtl = &data->mesh->materials[idx]; + if (mtl->name && string_equal(mtl->name, s, e)) + break; + + idx++; + } + + /* If doesn't exists, create a default one with this name + Note: this case happens when OBJ doesn't have its MTL */ + if (idx == array_size(data->mesh->materials)) + { + fastObjMaterial new_mtl = mtl_default(); + new_mtl.name = string_copy(s, e); + array_push(data->mesh->materials, new_mtl); + } + + data->material = idx; + + return ptr; +} + + +static +void map_clean(fastObjTexture* map) +{ + memory_dealloc(map->name); + memory_dealloc(map->path); +} + + +static +void mtl_clean(fastObjMaterial* mtl) +{ + map_clean(&mtl->map_Ka); + map_clean(&mtl->map_Kd); + map_clean(&mtl->map_Ks); + map_clean(&mtl->map_Ke); + map_clean(&mtl->map_Kt); + map_clean(&mtl->map_Ns); + map_clean(&mtl->map_Ni); + map_clean(&mtl->map_d); + map_clean(&mtl->map_bump); + + memory_dealloc(mtl->name); +} + + +static +const char* read_mtl_int(const char* p, int* v) +{ + return parse_int(p, v); +} + + +static +const char* read_mtl_single(const char* p, float* v) +{ + return parse_float(p, v); +} + + +static +const char* read_mtl_triple(const char* p, float v[3]) +{ + p = read_mtl_single(p, &v[0]); + p = read_mtl_single(p, &v[1]); + p = read_mtl_single(p, &v[2]); + + return p; +} + + +static +const char* read_map(fastObjData* data, const char* ptr, fastObjTexture* map) +{ + const char* s; + const char* e; + char* name; + char* path; + + ptr = skip_whitespace(ptr); + + /* Don't support options at present */ + if (*ptr == '-') + return ptr; + + + /* Read name */ + s = ptr; + while (!is_end_of_name(*ptr)) + ptr++; + + e = ptr; + + name = string_copy(s, e); + + path = string_concat(data->base, s, e); + string_fix_separators(path); + + map->name = name; + map->path = path; + + return e; +} + + +static +int read_mtllib(fastObjData* data, void* file, const fastObjCallbacks* callbacks, void* user_data) +{ + unsigned long n; + const char* s; + char* contents; + size_t l; + const char* p; + const char* e; + int found_d; + fastObjMaterial mtl; + + + /* Read entire file */ + n = callbacks->file_size(file, user_data); + + contents = (char*)(memory_realloc(0, n + 1)); + if (!contents) + return 0; + + l = callbacks->file_read(file, contents, n, user_data); + contents[l] = '\n'; + + mtl = mtl_default(); + + found_d = 0; + + p = contents; + e = contents + l; + while (p < e) + { + p = skip_whitespace(p); + + switch (*p) + { + case 'n': + p++; + if (p[0] == 'e' && + p[1] == 'w' && + p[2] == 'm' && + p[3] == 't' && + p[4] == 'l' && + is_whitespace(p[5])) + { + /* Push previous material (if there is one) */ + if (mtl.name) + { + array_push(data->mesh->materials, mtl); + mtl = mtl_default(); + } + + + /* Read name */ + p += 5; + + while (is_whitespace(*p)) + p++; + + s = p; + while (!is_end_of_name(*p)) + p++; + + mtl.name = string_copy(s, p); + } + break; + + case 'K': + if (p[1] == 'a') + p = read_mtl_triple(p + 2, mtl.Ka); + else if (p[1] == 'd') + p = read_mtl_triple(p + 2, mtl.Kd); + else if (p[1] == 's') + p = read_mtl_triple(p + 2, mtl.Ks); + else if (p[1] == 'e') + p = read_mtl_triple(p + 2, mtl.Ke); + else if (p[1] == 't') + p = read_mtl_triple(p + 2, mtl.Kt); + break; + + case 'N': + if (p[1] == 's') + p = read_mtl_single(p + 2, &mtl.Ns); + else if (p[1] == 'i') + p = read_mtl_single(p + 2, &mtl.Ni); + break; + + case 'T': + if (p[1] == 'r') + { + float Tr; + p = read_mtl_single(p + 2, &Tr); + if (!found_d) + { + /* Ignore Tr if we've already read d */ + mtl.d = 1.0f - Tr; + } + } + else if (p[1] == 'f') + p = read_mtl_triple(p + 2, mtl.Tf); + break; + + case 'd': + if (is_whitespace(p[1])) + { + p = read_mtl_single(p + 1, &mtl.d); + found_d = 1; + } + break; + + case 'i': + p++; + if (p[0] == 'l' && + p[1] == 'l' && + p[2] == 'u' && + p[3] == 'm' && + is_whitespace(p[4])) + { + p = read_mtl_int(p + 4, &mtl.illum); + } + break; + + case 'm': + p++; + if (p[0] == 'a' && + p[1] == 'p' && + p[2] == '_') + { + p += 3; + if (*p == 'K') + { + p++; + if (is_whitespace(p[1])) + { + if (*p == 'a') + p = read_map(data, p + 1, &mtl.map_Ka); + else if (*p == 'd') + p = read_map(data, p + 1, &mtl.map_Kd); + else if (*p == 's') + p = read_map(data, p + 1, &mtl.map_Ks); + else if (*p == 'e') + p = read_map(data, p + 1, &mtl.map_Ke); + else if (*p == 't') + p = read_map(data, p + 1, &mtl.map_Kt); + } + } + else if (*p == 'N') + { + p++; + if (is_whitespace(p[1])) + { + if (*p == 's') + p = read_map(data, p + 1, &mtl.map_Ns); + else if (*p == 'i') + p = read_map(data, p + 1, &mtl.map_Ni); + } + } + else if (*p == 'd') + { + p++; + if (is_whitespace(*p)) + p = read_map(data, p, &mtl.map_d); + } + else if ((p[0] == 'b' || p[0] == 'B') && + p[1] == 'u' && + p[2] == 'm' && + p[3] == 'p' && + is_whitespace(p[4])) + { + p = read_map(data, p + 4, &mtl.map_bump); + } + } + break; + + case '#': + break; + } + + p = skip_line(p); + } + + /* Push final material */ + if (mtl.name) + array_push(data->mesh->materials, mtl); + + memory_dealloc(contents); + + return 1; +} + + +static +const char* parse_mtllib(fastObjData* data, const char* ptr, const fastObjCallbacks* callbacks, void* user_data) +{ + const char* s; + const char* e; + char* lib; + void* file; + + + ptr = skip_whitespace(ptr); + + s = ptr; + while (!is_end_of_name(*ptr)) + ptr++; + + e = ptr; + + lib = string_concat(data->base, s, e); + if (lib) + { + string_fix_separators(lib); + + file = callbacks->file_open(lib, user_data); + if (file) + { + read_mtllib(data, file, callbacks, user_data); + callbacks->file_close(file, user_data); + } + + memory_dealloc(lib); + } + + return ptr; +} + + +static +void parse_buffer(fastObjData* data, const char* ptr, const char* end, const fastObjCallbacks* callbacks, void* user_data) +{ + const char* p; + + + p = ptr; + while (p != end) + { + p = skip_whitespace(p); + + switch (*p) + { + case 'v': + p++; + + switch (*p++) + { + case ' ': + case '\t': + p = parse_vertex(data, p); + break; + + case 't': + p = parse_texcoord(data, p); + break; + + case 'n': + p = parse_normal(data, p); + break; + + default: + p--; /* roll p++ back in case *p was a newline */ + } + break; + + case 'f': + p++; + + switch (*p++) + { + case ' ': + case '\t': + p = parse_face(data, p); + break; + + default: + p--; /* roll p++ back in case *p was a newline */ + } + break; + + case 'o': + p++; + + switch (*p++) + { + case ' ': + case '\t': + p = parse_object(data, p); + break; + + default: + p--; /* roll p++ back in case *p was a newline */ + } + break; + + case 'g': + p++; + + switch (*p++) + { + case ' ': + case '\t': + p = parse_group(data, p); + break; + + default: + p--; /* roll p++ back in case *p was a newline */ + } + break; + + case 'm': + p++; + if (p[0] == 't' && + p[1] == 'l' && + p[2] == 'l' && + p[3] == 'i' && + p[4] == 'b' && + is_whitespace(p[5])) + p = parse_mtllib(data, p + 5, callbacks, user_data); + break; + + case 'u': + p++; + if (p[0] == 's' && + p[1] == 'e' && + p[2] == 'm' && + p[3] == 't' && + p[4] == 'l' && + is_whitespace(p[5])) + p = parse_usemtl(data, p + 5); + break; + + case '#': + break; + } + + p = skip_line(p); + + data->line++; + } +} + + +void fast_obj_destroy(fastObjMesh* m) +{ + unsigned int ii; + + + for (ii = 0; ii < array_size(m->objects); ii++) + object_clean(&m->objects[ii]); + + for (ii = 0; ii < array_size(m->groups); ii++) + group_clean(&m->groups[ii]); + + for (ii = 0; ii < array_size(m->materials); ii++) + mtl_clean(&m->materials[ii]); + + array_clean(m->positions); + array_clean(m->texcoords); + array_clean(m->normals); + array_clean(m->face_vertices); + array_clean(m->face_materials); + array_clean(m->indices); + array_clean(m->objects); + array_clean(m->groups); + array_clean(m->materials); + + memory_dealloc(m); +} + + +fastObjMesh* fast_obj_read(const char* path) +{ + fastObjCallbacks callbacks; + callbacks.file_open = file_open; + callbacks.file_close = file_close; + callbacks.file_read = file_read; + callbacks.file_size = file_size; + + return fast_obj_read_with_callbacks(path, &callbacks, 0); +} + + +fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbacks* callbacks, void* user_data) +{ + fastObjData data; + fastObjMesh* m; + void* file; + char* buffer; + char* start; + char* end; + char* last; + fastObjUInt read; + fastObjUInt bytes; + + /* Check if callbacks are valid */ + if(!callbacks) + return 0; + + + /* Open file */ + file = callbacks->file_open(path, user_data); + if (!file) + return 0; + + + /* Empty mesh */ + m = (fastObjMesh*)(memory_realloc(0, sizeof(fastObjMesh))); + if (!m) + return 0; + + m->positions = 0; + m->texcoords = 0; + m->normals = 0; + m->face_vertices = 0; + m->face_materials = 0; + m->indices = 0; + m->materials = 0; + m->objects = 0; + m->groups = 0; + + + /* Add dummy position/texcoord/normal */ + array_push(m->positions, 0.0f); + array_push(m->positions, 0.0f); + array_push(m->positions, 0.0f); + + array_push(m->texcoords, 0.0f); + array_push(m->texcoords, 0.0f); + + array_push(m->normals, 0.0f); + array_push(m->normals, 0.0f); + array_push(m->normals, 1.0f); + + + /* Data needed during parsing */ + data.mesh = m; + data.object = object_default(); + data.group = group_default(); + data.material = 0; + data.line = 1; + data.base = 0; + + + /* Find base path for materials/textures */ + { + const char* sep1 = strrchr(path, FAST_OBJ_SEPARATOR); + const char* sep2 = strrchr(path, FAST_OBJ_OTHER_SEP); + + /* Use the last separator in the path */ + const char* sep = sep2 && (!sep1 || sep1 < sep2) ? sep2 : sep1; + + if (sep) + data.base = string_substr(path, 0, sep - path + 1); + } + + + /* Create buffer for reading file */ + buffer = (char*)(memory_realloc(0, 2 * BUFFER_SIZE * sizeof(char))); + if (!buffer) + return 0; + + start = buffer; + for (;;) + { + /* Read another buffer's worth from file */ + read = (fastObjUInt)(callbacks->file_read(file, start, BUFFER_SIZE, user_data)); + if (read == 0 && start == buffer) + break; + + + /* Ensure buffer ends in a newline */ + if (read < BUFFER_SIZE) + { + if (read == 0 || start[read - 1] != '\n') + start[read++] = '\n'; + } + + end = start + read; + if (end == buffer) + break; + + + /* Find last new line */ + last = end; + while (last > buffer) + { + last--; + if (*last == '\n') + break; + } + + + /* Check there actually is a new line */ + if (*last != '\n') + break; + + last++; + + + /* Process buffer */ + parse_buffer(&data, buffer, last, callbacks, user_data); + + + /* Copy overflow for next buffer */ + bytes = (fastObjUInt)(end - last); + memmove(buffer, last, bytes); + start = buffer + bytes; + } + + + /* Flush final object/group */ + flush_object(&data); + object_clean(&data.object); + + flush_group(&data); + group_clean(&data.group); + + m->position_count = array_size(m->positions) / 3; + m->texcoord_count = array_size(m->texcoords) / 2; + m->normal_count = array_size(m->normals) / 3; + m->face_count = array_size(m->face_vertices); + m->index_count = array_size(m->indices); + m->material_count = array_size(m->materials); + m->object_count = array_size(m->objects); + m->group_count = array_size(m->groups); + + + /* Clean up */ + memory_dealloc(buffer); + memory_dealloc(data.base); + + callbacks->file_close(file, user_data); + + return m; +} + +#endif diff --git a/src/fast_obj/test/test.cpp b/src/fast_obj/test/test.cpp new file mode 100644 index 0000000..05b427f --- /dev/null +++ b/src/fast_obj/test/test.cpp @@ -0,0 +1,181 @@ +/* + * + * MIT License + * + * Copyright (c) 2018 Richard Knight + * + * 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 FAST_OBJ_IMPLEMENTATION +#include "fast_obj.h" + +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" + +#include + +using namespace tinyobj; + + +struct tinyObj +{ + attrib_t attrib; + std::vector shapes; + std::vector materials; +}; + + + +static +bool read_tiny_obj(const char* path, tinyObj* o) +{ + std::string err; + std::string warn; + return LoadObj(&o->attrib, &o->shapes, &o->materials, &warn, &err, path, 0, false); +} + + +static +void check(bool c, const char* m) +{ + if (!c) + printf("CHECK FAILED : %s\n", m); +} + +#define CHECK(_c) check(_c, #_c) + + +static +void compare_mesh(fastObjMesh* m, tinyObj* o) +{ + CHECK(m->group_count == o->shapes.size()); + + for (unsigned int ii = 0; ii < m->group_count; ii++) + { + const fastObjGroup& grp = m->groups[ii]; + const shape_t& shp = o->shapes[ii]; + + std::string grp_name; + if (grp.name) + grp_name = std::string(grp.name); + + CHECK(shp.name == grp_name); + CHECK(shp.mesh.num_face_vertices.size() == grp.face_count); + + int idx = 0; + for (unsigned int jj = 0; jj < grp.face_count; jj++) + { + unsigned int fv = m->face_vertices[grp.face_offset + jj]; + + CHECK(shp.mesh.num_face_vertices[jj] == fv); + + for (unsigned int kk = 0; kk < fv; kk++) + { + index_t oi = shp.mesh.indices[idx]; + fastObjIndex mi = m->indices[grp.index_offset + idx]; + + CHECK(oi.vertex_index + 1 == mi.p); + CHECK(oi.texcoord_index + 1 == mi.t); + CHECK(oi.normal_index + 1 == mi.n); + + if (mi.p) + { + CHECK(o->attrib.vertices[3 * oi.vertex_index + 0] == m->positions[3 * mi.p + 0]); + CHECK(o->attrib.vertices[3 * oi.vertex_index + 1] == m->positions[3 * mi.p + 1]); + CHECK(o->attrib.vertices[3 * oi.vertex_index + 2] == m->positions[3 * mi.p + 2]); + } + + if (mi.t) + { + CHECK(o->attrib.texcoords[2 * oi.texcoord_index + 0] == m->texcoords[2 * mi.t + 0]); + CHECK(o->attrib.texcoords[2 * oi.texcoord_index + 1] == m->texcoords[2 * mi.t + 1]); + } + + if (mi.n) + { + CHECK(o->attrib.normals[3 * oi.normal_index + 0] == m->normals[3 * mi.n + 0]); + CHECK(o->attrib.normals[3 * oi.normal_index + 1] == m->normals[3 * mi.n + 1]); + CHECK(o->attrib.normals[3 * oi.normal_index + 2] == m->normals[3 * mi.n + 2]); + } + + idx++; + } + } + } +} + + +int main(int argc, const char* argv[]) +{ + if (argc != 2) + { + printf("%s \n", argv[0]); + return -1; + } + + printf("Reading with fast_obj\n"); + + auto fast_start = std::chrono::high_resolution_clock::now(); + + fastObjMesh* m = fast_obj_read(argv[1]); + + auto fast_end = std::chrono::high_resolution_clock::now(); + std::chrono::duration fast_time = fast_end - fast_start; + + if (!m) + { + printf("Failed!\n"); + return -1; + } + + printf("Took %0.2f secs\n", fast_time.count()); + + + printf("Reading with tiny_obj_loader\n"); + tinyObj o; + + auto tiny_start = std::chrono::high_resolution_clock::now(); + + bool success = read_tiny_obj(argv[1], &o); + + auto tiny_end = std::chrono::high_resolution_clock::now(); + std::chrono::duration tiny_time = tiny_end - tiny_start; + + if (!success) + { + printf("Failed!\n"); + return -1; + } + + printf("Took %0.2f secs\n", tiny_time.count()); + + printf("Comparing...\n"); + compare_mesh(m, &o); + + printf("Done\n"); + + fast_obj_destroy(m); + + return 0; +} + + + diff --git a/src/fast_obj/test/tiny_obj_loader.h b/src/fast_obj/test/tiny_obj_loader.h new file mode 100644 index 0000000..54ebe09 --- /dev/null +++ b/src/fast_obj/test/tiny_obj_loader.h @@ -0,0 +1,2926 @@ +/* +The MIT License (MIT) + +Copyright (c) 2012-2018 Syoyo Fujita and many contributors. + +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. +*/ + +// +// version 2.0.0 : Add new object oriented API. 1.x API is still provided. +// * Support line primitive. +// * Support points primitive. +// version 1.4.0 : Modifed ParseTextureNameAndOption API +// version 1.3.1 : Make ParseTextureNameAndOption API public +// version 1.3.0 : Separate warning and error message(breaking API of LoadObj) +// version 1.2.3 : Added color space extension('-colorspace') to tex opts. +// version 1.2.2 : Parse multiple group names. +// version 1.2.1 : Added initial support for line('l') primitive(PR #178) +// version 1.2.0 : Hardened implementation(#175) +// version 1.1.1 : Support smoothing groups(#162) +// version 1.1.0 : Support parsing vertex color(#144) +// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) +// version 1.0.7 : Support multiple tex options(#126) +// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) +// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) +// version 1.0.4 : Support multiple filenames for 'mtllib'(#112) +// version 1.0.3 : Support parsing texture options(#85) +// version 1.0.2 : Improve parsing speed by about a factor of 2 for large +// files(#105) +// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) +// version 1.0.0 : Change data structure. Change license from BSD to MIT. +// + +// +// Use this in *one* .cc +// #define TINYOBJLOADER_IMPLEMENTATION +// #include "tiny_obj_loader.h" +// + +#ifndef TINY_OBJ_LOADER_H_ +#define TINY_OBJ_LOADER_H_ + +#include +#include +#include + +namespace tinyobj { + +#ifdef __clang__ +#pragma clang diagnostic push +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + +#pragma clang diagnostic ignored "-Wpadded" + +#endif + +// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... +// +// -blendu on | off # set horizontal texture blending +// (default on) +// -blendv on | off # set vertical texture blending +// (default on) +// -boost real_value # boost mip-map sharpness +// -mm base_value gain_value # modify texture map values (default +// 0 1) +// # base_value = brightness, +// gain_value = contrast +// -o u [v [w]] # Origin offset (default +// 0 0 0) +// -s u [v [w]] # Scale (default +// 1 1 1) +// -t u [v [w]] # Turbulence (default +// 0 0 0) +// -texres resolution # texture resolution to create +// -clamp on | off # only render texels in the clamped +// 0-1 range (default off) +// # When unclamped, textures are +// repeated across a surface, +// # when clamped, only texels which +// fall within the 0-1 +// # range are rendered. +// -bm mult_value # bump multiplier (for bump maps +// only) +// +// -imfchan r | g | b | m | l | z # specifies which channel of the file +// is used to +// # create a scalar or bump texture. +// r:red, g:green, +// # b:blue, m:matte, l:luminance, +// z:z-depth.. +// # (the default for bump is 'l' and +// for decal is 'm') +// bump -imfchan r bumpmap.tga # says to use the red channel of +// bumpmap.tga as the bumpmap +// +// For reflection maps... +// +// -type sphere # specifies a sphere for a "refl" +// reflection map +// -type cube_top | cube_bottom | # when using a cube map, the texture +// file for each +// cube_front | cube_back | # side of the cube is specified +// separately +// cube_left | cube_right +// +// TinyObjLoader extension. +// +// -colorspace SPACE # Color space of the texture. e.g. +// 'sRGB` or 'linear' +// + +#ifdef TINYOBJLOADER_USE_DOUBLE +//#pragma message "using double" +typedef double real_t; +#else +//#pragma message "using float" +typedef float real_t; +#endif + +typedef enum { + TEXTURE_TYPE_NONE, // default + TEXTURE_TYPE_SPHERE, + TEXTURE_TYPE_CUBE_TOP, + TEXTURE_TYPE_CUBE_BOTTOM, + TEXTURE_TYPE_CUBE_FRONT, + TEXTURE_TYPE_CUBE_BACK, + TEXTURE_TYPE_CUBE_LEFT, + TEXTURE_TYPE_CUBE_RIGHT +} texture_type_t; + +typedef struct { + texture_type_t type; // -type (default TEXTURE_TYPE_NONE) + real_t sharpness; // -boost (default 1.0?) + real_t brightness; // base_value in -mm option (default 0) + real_t contrast; // gain_value in -mm option (default 1) + real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) + real_t scale[3]; // -s u [v [w]] (default 1 1 1) + real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) + // int texture_resolution; // -texres resolution (default = ?) TODO + bool clamp; // -clamp (default false) + char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') + bool blendu; // -blendu (default on) + bool blendv; // -blendv (default on) + real_t bump_multiplier; // -bm (for bump maps only, default 1.0) + + // extension + std::string colorspace; // Explicitly specify color space of stored texel + // value. Usually `sRGB` or `linear` (default empty). +} texture_option_t; + +typedef struct { + std::string name; + + real_t ambient[3]; + real_t diffuse[3]; + real_t specular[3]; + real_t transmittance[3]; + real_t emission[3]; + real_t shininess; + real_t ior; // index of refraction + real_t dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + int dummy; // Suppress padding warning. + + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, map_Bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d + std::string reflection_texname; // refl + + texture_option_t ambient_texopt; + texture_option_t diffuse_texopt; + texture_option_t specular_texopt; + texture_option_t specular_highlight_texopt; + texture_option_t bump_texopt; + texture_option_t displacement_texopt; + texture_option_t alpha_texopt; + texture_option_t reflection_texopt; + + // PBR extension + // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr + real_t roughness; // [0, 1] default 0 + real_t metallic; // [0, 1] default 0 + real_t sheen; // [0, 1] default 0 + real_t clearcoat_thickness; // [0, 1] default 0 + real_t clearcoat_roughness; // [0, 1] default 0 + real_t anisotropy; // aniso. [0, 1] default 0 + real_t anisotropy_rotation; // anisor. [0, 1] default 0 + real_t pad0; + std::string roughness_texname; // map_Pr + std::string metallic_texname; // map_Pm + std::string sheen_texname; // map_Ps + std::string emissive_texname; // map_Ke + std::string normal_texname; // norm. For normal mapping. + + texture_option_t roughness_texopt; + texture_option_t metallic_texopt; + texture_option_t sheen_texopt; + texture_option_t emissive_texopt; + texture_option_t normal_texopt; + + int pad2; + + std::map unknown_parameter; + +#ifdef TINY_OBJ_LOADER_PYTHON_BINDING + // For pybind11 + std::array GetDiffuse() { + std::array values; + values[0] = double(diffuse[0]); + values[1] = double(diffuse[1]); + values[2] = double(diffuse[2]); + + return values; + } + + std::array GetSpecular() { + std::array values; + values[0] = double(specular[0]); + values[1] = double(specular[1]); + values[2] = double(specular[2]); + + return values; + } + + std::array GetTransmittance() { + std::array values; + values[0] = double(transmittance[0]); + values[1] = double(transmittance[1]); + values[2] = double(transmittance[2]); + + return values; + } + + std::array GetEmission() { + std::array values; + values[0] = double(emission[0]); + values[1] = double(emission[1]); + values[2] = double(emission[2]); + + return values; + } + + std::array GetAmbient() { + std::array values; + values[0] = double(ambient[0]); + values[1] = double(ambient[1]); + values[2] = double(ambient[2]); + + return values; + } + + void SetDiffuse(std::array &a) { + diffuse[0] = real_t(a[0]); + diffuse[1] = real_t(a[1]); + diffuse[2] = real_t(a[2]); + } + + void SetAmbient(std::array &a) { + ambient[0] = real_t(a[0]); + ambient[1] = real_t(a[1]); + ambient[2] = real_t(a[2]); + } + + void SetSpecular(std::array &a) { + specular[0] = real_t(a[0]); + specular[1] = real_t(a[1]); + specular[2] = real_t(a[2]); + } + + void SetTransmittance(std::array &a) { + transmittance[0] = real_t(a[0]); + transmittance[1] = real_t(a[1]); + transmittance[2] = real_t(a[2]); + } + + std::string GetCustomParameter(const std::string &key) { + std::map::const_iterator it = + unknown_parameter.find(key); + + if (it != unknown_parameter.end()) { + return it->second; + } + return std::string(); + } + +#endif + +} material_t; + +typedef struct { + std::string name; + + std::vector intValues; + std::vector floatValues; + std::vector stringValues; +} tag_t; + +// Index struct to support different indices for vtx/normal/texcoord. +// -1 means not used. +typedef struct { + int vertex_index; + int normal_index; + int texcoord_index; +} index_t; + +typedef struct { + std::vector indices; + std::vector num_face_vertices; // The number of vertices per + // face. 3 = polygon, 4 = quad, + // ... Up to 255. + std::vector material_ids; // per-face material ID + std::vector smoothing_group_ids; // per-face smoothing group + // ID(0 = off. positive value + // = group id) + std::vector tags; // SubD tag +} mesh_t; + +// typedef struct { +// std::vector indices; // pairs of indices for lines +//} path_t; + +typedef struct { + // Linear flattened indices. + std::vector indices; // indices for vertices(poly lines) + std::vector num_line_vertices; // The number of vertices per line. +} lines_t; + +typedef struct { + std::vector indices; // indices for points +} points_t; + +typedef struct { + std::string name; + mesh_t mesh; + lines_t lines; + points_t points; +} shape_t; + +// Vertex attributes +struct attrib_t { + std::vector vertices; // 'v'(xyz) + + // For backward compatibility, we store vertex weight in separate array. + std::vector vertex_weights; // 'v'(w) + std::vector normals; // 'vn' + std::vector texcoords; // 'vt'(uv) + + // For backward compatibility, we store texture coordinate 'w' in separate + // array. + std::vector texcoord_ws; // 'vt'(w) + std::vector colors; // extension: vertex colors + + attrib_t() {} + + // + // For pybind11 + // + const std::vector &GetVertices() const { return vertices; } + + const std::vector &GetVertexWeights() const { return vertex_weights; } +}; + +typedef struct callback_t_ { + // W is optional and set to 1 if there is no `w` item in `v` line + void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); + void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); + + // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in + // `vt` line. + void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); + + // called per 'f' line. num_indices is the number of face indices(e.g. 3 for + // triangle, 4 for quad) + // 0 will be passed for undefined index in index_t members. + void (*index_cb)(void *user_data, index_t *indices, int num_indices); + // `name` material name, `material_id` = the array index of material_t[]. -1 + // if + // a material not found in .mtl + void (*usemtl_cb)(void *user_data, const char *name, int material_id); + // `materials` = parsed material data. + void (*mtllib_cb)(void *user_data, const material_t *materials, + int num_materials); + // There may be multiple group names + void (*group_cb)(void *user_data, const char **names, int num_names); + void (*object_cb)(void *user_data, const char *name); + + callback_t_() + : vertex_cb(NULL), + normal_cb(NULL), + texcoord_cb(NULL), + index_cb(NULL), + usemtl_cb(NULL), + mtllib_cb(NULL), + group_cb(NULL), + object_cb(NULL) {} +} callback_t; + +class MaterialReader { + public: + MaterialReader() {} + virtual ~MaterialReader(); + + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err) = 0; +}; + +/// +/// Read .mtl from a file. +/// +class MaterialFileReader : public MaterialReader { + public: + explicit MaterialFileReader(const std::string &mtl_basedir) + : m_mtlBaseDir(mtl_basedir) {} + virtual ~MaterialFileReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err); + + private: + std::string m_mtlBaseDir; +}; + +/// +/// Read .mtl from a stream. +/// +class MaterialStreamReader : public MaterialReader { + public: + explicit MaterialStreamReader(std::istream &inStream) + : m_inStream(inStream) {} + virtual ~MaterialStreamReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err); + + private: + std::istream &m_inStream; +}; + +// v2 API +struct ObjReaderConfig { + bool triangulate; // triangulate polygon? + + /// Parse vertex color. + /// If vertex color is not present, its filled with default value. + /// false = no vertex color + /// This will increase memory of parsed .obj + bool vertex_color; + + /// + /// Search path to .mtl file. + /// Default = "" = search from the same directory of .obj file. + /// Valid only when loading .obj from a file. + /// + std::string mtl_search_path; + + ObjReaderConfig() : triangulate(true), vertex_color(true) {} +}; + +/// +/// Wavefront .obj reader class(v2 API) +/// +class ObjReader { + public: + ObjReader() : valid_(false) {} + ~ObjReader() {} + + /// + /// Load .obj and .mtl from a file. + /// + /// @param[in] filename wavefront .obj filename + /// @param[in] config Reader configuration + /// + bool ParseFromFile(const std::string &filename, + const ObjReaderConfig &config = ObjReaderConfig()); + + /// + /// Parse .obj from a text string. + /// Need to supply .mtl text string by `mtl_text`. + /// This function ignores `mtllib` line in .obj text. + /// + /// @param[in] obj_text wavefront .obj filename + /// @param[in] mtl_text wavefront .mtl filename + /// @param[in] config Reader configuration + /// + bool ParseFromString(const std::string &obj_text, const std::string &mtl_text, + const ObjReaderConfig &config = ObjReaderConfig()); + + /// + /// .obj was loaded or parsed correctly. + /// + bool Valid() const { return valid_; } + + const attrib_t &GetAttrib() const { return attrib_; } + + const std::vector &GetShapes() const { return shapes_; } + + const std::vector &GetMaterials() const { return materials_; } + + /// + /// Warning message(may be filled after `Load` or `Parse`) + /// + const std::string &Warning() const { return warning_; } + + /// + /// Error message(filled when `Load` or `Parse` failed) + /// + const std::string &Error() const { return error_; } + + private: + bool valid_; + + attrib_t attrib_; + std::vector shapes_; + std::vector materials_; + + std::string warning_; + std::string error_; +}; + +/// ==>>========= Legacy v1 API ============================================= + +/// Loads .obj from a file. +/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data +/// 'shapes' will be filled with parsed shape data +/// Returns true when loading .obj become success. +/// Returns warning message into `warn`, and error message into `err` +/// 'mtl_basedir' is optional, and used for base directory for .mtl file. +/// In default(`NULL'), .mtl file is searched from an application's working +/// directory. +/// 'triangulate' is optional, and used whether triangulate polygon face in .obj +/// or not. +/// Option 'default_vcols_fallback' specifies whether vertex colors should +/// always be defined, even if no colors are given (fallback to white). +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, const char *filename, + const char *mtl_basedir = NULL, bool triangulate = true, + bool default_vcols_fallback = true); + +/// Loads .obj from a file with custom user callback. +/// .mtl is loaded as usual and parsed material_t data will be passed to +/// `callback.mtllib_cb`. +/// Returns true when loading .obj/.mtl become success. +/// Returns warning message into `warn`, and error message into `err` +/// See `examples/callback_api/` for how to use this function. +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data = NULL, + MaterialReader *readMatFn = NULL, + std::string *warn = NULL, std::string *err = NULL); + +/// Loads object from a std::istream, uses `readMatFn` to retrieve +/// std::istream for materials. +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn = NULL, bool triangulate = true, + bool default_vcols_fallback = true); + +/// Loads materials into std::map +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream, + std::string *warning, std::string *err); + +/// +/// Parse texture name and texture option for custom texture parameter through +/// material::unknown_parameter +/// +/// @param[out] texname Parsed texture name +/// @param[out] texopt Parsed texopt +/// @param[in] linebuf Input string +/// +bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, + const char *linebuf); + +/// =<<========== Legacy v1 API ============================================= + +} // namespace tinyobj + +#endif // TINY_OBJ_LOADER_H_ + +#ifdef TINYOBJLOADER_IMPLEMENTATION +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace tinyobj { + +MaterialReader::~MaterialReader() {} + +struct vertex_index_t { + int v_idx, vt_idx, vn_idx; + vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} + explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} + vertex_index_t(int vidx, int vtidx, int vnidx) + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} +}; + +// Internal data structure for face representation +// index + smoothing group. +struct face_t { + unsigned int + smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off. + int pad_; + std::vector vertex_indices; // face vertex indices. + + face_t() : smoothing_group_id(0), pad_(0) {} +}; + +// Internal data structure for line representation +struct __line_t { + // l v1/vt1 v2/vt2 ... + // In the specification, line primitrive does not have normal index, but + // TinyObjLoader allow it + std::vector vertex_indices; +}; + +// Internal data structure for points representation +struct __points_t { + // p v1 v2 ... + // In the specification, point primitrive does not have normal index and + // texture coord index, but TinyObjLoader allow it. + std::vector vertex_indices; +}; + +struct tag_sizes { + tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} + int num_ints; + int num_reals; + int num_strings; +}; + +struct obj_shape { + std::vector v; + std::vector vn; + std::vector vt; +}; + +// +// Manages group of primitives(face, line, points, ...) +struct PrimGroup { + std::vector faceGroup; + std::vector<__line_t> lineGroup; + std::vector<__points_t> pointsGroup; + + void clear() { + faceGroup.clear(); + lineGroup.clear(); + pointsGroup.clear(); + } + + bool IsEmpty() const { + return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty(); + } + + // TODO(syoyo): bspline, surface, ... +}; + +// See +// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf +static std::istream &safeGetline(std::istream &is, std::string &t) { + t.clear(); + + // The characters in the stream are read one-by-one using a std::streambuf. + // That is faster than reading them one-by-one using the std::istream. + // Code that uses streambuf this way must be guarded by a sentry object. + // The sentry object performs various tasks, + // such as thread synchronization and updating the stream state. + + std::istream::sentry se(is, true); + std::streambuf *sb = is.rdbuf(); + + if (se) { + for (;;) { + int c = sb->sbumpc(); + switch (c) { + case '\n': + return is; + case '\r': + if (sb->sgetc() == '\n') sb->sbumpc(); + return is; + case EOF: + // Also handle the case when the last line has no line ending + if (t.empty()) is.setstate(std::ios::eofbit); + return is; + default: + t += static_cast(c); + } + } + } + + return is; +} + +#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) +#define IS_DIGIT(x) \ + (static_cast((x) - '0') < static_cast(10)) +#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) + +// Make index zero-base, and also support relative index. +static inline bool fixIndex(int idx, int n, int *ret) { + if (!ret) { + return false; + } + + if (idx > 0) { + (*ret) = idx - 1; + return true; + } + + if (idx == 0) { + // zero is not allowed according to the spec. + return false; + } + + if (idx < 0) { + (*ret) = n + idx; // negative value = relative + return true; + } + + return false; // never reach here. +} + +static inline std::string parseString(const char **token) { + std::string s; + (*token) += strspn((*token), " \t"); + size_t e = strcspn((*token), " \t\r"); + s = std::string((*token), &(*token)[e]); + (*token) += e; + return s; +} + +static inline int parseInt(const char **token) { + (*token) += strspn((*token), " \t"); + int i = atoi((*token)); + (*token) += strcspn((*token), " \t\r"); + return i; +} + +// Tries to parse a floating point number located at s. +// +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. +// +// The following situations triggers a failure: +// - s >= s_end. +// - parse failure. +// +static bool tryParseDouble(const char *s, const char *s_end, double *result) { + if (s >= s_end) { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + bool leading_decimal_dots = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + if ((curr != s_end) && (*curr == '.')) { + // accept. Somethig like `.7e+2`, `-.5234` + leading_decimal_dots = true; + } + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else if (*curr == '.') { + // accept. Somethig like `.7e+2`, `-.5234` + leading_decimal_dots = true; + } else { + goto fail; + } + + // Read the integer part. + end_not_reached = (curr != s_end); + if (!leading_decimal_dots) { + while (end_not_reached && IS_DIGIT(*curr)) { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + } + + // We must make sure we actually got something. + if (!leading_decimal_dots) { + if (read == 0) goto fail; + } + + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) goto assemble; + + // Read the decimal part. + if (*curr == '.') { + curr++; + read = 1; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + static const double pow_lut[] = { + 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, + }; + const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; + + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * + (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read)); + read++; + curr++; + end_not_reached = (curr != s_end); + } + } else if (*curr == 'e' || *curr == 'E') { + } else { + goto assemble; + } + + if (!end_not_reached) goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') { + curr++; + // Figure out if a sign is present and if it is. + end_not_reached = (curr != s_end); + if (end_not_reached && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + // Empty E is not allowed. + goto fail; + } + + read = 0; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + exponent *= (exp_sign == '+' ? 1 : -1); + if (read == 0) goto fail; + } + +assemble: + *result = (sign == '+' ? 1 : -1) * + (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) + : mantissa); + return true; +fail: + return false; +} + +static inline real_t parseReal(const char **token, double default_value = 0.0) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val = default_value; + tryParseDouble((*token), end, &val); + real_t f = static_cast(val); + (*token) = end; + return f; +} + +static inline bool parseReal(const char **token, real_t *out) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val; + bool ret = tryParseDouble((*token), end, &val); + if (ret) { + real_t f = static_cast(val); + (*out) = f; + } + (*token) = end; + return ret; +} + +static inline void parseReal2(real_t *x, real_t *y, const char **token, + const double default_x = 0.0, + const double default_y = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); +} + +static inline void parseReal3(real_t *x, real_t *y, real_t *z, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); +} + +static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0, + const double default_w = 1.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + (*w) = parseReal(token, default_w); +} + +// Extension: parse vertex with colors(6 items) +static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, + real_t *r, real_t *g, real_t *b, + const char **token, + const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + + const bool found_color = + parseReal(token, r) && parseReal(token, g) && parseReal(token, b); + + if (!found_color) { + (*r) = (*g) = (*b) = 1.0; + } + + return found_color; +} + +static inline bool parseOnOff(const char **token, bool default_value = true) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + + bool ret = default_value; + if ((0 == strncmp((*token), "on", 2))) { + ret = true; + } else if ((0 == strncmp((*token), "off", 3))) { + ret = false; + } + + (*token) = end; + return ret; +} + +static inline texture_type_t parseTextureType( + const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + texture_type_t ty = default_value; + + if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { + ty = TEXTURE_TYPE_CUBE_TOP; + } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { + ty = TEXTURE_TYPE_CUBE_BOTTOM; + } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { + ty = TEXTURE_TYPE_CUBE_LEFT; + } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { + ty = TEXTURE_TYPE_CUBE_RIGHT; + } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { + ty = TEXTURE_TYPE_CUBE_FRONT; + } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { + ty = TEXTURE_TYPE_CUBE_BACK; + } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { + ty = TEXTURE_TYPE_SPHERE; + } + + (*token) = end; + return ty; +} + +static tag_sizes parseTagTriple(const char **token) { + tag_sizes ts; + + (*token) += strspn((*token), " \t"); + ts.num_ints = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + + (*token)++; // Skip '/' + + (*token) += strspn((*token), " \t"); + ts.num_reals = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + (*token)++; // Skip '/' + + ts.num_strings = parseInt(token); + + return ts; +} + +// Parse triples with index offsets: i, i/j/k, i//k, i/j +static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize, + vertex_index_t *ret) { + if (!ret) { + return false; + } + + vertex_index_t vi(-1); + + if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) { + return false; + } + + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + (*ret) = vi; + return true; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { + return false; + } + (*token) += strcspn((*token), "/ \t\r"); + (*ret) = vi; + return true; + } + + // i/j/k or i/j + if (!fixIndex(atoi((*token)), vtsize, &(vi.vt_idx))) { + return false; + } + + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + (*ret) = vi; + return true; + } + + // i/j/k + (*token)++; // skip '/' + if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { + return false; + } + (*token) += strcspn((*token), "/ \t\r"); + + (*ret) = vi; + + return true; +} + +// Parse raw triples: i, i/j/k, i//k, i/j +static vertex_index_t parseRawTriple(const char **token) { + vertex_index_t vi(static_cast(0)); // 0 is an invalid index in OBJ + + vi.v_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + + // i/j/k + (*token)++; // skip '/' + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; +} + +bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, + const char *linebuf) { + // @todo { write more robust lexer and parser. } + bool found_texname = false; + std::string texture_name; + + const char *token = linebuf; // Assume line ends with NULL + + while (!IS_NEW_LINE((*token))) { + token += strspn(token, " \t"); // skip space + if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendu = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendv = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->clamp = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->sharpness = parseReal(&token, 1.0); + } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { + token += 4; + texopt->bump_multiplier = parseReal(&token, 1.0); + } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), + &(texopt->origin_offset[2]), &token); + } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), + &token, 1.0, 1.0, 1.0); + } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), + &(texopt->turbulence[2]), &token); + } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { + token += 5; + texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); + } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { + token += 9; + token += strspn(token, " \t"); + const char *end = token + strcspn(token, " \t\r"); + if ((end - token) == 1) { // Assume one char for -imfchan + texopt->imfchan = (*token); + } + token = end; + } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { + token += 4; + parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); + } else if ((0 == strncmp(token, "-colorspace", 11)) && + IS_SPACE((token[11]))) { + token += 12; + texopt->colorspace = parseString(&token); + } else { +// Assume texture filename +#if 0 + size_t len = strcspn(token, " \t\r"); // untile next space + texture_name = std::string(token, token + len); + token += len; + + token += strspn(token, " \t"); // skip space +#else + // Read filename until line end to parse filename containing whitespace + // TODO(syoyo): Support parsing texture option flag after the filename. + texture_name = std::string(token); + token += texture_name.length(); +#endif + + found_texname = true; + } + } + + if (found_texname) { + (*texname) = texture_name; + return true; + } else { + return false; + } +} + +static void InitTexOpt(texture_option_t *texopt, const bool is_bump) { + if (is_bump) { + texopt->imfchan = 'l'; + } else { + texopt->imfchan = 'm'; + } + texopt->bump_multiplier = static_cast(1.0); + texopt->clamp = false; + texopt->blendu = true; + texopt->blendv = true; + texopt->sharpness = static_cast(1.0); + texopt->brightness = static_cast(0.0); + texopt->contrast = static_cast(1.0); + texopt->origin_offset[0] = static_cast(0.0); + texopt->origin_offset[1] = static_cast(0.0); + texopt->origin_offset[2] = static_cast(0.0); + texopt->scale[0] = static_cast(1.0); + texopt->scale[1] = static_cast(1.0); + texopt->scale[2] = static_cast(1.0); + texopt->turbulence[0] = static_cast(0.0); + texopt->turbulence[1] = static_cast(0.0); + texopt->turbulence[2] = static_cast(0.0); + texopt->type = TEXTURE_TYPE_NONE; +} + +static void InitMaterial(material_t *material) { + InitTexOpt(&material->ambient_texopt, /* is_bump */ false); + InitTexOpt(&material->diffuse_texopt, /* is_bump */ false); + InitTexOpt(&material->specular_texopt, /* is_bump */ false); + InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false); + InitTexOpt(&material->bump_texopt, /* is_bump */ true); + InitTexOpt(&material->displacement_texopt, /* is_bump */ false); + InitTexOpt(&material->alpha_texopt, /* is_bump */ false); + InitTexOpt(&material->reflection_texopt, /* is_bump */ false); + InitTexOpt(&material->roughness_texopt, /* is_bump */ false); + InitTexOpt(&material->metallic_texopt, /* is_bump */ false); + InitTexOpt(&material->sheen_texopt, /* is_bump */ false); + InitTexOpt(&material->emissive_texopt, /* is_bump */ false); + InitTexOpt(&material->normal_texopt, + /* is_bump */ false); // @fixme { is_bump will be true? } + material->name = ""; + material->ambient_texname = ""; + material->diffuse_texname = ""; + material->specular_texname = ""; + material->specular_highlight_texname = ""; + material->bump_texname = ""; + material->displacement_texname = ""; + material->reflection_texname = ""; + material->alpha_texname = ""; + for (int i = 0; i < 3; i++) { + material->ambient[i] = static_cast(0.0); + material->diffuse[i] = static_cast(0.0); + material->specular[i] = static_cast(0.0); + material->transmittance[i] = static_cast(0.0); + material->emission[i] = static_cast(0.0); + } + material->illum = 0; + material->dissolve = static_cast(1.0); + material->shininess = static_cast(1.0); + material->ior = static_cast(1.0); + + material->roughness = static_cast(0.0); + material->metallic = static_cast(0.0); + material->sheen = static_cast(0.0); + material->clearcoat_thickness = static_cast(0.0); + material->clearcoat_roughness = static_cast(0.0); + material->anisotropy_rotation = static_cast(0.0); + material->anisotropy = static_cast(0.0); + material->roughness_texname = ""; + material->metallic_texname = ""; + material->sheen_texname = ""; + material->emissive_texname = ""; + material->normal_texname = ""; + + material->unknown_parameter.clear(); +} + +// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html +template +static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) { + int i, j, c = 0; + for (i = 0, j = nvert - 1; i < nvert; j = i++) { + if (((verty[i] > testy) != (verty[j] > testy)) && + (testx < + (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + + vertx[i])) + c = !c; + } + return c; +} + +// TODO(syoyo): refactor function. +static bool exportGroupsToShape(shape_t *shape, const PrimGroup &prim_group, + const std::vector &tags, + const int material_id, const std::string &name, + bool triangulate, + const std::vector &v) { + if (prim_group.IsEmpty()) { + return false; + } + + shape->name = name; + + // polygon + if (!prim_group.faceGroup.empty()) { + // Flatten vertices and indices + for (size_t i = 0; i < prim_group.faceGroup.size(); i++) { + const face_t &face = prim_group.faceGroup[i]; + + size_t npolys = face.vertex_indices.size(); + + if (npolys < 3) { + // Face must have 3+ vertices. + continue; + } + + vertex_index_t i0 = face.vertex_indices[0]; + vertex_index_t i1(-1); + vertex_index_t i2 = face.vertex_indices[1]; + + if (triangulate) { + // find the two axes to work in + size_t axes[2] = {1, 2}; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + i2 = face.vertex_indices[(k + 2) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + size_t vi2 = size_t(i2.v_idx); + + if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || + ((3 * vi2 + 2) >= v.size())) { + // Invalid triangle. + // FIXME(syoyo): Is it ok to simply skip this invalid triangle? + continue; + } + real_t v0x = v[vi0 * 3 + 0]; + real_t v0y = v[vi0 * 3 + 1]; + real_t v0z = v[vi0 * 3 + 2]; + real_t v1x = v[vi1 * 3 + 0]; + real_t v1y = v[vi1 * 3 + 1]; + real_t v1z = v[vi1 * 3 + 2]; + real_t v2x = v[vi2 * 3 + 0]; + real_t v2y = v[vi2 * 3 + 1]; + real_t v2z = v[vi2 * 3 + 2]; + real_t e0x = v1x - v0x; + real_t e0y = v1y - v0y; + real_t e0z = v1z - v0z; + real_t e1x = v2x - v1x; + real_t e1y = v2y - v1y; + real_t e1z = v2z - v1z; + real_t cx = std::fabs(e0y * e1z - e0z * e1y); + real_t cy = std::fabs(e0z * e1x - e0x * e1z); + real_t cz = std::fabs(e0x * e1y - e0y * e1x); + const real_t epsilon = std::numeric_limits::epsilon(); + if (cx > epsilon || cy > epsilon || cz > epsilon) { + // found a corner + if (cx > cy && cx > cz) { + } else { + axes[0] = 0; + if (cz > cx && cz > cy) axes[1] = 1; + } + break; + } + } + + real_t area = 0; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + if (((vi0 * 3 + axes[0]) >= v.size()) || + ((vi0 * 3 + axes[1]) >= v.size()) || + ((vi1 * 3 + axes[0]) >= v.size()) || + ((vi1 * 3 + axes[1]) >= v.size())) { + // Invalid index. + continue; + } + real_t v0x = v[vi0 * 3 + axes[0]]; + real_t v0y = v[vi0 * 3 + axes[1]]; + real_t v1x = v[vi1 * 3 + axes[0]]; + real_t v1y = v[vi1 * 3 + axes[1]]; + area += (v0x * v1y - v0y * v1x) * static_cast(0.5); + } + + face_t remainingFace = face; // copy + size_t guess_vert = 0; + vertex_index_t ind[3]; + real_t vx[3]; + real_t vy[3]; + + // How many iterations can we do without decreasing the remaining + // vertices. + size_t remainingIterations = face.vertex_indices.size(); + size_t previousRemainingVertices = remainingFace.vertex_indices.size(); + + while (remainingFace.vertex_indices.size() > 3 && + remainingIterations > 0) { + npolys = remainingFace.vertex_indices.size(); + if (guess_vert >= npolys) { + guess_vert -= npolys; + } + + if (previousRemainingVertices != npolys) { + // The number of remaining vertices decreased. Reset counters. + previousRemainingVertices = npolys; + remainingIterations = npolys; + } else { + // We didn't consume a vertex on previous iteration, reduce the + // available iterations. + remainingIterations--; + } + + for (size_t k = 0; k < 3; k++) { + ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; + size_t vi = size_t(ind[k].v_idx); + if (((vi * 3 + axes[0]) >= v.size()) || + ((vi * 3 + axes[1]) >= v.size())) { + // ??? + vx[k] = static_cast(0.0); + vy[k] = static_cast(0.0); + } else { + vx[k] = v[vi * 3 + axes[0]]; + vy[k] = v[vi * 3 + axes[1]]; + } + } + real_t e0x = vx[1] - vx[0]; + real_t e0y = vy[1] - vy[0]; + real_t e1x = vx[2] - vx[1]; + real_t e1y = vy[2] - vy[1]; + real_t cross = e0x * e1y - e0y * e1x; + // if an internal angle + if (cross * area < static_cast(0.0)) { + guess_vert += 1; + continue; + } + + // check all other verts in case they are inside this triangle + bool overlap = false; + for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { + size_t idx = (guess_vert + otherVert) % npolys; + + if (idx >= remainingFace.vertex_indices.size()) { + // ??? + continue; + } + + size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx); + + if (((ovi * 3 + axes[0]) >= v.size()) || + ((ovi * 3 + axes[1]) >= v.size())) { + // ??? + continue; + } + real_t tx = v[ovi * 3 + axes[0]]; + real_t ty = v[ovi * 3 + axes[1]]; + if (pnpoly(3, vx, vy, tx, ty)) { + overlap = true; + break; + } + } + + if (overlap) { + guess_vert += 1; + continue; + } + + // this triangle is an ear + { + index_t idx0, idx1, idx2; + idx0.vertex_index = ind[0].v_idx; + idx0.normal_index = ind[0].vn_idx; + idx0.texcoord_index = ind[0].vt_idx; + idx1.vertex_index = ind[1].v_idx; + idx1.normal_index = ind[1].vn_idx; + idx1.texcoord_index = ind[1].vt_idx; + idx2.vertex_index = ind[2].v_idx; + idx2.normal_index = ind[2].vn_idx; + idx2.texcoord_index = ind[2].vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } + + // remove v1 from the list + size_t removed_vert_index = (guess_vert + 1) % npolys; + while (removed_vert_index + 1 < npolys) { + remainingFace.vertex_indices[removed_vert_index] = + remainingFace.vertex_indices[removed_vert_index + 1]; + removed_vert_index += 1; + } + remainingFace.vertex_indices.pop_back(); + } + + if (remainingFace.vertex_indices.size() == 3) { + i0 = remainingFace.vertex_indices[0]; + i1 = remainingFace.vertex_indices[1]; + i2 = remainingFace.vertex_indices[2]; + { + index_t idx0, idx1, idx2; + idx0.vertex_index = i0.v_idx; + idx0.normal_index = i0.vn_idx; + idx0.texcoord_index = i0.vt_idx; + idx1.vertex_index = i1.v_idx; + idx1.normal_index = i1.vn_idx; + idx1.texcoord_index = i1.vt_idx; + idx2.vertex_index = i2.v_idx; + idx2.normal_index = i2.vn_idx; + idx2.texcoord_index = i2.vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } + } + } else { + for (size_t k = 0; k < npolys; k++) { + index_t idx; + idx.vertex_index = face.vertex_indices[k].v_idx; + idx.normal_index = face.vertex_indices[k].vn_idx; + idx.texcoord_index = face.vertex_indices[k].vt_idx; + shape->mesh.indices.push_back(idx); + } + + shape->mesh.num_face_vertices.push_back( + static_cast(npolys)); + shape->mesh.material_ids.push_back(material_id); // per face + shape->mesh.smoothing_group_ids.push_back( + face.smoothing_group_id); // per face + } + } + + shape->mesh.tags = tags; + } + + // line + if (!prim_group.lineGroup.empty()) { + // Flatten indices + for (size_t i = 0; i < prim_group.lineGroup.size(); i++) { + for (size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size(); + j++) { + const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j]; + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + shape->lines.indices.push_back(idx); + } + + shape->lines.num_line_vertices.push_back( + int(prim_group.lineGroup[i].vertex_indices.size())); + } + } + + // points + if (!prim_group.pointsGroup.empty()) { + // Flatten & convert indices + for (size_t i = 0; i < prim_group.pointsGroup.size(); i++) { + for (size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size(); + j++) { + const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j]; + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + shape->points.indices.push_back(idx); + } + } + } + + return true; +} + +// Split a string with specified delimiter character. +// http://stackoverflow.com/questions/236129/split-a-string-in-c +static void SplitString(const std::string &s, char delim, + std::vector &elems) { + std::stringstream ss; + ss.str(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } +} + +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream, + std::string *warning, std::string *err) { + (void)err; + + // Create a default material anyway. + material_t material; + InitMaterial(&material); + + // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification. + bool has_d = false; + bool has_tr = false; + + std::stringstream warn_ss; + + size_t line_no = 0; + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + line_no++; + + // Trim trailing whitespace. + if (linebuf.size() > 0) { + linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); + } + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { + // flush previous material. + if (!material.name.empty()) { + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + } + + // initial temporary material + InitMaterial(&material); + + has_d = false; + has_tr = false; + + // set new mtl name + token += 7; + { + std::stringstream sstr; + sstr << token; + material.name = sstr.str(); + } + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || + (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { + token += 2; + material.ior = parseReal(&token); + continue; + } + + // emission + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.shininess = parseReal(&token); + continue; + } + + // illum model + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { + token += 6; + material.illum = parseInt(&token); + continue; + } + + // dissolve + if ((token[0] == 'd' && IS_SPACE(token[1]))) { + token += 1; + material.dissolve = parseReal(&token); + + if (has_tr) { + warn_ss << "Both `d` and `Tr` parameters defined for \"" + << material.name + << "\". Use the value of `d` for dissolve (line " << line_no + << " in .mtl.)" << std::endl; + } + has_d = true; + continue; + } + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + if (has_d) { + // `d` wins. Ignore `Tr` value. + warn_ss << "Both `d` and `Tr` parameters defined for \"" + << material.name + << "\". Use the value of `d` for dissolve (line " << line_no + << " in .mtl.)" << std::endl; + } else { + // We invert value of Tr(assume Tr is in range [0, 1]) + // NOTE: Interpretation of Tr is application(exporter) dependent. For + // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) + material.dissolve = static_cast(1.0) - parseReal(&token); + } + has_tr = true; + continue; + } + + // PBR: roughness + if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + material.roughness = parseReal(&token); + continue; + } + + // PBR: metallic + if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { + token += 2; + material.metallic = parseReal(&token); + continue; + } + + // PBR: sheen + if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.sheen = parseReal(&token); + continue; + } + + // PBR: clearcoat thickness + if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { + token += 2; + material.clearcoat_thickness = parseReal(&token); + continue; + } + + // PBR: clearcoat roughness + if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { + token += 4; + material.clearcoat_roughness = parseReal(&token); + continue; + } + + // PBR: anisotropy + if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { + token += 6; + material.anisotropy = parseReal(&token); + continue; + } + + // PBR: anisotropy rotation + if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { + token += 7; + material.anisotropy_rotation = parseReal(&token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.ambient_texname), + &(material.ambient_texopt), token); + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.diffuse_texname), + &(material.diffuse_texopt), token); + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_texname), + &(material.specular_texopt), token); + continue; + } + + // specular highlight texture + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_highlight_texname), + &(material.specular_highlight_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_Bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { + token += 6; + material.alpha_texname = token; + ParseTextureNameAndOption(&(material.alpha_texname), + &(material.alpha_texopt), token); + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.displacement_texname), + &(material.displacement_texopt), token); + continue; + } + + // reflection map + if ((0 == strncmp(token, "refl", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.reflection_texname), + &(material.reflection_texopt), token); + continue; + } + + // PBR: roughness texture + if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.roughness_texname), + &(material.roughness_texopt), token); + continue; + } + + // PBR: metallic texture + if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.metallic_texname), + &(material.metallic_texopt), token); + continue; + } + + // PBR: sheen texture + if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.sheen_texname), + &(material.sheen_texopt), token); + continue; + } + + // PBR: emissive texture + if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.emissive_texname), + &(material.emissive_texopt), token); + continue; + } + + // PBR: normal map texture + if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.normal_texname), + &(material.normal_texopt), token); + continue; + } + + // unknown parameter + const char *_space = strchr(token, ' '); + if (!_space) { + _space = strchr(token, '\t'); + } + if (_space) { + std::ptrdiff_t len = _space - token; + std::string key(token, static_cast(len)); + std::string value = _space + 1; + material.unknown_parameter.insert( + std::pair(key, value)); + } + } + // flush last material. + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + + if (warning) { + (*warning) = warn_ss.str(); + } +} + +bool MaterialFileReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *warn, std::string *err) { + std::string filepath; + + if (!m_mtlBaseDir.empty()) { + filepath = std::string(m_mtlBaseDir) + matId; + } else { + filepath = matId; + } + + std::ifstream matIStream(filepath.c_str()); + if (!matIStream) { + std::stringstream ss; + ss << "Material file [ " << filepath << " ] not found." << std::endl; + if (warn) { + (*warn) += ss.str(); + } + return false; + } + + LoadMtl(matMap, materials, &matIStream, warn, err); + + return true; +} + +bool MaterialStreamReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *warn, std::string *err) { + (void)err; + (void)matId; + if (!m_inStream) { + std::stringstream ss; + ss << "Material stream in error state. " << std::endl; + if (warn) { + (*warn) += ss.str(); + } + return false; + } + + LoadMtl(matMap, materials, &m_inStream, warn, err); + + return true; +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, const char *filename, const char *mtl_basedir, + bool trianglulate, bool default_vcols_fallback) { + attrib->vertices.clear(); + attrib->normals.clear(); + attrib->texcoords.clear(); + attrib->colors.clear(); + shapes->clear(); + + std::stringstream errss; + + std::ifstream ifs(filename); + if (!ifs) { + errss << "Cannot open file [" << filename << "]" << std::endl; + if (err) { + (*err) = errss.str(); + } + return false; + } + + std::string baseDir = mtl_basedir ? mtl_basedir : ""; + if (!baseDir.empty()) { +#ifndef _WIN32 + const char dirsep = '/'; +#else + const char dirsep = '\\'; +#endif + if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep; + } + MaterialFileReader matFileReader(baseDir); + + return LoadObj(attrib, shapes, materials, warn, err, &ifs, &matFileReader, + trianglulate, default_vcols_fallback); +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn /*= NULL*/, bool triangulate, + bool default_vcols_fallback) { + std::stringstream errss; + + std::vector v; + std::vector vn; + std::vector vt; + std::vector vc; + std::vector tags; + PrimGroup prim_group; + std::string name; + + // material + std::map material_map; + int material = -1; + + // smoothing group id + unsigned int current_smoothing_id = + 0; // Initial value. 0 means no smoothing. + + int greatest_v_idx = -1; + int greatest_vn_idx = -1; + int greatest_vt_idx = -1; + + shape_t shape; + + bool found_all_colors = true; + + size_t line_num = 0; + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + + line_num++; + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + real_t x, y, z; + real_t r, g, b; + + found_all_colors &= parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); + + v.push_back(x); + v.push_back(y); + v.push_back(z); + + if (found_all_colors || default_vcols_fallback) { + vc.push_back(r); + vc.push_back(g); + vc.push_back(b); + } + + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + vn.push_back(x); + vn.push_back(y); + vn.push_back(z); + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y; + parseReal2(&x, &y, &token); + vt.push_back(x); + vt.push_back(y); + continue; + } + + // line + if (token[0] == 'l' && IS_SPACE((token[1]))) { + token += 2; + + __line_t line; + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `l' line(e.g. zero value for vertex index. " + "line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + line.vertex_indices.push_back(vi); + + size_t n = strspn(token, " \t\r"); + token += n; + } + + prim_group.lineGroup.push_back(line); + + continue; + } + + // points + if (token[0] == 'p' && IS_SPACE((token[1]))) { + token += 2; + + __points_t pts; + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `p' line(e.g. zero value for vertex index. " + "line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + pts.vertex_indices.push_back(vi); + + size_t n = strspn(token, " \t\r"); + token += n; + } + + prim_group.pointsGroup.push_back(pts); + + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + face_t face; + + face.smoothing_group_id = current_smoothing_id; + face.vertex_indices.reserve(3); + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `f' line(e.g. zero value for face index. line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx; + greatest_vn_idx = + greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx; + greatest_vt_idx = + greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx; + + face.vertex_indices.push_back(vi); + size_t n = strspn(token, " \t\r"); + token += n; + } + + // replace with emplace_back + std::move on C++11 + prim_group.faceGroup.push_back(face); + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + token += 7; + std::stringstream ss; + ss << token; + std::string namebuf = ss.str(); + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material) { + // Create per-face material. Thus we don't add `shape` to `shapes` at + // this time. + // just clear `faceGroup` after `exportGroupsToShape()` call. + exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + prim_group.faceGroup.clear(); + material = newMaterialId; + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + token += 7; + + std::vector filenames; + SplitString(std::string(token), ' ', filenames); + + if (filenames.empty()) { + if (warn) { + std::stringstream ss; + ss << "Looks like empty filename for mtllib. Use default " + "material (line " + << line_num << ".)\n"; + + (*warn) += ss.str(); + } + } else { + bool found = false; + for (size_t s = 0; s < filenames.size(); s++) { + std::string warn_mtl; + std::string err_mtl; + bool ok = (*readMatFn)(filenames[s].c_str(), materials, + &material_map, &warn_mtl, &err_mtl); + if (warn && (!warn_mtl.empty())) { + (*warn) += warn_mtl; + } + + if (err && (!err_mtl.empty())) { + (*err) += err_mtl; + } + + if (ok) { + found = true; + break; + } + } + + if (!found) { + if (warn) { + (*warn) += + "Failed to load material file(s). Use default " + "material.\n"; + } + } + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + (void)ret; // return value not used. + + if (shape.mesh.indices.size() > 0) { + shapes->push_back(shape); + } + + shape = shape_t(); + + // material = -1; + prim_group.clear(); + + std::vector names; + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + // names[0] must be 'g' + + if (names.size() < 2) { + // 'g' with empty names + if (warn) { + std::stringstream ss; + ss << "Empty group name. line: " << line_num << "\n"; + (*warn) += ss.str(); + name = ""; + } + } else { + std::stringstream ss; + ss << names[1]; + + // tinyobjloader does not support multiple groups for a primitive. + // Currently we concatinate multiple group names with a space to get + // single group name. + + for (size_t i = 2; i < names.size(); i++) { + ss << " " << names[i]; + } + + name = ss.str(); + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + if (ret) { + shapes->push_back(shape); + } + + // material = -1; + prim_group.clear(); + shape = shape_t(); + + // @todo { multiple object name? } + token += 2; + std::stringstream ss; + ss << token; + name = ss.str(); + + continue; + } + + if (token[0] == 't' && IS_SPACE(token[1])) { + const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize. + tag_t tag; + + token += 2; + + tag.name = parseString(&token); + + tag_sizes ts = parseTagTriple(&token); + + if (ts.num_ints < 0) { + ts.num_ints = 0; + } + if (ts.num_ints > max_tag_nums) { + ts.num_ints = max_tag_nums; + } + + if (ts.num_reals < 0) { + ts.num_reals = 0; + } + if (ts.num_reals > max_tag_nums) { + ts.num_reals = max_tag_nums; + } + + if (ts.num_strings < 0) { + ts.num_strings = 0; + } + if (ts.num_strings > max_tag_nums) { + ts.num_strings = max_tag_nums; + } + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = parseInt(&token); + } + + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + tag.stringValues[i] = parseString(&token); + } + + tags.push_back(tag); + + continue; + } + + if (token[0] == 's' && IS_SPACE(token[1])) { + // smoothing group id + token += 2; + + // skip space. + token += strspn(token, " \t"); // skip space + + if (token[0] == '\0') { + continue; + } + + if (token[0] == '\r' || token[1] == '\n') { + continue; + } + + if (strlen(token) >= 3) { + if (token[0] == 'o' && token[1] == 'f' && token[2] == 'f') { + current_smoothing_id = 0; + } + } else { + // assume number + int smGroupId = parseInt(&token); + if (smGroupId < 0) { + // parse error. force set to 0. + // FIXME(syoyo): Report warning. + current_smoothing_id = 0; + } else { + current_smoothing_id = static_cast(smGroupId); + } + } + + continue; + } // smoothing group id + + // Ignore unknown command. + } + + // not all vertices have colors, no default colors desired? -> clear colors + if (!found_all_colors && !default_vcols_fallback) { + vc.clear(); + } + + if (greatest_v_idx >= static_cast(v.size() / 3)) { + if (warn) { + std::stringstream ss; + ss << "Vertex indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + if (greatest_vn_idx >= static_cast(vn.size() / 3)) { + if (warn) { + std::stringstream ss; + ss << "Vertex normal indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + if (greatest_vt_idx >= static_cast(vt.size() / 2)) { + if (warn) { + std::stringstream ss; + ss << "Vertex texcoord indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + // exportGroupsToShape return false when `usemtl` is called in the last + // line. + // we also add `shape` to `shapes` when `shape.mesh` has already some + // faces(indices) + if (ret || shape.mesh.indices + .size()) { // FIXME(syoyo): Support other prims(e.g. lines) + shapes->push_back(shape); + } + prim_group.clear(); // for safety + + if (err) { + (*err) += errss.str(); + } + + attrib->vertices.swap(v); + attrib->vertex_weights.swap(v); + attrib->normals.swap(vn); + attrib->texcoords.swap(vt); + attrib->texcoord_ws.swap(vt); + attrib->colors.swap(vc); + + return true; +} + +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data /*= NULL*/, + MaterialReader *readMatFn /*= NULL*/, + std::string *warn, /* = NULL*/ + std::string *err /*= NULL*/) { + std::stringstream errss; + + // material + std::map material_map; + int material_id = -1; // -1 = invalid + + std::vector indices; + std::vector materials; + std::vector names; + names.reserve(2); + std::vector names_out; + + std::string linebuf; + while (inStream.peek() != -1) { + safeGetline(inStream, linebuf); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + // TODO(syoyo): Support parsing vertex color extension. + real_t x, y, z, w; // w is optional. default = 1.0 + parseV(&x, &y, &z, &w, &token); + if (callback.vertex_cb) { + callback.vertex_cb(user_data, x, y, z, w); + } + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + if (callback.normal_cb) { + callback.normal_cb(user_data, x, y, z); + } + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; // y and z are optional. default = 0.0 + parseReal3(&x, &y, &z, &token); + if (callback.texcoord_cb) { + callback.texcoord_cb(user_data, x, y, z); + } + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + indices.clear(); + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi = parseRawTriple(&token); + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + indices.push_back(idx); + size_t n = strspn(token, " \t\r"); + token += n; + } + + if (callback.index_cb && indices.size() > 0) { + callback.index_cb(user_data, &indices.at(0), + static_cast(indices.size())); + } + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + token += 7; + std::stringstream ss; + ss << token; + std::string namebuf = ss.str(); + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material_id) { + material_id = newMaterialId; + } + + if (callback.usemtl_cb) { + callback.usemtl_cb(user_data, namebuf.c_str(), material_id); + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + token += 7; + + std::vector filenames; + SplitString(std::string(token), ' ', filenames); + + if (filenames.empty()) { + if (warn) { + (*warn) += + "Looks like empty filename for mtllib. Use default " + "material. \n"; + } + } else { + bool found = false; + for (size_t s = 0; s < filenames.size(); s++) { + std::string warn_mtl; + std::string err_mtl; + bool ok = (*readMatFn)(filenames[s].c_str(), &materials, + &material_map, &warn_mtl, &err_mtl); + + if (warn && (!warn_mtl.empty())) { + (*warn) += warn_mtl; // This should be warn message. + } + + if (err && (!err_mtl.empty())) { + (*err) += err_mtl; + } + + if (ok) { + found = true; + break; + } + } + + if (!found) { + if (warn) { + (*warn) += + "Failed to load material file(s). Use default " + "material.\n"; + } + } else { + if (callback.mtllib_cb) { + callback.mtllib_cb(user_data, &materials.at(0), + static_cast(materials.size())); + } + } + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + names.clear(); + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + if (callback.group_cb) { + if (names.size() > 1) { + // create const char* array. + names_out.resize(names.size() - 1); + for (size_t j = 0; j < names_out.size(); j++) { + names_out[j] = names[j + 1].c_str(); + } + callback.group_cb(user_data, &names_out.at(0), + static_cast(names_out.size())); + + } else { + callback.group_cb(user_data, NULL, 0); + } + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // @todo { multiple object name? } + token += 2; + + std::stringstream ss; + ss << token; + std::string object_name = ss.str(); + + if (callback.object_cb) { + callback.object_cb(user_data, object_name.c_str()); + } + + continue; + } + +#if 0 // @todo + if (token[0] == 't' && IS_SPACE(token[1])) { + tag_t tag; + + token += 2; + std::stringstream ss; + ss << token; + tag.name = ss.str(); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(&token); + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + std::stringstream ss; + ss << token; + tag.stringValues[i] = ss.str(); + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); + } +#endif + + // Ignore unknown command. + } + + if (err) { + (*err) += errss.str(); + } + + return true; +} + +bool ObjReader::ParseFromFile(const std::string &filename, + const ObjReaderConfig &config) { + std::string mtl_search_path; + + if (config.mtl_search_path.empty()) { + // + // split at last '/'(for unixish system) or '\\'(for windows) to get + // the base directory of .obj file + // + if (filename.find_last_of("/\\") != std::string::npos) { + mtl_search_path = filename.substr(0, filename.find_last_of("/\\")); + } + } + + valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, + filename.c_str(), mtl_search_path.c_str(), + config.triangulate, config.vertex_color); + + return valid_; +} + +bool ObjReader::ParseFromString(const std::string &obj_text, + const std::string &mtl_text, + const ObjReaderConfig &config) { + std::stringbuf obj_buf(obj_text); + std::stringbuf mtl_buf(mtl_text); + + std::istream obj_ifs(&obj_buf); + std::istream mtl_ifs(&mtl_buf); + + MaterialStreamReader mtl_ss(mtl_ifs); + + valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, + &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color); + + return valid_; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace tinyobj + +#endif diff --git a/src/fntsys.c b/src/fntsys.c index 525abd3..708db06 100644 --- a/src/fntsys.c +++ b/src/fntsys.c @@ -331,7 +331,7 @@ void fntInit() for (; i < FNT_MAX_COUNT; ++i) fntInitSlot(&fonts[i]); - fntLoadDefault(NULL); + //fntLoadDefault(NULL); fntUpdateAspectRatio(); } diff --git a/src/graphics.c b/src/graphics.c index c2a0791..e3d9c1f 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -10,12 +10,12 @@ #include #include "include/graphics.h" +#include "include/athena_math.h" #include "include/dbgprintf.h" +#include "include/fntsys.h" #include "include/pad.h" -#define PI 3.14159265359 - static const u64 BLACK_RGBAQ = GS_SETREG_RGBAQ(0x00,0x00,0x00,0x80,0x00); #define RENDER_QUEUE_PER_POOLSIZE 1024 * 256 // 256K of persistent renderqueue @@ -91,9 +91,11 @@ int athena_load_png(GSTEXTURE* tex, FILE* File, bool delayed) png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,&interlace_type, NULL, NULL); - if (bit_depth == 16) png_set_strip_16(png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY || bit_depth < 4) png_set_expand(png_ptr); - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA || bit_depth < 4) + png_set_expand(png_ptr); png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); @@ -105,7 +107,9 @@ int athena_load_png(GSTEXTURE* tex, FILE* File, bool delayed) tex->VramClut = 0; tex->Clut = NULL; - if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA) + color_type = png_get_color_type(png_ptr, info_ptr); + + if(color_type == PNG_COLOR_TYPE_RGB_ALPHA) { int row_bytes = png_get_rowbytes(png_ptr, info_ptr); tex->PSM = GS_PSM_CT32; @@ -131,7 +135,7 @@ int athena_load_png(GSTEXTURE* tex, FILE* File, bool delayed) free(row_pointers); } - else if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB) + else if(color_type == PNG_COLOR_TYPE_RGB) { int row_bytes = png_get_rowbytes(png_ptr, info_ptr); tex->PSM = GS_PSM_CT24; @@ -156,7 +160,7 @@ int athena_load_png(GSTEXTURE* tex, FILE* File, bool delayed) free(row_pointers); } - else if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE){ + else if(color_type == PNG_COLOR_TYPE_PALETTE){ struct png_clut { u8 r, g, b, a; }; @@ -987,19 +991,18 @@ void drawCircle(float x, float y, float radius, u64 color, u8 filled) int a; for (a = 0; a < 36; a++) { - v[a*2] = (cosf(a * (PI*2)/36) * radius) + x; - v[a*2+1] = (sinf(a * (PI*2)/36) * radius) + y; + v[a*2] = (cosf(a * (M_PI*2)/36) * radius) + x; + v[a*2+1] = (sinf(a * (M_PI*2)/36) * radius) + y; } - if (!filled) { + if (filled) { + gsKit_prim_triangle_fan(gsGlobal, v, 36, 1, color); + } else { v[36*2] = radius + x; v[36*2 + 1] = y; - } - - if (filled) - gsKit_prim_triangle_fan(gsGlobal, v, 36, 1, color); - else + gsKit_prim_line_strip(gsGlobal, v, 37, 1, color); + } } void InvalidateTexture(GSTEXTURE *txt) @@ -1020,7 +1023,9 @@ int GetInterlacedFrameMode() return 0; } -GSGLOBAL *getGSGLOBAL(){return gsGlobal;} +GSGLOBAL *getGSGLOBAL(){ return gsGlobal; } + +static void switchFlipScreenFunction(); void setVideoMode(s16 mode, int width, int height, int psm, s16 interlace, s16 field, bool zbuffering, int psmz, bool double_buffering, uint8_t pass_count) { gsGlobal->Mode = mode; @@ -1130,7 +1135,7 @@ void athena_error_screen(const char* errMsg, bool dark_mode) { if (errMsg != NULL) { - dbgprintf("AthenaEnv ERROR!\n%s", errMsg); + printf("AthenaEnv ERROR!\n%s", errMsg); if (strstr(errMsg, "EvalError") != NULL) { color = GS_SETREG_RGBAQ(0x56,0x71,0x7D,0x80,0x00); @@ -1144,10 +1149,12 @@ void athena_error_screen(const char* errMsg, bool dark_mode) { color = GS_SETREG_RGBAQ(0xD0,0x31,0x3D,0x80,0x00); } else if (strstr(errMsg, "InternalError") != NULL) { color = GS_SETREG_RGBAQ(0x8A,0x00,0xC2,0x80,0x00); - } else if(strstr(errMsg, "URIError") != NULL) { + } else if (strstr(errMsg, "URIError") != NULL) { color = GS_SETREG_RGBAQ(0xFF,0x78,0x1F,0x80,0x00); - } else if(strstr(errMsg, "AggregateError") != NULL) { + } else if (strstr(errMsg, "AggregateError") != NULL) { color = GS_SETREG_RGBAQ(0xE2,0x61,0x9F,0x80,0x00); + } else if (strstr(errMsg, "AthenaError") != NULL) { + color = GS_SETREG_RGBAQ(0x70,0x29,0x63,0x80,0x00); } if(dark_mode) { @@ -1157,11 +1164,14 @@ void athena_error_screen(const char* errMsg, bool dark_mode) { color2 = GS_SETREG_RGBAQ(0x80,0x80,0x80,0x80,0x00); } + fntLoadDefault(NULL); + fntSetCharSize(0, FNTSYS_CHAR_SIZE*64*0.8f, FNTSYS_CHAR_SIZE*64*0.8f); + while (!isButtonPressed(PAD_START)) { clearScreen(color); - printFontMText("AthenaEnv ERROR!", 15.0f, 15.0f, 0.9f, color2); - printFontMText(errMsg, 15.0f, 80.0f, 0.6f, color2); - printFontMText("\nPress [start] to restart\n", 15.0f, 400.0f, 0.6f, color2); + fntRenderString(0, 15, 15, 0, 640, 448, "AthenaEnv ERROR!", color2); + fntRenderString(0, 15, 80, 0, 640, 448, errMsg, color2); + fntRenderString(0, 15, 400, 0, 640, 448, "Press [start] to restart", color2); flipScreen(); } } @@ -1180,7 +1190,25 @@ inline void processFrameCounter() frames++; } -void flipScreenSingleBuffering() +void vu1_queue_init(GSGLOBAL *gsGlobal, GSQUEUE *Queue, u8 mode, int size) +{ + // Init pool 0 + Queue->pool[0] = gsKit_alloc_ucab(size); + Queue->pool_max[0] = (u64 *)((u32)Queue->pool[0] + size); + + Queue->pool[1] = gsKit_alloc_ucab(size); + Queue->pool_max[1] = (u64 *)((u32)Queue->pool[1] + size); + + Queue->dma_tag = Queue->pool[0]; + Queue->pool_cur = (u64 *)((u32)Queue->pool[0] + 16); + Queue->dbuf = 0; + Queue->tag_size = 0; + Queue->last_tag = Queue->pool_cur; + Queue->last_type = GIF_RESERVED; + Queue->mode = mode; +} + +static void flipScreenSingleBuffering() { //gsKit_set_finish(gsGlobal); gsKit_sync(gsGlobal); @@ -1189,7 +1217,7 @@ void flipScreenSingleBuffering() gsKit_TexManager_nextFrame(gsGlobal); } -void flipScreenSingleBufferingPerf() +static void flipScreenSingleBufferingPerf() { //gsKit_set_finish(gsGlobal); gsKit_sync(gsGlobal); @@ -1200,26 +1228,26 @@ void flipScreenSingleBufferingPerf() processFrameCounter(); } -void flipScreenDoubleBuffering() +static void flipScreenDoubleBuffering() { //gsKit_set_finish(gsGlobal); - gsKit_queue_exec(gsGlobal); - gsKit_finish(); gsKit_sync(gsGlobal); gsKit_flip(gsGlobal); + gsKit_queue_exec(gsGlobal); + gsKit_finish(); gsKit_TexManager_nextFrame(gsGlobal); } -void flipScreenDoubleBufferingPerf() +static void flipScreenDoubleBufferingPerf() { //gsKit_set_finish(gsGlobal); - gsKit_queue_exec(gsGlobal); - gsKit_finish(); gsKit_sync(gsGlobal); gsKit_flip(gsGlobal); + gsKit_queue_exec(gsGlobal); + gsKit_finish(); gsKit_TexManager_nextFrame(gsGlobal); @@ -1228,13 +1256,13 @@ void flipScreenDoubleBufferingPerf() ////////////////////////////////////////////////////////////////////////////////////////////// -void flipScreenSingleBufferingNoVSync() +static void flipScreenSingleBufferingNoVSync() { gsKit_queue_exec(gsGlobal); gsKit_TexManager_nextFrame(gsGlobal); } -void flipScreenSingleBufferingPerfNoVSync() +static void flipScreenSingleBufferingPerfNoVSync() { gsKit_queue_exec(gsGlobal); @@ -1243,19 +1271,19 @@ void flipScreenSingleBufferingPerfNoVSync() processFrameCounter(); } -void flipScreenDoubleBufferingNoVSync() +static void flipScreenDoubleBufferingNoVSync() { + gsKit_flip(gsGlobal); gsKit_queue_exec(gsGlobal); gsKit_finish(); - gsKit_flip(gsGlobal); gsKit_TexManager_nextFrame(gsGlobal); } -void flipScreenDoubleBufferingPerfNoVSync() +static void flipScreenDoubleBufferingPerfNoVSync() { + gsKit_flip(gsGlobal); gsKit_queue_exec(gsGlobal); gsKit_finish(); - gsKit_flip(gsGlobal); gsKit_TexManager_nextFrame(gsGlobal); processFrameCounter(); @@ -1263,7 +1291,7 @@ void flipScreenDoubleBufferingPerfNoVSync() ////////////////////////////////////////////////////////////////////////////////////////////// -void flipScreenHiRes() +static void flipScreenHiRes() { gsKit_hires_sync(gsGlobal); gsKit_hires_flip(gsGlobal); @@ -1271,7 +1299,7 @@ void flipScreenHiRes() } -void flipScreenHiResPerf() +static void flipScreenHiResPerf() { gsKit_hires_sync(gsGlobal); gsKit_hires_flip(gsGlobal); @@ -1280,7 +1308,7 @@ void flipScreenHiResPerf() processFrameCounter(); } -void switchFlipScreenFunction() +static void switchFlipScreenFunction() { if (hires) { if(perf) { @@ -1351,6 +1379,10 @@ void init_graphics() dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF); dmaKit_chan_init(DMA_CHANNEL_GIF); + dmaKit_chan_init(DMA_CHANNEL_VIF1); + dmaKit_wait(DMA_CHANNEL_GIF, 0); + dmaKit_wait(DMA_CHANNEL_VIF1, 0); + flipScreen = flipScreenDoubleBuffering; @@ -1376,6 +1408,7 @@ void init_graphics() gsKit_vsync_wait(); flipScreen(); + vu1_set_double_buffer_settings(); } void graphicWaitVblankStart(){ diff --git a/src/include/athena_math.h b/src/include/athena_math.h new file mode 100644 index 0000000..9692288 --- /dev/null +++ b/src/include/athena_math.h @@ -0,0 +1,13 @@ +#ifndef ATHENA_MATH_H +#define ATHENA_MATH_H + +float athena_cosf(float x); +float athena_atan2f(float y, float x); +float athena_randomf(float min, float max); +int athena_randomi(int min, int max); +float athena_asinf(float x); +float athena_acosf(float x); +float athena_sinf(float x); +float athena_tanf(float x); + +#endif \ No newline at end of file diff --git a/src/include/graphics.h b/src/include/graphics.h index da8b896..0fae349 100644 --- a/src/include/graphics.h +++ b/src/include/graphics.h @@ -37,16 +37,12 @@ typedef struct GSTEXTURE *txt; } rm_quad_t; -typedef struct { - uint32_t facesCount; - uint16_t* idxList; - VECTOR* positions; - VECTOR* texcoords; - VECTOR* normals; - VECTOR* colours; - VECTOR* bounding_box; - GSTEXTURE* texture; -} model; +typedef enum { + COLOR_MODULATE, + COLOR_DECAL, + COLOR_HIGHLIGHT, + COLOR_HIGHLIGHT2 +} eColorFunctions; typedef u64 Color; #define A(color) ((u8)(color >> 24 & 0xFF)) @@ -105,20 +101,6 @@ void loadFontM(); void printFontMText(const char* text, float x, float y, float scale, Color color); void unloadFontM(); - -void init3D(float aspect); - -void setCameraPosition(float x, float y, float z); -void setCameraRotation(float x, float y, float z); - -void setLightQuantity(int quantity); -void createLight(int lightid, float dir_x, float dir_y, float dir_z, int type, float r, float g, float b); - -model* loadOBJ(const char* path, GSTEXTURE* text); -void drawOBJ(model* m, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z); -void draw_bbox(model* m, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z, Color color); - - void athena_error_screen(const char* errMsg, bool dark_mode); #endif diff --git a/src/include/network.h b/src/include/network.h index 9422bb6..ab52f12 100644 --- a/src/include/network.h +++ b/src/include/network.h @@ -1,6 +1,9 @@ #include "../ath_env.h" #include -#include +#include /* socket, connect */ +#include /* struct sockaddr_in, struct sockaddr */ +#include /* struct hostent, gethostbyname */ +#include #include #include #include @@ -46,4 +49,4 @@ char* jsonToUrl(char *json); int ethApplyNetIFConfig(int mode); int ethWaitValidNetIFLinkState(void); int ethWaitValidDHCPState(void); -int ethApplyIPConfig(int use_dhcp, const struct ip4_addr *ip, const struct ip4_addr *netmask, const struct ip4_addr *gateway, const struct ip4_addr *dns); \ No newline at end of file +int ethApplyIPConfig(int use_dhcp, const struct ip4_addr *ip, const struct ip4_addr *netmask, const struct ip4_addr *gateway, const struct ip4_addr *dns); diff --git a/src/include/render.h b/src/include/render.h index 66e8584..89a06be 100644 --- a/src/include/render.h +++ b/src/include/render.h @@ -1,9 +1,108 @@ +#ifndef ATHENA_RENDER_H +#define ATHENA_RENDER_H + #include "graphics.h" //3D math -int clip_bounding_box(MATRIX local_clip, VECTOR *bounding_box); +typedef struct { + VECTOR direction[4]; + VECTOR ambient[4]; + VECTOR diffuse[4]; + VECTOR specular[4]; +} LightData; -void calculate_vertices_clipped(VECTOR *output, int count, VECTOR *vertices, MATRIX local_screen); +typedef enum { + ATHENA_LIGHT_DIRECTION, + ATHENA_LIGHT_AMBIENT, + ATHENA_LIGHT_DIFFUSE, + ATHENA_LIGHT_SPECULAR, +} eLightAttributes; + +typedef enum { + PL_NO_LIGHTS_COLORS, + PL_NO_LIGHTS_COLORS_TEX, + PL_NO_LIGHTS, + PL_NO_LIGHTS_TEX, + PL_DEFAULT, + PL_DEFAULT_NO_TEX, +} eRenderPipelines; + +typedef struct ath_model { + uint32_t facesCount; + uint32_t indexCount; + + VECTOR* positions; + VECTOR* texcoords; + VECTOR* normals; + VECTOR* colours; + + VECTOR bounding_box[8]; + + void (*render)(struct ath_model* m, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z); + eRenderPipelines pipeline; + GSTEXTURE** textures; + int *tex_ranges; + int tex_count; +} model; + +int athena_render_set_pipeline(model* m, int pl_id); + +typedef enum { + CAMERA_DEFAULT, + CAMERA_LOOKAT, +} eCameraTypes; + +void setCameraType(eCameraTypes type); + +void cameraUpdate(); + +#define BATCH_SIZE 48 + +int clip_bounding_box(MATRIX local_clip, VECTOR *bounding_box); +void calculate_vertices_clipped(VECTOR *output, int count, VECTOR *vertices, MATRIX local_screen); int draw_convert_xyz(xyz_t *output, float x, float y, int z, int count, vertex_f_t *vertices); + +unsigned int get_max_z(GSGLOBAL* gsGlobal); + +void vu0_vector_clamp(VECTOR v0, VECTOR v1, float min, float max); + +float vu0_innerproduct(VECTOR v0, VECTOR v1); + +void athena_set_tw_th(const GSTEXTURE *Texture, int *tw, int *th); + +void athena_line_goraud_3d(GSGLOBAL *gsGlobal, float x1, float y1, int iz1, float x2, float y2, int iz2, u64 color1, u64 color2); + +void CameraMatrix(MATRIX m, VECTOR p, VECTOR zd, VECTOR yd); + +void RotCameraMatrix(MATRIX m, VECTOR p, VECTOR zd, VECTOR yd, VECTOR rot); + +void LookAtCameraMatrix(MATRIX m, VECTOR position, VECTOR target, VECTOR up); + +void init3D(float aspect, float fov, float near, float far); + +void setCameraPosition(float x, float y, float z); +void setCameraRotation(float x, float y, float z); + +void setCameraTarget(float x, float y, float z); +void turnCamera(float yaw, float pitch); +void orbitCamera(float yaw, float pitch); +void dollyCamera(float dist); +void zoomCamera(float dist); +void panCamera(float x, float y); + +void SetLightAttribute(int id, float x, float y, float z, int attr); + +void loadOBJ(model* res_m, const char* path, GSTEXTURE* text); +void draw_bbox(model* m, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z, Color color); + +void SubVector(VECTOR v0, VECTOR v1, VECTOR v2); +void AddVector(VECTOR res, VECTOR v1, VECTOR v2); +float LenVector(VECTOR v); +void SetLenVector(VECTOR v, float newLength); +void Normalize(VECTOR v0, VECTOR v1); +void OuterProduct(VECTOR v0, VECTOR v1, VECTOR v2); +void ScaleVector(VECTOR res, VECTOR v, float size); + +#endif \ No newline at end of file diff --git a/src/include/sound.h b/src/include/sound.h index 4d67eff..4a4c51f 100644 --- a/src/include/sound.h +++ b/src/include/sound.h @@ -45,6 +45,9 @@ void sound_pause(void); void sound_resume(Sound* snd); void sound_free(Sound* snd); void sound_deinit(void); +void sound_restart(void); +void sound_set_position(Sound* snd, int ms); +int sound_get_position(Sound* snd); audsrv_adpcm_t* sound_loadadpcm(const char* path); void sound_playadpcm(int slot, audsrv_adpcm_t *sample); diff --git a/src/include/system.h b/src/include/system.h index d6013fe..392ae2d 100644 --- a/src/include/system.h +++ b/src/include/system.h @@ -14,8 +14,3 @@ typedef struct char name[16]; int value; } DiscType; - -extern char* __ps2_normalize_path(char *path_name); -extern void load_elf_NoIOPReset(const char *elf_path); -extern void load_elf(const char *elf_path); -extern size_t GetFreeSize(void); \ No newline at end of file diff --git a/src/main.c b/src/main.c index 42707bc..8bfe77b 100644 --- a/src/main.c +++ b/src/main.c @@ -24,59 +24,6 @@ #include "include/fntsys.h" #endif -#ifdef ATHENA_CLI -#include - -void athena_error_screen(const char* errMsg, bool dark_mode) { - uint32_t color = 0x000000; - uint32_t color2 = 0xFFFFFF; - - if (errMsg != NULL) - { - dbgprintf("AthenaEnv ERROR!\n%s", errMsg); - - if (strstr(errMsg, "EvalError") != NULL) { - color = 0x7D7156; - } else if (strstr(errMsg, "SyntaxError") != NULL) { - color = 0xB06020; - } else if (strstr(errMsg, "TypeError") != NULL) { - color = 0x32813b; - } else if (strstr(errMsg, "ReferenceError") != NULL) { - color = 0x00DEE5; - } else if (strstr(errMsg, "RangeError") != NULL) { - color = 0x3D31D0; - } else if (strstr(errMsg, "InternalError") != NULL) { - color = 0xC2008A; - } else if(strstr(errMsg, "URIError") != NULL) { - color = 0x1F78FF; - } else if(strstr(errMsg, "AggregateError") != NULL) { - color = 0x9F61E2; - } - - if(dark_mode) { - color2 = color; - color = 0x000000; - } else { - color2 = 0xFFFFFF; - } - - scr_clear(); - - scr_setbgcolor(color); - scr_setCursor(0); - scr_setfontcolor(color2); - - scr_printf("AthenaEnv ERROR!\n"); - scr_printf("%s\n", errMsg); - scr_printf("\n\nPress [start] to restart"); - - while (!isButtonPressed(PAD_START)) { - nopdelay(); - } - } -} -#endif - char boot_path[255]; bool dark_mode; @@ -98,15 +45,46 @@ static void init_drivers() { } +int mnt(const char* path, int index, int openmod) +{ + char PFS[5+1] = "pfs0:"; + if (index > 0) + PFS[3] = '0' + index; + + dbgprintf("Mounting '%s' into pfs%d:\n", path, index); + if (fileXioMount(PFS, path, openmod) < 0) // mount + { + dbgprintf("Mount failed. unmounting & trying again...\n"); + if (fileXioUmount(PFS) < 0) //try to unmount then mount again in case it got mounted by something else + { + dbgprintf("Unmount failed!!!\n"); + } + if (fileXioMount(PFS, path, openmod) < 0) + { + dbgprintf("mount failed again!\n"); + return -1; + } else { + dbgprintf("Second mount succeed!\n"); + } + } else { + dbgprintf("mount successfull on first attempt\n"); + } + return 0; +} + int main(int argc, char **argv) { init_memory_manager(); char MountPoint[32+6+1]; // max partition name + 'hdd0:/' = '\0' char newCWD[255]; + dbginit(); // if we are using serial port. initialize it here before the fun starts + prepare_IOP(); init_drivers(); + getcwd(boot_path, sizeof(boot_path)); + if ((!strncmp(boot_path, "hdd0:", 5)) && (strstr(boot_path, ":pfs:") != NULL) && HDD_USABLE) // we booted from HDD and our modules are loaded and running... { if (getMountInfo(boot_path, NULL, MountPoint, newCWD)) // ...if we can parse the boot path... @@ -122,16 +100,12 @@ int main(int argc, char **argv) { } } + waitUntilDeviceIsReady(boot_path); #ifdef ATHENA_GRAPHICS init_graphics(); fntInit(); - loadFontM(); - #else - #ifdef ATHENA_CLI - init_scr(); - #endif #endif init_taskman(); @@ -158,29 +132,3 @@ int main(int argc, char **argv) { } -int mnt(const char* path, int index, int openmod) -{ - char PFS[5+1] = "pfs0:"; - if (index > 0) - PFS[3] = '0' + index; - - dbgprintf("Mounting '%s' into pfs%d:\n", path, index); - if (fileXioMount(PFS, path, openmod) < 0) // mount - { - dbgprintf("Mount failed. unmounting & trying again...\n"); - if (fileXioUmount(PFS) < 0) //try to unmount then mount again in case it got mounted by something else - { - dbgprintf("Unmount failed!!!\n"); - } - if (fileXioMount(PFS, path, openmod) < 0) - { - dbgprintf("mount failed again!\n"); - return -1; - } else { - dbgprintf("Second mount succeed!\n"); - } - } else { - dbgprintf("mount successfull on first attempt\n"); - } - return 0; -} \ No newline at end of file diff --git a/src/network.c b/src/network.c index 093e76b..1d48ba7 100644 --- a/src/network.c +++ b/src/network.c @@ -86,7 +86,7 @@ void requestThread(void* data) { curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_3); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); curl_easy_setopt(curl, CURLOPT_USERPWD, s->userpwd); curl_easy_setopt(curl, CURLOPT_USERAGENT, s->useragent); @@ -97,8 +97,11 @@ void requestThread(void* data) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); + s->chunk.timer = clock(); res = curl_easy_perform(curl); + if (res != CURLE_OK) { s->error = "Error while downloading file\n"; //, curl_easy_strerror(res)); } @@ -224,4 +227,5 @@ int ethApplyIPConfig(int use_dhcp, const struct ip4_addr *ip, const struct ip4_a } return result; -} \ No newline at end of file +} + diff --git a/src/pad.c b/src/pad.c index 4c325c4..9b3559f 100644 --- a/src/pad.c +++ b/src/pad.c @@ -5,7 +5,8 @@ #include "include/pad.h" #include "include/dbgprintf.h" -static char padBuf[256] __attribute__((aligned(64))); +static char pad0Buf[256] __attribute__((aligned(64))); +static char pad1Buf[256] __attribute__((aligned(64))); static char actAlign[6]; static int actuators; @@ -46,6 +47,7 @@ int initializePad(int port, int slot) int ret; int modes; int i; + int status; waitPadReady(port, slot); @@ -57,13 +59,15 @@ int initializePad(int port, int slot) if (modes > 0) { dbgprintf("( "); for (i = 0; i < modes; i++) { - dbgprintf("%d ", padInfoMode(port, slot, PAD_MODETABLE, i)); + status = padInfoMode(port, slot, PAD_MODETABLE, i); + dbgprintf("%d ", status); } dbgprintf(")"); } + status = padInfoMode(port, slot, PAD_MODECURID, 0); dbgprintf("It is currently using mode %d\n", - padInfoMode(port, slot, PAD_MODECURID, 0)); + status); // If modes == 0, this is not a Dual shock controller // (it has no actuator engines) @@ -98,14 +102,16 @@ int initializePad(int port, int slot) padSetMainMode(port, slot, PAD_MMODE_DUALSHOCK, PAD_MMODE_LOCK); waitPadReady(port, slot); - dbgprintf("infoPressMode: %d\n", padInfoPressMode(port, slot)); + status = padInfoPressMode(port, slot); + dbgprintf("infoPressMode: %d\n", status); waitPadReady(port, slot); - dbgprintf("enterPressMode: %d\n", padEnterPressMode(port, slot)); + status = padEnterPressMode(port, slot); + dbgprintf("enterPressMode: %d\n", status); waitPadReady(port, slot); actuators = padInfoAct(port, slot, -1, 0); - dbgprintf("# of actuators: %d\n",actuators); + dbgprintf("# of actuators: %d\n", actuators); if (actuators != 0) { actAlign[0] = 0; // Enable small engine @@ -116,8 +122,8 @@ int initializePad(int port, int slot) actAlign[5] = 0xff; waitPadReady(port, slot); - dbgprintf("padSetActAlign: %d\n", - padSetActAlign(port, slot, actAlign)); + status = padSetActAlign(port, slot, actAlign); + dbgprintf("padSetActAlign: %d\n", status); } else { dbgprintf("Did not find any actuators.\n"); @@ -173,7 +179,7 @@ void pad_init() dbgprintf("PortMax: %d\n", padGetPortMax()); dbgprintf("SlotMax: %d\n", padGetSlotMax(port)); - if((ret = padPortOpen(port, slot, padBuf)) == 0) { + if((ret = padPortOpen(0, 0, pad0Buf)) == 0) { dbgprintf("padOpenPort failed: %d\n", ret); SleepThread(); } @@ -182,5 +188,10 @@ void pad_init() dbgprintf("pad initalization failed!\n"); SleepThread(); } + + if((ret = padPortOpen(1, 0, pad1Buf)) == 0) { + dbgprintf("padOpenPort failed: %d\n", ret); + SleepThread(); + } } diff --git a/src/quickjs/quickjs-libc.c b/src/quickjs/quickjs-libc.c index 0339a90..ed323de 100644 --- a/src/quickjs/quickjs-libc.c +++ b/src/quickjs/quickjs-libc.c @@ -37,35 +37,13 @@ #include #include #include -#if defined(_WIN32) -#include -#include -#include -#else + //#include //#include #include #include -#if defined(__APPLE__) -typedef sig_t sighandler_t; -#if !defined(environ) -#include -#define environ (*_NSGetEnviron()) -#endif -#endif /* __APPLE__ */ - -#endif - -#if !defined(_WIN32) -/* enable the os.Worker API. IT relies on POSIX threads */ -#define USE_WORKER -#endif - -#ifdef USE_WORKER -#include -#include -#endif +#include "../include/graphics.h" #include "cutils.h" #include "list.h" @@ -277,23 +255,12 @@ static JSValue js_printf_internal(JSContext *ctx, goto fail; if (mod == 'l') { /* 64 bit number */ -#if defined(_WIN32) - if (q >= fmtbuf + sizeof(fmtbuf) - 3) - goto invalid; - q[2] = q[-1]; - q[-1] = 'I'; - q[0] = '6'; - q[1] = '4'; - q[3] = '\0'; - dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg); -#else if (q >= fmtbuf + sizeof(fmtbuf) - 2) goto invalid; q[1] = q[-1]; q[-1] = q[0] = 'l'; q[2] = '\0'; dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg); -#endif } else { dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg); } @@ -453,21 +420,11 @@ static JSValue js_std_loadFile(JSContext *ctx, JSValueConst this_val, typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, const char *module_name); - -#if defined(_WIN32) -static JSModuleDef *js_module_loader_so(JSContext *ctx, - const char *module_name) -{ - JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); - return NULL; -} -#else static JSModuleDef *js_module_loader_so(JSContext *ctx, const char *module_name) { return NULL; } -#endif /* !_WIN32 */ int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, JS_BOOL use_realpath, JS_BOOL is_main) @@ -823,6 +780,31 @@ static void js_set_error_object(JSContext *ctx, JSValue obj, int err) } } +static JSValue js_std_exists(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + FILE *f; + int err; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + + f = fopen(filename, "r"); + + JS_FreeCString(ctx, filename); + + if (!f) + return JS_NewBool(ctx, FALSE); + + return JS_NewBool(ctx, TRUE); + + fail: + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; +} + static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1233,51 +1215,6 @@ static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, return JS_NewInt32(ctx, c); } -/* urlGet */ - -#define URL_GET_PROGRAM "curl -s -i" -#define URL_GET_BUF_SIZE 4096 - -static int http_get_header_line(FILE *f, char *buf, size_t buf_size, - DynBuf *dbuf) -{ - int c; - char *p; - - p = buf; - for(;;) { - c = fgetc(f); - if (c < 0) - return -1; - if ((p - buf) < buf_size - 1) - *p++ = c; - if (dbuf) - dbuf_putc(dbuf, c); - if (c == '\n') - break; - } - *p = '\0'; - return 0; -} - -static int http_get_status(const char *buf) -{ - const char *p = buf; - while (*p != ' ' && *p != '\0') - p++; - if (*p != ' ') - return 0; - while (*p == ' ') - p++; - return atoi(p); -} - -static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_UNDEFINED; -} - static JSClassDef js_std_file_class = { "FILE", .finalizer = js_std_file_finalizer, @@ -1309,13 +1246,13 @@ static const JSCFunctionListEntry js_std_funcs[] = { JS_CFUNC_DEF("setenv", 1, js_std_setenv ), JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ), JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), - JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), JS_CFUNC_DEF("strerror", 1, js_std_strerror ), JS_CFUNC_DEF("parseExtJSON", 1, js_std_parseExtJSON ), /* FILE I/O */ JS_CFUNC_DEF("open", 2, js_std_open ), + JS_CFUNC_DEF("exists", 1, js_std_exists ), JS_CFUNC_DEF("popen", 2, js_std_popen ), JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ), JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ), @@ -1842,7 +1779,7 @@ static void js_os_timer_mark(JSRuntime *rt, JSValueConst val, } } -static JSValue js_os_create_timer(JSContext *ctx, int64_t interval, +JSValue js_os_create_timer(JSContext *ctx, int64_t interval, int64_t delay, JSValueConst func) { JSRuntime *rt = JS_GetRuntime(ctx); @@ -1926,8 +1863,9 @@ static JSClassDef js_os_timer_class = { .gc_mark = js_os_timer_mark, }; -static void call_handler(JSContext *ctx, JSValueConst func) +static int call_handler(JSContext *ctx, JSValueConst func) { + int result = 0; JSValue ret, func1; /* 'func' might be destroyed when calling itself (if it frees the handler), so must take extra care */ @@ -1935,8 +1873,11 @@ static void call_handler(JSContext *ctx, JSValueConst func) ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); JS_FreeValue(ctx, func1); if (JS_IsException(ret)) - js_std_dump_error(ctx); + result = -1; + //js_std_dump_error(ctx); JS_FreeValue(ctx, ret); + + return result; } #if defined(_WIN32) @@ -2093,6 +2034,10 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, } #endif +#define JS_POLL_OK 0 +#define JS_POLL_EMPTY 1 +#define JS_POLL_EXCEPTION -1 + static int js_os_poll(JSContext *ctx) { JSRuntime *rt = JS_GetRuntime(ctx); @@ -2115,15 +2060,15 @@ static int js_os_poll(JSContext *ctx) mask = (uint64_t)1 << sh->sig_num; if (os_pending_signals & mask) { os_pending_signals &= ~mask; - call_handler(ctx, sh->func); - return 0; + + return call_handler(ctx, sh->func); } } } if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && list_empty(&ts->port_list)) - return -1; /* no more events */ + return JS_POLL_EMPTY; /* no more events */ if (!list_empty(&ts->os_timers)) { cur_time = get_time_ms(); @@ -2132,21 +2077,22 @@ static int js_os_poll(JSContext *ctx) JSOSTimer *th = list_entry(el, JSOSTimer, link); delay = th->timeout - cur_time; if (delay <= 0) { + int call_res = 0; JSValue func; /* the timer expired */ func = th->func; if (th->interval != -1) { th->timeout = cur_time + th->interval; - call_handler(ctx, func); + call_res = call_handler(ctx, func); } else { th->func = JS_UNDEFINED; unlink_timer(JS_GetRuntime(ctx), th); if (!th->has_object) free_timer(JS_GetRuntime(ctx), th); - call_handler(ctx, func); + call_res = call_handler(ctx, func); JS_FreeValue(ctx, func); } - return 0; + return call_res; } else if (delay < min_delay) { min_delay = delay; } @@ -3592,10 +3538,59 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, } /* main loop which calls the user JS callbacks */ -void js_std_loop(JSContext *ctx) + +static JSValueConst render_loop_func = JS_UNDEFINED; +static uint64_t clear_color = GS_SETREG_RGBAQ(0x00, 0x00, 0x00, 0x80, 0x00); + +void js_set_render_loop_func(JSValueConst func) { + render_loop_func = func; +} + +void js_set_clear_color(uint64_t color) { + clear_color = color; +} + +typedef struct { + int buttons; + JSValueConst function; + EventFlavours flavour; +} InputEvent; + +static InputEvent padEvents[64]; +static uint8_t totalPadEvents; + +static JSPads* inputEventHandler = NULL; + +void js_set_input_event_handler(JSPads* pad) { + inputEventHandler = pad; +} + +int js_new_input_event(int buttons, JSValueConst func, EventFlavours flavour) { + for (int i = 0; i < 64; i++) { + if (!padEvents[i].function) { + padEvents[i].buttons = buttons; + padEvents[i].function = func; + padEvents[i].flavour = flavour; + totalPadEvents++; + return i; + } + } + return -1; +} + +void js_delete_input_event(int id) { + padEvents[id].buttons = 0; + padEvents[id].function = NULL; + padEvents[id].flavour = PRESSED_EVENT; + totalPadEvents--; +} + +int js_std_loop(JSContext *ctx) { JSContext *ctx1; + JSValue ret; int err; + int poll_result; for(;;) { /* execute the pending jobs */ @@ -3609,9 +3604,70 @@ void js_std_loop(JSContext *ctx) } } - if (!os_poll_func || os_poll_func(ctx)) + if (render_loop_func != JS_UNDEFINED) { + clearScreen(clear_color); + ret = JS_Call(ctx, render_loop_func, JS_UNDEFINED, 0, NULL); + flipScreen(); + + if (JS_IsException(ret)) { + err = -1; + } + } + + if (inputEventHandler && err != -1) { + js_pads_update(inputEventHandler); + if (totalPadEvents) { + for (int i = 0; i < 64; i++) { + if (padEvents[i].function) { + bool trigger_event = false; + switch (padEvents[i].flavour) { + case PRESSED_EVENT: + trigger_event = (inputEventHandler->btns & padEvents[i].buttons); + break; + case JUSTPRESSED_EVENT: + trigger_event = ((inputEventHandler->btns & padEvents[i].buttons) && !(inputEventHandler->old_btns & padEvents[i].buttons)); + break; + case NONPRESSED_EVENT: + trigger_event = !(inputEventHandler->btns & padEvents[i].buttons); + break; + }; + + if (trigger_event) { + ret = JS_Call(ctx, padEvents[i].function, JS_UNDEFINED, 0, NULL); + + if (JS_IsException(ret)) { + err = -1; + } + } + } + } + } + } + + if ((poll_result = js_os_poll(ctx)) == JS_POLL_EXCEPTION || err == -1) { + if (render_loop_func != JS_UNDEFINED) + JS_FreeValue(ctx, render_loop_func); + + if (totalPadEvents) { + for (int i = 0; i < 64; i++) { + if (padEvents[i].function) { + JS_FreeValue(ctx, padEvents[i].function); + } + } + } + + if (poll_result == JS_POLL_EXCEPTION) + return poll_result; + + return err; + } + + + if (poll_result == JS_POLL_EMPTY && render_loop_func == JS_UNDEFINED && !totalPadEvents) break; } + + return err; } void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, diff --git a/src/quickjs/quickjs-libc.h b/src/quickjs/quickjs-libc.h index eae09da..c310114 100644 --- a/src/quickjs/quickjs-libc.h +++ b/src/quickjs/quickjs-libc.h @@ -35,7 +35,7 @@ extern "C" { JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); void js_std_add_helpers(JSContext *ctx, int argc, char **argv); -void js_std_loop(JSContext *ctx); +int js_std_loop(JSContext *ctx); void js_std_init_handlers(JSRuntime *rt); void js_std_free_handlers(JSRuntime *rt); void js_std_dump_error(JSContext *ctx); @@ -50,7 +50,37 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void *opaque); void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); - + +void js_set_render_loop_func(JSValue func); +void js_set_clear_color(uint64_t color); + +typedef enum { + PRESSED_EVENT, + JUSTPRESSED_EVENT, + NONPRESSED_EVENT +} EventFlavours; + +typedef struct { + char port; + + uint32_t btns; + int lx; + int ly; + int rx; + int ry; + + uint32_t old_btns; + char old_lx; + char old_ly; + char old_rx; + char old_ry; +} JSPads; + +int js_new_input_event(int buttons, JSValueConst func, EventFlavours flavour); +void js_delete_input_event(int id); +void js_set_input_event_handler(JSPads* pad); +void js_pads_update(JSPads *pad); + #ifdef __cplusplus } /* extern "C" { */ #endif diff --git a/src/quickjs/quickjs.c b/src/quickjs/quickjs.c index cbdfaf9..e4e7445 100644 --- a/src/quickjs/quickjs.c +++ b/src/quickjs/quickjs.c @@ -121,6 +121,8 @@ #include #endif +#include "../include/athena_math.h" + enum { /* classid tag */ /* union usage | properties */ JS_CLASS_OBJECT = 1, /* must be first */ @@ -2177,7 +2179,6 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) { JSContext *ctx; int i; - ctx = js_mallocz_rt(rt, sizeof(JSContext)); if (!ctx) return NULL; @@ -42398,12 +42399,35 @@ static const JSCFunctionListEntry js_math_funcs[] = { JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ), JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ), JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ), + + JS_CFUNC_SPECIAL_DEF("fast_sinf", 1, f_f, athena_sinf ), + JS_CFUNC_SPECIAL_DEF("fast_cosf", 1, f_f, athena_cosf ), + JS_CFUNC_SPECIAL_DEF("fast_tanf", 1, f_f, athena_tanf ), + JS_CFUNC_SPECIAL_DEF("fast_atan2f", 2, f_f_f, athena_atan2f ), + JS_CFUNC_SPECIAL_DEF("fast_asinf", 1, f_f, athena_asinf ), + JS_CFUNC_SPECIAL_DEF("fast_acosf", 1, f_f, athena_acosf ), + JS_CFUNC_SPECIAL_DEF("randomf", 2, f_f_f, athena_randomf ), + JS_CFUNC_SPECIAL_DEF("randomi", 2, i_i_i, athena_randomi ), }; static const JSCFunctionListEntry js_math_obj[] = { JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), }; + +static const JSCFunctionListEntry js_fast_math_funcs[] = { + JS_CFUNC_SPECIAL_DEF("sinf", 1, f_f, athena_sinf ), + JS_CFUNC_SPECIAL_DEF("cosf", 1, f_f, athena_cosf ), + JS_CFUNC_SPECIAL_DEF("tanf", 1, f_f, athena_tanf ), + JS_CFUNC_SPECIAL_DEF("atan2f", 2, f_f_f, athena_atan2f ), + JS_CFUNC_SPECIAL_DEF("asinf", 1, f_f, athena_asinf ), + JS_CFUNC_SPECIAL_DEF("acosf", 1, f_f, athena_acosf ), +}; + +static const JSCFunctionListEntry js_fast_math_obj[] = { + JS_OBJECT_DEF("FastMath", js_fast_math_funcs, countof(js_fast_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + /* Date */ #if 0 @@ -51493,6 +51517,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) /* Math: create as autoinit object */ js_random_init(ctx); JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj)); + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_fast_math_obj, countof(js_fast_math_obj)); /* ES6 Reflect: create as autoinit object */ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj)); diff --git a/src/quickjs/quickjs.h b/src/quickjs/quickjs.h index 2f0ec7c..b92ce0f 100644 --- a/src/quickjs/quickjs.h +++ b/src/quickjs/quickjs.h @@ -991,6 +991,8 @@ typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ JS_CFUNC_f_f, JS_CFUNC_f_f_f, JS_CFUNC_f_d, + JS_CFUNC_i_i, + JS_CFUNC_i_i_i, JS_CFUNC_getter, JS_CFUNC_setter, JS_CFUNC_getter_magic, @@ -1009,6 +1011,8 @@ typedef union JSCFunctionType { float (*f_f)(float); float (*f_f_f)(float, float); float (*f_d)(double); + int (*i_i)(int); + int (*i_i_i)(int, int); JSValue (*getter)(JSContext *ctx, JSValueConst this_val); JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val); JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic); diff --git a/src/render.c b/src/render.c index 3f995d7..710b29e 100644 --- a/src/render.c +++ b/src/render.c @@ -4,55 +4,90 @@ #include #include #include +#include "fast_obj/fast_obj.h" #include "include/render.h" -MATRIX view_screen; +#include "include/dbgprintf.h" -VECTOR camera_position = { 0.00f, 0.00f, 0.00f, 1.00f }; -VECTOR camera_rotation = { 0.00f, 0.00f, 0.00f, 1.00f }; +#include "vif.h" -int light_count; -VECTOR* light_direction; -VECTOR* light_colour; -int* light_type; +extern u32 VU1Draw3D_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1Draw3D_CodeEnd __attribute__((section(".vudata"))); +extern u32 VU1Draw3DNoTex_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1Draw3DNoTex_CodeEnd __attribute__((section(".vudata"))); -typedef struct { - float x; - float y; - float z; - float t1; - float t2; - float n1; - float n2; - float n3; -} vertex; +extern u32 VU1Draw3DColors_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1Draw3DColors_CodeEnd __attribute__((section(".vudata"))); -typedef struct { - vertex v1; - vertex v2; - vertex v3; - struct vertexList* next; -} vertexList; +extern u32 VU1Draw3DColorsNoTex_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1Draw3DColorsNoTex_CodeEnd __attribute__((section(".vudata"))); -typedef struct { - vertex* vert; - struct rawVertexList* next; -} rawVertexList; +extern u32 VU1Draw3DLightsColors_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1Draw3DLightsColors_CodeEnd __attribute__((section(".vudata"))); + +extern u32 VU1Draw3DLightsColorsNoTex_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1Draw3DLightsColorsNoTex_CodeEnd __attribute__((section(".vudata"))); + +MATRIX view_screen; +MATRIX world_view; +VECTOR camera_position = { 0.00f, 0.00f, 0.00f, 0.00f }; +VECTOR camera_target = { 0.00f, 0.00f, 0.00f, 0.00f }; +VECTOR camera_yd = { 0.00f, 1.00f, 0.00f, 0.00f }; +VECTOR camera_zd = { 0.00f, 0.00f, 1.00f, 0.00f }; +VECTOR camera_up = { 0.00f, 1.00f, 0.00f, 0.00f }; +VECTOR camera_rotation = { 0.00f, 0.00f, 0.00f, 0.00f }; +VECTOR local_up = { 0.00f, 1.00f, 0.00f, 1.00f }; -void init3D(float aspect) +eCameraTypes camera_type = CAMERA_DEFAULT; + +static LightData lights; + +void init3D(float aspect, float fov, float near, float far) { - create_view_screen(view_screen, aspect, -0.20f, 0.20f, -0.20f, 0.20f, 1.00f, 2000.00f); + create_view_screen(view_screen, aspect, -fov, fov, -fov, fov, near, far); + +} + +void setCameraType(eCameraTypes type) { + camera_type = type; +} +void cameraUpdate() { + switch (camera_type) { + case CAMERA_DEFAULT: + RotCameraMatrix(world_view, camera_position, camera_zd, camera_yd, camera_rotation); + break; + case CAMERA_LOOKAT: + LookAtCameraMatrix(world_view, camera_position, camera_target, camera_up); + break; + } + dbgprintf("Camera matrix:\n"); + dbgprintf("%f %f %f %f\n", world_view[0], world_view[1], world_view[2], world_view[3]); + dbgprintf("%f %f %f %f\n", world_view[4], world_view[5], world_view[6], world_view[7]); + dbgprintf("%f %f %f %f\n", world_view[8], world_view[9], world_view[10], world_view[11]); + dbgprintf("%f %f %f %f\n", world_view[12], world_view[13], world_view[14], world_view[15]); } void setCameraPosition(float x, float y, float z){ camera_position[0] = x; camera_position[1] = y; camera_position[2] = z; - camera_position[3] = 1.00f; + camera_position[3] = 0.00f; +} + +void setCameraTarget(float x, float y, float z){ + VECTOR tmp_target = { x, y, z, 0.0f }; + + SubVector(camera_target, camera_target, tmp_target); + SubVector(camera_position, camera_position, camera_target); + + camera_target[0] = x; + camera_target[1] = y; + camera_target[2] = z; + camera_target[3] = 0.00f; } void setCameraRotation(float x, float y, float z){ @@ -62,427 +97,373 @@ void setCameraRotation(float x, float y, float z){ camera_rotation[3] = 1.00f; } -void setLightQuantity(int quantity){ - light_count = quantity; - light_direction = (VECTOR*)memalign(128, sizeof(VECTOR) * light_count); - light_colour = (VECTOR*)memalign(128, sizeof(VECTOR) * light_count); - light_type = (int*)memalign(128, sizeof(int) * light_count); +typedef struct { + float w, x, y, z; +} Quat; + +static Quat Quat_rotation(float rotation, VECTOR axis) { + Quat result; + + Normalize(axis, axis); + + float halfAngle = rotation * 0.5f; + float sinHalfAngle = sinf(halfAngle); + + result.w = cosf(halfAngle); + result.x = axis[0] * sinHalfAngle; + result.y = axis[1] * sinHalfAngle; + result.z = axis[2] * sinHalfAngle; + + return result; } -void createLight(int lightid, float dir_x, float dir_y, float dir_z, int type, float r, float g, float b){ - light_direction[lightid-1][0] = dir_x; - light_direction[lightid-1][1] = dir_y; - light_direction[lightid-1][2] = dir_z; - light_direction[lightid-1][3] = 1.00f; +static void rotate(VECTOR output, VECTOR input, Quat r) { + Quat v = {0.0f, input[0], input[1], input[2]}; - light_colour[lightid-1][0] = r; - light_colour[lightid-1][1] = g; - light_colour[lightid-1][2] = b; - light_colour[lightid-1][3] = 1.00f; + Quat conjR = {r.w, -r.x, -r.y, -r.z}; - light_type[lightid-1] = type; + Quat rotatedV = { + v.w * conjR.w - v.x * conjR.x - v.y * conjR.y - v.z * conjR.z, + v.w * conjR.x + v.x * conjR.w + v.y * conjR.z - v.z * conjR.y, + v.w * conjR.y - v.x * conjR.z + v.y * conjR.w + v.z * conjR.x, + v.w * conjR.z + v.x * conjR.y - v.y * conjR.x + v.z * conjR.w + }; + + output[0] = rotatedV.x; + output[1] = rotatedV.y; + output[2] = rotatedV.z; } -model* loadOBJ(const char* path, GSTEXTURE* text){ - // Opening model file and loading it on RAM - int file = open(path, O_RDONLY, 0777); - uint32_t size = lseek(file, 0, SEEK_END); - lseek(file, 0, SEEK_SET); - char* content = (char*)malloc(size+1); - read(file, content, size); - content[size] = 0; - - // Closing file - close(file); - - // Creating temp vertexList - rawVertexList* vl = (rawVertexList*)malloc(sizeof(rawVertexList)); - rawVertexList* init = vl; - - // Init variables - char* str = content; - char* ptr = strstr(str,"v "); - int idx; - char float_val[16]; - char* init_val; - char magics[3][3] = {"v ","vt","vn"}; - int magics_idx = 0; - vertex* res; - int v_idx = 0; - bool skip = false; - char* end_vert; - char* end_val; - float* vert_args; - rawVertexList* old_vl; - - // Vertices extraction - for(;;){ - - // Check if a magic change is needed - while (ptr == NULL){ - if (magics_idx < 2){ - res = init->vert; - vl = init; - magics_idx++; - ptr = strstr(str,magics[magics_idx]); - }else{ - skip = true; - break; - } - } - if (skip) break; - - // Extract vertex - if (magics_idx == 0) idx = 0; - else if (magics_idx == 1) idx = 3; - else idx = 5; - if (magics_idx == 0) init_val = ptr + 2; - else init_val = ptr + 3; - while (init_val[0] == ' ') init_val++; - end_vert = strstr(init_val,"\n"); - if (magics_idx == 0) res = (vertex*)malloc(sizeof(vertex)); - end_val = strstr(init_val," "); - vert_args = (float*)res; // Hacky way to iterate in vertex struct - while (init_val < end_vert){ - if (end_val > end_vert) end_val = end_vert; - strncpy(float_val, init_val, end_val - init_val); - float_val[end_val - init_val] = 0; - vert_args[idx] = atof(float_val); - idx++; - init_val = end_val + 1; - while (init_val[0] == ' ') init_val++; - end_val = strstr(init_val," "); - } - - // Update rawVertexList struct - if (magics_idx == 0){ - vl->vert = res; - vl->next = (rawVertexList*)malloc(sizeof(rawVertexList)); - } - old_vl = vl; - vl = vl->next; - if (magics_idx == 0){ - vl->vert = NULL; - vl->next = NULL; - }else{ - if (vl == NULL){ - old_vl->next = (rawVertexList*)malloc(sizeof(rawVertexList)); - vl = old_vl->next; - vl->vert = (vertex*)malloc(sizeof(vertex)); - vl->next = NULL; - }else if(vl->vert == NULL) vl->vert = (vertex*)malloc(sizeof(vertex)); - res = vl->vert; - } - - // Searching for next vertex - str = ptr + 1; - ptr = strstr(str,magics[magics_idx]); - +void turnCamera(float yaw, float pitch) +{ + VECTOR dir; + SubVector(dir, camera_target, camera_position); + + VECTOR dy = {0.0f, 1.0f, 0.0f, 1.0f}; + + Quat r = Quat_rotation(yaw, dy); + rotate(dir, dir, r); + rotate(local_up, local_up, r); + + VECTOR right; + OuterProduct(right, dir, local_up); + Normalize(right, right); + + r = Quat_rotation(pitch, right); + rotate(dir, dir, r); + + OuterProduct(local_up, right, dir); + Normalize(local_up, local_up); + + if(local_up[1] >= 0.0f) { + camera_up[1] = 1.0f; + } else { + camera_up[1] = -1.0f; } - // Creating real RAW vertexList - ptr = strstr(str, "f "); - rawVertexList* faces = (rawVertexList*)malloc(sizeof(rawVertexList)); - rawVertexList* initFaces = faces; - faces->vert = NULL; - faces->next = NULL; - int len = 0; - char val[8]; - int f_idx; - char* ptr2; - int t_idx; - rawVertexList* tmp; + AddVector(camera_target, camera_position, dir); +} + +void orbitCamera(float yaw, float pitch) +{ + VECTOR dir; + SubVector(dir, camera_target, camera_position); + + VECTOR dy = {0.0f, 1.0f, 0.0f, 1.0f}; + + Quat r = Quat_rotation(yaw, dy); + rotate(dir, dir, r); + rotate(local_up, local_up, r); + + VECTOR right; + OuterProduct(right, dir, local_up); + Normalize(right, right); + + r = Quat_rotation(-pitch, right); + rotate(dir, dir, r); + + OuterProduct(local_up, right, dir); + Normalize(local_up, local_up); + + if(local_up[1] >= 0.0f) { + camera_up[1] = 1.0f; + } else { + camera_up[1] = -1.0f; + } + + SubVector(camera_position, camera_target, dir); +} + +void dollyCamera(float dist) +{ + VECTOR dir; + SubVector(dir, camera_target, camera_position); + SetLenVector(dir, dist); + + AddVector(camera_position, camera_position, dir); + AddVector(camera_target, camera_target, dir); +} + +void zoomCamera(float dist) +{ + VECTOR dir; + SubVector(dir, camera_target, camera_position); + float curdist = LenVector(dir); + + if(dist >= curdist) + dist = curdist - 0.01f; + + SetLenVector(dir, dist); + + AddVector(camera_position, camera_position, dir); +} + +void panCamera(float x, float y) +{ + VECTOR dir; + SubVector(dir, camera_target, camera_position); + Normalize(dir, dir); + + VECTOR right; + OuterProduct(right, dir, camera_up); + Normalize(right, right); - // Faces extraction - while (ptr != NULL){ - - // Skipping padding - ptr+=2; - - // Extracting face info - f_idx = 0; - while (f_idx < 3){ - - // Allocating new vertex - faces->vert = (vertex*)malloc(sizeof(vertex)); - - // Extracting x,y,z - ptr2 = strstr(ptr,"/"); - strncpy(val,ptr,ptr2-ptr); - val[ptr2-ptr] = 0; - v_idx = atoi(val); - t_idx = 1; - tmp = init; - while (t_idx < v_idx){ - tmp = tmp->next; - t_idx++; - } - faces->vert->x = tmp->vert->x; - faces->vert->y = tmp->vert->y; - faces->vert->z = tmp->vert->z; - - // Extracting texture info - ptr = ptr2+1; - ptr2 = strstr(ptr,"/"); - if (ptr2 != ptr){ - strncpy(val,ptr,ptr2-ptr); - val[ptr2-ptr] = 0; - v_idx = atoi(val); - t_idx = 1; - tmp = init; - while (t_idx < v_idx){ - tmp = tmp->next; - t_idx++; - } - faces->vert->t1 = tmp->vert->t1; - faces->vert->t2 = 1.0f - tmp->vert->t2; - }else{ - faces->vert->t1 = 0.0f; - faces->vert->t2 = 0.0f; + OuterProduct(local_up, right, dir); + Normalize(local_up, local_up); + + VECTOR work0, work1; + ScaleVector(work0, right, x); + ScaleVector(work1, local_up, y); + + AddVector(dir, work0, work1); + + AddVector(camera_position, camera_position, dir); + AddVector(camera_target, camera_target, dir); +} + + +void SetLightAttribute(int id, float x, float y, float z, int attr){ + switch (attr) { + case ATHENA_LIGHT_DIRECTION: + lights.direction[id][0] = x; + lights.direction[id][1] = y; + lights.direction[id][2] = z; + lights.direction[id][3] = 1.00f; + break; + case ATHENA_LIGHT_AMBIENT: + lights.ambient[id][0] = x; + lights.ambient[id][1] = y; + lights.ambient[id][2] = z; + lights.ambient[id][3] = 1.00f; + break; + case ATHENA_LIGHT_DIFFUSE: + lights.diffuse[id][0] = x; + lights.diffuse[id][1] = y; + lights.diffuse[id][2] = z; + lights.diffuse[id][3] = 1.00f; + break; + case ATHENA_LIGHT_SPECULAR: + lights.specular[id][0] = x; + lights.specular[id][1] = y; + lights.specular[id][2] = z; + lights.specular[id][3] = 1.00f; + break; + } +} + +void draw_vu1_notex(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z); +void draw_vu1(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z); + +void draw_vu1_with_colors_notex(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z); +void draw_vu1_with_colors(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z); + +void draw_vu1_with_lights_notex(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z); +void draw_vu1_with_lights(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z); + +int athena_render_set_pipeline(model* m, int pl_id) { + switch (pl_id) { + case PL_NO_LIGHTS_COLORS: + if (m->tex_count) { + m->render = draw_vu1; + m->pipeline = PL_NO_LIGHTS_COLORS; + } else { + m->render = draw_vu1_notex; + m->pipeline = PL_NO_LIGHTS_COLORS_TEX; } - - // Extracting normals info - ptr = ptr2+1; - if (f_idx < 2) ptr2 = strstr(ptr," "); - else{ - ptr2 = strstr(ptr,"\n"); - if (ptr2 == NULL) ptr2 = content + size; + break; + case PL_NO_LIGHTS_COLORS_TEX: + m->render = draw_vu1_notex; + m->pipeline = PL_NO_LIGHTS_COLORS_TEX; + break; + case PL_NO_LIGHTS: + if (m->tex_count) { + m->render = draw_vu1_with_colors; + m->pipeline = PL_NO_LIGHTS; + } else { + m->render = draw_vu1_with_colors_notex; + m->pipeline = PL_NO_LIGHTS_TEX; } - strncpy(val,ptr,ptr2-ptr); - val[ptr2-ptr] = 0; - v_idx = atoi(val); - t_idx = 1; - tmp = init; - while (t_idx < v_idx){ - tmp = tmp->next; - t_idx++; + break; + case PL_NO_LIGHTS_TEX: + m->render = draw_vu1_with_colors_notex; + m->pipeline = PL_NO_LIGHTS_TEX; + break; + case PL_DEFAULT: + if (m->tex_count) { + m->render = draw_vu1_with_lights; + m->pipeline = PL_DEFAULT; + } else { + m->render = draw_vu1_with_lights_notex; + m->pipeline = PL_DEFAULT_NO_TEX; } - faces->vert->n1 = tmp->vert->n1; - faces->vert->n2 = tmp->vert->n2; - faces->vert->n3 = tmp->vert->n3; - - // Setting values for next vertex - ptr = ptr2; - faces->next = (rawVertexList*)malloc(sizeof(rawVertexList)); - faces = faces->next; - faces->next = NULL; - faces->vert = NULL; - len++; - f_idx++; - } - - ptr = strstr(ptr,"f "); - - } - - // Freeing temp vertexList and allocated file - free(content); - rawVertexList* tmp_init; - while (init != NULL){ - tmp_init = init; - free(init->vert); - init = init->next; - free(tmp_init); + break; + case PL_DEFAULT_NO_TEX: + m->render = draw_vu1_with_lights_notex; + m->pipeline = PL_DEFAULT_NO_TEX; + break; } - - // Create the model struct and populating vertex list - model* res_m = (model*)malloc(sizeof(model)); - vertexList* vlist = (vertexList*)malloc(sizeof(vertexList)); - vertexList* vlist_start = vlist; - vlist->next = NULL; - bool first = true; - for(int i = 0; i < len; i+=3) { - if (first) first = false; - else{ - vlist->next = (vertexList*)malloc(sizeof(vertexList)); - vlist = vlist->next; - vlist->next = NULL; + return m->pipeline; +} + +void loadOBJ(model* res_m, const char* path, GSTEXTURE* text) { + fastObjMesh* m = fast_obj_read(path); + + int positionCount = m->position_count; + int texcoordCount = m->texcoord_count; + int normalCount = m->normal_count; + int indexCount = m->index_count; + + VECTOR* c_verts = (VECTOR*)malloc(positionCount * sizeof(VECTOR)); + VECTOR* c_texcoords = (VECTOR*)malloc(texcoordCount * sizeof(VECTOR)); + VECTOR* c_normals = (VECTOR*)malloc(normalCount * sizeof(VECTOR)); + VECTOR* c_colours = (VECTOR*)malloc(indexCount * sizeof(VECTOR)); + + for (int i = 0; i < positionCount; i++) { + memcpy(c_verts[i], m->positions + (3 * i), sizeof(VECTOR)); + c_verts[i][3] = 1.0f; + } + + for (int i = 0; i < normalCount; i++) { + memcpy(c_normals[i], m->normals + (3 * i), sizeof(VECTOR)); + c_normals[i][3] = 1.0f; + } + + float oneMinusTexcoord; + for (int i = 0; i < texcoordCount; i++) { + c_texcoords[i][0] = m->texcoords[(2 * i) + 0]; + oneMinusTexcoord = 1.0f - m->texcoords[(2 * i) + 1]; + c_texcoords[i][1] = oneMinusTexcoord; + c_texcoords[i][2] = 1.0f; + c_texcoords[i][3] = 1.0f; + } + + res_m->facesCount = m->face_count; + res_m->indexCount = m->index_count; + + res_m->positions = (VECTOR*)malloc(indexCount * sizeof(VECTOR)); + res_m->texcoords = (VECTOR*)malloc(indexCount * sizeof(VECTOR)); + res_m->normals = (VECTOR*)malloc(indexCount * sizeof(VECTOR)); + res_m->colours = (VECTOR*)malloc(indexCount * sizeof(VECTOR)); + + int faceMaterialIndex; + char* oldTex = NULL; + char* curTex = NULL; + + res_m->tex_count = 0; + res_m->textures = NULL; + res_m->tex_ranges = NULL; + + for (int i = 0, j = 0; i < indexCount; i++, j += 3) { + int vertIndex = m->indices[i].p; + int texcoordIndex = m->indices[i].t; + int normalIndex = m->indices[i].n; + + memcpy(&res_m->positions[i], &c_verts[vertIndex], sizeof(VECTOR)); + memcpy(&res_m->texcoords[i], &c_texcoords[texcoordIndex], sizeof(VECTOR)); + memcpy(&res_m->normals[i], &c_normals[normalIndex], sizeof(VECTOR)); + + if(m->material_count > 0) { + faceMaterialIndex = m->face_materials[i / 3]; + + if(oldTex != m->materials[faceMaterialIndex].map_Kd.name) { + + curTex = m->materials[faceMaterialIndex].map_Kd.name; + oldTex = curTex; + + res_m->textures = realloc(res_m->textures, sizeof(GSTEXTURE*)*(res_m->tex_count+1)); + res_m->tex_ranges = realloc(res_m->tex_ranges, sizeof(int)*(res_m->tex_count+1)); + + res_m->textures[res_m->tex_count] = malloc(sizeof(GSTEXTURE)); + + load_image(res_m->textures[res_m->tex_count], curTex, true); + + res_m->tex_ranges[res_m->tex_count] = i; + res_m->tex_count++; + + } else if (m->materials[faceMaterialIndex].map_Kd.name) { + res_m->tex_ranges[res_m->tex_count-1] = i; + } + + c_colours[i][0] = m->materials[faceMaterialIndex].Kd[0]; + c_colours[i][1] = m->materials[faceMaterialIndex].Kd[1]; + c_colours[i][2] = m->materials[faceMaterialIndex].Kd[2]; + c_colours[i][3] = 1.0f; + } else { + c_colours[i][0] = 1.0f; + c_colours[i][1] = 1.0f; + c_colours[i][2] = 1.0f; + c_colours[i][3] = 1.0f; } - tmp_init = initFaces; - memcpy(&vlist->v1,initFaces->vert,sizeof(vertex)); - initFaces = initFaces->next; - free(tmp_init->vert); - free(tmp_init); - tmp_init = initFaces; - memcpy(&vlist->v2,initFaces->vert,sizeof(vertex)); - initFaces = initFaces->next; - free(tmp_init->vert); - free(tmp_init); - tmp_init = initFaces; - memcpy(&vlist->v3,initFaces->vert,sizeof(vertex)); - initFaces = initFaces->next; - free(tmp_init->vert); - free(tmp_init); - } - res_m->facesCount = len / 3; - - - // Setting texture - res_m->texture = text; - - vlist = vlist_start; - res_m->positions = (VECTOR*)memalign(128, res_m->facesCount * 3 * sizeof(VECTOR)); - res_m->texcoords = (VECTOR*)memalign(128, res_m->facesCount * 3 * sizeof(VECTOR)); - res_m->normals = (VECTOR*)memalign(128, res_m->facesCount * 3 * sizeof(VECTOR)); - res_m->colours = (VECTOR*)memalign(128, res_m->facesCount * 3 * sizeof(VECTOR)); - res_m->idxList = (uint16_t*)memalign(128, res_m->facesCount * 3 * sizeof(uint16_t)); - vertexList* object = vlist; - int n = 0; - while (object != NULL){ - - //v1 - res_m->positions[n][0] = object->v1.x; - res_m->positions[n][1] = object->v1.y; - res_m->positions[n][2] = object->v1.z; - res_m->positions[n][3] = 1.000f; - - res_m->normals[n][0] = object->v1.n1; - res_m->normals[n][1] = object->v1.n2; - res_m->normals[n][2] = object->v1.n3; - res_m->normals[n][3] = 1.000f; - - res_m->texcoords[n][0] = object->v1.t1; - res_m->texcoords[n][1] = object->v1.t2; - res_m->texcoords[n][2] = 0.000f; - res_m->texcoords[n][3] = 0.000f; - - res_m->colours[n][0] = 1.000f; - res_m->colours[n][1] = 1.000f; - res_m->colours[n][2] = 1.000f; - res_m->colours[n][3] = 1.000f; - - //v2 - res_m->positions[n+1][0] = object->v2.x; - res_m->positions[n+1][1] = object->v2.y; - res_m->positions[n+1][2] = object->v2.z; - res_m->positions[n+1][3] = 1.000f; - - res_m->normals[n+1][0] = object->v2.n1; - res_m->normals[n+1][1] = object->v2.n2; - res_m->normals[n+1][2] = object->v2.n3; - res_m->normals[n+1][3] = 1.000f; - - res_m->texcoords[n+1][0] = object->v2.t1; - res_m->texcoords[n+1][1] = object->v2.t2; - res_m->texcoords[n+1][2] = 0.000f; - res_m->texcoords[n+1][3] = 0.000f; - - res_m->colours[n+1][0] = 1.000f; - res_m->colours[n+1][1] = 1.000f; - res_m->colours[n+1][2] = 1.000f; - res_m->colours[n+1][3] = 1.000f; - - //v3 - res_m->positions[n+2][0] = object->v3.x; - res_m->positions[n+2][1] = object->v3.y; - res_m->positions[n+2][2] = object->v3.z; - res_m->positions[n+2][3] = 1.000f; - - res_m->normals[n+2][0] = object->v3.n1; - res_m->normals[n+2][1] = object->v3.n2; - res_m->normals[n+2][2] = object->v3.n3; - res_m->normals[n+2][3] = 1.000f; - - res_m->texcoords[n+2][0] = object->v3.t1; - res_m->texcoords[n+2][1] = object->v3.t2; - res_m->texcoords[n+2][2] = 0.000f; - res_m->texcoords[n+2][3] = 0.000f; - - res_m->colours[n+2][0] = 1.000f; - res_m->colours[n+2][1] = 1.000f; - res_m->colours[n+2][2] = 1.000f; - res_m->colours[n+2][3] = 1.000f; - - res_m->idxList[n] = n; - res_m->idxList[n+1] = n+1; - res_m->idxList[n+2] = n+2; - object = object->next; - n += 3; - } - while (vlist != NULL){ - vertexList* old = vlist; - vlist = vlist->next; - free(old); - } + } - VECTOR* c_verts = (VECTOR*)memalign(128, res_m->facesCount * 3 * sizeof(VECTOR)); - VECTOR* c_texcoords = (VECTOR*)memalign(128, res_m->facesCount * 3 * sizeof(VECTOR)); - VECTOR* c_normals = (VECTOR*)memalign(128, res_m->facesCount * 3 * sizeof(VECTOR)); + memcpy(res_m->colours, c_colours, indexCount * sizeof(VECTOR)); - for (int i = 0; i < res_m->facesCount*3; i++) - { - memcpy(&c_verts[i], &res_m->positions[res_m->idxList[i]], sizeof(VECTOR)); - memcpy(&c_texcoords[i], &res_m->texcoords[res_m->idxList[i]], sizeof(VECTOR)); - memcpy(&c_normals[i], &res_m->normals[res_m->idxList[i]], sizeof(VECTOR)); - } + free(c_verts); + free(c_colours); + free(c_normals); + free(c_texcoords); + + float lowX, lowY, lowZ, hiX, hiY, hiZ; + lowX = hiX = res_m->positions[0][0]; + lowY = hiY = res_m->positions[0][1]; + lowZ = hiZ = res_m->positions[0][2]; - //calculate bounding box - float lowX, lowY, lowZ, hiX, hiY, hiZ; - lowX = hiX = res_m->positions[res_m->idxList[0]][0]; - lowY = hiY = res_m->positions[res_m->idxList[0]][1]; - lowZ = hiZ = res_m->positions[res_m->idxList[0]][2]; - for (int i = 0; i < res_m->facesCount; i++) - { - if (lowX > res_m->positions[res_m->idxList[i]][0]) lowX = res_m->positions[res_m->idxList[i]][0]; - if (hiX < res_m->positions[res_m->idxList[i]][0]) hiX = res_m->positions[res_m->idxList[i]][0]; - if (lowY > res_m->positions[res_m->idxList[i]][1]) lowY = res_m->positions[res_m->idxList[i]][1]; - if (hiY < res_m->positions[res_m->idxList[i]][1]) hiY = res_m->positions[res_m->idxList[i]][1]; - if (lowZ > res_m->positions[res_m->idxList[i]][2]) lowZ = res_m->positions[res_m->idxList[i]][2]; - if (hiZ < res_m->positions[res_m->idxList[i]][2]) hiZ = res_m->positions[res_m->idxList[i]][2]; + for (int i = 0; i < res_m->indexCount; i++) { + float* pos = res_m->positions[i]; + lowX = fmin(lowX, pos[0]); + hiX = fmax(hiX, pos[0]); + lowY = fmin(lowY, pos[1]); + hiY = fmax(hiY, pos[1]); + lowZ = fmin(lowZ, pos[2]); + hiZ = fmax(hiZ, pos[2]); } - res_m->bounding_box = (VECTOR*)malloc(sizeof(VECTOR)*8); - - res_m->bounding_box[0][0] = lowX; - res_m->bounding_box[0][1] = lowY; - res_m->bounding_box[0][2] = lowZ; - res_m->bounding_box[0][3] = 1.00f; - - res_m->bounding_box[1][0] = lowX; - res_m->bounding_box[1][1] = lowY; - res_m->bounding_box[1][2] = hiZ; - res_m->bounding_box[1][3] = 1.00f; - - res_m->bounding_box[2][0] = lowX; - res_m->bounding_box[2][1] = hiY; - res_m->bounding_box[2][2] = lowZ; - res_m->bounding_box[2][3] = 1.00f; - - res_m->bounding_box[3][0] = lowX; - res_m->bounding_box[3][1] = hiY; - res_m->bounding_box[3][2] = hiZ; - res_m->bounding_box[3][3] = 1.00f; - - res_m->bounding_box[4][0] = hiX; - res_m->bounding_box[4][1] = lowY; - res_m->bounding_box[4][2] = lowZ; - res_m->bounding_box[4][3] = 1.00f; - - res_m->bounding_box[5][0] = hiX; - res_m->bounding_box[5][1] = lowY; - res_m->bounding_box[5][2] = hiZ; - res_m->bounding_box[5][3] = 1.00f; - - res_m->bounding_box[6][0] = hiX; - res_m->bounding_box[6][1] = hiY; - res_m->bounding_box[6][2] = lowZ; - res_m->bounding_box[6][3] = 1.00f; - - res_m->bounding_box[7][0] = hiX; - res_m->bounding_box[7][1] = hiY; - res_m->bounding_box[7][2] = hiZ; - res_m->bounding_box[7][3] = 1.00f; - - free(res_m->positions); - free(res_m->normals); - free(res_m->texcoords); - - res_m->positions = c_verts; - res_m->normals = c_normals; - res_m->texcoords = c_texcoords; - - return res_m; + VECTOR bbox[8] = { + {lowX, lowY, lowZ, 1.0f}, {lowX, lowY, hiZ, 1.0f}, {lowX, hiY, lowZ, 1.0f}, {lowX, hiY, hiZ, 1.0f}, + {hiX, lowY, lowZ, 1.0f}, {hiX, lowY, hiZ, 1.0f}, {hiX, hiY, lowZ, 1.0f}, {hiX, hiY, hiZ, 1.0f} + }; + + memcpy(res_m->bounding_box, bbox, sizeof(bbox)); + + if(text) { + res_m->textures = malloc(sizeof(GSTEXTURE*)); + res_m->tex_ranges = malloc(sizeof(int)); + + res_m->textures[0] = text; + res_m->tex_count = 1; + res_m->tex_ranges[0] = res_m->indexCount; + res_m->render = draw_vu1_with_lights; + res_m->pipeline = PL_DEFAULT; + } else if (res_m->tex_count > 0) { + res_m->render = draw_vu1_with_lights; + res_m->pipeline = PL_DEFAULT; + } else { + res_m->render = draw_vu1_with_lights_notex; + res_m->pipeline = PL_DEFAULT_NO_TEX; + } + + fast_obj_destroy(m); } @@ -495,216 +476,784 @@ void draw_bbox(model* m, float pos_x, float pos_y, float pos_z, float rot_x, flo // Matrices to setup the 3D environment and camera MATRIX local_world; - MATRIX world_view; MATRIX local_screen; create_local_world(local_world, object_position, object_rotation); - create_world_view(world_view, camera_position, camera_rotation); create_local_screen(local_screen, local_world, world_view, view_screen); if(clip_bounding_box(local_screen, m->bounding_box)) return; - vertex_f_t *t_xyz = (vertex_f_t *)memalign(128, sizeof(vertex_f_t)*8); - calculate_vertices_clipped((VECTOR *)t_xyz, 8, m->bounding_box, local_screen); - - xyz_t *xyz = (xyz_t *)memalign(128, sizeof(xyz_t)*8); - draw_convert_xyz(xyz, 2048, 2048, 16, 8, t_xyz); - - float fX=gsGlobal->Width/2; - float fY=gsGlobal->Height/2; - - gsKit_prim_line_3d(gsGlobal, (t_xyz[0].x+1.0f)*fX, (t_xyz[0].y+1.0f)*fY, xyz[0].z, (t_xyz[1].x+1.0f)*fX, (t_xyz[1].y+1.0f)*fY, xyz[1].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[1].x+1.0f)*fX, (t_xyz[1].y+1.0f)*fY, xyz[1].z, (t_xyz[3].x+1.0f)*fX, (t_xyz[3].y+1.0f)*fY, xyz[3].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[2].x+1.0f)*fX, (t_xyz[2].y+1.0f)*fY, xyz[2].z, (t_xyz[3].x+1.0f)*fX, (t_xyz[3].y+1.0f)*fY, xyz[3].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[0].x+1.0f)*fX, (t_xyz[0].y+1.0f)*fY, xyz[0].z, (t_xyz[2].x+1.0f)*fX, (t_xyz[2].y+1.0f)*fY, xyz[2].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[4].x+1.0f)*fX, (t_xyz[4].y+1.0f)*fY, xyz[4].z, (t_xyz[5].x+1.0f)*fX, (t_xyz[5].y+1.0f)*fY, xyz[5].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[5].x+1.0f)*fX, (t_xyz[5].y+1.0f)*fY, xyz[5].z, (t_xyz[7].x+1.0f)*fX, (t_xyz[7].y+1.0f)*fY, xyz[7].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[6].x+1.0f)*fX, (t_xyz[6].y+1.0f)*fY, xyz[6].z, (t_xyz[7].x+1.0f)*fX, (t_xyz[7].y+1.0f)*fY, xyz[7].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[4].x+1.0f)*fX, (t_xyz[4].y+1.0f)*fY, xyz[4].z, (t_xyz[6].x+1.0f)*fX, (t_xyz[6].y+1.0f)*fY, xyz[6].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[0].x+1.0f)*fX, (t_xyz[0].y+1.0f)*fY, xyz[0].z, (t_xyz[4].x+1.0f)*fX, (t_xyz[4].y+1.0f)*fY, xyz[4].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[1].x+1.0f)*fX, (t_xyz[1].y+1.0f)*fY, xyz[1].z, (t_xyz[5].x+1.0f)*fX, (t_xyz[5].y+1.0f)*fY, xyz[5].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[2].x+1.0f)*fX, (t_xyz[2].y+1.0f)*fY, xyz[2].z, (t_xyz[6].x+1.0f)*fX, (t_xyz[6].y+1.0f)*fY, xyz[6].z, color); - gsKit_prim_line_3d(gsGlobal, (t_xyz[3].x+1.0f)*fX, (t_xyz[3].y+1.0f)*fY, xyz[3].z, (t_xyz[7].x+1.0f)*fX, (t_xyz[7].y+1.0f)*fY, xyz[7].z, color); - - free(t_xyz); + vertex_f_t *xyz = (vertex_f_t *)memalign(128, sizeof(vertex_f_t)*8); + calculate_vertices_clipped((VECTOR *)xyz, 8, m->bounding_box, local_screen); + + athena_line_goraud_3d(gsGlobal, xyz[0].x, xyz[0].y, xyz[0].z, xyz[1].x, xyz[1].y, xyz[1].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[1].x, xyz[1].y, xyz[1].z, xyz[3].x, xyz[3].y, xyz[3].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[2].x, xyz[2].y, xyz[2].z, xyz[3].x, xyz[3].y, xyz[3].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[0].x, xyz[0].y, xyz[0].z, xyz[2].x, xyz[2].y, xyz[2].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[4].x, xyz[4].y, xyz[4].z, xyz[5].x, xyz[5].y, xyz[5].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[5].x, xyz[5].y, xyz[5].z, xyz[7].x, xyz[7].y, xyz[7].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[6].x, xyz[6].y, xyz[6].z, xyz[7].x, xyz[7].y, xyz[7].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[4].x, xyz[4].y, xyz[4].z, xyz[6].x, xyz[6].y, xyz[6].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[0].x, xyz[0].y, xyz[0].z, xyz[4].x, xyz[4].y, xyz[4].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[1].x, xyz[1].y, xyz[1].z, xyz[5].x, xyz[5].y, xyz[5].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[2].x, xyz[2].y, xyz[2].z, xyz[6].x, xyz[6].y, xyz[6].z, color, color); + athena_line_goraud_3d(gsGlobal, xyz[3].x, xyz[3].y, xyz[3].z, xyz[7].x, xyz[7].y, xyz[7].z, color, color); + free(xyz); } -int athena_process_xyz_rgbaq(GSPRIMPOINT *output, GSGLOBAL* gsGlobal, int count, color_f_t *colours, vertex_f_t *vertices) + +u64 vif_packets[2][44] __attribute__((aligned(64))); +u64* curr_vif_packet; + +/** Cube data */ +u64 cube_packet[12] __attribute__((aligned(64))); + +u8 context = 0; + +static u32* last_mpg = NULL; + +void draw_vu1(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z) { + VECTOR object_position = { pos_x, pos_y, pos_z, 1.00f }; + VECTOR object_rotation = { rot_x, rot_y, rot_z, 1.00f }; - int z; - unsigned int max_z; - float q = 1.00f; + MATRIX local_world; + MATRIX local_screen; - switch(gsGlobal->PSMZ){ - case GS_PSMZ_32: - z = 32; - break; + GSGLOBAL *gsGlobal = getGSGLOBAL(); - case GS_PSMZ_24: - z = 24; - break; + if (last_mpg != &VU1Draw3D_CodeStart) { + vu1_upload_micro_program(&VU1Draw3D_CodeStart, &VU1Draw3D_CodeEnd); + last_mpg = &VU1Draw3D_CodeStart; + } - case GS_PSMZ_16: - case GS_PSMZ_16S: - z = 16; - break; + gsGlobal->PrimAAEnable = GS_SETTING_ON; + gsKit_set_test(gsGlobal, GS_ZTEST_ON); + + create_local_world(local_world, object_position, object_rotation); + create_local_screen(local_screen, local_world, world_view, view_screen); + + int lastIdx = -1; + for(int i = 0; i < model_test->tex_count; i++) { + gsKit_TexManager_bind(gsGlobal, model_test->textures[i]); + + VECTOR* positions = &model_test->positions[lastIdx+1]; + VECTOR* texcoords = &model_test->texcoords[lastIdx+1]; + GSTEXTURE* tex = model_test->textures[i]; + + int idxs_to_draw = (model_test->tex_ranges[i]-lastIdx); + int idxs_drawn = 0; + + while (idxs_to_draw > 0) { + dmaKit_wait(DMA_CHANNEL_VIF1, 0); + + int count = BATCH_SIZE; + if (idxs_to_draw < BATCH_SIZE) + { + count = idxs_to_draw; + } + + float fX = 2048.0f+gsGlobal->Width/2; + float fY = 2048.0f+gsGlobal->Height/2; + float fZ = ((float)get_max_z(gsGlobal)); + + u64* p_data = cube_packet; + + *p_data++ = (*(u32*)(&fX) | (u64)*(u32*)(&fY) << 32); + *p_data++ = (*(u32*)(&fZ) | (u64)count << 32); - default: - return -1; + *p_data++ = GIF_TAG(1, 0, 0, 0, 0, 1); + *p_data++ = GIF_AD; + + *p_data++ = GS_SETREG_TEX1(1, 0, tex->Filter, tex->Filter, 0, 0, 0); + *p_data++ = GS_TEX1_1; + + int tw, th; + athena_set_tw_th(tex, &tw, &th); + + if(tex->VramClut == 0) + { + *p_data++ = GS_SETREG_TEX0(tex->Vram/256, tex->TBW, tex->PSM, + tw, th, gsGlobal->PrimAlphaEnable, 0, + 0, 0, 0, 0, GS_CLUT_STOREMODE_NOLOAD); + } + else + { + *p_data++ = GS_SETREG_TEX0(tex->Vram/256, tex->TBW, tex->PSM, + tw, th, gsGlobal->PrimAlphaEnable, 0, + tex->VramClut/256, tex->ClutPSM, 0, 0, GS_CLUT_STOREMODE_LOAD); + } + + *p_data++ = GS_TEX0_1; + + *p_data++ = VU_GS_GIFTAG(count, 1, 1, + VU_GS_PRIM(GS_PRIM_PRIM_TRIANGLE, 1, 1, gsGlobal->PrimFogEnable, + 0, gsGlobal->PrimAAEnable, 0, 0, 0), + 0, 3); + + *p_data++ = DRAW_STQ2_REGLIST; + + *p_data++ = (128 | (u64)128 << 32); + *p_data++ = (128 | (u64)128 << 32); + + curr_vif_packet = vif_packets[context]; + + //memset(curr_vif_packet, 0, 16*22); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32)); + + // Add matrix at the beggining of VU mem (skip TOP) + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, 0, &local_screen, 8, 0); + + u32 vif_added_bytes = 0; // zero because now we will use TOP register (double buffer) + // we don't wan't to unpack at 8 + beggining of buffer, but at + // the beggining of the buffer + + // Merge packets + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, cube_packet, 6, 1); + vif_added_bytes += 6; + + // Add vertices + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &positions[idxs_drawn], count, 1); + vif_added_bytes += count; // one VECTOR is size of qword + + // Add sts + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &texcoords[idxs_drawn], count, 1); + vif_added_bytes += count; + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_MSCALF, 0) << 32)); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_END, 0, 0 , 0); + *curr_vif_packet++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32); + + asm volatile("nop":::"memory"); + + vifSendPacket(vif_packets[context], DMA_CHANNEL_VIF1); + + idxs_to_draw -= count; + idxs_drawn += count; + } + + lastIdx = model_test->tex_ranges[i]; + } + + + // Switch packet, so we can proceed during DMA transfer + context = !context; +} + +void draw_vu1_notex(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z) +{ + VECTOR object_position = { pos_x, pos_y, pos_z, 1.00f }; + VECTOR object_rotation = { rot_x, rot_y, rot_z, 1.00f }; + + MATRIX local_world; + MATRIX local_screen; + + GSGLOBAL *gsGlobal = getGSGLOBAL(); + + if (last_mpg != &VU1Draw3DNoTex_CodeStart) { + vu1_upload_micro_program(&VU1Draw3DNoTex_CodeStart, &VU1Draw3DNoTex_CodeEnd); + last_mpg = &VU1Draw3DNoTex_CodeStart; } - int center_x = gsGlobal->Width/2; - int center_y = gsGlobal->Height/2; - max_z = 1 << (z - 1); + gsGlobal->PrimAAEnable = GS_SETTING_ON; + gsKit_set_test(gsGlobal, GS_ZTEST_ON); + + create_local_world(local_world, object_position, object_rotation); + create_local_screen(local_screen, local_world, world_view, view_screen); + + int idxs_to_draw = model_test->indexCount; + int idxs_drawn = 0; + + while (idxs_to_draw > 0) { + dmaKit_wait(DMA_CHANNEL_VIF1, 0); - for (int i = 0; i < count; i++) - { - // Calculate the Q value. - if (vertices[i].w != 0) + int count = BATCH_SIZE; + if (idxs_to_draw < BATCH_SIZE) { - q = 1 / vertices[i].w; + count = idxs_to_draw; } - output[i].rgbaq.color.r = (int)(colours[i].r * 128.0f); - output[i].rgbaq.color.g = (int)(colours[i].g * 128.0f); - output[i].rgbaq.color.b = (int)(colours[i].b * 128.0f); - output[i].rgbaq.color.a = 0x80; - output[i].rgbaq.color.q = q; - output[i].rgbaq.tag = GS_RGBAQ; + float fX = 2048.0f+gsGlobal->Width/2; + float fY = 2048.0f+gsGlobal->Height/2; + float fZ = ((float)get_max_z(gsGlobal)); + + float texCol = 128.0f; + + u64* p_data = cube_packet; + + *p_data++ = (*(u32*)(&fX) | (u64)*(u32*)(&fY) << 32); + *p_data++ = (*(u32*)(&fZ) | (u64)(count) << 32); + + *p_data++ = GIF_TAG(1, 0, 0, 0, 0, 1); + *p_data++ = GIF_AD; + + *p_data++ = VU_GS_GIFTAG(count, 1, 1, + VU_GS_PRIM(GS_PRIM_PRIM_TRIANGLE, 1, 0, gsGlobal->PrimFogEnable, + 0, gsGlobal->PrimAAEnable, 0, 0, 0), + 0, 2); + + *p_data++ = DRAW_NOTEX_REGLIST; + + *p_data++ = (128 | (u64)128 << 32); + *p_data++ = (128 | (u64)128 << 32); + + curr_vif_packet = vif_packets[context]; + + //memset(curr_vif_packet, 0, 16*22); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32)); + + // Add matrix at the beggining of VU mem (skip TOP) + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, 0, &local_screen, 8, 0); + + u32 vif_added_bytes = 0; // zero because now we will use TOP register (double buffer) + // we don't wan't to unpack at 8 + beggining of buffer, but at + // the beggining of the buffer + + // Merge packets + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, cube_packet, 4, 1); + vif_added_bytes += 4; + + // Add vertices + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &model_test->positions[idxs_drawn], count, 1); + vif_added_bytes += count; // one VECTOR is size of qword + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_MSCAL, 0) << 32)); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_END, 0, 0 , 0); + *curr_vif_packet++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32); + + asm volatile("nop":::"memory"); - output[i].xyz2.xyz.x = gsKit_float_to_int_x(gsGlobal, (vertices[i].x + 1.0f) * center_x); - output[i].xyz2.xyz.y = gsKit_float_to_int_y(gsGlobal, (vertices[i].y + 1.0f) * center_y); - output[i].xyz2.xyz.z = (unsigned int)((vertices[i].z + 1.0f) * max_z); - output[i].xyz2.tag = GS_XYZ2; + vifSendPacket(vif_packets[context], DMA_CHANNEL_VIF1); + idxs_to_draw -= count; + idxs_drawn += count; } - // End function. - return 0; + // Switch packet, so we can proceed during DMA transfer + context = !context; } -int athena_process_xyz_rgbaq_st(GSPRIMSTQPOINT *output, GSGLOBAL* gsGlobal, int count, color_f_t *colours, vertex_f_t *vertices, texel_f_t *coords) +void draw_vu1_with_colors(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z) { + VECTOR object_position = { pos_x, pos_y, pos_z, 1.00f }; + VECTOR object_rotation = { rot_x, rot_y, rot_z, 1.00f }; - int z; - unsigned int max_z; - float q = 1.00f; + MATRIX local_world; + MATRIX local_light; + MATRIX local_screen; - switch(gsGlobal->PSMZ){ - case GS_PSMZ_32: - z = 32; - break; + GSGLOBAL *gsGlobal = getGSGLOBAL(); - case GS_PSMZ_24: - z = 24; - break; + if (last_mpg != &VU1Draw3DColors_CodeStart) { + vu1_upload_micro_program(&VU1Draw3DColors_CodeStart, &VU1Draw3DColors_CodeEnd); + last_mpg = &VU1Draw3DColors_CodeStart; + } - case GS_PSMZ_16: - case GS_PSMZ_16S: - z = 16; - break; - - default: - return -1; + gsGlobal->PrimAAEnable = GS_SETTING_ON; + gsKit_set_test(gsGlobal, GS_ZTEST_ON); + + create_local_world(local_world, object_position, object_rotation); + create_local_screen(local_screen, local_world, world_view, view_screen); + + int lastIdx = -1; + for(int i = 0; i < model_test->tex_count; i++) { + gsKit_TexManager_bind(gsGlobal, model_test->textures[i]); + + VECTOR* positions = &model_test->positions[lastIdx+1]; + VECTOR* texcoords = &model_test->texcoords[lastIdx+1]; + VECTOR* colours = &model_test->colours[lastIdx+1]; + GSTEXTURE* tex = model_test->textures[i]; + + int idxs_to_draw = (model_test->tex_ranges[i]-lastIdx); + int idxs_drawn = 0; + + while (idxs_to_draw > 0) { + dmaKit_wait(DMA_CHANNEL_VIF1, 0); + + int count = BATCH_SIZE; + if (idxs_to_draw < BATCH_SIZE) + { + count = idxs_to_draw; + } + + float fX = 2048.0f+gsGlobal->Width/2; + float fY = 2048.0f+gsGlobal->Height/2; + float fZ = ((float)get_max_z(gsGlobal)); + + float texCol = 128.0f; + + u64* p_data = cube_packet; + + *p_data++ = (*(u32*)(&fX) | (u64)*(u32*)(&fY) << 32); + *p_data++ = (*(u32*)(&fZ) | (u64)(count) << 32); + + *p_data++ = GIF_TAG(1, 0, 0, 0, 0, 1); + *p_data++ = GIF_AD; + + *p_data++ = GS_SETREG_TEX1(1, 0, tex->Filter, tex->Filter, 0, 0, 0); + *p_data++ = GS_TEX1_1; + + int tw, th; + athena_set_tw_th(tex, &tw, &th); + + if(tex->VramClut == 0) + { + *p_data++ = GS_SETREG_TEX0(tex->Vram/256, tex->TBW, tex->PSM, + tw, th, gsGlobal->PrimAlphaEnable, 0, + 0, 0, 0, 0, GS_CLUT_STOREMODE_NOLOAD); + } + else + { + *p_data++ = GS_SETREG_TEX0(tex->Vram/256, tex->TBW, tex->PSM, + tw, th, gsGlobal->PrimAlphaEnable, 0, + tex->VramClut/256, tex->ClutPSM, 0, 0, GS_CLUT_STOREMODE_LOAD); + } + + *p_data++ = GS_TEX0_1; + + *p_data++ = VU_GS_GIFTAG(count, 1, 1, + VU_GS_PRIM(GS_PRIM_PRIM_TRIANGLE, 1, 1, gsGlobal->PrimFogEnable, + 0, gsGlobal->PrimAAEnable, 0, 0, 0), + 0, 3); + + *p_data++ = DRAW_STQ2_REGLIST; + + *p_data++ = (*(u32*)(&texCol) | (u64)*(u32*)(&texCol) << 32); + *p_data++ = (*(u32*)(&texCol) | (u64)*(u32*)(&texCol) << 32); + + curr_vif_packet = vif_packets[context]; + + //memset(curr_vif_packet, 0, 16*22); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32)); + + // Add matrix at the beggining of VU mem (skip TOP) + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, 0, &local_screen, 8, 0); + + u32 vif_added_bytes = 0; // zero because now we will use TOP register (double buffer) + // we don't wan't to unpack at 8 + beggining of buffer, but at + // the beggining of the buffer + + // Merge packets + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, cube_packet, 6, 1); + vif_added_bytes += 6; + + // Add vertices + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &positions[idxs_drawn], count, 1); + vif_added_bytes += count; // one VECTOR is size of qword + + // Add sts + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &texcoords[idxs_drawn], count, 1); + vif_added_bytes += count; + + // Add colors + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &colours[idxs_drawn], count, 1); + vif_added_bytes += count; + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_MSCAL, 0) << 32)); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_END, 0, 0 , 0); + *curr_vif_packet++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32); + + asm volatile("nop":::"memory"); + + vifSendPacket(vif_packets[context], DMA_CHANNEL_VIF1); + + idxs_to_draw -= count; + idxs_drawn += count; + } + + lastIdx = model_test->tex_ranges[i]; } - int center_x = gsGlobal->Width/2; - int center_y = gsGlobal->Height/2; - max_z = 1 << (z - 1); + // Switch packet, so we can proceed during DMA transfer + context = !context; +} + + +void draw_vu1_with_colors_notex(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z) +{ + VECTOR object_position = { pos_x, pos_y, pos_z, 1.00f }; + VECTOR object_rotation = { rot_x, rot_y, rot_z, 1.00f }; + + MATRIX local_world; + MATRIX local_light; + MATRIX local_screen; - for (int i = 0; i < count; i++) - { - // Calculate the Q value. - if (vertices[i].w != 0) + GSGLOBAL *gsGlobal = getGSGLOBAL(); + + if (last_mpg != &VU1Draw3DColorsNoTex_CodeStart) { + vu1_upload_micro_program(&VU1Draw3DColorsNoTex_CodeStart, &VU1Draw3DColorsNoTex_CodeEnd); + last_mpg = &VU1Draw3DColorsNoTex_CodeStart; + } + + gsGlobal->PrimAAEnable = GS_SETTING_ON; + gsKit_set_test(gsGlobal, GS_ZTEST_ON); + + create_local_world(local_world, object_position, object_rotation); + create_local_screen(local_screen, local_world, world_view, view_screen); + + int idxs_to_draw = model_test->indexCount; + int idxs_drawn = 0; + + while (idxs_to_draw > 0) { + dmaKit_wait(DMA_CHANNEL_VIF1, 0); + + int count = BATCH_SIZE; + if (idxs_to_draw < BATCH_SIZE) { - q = 1 / vertices[i].w; + count = idxs_to_draw; } - output[i].rgbaq.color.r = (int)(colours[i].r * 128.0f); - output[i].rgbaq.color.g = (int)(colours[i].g * 128.0f); - output[i].rgbaq.color.b = (int)(colours[i].b * 128.0f); - output[i].rgbaq.color.a = 0x80; - output[i].rgbaq.color.q = q; - output[i].rgbaq.tag = GS_RGBAQ; + float fX = 2048.0f+gsGlobal->Width/2; + float fY = 2048.0f+gsGlobal->Height/2; + float fZ = ((float)get_max_z(gsGlobal)); - output[i].stq.st.s = coords[i].s * q; - output[i].stq.st.t = coords[i].t * q; - output[i].stq.tag = GS_ST; + float texCol = 128.0f; - output[i].xyz2.xyz.x = gsKit_float_to_int_x(gsGlobal, (vertices[i].x + 1.0f) * center_x); - output[i].xyz2.xyz.y = gsKit_float_to_int_y(gsGlobal, (vertices[i].y + 1.0f) * center_y); - output[i].xyz2.xyz.z = (unsigned int)((vertices[i].z + 1.0f) * max_z); - output[i].xyz2.tag = GS_XYZ2; + u64* p_data = cube_packet; - } + *p_data++ = (*(u32*)(&fX) | (u64)*(u32*)(&fY) << 32); + *p_data++ = (*(u32*)(&fZ) | (u64)(count) << 32); - // End function. - return 0; + *p_data++ = GIF_TAG(1, 0, 0, 0, 0, 1); + *p_data++ = GIF_AD; + *p_data++ = VU_GS_GIFTAG(count, 1, 1, + VU_GS_PRIM(GS_PRIM_PRIM_TRIANGLE, 1, 0, gsGlobal->PrimFogEnable, + 0, gsGlobal->PrimAAEnable, 0, 0, 0), + 0, 2); + + *p_data++ = DRAW_NOTEX_REGLIST; + + *p_data++ = (*(u32*)(&texCol) | (u64)*(u32*)(&texCol) << 32); + *p_data++ = (*(u32*)(&texCol) | (u64)*(u32*)(&texCol) << 32); + + curr_vif_packet = vif_packets[context]; + + //memset(curr_vif_packet, 0, 16*22); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32)); + + // Add matrix at the beggining of VU mem (skip TOP) + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, 0, &local_screen, 8, 0); + + u32 vif_added_bytes = 0; // zero because now we will use TOP register (double buffer) + // we don't wan't to unpack at 8 + beggining of buffer, but at + // the beggining of the buffer + + // Merge packets + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, cube_packet, 4, 1); + vif_added_bytes += 4; + + // Add vertices + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &model_test->positions[idxs_drawn], count, 1); + vif_added_bytes += count; // one VECTOR is size of qword + + // Add colors + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &model_test->colours[idxs_drawn], count, 1); + vif_added_bytes += count; + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_MSCAL, 0) << 32)); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_END, 0, 0 , 0); + *curr_vif_packet++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32); + + asm volatile("nop":::"memory"); + + vifSendPacket(vif_packets[context], DMA_CHANNEL_VIF1); + + idxs_to_draw -= count; + idxs_drawn += count; + } + + // Switch packet, so we can proceed during DMA transfer + context = !context; } -void drawOBJ(model* m, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z) +void draw_vu1_with_lights(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z) { + GSGLOBAL *gsGlobal = getGSGLOBAL(); + + if (last_mpg != &VU1Draw3DLightsColors_CodeStart) { + vu1_upload_micro_program(&VU1Draw3DLightsColors_CodeStart, &VU1Draw3DLightsColors_CodeEnd); + last_mpg = &VU1Draw3DLightsColors_CodeStart; + } + + gsGlobal->PrimAAEnable = GS_SETTING_ON; + gsKit_set_test(gsGlobal, GS_ZTEST_ON); + VECTOR object_position = { pos_x, pos_y, pos_z, 1.00f }; VECTOR object_rotation = { rot_x, rot_y, rot_z, 1.00f }; - GSGLOBAL *gsGlobal = getGSGLOBAL(); + MATRIX local_world; + MATRIX local_light; + MATRIX local_screen; + + // Create the local_world matrix. + matrix_unit(local_world); + matrix_rotate(local_world, local_world, object_rotation); + matrix_translate(local_world, local_world, object_position); + + // Create the local_light matrix. + matrix_unit(local_light); + matrix_rotate(local_light, local_light, object_rotation); + + // Create the local_screen matrix. + matrix_unit(local_screen); + + matrix_multiply(local_screen, local_screen, local_world); + matrix_multiply(local_screen, local_screen, world_view); + matrix_multiply(local_screen, local_screen, view_screen); + + int lastIdx = -1; + for(int i = 0; i < model_test->tex_count; i++) { + gsKit_TexManager_bind(gsGlobal, model_test->textures[i]); + + VECTOR* positions = &model_test->positions[lastIdx+1]; + VECTOR* texcoords = &model_test->texcoords[lastIdx+1]; + VECTOR* normals = &model_test->normals[lastIdx+1]; + VECTOR* colours = &model_test->colours[lastIdx+1]; + GSTEXTURE* tex = model_test->textures[i]; + + int idxs_to_draw = (model_test->tex_ranges[i]-lastIdx); + int idxs_drawn = 0; + + while (idxs_to_draw > 0) { + dmaKit_wait(DMA_CHANNEL_VIF1, 0); + + int count = BATCH_SIZE; + if (idxs_to_draw < BATCH_SIZE) + { + count = idxs_to_draw; + } + + float fX = 2048.0f+gsGlobal->Width/2; + float fY = 2048.0f+gsGlobal->Height/2; + float fZ = ((float)get_max_z(gsGlobal)); + + float texCol = 128.0f; + + u64* p_data = cube_packet; + + *p_data++ = (*(u32*)(&fX) | (u64)*(u32*)(&fY) << 32); + *p_data++ = (*(u32*)(&fZ) | (u64)(count) << 32); + + *p_data++ = GIF_TAG(1, 0, 0, 0, 0, 1); + *p_data++ = GIF_AD; + + *p_data++ = GS_SETREG_TEX1(1, 0, tex->Filter, tex->Filter, 0, 0, 0); + *p_data++ = GS_TEX1_1; + + int tw, th; + athena_set_tw_th(tex, &tw, &th); + + if(tex->VramClut == 0) + { + *p_data++ = GS_SETREG_TEX0(tex->Vram/256, tex->TBW, tex->PSM, + tw, th, gsGlobal->PrimAlphaEnable, COLOR_MODULATE, + 0, 0, 0, 0, GS_CLUT_STOREMODE_NOLOAD); + } + else + { + *p_data++ = GS_SETREG_TEX0(tex->Vram/256, tex->TBW, tex->PSM, + tw, th, gsGlobal->PrimAlphaEnable, 0, + tex->VramClut/256, tex->ClutPSM, 0, 0, GS_CLUT_STOREMODE_LOAD); + } + + *p_data++ = GS_TEX0_1; + + *p_data++ = VU_GS_GIFTAG(count, 1, 1, + VU_GS_PRIM(GS_PRIM_PRIM_TRIANGLE, 1, 1, gsGlobal->PrimFogEnable, + gsGlobal->PrimAlphaEnable, gsGlobal->PrimAAEnable, 0, 0, 0), + 0, 3); + + *p_data++ = DRAW_STQ2_REGLIST; + + *p_data++ = (*(u32*)(&texCol) | (u64)*(u32*)(&texCol) << 32); + *p_data++ = (*(u32*)(&texCol) | (u64)*(u32*)(&texCol) << 32); + + curr_vif_packet = vif_packets[context]; + + //memset(curr_vif_packet, 0, 16*22); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32)); + + // Add matrix at the beggining of VU mem (skip TOP) + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, 0, &local_screen, 4, 0); + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, 4, &local_light, 4, 0); + + u32 vif_added_bytes = 0; // zero because now we will use TOP register (double buffer) + // we don't wan't to unpack at 8 + beggining of buffer, but at + // the beggining of the buffer + + // Merge packets + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, cube_packet, 6, 1); + vif_added_bytes += 6; + + // Add vertices + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &positions[idxs_drawn], count, 1); + vif_added_bytes += count; // one VECTOR is size of qword + + // Add sts + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &texcoords[idxs_drawn], count, 1); + vif_added_bytes += count; + + // Add colors + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &colours[idxs_drawn], count, 1); + vif_added_bytes += count; + + // Add normals + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &normals[idxs_drawn], count, 1); + vif_added_bytes += count; + + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &lights, 12, 1); + vif_added_bytes += 12; + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_MSCAL, 0) << 32)); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_END, 0, 0 , 0); + *curr_vif_packet++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32); + + asm volatile("nop":::"memory"); + + vifSendPacket(vif_packets[context], DMA_CHANNEL_VIF1); + + idxs_to_draw -= count; + idxs_drawn += count; + } + + lastIdx = model_test->tex_ranges[i]; + } + + // Switch packet, so we can proceed during DMA transfer + context = !context; +} + + +void draw_vu1_with_lights_notex(model* model_test, float pos_x, float pos_y, float pos_z, float rot_x, float rot_y, float rot_z) +{ + VECTOR object_position = { pos_x, pos_y, pos_z, 1.00f }; + VECTOR object_rotation = { rot_x, rot_y, rot_z, 1.00f }; - // Matrices to setup the 3D environment and camera MATRIX local_world; MATRIX local_light; - MATRIX world_view; MATRIX local_screen; - //gsGlobal->PrimAlphaEnable = GS_SETTING_OFF; - //gsKit_set_test(gsGlobal, GS_ATEST_OFF); + GSGLOBAL *gsGlobal = getGSGLOBAL(); + + if (last_mpg != &VU1Draw3DLightsColorsNoTex_CodeStart) { + vu1_upload_micro_program(&VU1Draw3DLightsColorsNoTex_CodeStart, &VU1Draw3DLightsColorsNoTex_CodeEnd); + last_mpg = &VU1Draw3DLightsColorsNoTex_CodeStart; + } + gsGlobal->PrimAAEnable = GS_SETTING_ON; gsKit_set_test(gsGlobal, GS_ZTEST_ON); - // Create the local_world matrix. create_local_world(local_world, object_position, object_rotation); create_local_light(local_light, object_rotation); - create_world_view(world_view, camera_position, camera_rotation); create_local_screen(local_screen, local_world, world_view, view_screen); - if(clip_bounding_box(local_screen, m->bounding_box)) return; - // Allocate calculation space. - VECTOR *t_normals = (VECTOR*)memalign(128, sizeof(VECTOR)*(m->facesCount*3)); - VECTOR *t_lights = (VECTOR*)memalign(128, sizeof(VECTOR)*(m->facesCount*3)); - color_f_t *t_colours = (color_f_t*)memalign(128, sizeof(color_f_t)*(m->facesCount*3)); - vertex_f_t *t_xyz = (vertex_f_t*)memalign(128, sizeof(vertex_f_t)*(m->facesCount*3)); + int idxs_to_draw = model_test->indexCount; + int idxs_drawn = 0; - // Calculate the normal values. - calculate_normals(t_normals, m->facesCount*3, m->normals, local_light); - calculate_lights(t_lights, m->facesCount*3, t_normals, light_direction, light_colour, light_type, light_count); - calculate_colours((VECTOR *)t_colours, m->facesCount, m->colours, t_lights); - calculate_vertices_clipped((VECTOR *)t_xyz, m->facesCount*3, m->positions, local_screen); + while (idxs_to_draw > 0) { + dmaKit_wait(DMA_CHANNEL_VIF1, 0); - if (m->texture != NULL) { - GSPRIMSTQPOINT* gs_vertices = (GSPRIMSTQPOINT*)memalign(128, sizeof(GSPRIMSTQPOINT)*m->facesCount*3); + int count = BATCH_SIZE; + if (idxs_to_draw < BATCH_SIZE) + { + count = idxs_to_draw; + } - athena_process_xyz_rgbaq_st(gs_vertices, gsGlobal, m->facesCount*3, (color_f_t*)t_lights, t_xyz, (texel_f_t *)m->texcoords); + float fX = 2048.0f+gsGlobal->Width/2; + float fY = 2048.0f+gsGlobal->Height/2; + float fZ = ((float)get_max_z(gsGlobal)); - gsKit_TexManager_bind(gsGlobal, m->texture); - gsKit_prim_list_triangle_goraud_texture_stq_3d(gsGlobal, m->texture, m->facesCount*3, gs_vertices); + float texCol = 128.0f; - free(gs_vertices); - - } else { - GSPRIMPOINT* gs_vertices = (GSPRIMPOINT*)memalign(128, sizeof(GSPRIMPOINT)*m->facesCount*3); + u64* p_data = cube_packet; - athena_process_xyz_rgbaq(gs_vertices, gsGlobal, m->facesCount*3, (color_f_t*)t_lights, t_xyz); + *p_data++ = (*(u32*)(&fX) | (u64)*(u32*)(&fY) << 32); + *p_data++ = (*(u32*)(&fZ) | (u64)(count) << 32); - gsKit_prim_list_triangle_gouraud_3d(gsGlobal, m->facesCount*3, gs_vertices); + *p_data++ = GIF_TAG(1, 0, 0, 0, 0, 1); + *p_data++ = GIF_AD; - free(gs_vertices); - } + *p_data++ = VU_GS_GIFTAG(count, 1, 1, + VU_GS_PRIM(GS_PRIM_PRIM_TRIANGLE, 1, 0, gsGlobal->PrimFogEnable, + 0, gsGlobal->PrimAAEnable, 0, 0, 0), + 0, 2); + + *p_data++ = DRAW_NOTEX_REGLIST; + *p_data++ = (*(u32*)(&texCol) | (u64)*(u32*)(&texCol) << 32); + *p_data++ = (*(u32*)(&texCol) | (u64)*(u32*)(&texCol) << 32); + + curr_vif_packet = vif_packets[context]; - free(t_normals); free(t_lights); free(t_colours); free(t_xyz); + ////memset(curr_vif_packet, 0, 16*22); -} + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32)); + + // Add matrix at the beggining of VU mem (skip TOP) + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, 0, &local_screen, 4, 0); + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, 4, &local_light, 4, 0); + + u32 vif_added_bytes = 0; // zero because now we will use TOP register (double buffer) + // we don't wan't to unpack at 8 + beggining of buffer, but at + // the beggining of the buffer + + // Merge packets + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, cube_packet, 4, 1); + vif_added_bytes += 4; + + // Add vertices + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &model_test->positions[idxs_drawn], count, 1); + vif_added_bytes += count; // one VECTOR is size of qword + + // Add colors + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &model_test->colours[idxs_drawn], count, 1); + vif_added_bytes += count; + + // Add normals + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &model_test->normals[idxs_drawn], count, 1); + vif_added_bytes += count; + + curr_vif_packet = vu_add_unpack_data(curr_vif_packet, vif_added_bytes, &lights, 12, 1); + vif_added_bytes += 12; + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_CNT, 0, 0, 0); + *curr_vif_packet++ = ((VIF_CODE(0, 0, VIF_FLUSH, 0) | (u64)VIF_CODE(0, 0, VIF_MSCAL, 0) << 32)); + + *curr_vif_packet++ = DMA_TAG(0, 0, DMA_END, 0, 0 , 0); + *curr_vif_packet++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32); + + asm volatile("nop":::"memory"); + + vifSendPacket(vif_packets[context], DMA_CHANNEL_VIF1); + + idxs_to_draw -= count; + idxs_drawn += count; + } + // Switch packet, so we can proceed during DMA transfer + context = !context; +} \ No newline at end of file diff --git a/src/sioprintf.c b/src/sioprintf.c index d184909..7c9a4b8 100644 --- a/src/sioprintf.c +++ b/src/sioprintf.c @@ -1,6 +1,7 @@ #include #include #include + void sio_printf(const char *fmt, ...) { va_list args; diff --git a/src/sound.c b/src/sound.c index 3155572..871e9fc 100644 --- a/src/sound.c +++ b/src/sound.c @@ -22,11 +22,12 @@ static bool adpcm_started = false; static bool stream_repeat = false; static bool stream_paused = false; +static bool stream_restart = false; static int oggThreadID, oggIoThreadID, wavThreadId; static int outSema, inSema; static unsigned char rdPtr, wrPtr; -static char oggBuffer[STREAM_RING_BUFFER_COUNT][STREAM_RING_BUFFER_SIZE]; +static char soundBuffer[STREAM_RING_BUFFER_COUNT][STREAM_RING_BUFFER_SIZE]; static volatile unsigned char oggThreadRunning, oggIoThreadRunning, wav_thread_running; static Sound *cur_snd; @@ -38,20 +39,22 @@ static void wavThread(void *arg) while (!terminate_flag) { //SleepThread(); - ret = fread(oggBuffer[0], 1, sizeof(oggBuffer[0]), cur_snd->fp); + ret = fread(soundBuffer[0], 1, sizeof(soundBuffer[0]), cur_snd->fp); + if (ret > 0) { audsrv_wait_audio(STREAM_RING_BUFFER_SIZE); - audsrv_play_audio(oggBuffer[0], ret); + audsrv_play_audio(soundBuffer[0], ret); } - if (ret < sizeof(oggBuffer[0])) + if (ret < sizeof(soundBuffer[0])) { + fseek(cur_snd->fp, 0x30, SEEK_SET); + if (!stream_repeat) { - terminate_flag = 1; - break; + audsrv_stop_audio(); + sound_pause(); } - fseek(cur_snd->fp, 0x30, SEEK_SET); } if(stream_paused) { @@ -62,6 +65,15 @@ static void wavThread(void *arg) } else if (!flag_start) { flag_start = true; } + + if(stream_restart) { + audsrv_wait_audio(STREAM_RING_BUFFER_SIZE); + audsrv_stop_audio(); + + fseek(cur_snd->fp, 0x30, SEEK_SET); + + stream_restart = false; + } } audsrv_stop_audio(); @@ -83,7 +95,7 @@ static int init_wav() { thread.attr = 0; thread.option = 0; - // ogg thread will start in DORMANT state. + // sound thread will start in DORMANT state. wavThreadId = CreateThread(&thread); if (wavThreadId >= 0) { @@ -205,6 +217,11 @@ void sound_setadpcmvolume(int slot, int volume) { audsrv_adpcm_set_volume_and_pan(slot, volume, 0); } +void sound_restart() +{ + stream_restart = true; +} + audsrv_adpcm_t* sound_loadadpcm(const char* path){ if(!adpcm_started) { audsrv_adpcm_init(); @@ -284,6 +301,64 @@ int sound_get_duration(Sound* snd) { return -1; } +void sound_set_position(Sound* snd, int ms) { + uint32_t f_pos, n_samples; + + if (snd->type == OGG_AUDIO) { + if (ms < sound_get_duration(snd)) { + if (snd == cur_snd) + sound_pause(); + + n_samples = ms / 1000 * snd->fmt.freq; + + f_pos = (ms / 1000 * snd->fmt.freq) * (snd->fmt.bits / 16); + + ov_pcm_seek(cur_snd->fp, round(f_pos / STREAM_RING_BUFFER_SIZE) * STREAM_RING_BUFFER_SIZE); + + if (snd == cur_snd) + sound_resume(snd); + } + + } else if (snd->type == WAV_AUDIO) { + if (ms < sound_get_duration(snd)) { + if (snd == cur_snd) + sound_pause(); + + n_samples = ms / 1000 * snd->fmt.freq; + + f_pos = (ms / 1000 * snd->fmt.freq) * (snd->fmt.bits / 4); + + fseek(snd->fp, f_pos, SEEK_SET); + + if (snd == cur_snd) + sound_resume(snd); + } + + } else if (snd->type == ADPCM_AUDIO) { + return -1; + } +} + +int sound_get_position(Sound* snd) { + uint32_t f_pos, ms; + + if (snd->type == OGG_AUDIO) { + f_pos = ov_pcm_tell(snd->fp); + + ms = round(f_pos / (snd->fmt.freq / 1000 * (snd->fmt.bits / 16))); + + return ms; + } else if (snd->type == WAV_AUDIO) { + f_pos = ftell(snd->fp); + + ms = round(f_pos / (snd->fmt.freq / 1000 * (snd->fmt.bits / 4))); + + return ms; + } else if (snd->type == ADPCM_AUDIO) { + return -1; + } +} + // OGG Support static void oggThread(void *arg) @@ -295,7 +370,7 @@ static void oggThread(void *arg) while (PollSema(outSema) == outSema) { audsrv_wait_audio(STREAM_RING_BUFFER_SIZE); - audsrv_play_audio(oggBuffer[rdPtr], STREAM_RING_BUFFER_SIZE); + audsrv_play_audio(soundBuffer[rdPtr], STREAM_RING_BUFFER_SIZE); rdPtr = (rdPtr + 1) % STREAM_RING_BUFFER_COUNT; SignalSema(inSema); @@ -308,6 +383,15 @@ static void oggThread(void *arg) } else if (!flag_start) { flag_start = true; } + + if(stream_restart) { + audsrv_wait_audio(STREAM_RING_BUFFER_SIZE); + audsrv_stop_audio(); + + fseek(cur_snd->fp, 0x30, SEEK_SET); + + stream_restart = false; + } } audsrv_stop_audio(); @@ -336,7 +420,7 @@ static void oggIoThread(void *arg) decodeTotal = STREAM_RING_BUFFER_SIZE; int bufferPtr = 0; do { - int ret = ov_read(cur_snd->fp, oggBuffer[wrPtr] + bufferPtr, decodeTotal, 0, 2, 1, &bitStream); + int ret = ov_read(cur_snd->fp, soundBuffer[wrPtr] + bufferPtr, decodeTotal, 0, 2, 1, &bitStream); if (ret > 0) { bufferPtr += ret; decodeTotal -= ret; @@ -345,11 +429,12 @@ static void oggIoThread(void *arg) terminate_flag = 1; break; } else if (ret == 0) { + ov_pcm_seek(cur_snd->fp, 0); + if (!stream_repeat) { - terminate_flag = 1; - break; + audsrv_stop_audio(); + sound_pause(); } - ov_pcm_seek(cur_snd->fp, 0); } } while (decodeTotal > 0); diff --git a/src/system.c b/src/system.c index fb973f7..8818379 100644 --- a/src/system.c +++ b/src/system.c @@ -7,139 +7,3 @@ #include #include "include/system.h" - -/* Normalize a pathname by removing - . and .. components, duplicated /, etc. */ -char* __ps2_normalize_path(char *path_name) -{ - int i, j; - int first, next; - static char out[255]; - - /* First copy the path into our temp buffer */ - strcpy(out, path_name); - /* Then append "/" to make the rest easier */ - strcat(out,"/"); - - /* Convert "//" to "/" */ - for(i=0; out[i+1]; i++) { - if(out[i]=='/' && out[i+1]=='/') { - for(j=i+1; out[j]; j++) - out[j] = out[j+1]; - i--; - } - } - - /* Convert "/./" to "/" */ - for(i=0; out[i] && out[i+1] && out[i+2]; i++) { - if(out[i]=='/' && out[i+1]=='.' && out[i+2]=='/') { - for(j=i+1; out[j]; j++) - out[j] = out[j+2]; - i--; - } - } - - /* Convert "/path/../" to "/" until we can't anymore. Also - * convert leading "/../" to "/" */ - first = next = 0; - while(1) { - /* If a "../" follows, remove it and the parent */ - if(out[next+1] && out[next+1]=='.' && - out[next+2] && out[next+2]=='.' && - out[next+3] && out[next+3]=='/') { - for(j=0; out[first+j+1]; j++) - out[first+j+1] = out[next+j+4]; - first = next = 0; - continue; - } - - /* Find next slash */ - first = next; - for(next=first+1; out[next] && out[next] != '/'; next++) - continue; - if(!out[next]) break; - } - - /* Remove trailing "/" */ - for(i=1; out[i]; i++) - continue; - if(i >= 1 && out[i-1] == '/') - out[i-1] = 0; - - return (char*)out; -} - -/////////////////////////////////////////////// - -void* AllocateLargestFreeBlock(size_t* Size) -{ - size_t s0, s1; - void* p; - - s0 = ~(size_t)0 ^ (~(size_t)0 >> 1); - - while (s0 && (p = malloc(s0)) == NULL) - s0 >>= 1; - - if (p) - free(p); - - s1 = s0 >> 1; - - while (s1) - { - if ((p = malloc(s0 + s1)) != NULL) - { - s0 += s1; - free(p); - } - s1 >>= 1; - } - - while (s0 && (p = malloc(s0)) == NULL) - s0 ^= s0 & -s0; - - *Size = s0; - return p; -} - -size_t GetFreeSize(void) -{ - size_t total = 0; - void* pFirst = NULL; - void* pLast = NULL; - - for (;;) - { - size_t largest; - void* p = AllocateLargestFreeBlock(&largest); - - if (largest < sizeof(void*)) - { - if (p != NULL) - free(p); - break; - } - - *(void**)p = NULL; - - total += largest; - - if (pFirst == NULL) - pFirst = p; - - if (pLast != NULL) - *(void**)pLast = p; - - pLast = p; - } - - while (pFirst != NULL) - { - void* p = *(void**)pFirst; - free(pFirst); - pFirst = p; - } - - return total; -} \ No newline at end of file diff --git a/src/taskman.c b/src/taskman.c index 62fa8ae..44ea897 100644 --- a/src/taskman.c +++ b/src/taskman.c @@ -99,7 +99,7 @@ void init_taskman() tasks_size++; } - } while(info.stack_size != 0) //A way to list already created threads + } while(info.stack_size != 0); //A way to list already created threads dbgprintf("Threads running during boot: %d\n", tasks_size); diff --git a/src/vcl_sml.i b/src/vcl_sml.i new file mode 100644 index 0000000..98e6ec6 --- /dev/null +++ b/src/vcl_sml.i @@ -0,0 +1,974 @@ +;//-------------------------------------------------------------------------------- +;// VCLSML - VCL Standard Macros Library +;// +;// Geoff Audy, January 17th 2002 Initial macro set +;// Geoff Audy, March 26th 2002 Fixed some macros that were broken +;// Geoff Audy, March 27th 2002 Added some macros, most from Colin Hugues (SCEE) +;// Linux (for PlayStation 2) release version 1.10 July 2002 +;// +;// Copyright (C) 2002, Sony Computer Entertainment America Inc. +;// All rights reserved. +;// +;// Note: Some macros generate the following temporary variables: +;// vclsmlftemp: Temporary float register +;// vclsmlitemp: Temporary integer register +;//-------------------------------------------------------------------------------- + +;//-------------------------------------------------------------------- +;// MatrixLoad - Load "matrix" from VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- + .macro MatrixLoad matrix,offset,vumemlocation + lq \matrix[0], \offset+0(\vumemlocation) + lq \matrix[1], \offset+1(\vumemlocation) + lq \matrix[2], \offset+2(\vumemlocation) + lq \matrix[3], \offset+3(\vumemlocation) + .endm + +;//-------------------------------------------------------------------- +;// MatrixSave - Save "matrix" to VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- + .macro MatrixSave matrix,offset,vumemlocation + sq \matrix[0], \offset+0(\vumemlocation) + sq \matrix[1], \offset+1(\vumemlocation) + sq \matrix[2], \offset+2(\vumemlocation) + sq \matrix[3], \offset+3(\vumemlocation) + .endm + +;//-------------------------------------------------------------------- +;// MatrixIdentity - Set "matrix" to be an identity matrix +;// Thanks to Colin Hugues (SCEE) for that one +;//-------------------------------------------------------------------- + .macro MatrixIdentity matrix + add.x \matrix[0], vf00, vf00[w] + mfir.yzw \matrix[0], vi00 + + mfir.xzw \matrix[1], vi00 + add.y \matrix[1], vf00, vf00[w] + + mr32 \matrix[2], vf00 + + max \matrix[3], vf00, vf00 + .endm + +;//-------------------------------------------------------------------- +;// MatrixCopy - Copy "matrixsrc" to "matrixdest" +;// Thanks to Colin Hugues (SCEE) for that one +;//-------------------------------------------------------------------- + .macro MatrixCopy matrixdest,matrixsrc + max \matrixdest[0], \matrixsrc[0], \matrixsrc[0] + move \matrixdest[1], \matrixsrc[1] + max \matrixdest[2], \matrixsrc[2], \matrixsrc[2] + move \matrixdest[3], \matrixsrc[3] + .endm + +;//-------------------------------------------------------------------- +;// MatrixSwap - Swap the content of "matrix1" and "matrix2" +;// The implementation seems lame, but VCL will convert moves to maxes +;// if it sees fit +;//-------------------------------------------------------------------- + .macro MatrixSwap matrix1,matrix2 + move vclsmlftemp, \matrix1[0] + move \matrix1[0], \matrix2[0] + move \matrix2[0], vclsmlftemp + + move vclsmlftemp, \matrix1[1] + move \matrix1[1], \matrix2[1] + move \matrix2[1], vclsmlftemp + + move vclsmlftemp, \matrix1[2] + move \matrix1[2], \matrix2[2] + move \matrix2[2], vclsmlftemp + + move vclsmlftemp, \matrix1[3] + move \matrix1[3], \matrix2[3] + move \matrix2[3], vclsmlftemp + .endm + +;//-------------------------------------------------------------------- +;// MatrixTranspose - Transpose "matrixsrc" to "matresult". It is safe +;// for "matrixsrc" and "matresult" to be the same. +;// Thanks to Colin Hugues (SCEE) for that one +;// Had to modify it though, it was too... smart. +;//-------------------------------------------------------------------- + .macro MatrixTranspose matresult,matrixsrc + mr32.y vclsmlftemp, \matrixsrc[1] + add.z \matresult[1], vf00, \matrixsrc[2][y] + move.y \matresult[2], vclsmlftemp + mr32.y vclsmlftemp, \matrixsrc[0] + add.z \matresult[0], vf00, \matrixsrc[2][x] + mr32.z vclsmlftemp, \matrixsrc[1] + mul.w \matresult[1], vf00, \matrixsrc[3][y] + mr32.x vclsmlftemp, \matrixsrc[0] + add.y \matresult[0], vf00, \matrixsrc[1][x] + move.x \matresult[1], vclsmlftemp + mul.w vclsmlftemp, vf00, \matrixsrc[3][z] + mr32.z \matresult[3], \matrixsrc[2] + move.w \matresult[2], vclsmlftemp + mr32.w vclsmlftemp, \matrixsrc[3] + add.x \matresult[3], vf00, \matrixsrc[0][w] + move.w \matresult[0], vclsmlftemp + mr32.y \matresult[3], vclsmlftemp + add.x \matresult[2], vf00, vclsmlftemp[y] + + move.x \matresult[0], \matrixsrc[0] ;// These 4 instructions will be + move.y \matresult[1], \matrixsrc[1] ;// removed if "matrixsrc" and + move.z \matresult[2], \matrixsrc[2] ;// "matresult" are the same + move.w \matresult[3], \matrixsrc[3] ;// + .endm + +;//-------------------------------------------------------------------- +;// MatrixMultiply - Multiply 2 matrices, "matleft" and "matright", and +;// output the result in "matresult". Dont forget matrix multipli- +;// cations arent commutative, i.e. left X right wont give you the +;// same result as right X left. +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro MatrixMultiply matresult,matleft,matright + mul acc, \matright[0], \matleft[0][x] + madd acc, \matright[1], \matleft[0][y] + madd acc, \matright[2], \matleft[0][z] + madd \matresult[0], \matright[3], \matleft[0][w] + + mul acc, \matright[0], \matleft[1][x] + madd acc, \matright[1], \matleft[1][y] + madd acc, \matright[2], \matleft[1][z] + madd \matresult[1], \matright[3], \matleft[1][w] + + mul acc, \matright[0], \matleft[2][x] + madd acc, \matright[1], \matleft[2][y] + madd acc, \matright[2], \matleft[2][z] + madd \matresult[2], \matright[3], \matleft[2][w] + + mul acc, \matright[0], \matleft[3][x] + madd acc, \matright[1], \matleft[3][y] + madd acc, \matright[2], \matleft[3][z] + madd \matresult[3], \matright[3], \matleft[3][w] + .endm + +;//-------------------------------------------------------------------- +;// LocalizeLightMatrix - Transform the light matrix "lightmatrix" into +;// local space, as described by "matrix", and output the result in +;// "locallightmatrix" +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro LocalizeLightMatrix locallightmatrix,matrix,lightmatrix + mul acc, \lightmatrix[0], \matrix[0][x] + madd acc, \lightmatrix[1], \matrix[0][y] + madd acc, \lightmatrix[2], \matrix[0][z] + madd \locallightmatrix[0], \lightmatrix[3], \matrix[0][w] + + mul acc, \lightmatrix[0], \matrix[1][x] + madd acc, \lightmatrix[1], \matrix[1][y] + madd acc, \lightmatrix[2], \matrix[1][z] + madd \locallightmatrix[1], \lightmatrix[3], \matrix[1][w] + + mul acc, \lightmatrix[0], \matrix[2][x] + madd acc, \lightmatrix[1], \matrix[2][y] + madd acc, \lightmatrix[2], \matrix[2][z] + madd \locallightmatrix[2], \lightmatrix[3], \matrix[2][w] + + move \locallightmatrix[3], \lightmatrix[3] + .endm + +;//-------------------------------------------------------------------- +;// MatrixMultiplyVertex - Multiply "matrix" by "vertex", and output +;// the result in "vertexresult" +;// +;// Note: Apply rotation, scale and translation +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro MatrixMultiplyVertex vertexresult,matrix,vertex + mul acc, \matrix[0], \vertex[x] + madd acc, \matrix[1], \vertex[y] + madd acc, \matrix[2], \vertex[z] + madd \vertexresult, \matrix[3], \vertex[w] + .endm + +;//-------------------------------------------------------------------- +;// MatrixMultiplyVertex - Multiply "matrix" by "vertex", and output +;// the result in "vertexresult" +;// +;// Note: Apply rotation, scale and translation +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro MatrixMultiplyVertexXYZ1 vertexresult,matrix,vertex + mul acc, \matrix[0], \vertex[x] + madd acc, \matrix[1], \vertex[y] + madd acc, \matrix[2], \vertex[z] + madd \vertexresult, \matrix[3], vf00[w] + .endm + +;//-------------------------------------------------------------------- +;// MatrixMultiplyVector - Multiply "matrix" by "vector", and output +;// the result in "vectorresult" +;// +;// Note: Apply rotation and scale, but no translation +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro MatrixMultiplyVector vectorresult,matrix,vector + mul acc, \matrix[0], \vector[x] + madd acc, \matrix[1], \vector[y] + madd \vectorresult, \matrix[2], \vector[z] + .endm + +;//-------------------------------------------------------------------- +;// VectorLoad - Load "vector" from VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- + .macro VectorLoad vector,offset,vumemlocation + lq \vector, \offset(\vumemlocation) + .endm + +;//-------------------------------------------------------------------- +;// VectorSave - Save "vector" to VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- + .macro VectorSave vector,offset,vumemlocation + sq \vector, \offset(\vumemlocation) + .endm + +;//-------------------------------------------------------------------- +;// VectorAdd - Add 2 vectors, "vector1" and "vector2" and output the +;// result in "vectorresult" +;//-------------------------------------------------------------------- + .macro VectorAdd vectorresult,vector1,vector2 + add \vectorresult, \vector1, \vector2 + .endm + +;//-------------------------------------------------------------------- +;// VectorSub - Subtract "vector2" from "vector1", and output the +;// result in "vectorresult" +;//-------------------------------------------------------------------- + .macro VectorSub vectorresult,vector1,vector2 + sub \vectorresult, \vector1, \vector2 + .endm + +;//-------------------------------------------------------------------- +;// VertexLoad - Load "vertex" from VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- + .macro VertexLoad vertex,offset,vumemlocation + lq \vertex, \offset(\vumemlocation) + .endm + +;//-------------------------------------------------------------------- +;// VertexSave - Save "vertex" to VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- + .macro VertexSave vertex,offset,vumemlocation + sq \vertex, \offset(\vumemlocation) + .endm + +;//-------------------------------------------------------------------- +;// VertexPersCorr - Apply perspective correction onto "vertex" and +;// output the result in "vertexoutput" +;// +;// Note: Q register is modified +;//-------------------------------------------------------------------- + .macro VertexPersCorr vertexoutput,vertex + div q, vf00[w], \vertex[w] +; mul.xyz \vertexoutput, \vertex, q + mul \vertexoutput, \vertex, q + .endm + +;//-------------------------------------------------------------------- +;// VertexPersCorrST - Apply perspective correction onto "vertex" and +;// "st", and output the result in "vertexoutput" and "stoutput" +;// +;// Note: Q register is modified +;//-------------------------------------------------------------------- + .macro VertexPersCorrST vertexoutput,stoutput,vertex,st + div q, vf00[w], \vertex[w] + mul.xyz \vertexoutput, \vertex, q + move.w \vertexoutput, \vertex +; mul \vertexoutput, \vertex, q + mul \stoutput, \st, q + .endm + +;//-------------------------------------------------------------------- +;// VertexFPtoGsXYZ2 - Convert an XYZW, floating-point vertex to GS +;// XYZ2 format (ADC bit isnt set) +;//-------------------------------------------------------------------- + .macro VertexFpToGsXYZ2 outputxyz,vertex + ftoi4.xy \outputxyz, \vertex + ftoi0.z \outputxyz, \vertex + mfir.w \outputxyz, vi00 + .endm + +;//-------------------------------------------------------------------- +;// VertexFPtoGsXYZ2Adc - Convert an XYZW, floating-point vertex to GS +;// XYZ2 format (ADC bit is set) +;//-------------------------------------------------------------------- + .macro VertexFpToGsXYZ2Adc outputxyz,vertex + ftoi4.xy \outputxyz, \vertex + ftoi0.z \outputxyz, \vertex + ftoi15.w \outputxyz, vf00 + .endm + +;//-------------------------------------------------------------------- +;// VertexFpToGsXYZF2 - Convert an XYZF, floating-point vertex to GS +;// XYZF2 format (ADC bit isnt set) +;//-------------------------------------------------------------------- + .macro VertexFpToGsXYZF2 outputxyz,vertex + ftoi4 \outputxyz, \vertex + .endm + +;//-------------------------------------------------------------------- +;// VertexFpToGsXYZF2Adc - Convert an XYZF, floating-point vertex to GS +;// XYZF2 format (ADC bit is set) +;//-------------------------------------------------------------------- + .macro VertexFpToGsXYZF2Adc outputxyz,vertex + ftoi4 \outputxyz, \vertex + mtir vclsmlitemp, \outputxyz[w] + iaddiu vclsmlitemp, 0x7FFF + iaddi vclsmlitemp, 1 + mfir.w \outputxyz, vclsmlitemp + .endm + +;//-------------------------------------------------------------------- +;// ColorFPtoGsRGBAQ - Convert an RGBA, floating-point color to GS +;// RGBAQ format +;//-------------------------------------------------------------------- + .macro ColorFPtoGsRGBAQ outputrgba,color + ftoi0 \outputrgba, \color + .endm + +;//-------------------------------------------------------------------- +;// ColorGsRGBAQtoFP - Convert an RGBA, GS RGBAQ format to floating- +;// point color +;//-------------------------------------------------------------------- + .macro ColorGsRGBAQtoFP outputrgba,color + itof0 \outputrgba, \color + .endm + +;//-------------------------------------------------------------------- +;// CreateGsPRIM - Create a GS-packed-format PRIM command, according to +;// a specified immediate value "prim" +;// +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- + .macro CreateGsPRIM outputprim,prim + iaddiu vclsmlitemp, vi00, \prim + mfir \outputprim, vclsmlitemp + .endm + +;//-------------------------------------------------------------------- +;// CreateGsRGBA - Create a GS-packed-format RGBA command, according to +;// specified immediate values "r", "g", "b" and "a" (integer 0-255) +;// +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- + .macro CreateGsRGBA outputrgba,r,g,b,a + iaddiu vclsmlitemp, vi00, \r + mfir.x \outputrgba, vclsmlitemp + iaddiu vclsmlitemp, vi00, \g + mfir.y \outputrgba, vclsmlitemp + iaddiu vclsmlitemp, vi00, \b + mfir.z \outputrgba, vclsmlitemp + iaddiu vclsmlitemp, vi00, \a + mfir.w \outputrgba, vclsmlitemp + .endm + +;//-------------------------------------------------------------------- +;// CreateGsSTQ - Create a GS-packed-format STQ command, according to +;// specified immediate values "s", "t" and "q" (floats) +;// +;// Note: I register is modified +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- + .macro CreateGsSTQ outputstq,s,t,q + loi \s + add.x \outputstq, vf00, i + loi \t + add.y \outputstq, vf00, i + loi \q + add.z \outputstq, vf00, i + .endm + +;//-------------------------------------------------------------------- +;// CreateGsUV - Create a GS-packed-format VU command, according to +;// specified immediate values "u" and "v" (integer -32768 - 32768, +;// with 4 LSB as precision) +;// +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- + .macro CreateGsUV outputuv,u,v + iaddiu vclsmlitemp, vi00, \u + mfir.x \outputuv, vclsmlitemp + iaddiu vclsmlitemp, vi00, \v + mfir.y \outputuv, vclsmlitemp + .endm + +;//-------------------------------------------------------------------- +;// CreateGsRGBA - Create a GS-packed-format RGBA command, according to +;// a specified immediate value "fog" (integer 0-255) +;// +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- + .macro CreateGsFOG outputfog,fog + iaddiu vclsmlitemp, vi00, \fog * 16 + mfir.w \outputfog, vclsmlitemp + .endm + +;//-------------------------------------------------------------------- +;// CreateGifTag - Create a packed-mode giftag, according to specified +;// immediate values. Currently only support up to 4 registers. +;// +;// Note: I register is modified +;// Note: Definitely meant for debugging purposes, NOT for a final +;// solution +;//-------------------------------------------------------------------- +;// MIGHT NOT BE IMPLEMENTABLE AFTER ALL (AT LEAST NOT UNTIL VCL EVALUATES CONSTANTS!) +;// THAT WOULD HAVE BEEN KINDA COOL... DAMN. --GEOFF +;// .macro CreateGifTag outputgiftag,nloop,prim,nreg,reg1,reg2,reg3,reg4 +;// iaddiu vclsmlitemp, vi00, \nloop + 0x8000 +;// mfir.x \outputgiftag, vclsmlitemp +;// loi 0x00004000 + (\prim * 0x8000) + (\nreg * 0x10000000) +;// add.y \outputgiftag, vf00, i +;// iaddiu vclsmlitemp, vi00, \reg1 + (\reg2 * 16) + (\reg3 * 256) + (\reg4 * 4096) +;// mfir.z \outputgiftag, vclsmlitemp +;// .endm + +;//-------------------------------------------------------------------- +;// VectorDotProduct - Calculate the dot product of "vector1" and +;// "vector2", and output to "dotproduct"[x] +;//-------------------------------------------------------------------- + .macro VectorDotProduct dotproduct,vector1,vector2 + mul.xyz \dotproduct, \vector1, \vector2 + add.x \dotproduct, \dotproduct, \dotproduct[y] + add.x \dotproduct, \dotproduct, \dotproduct[z] + .endm + +;//-------------------------------------------------------------------- +;// VectorDotProductACC - Calculate the dot product of "vector1" and +;// "vector2", and output to "dotproduct"[x]. This one does it using +;// the ACC register which, depending on the case, might turn out to be +;// faster or slower. +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro VectorDotProductACC dotproduct,vector1,vector2 + max Vector1111, vf00, vf00[w] + mul vclsmlftemp, \vector1, \vector2 + add.x acc, vclsmlftemp, vclsmlftemp[y] + madd.x \dotproduct, Vector1111, vclsmlftemp + .endm + +;//-------------------------------------------------------------------- +;// VectorCrossProduct - Calculate the cross product of "vector1" and +;// "vector2", and output to "vectoroutput" +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro VectorCrossProduct vectoroutput,vector1,vector2 + opmula.xyz ACC, \vector1, \vector2 + opmsub.xyz \vectoroutput, \vector2, \vector1 + sub.w \vectoroutput, vf00, vf00 + .endm + +;//-------------------------------------------------------------------- +;// VectorNormalize - Bring the length of "vector" to 1.f, and output +;// it to "vectoroutput" +;// +;// Note: Q register is modified +;//-------------------------------------------------------------------- + .macro VectorNormalize vecoutput,vector + mul.xyz vclsmlftemp, \vector, \vector + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[y] + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[z] + rsqrt q, vf00[w], vclsmlftemp[x] + sub.w \vecoutput, vf00, vf00 + mul.xyz \vecoutput, \vector, q + .endm + +;//-------------------------------------------------------------------- +;// VectorNormalizeXYZ - Bring the length of "vector" to 1.f, and out- +;// put it to "vectoroutput". The "w" field isn't transfered. +;// +;// Note: Q register is modified +;//-------------------------------------------------------------------- + .macro VectorNormalizeXYZ vecoutput,vector + mul.xyz vclsmlftemp, \vector, \vector + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[y] + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[z] + rsqrt q, vf00[w], vclsmlftemp[x] + mul.xyz \vecoutput, \vector, q + .endm + +;//-------------------------------------------------------------------- +;// VertexLightAmb - Apply ambient lighting "ambientrgba" to a vertex +;// of color "vertexrgba", and output the result in "outputrgba" +;//-------------------------------------------------------------------- + .macro VertexLightAmb rgbaout,vertexrgba,ambientrgba + mul \rgbaout, \vertexrgba, \ambientrgba + .endm + +;//-------------------------------------------------------------------- +;// VertexLightDir3 - Apply up to 3 directional lights contained in a +;// light matrix "lightmatrix" to a vertex of color "vertexrgba" and +;// having a normal "vertexnormal", and output the result in +;// "outputrgba" +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro VertexLightDir3 rgbaout,vertexrgba,vertexnormal,lightcolors,lightnormals + mul acc, \lightnormals[0], \vertexnormal[x] + madd acc, \lightnormals[1], \vertexnormal[y] + madd acc, \lightnormals[2], \vertexnormal[z] + madd \rgbaout, \lightnormals[3], \vertexnormal[w] ;// Here "rgbaout" is the dot product for the 3 lights + max \rgbaout, \rgbaout, vf00[x] ;// Here "rgbaout" is the dot product for the 3 lights + mul acc, \lightcolors[0], \rgbaout[x] + madd acc, \lightcolors[1], \rgbaout[y] + madd \rgbaout, \lightcolors[2], \rgbaout[z] ;// Here "rgbaout" is the light applied on the vertex + mul \rgbaout, \vertexrgba, \rgbaout ;// Here "rgbaout" is the amount of light reflected by the vertex + .endm + +;//-------------------------------------------------------------------- +;// VertexLightDir3Amb - Apply up to 3 directional lights, plus an +;// ambient light contained in a light matrix "lightmatrix" to a vertex +;// of color "vertexrgba" and having a normal "vertexnormal", and +;// output the result in "outputrgba" +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro VertexLightDir3Amb rgbaout,vertexrgba,vertexnormal,lightcolors,lightnormals + mul acc, \lightnormals[0], \vertexnormal[x] + madd acc, \lightnormals[1], \vertexnormal[y] + madd acc, \lightnormals[2], \vertexnormal[z] + madd \rgbaout, \lightnormals[3], \vertexnormal[w] ;// Here "rgbaout" is the dot product for the 3 lights + max \rgbaout, \rgbaout, vf00[x] ;// Here "rgbaout" is the dot product for the 3 lights + mul acc, \lightcolors[0], \rgbaout[x] + madd acc, \lightcolors[1], \rgbaout[y] + madd acc, \lightcolors[2], \rgbaout[z] + madd \rgbaout, \lightcolors[3], \rgbaout[w] ;// Here "rgbaout" is the light applied on the vertex + mul.xyz \rgbaout, \vertexrgba, \rgbaout ;// Here "rgbaout" is the amount of light reflected by the vertex + .endm + +;//-------------------------------------------------------------------- +;// FogSetup - Set up fog "fogparams", by specifying "nearfog" and +;// "farfog". "fogparams" will afterward be ready to be used by fog- +;// related macros, like "VertexFogLinear" for example. +;// +;// Note: I register is modified +;//-------------------------------------------------------------------- + .macro FogSetup fogparams,nearfogz,farfogz + sub \fogparams, vf00, vf00 ;// Set XYZW to 0 + loi \farfogz ;// + add.w \fogparams, \fogparams, i ;// fogparam[w] is farfogz + loi \nearfogz + add.z \fogparams, \fogparams, \fogparams[w] + sub.z \fogparams, \fogparams, i + loi 255.0 + add.xy \fogparams, \fogparams, i ;// fogparam[y] is 255.0 + sub.x \fogparams, \fogparams, vf00[w] ;// fogparam[x] is 254.0 + div q, \fogparams[y], \fogparams[z] + sub.z \fogparams, \fogparams, \fogparams + add.z \fogparams, \fogparams, q ;// fogparam[z] is 255.f / (farfogz - nearfogz) + .endm + +;//-------------------------------------------------------------------- +;// VertexFogLinear - Apply fog "fogparams" to a vertex "xyzw", and +;// output the result in "xyzfoutput". "xyzw" [w] is assumed to be +;// the distance from the camera. "fogparams" must contain farfogz in +;// [w], and (255.f / (farfogz - nearfogz)) in [z]. "xyzfoutputf" [w] +;// will contain a float value between 0.0 and 255.0, inclusively. +;//-------------------------------------------------------------------- + .macro VertexFogLinear xyzfoutput,xyzw,fogparams + move.xyz \xyzfoutput, \xyzw ;// XYZ part won't be modified + sub.w \xyzfoutput, \fogparams, \xyzw[w] ;// fog = (farfogz - z) * 255.0 / + mul.w \xyzfoutput, \xyzfoutput, \fogparams[z] ;// (farfogz - nearfogz) + max.w \xyzfoutput, \xyzfoutput, vf00[x] ;// Clamp fog values outside the range 0.0-255.0 + mini.w \xyzfoutput, \xyzfoutput, \fogparams[y] ;// + .endm + +;//-------------------------------------------------------------------- +;// VertexFogRemove - Remove any effect of fog to "xyzf". "fogparams" +;// [x] must be set to 254.0. "xyzf" will be modified directly. +;//-------------------------------------------------------------------- + .macro VertexFogRemove xyzf,fogparams + add.w \xyzf, vf00, \fogparams[x] ;// xyzw[w] = 1.0 + 254.0 = 255.0 = no fog + .endm + +;//-------------------------------------------------------------------- +;// PushInteger1 - Push "integer1" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PushInteger1 stackptr,integer1 + isubiu \stackptr, \stackptr, 1 + iswr.x \integer1, (\stackptr):VCLSML_STACK + .endm + +;//-------------------------------------------------------------------- +;// PushInteger2 - Push "integer1" and "integer2" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PushInteger2 stackptr,integer1,integer2 + isubiu \stackptr, \stackptr, 1 + iswr.x \integer1, (\stackptr):VCLSML_STACK + iswr.y \integer2, (\stackptr):VCLSML_STACK + .endm + +;//-------------------------------------------------------------------- +;// PushInteger3 - Push "integer1", "integer2" and "integer3" on +;// "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PushInteger3 stackptr,integer1,integer2,integer3 + isubiu \stackptr, \stackptr, 1 + iswr.x \integer1, (\stackptr):VCLSML_STACK + iswr.y \integer2, (\stackptr):VCLSML_STACK + iswr.z \integer3, (\stackptr):VCLSML_STACK + .endm + +;//-------------------------------------------------------------------- +;// PushInteger4 - Push "integer1", "integer2", "integer3" and +;// "integer4" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PushInteger4 stackptr,integer1,integer2,integer3,integer4 + isubiu \stackptr, \stackptr, 1 + iswr.x \integer1, (\stackptr):VCLSML_STACK + iswr.y \integer2, (\stackptr):VCLSML_STACK + iswr.z \integer3, (\stackptr):VCLSML_STACK + iswr.w \integer4, (\stackptr):VCLSML_STACK + .endm + +;//-------------------------------------------------------------------- +;// PopInteger1 - Pop "integer1" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PopInteger1 stackptr,integer1 + ilwr.x \integer1, (\stackptr):VCLSML_STACK + iaddiu \stackptr, \stackptr, 1 + .endm + +;//-------------------------------------------------------------------- +;// PopInteger2 - Pop "integer1" and "integer2" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PopInteger2 stackptr,integer1,integer2 + ilwr.y \integer2, (\stackptr):VCLSML_STACK + ilwr.x \integer1, (\stackptr):VCLSML_STACK + iaddiu \stackptr, \stackptr, 1 + .endm + +;//-------------------------------------------------------------------- +;// PopInteger3 - Pop "integer1", "integer2" and "integer3" on +;// "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PopInteger3 stackptr,integer1,integer2,integer3 + ilwr.z \integer3, (\stackptr):VCLSML_STACK + ilwr.y \integer2, (\stackptr):VCLSML_STACK + ilwr.x \integer1, (\stackptr):VCLSML_STACK + iaddiu \stackptr, \stackptr, 1 + .endm + +;//-------------------------------------------------------------------- +;// PopInteger4 - Pop "integer1", "integer2", "integer3" and +;// "integer4" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PopInteger4 stackptr,integer1,integer2,integer3,integer4 + ilwr.w \integer4, (\stackptr):VCLSML_STACK + ilwr.z \integer3, (\stackptr):VCLSML_STACK + ilwr.y \integer2, (\stackptr):VCLSML_STACK + ilwr.x \integer1, (\stackptr):VCLSML_STACK + iaddiu \stackptr, \stackptr, 1 + .endm + +;//-------------------------------------------------------------------- +;// PushMatrix - Push "matrix" onto the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PushMatrix stackptr,matrix + sq \matrix[0], -1(\stackptr):VCLSML_STACK + sq \matrix[1], -2(\stackptr):VCLSML_STACK + sq \matrix[2], -3(\stackptr):VCLSML_STACK + sq \matrix[3], -4(\stackptr):VCLSML_STACK + iaddi \stackptr, \stackptr, -4 + .endm + +;//-------------------------------------------------------------------- +;// PopMatrix - Pop "matrix" out of the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PopMatrix stackptr,matrix + lq \matrix[0], 0(\stackptr):VCLSML_STACK + lq \matrix[1], 1(\stackptr):VCLSML_STACK + lq \matrix[2], 2(\stackptr):VCLSML_STACK + lq \matrix[3], 3(\stackptr):VCLSML_STACK + iaddi \stackptr, \stackptr, 4 + .endm + +;//-------------------------------------------------------------------- +;// PushVector - Push "vector" onto the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PushVector stackptr,vector + sqd \vector, (--\stackptr):VCLSML_STACK + .endm + +;//-------------------------------------------------------------------- +;// PopVector - Pop "vector" out of the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PopVector stackptr,vector + lqi \vector, (\stackptr++):VCLSML_STACK + .endm + +;//-------------------------------------------------------------------- +;// PushVertex - Push "vector" onto the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PushVertex stackptr,vertex + sqd \vertex, (--\stackptr):VCLSML_STACK + .endm + +;//-------------------------------------------------------------------- +;// PopVertex - Pop "vertex" out of the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- + .macro PopVertex stackptr,vertex + lqi \vertex, (\stackptr++):VCLSML_STACK + .endm + +;//-------------------------------------------------------------------- +;// AngleSinCos - Returns the sin and cos of up to 2 angles, which must +;// be contained in the X and Z elements of "angle". The sin/cos pair +;// will be contained in the X/Y elements of "sincos" for the first +;// angle, and Z/W for the second one. +;// Thanks to Colin Hugues (SCEE) for that one +;// +;// Note: ACC and I registers are modified, and a bunch of temporary +;// variables are created... Maybe bad for VCL register pressure +;//-------------------------------------------------------------------- + .macro AngleSinCos angle,sincos + move.xz \sincos, \angle ; To avoid modifying the original angles... + + mul.w \sincos, vf00, \sincos[z] ; Copy angle from z to w + add.y \sincos, vf00, \sincos[x] ; Copy angle from x to y + + loi 1.570796 ; Phase difference for sin as cos ( PI/2 ) + sub.xz \sincos, \sincos, I ; + + abs \sincos, \sincos ; Mirror cos around zero + + max Vector1111, vf00, vf00[w] ; Initialise all 1s + + loi -0.159155 ; Scale so single cycle is range 0 to -1 ( *-1/2PI ) + mul ACC, \sincos, I ; + + loi 12582912.0 ; Apply bias to remove fractional part + msub ACC, Vector1111, I ; + madd ACC, Vector1111, I ; Remove bias to leave original int part + + loi -0.159155 ; Apply original number to leave fraction range only + msub ACC, \sincos, I ; + + loi 0.5 ; Ajust range: -0.5 to +0.5 + msub \sincos, Vector1111, I ; + + abs \sincos, \sincos ; Clamp: 0 to +0.5 + + loi 0.25 ; Ajust range: -0.25 to +0.25 + sub \sincos, \sincos, I ; + + mul anglepower2, \sincos, \sincos ; a^2 + + loi -76.574959 ; + mul k4angle, \sincos, I ; k4 a + + loi -41.341675 ; + mul k2angle, \sincos, I ; k2 a + + loi 81.602226 ; + mul k3angle, \sincos, I ; k3 a + + mul anglepower4, anglepower2, anglepower2 ; a^4 + mul k4angle, k4angle, anglepower2 ; k4 a^3 + mul ACC, k2angle, anglepower2 ; + k2 a^3 + + loi 39.710659 ; k5 a + mul k2angle, \sincos, I ; + + mul anglepower8, anglepower4, anglepower4 ; a^8 + madd ACC, k4angle, anglepower4 ; + k4 a^7 + madd ACC, k3angle, anglepower4 ; + k3 a^5 + loi 6.283185 ; + madd ACC, \sincos, I ; + k1 a + madd \sincos, k2angle, anglepower8 ; + k5 a^9 + .endm + +;//-------------------------------------------------------------------- +;// QuaternionToMatrix - Converts a quaternion rotation to a matrix +;// Thanks to Colin Hugues (SCEE) for that one +;// +;// Note: ACC and I registers are modified +;//-------------------------------------------------------------------- + .macro QuaternionToMatrix matresult,quaternion + + mula.xyz ACC, \quaternion, \quaternion ; xx yy zz + + loi 1.414213562 + muli vclsmlftemp, \quaternion, I ; x sqrt2 y sqrt2 z sqrt2 w sqrt2 + + mr32.w \matresult[0], vf00 ; Set rhs matrix line 0 to 0 + mr32.w \matresult[1], vf00 ; + mr32.w \matresult[2], vf00 ; Set rhs matrix + move \matresult[3], vf00 ; Set bottom line to 0 0 0 1 + + madd.xyz vcl_2qq, \quaternion, \quaternion ; 2xx 2yy 2zz + addw.xyz Vector111, vf00, vf00 ; 1 1 1 - + + opmula.xyz ACC, vclsmlftemp, vclsmlftemp ; 2yz 2xz 2xy - + msubw.xyz vclsmlftemp2, vclsmlftemp, vclsmlftemp ; 2yz-2xw 2xz-2yz 2xy-2zw - + maddw.xyz vclsmlftemp3, vclsmlftemp, vclsmlftemp ; 2yz+2xw 2xz+2yz 2xy+2zw - + addaw.xyz ACC, vf00, vf00 ; 1 1 1 - + msubax.yz ACC, Vector111, vcl_2qq ; 1 1-2xx 1-2xx + + msuby.z \matresult[2], Vector111, vcl_2qq ; - - 1-2xx-2yy - + msubay.x ACC, Vector111, vcl_2qq ; 1-2yy 1-2xx 1-2xx-2yy - + msubz.y \matresult[1], Vector111, vcl_2qq ; - 1-2xx-2zz - - + mr32.y \matresult[0], vclsmlftemp2 + msubz.x \matresult[0], Vector111, vcl_2qq ; 1-2yy-2zz - - - + mr32.x \matresult[2], vclsmlftemp2 + addy.z \matresult[0], vf00, vclsmlftemp3 + mr32.w vclsmlftemp, vclsmlftemp2 + mr32.z \matresult[1], vclsmlftemp + addx.y \matresult[2], vf00, vclsmlftemp3 + mr32.y vclsmlftemp3, vclsmlftemp3 + mr32.x \matresult[1], vclsmlftemp3 + + .endm + +;//-------------------------------------------------------------------- +;// QuaternionMultiply - Multiplies "quaternion1" and "quaternion2", +;// and puts the result in "quatresult". +;// Thanks to Colin Hugues (SCEE) for that one +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro QuaternionMultiply quatresult,quaternion1,quaternion2 + mul vclsmlftemp, \quaternion1, \quaternion2 ; xx yy zz ww + + opmula.xyz ACC, \quaternion1, \quaternion2 ; Start Outerproduct + madd.xyz ACC, \quaternion1, \quaternion2[w]; Add w2.xyz1 + madd.xyz ACC, \quaternion2, \quaternion1[w]; Add w1.xyz2 + opmsub.xyz \quatresult, \quaternion2, \quaternion1 ; Finish Outerproduct + + sub.w ACC, vclsmlftemp, vclsmlftemp[z] ; ww - zz + msub.w ACC, vf00, vclsmlftemp[y] ; ww - zz - yy + msub.w \quatresult, vf00, vclsmlftemp[x] ; ww - zz - yy - xx + .endm + +;//-------------------------------------------------------------------- +;// TriangleWinding - Compute winding of triangle relative to "eyepos" +;// result is nonzero if winding is CW (actually depends on your +;// coordinate system) +;// Thanks to David Etherton (Angel Studios) for that one +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- + .macro TriangleWinding result, vert1, vert2, vert3, eyepos + sub.xyz tw_vert12, \vert2, \vert1 + sub.xyz tw_vert13, \vert3, \vert1 + + opmula.xyz ACC, tw_vert12, tw_vert13 + opmsub.xyz tw_normal, tw_vert13, tw_vert12 + + sub.xyz tw_dot, \eyepos, \vert1 + + mul.xyz tw_dot, tw_dot, tw_normal + add.x tw_dot, tw_dot, tw_dot[y] + add.x tw_dot, tw_dot, tw_dot[z] + + fsand \result, 0x2 + .endm + +;//-------------------------------------------------------------------- +;// STATUSFLAGS_BGTZ - Branch if status shows "greater than zero". +;// Thanks to David Etherton (Angel Studios) for that one +;//-------------------------------------------------------------------- + .macro STATUSFLAGS_BGTZ label + fsand vclsmlitemp, 0x3 ; NEG | ZERO + ibeq vclsmlitemp, VI00, \label ; Jump if NEITHER NEG NOR ZERO + .endm + +;//-------------------------------------------------------------------- +;// STATUSFLAGS_BGEZ - Branch if status shows "greater or equal to +;// zero". +;// Thanks to David Etherton (Angel Studios) for that one +;//-------------------------------------------------------------------- + .macro STATUSFLAGS_BGEZ label + fsand vclsmlitemp, 0x2 ; NEG + ibeq vclsmlitemp, VI00, \label ; Jump if NOT NEG + .endm + +;//-------------------------------------------------------------------- +;// STATUSFLAGS_BLEZ - Branch if status shows "less or equal to zero". +;// Thanks to David Etherton (Angel Studios) for that one +;//-------------------------------------------------------------------- + .macro STATUSFLAGS_BLEZ label + fsand vclsmlitemp, 0x3 ; NEG | ZERO + ibne vclsmlitemp, VI00, \label ; Jump if NEG OR ZERO + .endm + +;//-------------------------------------------------------------------- +;// STATUSFLAGS_BLTZ - Branch if status shows "less than zero". +;// Thanks to David Etherton (Angel Studios) for that one +;//-------------------------------------------------------------------- + .macro STATUSFLAGS_BLTZ label + fsand vclsmlitemp, 0x2 ; NEG + ibne vclsmlitemp, VI00, \label ; Jump if NEG + .endm + +;//-------------------------------------------------------------------- +;// Name Here - Description here +;// +;// Note: +;//-------------------------------------------------------------------- + + .macro LoadFloat output, immediate + loi \immediate ;// + addi output, vf00, i ;// + .endm + +;//-------------------------------------------------------------------- +;// VectorClamp - Clamp vector to a range +;// +;// Note: +;//-------------------------------------------------------------------- + + .macro VectorClamp output, input, min, max + loi \min ;// + addi minvec, vf00, i ;// + loi \max ;// + addi maxvec, vf00, i ;// + maxx.xyzw \output, \input, minvec ;// + minix.xyzw \output, \output, maxvec ;// + .endm + +;//-------------------------------------------------------------------- +;// Name Here - Description here +;// +;// Note: +;//-------------------------------------------------------------------- diff --git a/src/vif.c b/src/vif.c new file mode 100644 index 0000000..85ffaef --- /dev/null +++ b/src/vif.c @@ -0,0 +1,75 @@ +#include "vif.h" + +void vifSendPacket(void* packet, u32 vif_channel) { + dmaKit_wait(DMA_CHANNEL_GIF, 0); + dmaKit_wait(vif_channel, 0); + FlushCache(0); + dmaKit_send_chain(vif_channel, (void *)((u32)packet & 0x0FFFFFFF), 0); +} + +void *vifCreatePacket(u32 size) { + return memalign(128, size*16); +} + +void vifDestroyPacket(void* packet) { + free(packet); +} + +static inline u32 get_packet_size_for_program(u32 *start, u32 *end) +{ + // Count instructions + u32 count = (end - start) / 2; + if (count & 1) + count++; + return (count >> 8) + 1; +} + +void vu1_upload_micro_program(u32* start, u32* end) +{ + u32 packet_size = get_packet_size_for_program(start, end) + 1; // + 1 for end tag + u64* p_store; + u64* p_data = p_store = vifCreatePacket(packet_size); + + // get the size of the code as we can only send 256 instructions in each MPGtag + u32 dest = 0; + u32 count = (end - start) / 2; + if (count & 1) + count++; + + u32 *l_start = start; + + while (count > 0) + { + u16 curr_count = count > 256 ? 256 : count; + + *p_data++ = DMA_TAG(curr_count / 2, 0, DMA_REF, 0, (const u128 *)l_start, 0); + + *p_data++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(dest, curr_count & 0xFF, VIF_MPG, 0) << 32); + + l_start += curr_count * 2; + count -= curr_count; + dest += curr_count; + } + + *p_data++ = DMA_TAG(0, 0, DMA_END, 0, 0 , 0); + *p_data++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32); + + vifSendPacket(p_store, 1); + vifDestroyPacket(p_store); +} + +void vu1_set_double_buffer_settings() +{ + u64* p_data; + u64* p_store; + p_data = p_store = vifCreatePacket(2); + + *p_data++ = DMA_TAG(0, 0, DMA_CNT, 0, 0 , 0); + *p_data++ = (VIF_CODE(8, 0, VIF_BASE, 0) | (u64)VIF_CODE(496, 0, VIF_OFFSET, 0) << 32); + + *p_data++ = DMA_TAG(0, 0, DMA_END, 0, 0 , 0); + *p_data++ = (VIF_CODE(0, 0, VIF_NOP, 0) | (u64)VIF_CODE(0, 0, VIF_NOP, 0) << 32); + + vifSendPacket(p_store, 1); + vifDestroyPacket(p_store); +} \ No newline at end of file diff --git a/src/vif.h b/src/vif.h new file mode 100644 index 0000000..c9deef7 --- /dev/null +++ b/src/vif.h @@ -0,0 +1,110 @@ +#ifndef VIF_H +#define VIF_H + +#include +#include +#include +#include +#include +#include + +typedef struct { + uint32_t VPS : 2; // VIF command status + uint32_t VEW : 1; // VU is executing microprogram + uint32_t VGW : 1; // Stalled waiting for GIF (VIF1 only) + uint32_t reserved1 : 2; + uint32_t MRK : 1; // MARK detected + uint32_t DBF : 1; // Double buffer flag (VIF1 only) + uint32_t VSS : 1; // Stalled after STOP was sent to FBRST + uint32_t VFS : 1; // Stalled after force break was sent to FBRST + uint32_t VIS : 1; // Stalled on interrupt ibt + uint32_t INT : 1; // Interrupt bit detected + uint32_t ER0 : 1; // DMAtag mismatch error + uint32_t ER1 : 1; // Invalid VIF command was sent + uint32_t reserved2 : 9; + uint32_t FDR : 1; // FIFO direction (VIF1 only) + uint32_t FQC : 5; // Amount of quadwords in FIFO +} VIFn_STAT; + +typedef enum { + IDLE, + WAIT_FOR_DATA_FOLLOWING_CMD, + DECODING_CMD, + DECOMPRESSING_OR_TRANSFERRING_DATA, +} VIF_CMD_STATUS; + +#define VIF1_STAT ((VIFn_STAT*)(0x10003C00)) + +#define UNPACK_S_32 0x00 +#define UNPACK_S_16 0x01 +#define UNPACK_S_8 0x02 +#define UNPACK_V2_32 0x04 +#define UNPACK_V2_16 0x05 +#define UNPACK_V2_8 0x06 +#define UNPACK_V3_32 0x08 +#define UNPACK_V3_16 0x09 +#define UNPACK_V3_8 0x0A +#define UNPACK_V4_32 0x0C +#define UNPACK_V4_16 0x0D +#define UNPACK_V4_8 0x0E +#define UNPACK_V4_5 0x0F + +#define VIF_NOP 0 +#define VIF_STCYCL 1 +#define VIF_OFFSET 2 +#define VIF_BASE 3 +#define VIF_ITOP 4 +#define VIF_STMOD 5 +#define VIF_MSKPATH3 6 +#define VIF_MARK 7 +#define VIF_FLUSHE 16 +#define VIF_FLUSH 17 +#define VIF_FLUSHA 19 +#define VIF_MSCAL 20 +#define VIF_MSCNT 23 +#define VIF_MSCALF 21 +#define VIF_STMASK 32 +#define VIF_STROW 48 +#define VIF_STCOL 49 +#define VIF_MPG 74 +#define VIF_DIRECT 80 +#define VIF_DIRECTHL 81 + +#define VIF_CODE(_immediate, _num, _cmd, _irq) ((u32)(_immediate) | ((u32)(_num) << 16) | ((u32)(_cmd) << 24) | ((u32)(_irq) << 31)) + +#define DRAW_STQ2_REGLIST \ + ((u64)GS_ST) << 0 | \ + ((u64)GS_RGBAQ) << 4 | \ + ((u64)GS_XYZ2) << 8 + +#define DRAW_NOTEX_REGLIST \ + ((u64)GS_RGBAQ) << 0 | \ + ((u64)GS_XYZ2) << 4 + +/** Texture Alpha Expansion */ +#define ALPHA_EXPAND_NORMAL 0 +#define ALPHA_EXPAND_TRANSPARENT 1 + +#define VU_GS_PRIM(PRIM, IIP, TME, FGE, ABE, AA1, FST, CTXT, FIX) (u128)(((FIX << 10) | (CTXT << 9) | (FST << 8) | (AA1 << 7) | (ABE << 6) | (FGE << 5) | (TME << 4) | (IIP << 3) | (PRIM))) +#define VU_GS_GIFTAG(NLOOP, EOP, PRE, PRIM, FLG, NREG) (((u64)(NREG) << 60) | ((u64)(FLG) << 58) | ((u64)(PRIM) << 47) | ((u64)(PRE) << 46) | (EOP << 15) | (NLOOP << 0)) + +inline u64* vu_add_unpack_data(u64 *p_data, u32 t_dest_address, void *t_data, u32 t_size, u8 t_use_top) +{ + *p_data++ = DMA_TAG(t_size, 0, DMA_REF, 0, t_data, 0); + *p_data++ = (VIF_CODE(0x0101 | (0 << 8), 0, VIF_STCYCL, 0) | (u64) + VIF_CODE(t_dest_address | ((u32)1 << 14) | ((u32)t_use_top << 15), ((t_size == 256) ? 0 : t_size), UNPACK_V4_32 | ((u32)0 << 4) | 0x60, 0) << 32 ); + + return p_data; +} + +void vu1_upload_micro_program(u32* start, u32* end); + +void vu1_set_double_buffer_settings(); + +void vifSendPacket(void* packet, u32 vif_channel); + +void *vifCreatePacket(u32 size); + +void vifDestroyPacket(void* packet); + +#endif