├── .github └── workflows │ └── main.yml ├── .gitignore ├── .idea ├── .gitignore ├── DOOM.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── .vscode ├── c_cpp_properties.json └── tasks.json ├── LICENSE ├── README.md ├── build.bat ├── built_rom.bin ├── res ├── enemy1.png ├── graphics_res.h ├── graphics_res.res ├── hud.h ├── hud.res ├── images │ ├── HUD.png │ ├── HUD2.png │ ├── SHOTGUN.png │ ├── SKYBOX1.png │ ├── armor_inv.png │ ├── blue_key_inv.png │ ├── bullet_inv.png │ ├── heart_inv.png │ └── shield_inv.png ├── music_res.h ├── music_res.res ├── no_dist_lighting_palette_tweaked.png ├── palette_for_bright_wall.png ├── palette_old.png ├── sfx │ ├── closedoor.wav │ ├── opendoor.wav │ ├── select.wav │ └── shoot.wav ├── sfx_res.h ├── sfx_res.res ├── sprite_pal.png ├── sprites_res.h ├── sprites_res.res ├── textures │ ├── DOOR.png │ ├── DOOR_15COL.png │ ├── DOOR_MORE_COLOR.png │ ├── KEY.png │ ├── KEY_32_32.png │ ├── STAIRS.png │ ├── TEXTURE_SHEET.png │ ├── WALLA_15COL.png │ ├── WALLB_15COL.png │ ├── WALLC_15COL.png │ ├── WALL_A.png │ ├── WALL_A_DARKER.png │ ├── WALL_A_DARKEST.png │ ├── WALL_A_FOG.png │ ├── WALL_A_FOG_DARKER.png │ ├── WALL_A_FOG_DARKEST.png │ ├── WALL_B.png │ ├── WALL_B_DARKER.png │ ├── WALL_B_DARKEST.png │ ├── WALL_C.png │ ├── WALL_C_DARKER.png │ ├── WALL_C_DARKEST.png │ ├── doobguy.png │ ├── sci-fi-wall-texture-atlas.png │ ├── sci-fi-wallc.png │ ├── sci-fi-wallc_dark.png │ ├── sprites.png │ ├── texture_atlas.png │ └── texture_atlas_sprite_palette.png └── two_light_levels_pal.png ├── screen0.png ├── screen1.png ├── screen2.png ├── screen3.png └── src ├── NOTES ├── TODO.md ├── active_sectors.c ├── active_sectors.h ├── bob.c ├── boot ├── rom_head.c └── sega.s ├── building_test_map.c ├── building_test_map.h ├── bunch_render.c ├── bunch_render.h ├── clip_buf.c ├── clip_buf.h ├── collision.c ├── collision.h ├── colors.c ├── colors.h ├── config.h ├── console.c ├── console.h ├── div_lut.c ├── div_lut.h ├── draw.c ├── draw.h ├── editor_test_map.c ├── editor_test_map.h ├── fire.c ├── fire.h ├── fire_native.s ├── fire_tables.py ├── game.c ├── game.h ├── game_mode.c ├── game_mode.h ├── imgui.ini ├── init.c ├── init.h ├── inventory.c ├── inventory.h ├── joy_helper.c ├── joy_helper.h ├── js ├── pvs.html └── pvs.js ├── level.c ├── level.h ├── main.c ├── main_menu.c ├── main_menu.h ├── map_table.c ├── map_table.h ├── maps.h ├── math3d.c ├── math3d.h ├── menu_helper.c ├── menu_helper.h ├── music.c ├── music.h ├── my_bmp.c ├── my_bmp.h ├── my_bmp_a.s ├── obj_sprite.c ├── obj_sprite.h ├── object.c ├── object.h ├── overlapping_test_map.c ├── overlapping_test_map.h ├── portal.c ├── portal.h ├── portal_map.c ├── portal_map.h ├── python ├── __init__.py ├── bsp.py ├── draw.py ├── e1m1.c ├── editor │ ├── .idea │ │ ├── editor.iml │ │ ├── inspectionProfiles │ │ │ └── profiles_settings.xml │ │ ├── misc.xml │ │ ├── modules.xml │ │ ├── vcs.xml │ │ └── workspace.xml │ ├── DLLs │ │ └── geos_c.dll │ ├── README │ ├── README.md │ ├── build_conf.ini │ ├── compress.py │ ├── defaults.py │ ├── editor.py │ ├── editor.spec │ ├── file_utils.py │ ├── geos_c.dll │ ├── imgui.ini │ ├── levels.py │ ├── light_levels.py │ ├── light_remapping_table.png │ ├── light_remapping_table_autogen.png │ ├── light_remapping_table_mix.png │ ├── light_remapping_table_mix_rotated.png │ ├── light_remapping_table_no_mix_rotated.png │ ├── line.py │ ├── map.py │ ├── modes.py │ ├── music.py │ ├── palette.py │ ├── palette_light_table.py │ ├── poetry.lock │ ├── pvs.py │ ├── pyproject.toml │ ├── remapping.py │ ├── render_3d.py │ ├── requirements.txt │ ├── rom.py │ ├── run_editor.bat │ ├── script.py │ ├── sector.py │ ├── sector_group.py │ ├── sprite_utils.py │ ├── state.py │ ├── texture_utils.py │ ├── things.py │ ├── tree.py │ ├── trigger.py │ ├── undo.py │ ├── utils.py │ ├── vertex.py │ └── wad.py ├── gen_font_shadow_lut.py ├── gen_linedef_rasterizer.py ├── gen_postinc_tex_tables.py ├── gen_raster_loops.py ├── gen_recip_table.py ├── gen_sprite_scale_routine_tables.py ├── gen_sprite_scale_tables.py ├── main.py ├── optimizer.py ├── plane_lighting_pre_calc.py ├── portal.py ├── profile.stats ├── span_buffer.py ├── util.py └── wad.py ├── random.c ├── random.h ├── real_timer.c ├── real_timer.h ├── sector_group.c ├── sector_group.h ├── sfx.c ├── sfx.h ├── sprite ├── tex_draw_tables.s ├── tex_table_lookup.c ├── tex_tables_lookup.h ├── texture.c ├── texture.h ├── textures.c ├── textures.h ├── tile.h ├── utils.c ├── utils.h ├── vertex.h ├── vwf.c ├── vwf.h ├── vwf_decl.c ├── weapon_sprites.c └── weapon_sprites.h /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build editor exe with Pyinstaller 2 | 3 | on: 4 | # Triggers the workflow on push or pull request events but only for the "master" branch 5 | push: 6 | branches: [ "master" ] 7 | pull_request: 8 | branches: [ "master" ] 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Add path 18 | run: export PATH=src/python/editor/DLLs:$PATH 19 | 20 | - name: Package Application 21 | uses: JackMcKew/pyinstaller-action-windows@main 22 | with: 23 | path: src/python/editor 24 | 25 | - name: Copy config file 26 | run: sudo cp src/python/editor/build_conf.ini src/python/editor/dist/windows/conf.ini 27 | 28 | - name: Copy readme 29 | run: sudo cp src/python/editor/README src/python/editor/dist/windows/README 30 | 31 | - name: Copy engine binary 32 | run: sudo cp built_rom.bin src/python/editor/dist/windows/rom.bin 33 | 34 | - name: Create textures directory 35 | run: sudo mkdir src/python/editor/dist/windows/textures 36 | 37 | - name: Create sprites directory 38 | run: sudo mkdir src/python/editor/dist/windows/sprites 39 | 40 | - name: Create music directory 41 | run: sudo mkdir src/python/editor/dist/windows/music 42 | 43 | - uses: actions/upload-artifact@v2 44 | with: 45 | name: portal-editor 46 | path: src/python/editor/dist/windows/* 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.so 27 | *.so.* 28 | *.dylib 29 | 30 | # Executables 31 | *.exe 32 | *.out 33 | *.app 34 | *.i*86 35 | *.x86_64 36 | *.hex 37 | 38 | # Debug files 39 | *.dSYM/ 40 | *.su 41 | *.idb 42 | *.pdb 43 | 44 | # Kernel Module Compile Results 45 | *.mod* 46 | *.cmd 47 | .tmp_versions/ 48 | modules.order 49 | Module.symvers 50 | Mkfile.old 51 | dkms.conf 52 | out/ 53 | 54 | # cached python modules, and cached wads loaded by python tool 55 | *.pyc 56 | *.cache 57 | 58 | .vscode* 59 | .vscode/ 60 | .vscode/* 61 | .idea 62 | .idea* 63 | .idea/* 64 | .venv/ 65 | src/python/editor/build/ 66 | imgui.ini 67 | src/python/dist.7z 68 | src/python/editor/dist/ 69 | *.vgm 70 | src/python/editor/imgui.ini 71 | src/python/editor/sprites/ 72 | src/python/editor/textures/ 73 | res/sprites/ 74 | src/python/editor/editor.build/ 75 | src/python/editor/editor.dist/ 76 | src/python/editor/conf.ini 77 | res/sfx/*.wav 78 | res/music/*.wav 79 | res/sprites/*.png 80 | res/images/*.png 81 | res/textures/*.png 82 | src/python/editor/SDL2.dll 83 | src/python/editor/editor.onefile-build/ 84 | src/python/editor/editor.dist.7z 85 | src/python/editor/DLLs/ 86 | src/python/editor/fixed_rom.bin 87 | src/python/editor/xgm_output.bin 88 | src/python/editor/maps/ 89 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/DOOM.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "configurations": [ 4 | { 5 | "name": "SGDK", 6 | "includePath": [ 7 | "${env:GDK_WIN}/inc/", 8 | "${env:GDK_WIN}/res/", 9 | "res/" 10 | ], 11 | "browse" : { 12 | "limitSymbolsToIncludedHeaders" : true, 13 | "databaseFilename" : "" 14 | } 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "SGDK - Build", 8 | "type": "shell", 9 | "command": ".\\build.bat release", 10 | "group": "build", 11 | "presentation": { 12 | "reveal": "always", 13 | "panel": "new" 14 | }, 15 | "problemMatcher": [] 16 | }, 17 | { 18 | "label": "SGDK - Clean Build", 19 | "type": "shell", 20 | "command": ".\\build.bat cleanbuild", 21 | "group": "build", 22 | "presentation": { 23 | "reveal": "always", 24 | "panel": "new" 25 | }, 26 | "problemMatcher": [] 27 | }, 28 | { 29 | "label": "SGDK - Asm", 30 | "type": "shell", 31 | "command": ".\\build.bat asm", 32 | "group": "build", 33 | "presentation": { 34 | "reveal": "always", 35 | "panel": "new" 36 | }, 37 | "problemMatcher": [] 38 | }, 39 | { 40 | "label": "SGDK - Build & Test", 41 | "type": "shell", 42 | "command": ".\\build.bat", 43 | "group": "build", 44 | "presentation": { 45 | "reveal": "always", 46 | "panel": "new" 47 | }, 48 | "problemMatcher": [] 49 | }, 50 | { 51 | "label": "SGDK - Build & Debug", 52 | "type": "shell", 53 | "command": ".\\build.bat debug", 54 | "group": "build", 55 | "presentation": { 56 | "reveal": "always", 57 | "panel": "new" 58 | }, 59 | "problemMatcher": [] 60 | }, 61 | { 62 | "label": "Build and launch on mega everdrive pro", 63 | "type": "shell", 64 | "command": ".\\build.bat medbuild", 65 | "group": { 66 | "kind": "build", 67 | "isDefault": true 68 | }, 69 | "presentation": { 70 | "echo": true, 71 | "reveal": "always", 72 | "focus": false, 73 | "panel": "shared", 74 | "showReuseMessage": true, 75 | "clear": false 76 | } 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Erik Haliewicz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Manifold 2 | Portal renderer for Sega Genesis/Megadrive. 3 | 4 | Uses a "2.5D", doom or build engine-style rendering pipeline, but with convex sectors and portals. 5 | 6 | Graphics features 7 | - Arbitrary angled walls, ala doom and build-engine. 8 | - Perspective-correct texture mapping 9 | - Sector-based lighting levels with per-pixel distance lighting. 10 | - Overlapping "non-euclidean" geometry 11 | - Transparent surfaces. 12 | 13 | ![Textures and lighting](./screen2.png) 14 | 15 | ![Moving sectors with clipped textures](./screen3.png) 16 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | 2 | IF "x%1" == "x" ( 3 | %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen 4 | %GENS% %CD%\out\rom.bin 5 | ) ELSE IF "%1" == "test" ( 6 | %GENS% %CD%\out\rom.bin 7 | ) ELSE IF "%1" == "asm" ( 8 | %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen asm 9 | ) ELSE IF "%1" == "debug" ( 10 | %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen debug 11 | REM %GENS% %CD%\out\rom.bin -D 12 | REM %GENS_KMOD% %CD%\out\rom.bin 13 | %BLASTEM% %CD%\out\rom.bin -D 14 | ) ELSE IF "%1" == "cleanbuild" ( 15 | 16 | %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen clean 17 | %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen release 18 | ) ELSE IF "%1" == "medbuild" ( 19 | %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen 20 | %MEGALINK% %CD%\out\rom.bin 21 | ) ELSE ( 22 | %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen %1 23 | ) 24 | -------------------------------------------------------------------------------- /built_rom.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/built_rom.bin -------------------------------------------------------------------------------- /res/enemy1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/enemy1.png -------------------------------------------------------------------------------- /res/graphics_res.h: -------------------------------------------------------------------------------- 1 | #ifndef _RES_GRAPHICS_RES_H_ 2 | #define _RES_GRAPHICS_RES_H_ 3 | 4 | extern const Palette two_light_levels_pal; 5 | extern const TileSet pause_checker; 6 | extern const Image skybox_gradient; 7 | 8 | #endif // _RES_GRAPHICS_RES_H_ 9 | -------------------------------------------------------------------------------- /res/graphics_res.res: -------------------------------------------------------------------------------- 1 | PALETTE two_light_levels_pal "two_light_levels_pal.png" NONE 2 | IMAGE skybox_gradient "images/SKYBOX1.png" NONE ALL 3 | TILESET pause_checker "images/pause_checker.png" NONE NONE -------------------------------------------------------------------------------- /res/hud.h: -------------------------------------------------------------------------------- 1 | #ifndef _RES_HUD_H_ 2 | #define _RES_HUD_H_ 3 | 4 | extern const Image hud; 5 | extern const Image armor_inv; 6 | extern const Image blue_key_inv; 7 | extern const Image green_key_inv; 8 | extern const Image red_key_inv; 9 | extern const Image bullet_inv; 10 | extern const Image heart_inv; 11 | extern const Image shield_inv; 12 | 13 | #endif // _RES_HUD_H_ 14 | -------------------------------------------------------------------------------- /res/hud.res: -------------------------------------------------------------------------------- 1 | IMAGE hud "images/HUD2.png" NONE ALL 2 | IMAGE armor_inv "images/armor_inv.png" NONE ALL 3 | IMAGE blue_key_inv "images/blue_key_inv.png" NONE ALL 4 | IMAGE green_key_inv "images/green_key_inv.png" NONE ALL 5 | IMAGE red_key_inv "images/red_key_inv.png" NONE ALL 6 | IMAGE bullet_inv "images/bullet_inv.png" NONE ALL 7 | IMAGE heart_inv "images/heart_inv.png" NONE ALL 8 | IMAGE shield_inv "images/shield_inv.png" NONE ALL -------------------------------------------------------------------------------- /res/images/HUD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/HUD.png -------------------------------------------------------------------------------- /res/images/HUD2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/HUD2.png -------------------------------------------------------------------------------- /res/images/SHOTGUN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/SHOTGUN.png -------------------------------------------------------------------------------- /res/images/SKYBOX1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/SKYBOX1.png -------------------------------------------------------------------------------- /res/images/armor_inv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/armor_inv.png -------------------------------------------------------------------------------- /res/images/blue_key_inv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/blue_key_inv.png -------------------------------------------------------------------------------- /res/images/bullet_inv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/bullet_inv.png -------------------------------------------------------------------------------- /res/images/heart_inv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/heart_inv.png -------------------------------------------------------------------------------- /res/images/shield_inv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/images/shield_inv.png -------------------------------------------------------------------------------- /res/music_res.h: -------------------------------------------------------------------------------- 1 | #ifndef _RES_MUSIC_RES_H_ 2 | #define _RES_MUSIC_RES_H_ 3 | 4 | 5 | #endif // _RES_MUSIC_RES_H_ 6 | -------------------------------------------------------------------------------- /res/music_res.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/music_res.res -------------------------------------------------------------------------------- /res/no_dist_lighting_palette_tweaked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/no_dist_lighting_palette_tweaked.png -------------------------------------------------------------------------------- /res/palette_for_bright_wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/palette_for_bright_wall.png -------------------------------------------------------------------------------- /res/palette_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/palette_old.png -------------------------------------------------------------------------------- /res/sfx/closedoor.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/sfx/closedoor.wav -------------------------------------------------------------------------------- /res/sfx/opendoor.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/sfx/opendoor.wav -------------------------------------------------------------------------------- /res/sfx/select.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/sfx/select.wav -------------------------------------------------------------------------------- /res/sfx/shoot.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/sfx/shoot.wav -------------------------------------------------------------------------------- /res/sfx_res.h: -------------------------------------------------------------------------------- 1 | #ifndef _RES_SFX_RES_H_ 2 | #define _RES_SFX_RES_H_ 3 | 4 | extern const u8 sfx_select[1792]; 5 | extern const u8 sfx_shotgun[20480]; 6 | extern const u8 sfx_jump1[4352]; 7 | extern const u8 sfx_jump2[3072]; 8 | extern const u8 sfx_lift_go_up[18176]; 9 | extern const u8 sfx_door_open[9984]; 10 | extern const u8 sfx_door_close[9728]; 11 | extern const u8 sfx_enemy_a_wake[7680]; 12 | 13 | #endif // _RES_SFX_RES_H_ 14 | -------------------------------------------------------------------------------- /res/sfx_res.res: -------------------------------------------------------------------------------- 1 | WAV sfx_select "sfx/select.wav" XGM 2 | WAV sfx_shotgun "sfx/shotgun.wav" XGM 3 | WAV sfx_jump1 "sfx/jump1.wav" XGM 4 | WAV sfx_jump2 "sfx/jump2.wav" XGM 5 | WAV sfx_lift_go_up "sfx/liftgoup.wav" XGM 6 | WAV sfx_door_open "sfx/opendoor.wav" XGM 7 | WAV sfx_door_close "sfx/closedoor.wav" XGM 8 | WAV sfx_enemy_a_wake "sfx/enemya_wake.wav" XGM -------------------------------------------------------------------------------- /res/sprite_pal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/sprite_pal.png -------------------------------------------------------------------------------- /res/sprites_res.h: -------------------------------------------------------------------------------- 1 | #ifndef _RES_SPRITES_RES_H_ 2 | #define _RES_SPRITES_RES_H_ 3 | 4 | extern const Palette sprite_pal; 5 | extern const TileSet smile; 6 | extern const TileSet frown; 7 | extern const SpriteDefinition shotgun; 8 | extern const SpriteDefinition shotgun_reload; 9 | 10 | #endif // _RES_SPRITES_RES_H_ 11 | -------------------------------------------------------------------------------- /res/sprites_res.res: -------------------------------------------------------------------------------- 1 | SPRITE shotgun "images/SHOTGUN.png" 8 8 0 0 NONE TILE 2 | SPRITE shotgun_reload "images/SHOTGUN_RELOAD.png" 8 16 0 0 NONE SPRITE 3 | PALETTE sprite_pal "sprite_pal.png" NONE 4 | TILESET smile "images/SMILE.png" NONE 5 | TILESET frown "images/FROWN.png" NONE -------------------------------------------------------------------------------- /res/textures/DOOR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/DOOR.png -------------------------------------------------------------------------------- /res/textures/DOOR_15COL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/DOOR_15COL.png -------------------------------------------------------------------------------- /res/textures/DOOR_MORE_COLOR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/DOOR_MORE_COLOR.png -------------------------------------------------------------------------------- /res/textures/KEY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/KEY.png -------------------------------------------------------------------------------- /res/textures/KEY_32_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/KEY_32_32.png -------------------------------------------------------------------------------- /res/textures/STAIRS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/STAIRS.png -------------------------------------------------------------------------------- /res/textures/TEXTURE_SHEET.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/TEXTURE_SHEET.png -------------------------------------------------------------------------------- /res/textures/WALLA_15COL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALLA_15COL.png -------------------------------------------------------------------------------- /res/textures/WALLB_15COL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALLB_15COL.png -------------------------------------------------------------------------------- /res/textures/WALLC_15COL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALLC_15COL.png -------------------------------------------------------------------------------- /res/textures/WALL_A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_A.png -------------------------------------------------------------------------------- /res/textures/WALL_A_DARKER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_A_DARKER.png -------------------------------------------------------------------------------- /res/textures/WALL_A_DARKEST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_A_DARKEST.png -------------------------------------------------------------------------------- /res/textures/WALL_A_FOG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_A_FOG.png -------------------------------------------------------------------------------- /res/textures/WALL_A_FOG_DARKER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_A_FOG_DARKER.png -------------------------------------------------------------------------------- /res/textures/WALL_A_FOG_DARKEST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_A_FOG_DARKEST.png -------------------------------------------------------------------------------- /res/textures/WALL_B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_B.png -------------------------------------------------------------------------------- /res/textures/WALL_B_DARKER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_B_DARKER.png -------------------------------------------------------------------------------- /res/textures/WALL_B_DARKEST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_B_DARKEST.png -------------------------------------------------------------------------------- /res/textures/WALL_C.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_C.png -------------------------------------------------------------------------------- /res/textures/WALL_C_DARKER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_C_DARKER.png -------------------------------------------------------------------------------- /res/textures/WALL_C_DARKEST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/WALL_C_DARKEST.png -------------------------------------------------------------------------------- /res/textures/doobguy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/doobguy.png -------------------------------------------------------------------------------- /res/textures/sci-fi-wall-texture-atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/sci-fi-wall-texture-atlas.png -------------------------------------------------------------------------------- /res/textures/sci-fi-wallc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/sci-fi-wallc.png -------------------------------------------------------------------------------- /res/textures/sci-fi-wallc_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/sci-fi-wallc_dark.png -------------------------------------------------------------------------------- /res/textures/sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/sprites.png -------------------------------------------------------------------------------- /res/textures/texture_atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/texture_atlas.png -------------------------------------------------------------------------------- /res/textures/texture_atlas_sprite_palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/textures/texture_atlas_sprite_palette.png -------------------------------------------------------------------------------- /res/two_light_levels_pal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/res/two_light_levels_pal.png -------------------------------------------------------------------------------- /screen0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/screen0.png -------------------------------------------------------------------------------- /screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/screen1.png -------------------------------------------------------------------------------- /screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/screen2.png -------------------------------------------------------------------------------- /screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/screen3.png -------------------------------------------------------------------------------- /src/NOTES: -------------------------------------------------------------------------------- 1 | NEED portal walls inserted into pvs when floor/ceil color or height changes. Otherwise they are unnecessary. -------------------------------------------------------------------------------- /src/TODO.md: -------------------------------------------------------------------------------- 1 | # 3d view 2 | 3 | # - 4 | 5 | Wall lengths are CONSTANT! 6 | pre-calculate them, and determine how many times a texture repeats at map-build time. 7 | For now we use an efficient distance algorithm, but it's not ideal. -------------------------------------------------------------------------------- /src/active_sectors.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "active_sectors.h" 3 | #include "portal_map.h" 4 | #include "utils.h" 5 | 6 | 7 | static u32* active_sectors; 8 | 9 | void init_active_sectors(u16 num_sect_groups) { 10 | // 64 bytes 11 | if(num_sect_groups > MAX_SECTOR_GROUPS) { 12 | die("Too many sector groups!"); 13 | } 14 | active_sectors = malloc((num_sect_groups + (8-1))/8, "active_sector_bitmap"); 15 | } 16 | 17 | void cleanup_active_sectors() { 18 | free(active_sectors, "active_sector_bitmap"); 19 | } 20 | 21 | void register_sect_group_as_active(u16 sect_group) { 22 | u16 lw_index = sect_group>>5; 23 | u8 bit_index = sect_group & 0b111; 24 | active_sectors[lw_index] |= (1 << bit_index); 25 | } 26 | 27 | void register_sect_group_as_inactive(u16 sect_group) { 28 | u16 lw_index = sect_group>>5; 29 | u8 bit_index = sect_group & 0b111; 30 | active_sectors[lw_index] &= ~(1 << bit_index); 31 | } 32 | 33 | 34 | void iterate_active_sectors(active_sector_callback cb) { 35 | for(u16 lw_index = 0; lw_index < MAX_SECTOR_GROUPS/32; lw_index++) { 36 | u32 lw = active_sectors[lw_index]; 37 | if(lw == 0) { continue; } 38 | 39 | u16 base_index = lw_index<<5; 40 | 41 | for(u32 bit_index = 0; bit_index < 32; bit_index++) { 42 | if(lw & (1< 5 | 6 | typedef void(*active_sector_callback)(u16 sect_group); 7 | 8 | 9 | void init_active_sectors(u16 num_sect_groups); 10 | 11 | void cleanup_active_sectors(); 12 | 13 | void register_sect_group_as_active(u16 sect_group); 14 | 15 | void register_sect_group_as_inactive(u16 sect_group); 16 | 17 | void iterate_active_sectors(active_sector_callback cb); 18 | 19 | #endif -------------------------------------------------------------------------------- /src/bob.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/bob.c -------------------------------------------------------------------------------- /src/boot/rom_head.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | __attribute__((externally_visible)) 4 | const struct 5 | { 6 | char console[16]; /* Console Name (16) */ 7 | char copyright[16]; /* Copyright Information (16) */ 8 | char title_local[48]; /* Domestic Name (48) */ 9 | char title_int[48]; /* Overseas Name (48) */ 10 | char serial[14]; /* Serial Number (2, 12) */ 11 | u16 checksum; /* Checksum (2) */ 12 | char IOSupport[16]; /* I/O Support (16) */ 13 | u32 rom_start; /* ROM Start Address (4) */ 14 | u32 rom_end; /* ROM End Address (4) */ 15 | u32 ram_start; /* Start of Backup RAM (4) */ 16 | u32 ram_end; /* End of Backup RAM (4) */ 17 | char sram_sig[2]; /* "RA" for save ram (2) */ 18 | u16 sram_type; /* 0xF820 for save ram on odd bytes (2) */ 19 | u32 sram_start; /* SRAM start address - normally 0x200001 (4) */ 20 | u32 sram_end; /* SRAM end address - start + 2*sram_size (4) */ 21 | char modem_support[12]; /* Modem Support (24) */ 22 | char notes[40]; /* Memo (40) */ 23 | char region[16]; /* Country Support (16) */ 24 | } rom_header = { 25 | "SEGA MEGA DRIVE ", 26 | "(C)FLEMTEAM 2013", 27 | "PORTAL_ENGINE ", 28 | "PORTAL_ENGINE ", 29 | "GM 00000000-00", 30 | 0x0000, 31 | "JD ", 32 | 0x00000000, 33 | 0x00100000, 34 | 0x00FF0000, 35 | 0x00FFFFFF, 36 | "RA", 37 | 0xE020, 38 | 0x00200000, 39 | 0x0027FFFF, 40 | " ", 41 | " ", 42 | "JUE " 43 | }; 44 | -------------------------------------------------------------------------------- /src/building_test_map.h: -------------------------------------------------------------------------------- 1 | #ifndef BUILDING_TEST_MAP_H 2 | #define BUILDING_TEST_MAP_H 3 | 4 | #include "portal_map.h" 5 | 6 | extern const portal_map building_test_map; 7 | 8 | #endif -------------------------------------------------------------------------------- /src/bunch_render.h: -------------------------------------------------------------------------------- 1 | #ifndef BUNCH_RENDER_H 2 | #define BUNCH_RENDER_H 3 | 4 | #include 5 | 6 | void prepare_bunches(u16 src_sector, u32 cur_frame); 7 | //void draw_bunches(); 8 | void draw_bunches(u16 src_sector, u32 cur_frame); 9 | 10 | 11 | #endif -------------------------------------------------------------------------------- /src/clip_buf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "clip_buf.h" 3 | #include "utils.h" 4 | 5 | 6 | clip_buf *clip_buffers; 7 | 8 | clip_buf* clip_buf_list_head = NULL; 9 | 10 | static int sp; 11 | 12 | void init_clip_buffer_list() { 13 | clip_buffers = malloc(sizeof(clip_buf) * NUM_CLIP_BUFS, "silhouette clip buffers"); 14 | sp = 0; 15 | clip_buf_list_head = &clip_buffers[0]; 16 | for(int i = 0; i < NUM_CLIP_BUFS; i++) { 17 | clip_buffers[i].id = i; 18 | } 19 | } 20 | 21 | void free_clip_buffer_list() { 22 | free(clip_buffers, "silhouette clip buffers"); 23 | } 24 | 25 | clip_buf* alloc_clip_buffer() { 26 | //KLog("allocating clip buffer"); 27 | if(sp >= NUM_CLIP_BUFS) { 28 | die("no more clip bufs"); 29 | return NULL; 30 | } 31 | return &clip_buffers[sp++]; 32 | } 33 | 34 | void free_clip_buffer(clip_buf* buf) { 35 | clip_buf* freed = &clip_buffers[--sp]; 36 | //KLog("free clip buffer"); 37 | if(freed != buf) { 38 | char sbuf[64]; 39 | sprintf(sbuf, "Freed clip buffer %i but expected %i", buf->id, freed->id); 40 | die(sbuf); 41 | } 42 | } -------------------------------------------------------------------------------- /src/clip_buf.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIP_BUF_H 2 | #define CLIP_BUF_H 3 | 4 | #include 5 | 6 | // 128 bytes * 2 = 256 bytes 7 | #define NUM_CLIP_BUFS 16 8 | typedef struct clip_buf clip_buf; 9 | 10 | // we could be really fancy and create just one big buffer, and allocate chunks out of that as needed 11 | // but i doubt we'll ever want to draw four of these on top of each other 12 | struct clip_buf { 13 | u8 y_clip_buffer[128]; 14 | u8 id; 15 | }; 16 | 17 | 18 | 19 | 20 | void init_clip_buffer_list(); 21 | void free_clip_buffer_list(); 22 | 23 | clip_buf* clip_buffers; 24 | 25 | clip_buf* alloc_clip_buffer(); 26 | void free_clip_buffer(clip_buf* buf); 27 | 28 | 29 | #endif -------------------------------------------------------------------------------- /src/collision.h: -------------------------------------------------------------------------------- 1 | #ifndef COLLISION_H 2 | #define COLLISION_H 3 | 4 | #include 5 | #include "game.h" 6 | 7 | typedef struct { 8 | Vect2D_f32 pos; 9 | u16 new_sector; 10 | } collision_result; 11 | 12 | #define PLAYER_COLLISION_DISTANCE 20 13 | #define PLAYER_COLLISION_DISTANCE_SQR (PLAYER_COLLISION_DISTANCE*PLAYER_COLLISION_DISTANCE) 14 | 15 | collision_result check_for_collision(fix32 curx, fix32 cury, fix32 newx, fix32 newy, u16 cur_sector); 16 | collision_result check_for_collision_radius(fix32 curx, fix32 cury, fix32 curz, fix32 newx, fix32 newy, f32 radius_sqr, u16 cur_sector); 17 | u16 find_sector(player_pos cur_player_pos); 18 | s32 sq_shortest_dist_to_point(fix32 px, fix32 py, vertex a, vertex b); 19 | 20 | #endif -------------------------------------------------------------------------------- /src/colors.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "colors.h" 3 | 4 | 5 | // #define PIX(p) ((p << 4) | p) 6 | // #define DPIX(p,q) ((p << 4) | q) 7 | 8 | 9 | #define BPIX(p) ((p << 4) | p) 10 | 11 | #define BDPIX(p,q) ((p << 4) | q) 12 | 13 | #define PIX(p) ((BPIX(p) << 24) | (BPIX(p) << 16) | (BPIX(p) << 8) | BPIX(p)) 14 | #define DPIX(p,q) ((BDPIX(p,q) << 24) | (BDPIX(p,q) << 16) | (BDPIX(p,q) << 8) | BDPIX(p,q)) 15 | 16 | // DISTANCE 17 | // LIGHT LEVEL NEAR FAR 18 | // 1 LIGHT LIGHT 19 | // 0 LIGHT DARK 20 | // -1 DARK DARK 21 | 22 | // LIGHT LEVEL 23 | // DISTANCE 1 0 -1 24 | // FAR LIGHT DARK DARK 25 | // NEAR LIGHT LIGHT DARK 26 | 27 | // dark dist is -2 brightness 28 | // mid dist is -1 brightness 29 | // -2 is negative 2, etc 30 | 31 | //split byte colors 32 | const u32 dither_color_calc_table[16*5*2] = { 33 | // far table 34 | // -2 light level 35 | 0, DPIX(10,10), DPIX(9,9), DPIX(9,9), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(10,10), DPIX(10,10), DPIX(10,10), DPIX(6,6), DPIX(6,6), DPIX(11,11), DPIX(15,15), 36 | // -1 light level 37 | 0, DPIX(9,10), DPIX(1,9), DPIX(2,9), DPIX(7,6), DPIX(5,6), DPIX(6,6), DPIX(7,6), DPIX(8,6), DPIX(11,10), DPIX(10,10), DPIX(11,10), DPIX(13,6), DPIX(13,6), DPIX(9,11), DPIX(15,15), 38 | // 0 light level 39 | 0, DPIX(9,9), DPIX(1,1), DPIX(2,2), DPIX(7,7), DPIX(5,6), DPIX(6,6), DPIX(7,6), DPIX(8,6), DPIX(11,11), DPIX(10,10), DPIX(11,10), DPIX(13,13), DPIX(13,6), DPIX(9,9), DPIX(15,15), 40 | // 1 light level 41 | 0, DPIX(1,1), DPIX(2,2), DPIX(3,3), DPIX(4,4), DPIX(5,5), DPIX(6,6), DPIX(7,7), DPIX(8,8), DPIX(9,9), DPIX(10,10), DPIX(11,11), DPIX(12,12), DPIX(13,13), DPIX(14,14), DPIX(15,15), 42 | // 2 light level 43 | 0, DPIX(3,3), DPIX(3,3), DPIX(3,3), DPIX(4,4), DPIX(13,13), DPIX(13,13), DPIX(4,4), DPIX(13,13), DPIX(3,3), DPIX(9,9), DPIX(9,9), DPIX(4,4), DPIX(4,4), DPIX(14,14), DPIX(15,15), 44 | // near table 45 | // -2 light level 46 | 0, DPIX(10,10), DPIX(9,9), DPIX(9,9), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(10,10), DPIX(10,10), DPIX(10,10), DPIX(6,6), DPIX(6,6), DPIX(11,11), DPIX(15,15), 47 | // -1 light level 48 | 0, DPIX(9,9), DPIX(1,1), DPIX(2,2), DPIX(7,7), DPIX(5,6), DPIX(6,6), DPIX(7,6), DPIX(8,6), DPIX(11,11), DPIX(10,10), DPIX(11,10), DPIX(13,13), DPIX(6,6), DPIX(9,9), DPIX(15,15), 49 | // 0 light level 50 | 0, DPIX(1,1), DPIX(2,2), DPIX(3,3), DPIX(4,4), DPIX(5,5), DPIX(6,6), DPIX(7,7), DPIX(8,8), DPIX(9,9), DPIX(10,10), DPIX(11,11), DPIX(12,12), DPIX(13,13), DPIX(14,14), DPIX(15,15), 51 | // 1 light level 52 | 0, DPIX(2,2), DPIX(3,2), DPIX(3,2), DPIX(4,4), DPIX(13,5), DPIX(5,5), DPIX(4,7), DPIX(5,5), DPIX(2,2), DPIX(11,11), DPIX(9,11), DPIX(4,12), DPIX(12,12), DPIX(14,14), DPIX(15,15), 53 | // 2 light level 54 | 0, DPIX(3,3), DPIX(3,3), DPIX(3,3), DPIX(4,4), DPIX(13,13), DPIX(13,13), DPIX(4,4), DPIX(13,13), DPIX(3,3), DPIX(9,9), DPIX(9,9), DPIX(4,4), DPIX(4,4), DPIX(14,14), DPIX(15,15), 55 | }; 56 | 57 | 58 | const u32 no_dither_color_calc_table[16*5*2] = { 59 | // far table 60 | // -2 light level 61 | 0, DPIX(10,10), DPIX(9,9), DPIX(9,9), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(10,10), DPIX(10,10), DPIX(10,10), DPIX(6,6), DPIX(6,6), DPIX(11,11), DPIX(15,15), 62 | // -1 light level 63 | 0, DPIX(10,10), DPIX(9,9), DPIX(9,9), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(10,10), DPIX(10,10), DPIX(10,10), DPIX(6,6), DPIX(6,6), DPIX(11,11), DPIX(15,15), 64 | // 0 light level 65 | 0, DPIX(9,9), DPIX(1,1), DPIX(2,2), DPIX(7,7), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(11,11), DPIX(10,10), DPIX(10,10), DPIX(13,13), DPIX(6,6), DPIX(9,9), DPIX(15,15), 66 | // 1 light level 67 | 0, DPIX(1,1), DPIX(2,2), DPIX(3,3), DPIX(4,4), DPIX(5,5), DPIX(6,6), DPIX(7,7), DPIX(8,8), DPIX(9,9), DPIX(10,10), DPIX(11,11), DPIX(12,12), DPIX(13,13), DPIX(14,14), DPIX(15,15), 68 | // 2 light level 69 | 0, DPIX(3,3), DPIX(3,3), DPIX(3,3), DPIX(4,4), DPIX(13,13), DPIX(13,13), DPIX(4,4), DPIX(13,13), DPIX(3,3), DPIX(9,9), DPIX(9,9), DPIX(4,4), DPIX(4,4), DPIX(14,14), DPIX(15,15), 70 | // near table 71 | // -2 light level 72 | 0, DPIX(10,10), DPIX(9,9), DPIX(9,9), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(10,10), DPIX(10,10), DPIX(10,10), DPIX(6,6), DPIX(6,6), DPIX(11,11), DPIX(15,15), 73 | // -1 light level 74 | 0, DPIX(9,9), DPIX(1,1), DPIX(2,2), DPIX(7,7), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(6,6), DPIX(11,11), DPIX(10,10), DPIX(10,10), DPIX(13,13), DPIX(6,6), DPIX(9,9), DPIX(15,15), 75 | // 0 light level 76 | 0, DPIX(1,1), DPIX(2,2), DPIX(3,3), DPIX(4,4), DPIX(5,5), DPIX(6,6), DPIX(7,7), DPIX(8,8), DPIX(9,9), DPIX(10,10), DPIX(11,11), DPIX(12,12), DPIX(13,13), DPIX(14,14), DPIX(15,15), 77 | // 1 light level 78 | 0, DPIX(2,2), DPIX(3,3), DPIX(3,3), DPIX(4,4), DPIX(13,13), DPIX(5,5), DPIX(4,4), DPIX(5,5), DPIX(2,2), DPIX(11,11), DPIX(9,9), DPIX(12,12), DPIX(12,12), DPIX(14,14), DPIX(15,15), 79 | // 2 light level 80 | 0, DPIX(3,3), DPIX(3,3), DPIX(3,3), DPIX(4,4), DPIX(13,13), DPIX(13,13), DPIX(4,4), DPIX(13,13), DPIX(3,3), DPIX(9,9), DPIX(9,9), DPIX(4,4), DPIX(4,4), DPIX(14,14), DPIX(15,15), 81 | }; 82 | 83 | 84 | u8 dither_enabled = 0; 85 | u32* color_calc_table = (u32*)no_dither_color_calc_table; 86 | 87 | void toggle_dither_mode() { 88 | if(dither_enabled) { 89 | dither_enabled = 0; 90 | color_calc_table = (u32*)no_dither_color_calc_table; 91 | } else { 92 | dither_enabled = 1; 93 | color_calc_table = (u32*)dither_color_calc_table; 94 | } 95 | } 96 | 97 | 98 | void init_color_table() { 99 | if(dither_enabled) { 100 | color_calc_table = dither_color_calc_table; 101 | } else { 102 | color_calc_table = no_dither_color_calc_table; 103 | } 104 | } 105 | 106 | // 960 bytes 107 | 108 | u32 get_dark_color(u8 col_idx, s8 light_level) { 109 | u16 light_off = (light_level+2)<<4; 110 | return color_calc_table[light_off+col_idx]; 111 | } 112 | 113 | //inline u32 get_mid_dark_color(u8 col_idx, s8 light_level) { 114 | // u16 light_off = (light_level+1)<<4; 115 | // u16 dist_off = (16*5); 116 | // return color_calc_table[light_off+dist_off+col_idx]; 117 | //} 118 | 119 | u32 get_light_color(u8 col_idx, s8 light_level) { 120 | u16 light_off = (light_level+2)<<4; 121 | //u16 dist_off = (16*5*2); 122 | u16 dist_off = (16*5); 123 | return color_calc_table[light_off+dist_off+col_idx]; 124 | } 125 | -------------------------------------------------------------------------------- /src/colors.h: -------------------------------------------------------------------------------- 1 | #ifndef COLORS_H 2 | #define COLORS_H 3 | 4 | #include 5 | 6 | #define NEAR_DIST 50 7 | #define FIX_0_16_INV_NEAR_DIST (65536/NEAR_DIST) 8 | #define MID_DIST 150 9 | #define FIX_0_16_INV_MID_DIST (65536/MID_DIST) 10 | #define MID_DARK_DIST 250 11 | #define FIX_0_16_INV_MID_DARK_DIST (65536/MID_DARK_DIST) 12 | #define DARK_DIST 350 13 | #define FIX_0_16_INV_DARK_DIST (65536/DARK_DIST) 14 | #define FADE_DIST 400 15 | #define FIX_0_16_INV_FADE_DIST (65536/FADE_DIST) 16 | 17 | extern u8 dither_enabled; 18 | 19 | #define TRANSPARENT_IDX 0x0 20 | #define LIGHT_YELLOW_IDX 0x1 21 | #define LIGHT_BLUE_IDX 0x2 22 | #define LIGHT_GREEN_IDX 0x3 23 | #define LIGHT_RED_IDX 0x4 24 | #define LIGHT_PURPLE_IDX 0x5 25 | #define LIGHT_STEEL_IDX 0x6 26 | #define YELLOW_IDX 0x7 27 | #define BLUE_IDX 0x8 28 | #define GREEN_IDX 0x9 29 | #define RED_IDX 0xA 30 | #define PURPLE_IDX 0xB 31 | #define STEEL_IDX 0xC 32 | #define DARK_YELLOW_IDX 0xD 33 | #define DARK_BLUE_IDX 0xE 34 | #define BLACK_IDX 0xF 35 | 36 | 37 | //u32 get_dark_color(u8 col_idx, s8 light_level); 38 | //u32 get_mid_dark_color(u8 col_idx, s8 light_level); 39 | //u32 get_light_color(u8 col_idx, s8 light_level); 40 | 41 | //extern const u32 color_calc_table[16*3*2]; 42 | //extern const u32 *color_calc_table; //[16*5*2]; 43 | 44 | extern const u32 long_color_table[16]; 45 | 46 | u32 get_dark_color(u8 col_idx, s8 light_level); 47 | u32 get_light_color(u8 col_idx, s8 light_level); 48 | 49 | void init_color_table(); 50 | void toggle_dither_mode(); 51 | 52 | #endif -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | //#define H32_MODE 5 | //#define RAM_TEXTURE 6 | #endif -------------------------------------------------------------------------------- /src/console.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSOLE_H 2 | #define CONSOLE_H 3 | 4 | #include 5 | 6 | uint16_t console_init(uint16_t start_addr); 7 | void console_tick(); 8 | void console_cleanup(); 9 | void console_push_message(char* msg, int len, uint16_t ticks); 10 | void console_push_message_high_priority(char* msg, int len, uint16_t ticks); 11 | 12 | #endif -------------------------------------------------------------------------------- /src/div_lut.h: -------------------------------------------------------------------------------- 1 | #ifndef DIV_LUT_H 2 | #define DIV_LUT_H 3 | 4 | #include 5 | 6 | extern const u32 z_recip_table_32[4096]; 7 | extern const u16 z_recip_table_16[4096]; 8 | extern const u32 z_12_4_to_one_over_z_26[65536]; 9 | #endif -------------------------------------------------------------------------------- /src/draw.h: -------------------------------------------------------------------------------- 1 | #ifndef DRAW_H 2 | #define DRAW_H 3 | 4 | #include 5 | #include "clip_buf.h" 6 | #include "obj_sprite.h" 7 | #include "object.h" 8 | #include "texture.h" 9 | #include "vertex.h" 10 | 11 | #define WALLS_DIST_LIGHTING 12 | #define FLATS_DIST_LIGHTING 13 | 14 | 15 | typedef struct { 16 | u8 x; 17 | u8 y0; 18 | u8 y1; 19 | u8 clip_y0; 20 | u8 clip_y1; 21 | Bitmap *bmp; 22 | } col_params; 23 | 24 | extern int debug_draw_cleared; 25 | 26 | 27 | //void draw_native_vertical_line_unrolled(s16 y0, s16 y1, u8 col, u8* col_ptr); 28 | void draw_native_double_vertical_line_unrolled(s16 y0, s16 y1, s16 y2, u32 full_col1, u32 full_col2, u8* col_ptr); 29 | void draw_native_vertical_line_unrolled(s16 y0, s16 y1, u32 full_col, u8* col_ptr); 30 | void copy_2d_buffer(u16 left, u16 right, clip_buf* dest); 31 | void draw_native_vertical_transparent_line_unrolled(s16 y0, s16 y1, u8 col, u8* col_ptr, u8 odd); 32 | 33 | 34 | typedef struct { 35 | u8 needs_lighting; 36 | u8 mid_y; 37 | u8 dark_y; 38 | u8 fade_y; 39 | u32 light_color; 40 | u32 mid_color; 41 | u32 dark_color; 42 | } light_params; 43 | 44 | void clear_light_cache(); 45 | void cache_floor_light_params(s16 rel_floor_height, u8 floor_col, s8 light_level, light_params* params); 46 | void cache_ceil_light_params(s16 rel_ceil_height, u8 ceil_col, s8 light_level, light_params* params); 47 | 48 | void draw_solid_color_wall(s16 x1, s16 x1_ytop, s16 x1_ybot, 49 | s16 x2, s16 x2_ytop, s16 x2_ybot, 50 | u16 inv_z1, u16 inv_z2, 51 | u16 window_min, u16 window_max, s8 light_level, 52 | u8 wall_color, 53 | light_params* floor_params, light_params* ceil_params); 54 | 55 | void draw_wall(s16 x1, s16 x1_ytop, s16 x1_ybot, 56 | s16 x2, s16 x2_ytop, s16 x2_ybot, 57 | u16 z1, u16 z2, 58 | u16 inv_z1, u16 inv_z2, 59 | u16 window_min, u16 window_max, s8 light_level, 60 | texmap_params* tmap_info, 61 | light_params* floor_params, light_params* ceil_params); 62 | 63 | 64 | void draw_wireframe_wall( 65 | s16 x1, s16 x1_ytop, s16 x1_ybot, 66 | s16 x2, s16 x2_ytop, s16 x2_ybot, 67 | u16 window_min, u16 window_max); 68 | void draw_wireframe_lower_step( 69 | s16 x1, s16 x1_ytop, s16 x1_ybot, 70 | s16 x2, s16 x2_ytop, s16 x2_ybot, 71 | u16 window_min, u16 window_max); 72 | 73 | 74 | void draw_top_pegged_wall(s16 x1, s16 x1_ytop, s16 x1_ybot, 75 | s16 x2, s16 x2_ytop, s16 x2_ybot, 76 | u16 z1_12_4, u16 z2_12_4, 77 | u16 inv_z1, u16 inv_z2, 78 | u16 window_min, u16 window_max, s8 light_level, 79 | texmap_params *tmap_info, 80 | light_params* floor_params, light_params* ceil_params, 81 | s16 x1_pegged_top, s16 x2_pegged_top); 82 | 83 | void draw_bot_pegged_wall(s16 x1, s16 x1_ytop, s16 x1_ybot, 84 | s16 x2, s16 x2_ytop, s16 x2_ybot, 85 | u16 z1_12_4, u16 z2_12_4, 86 | u16 inv_z1, u16 inv_z2, 87 | u16 window_min, u16 window_max, s8 light_level, 88 | texmap_params* tmap_info, 89 | light_params* floor_params, light_params* ceil_params, 90 | s16 x1_pegged_top, s16 x2_pegged_top); 91 | 92 | void draw_top_pegged_textured_upper_step(s16 x1, s16 x1_ytop, s16 nx1_ytop, s16 x2, s16 x2_ytop, s16 nx2_ytop, 93 | u16 z1_12_4, u16 z2_12_4, 94 | u16 inv_z1, u16 inv_z2, 95 | u16 window_min, u16 window_max, s8 light_level, 96 | texmap_params* tmap_info, light_params* params, 97 | s16 x1_pegged_top, s16 x2_pegged_top); 98 | 99 | void draw_upper_step(s16 x1, s16 x1_ytop, s16 nx1_ytop, s16 x2, s16 x2_ytop, s16 nx2_ytop, 100 | u16 inv_z1, u16 inv_z2, 101 | u16 window_min, u16 window_max, u8 upper_color, s8 light_level, light_params* params); 102 | 103 | void draw_ceiling_update_clip(s16 x1, s16 x1_ytop, s16 x2, s16 x2_ytop, 104 | u16 far_inv_z, 105 | u16 window_min, u16 window_max, light_params* params); 106 | 107 | 108 | void draw_bottom_pegged_textured_lower_step(s16 x1, s16 x1_ybot, s16 nx1_ybot, s16 x2, s16 x2_ybot, s16 nx2_ybot, 109 | u16 z1_12_4, u16 z2_12_4, 110 | u16 inv_z1, u16 inv_z2, 111 | u16 window_min, u16 window_max, s8 light_level, 112 | texmap_params* tmap_info, light_params* params, 113 | s16 x1_pegged_bot, s16 x2_pegged_bot); 114 | 115 | void draw_lower_step(s16 x1, s16 x1_ybot, s16 nx1_ybot, s16 x2, s16 x2_ybot, s16 nx2_ybot, 116 | u16 inv_z1, u16 inv_z2, 117 | u16 window_min, u16 window_max, u8 lower_color, s8 light_level, light_params* params); 118 | 119 | void draw_floor_update_clip(s16 x1, s16 x1_ybot, s16 x2, s16 x2_ybot, 120 | u16 far_inv_z, 121 | u16 window_min, u16 window_max, light_params* params); 122 | 123 | 124 | void init_sprite_draw_cache(); 125 | 126 | 127 | extern obj_type drawn_to_center_cols; 128 | extern object_link sprite_on_center_col; 129 | extern u16 center_object_sector; 130 | 131 | // reset whether a sprite was drawn to the center of the screen 132 | void reset_sprite_hit_info(); 133 | 134 | void draw_rle_sprite(s16 x1, s16 x2, s16 ytop, s16 ybot, 135 | u16 window_min, u16 window_max, 136 | clip_buf* clipping_buffer, 137 | const rle_sprite* obj, 138 | object_link obj_link, obj_type obj_type, u16 sector); 139 | 140 | void draw_forcefield(s16 x1, s16 x2, 141 | u16 window_min, u16 window_max, 142 | clip_buf* clipping_buffer, 143 | u8 wall_col); 144 | 145 | 146 | void clear_2d_buffers(); 147 | void init_2d_buffers(); 148 | void release_2d_buffers(); 149 | 150 | 151 | 152 | 153 | #endif -------------------------------------------------------------------------------- /src/editor_test_map.h: -------------------------------------------------------------------------------- 1 | #ifndef EDITOR_TEST_MAP 2 | #define EDITOR_TEST_MAP 3 | 4 | #include "portal_map.h" 5 | 6 | 7 | extern const portal_map editor_test_map; 8 | 9 | #endif -------------------------------------------------------------------------------- /src/fire.h: -------------------------------------------------------------------------------- 1 | #ifndef FIRE_H 2 | #define FIRE_H 3 | 4 | #include "game_mode.h" 5 | 6 | 7 | void init_fire(); 8 | game_mode run_fire(); 9 | void cleanup_fire(); 10 | 11 | 12 | #endif -------------------------------------------------------------------------------- /src/fire_tables.py: -------------------------------------------------------------------------------- 1 | NUM_PIX = 2 2 | NUM_BITS = NUM_PIX+1 3 | 4 | def rel_dst_for_bits(bits): 5 | tbl = [-1,0,1,2] 6 | return tbl[bits] 7 | 8 | def use_tbl_for_bits(bits): 9 | return bits > 0 10 | 11 | 12 | def tbl_lookup(name, bits, is_left_side=False): 13 | if bits > 0: 14 | if is_left_side: 15 | return "table_{}_shift[{}]".format(bits-1, name) 16 | else: 17 | return "table_{}[{}]".format(bits-1,name) 18 | else: 19 | return name 20 | 21 | def compile_routine(bits): 22 | dsts = {} 23 | 24 | for i in range(NUM_PIX): 25 | pix_bits = ((0b11<>i 26 | rel_dst = rel_dst_for_bits(pix_bits) 27 | abs_dst = rel_dst + i 28 | dsts[abs_dst] = (i, pix_bits) 29 | 30 | reads = {} 31 | writes = {} 32 | code = [] 33 | for write_off,(read_off,bits) in dsts.items(): 34 | name = 'fire_{}'.format(read_off, write_off) 35 | reads[read_off] = (name, bits) 36 | writes[write_off] = (name,bits) 37 | 38 | cur_read_off = 0 39 | for read_off, (name,bits) in reads.items(): 40 | if cur_read_off < read_off: 41 | change = read_off - cur_read_off 42 | code.append('src_ptr += {};'.format(change)) 43 | cur_read_off += change 44 | code.append('{} = *src_ptr++;'.format(name)) 45 | cur_read_off += 1 46 | 47 | 48 | cur_write_off = 0 49 | wrote_aligned = False 50 | 51 | for write_off,(name,bits) in writes.items(): 52 | if wrote_aligned: 53 | wrote_aligned = False 54 | continue 55 | 56 | aligned = (write_off&0b1 == 0) 57 | sibling = write_off+1 in writes 58 | 59 | if cur_write_off < write_off: 60 | if write_off > NUM_PIX: 61 | diff = write_off - cur_write_off 62 | tbl_var = tbl_lookup(name, bits) 63 | code.append('*(dst_ptr-{}) = {};'.format(diff, tbl_var)) 64 | continue 65 | else: 66 | change = write_off - cur_write_off 67 | code.append('dst_ptr += {};'.format(change)) 68 | cur_write_off += change 69 | if aligned and sibling: 70 | sibling_name,sibling_bits = writes[write_off+1] 71 | tbl_var = tbl_lookup(name, bits, True) 72 | sibling_tbl_var = tbl_lookup(sibling_name, sibling_bits) 73 | code.append("*((u16*)dst_ptr) = {}|{};".format(tbl_var, sibling_tbl_var)) 74 | wrote_aligned = True 75 | else: 76 | tbl_var = tbl_lookup(name, bits) 77 | if write_off < cur_write_off: 78 | diff = cur_write_off - write_off 79 | code.append("*(dst_ptr-{}) = {};".format(diff, tbl_var)) 80 | else: 81 | if cur_write_off >= NUM_PIX: 82 | code.append("*dst_ptr = {};".format(tbl_var)) 83 | else: 84 | code.append("*dst_ptr++ = {};".format(tbl_var)) 85 | cur_write_off += 1 86 | 87 | 88 | 89 | if cur_read_off != NUM_PIX: 90 | change = NUM_PIX - cur_read_off 91 | code.append("src_ptr += {};".format(change)) 92 | 93 | if cur_write_off != NUM_PIX: 94 | change = NUM_PIX - cur_write_off 95 | code.append("dst_ptr += {};".format(change)) 96 | 97 | return code 98 | 99 | 100 | 101 | def compile_switch(): 102 | code = ["switch(rand_bits & 0b{})".format('1'*NUM_BITS) + " {"] 103 | 104 | for bits in range(2**NUM_BITS): 105 | code.append(" case {}:".format(bits)) 106 | for line in compile_routine(bits): 107 | code.append(" {}".format(line)) 108 | code.append(" break;") 109 | 110 | code.append("}") 111 | return "\n".join(code) 112 | -------------------------------------------------------------------------------- /src/game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | 4 | #include 5 | #include "draw.h" 6 | #include "game_mode.h" 7 | #include "object.h" 8 | #include "vertex.h" 9 | 10 | extern int cur_frame; 11 | 12 | extern u32 last_frame_ticks; 13 | extern u16 rot_speed; 14 | extern u32 move_speed; 15 | 16 | extern u8* bmp_buffer_0; 17 | extern u8* bmp_buffer_1; 18 | 19 | extern Vect2D_f32 *sector_centers; 20 | 21 | extern player_pos cur_player_pos; 22 | extern fix16 playerXFrac4; 23 | extern fix16 playerYFrac4; 24 | extern s16 playerZCam12Frac4; 25 | 26 | #define ANGLE_360_DEGREES 1024 27 | #define ANGLE_180_DEGREES 512 28 | #define ANGLE_90_DEGREES 256 29 | // 58 degrees from player viewpoint to top left of map 30 | #define ANGLE_58_DEGREES 164 31 | 32 | #define RENDER_WIDTH 64 33 | 34 | 35 | extern fix32 angleCos32; 36 | extern fix32 angleSin32; 37 | extern fix16 angleCos16; 38 | extern fix16 angleSin16; 39 | extern s16 angleSinFrac12; 40 | extern s16 angleCosFrac12; 41 | extern s16 playerXInt; 42 | extern s16 playerYInt; 43 | 44 | 45 | void init_game(); 46 | game_mode run_game(); 47 | void cleanup_game(); 48 | 49 | void request_flip(); 50 | 51 | extern u8 render_mode; 52 | 53 | typedef enum { 54 | RENDER_SOLID, 55 | RENDER_WIREFRAME 56 | } render_modes; 57 | 58 | #endif -------------------------------------------------------------------------------- /src/game_mode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fire.h" 3 | #include "game.h" 4 | #include "game_mode.h" 5 | #include "main_menu.h" 6 | 7 | void print_invalid_mode(); 8 | 9 | mode modes[7] = { 10 | {.name = "invalid mode", .start_up = &print_invalid_mode}, 11 | {.name = "invalid mode", .start_up = &print_invalid_mode}, 12 | {.name = "INTRO", .start_up = &init_fire, .clean_up = &cleanup_fire, .run = &run_fire}, 13 | {.name = "MAIN_MENU", .start_up = &init_main_menu, .clean_up = &cleanup_main_menu, .run = &run_main_menu}, 14 | {.name = "IN_GAME", .start_up = &init_game, .clean_up = &cleanup_game, .run = &run_game}, 15 | {.name = "PAUSE_MENU", .start_up = &print_invalid_mode}, 16 | {.name = "CREDITS", .start_up = &print_invalid_mode}, 17 | }; 18 | 19 | game_mode cur_game_mode; 20 | 21 | void print_invalid_mode() { 22 | char buf[32]; 23 | sprintf(buf, "Invalid mode %i/'%s'", cur_game_mode, modes[cur_game_mode].name); 24 | while(1) { 25 | VDP_drawText(buf, 10, 10); 26 | } 27 | } 28 | 29 | 30 | void set_game_mode(game_mode md) { 31 | if(md != RESET_MODE) { 32 | cur_game_mode = md; 33 | } 34 | KLog("going to game mode: "); 35 | KLog(modes[cur_game_mode].name); 36 | modes[cur_game_mode].start_up(); 37 | } 38 | 39 | void run_game_mode() { 40 | 41 | game_mode next_mode = modes[cur_game_mode].run(); 42 | 43 | if(next_mode != SAME_MODE) { 44 | modes[cur_game_mode].clean_up(); 45 | set_game_mode(next_mode); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/game_mode.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_MODE_H 2 | #define GAME_MODE_H 3 | 4 | 5 | typedef enum { 6 | SAME_MODE=0, 7 | RESET_MODE=1, 8 | INTRO=2, 9 | MAIN_MENU=3, 10 | IN_GAME=4, 11 | PAUSE_MENU=5, 12 | CREDITS=6 13 | } game_mode; 14 | 15 | 16 | typedef struct { 17 | char* name; 18 | void (*start_up)(); 19 | game_mode (*run)(); 20 | void (*clean_up)(); 21 | } mode; 22 | 23 | extern game_mode cur_game_mode; 24 | void run_game_mode(); 25 | void set_game_mode(game_mode md); 26 | 27 | #endif -------------------------------------------------------------------------------- /src/imgui.ini: -------------------------------------------------------------------------------- 1 | [Window][Map] 2 | Pos=0,0 3 | Size=1280,800 4 | Collapsed=0 5 | 6 | [Window][Debug##Default] 7 | Pos=60,60 8 | Size=400,400 9 | Collapsed=0 10 | 11 | [Window][Tools] 12 | Pos=823,154 13 | Size=420,536 14 | Collapsed=0 15 | 16 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #include "genesis.h" 2 | 3 | const u32 start_in_game_arr[2] = { 4 | 0xFEEDBEEF, 5 | 0 6 | }; 7 | int init_load_level = 0; 8 | 9 | const int instant_load_level_array[2] = { 10 | 0xBEEFFEED, 11 | 0 12 | }; -------------------------------------------------------------------------------- /src/init.h: -------------------------------------------------------------------------------- 1 | #ifndef INIT_H 2 | #define INIT_H 3 | 4 | 5 | extern const u32 start_in_game_arr[2]; 6 | 7 | extern int init_load_level; 8 | 9 | extern const int instant_load_level_array[2]; 10 | 11 | #endif -------------------------------------------------------------------------------- /src/inventory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hud.h" 3 | #include "inventory.h" 4 | #include "utils.h" 5 | 6 | #define MAX_ITEMS 8 7 | 8 | 9 | static int num_items; 10 | static int dirty; 11 | 12 | static u16 hud_tile_loc; 13 | 14 | static u8 *inventory; 15 | 16 | static u16 *item_vram_addresses; //[NUM_ITEM_TYPES]; 17 | 18 | #define HUD_BASE_Y 21 19 | 20 | int inventory_full() { 21 | return (num_items == MAX_ITEMS); 22 | } 23 | 24 | int inventory_add_item(item_type item) { 25 | if(inventory_full()) { 26 | return 0; 27 | } 28 | inventory[num_items++] = item; 29 | KLog_U1("added item to index: ", num_items-1); 30 | dirty = 1; 31 | return 1; 32 | } 33 | 34 | int inventory_has_item(item_type item) { 35 | for(int i = 0; i < num_items; i++) { 36 | if(inventory[i] == item) { return 1; } 37 | } 38 | return 0; 39 | } 40 | 41 | 42 | 43 | void inventory_draw() { 44 | if(!dirty) { 45 | return; 46 | } 47 | 48 | 49 | 50 | int ix,iy; 51 | ix = 0; 52 | iy = 0; 53 | for(int i = 0; i < num_items; i++) { 54 | u16 tile_addr = item_vram_addresses[inventory[i]]; 55 | 56 | 57 | VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(2, 1, 0, 0, tile_addr), 58 | ix*2+2, HUD_BASE_Y+2+iy); 59 | ix++; 60 | if(ix >= 5) { 61 | iy++; 62 | ix = 0; 63 | } 64 | } 65 | dirty = 0; 66 | } 67 | 68 | 69 | 70 | void inventory_reset() { 71 | num_items = 0; 72 | //inventory[0] = HEALTH_RECHARGE; 73 | //inventory[1] = ARMOR_CHARGE; 74 | //inventory[2] = ARMOR_CHARGE; 75 | //inventory[3] = SHIELD_CHARGE; 76 | //inventory[4] = BULLET_CHARGE; 77 | dirty = 1; 78 | } 79 | 80 | u32 load_item(const Image* img, item_type type, u32 tile_loc) { 81 | VDP_loadTileSet(img->tileset, tile_loc, CPU); 82 | item_vram_addresses[type] = tile_loc; 83 | tile_loc += img->tileset->numTile; 84 | return tile_loc; 85 | } 86 | 87 | u32 load_inventory_items_to_vram(u32 tile_loc) { 88 | hud_tile_loc = tile_loc; 89 | VDP_loadTileSet(hud.tileset, hud_tile_loc, DMA); 90 | 91 | PAL_setPalette(PAL2, hud.palette->data); 92 | tile_loc += hud.tileset->numTile; 93 | 94 | tile_loc = load_item(&armor_inv, ARMOR_CHARGE, tile_loc); 95 | tile_loc = load_item(&blue_key_inv, BLUE_KEY, tile_loc); 96 | tile_loc = load_item(&red_key_inv, RED_KEY, tile_loc); 97 | tile_loc = load_item(&green_key_inv, GREEN_KEY, tile_loc); 98 | tile_loc = load_item(&bullet_inv, BULLET_CHARGE, tile_loc); 99 | tile_loc = load_item(&heart_inv, HEALTH_RECHARGE, tile_loc); 100 | tile_loc = load_item(&shield_inv, SHIELD_CHARGE, tile_loc); 101 | 102 | 103 | return tile_loc; 104 | 105 | } 106 | 107 | u32 inventory_init(u32 free_tile_loc) { 108 | inventory = malloc(sizeof(u8) * MAX_ITEMS, "inventory table"); 109 | 110 | item_vram_addresses = malloc(sizeof(u16) * NUM_ITEM_TYPES, "inventory item vram addr table"); 111 | u32 new_free_tile_loc = load_inventory_items_to_vram(free_tile_loc); 112 | 113 | for(int y = 0; y < hud.tilemap->h; y++) { 114 | u16 y_off = y * hud.tilemap->w; 115 | for(int x = 0; x < hud.tilemap->w; x++) { 116 | u16 tile_attr = hud.tilemap->tilemap[y_off+x]; 117 | tile_attr += hud_tile_loc; 118 | tile_attr |= (1< 5 | 6 | typedef enum { 7 | RED_KEY, 8 | GREEN_KEY, 9 | BLUE_KEY, 10 | SPEED_UP, 11 | POWER_UP, 12 | HEALTH_RECHARGE, 13 | SHIELD_CHARGE, 14 | BULLET_CHARGE, 15 | ARMOR_CHARGE, 16 | } item_type; 17 | 18 | #define NUM_ITEM_TYPES 9 19 | 20 | void inventory_draw(); 21 | void inventory_reset(); 22 | int inventory_add_item(item_type item); 23 | int inventory_full(); 24 | int inventory_has_item(item_type item); 25 | void inventory_use_item(item_type item); 26 | 27 | 28 | u32 inventory_init(u32 free_tile_loc); 29 | void inventory_cleanup(); 30 | 31 | #endif -------------------------------------------------------------------------------- /src/joy_helper.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static u16 last_joy = 0; 4 | 5 | 6 | int joy_button_newly_pressed(u16 button) { 7 | return (JOY_readJoypad(JOY_1) & button & (~last_joy) & button); 8 | } 9 | 10 | int joy_button_held(u16 button) { 11 | return (JOY_readJoypad(JOY_1) & last_joy & button); 12 | } 13 | 14 | int joy_button_pressed(u16 button) { 15 | return (JOY_readJoypad(JOY_1) & button); 16 | } 17 | 18 | void update_joy() { 19 | last_joy = JOY_readJoypad(JOY_1); 20 | } -------------------------------------------------------------------------------- /src/joy_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef JOY_HELPER_H 2 | #define JOY_HELPER_H 3 | 4 | #include 5 | 6 | 7 | int joy_button_pressed(u16 button); 8 | int joy_button_newly_pressed(u16 button); 9 | int joy_button_held(u16 button); 10 | 11 | void update_joy(); 12 | 13 | 14 | 15 | #endif -------------------------------------------------------------------------------- /src/js/pvs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PVS construction 5 | 6 | 7 | 8 |
PVS construction test
9 | Construct PVS for Sector 10 | 25 | 26 |
27 |
28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/level.h: -------------------------------------------------------------------------------- 1 | #ifndef LEVEL_H 2 | #define LEVEL_H 3 | 4 | #include "portal_map.h" 5 | 6 | extern portal_map* cur_portal_map; 7 | 8 | 9 | 10 | void load_portal_map(portal_map* l); 11 | void clean_portal_map(); 12 | // returns 1 if a switch or door was triggered, else 0 13 | int check_trigger_switch(player_pos* pos); 14 | 15 | #endif -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "game_mode.h" 4 | #include "joy_helper.h" 5 | 6 | #include "my_bmp.h" 7 | 8 | #include "init.h" 9 | 10 | 11 | int main() { 12 | VDP_init(); 13 | MEM_pack(); 14 | KLog_U1("allocated mem at start: ", MEM_getAllocated()); 15 | 16 | KLog_U1("free bytes of ram at startup: ", MEM_getFree()); 17 | volatile u32* vp_start_in_game_arr = start_in_game_arr; 18 | u16 startup_joy = JOY_readJoypad(JOY_1); 19 | u8 bypass_autoload = (startup_joy & BUTTON_START); 20 | 21 | if(vp_start_in_game_arr[1] && !bypass_autoload) { 22 | // i don't know why this is required, I guess some VRAM remapping or something 23 | // without it, fps display doesn't work in-game 24 | // such a dumb hack 25 | 26 | // basically, it works if you run the INTRO fire mode, which calls these functions 27 | //bmp_init_horizontal(0, BG_A, PAL1, 0); 28 | //bmp_end(); 29 | //MEM_pack(); 30 | DMA_setBufferSize(2048); 31 | set_game_mode(IN_GAME); 32 | } else { 33 | set_game_mode(INTRO); 34 | } 35 | 36 | while(1) { 37 | 38 | update_joy(); 39 | 40 | run_game_mode(); 41 | 42 | } 43 | return (0); 44 | } 45 | -------------------------------------------------------------------------------- /src/main_menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "colors.h" 3 | #include "game.h" 4 | #include "game_mode.h" 5 | #include "init.h" 6 | #include "menu_helper.h" 7 | #include "portal_map.h" 8 | #include "sfx.h" 9 | #include "music.h" 10 | 11 | static int launch_new_mode = 0; 12 | game_mode target_mode; 13 | 14 | void go_to_credits() { 15 | launch_new_mode = 1; 16 | target_mode = CREDITS; 17 | } 18 | 19 | void go_to_new_game() { 20 | launch_new_mode = 1; 21 | target_mode = IN_GAME; 22 | } 23 | 24 | 25 | #define TEXT_ITEM(str) {.text = (str), .submenu = NULL, .select = NULL, .render = NULL, .selectable = 0} 26 | 27 | const menu load_game_menu = { 28 | .header_text = "Load game", 29 | .num_items = 1, 30 | .items = { 31 | TEXT_ITEM("No saves found") 32 | } 33 | }; 34 | 35 | #include "map_table.h" 36 | 37 | void select_map(int menu_idx) { 38 | launch_new_mode = 1; 39 | init_load_level = menu_idx; 40 | target_mode = IN_GAME; 41 | } 42 | 43 | menu level_select_menu = { 44 | .header_text = "Level select", 45 | .num_items = 4, 46 | .items = { 47 | {.text = "Slime room test map", .submenu = NULL, .select = &select_map, .selectable=1}, 48 | {.text = "Overlapping rooms test map", .submenu = NULL, .select = &select_map, .selectable=1}, 49 | {.text = "Building test map", .submenu = NULL, .select = &select_map, .selectable=1}, 50 | {.text = "NO MAP", .submenu = NULL, .select = &select_map, .selectable=0}, 51 | 52 | } 53 | }; 54 | 55 | 56 | void populate_level_select() { 57 | volatile u32* vmap_table = map_table; 58 | 59 | volatile uint32_t num_maps = map_table[1] >= 6 ? 6 : vmap_table[1]; 60 | KLog_U1("num maps: ", num_maps); 61 | for(u32 i = 0; i < num_maps; i++) { 62 | KLog_U1("setting map for index: ", i); 63 | portal_map* pm = (portal_map*)vmap_table[2+i]; 64 | level_select_menu.items[i].text = pm->name; 65 | KLog_U1("pointer to name is : ", (u32)(level_select_menu.items[i].text)); 66 | level_select_menu.items[i].selectable = 1; 67 | } 68 | } 69 | 70 | char* draw_sfx_state(int menu_idx) { 71 | return (sfx_on ? "ON " : "OFF"); 72 | 73 | } 74 | 75 | char* draw_music_state(int menu_idx) { 76 | return (music_on ? "ON " : "OFF"); 77 | } 78 | 79 | char* draw_dither_mode_state(int menu_idx) { 80 | return (dither_enabled ? "ON " : "OFF"); 81 | } 82 | 83 | const menu options_menu = { 84 | .header_text = "Options", 85 | .num_items = 3, 86 | .items = { 87 | {.text = "Sound Effects: ", .submenu = NULL, .select = &toggle_sfx, .render = &draw_sfx_state, .selectable=1}, 88 | {.text = "Music: ", .submenu = NULL, .select = &toggle_music, .render = &draw_music_state, .selectable=1}, 89 | {.text = "Dithering Mode: ", .submenu = NULL, .select = &toggle_dither_mode, .render = &draw_dither_mode_state, .selectable=1} 90 | } 91 | }; 92 | 93 | 94 | const menu credits_menu = { 95 | .header_text = "Credits", 96 | .num_items = 1, 97 | .items = { 98 | TEXT_ITEM("...."), 99 | } 100 | }; 101 | 102 | 103 | const menu main_menu = { 104 | .header_text = "", 105 | .num_items = 5, 106 | .items = { 107 | {.text = "New game", .submenu = NULL, .select = &go_to_new_game, .selectable=1}, 108 | {.text = "Level select", .submenu = &level_select_menu, .select = populate_level_select, .selectable=1}, 109 | {.text = "Load game", .submenu = &load_game_menu, .select = NULL, .selectable=1}, 110 | {.text = "Options", .submenu = &options_menu, .select = NULL, .selectable=1}, 111 | {.text = "Credits", .submenu = &credits_menu, .select = NULL, .selectable=1} // .select = &go_to_credits}, 112 | } 113 | }; 114 | 115 | menu_state main_menu_state; 116 | 117 | void init_main_menu() { 118 | VDP_setScreenWidth320(); 119 | VDP_setScreenHeight240(); 120 | launch_new_mode = 0; 121 | init_menu_state(&main_menu, &main_menu_state); 122 | } 123 | 124 | 125 | game_mode run_main_menu() { 126 | launch_new_mode = 0; 127 | run_menu(&main_menu_state); 128 | if(launch_new_mode) { 129 | return target_mode; 130 | } 131 | return SAME_MODE; 132 | } 133 | 134 | void cleanup_main_menu() { 135 | 136 | } -------------------------------------------------------------------------------- /src/main_menu.h: -------------------------------------------------------------------------------- 1 | #ifndef MENU_H 2 | #define MENU_H 3 | 4 | void init_main_menu(); 5 | game_mode run_main_menu(); 6 | void cleanup_main_menu(); 7 | 8 | 9 | 10 | #endif -------------------------------------------------------------------------------- /src/map_table.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "portal_map.h" 3 | #include "maps.h" 4 | 5 | #include "map_table.h" 6 | 7 | #include "utils.h" 8 | 9 | 10 | 11 | const uint32_t map_table[MAX_MAPS+2] = { 12 | 0xDEADBEEF, 13 | 3, // number of maps 14 | &overlapping_map, 15 | &editor_test_map, 16 | &building_test_map, 17 | 0, 0 18 | //&empty_map_1, //&empty_map_2, &empty_map_3, 19 | }; 20 | 21 | 22 | 23 | // 2MB of space for map, texture, sprite, and palette data 24 | const uint8_t wad_area[512*1024*2] = { 25 | 'W','A','D','?', 26 | }; 27 | // 2048*1024 28 | 29 | -------------------------------------------------------------------------------- /src/map_table.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_TABLE_H 2 | #define MAP_TABLE_H 3 | 4 | #include "portal_map.h" 5 | 6 | #define NUM_MAPS 3 7 | #define MAX_MAPS 5 8 | const uint32_t map_table[MAX_MAPS+2]; 9 | 10 | #endif -------------------------------------------------------------------------------- /src/maps.h: -------------------------------------------------------------------------------- 1 | #ifndef MAPS_H 2 | #define MAPS_H 3 | 4 | //#include "torus_map.h" 5 | #include "editor_test_map.h" 6 | #include "overlapping_test_map.h" 7 | #include "building_test_map.h" 8 | 9 | #endif -------------------------------------------------------------------------------- /src/math3d.h: -------------------------------------------------------------------------------- 1 | #ifndef MATH3D_H 2 | #define MATH3D_H 3 | 4 | #include 5 | #include "texture.h" 6 | #include "utils.h" 7 | #include "vertex.h" 8 | 9 | // .888 10 | #define ASPECT_RATIO (SCREEN_WIDTH / SCREEN_HEIGHT) // 0.44 11 | 12 | // 3d->2d projection constants 13 | 14 | /* 15 | #define SCALE 1 16 | #define CONST1 64 // (SCREEN_WIDTH/2) 17 | #define CONST2 60 // ((SCREEN_WIDTH/2) * SCALE / min(1, ASPECT_RATIO)) 18 | #define CONST3 72 //(SCREEN_HEIGHT/2) 19 | #define CONST4 72 //(SCREEN_HEIGHT/2 * SCALE / max(1, ASPECT_RATIO)) 20 | */ 21 | 22 | #define SCALE 1 23 | #define CONST1 32 24 | #define CONST2 32 //72 25 | #define CONST3 72 26 | #define CONST4 72 27 | 28 | 29 | typedef struct { 30 | s16 x, yfloor, yceil; 31 | } transformed_vert; 32 | 33 | 34 | typedef enum { 35 | UNCLIPPED = 0b00000000, 36 | //OFFSCREEN = 0b00000001, 37 | NEAR_Z_CULLED = 0b00000001, 38 | FRUSTUM_CULLED = 0b00000010, 39 | LEFT_Z_CLIPPED = 0b00000100, 40 | RIGHT_Z_CLIPPED = 0b00001000, 41 | LEFT_FRUSTUM_CLIPPED = 0b00010000, 42 | RIGHT_FRUSTUM_CLIPPED = 0b00100000, 43 | } clip_result; 44 | 45 | //#define NEAR_Z_32 (20 << FIX32_FRAC_BITS) 46 | // NEAR_Z_16 10 47 | #define NEAR_Z_16 10 48 | //25 49 | #define NEAR_Z_FIX (NEAR_Z_16< 2 | #include "menu_helper.h" 3 | #include "joy_helper.h" 4 | 5 | int cur_line; 6 | 7 | void reset_menu_frame() { 8 | cur_line = 0; 9 | } 10 | 11 | void draw_line(char* txt) { 12 | int num_chars = strlen(txt); 13 | int diff = 40 - num_chars; 14 | 15 | VDP_drawTextBG(BG_A, txt, (diff/2)-1, cur_line); // 12 16 | cur_line += 2; 17 | } 18 | 19 | int find_first_selectable_item(const menu* m) { 20 | for(int i = 0; i < m->num_items; i++) { 21 | if(m->items[i].selectable) { 22 | return i; 23 | } 24 | } 25 | return -1; 26 | } 27 | 28 | void init_menu_state(const menu* m, menu_state* s) { 29 | s->cur_menu = m; 30 | s->cur_item = find_first_selectable_item(m); 31 | } 32 | 33 | void clear_menu() { 34 | cur_line = 0; 35 | for(int i = 0; i < 240/8; i++) { 36 | VDP_drawText(" ", 0, i); 37 | } 38 | } 39 | 40 | void run_menu(menu_state* st) { 41 | 42 | const menu* cur_menu = st->cur_menu; 43 | int cur_item = st->cur_item; 44 | const menu* prev_menu = st->prev_menu; 45 | 46 | 47 | int max_lines = 30; 48 | int num_lines = cur_menu->num_items*2; 49 | int diff = max_lines - num_lines; 50 | int start_off = diff/2; 51 | 52 | cur_line = start_off; 53 | 54 | draw_line(cur_menu->header_text); 55 | 56 | char buf[40]; 57 | 58 | for(int i = 0; i < cur_menu->num_items; i++) { 59 | const menu_item* rend_item = &(cur_menu->items[i]); 60 | char* rend_str = (rend_item->render == NULL ? "" : rend_item->render(i)); 61 | char* cursor = (cur_item == i) ? ">" : " "; 62 | 63 | sprintf(buf, "%s %s %s", cursor, rend_item->text, rend_str); 64 | draw_line(buf); 65 | } 66 | 67 | int selectable = cur_menu->items[cur_item].selectable != 0; 68 | 69 | if(joy_button_newly_pressed(BUTTON_UP) && (cur_item != -1)) { 70 | cur_item = ((st->cur_item == 0) ? cur_item : cur_item-1); 71 | } else if (joy_button_newly_pressed(BUTTON_DOWN) && (cur_item != -1)) { 72 | cur_item = ((st->cur_item == cur_menu->num_items-1) ? cur_item : st->cur_item+1); 73 | } else if(joy_button_newly_pressed(BUTTON_A) || joy_button_newly_pressed(BUTTON_START) && selectable) { 74 | if(cur_menu->items[cur_item].select != NULL) { 75 | cur_menu->items[cur_item].select(cur_item); 76 | } 77 | 78 | if (cur_menu->items[cur_item].submenu != NULL) { 79 | clear_menu(cur_menu); 80 | prev_menu = cur_menu; 81 | cur_menu = cur_menu->items[cur_item].submenu; 82 | cur_item = find_first_selectable_item(cur_menu); 83 | } 84 | 85 | } else if(joy_button_newly_pressed(BUTTON_B) && (prev_menu != NULL)) { 86 | clear_menu(cur_menu); 87 | cur_menu = prev_menu; 88 | prev_menu = NULL; // CAN ONLY NEST ONCE! 89 | cur_item = find_first_selectable_item(cur_menu); 90 | 91 | } 92 | 93 | st->cur_item = cur_item; 94 | st->cur_menu = cur_menu; 95 | st->prev_menu = prev_menu; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /src/menu_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef MENU_HELPER_H 2 | #define MENU_HELPER_H 3 | 4 | // a menu is a list of items which are either text, or text + another submenu you can enter 5 | 6 | typedef struct menu menu; 7 | 8 | typedef struct { 9 | char* text; 10 | const menu* submenu; 11 | void (*select)(int menu_idx); 12 | char* (*render)(int menu_idx); 13 | int selectable; 14 | } menu_item; 15 | 16 | struct menu { 17 | char* header_text; 18 | int num_items; 19 | menu_item items[]; 20 | }; 21 | 22 | typedef struct { 23 | const menu* cur_menu; 24 | int cur_item; 25 | const menu* prev_menu; 26 | } menu_state; 27 | 28 | void init_menu_state(const menu* m, menu_state* st); 29 | 30 | void run_menu(menu_state* st); 31 | 32 | void clear_menu(); 33 | 34 | #define MAX_MENU_ITEMS 14 35 | 36 | 37 | #endif -------------------------------------------------------------------------------- /src/music.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "music_res.h" 3 | 4 | int music_on = TRUE; 5 | 6 | const u8* songs[2] = { 7 | //xgm_e2m2, xgm_e1m4 8 | NULL, NULL, 9 | }; 10 | const char const* song_names[2] = { 11 | "E2M2", 12 | "E1M4" 13 | }; 14 | 15 | void toggle_music(int menu_idx) { 16 | music_on = !music_on; 17 | } -------------------------------------------------------------------------------- /src/music.h: -------------------------------------------------------------------------------- 1 | #ifndef MUSIC_H 2 | #define MUSIC_H 3 | #include 4 | 5 | extern int music_on; 6 | void toggle_music(); 7 | 8 | #endif -------------------------------------------------------------------------------- /src/my_bmp.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | extern volatile u32 prev_end_of_frame_vints; 5 | extern volatile u32 end_of_frame_vints; 6 | 7 | extern volatile u32 vints; 8 | //VDPPlane bmp_plane; 9 | //u8 bmp_pal; 10 | //u8 bmp_prio; 11 | //u16* bmp_plane_addr; 12 | //u8 bmp_doublebuffer; 13 | 14 | /* 15 | 16 | **---- bitmap mode module ----** 17 | 18 | - copied with modifications from SGDK 19 | 20 | */ 21 | 22 | 23 | //u8* bmp_buffer_0; 24 | //u8* bmp_buffer_1; 25 | 26 | u8* bmp_get_write_pointer(u16 x, u16 y); 27 | u8* bmp_get_read_pointer(u16 x, u16 y); 28 | u16 bmp_get_dma_write_offset(u16 x, u16 y); 29 | 30 | void bmp_reset_phase(); 31 | 32 | void bmp_vertical_clear(); 33 | void bmp_clear(); 34 | 35 | void bmp_reset_horizontal(); 36 | void bmp_reset_vertical(); 37 | 38 | void bmp_init_horizontal(u16 double_buffer, VDPPlane plane, u16 palette, u16 priority); 39 | void bmp_init_vertical(u16 double_buffer, VDPPlane plane, u16 palette, u16 priority); 40 | 41 | 42 | void bmp_end(); 43 | 44 | u16 bmp_flip_partial(u16 async, u8 start_cell); 45 | void bmp_wait_while_flip_request_pending(); 46 | 47 | void bmp_show_fps(u16 float_display); 48 | void request_flip(); 49 | -------------------------------------------------------------------------------- /src/my_bmp_a.s: -------------------------------------------------------------------------------- 1 | .globl bmp_clear_vertical_bitmap_buffer 2 | .type bmp_clear_vertical_bitmap_buffer, @function 3 | bmp_clear_vertical_bitmap_buffer: 4 | move.l 4(%sp),%a0 | a0 = buffer 5 | lea 18432(%a0),%a0 | a0 = buffer end 6 | 7 | movm.l %d2-%d7/%a2-%a6,-(%sp) 8 | 9 | | the function consumes about 36000? cycles to clear the whole bitmap buffer 10 | 11 | moveq #0,%d1 12 | move.l %d1,%d2 13 | move.l %d1,%d3 14 | move.l %d1,%d4 15 | move.l %d1,%d5 16 | move.l %d1,%d6 17 | move.l %d1,%d7 18 | move.l %d1,%a1 19 | move.l %d1,%a2 20 | move.l %d1,%a3 21 | move.l %d1,%a4 22 | move.l %d1,%a5 23 | move.l %d1,%a6 24 | 25 | moveq #34,%d0 26 | 27 | .L000: 28 | movm.l %d1-%d7/%a1-%a6,-(%a0) 29 | movm.l %d1-%d7/%a1-%a6,-(%a0) 30 | movm.l %d1-%d7/%a1-%a6,-(%a0) 31 | movm.l %d1-%d7/%a1-%a6,-(%a0) 32 | movm.l %d1-%d7/%a1-%a6,-(%a0) 33 | movm.l %d1-%d7/%a1-%a6,-(%a0) 34 | movm.l %d1-%d7/%a1-%a6,-(%a0) 35 | movm.l %d1-%d7/%a1-%a6,-(%a0) 36 | movm.l %d1-%d7/%a1-%a6,-(%a0) 37 | movm.l %d1-%d7/%a1-%a6,-(%a0) 38 | dbra %d0,.L000 39 | 40 | 41 | movm.l %d1-%d7/%a1-%a6,-(%a0) 42 | movm.l %d1-%d7/%a1-%a6,-(%a0) 43 | movm.l %d1-%d7/%a1-%a6,-(%a0) 44 | movm.l %d1-%d7/%a1-%a6,-(%a0) 45 | 46 | movm.l %d1-%d6,-(%a0) 47 | 48 | movm.l (%sp)+,%d2-%d7/%a2-%a6 49 | rts 50 | 51 | .globl bmp_clear_bitmap_buffer 52 | .type bmp_clear_bitmap_buffer, @function 53 | bmp_clear_bitmap_buffer: 54 | move.l 4(%sp),%a0 | a0 = buffer 55 | lea 20480(%a0),%a0 | a0 = buffer end 56 | 57 | movm.l %d2-%d7/%a2-%a6,-(%sp) 58 | 59 | | the function consume about 43200 cycles to clear the whole bitmap buffer 60 | 61 | moveq #0,%d1 62 | move.l %d1,%d2 63 | move.l %d1,%d3 64 | move.l %d1,%d4 65 | move.l %d1,%d5 66 | move.l %d1,%d6 67 | move.l %d1,%d7 68 | move.l %d1,%a1 69 | move.l %d1,%a2 70 | move.l %d1,%a3 71 | move.l %d1,%a4 72 | move.l %d1,%a5 73 | move.l %d1,%a6 74 | 75 | moveq #38,%d0 76 | 77 | .L01: 78 | movm.l %d1-%d7/%a1-%a6,-(%a0) 79 | movm.l %d1-%d7/%a1-%a6,-(%a0) 80 | movm.l %d1-%d7/%a1-%a6,-(%a0) 81 | movm.l %d1-%d7/%a1-%a6,-(%a0) 82 | movm.l %d1-%d7/%a1-%a6,-(%a0) 83 | movm.l %d1-%d7/%a1-%a6,-(%a0) 84 | movm.l %d1-%d7/%a1-%a6,-(%a0) 85 | movm.l %d1-%d7/%a1-%a6,-(%a0) 86 | movm.l %d1-%d7/%a1-%a6,-(%a0) 87 | movm.l %d1-%d7/%a1-%a6,-(%a0) 88 | dbra %d0,.L01 89 | 90 | movm.l %d1-%d7/%a1-%a6,-(%a0) 91 | movm.l %d1-%d7/%a1-%a6,-(%a0) 92 | movm.l %d1-%d7/%a1-%a6,-(%a0) 93 | movm.l %d1-%d7/%a1-%a4,-(%a0) 94 | 95 | movm.l (%sp)+,%d2-%d7/%a2-%a6 96 | rts -------------------------------------------------------------------------------- /src/obj_sprite.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJ_SPRITE_H 2 | #define OBJ_SPRITE_H 3 | 4 | #include "genesis.h" 5 | #include "tile.h" 6 | 7 | // 10 bytes 8 | typedef struct __attribute__((__packed__)) { 9 | const u16 num_spans; 10 | const u16* spans; // use words here so they can be pre-scaled to word indexes into scaled_sprite_runlengths 11 | const u8* texels; 12 | } column; 13 | 14 | 15 | // 642 bytes 16 | typedef struct __attribute__((__packed__)) { 17 | // 2 bytes 18 | const u16 num_columns; 19 | // 640 bytes 20 | const column columns[64]; 21 | } rle_sprite; 22 | 23 | 24 | void render_object_to_tile_column(u32 tex_per_pix, u32* tex_per_pix_table_ptr, s16 start_y, u16 min_y, u16 max_y, const column* col, u16* tile_buf_ptr); 25 | u16 render_object_to_sprite(s16 left_x,s16 top_y, u16 scaled_size, const rle_sprite* obj); 26 | 27 | void obj_sprite_init(u16 free_tile_loc); 28 | const u32 scaled_sprite_run_lengths[64*513]; 29 | const u32 scaled_sprite_texel_per_pixel_lut[513]; 30 | 31 | const u16* const sprite_scale_coefficients_pointer_lut[513]; 32 | 33 | #endif -------------------------------------------------------------------------------- /src/object.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECT_H 2 | #define OBJECT_H 3 | 4 | #include 5 | 6 | #include "obj_sprite.h" 7 | 8 | // 13 bytes 9 | typedef struct { 10 | fix32 x; 11 | fix32 y; 12 | fix32 z; 13 | u8 ang; 14 | } object_pos; 15 | 16 | typedef struct { 17 | fix32 x; 18 | fix32 y; 19 | fix32 z; 20 | u16 cur_sector; 21 | u16 ang; 22 | } player_pos; 23 | 24 | // 4+2+2+2+1+32+2 bytes 25 | // 46 26 | 27 | typedef enum { 28 | OBJECT = 0, 29 | DECORATION = 1, 30 | } obj_type; 31 | 32 | #define FLAGS_KEY_TYPE_MASK 0b00011100 33 | #define FLAGS_KEY_TYPE_SHIFT 2 34 | #define FLAGS_ANCHOR_TOP_MASK 0b00000010 35 | #define FLAGS_ANCHOR_BOT_MASK 0b00000001 36 | #define FLAGS_RE 37 | 38 | 39 | typedef struct __attribute__((__packed__)) { 40 | rle_sprite* sprite; 41 | uint16_t from_anchor_draw_offset; 42 | uint16_t width; 43 | uint16_t height; 44 | uint8_t init_state; 45 | uint16_t speed; // 3 for claw guy? 46 | u8 is_player; 47 | u8 type; 48 | u8 flags; // KEY_TYPE: 2 bits, anchor_top: 1bit, anchor_bot: 1bit 49 | char name[32]; 50 | } object_template; 51 | 52 | extern const object_template object_types[]; 53 | 54 | 55 | 56 | // 64 active objects 57 | #define NULL_OBJ_LINK 0b00111111 58 | // 128 static objects 59 | #define NULL_DEC_LINK 0b01111111 60 | 61 | // if there's more than 32 objects to draw in a sector, too bad 62 | #define OBJ_SORT_BUF_SZ 32 63 | 64 | 65 | typedef u8 object_link; 66 | 67 | typedef u8 decoration_link; 68 | 69 | // 127 objects! 70 | #define MAX_OBJECTS (NULL_OBJ_LINK) 71 | // 256 decorations! 72 | #define MAX_DECORATIONS (NULL_DEC_LINK) 73 | 74 | #define OBJ_LINK_DEREF(lnk) (objects[(lnk)]) 75 | #define DEC_LINK_DEREF(lnk) (decorations[(lnk)]) 76 | 77 | #define IDLE_STATE 0 78 | #define MAYBE_GET_PICKED_UP_STATE 3 79 | typedef struct object object; 80 | 81 | // 22 bytes!, 2772 bytes for objects 82 | struct object { 83 | 84 | fix32 x; 85 | fix32 y; 86 | s16 z; 87 | u8 ang; 88 | 89 | object_link tgt; 90 | object_link prev; 91 | object_link next; 92 | 93 | uint8_t health; 94 | uint8_t current_state; 95 | uint8_t object_type; 96 | uint8_t activate_tick; 97 | }; 98 | 99 | typedef struct decoration_object decoration_object; 100 | 101 | // 8 bytes, 2040 bytes for all objects 102 | struct decoration_object { 103 | uint8_t object_type; 104 | s16 x; s16 y; s16 z; 105 | decoration_link next; 106 | }; 107 | 108 | void init_object_lists(int num_sectors); 109 | 110 | void clean_object_lists(); 111 | 112 | typedef struct __attribute__((__packed__)) { 113 | u16 sector_num; 114 | s16 x; 115 | s16 y; 116 | s16 z; 117 | u8 type; 118 | } map_object; 119 | 120 | object_link alloc_object_in_sector(u8 activate_tick, int sector_num, fix32 x, fix32 y, s16 z, uint8_t object_type); 121 | decoration_link alloc_decoration_in_sector(int sector_num, s16 x, s16 y, s16 z, uint8_t object_type); 122 | 123 | void free_object(object_link obj, u16 object_sector); 124 | void free_decoration(decoration_link dec, u16 deco_sector); 125 | void print_object_list(object_link lst); 126 | 127 | object_link objects_in_sector(int sector_num); 128 | 129 | decoration_link decorations_in_sector(int sector_num); 130 | 131 | 132 | void process_all_objects(uint32_t cur_tick); 133 | void wake_enemies_in_sector(u16 sector); 134 | 135 | typedef struct { 136 | uint16_t next_state; 137 | uint16_t ticks; 138 | int (*action)(object_link, uint16_t); // passed the current object as well as the sector 139 | } obj_state; 140 | 141 | 142 | // 12 bytes 143 | typedef struct { 144 | s16 x; 145 | u8 obj_type; 146 | s16 ybot; 147 | s16 ytop; 148 | u8 obj_link; 149 | } buf_obj; 150 | 151 | // 6 bytes 152 | typedef struct { 153 | u16 z_recip; 154 | u8 buf_idx; 155 | s16 height; 156 | //s16 x; 157 | //u8 obj_type; 158 | //s16 ybot; 159 | //s16 ytop; 160 | } z_buf_obj; 161 | 162 | 163 | extern z_buf_obj *z_sort_buf;//[64]; 164 | extern buf_obj *obj_sort_buf;//[64]; 165 | 166 | extern object* objects; 167 | extern decoration_object* decorations; 168 | 169 | #endif -------------------------------------------------------------------------------- /src/overlapping_test_map.h: -------------------------------------------------------------------------------- 1 | #ifndef OVERLAPPING_TEST_MAP_H 2 | #define OVERLAPPING_TEST_MAP_H 3 | 4 | #include "portal_map.h" 5 | 6 | extern const portal_map overlapping_map; 7 | void update_wall_vertex(); 8 | 9 | #endif -------------------------------------------------------------------------------- /src/portal.h: -------------------------------------------------------------------------------- 1 | #ifndef PORTAL_H 2 | #define PORTAL_H 3 | 4 | #include 5 | #include "game.h" 6 | #include "portal_map.h" 7 | #include "vertex.h" 8 | 9 | void init_portal_renderer(); 10 | void cleanup_portal_renderer(); 11 | 12 | void clear_portal_cache(); 13 | void portal_rend(u16 src_sector, u32 cur_frame); 14 | void portal_scan(u16 src_sector, u16 window_min, u16 window_max, u32 cur_frame); 15 | u8* sector_rendered_cache; 16 | 17 | #endif -------------------------------------------------------------------------------- /src/portal_map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "portal_map.h" 3 | 4 | s16* sector_data_start(s16 sector_idx, portal_map* mp) { 5 | return (s16*)(&mp->sectors[sector_idx*SECTOR_SIZE]); 6 | } 7 | 8 | s16 sector_wall_offset(s16 sector_idx, portal_map* mp) { 9 | return mp->sectors[sector_idx*SECTOR_SIZE]; 10 | } 11 | 12 | s16 sector_portal_offset(s16 sector_idx, portal_map* mp) { 13 | return mp->sectors[sector_idx*SECTOR_SIZE+1]; 14 | } 15 | 16 | s16 sector_num_walls(s16 sector_idx, portal_map* mp) { 17 | return mp->sectors[sector_idx*SECTOR_SIZE+2]; 18 | } 19 | 20 | u16 sector_group(s16 sector_idx, portal_map* mp) { 21 | return mp->sectors[sector_idx*SECTOR_SIZE+3]; 22 | } 23 | 24 | 25 | u8 sector_in_pvs_inner(u16 check_sector, s8* entries_for_src_sector) { 26 | 27 | u16 cur_sector = 0; 28 | s8 el = *entries_for_src_sector++; 29 | while(el != 0) { 30 | if(el < 0) { 31 | // run of zeros 32 | cur_sector += (-el); // skip past this many sectors 33 | if(cur_sector > check_sector) { 34 | return 0; 35 | } 36 | } else { 37 | // run of sectors 38 | cur_sector += el; 39 | if(cur_sector > check_sector) { 40 | return 1; 41 | } 42 | } 43 | el = *entries_for_src_sector++; 44 | } 45 | return 0; 46 | } 47 | u8 sector_in_pvs(u16 src_sector, u16 check_sector, portal_map* mp) { 48 | u32 pvs_offset = mp->sector_pvs_offsets[src_sector]; 49 | s8* entries = &mp->sector_pvs_entries[pvs_offset]; 50 | return sector_in_pvs_inner(check_sector, entries); 51 | } 52 | 53 | u8 sector_in_phs(u16 src_sector, u16 check_sector, portal_map* mp) { 54 | u32 phs_offset = mp->sector_phs_offsets[src_sector]; 55 | s16* entries = &mp->sector_phs_entries[phs_offset]; 56 | return sector_in_pvs_inner(check_sector, entries); 57 | } 58 | 59 | 60 | void run_in_pvs_inner(u16 src_sector, void (*sect_func)(u16), s8* entries_for_src_sector) { 61 | u16 cur_sector = 0; 62 | s8 el = *entries_for_src_sector++; 63 | while(el != 0) { 64 | if(el < 0) { 65 | // run of zeros 66 | cur_sector += (-el); // skip past this many sectors 67 | } else { 68 | // run of sectors 69 | for(u16 s = cur_sector; s < cur_sector+el; s++) { 70 | sect_func(s); 71 | } 72 | cur_sector += el; 73 | } 74 | el = *entries_for_src_sector++; 75 | } 76 | } 77 | 78 | 79 | void run_in_pvs(u16 src_sector, void (*sect_func)(u16), portal_map* mp) { 80 | u32 pvs_offset = mp->sector_pvs_offsets[src_sector]; 81 | s8* entries = &mp->sector_pvs_entries[pvs_offset]; 82 | run_in_pvs_inner(src_sector, sect_func, entries); 83 | } 84 | 85 | void run_in_phs(u16 src_sector, void (*sect_func)(u16), portal_map* mp) { 86 | u32 pvs_offset = mp->sector_phs_offsets[src_sector]; 87 | s8* entries = &mp->sector_phs_entries[pvs_offset]; 88 | run_in_pvs_inner(src_sector, sect_func, entries); 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/portal_map.h: -------------------------------------------------------------------------------- 1 | #ifndef PORTAL_MAP_H 2 | #define PORTAL_MAP_H 3 | 4 | #include 5 | 6 | #include "object.h" 7 | #include "sector_group.h" 8 | #include "texture.h" 9 | #include "vertex.h" 10 | 11 | #define SECTOR_SIZE 4 12 | #define VERT_SIZE 2 13 | 14 | #define MAX_SECTORS 1024 // 4kB max 15 | #define MAX_SECTOR_GROUPS 512 16 | #define MAX_WALLS 1024 // 4kB max 17 | #define MAX_VERTEXES 1024 // 8kB max 18 | #define MAX_PORTALS 512 // 1kB max 19 | #define MAX_TEXTURES 32 // 64 bytes, pointers into shared texture list 20 | 21 | typedef enum { 22 | QUADRANT_0=0, 23 | QUADRANT_1=1, 24 | QUADRANT_2=2, 25 | QUADRANT_3=3, 26 | FACING_UP=4, 27 | FACING_LEFT=5, 28 | FACING_DOWN=6, 29 | FACING_RIGHT=7 30 | } normal_quadrant; 31 | 32 | typedef struct { 33 | u8 upper_col; 34 | u8* texture; 35 | u8 lower_col; 36 | } wall_col; 37 | 38 | #define WALL_TEXTURE_IDX 0 39 | #define WALL_HIGH_COLOR_IDX 1 40 | #define WALL_LOW_COLOR_IDX 2 41 | #define WALL_SOLID_COLOR_IDX 3 42 | 43 | #define WALL_COLOR_NUM_PARAMS 4 44 | #define WALL_COLOR_NUM_PARAMS_SHIFT 2 45 | 46 | 47 | 48 | 49 | 50 | #define NUM_PVS_PARAMS 2 51 | #define PVS_SHIFT 1 52 | 53 | typedef struct __attribute__((__packed__)) { 54 | const u16 bunch_offset; 55 | const u8 num_bunches; 56 | } pvs_bunch_group; 57 | 58 | typedef struct __attribute__((__packed__)) { 59 | const u16 sector_num; 60 | const u16 wall_offset; 61 | const u8 num_walls; 62 | } pvs_bunch_entry; 63 | 64 | 65 | // in total 1088 bytes 66 | 67 | typedef struct __attribute__((__packed__)){ 68 | const u16 num_sector_groups; 69 | const u16 num_sectors; 70 | const u16 num_walls; 71 | const u16 num_verts; 72 | const s16* sectors; 73 | const u8* sector_group_types; 74 | const s16* sector_group_params; 75 | 76 | // each sector has a list of 8 s16s 77 | // the first is the trigger type, and the remaining 7 are sector group targets 78 | const s16* sector_group_triggers; 79 | 80 | const u16* walls; 81 | 82 | //const u16* collision_hull_walls; // 83 | 84 | //const s16* wall_dxs; 85 | //const s16* wall_dys; 86 | //const s16* wall_collision_hull; 87 | 88 | const s16* portals; 89 | const u8* wall_colors; 90 | const u8* wall_tex_repetitions; 91 | const vertex* vertexes; 92 | const vertex* collision_vertexes; 93 | const u8* wall_norm_quadrants; 94 | const u16 has_pvs; 95 | const u16* pvs; 96 | const u16* raw_pvs; 97 | 98 | const u16* pvs_offsets; 99 | const s16* sector_list_offsets; 100 | const u16* wall_pvs; // each index from above points to a list consisting of N:num_sectors, N 32-bit bitmaps 101 | 102 | 103 | const pvs_bunch_group* pvs_bunch_groups; 104 | const pvs_bunch_entry* pvs_bunch_entries; 105 | 106 | const u32* sector_pvs_offsets; // offsets rle-encoded sector pvs 107 | const s8* sector_pvs_entries; 108 | const u32* sector_phs_offsets; 109 | const s8* sector_phs_entries; 110 | char* name; 111 | void* xgm_track; 112 | u16* palette; 113 | u16 num_things; 114 | map_object* things; 115 | } portal_map; 116 | 117 | s16* sector_data_start(s16 sector_idx, portal_map* mp); 118 | 119 | s16 sector_wall_offset(s16 sector_idx, portal_map* mp); 120 | 121 | s16 sector_portal_offset(s16 sector_idx, portal_map* mp); 122 | 123 | s16 sector_num_walls(s16 sector_idx, portal_map* mp); 124 | 125 | u16 sector_group(s16 sector_idx, portal_map* mp); 126 | 127 | u8 sector_in_pvs(u16 src_sector, u16 check_sector, portal_map* mp); 128 | 129 | u8 sector_in_phs(u16 src_sector, u16 check_sector, portal_map* mp); 130 | 131 | void run_in_pvs(u16 src_sector, void (*sect_func)(u16), portal_map* mp); 132 | 133 | void run_in_phs(u16 src_sector, void (*sect_func)(u16), portal_map* mp); 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /src/python/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/__init__.py -------------------------------------------------------------------------------- /src/python/editor/.idea/editor.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/python/editor/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /src/python/editor/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/python/editor/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/python/editor/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/python/editor/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 1631381646397 60 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | file://$PROJECT_DIR$/editor.py 71 | 202 72 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/python/editor/DLLs/geos_c.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/editor/DLLs/geos_c.dll -------------------------------------------------------------------------------- /src/python/editor/README: -------------------------------------------------------------------------------- 1 | This release of the editor needs SDL2.dll in the same directory/folder as the exe. 2 | Plus you will need to provide a textures, sprites, and music directory/folder matching the one in conf.ini. 3 | You need to provide at least one texture and at least one sprite in these folders. 4 | 5 | For music, you will also need SGDK's xgmtool. You can find that on the SGDK github repository. 6 | 7 | Another helpful tool, is providing a path to an emulator to launch built ROMs with. -------------------------------------------------------------------------------- /src/python/editor/README.md: -------------------------------------------------------------------------------- 1 | This release of the editor needs SDL2.dll in the same directory/folder as the exe. 2 | Plus you will need to provide a textures, sprites, and music directory/folder matching the one in conf.ini. 3 | You need to provide at least one texture and at least one sprite in these folders. 4 | 5 | For music, you will also need SGDK's xgmtool. You can find that on the SGDK github repository. 6 | 7 | Another helpful tool, is providing a path to an emulator to launch built ROMs with. -------------------------------------------------------------------------------- /src/python/editor/build_conf.ini: -------------------------------------------------------------------------------- 1 | [Default Settings] 2 | emulator-path=gens.exe 3 | xgmtool-path=xgmtool.exe 4 | textures-path=textures 5 | sprites-path=sprites 6 | music-tracks-path=music -------------------------------------------------------------------------------- /src/python/editor/defaults.py: -------------------------------------------------------------------------------- 1 | import imgui 2 | 3 | import utils 4 | 5 | def draw_defaults_mode(cur_state): 6 | color_opts = ["{}".format(idx) for idx in range(16)] 7 | up_col_changed, new_up_col = imgui.core.combo("upper color", cur_state.default_up_color, color_opts) 8 | mid_col_changed, new_mid_col = imgui.core.combo("middle color", cur_state.default_mid_color, color_opts) 9 | low_col_changed, new_low_col = imgui.core.combo("lower color", cur_state.default_low_color, color_opts) 10 | 11 | tex_files = utils.get_texture_files(cur_state) 12 | 13 | def set_default_tex_file(f): 14 | cur_state.default_texture_file = f 15 | utils.file_selector("texture", cur_state.default_texture_file, tex_files, set_default_tex_file) 16 | 17 | 18 | floor_col_changed, new_floor_col = imgui.core.combo("floor color", cur_state.default_floor_color, color_opts) 19 | ceil_col_changed, new_ceil_col = imgui.core.combo("ceil color ", cur_state.default_ceil_color, color_opts) 20 | 21 | type_options = ['player'] + [ thing.name for thing in cur_state.map_data.thing_defs] 22 | thing_type_changed, new_thing_type = imgui.core.combo("thing type ", cur_state.default_thing_type, type_options) 23 | 24 | if up_col_changed: 25 | cur_state.default_up_color = new_up_col 26 | if mid_col_changed: 27 | cur_state.default_mid_color = new_mid_col 28 | if low_col_changed: 29 | cur_state.default_low_color = new_low_col 30 | if floor_col_changed: 31 | cur_state.default_floor_color = new_floor_col 32 | if ceil_col_changed: 33 | cur_state.default_ceil_color = new_ceil_col 34 | if thing_type_changed: 35 | cur_state.default_thing_type = new_thing_type 36 | -------------------------------------------------------------------------------- /src/python/editor/editor.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | 4 | block_cipher = None 5 | 6 | 7 | a = Analysis( 8 | ['editor.py'], 9 | pathex=[], 10 | binaries=[], 11 | datas=[], 12 | hiddenimports=[], 13 | hookspath=[], 14 | runtime_hooks=[], 15 | excludes=[], 16 | win_no_prefer_redirects=False, 17 | win_private_assemblies=False, 18 | cipher=block_cipher, 19 | noarchive=False, 20 | ) 21 | pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) 22 | 23 | exe = EXE( 24 | pyz, 25 | a.scripts, 26 | a.binaries, 27 | a.zipfiles, 28 | a.datas, 29 | [], 30 | name='editor', 31 | debug=False, 32 | bootloader_ignore_signals=False, 33 | strip=False, 34 | upx=True, 35 | upx_exclude=[], 36 | runtime_tmpdir=None, 37 | console=True, 38 | disable_windowed_traceback=False, 39 | argv_emulation=False, 40 | target_arch=None, 41 | codesign_identity=None, 42 | entitlements_file=None, 43 | ) 44 | -------------------------------------------------------------------------------- /src/python/editor/file_utils.py: -------------------------------------------------------------------------------- 1 | 2 | def _read_longword_(f, signed: bool) -> int: 3 | return int.from_bytes(f.read(4), "big", signed) 4 | 5 | def read_s32(f) -> int: 6 | return _read_longword_(f, True) 7 | 8 | def read_u32(f) -> int: 9 | return _read_longword_(f, False) 10 | 11 | def _write_longword_(f, lw: int, signed: bool) -> int: 12 | off = f.tell() 13 | f.write(lw.to_bytes(4, byteorder="big", signed=signed)) 14 | return off 15 | 16 | def write_s32(f, lw: int) -> int: 17 | return _write_longword_(f, lw, True) 18 | def write_u32(f, lw: int) -> int: 19 | return _write_longword_(f, lw, False) 20 | 21 | 22 | def _read_word_(f, signed: bool) -> int: 23 | return int.from_bytes(f.read(2), "big", signed) 24 | 25 | def read_s16(f) -> int: 26 | return _read_word_(f, True) 27 | def read_u16(f) -> int: 28 | return _read_word_(f, False) 29 | 30 | def _write_word_(f, w: int, signed: bool) -> int: 31 | off = f.tell() 32 | f.write(w.to_bytes(2, byteorder="big", signed=signed)) 33 | return off 34 | 35 | def write_u16(f, w: int) -> int: 36 | assert w >= 0 and w < 65536 37 | _write_word_(f, w, False) 38 | 39 | def write_s16(f, w: int) -> int: 40 | assert w >= -32768 and w < 32768 41 | _write_word_(f, w, True) 42 | 43 | 44 | def _read_byte_(f, signed: bool) -> int: 45 | return int.from_bytes(f.read(1), byteorder="big", signed=signed) 46 | def _write_byte_(f, b: int, signed: bool) -> int: 47 | off = f.tell() 48 | f.write(b.to_bytes(1, byteorder="big", signed=signed)) 49 | return off 50 | 51 | def read_s8(f) -> int: 52 | return _read_byte_(f, True) 53 | def read_u8(f) -> int: 54 | return _read_byte_(f, False) 55 | def write_s8(f, b: int) -> int: 56 | return _write_byte_(f, b, True) 57 | def write_u8(f, b: int) -> int: 58 | return _write_byte_(f, b, False) 59 | -------------------------------------------------------------------------------- /src/python/editor/geos_c.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/editor/geos_c.dll -------------------------------------------------------------------------------- /src/python/editor/imgui.ini: -------------------------------------------------------------------------------- 1 | [Window][Debug##Default] 2 | Pos=60,60 3 | Size=400,400 4 | Collapsed=0 5 | 6 | [Window][Your first window!] 7 | Pos=60,60 8 | Size=99,48 9 | Collapsed=0 10 | 11 | [Window][Custom window] 12 | Pos=62,45 13 | Size=269,240 14 | Collapsed=0 15 | 16 | [Window][ImGui Demo] 17 | Pos=659,44 18 | Size=550,680 19 | Collapsed=0 20 | 21 | [Window][Example: Custom rendering] 22 | Pos=97,63 23 | Size=350,560 24 | Collapsed=0 25 | 26 | [Window][Tools] 27 | Pos=832,43 28 | Size=373,593 29 | Collapsed=0 30 | 31 | [Window][Map] 32 | Pos=0,0 33 | Size=1600,900 34 | Collapsed=0 35 | 36 | -------------------------------------------------------------------------------- /src/python/editor/levels.py: -------------------------------------------------------------------------------- 1 | from tkinter import filedialog, messagebox 2 | 3 | def export_maps_to_rom( 4 | map_loader_func # dumb hack to get around circular imports 5 | ): 6 | f = filedialog.askopenfile(mode="r") 7 | 8 | textures = set() 9 | tracks = set() 10 | with open(f) as maplist: 11 | maps = maplist.readlines() 12 | for map in maps: 13 | map_data = map_loader_func(map) 14 | level_textures = gather_textures_from_level(map_data) 15 | textures = textures.union(level_textures) 16 | 17 | tracks.add(map_data.music_path) 18 | 19 | # write textures to WAD area 20 | 21 | # write music to WAD area 22 | 23 | # export maps to ROM with offsets to compiled textures and music 24 | 25 | def gather_textures_from_level(map_data): 26 | textures = set() 27 | for wall in map_data.sectors.walls: 28 | textures.add(wall.texture_file) 29 | return textures 30 | 31 | 32 | 33 | # get textures 34 | # get -------------------------------------------------------------------------------- /src/python/editor/light_levels.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/editor/light_levels.py -------------------------------------------------------------------------------- /src/python/editor/light_remapping_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/editor/light_remapping_table.png -------------------------------------------------------------------------------- /src/python/editor/light_remapping_table_autogen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/editor/light_remapping_table_autogen.png -------------------------------------------------------------------------------- /src/python/editor/light_remapping_table_mix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/editor/light_remapping_table_mix.png -------------------------------------------------------------------------------- /src/python/editor/light_remapping_table_mix_rotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/editor/light_remapping_table_mix_rotated.png -------------------------------------------------------------------------------- /src/python/editor/light_remapping_table_no_mix_rotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/editor/light_remapping_table_no_mix_rotated.png -------------------------------------------------------------------------------- /src/python/editor/map.py: -------------------------------------------------------------------------------- 1 | import palette 2 | import things 3 | import sector 4 | import sector_group 5 | import vertex 6 | 7 | import typing 8 | 9 | class Map(): 10 | def __init__(self, 11 | default_sprite, 12 | name="placeholder name", 13 | sectors=None, 14 | vertexes=None, 15 | music_path=""): 16 | self.bsp = False 17 | if not sectors: 18 | self.sectors: typing.List[sector.Sector] = [] 19 | else: 20 | self.sectors: typing.List[sector.Sector] = sectors 21 | #self.walls = [] 22 | 23 | if not vertexes: 24 | self.vertexes = [] 25 | else: 26 | self.vertexes = vertexes 27 | 28 | self.name: str = name 29 | self.music_path: str = music_path 30 | self.palette = palette.DEFAULT_PALETTE 31 | self.thing_defs = [things.ThingDef(default_sprite) for i in range(31)] 32 | self.things: typing.List[things.Thing] = [] 33 | self.sector_groups: typing.List[sector_group.SectorGroup] = [] 34 | -------------------------------------------------------------------------------- /src/python/editor/modes.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Mode(Enum): 4 | SECTOR = 'Sector' 5 | SECTOR_GROUP = 'Sector Groups' 6 | LINE = 'Line' 7 | VERTEX = 'Vertex' 8 | SCRIPT = 'Script' 9 | TRIGGER = 'Trigger' 10 | TEXTURE = 'Texture' 11 | TREE = 'Tree' 12 | PVS = 'PVS' 13 | MUSIC = 'Music' 14 | PALETTE = 'Palette' 15 | DEFAULTS = 'Defaults' 16 | THING_DEFS = 'Thing Defs' 17 | THINGS = 'Things' 18 | -------------------------------------------------------------------------------- /src/python/editor/music.py: -------------------------------------------------------------------------------- 1 | 2 | import imgui 3 | 4 | import undo 5 | import utils 6 | 7 | def draw_music_mode(cur_state): 8 | chg_xgm, xgm_val = imgui.input_text("XGM Tool path: ", cur_state.xgmtool_path, buffer_length=128) 9 | if chg_xgm: 10 | undo.push_state(cur_state) 11 | cur_state.xgmtool_path = xgm_val 12 | 13 | music_files = ["No track"] + utils.get_music_files(cur_state) 14 | def set_music_track(new_track): 15 | undo.push_state(cur_state) 16 | if new_track == "No track": 17 | cur_state.map_data.music_path = "" 18 | else: 19 | cur_state.map_data.music_path = new_track 20 | 21 | if cur_state.map_data.music_path == "": 22 | utils.file_selector("Music track: ", "No track", music_files, set_music_track) 23 | else: 24 | utils.file_selector("Music track: ", cur_state.map_data.music_path, music_files, set_music_track) 25 | -------------------------------------------------------------------------------- /src/python/editor/palette.py: -------------------------------------------------------------------------------- 1 | import imgui 2 | #define RGB24_TO_VDPCOLOR(color) 3 | # (((((color + 0x100000) < 0xFF0000 ? color + 0x100000 : 0xFF0000) >> (20)) & VDPPALETTE_REDMASK) | (((((color & 0xff00) + 0x1000) < 0xFF00 ? (color & 0xff00) + 0x1000 : 0xFF00) >> ((1 * 4) + 4)) & VDPPALETTE_GREENMASK) | (((((color & 0xff) + 0x10) < 0xFF ? (color & 0xff) + 0x10 : 0xFF) << 4) & VDPPALETTE_BLUEMASK)) 4 | #define RGB8_8_8_TO_VDPCOLOR(r, g, b) 5 | # 6 | # RGB24_TO_VDPCOLOR(((((b) << 0) & 0xFF) | (((g) & 0xFF) << 8) | (((r) & 0xFF) << 16))) 7 | 8 | 9 | VDPPALETTE_REDMASK = 0x000E 10 | VDPPALETTE_GREENMASK = 0x00E0 11 | VDPPALETTE_BLUEMASK = 0x0E00 12 | VDPPALETTE_COLORMASK = 0x0EEE 13 | 14 | col_lut = [0, 36, 72, 108, 144, 180, 216, 252] 15 | 16 | def hex_color_to_rgb(hex): 17 | r = ((hex>>16)&0xFF)/256 18 | g = ((hex>>8)&0xFF)/256 19 | b = ((hex>>0)&0xFF)/256 20 | return [r,g,b] 21 | 22 | def vdp_color_to_rgb(vdpcolor): 23 | r = col_lut[((vdpcolor>>1)&7)]/255 24 | g = col_lut[((vdpcolor>>5)&7)]/255 25 | b = col_lut[((vdpcolor>>9)&7)]/255 26 | return [r,g,b] 27 | 28 | DEFAULT_PALETTE = [hex_color_to_rgb(rgb) for rgb in [ 29 | 0xE31C79, #0x000000, 30 | 0x9b5d5d, 31 | 0xd99b7c, 32 | 0xf8ba9b, 33 | 0x7cf89b, 34 | 0x3e5d5d, 35 | 0x001f1f, 36 | 0x3e7c00, 37 | 0x3e3e3e, 38 | 0xd93e00, 39 | 0x1f001f, 40 | 0x9b003e, 41 | 0x3e9bba, 42 | 0x5d5dba, 43 | 0xd9ba00, 44 | 0x000000, 45 | ]] 46 | 47 | def rgb24_to_vdp_color(color): 48 | r = (((color + 0x100000 if ((color + 0x100000) < 0xFF0000) else 0xFF0000) >> (20)) & VDPPALETTE_REDMASK) 49 | g = ((((color & 0xff00) + 0x1000 if (((color & 0xff00) + 0x1000) < 0xFF00) else 0xFF00) >> ((1 * 4) + 4)) & VDPPALETTE_GREENMASK) 50 | b = (((((color & 0xff) + 0x10) if (((color & 0xff) + 0x10) < 0xFF) else 0xFF) << 4) & VDPPALETTE_BLUEMASK) 51 | return (r | g | b) 52 | 53 | def rgb_to_vdp_color(r,g,b): 54 | return rgb24_to_vdp_color((b & 0xFF)|((g & 0xFF)<<8)|((r & 0xFF) << 16)) 55 | 56 | 57 | def draw_palette_mode(cur_state): 58 | for idx in range(16): 59 | changed, val = imgui.color_edit3("{}:".format(idx), *cur_state.map_data.palette[idx]) 60 | if changed: 61 | cur_state.map_data.palette[idx] = val 62 | 63 | -------------------------------------------------------------------------------- /src/python/editor/palette_light_table.py: -------------------------------------------------------------------------------- 1 | 2 | # load image file 3 | 4 | 5 | # load all colors in image 6 | 7 | avail_colors = [ 8 | (155,93,93), 9 | (217,155,124), 10 | (248,186,155), 11 | (124,248,155), 12 | (62,93,93), 13 | (0,31,31), 14 | (62,124,0), 15 | (62,62,62), 16 | (217,62,0), 17 | (31,0,31), 18 | (155,0,62), 19 | (62,155,186), 20 | (93,93,186), 21 | (217,186,0), 22 | (0,0,0), 23 | ] 24 | 25 | # make sure to offset indexes 26 | 27 | def get_index_of_color(col): 28 | #(cr,cg,cb,) = col 29 | #colc = (cr,cg,cb) 30 | if len(col) == 4: 31 | colc = (col[0],col[1],col[2]) 32 | else: 33 | colc = col 34 | for idx,col2 in enumerate(avail_colors): 35 | if col2 == colc: 36 | return idx+1 37 | 38 | raise Exception("Couldn't find color: {}".format(col)) 39 | 40 | def get_8pix(p): 41 | dp = (p<<4|p) 42 | qp = (dp<<8 | dp) 43 | ep = (qp << 16 | qp) 44 | return ep 45 | 46 | from PIL import Image 47 | 48 | 49 | def write_u32(f, lw): 50 | f.write(lw.to_bytes(4, byteorder="big", signed=False)) 51 | 52 | 53 | #NEAR_START_OFFSET = 2335986 54 | #NEAR_FILE = "/Users/haliewicz/Desktop/near_light_table.png" 55 | 56 | 57 | MIX_FILE = "src\python\editor\light_remapping_table_mix_rotated.png" 58 | NO_MIX_FILE = "src\python\editor\light_remapping_table_no_mix_rotated.png" 59 | 60 | if __name__ == '__main__': 61 | 62 | 63 | with Image.open(NO_MIX_FILE) as im: 64 | #with Image.open(MIX_FILE) as im: 65 | #x,y = im.size 66 | 67 | for idx,table in enumerate(["far", "near"]): 68 | print('// {} table'.format(table)) 69 | dist_row_offset = idx * 5 * 2 70 | 71 | for light_level in range(5): 72 | print('// {} light level'.format(light_level-2)) 73 | light_level_offset = light_level * 2 74 | 75 | print('0', end=', ') 76 | 77 | row = dist_row_offset + light_level_offset 78 | for pal_idx in range(15): 79 | 80 | first_nib = get_index_of_color(im.getpixel((pal_idx, row))) 81 | second_nib = get_index_of_color(im.getpixel((pal_idx, row+1))) 82 | 83 | print('DPIX({},{})'.format(first_nib, second_nib), end=', ') 84 | 85 | print('') 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/python/editor/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "manifold" 3 | version = "0.6.0" 4 | description = "" 5 | authors = ["Erik Haliewicz "] 6 | license = "MIT" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "3.12" 11 | imgui = "1.4.1" 12 | pyopengl = "3.1.6" 13 | pysdl2 = "0.9.6" 14 | pysdl2-dll = "2.24.0" 15 | numpy = "^1.26.1" 16 | shapely = "^2.0.2" 17 | pillow = "^10.1.0" 18 | 19 | 20 | 21 | [build-system] 22 | requires = ["poetry-core"] 23 | build-backend = "poetry.core.masonry.api" 24 | -------------------------------------------------------------------------------- /src/python/editor/remapping.py: -------------------------------------------------------------------------------- 1 | import math 2 | from PIL import Image 3 | 4 | avail_colors = [ 5 | (154,93,93), 6 | (216,154,124), 7 | (247,185,154), 8 | (124,247,154), 9 | (62,93,93), 10 | (0,31,31), 11 | (62,124,0), 12 | (62,62,62), 13 | (216,62,0), 14 | (31,0,31), 15 | (154,0,64), 16 | (62,154,185), 17 | (93,93,185), 18 | (216,185,0), 19 | (0,0,0) 20 | ] 21 | 22 | def clamp(c): 23 | return 255 if c > 255 else 0 if c < 0 else c 24 | 25 | def lighten(c): 26 | return scale_color(c, 1.3) 27 | 28 | def darken(c): 29 | return scale_color(c, .7) 30 | 31 | def scale_color(c, amount): 32 | (r,g,b) = c 33 | return (clamp(int(r*amount)),clamp(int(g*amount)),clamp(int(b*amount))) 34 | 35 | def dist(c1, c2): 36 | (r1,g1,b1) = c1 37 | (r2,g2,b2) = c2 38 | rmean = int((r1+r2)/2) 39 | r = int(r1-r2) 40 | g = int(g1-g2) 41 | b = int(b1-b2) 42 | return math.sqrt((((512+rmean)*r*r)>>8)+(4*g*g)+(((767-rmean)*b*b)>>8)) 43 | 44 | def find_closest_idx(col): 45 | dists = [dist(c2,col) if c2 != col else 10000000000000 for c2 in avail_colors] 46 | #print(dists) 47 | min_dist = min(dists) 48 | #print(min_dist) 49 | closest_color_idx = dists.index(min_dist) 50 | return closest_color_idx 51 | 52 | #for idx,c in enumerate(avail_colors): 53 | # 54 | # # near colors 55 | # dark = find_closest_idx(darken(c)) 56 | # extra_dark = find_closest_idx(darken(darken(c))) 57 | # light = find_closest_idx(lighten(c)) 58 | # extra_light = find_closest_idx(lighten(lighten(c))) 59 | # 60 | # print(extra_dark+1, end=', ') 61 | # print(dark+1, end=', ') 62 | # print(idx+1, end=', ') 63 | # print(light+1, end=', ') 64 | # print(extra_light+1, end=',\n') 65 | 66 | tables = [ 67 | # far 68 | [ 69 | lambda c:find_closest_idx(darken(darken(c)))+1, 70 | lambda c:find_closest_idx(darken(darken(c)))+1, 71 | lambda c:find_closest_idx(darken(c))+1, 72 | lambda c:avail_colors.index(c)+1, 73 | lambda c:find_closest_idx(lighten(lighten(c)))+1 74 | ], 75 | # near 76 | [ 77 | lambda c:find_closest_idx(darken(darken(c)))+1, 78 | lambda c:find_closest_idx(darken(c))+1, 79 | lambda c:avail_colors.index(c)+1, 80 | lambda c:find_closest_idx(lighten(c))+1, 81 | lambda c:find_closest_idx(lighten(lighten(c)))+1 82 | ] 83 | ] 84 | 85 | #img = Image.new(mode="RGB",size=(10,15)) 86 | #pixels = img.load() 87 | 88 | #for y,base_col in enumerate(avail_colors): 89 | # for dist_idx in range(2): 90 | # base_x = dist_idx*5 91 | # light_levels = tables[1-dist_idx] 92 | # for idx,light_level in enumerate(light_levels): 93 | # x = base_x + idx 94 | # pixels[x,y] = avail_colors[light_level(base_col)-1] 95 | 96 | #with open("C:\\Users\\Erik\\code\\genesis\\DOOM\\src\\python\\editor\\light_remapping_table_autogen.png", "wb") as fp: 97 | # img.save(fp) 98 | 99 | 100 | with NEAR_FILE 101 | for idx in range(2): 102 | light_levels = tables[idx] 103 | print("// {} table".format(["far", "near"][idx])) 104 | for idx,light_level in enumerate(light_levels): 105 | print(" // {} light level".format(idx-2)) 106 | print(" 0", end=', ') 107 | 108 | for base_col in avail_colors: 109 | print("PIX({}), ".format(light_level(base_col)), end='') 110 | print() 111 | 112 | -------------------------------------------------------------------------------- /src/python/editor/render_3d.py: -------------------------------------------------------------------------------- 1 | import ctypes.wintypes 2 | 3 | import imgui 4 | import sdl2 5 | import sdl2.ext 6 | import random 7 | import math 8 | 9 | RENDER_WIDTH = 256 10 | RENDER_HEIGHT = 140 11 | 12 | SCALE = 2 13 | 14 | vertexes = [ 15 | (90,90), 16 | (100,100) 17 | ] 18 | 19 | walls = [ 20 | (0, 1, (255,0,0)), 21 | ] 22 | 23 | CEIL_HEIGHT = 100 24 | FLOOR_HEIGHT = 0 25 | 26 | surf: sdl2.SDL_Surface 27 | #window: sdl2.ext.Window 28 | renderer: sdl2.ext.Renderer 29 | 30 | cam_x = 95 31 | cam_y = 40 32 | cam_z = 20 33 | cam_ang = 0 34 | 35 | def init_sdl_window(): 36 | return 37 | global renderer, surf 38 | sdl2.ext.init() 39 | 40 | window = sdl2.ext.Window("Live View", size=(RENDER_WIDTH*SCALE, RENDER_HEIGHT*2)) 41 | window.show() 42 | 43 | #surf = sdl2.SDL_CreateRGBSurface(0, RENDER_WIDTH, RENDER_HEIGHT, 32, 0,0,0,0) 44 | #renderer = sdl2.ext.Renderer(surf, ) 45 | #renderer.present() 46 | renderer = sdl2.ext.Renderer(window) 47 | renderer.present() 48 | #processor = sdl2.ext.TestEventProcessor() 49 | #processor.run(window) 50 | 51 | 52 | def draw_preview(current_state): 53 | return 54 | global cam_ang 55 | renderer.clear((0,0,0)) 56 | render_from_position(cam_x, cam_y, cam_z, cam_ang, 0, RENDER_WIDTH) 57 | renderer.present() 58 | 59 | #tex = sdl2.SDL_CreateTextureFromSurface(renderer.sdlrenderer, surf) 60 | 61 | #sdl2.SDL_LockTexture(tex) 62 | 63 | cam_ang += 0.5 64 | #sdl2.SDL_GL_BindTexture(tex, None, None) 65 | 66 | #imgui.image(tex, RENDER_WIDTH, RENDER_HEIGHT) 67 | #sdl2.SDL_UnlockTexture(tex) 68 | 69 | cam_ang = cam_ang % 360 70 | 71 | def render_from_position(cam_x, cam_y, cam_z, angle, window_min, window_max): 72 | #surf: sdl2.SDL_Surface 73 | #surf = window.get_surface() 74 | for wall in walls: 75 | (v1_idx, v2_idx, col) = wall 76 | v1 = vertexes[v1_idx] 77 | v2 = vertexes[v2_idx] 78 | 79 | trans_v1_x, trans_v1_z = transform_vert(v1, cam_x, cam_y, angle) 80 | trans_v2_x, trans_v2_z = transform_vert(v2, cam_x, cam_y, angle) 81 | 82 | if(trans_v1_z <= 0 or trans_v2_z <= 0): 83 | continue 84 | 85 | print("v1 x,z {},{}".format(trans_v1_x, trans_v1_z)) 86 | print("v2 x,z {},{}".format(trans_v2_x, trans_v2_z)) 87 | lx = int(project_x(trans_v1_x, trans_v1_z)) 88 | rx = int(project_x(trans_v2_x, trans_v2_z)) 89 | print("lx: {}".format(lx)) 90 | print("rx: {}".format(rx)) 91 | 92 | x1_ytop = project_y(CEIL_HEIGHT, trans_v1_z, cam_z) 93 | x1_ybot = project_y(FLOOR_HEIGHT, trans_v1_z, cam_z) 94 | x2_ytop = project_y(CEIL_HEIGHT, trans_v2_z, cam_z) 95 | x2_ybot = project_y(FLOOR_HEIGHT, trans_v2_z, cam_z) 96 | 97 | if (lx > window_max): 98 | print("lx too big") 99 | continue 100 | 101 | if (rx < window_min): 102 | print("rx too small") 103 | continue 104 | 105 | if (lx >= rx): 106 | print("lx and rx are reversed") 107 | continue 108 | 109 | print("DRAWING!!!!") 110 | 111 | cur_ytop = x1_ytop 112 | cur_ybot = x1_ybot 113 | 114 | dx = rx-lx 115 | 116 | dy_top_per_dx = (x2_ytop - x1_ytop) / dx 117 | dy_bot_per_dx = (x2_ybot - x1_ybot) / dx 118 | print("drawing from {} to {}".format(lx, rx)) 119 | if lx < window_min: 120 | skip_x = window_min - lx 121 | cur_ytop += (dy_top_per_dx * skip_x) 122 | cur_ybot += (dy_bot_per_dx * skip_x) 123 | lx = window_min 124 | 125 | for x in range(lx, rx): 126 | draw_col(col, x, cur_ytop, cur_ybot) 127 | cur_ytop += dy_top_per_dx 128 | cur_ybot += dy_bot_per_dx 129 | 130 | 131 | def draw_col(color: (int,int,int), x: int, ytop: int, ybot: int): 132 | print("draw x: {}, {} to {}".format(x, ytop, ybot)) 133 | ytop = int(max(0, ytop)) 134 | ybot = int(min(RENDER_HEIGHT-1, ybot)) 135 | renderer.draw_line((x, ytop, x, ybot), color) 136 | #sdl2.ext.line(surf, color, (x, ytop, x, ybot)) 137 | 138 | 139 | 140 | 141 | 142 | 143 | def transform_vert(vert, cx, cy, cang): 144 | x,y = vert 145 | tlx = x - cx 146 | tly = y - cy 147 | 148 | rad_ang = math.radians(cang) 149 | sin_ang = math.sin(rad_ang) 150 | cos_ang = math.cos(rad_ang) 151 | 152 | rx = (tlx * sin_ang) - (tly * cos_ang) 153 | ry = (tlx * cos_ang) + (tly * sin_ang) 154 | 155 | return (rx,ry) 156 | 157 | def project_x(x, z): 158 | return 128 + (x / z) 159 | 160 | ASPECT_RATIO = (RENDER_WIDTH / RENDER_HEIGHT) 161 | CONST1 = RENDER_WIDTH/2 162 | CONST2 = ((RENDER_WIDTH/2) * SCALE / min(1, ASPECT_RATIO)) 163 | CONST3 = (RENDER_HEIGHT/2) 164 | CONST4 = (RENDER_HEIGHT/2 * SCALE / max(1, ASPECT_RATIO)) 165 | 166 | def project_y(y, z, player_z): 167 | rel_y = y - player_z 168 | 169 | scaled_y = (rel_y / z) * CONST4 170 | 171 | adj_y = CONST4 + scaled_y 172 | 173 | return (RENDER_HEIGHT-1) - adj_y 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /src/python/editor/requirements.txt: -------------------------------------------------------------------------------- 1 | imgui==1.4.1 2 | numpy==1.21.6 3 | PyOpenGL==3.1.6 4 | PySDL2==0.9.6 5 | pysdl2-dll==2.24.0 6 | Pillow==8.3.2 -------------------------------------------------------------------------------- /src/python/editor/run_editor.bat: -------------------------------------------------------------------------------- 1 | REM create virtualenv 2 | python -m venv ..\..\..\.venv 3 | REM activate virtualenv 4 | call ..\..\..\.venv\Scripts\activate.bat 5 | REM install dependencies if necessary 6 | python -m pip install -r requirements.txt 7 | REM set pysdl2 dll path 8 | set PYSDL2_DLL_PATH=..\..\..\.venv\Lib\site-packages\sdl2dll\dll\ 9 | REM launch editor 10 | python editor.py -------------------------------------------------------------------------------- /src/python/editor/script.py: -------------------------------------------------------------------------------- 1 | def draw_script_mode(cur_state): 2 | pass 3 | -------------------------------------------------------------------------------- /src/python/editor/sprite_utils.py: -------------------------------------------------------------------------------- 1 | # rle compress image 2 | # scale RLE spans 3 | 4 | from PIL import Image 5 | 6 | SPRITE_SIZE = 64 7 | 8 | def rle_comp_seq(seq, max_len, is_empty, debug=False): 9 | skip_items = 0 10 | run_items = [] 11 | 12 | runs = [] 13 | got_items = False 14 | 15 | def finish_span_helper(): 16 | nonlocal skip_items, run_items, got_items 17 | skip = skip_items 18 | run = run_items 19 | skip_items = 0 20 | run_items = [] 21 | if skip != 0 or len(run) != 0: 22 | if len(run) != 0: 23 | got_items = True 24 | runs.append((skip,len(run),run)) 25 | 26 | for item in seq: 27 | cur_run_len = len(run_items) 28 | in_run = cur_run_len != 0 29 | 30 | if is_empty(item): 31 | if in_run: 32 | finish_span_helper() 33 | 34 | if skip_items == max_len: 35 | finish_span_helper() 36 | skip_items += 1 37 | else: 38 | # if we were just skipping empty pixels so far, start keeping track of solid pixels 39 | if cur_run_len == max_len: 40 | finish_span_helper() 41 | run_items.append(item) 42 | 43 | finish_span_helper() 44 | 45 | #for idx,run in enumerate(runs): 46 | # if all((x[1] == 0 for x in run)): 47 | # runs[idx] = 48 | if got_items: 49 | while runs[-1][1] == 0: 50 | runs.pop() 51 | 52 | return runs 53 | else: 54 | return [] 55 | 56 | 57 | 58 | def compile_image(path): 59 | im = Image.open(path) 60 | px = im.load() 61 | 62 | # all possible span sizes up to 32 input pixels have scales from 1 to 512 output pixels 63 | 64 | # 16.16 fixed point 65 | # indexed by (output<<5)|input 66 | #scale_table = [0] 67 | #for ipx in range(1, 32): 68 | # for opx in range(1, 513): 69 | # scale_fix = int((opx*65536)/ipx) 70 | # scale_table.append(scale_fix) 71 | 72 | 73 | columns = [] 74 | 75 | def get_word_pix(x1,x2,x3,x4,y): 76 | p1 = px[x1,y] 77 | p2 = px[x2,y] 78 | p3 = px[x3,y] 79 | p4 = px[x4,y] 80 | 81 | p = p1<<12|p2<<8|p3<<4|p4 82 | return p 83 | 84 | def get_doubled_pix(x,y): 85 | p = px[x,y] 86 | return p<<4|p 87 | 88 | rle_compressed_columns = [] 89 | 90 | # gathers four separate pixels into a word column, not going to work when scaling up, due to texel duplication 91 | for col in range(0, im.width): 92 | pix_row = [] 93 | for row in range(SPRITE_SIZE): 94 | pix_row.append(get_doubled_pix(col, row)) 95 | rle_compressed_columns.append( 96 | rle_comp_seq( 97 | pix_row, 98 | 63, 99 | lambda p: p==0 100 | ) 101 | ) 102 | """ 103 | for col in range(0, im.width, 4): 104 | pix_row = [] 105 | for row in range(SPRITE_SIZE): 106 | pix_row.append(get_word_pix(col, col+1, col+2, col+3, row)) 107 | 108 | rle_compressed_columns.append( 109 | rle_comp_seq( 110 | pix_row, 111 | 256, 112 | lambda p: p==0 113 | ) 114 | ) 115 | """ 116 | #rle_compressed_columns = [ 117 | # rle_comp_seq( 118 | # (get_pix(col,col+1,row) for row in range(SIZE)), 119 | # 256, 120 | # lambda p: p==0 121 | # ) for col in range(0, im.width, 2)] 122 | 123 | num_spans = 0 124 | for col in rle_compressed_columns: 125 | num_spans += len(col) 126 | 127 | #res = [(x*2,col) for x,col in enumerate(rle_compressed_columns)] 128 | #print(res) 129 | 130 | 131 | #print(("scaled output size total: ", num_spans*511*2)) # 28 kilobytes... wow 132 | return rle_compressed_columns 133 | 134 | def half(n): 135 | ni = n//2 136 | nf = n/2 137 | assert ni == nf 138 | return ni 139 | 140 | def generate_c_for_column(column,name): 141 | column_c = "" 142 | c_texels = [] 143 | c_spans = [] 144 | for span in column: 145 | (skip,length,texels) = span 146 | for texel in texels: 147 | c_texels.append("{0:#04x}".format(texel)) 148 | c_spans.append(str(skip)) 149 | c_spans.append(str(length)) 150 | 151 | if len(c_spans) != 0: 152 | c_spans_out = "const u16 col_{}_spans[{}] = ".format(name, len(c_spans)) + "{" + ",".join(c_spans) + "};" 153 | c_texels_out = "const u8 col_{}_texels[{}] = ".format(name, len(c_texels)) + "{" + ",".join(c_texels) + "};" 154 | 155 | column = "{" + ".num_spans = {}, .spans = col_{}_spans, .texels = col_{}_texels".format(half(len(c_spans)), name, name) + "}" 156 | else: 157 | c_spans_out = "" 158 | c_texels_out = "" 159 | column = "{.num_spans = 0, .spans = NULL, .texels = NULL}" 160 | return c_spans_out, c_texels_out, column 161 | #return ",".join(c_texels), ",".join(c_spans) 162 | 163 | 164 | if __name__ == '__main__': 165 | 166 | name = "test_texture" 167 | #columns = compile_image("./res/textures/sprites.png") 168 | #columns = compile_image("./res/textures/doobguy_3d_palette.png") 169 | columns = compile_image("./res/textures/doobguy.png") 170 | output = "" 171 | array_cols = [] 172 | 173 | 174 | 175 | for idx, column in enumerate(columns): 176 | spns,txls,col = generate_c_for_column(column, idx) 177 | if spns != "": 178 | output += spns 179 | output += "\n" 180 | if txls != "": 181 | output += txls 182 | output += "\n" 183 | array_cols.append(col) 184 | 185 | output += "const rle_object test_obj = {\n" 186 | output += ".num_columns = {},\n".format(len(columns)) 187 | output += ".columns = {\n" 188 | output += ",\n".join(array_cols) 189 | output += "\n}\n" 190 | output += "};\n" 191 | print(output) -------------------------------------------------------------------------------- /src/python/editor/state.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import os 3 | 4 | import map 5 | from modes import Mode 6 | import utils 7 | 8 | class State(object): 9 | def __init__(self, cur_path): 10 | self.mode = Mode.SECTOR 11 | self.cur_sector = None 12 | self.cur_wall = None 13 | self.cur_vertex = None 14 | self.cur_thing = None 15 | self.cur_sector_group = None 16 | self.cur_sector_pvs = None 17 | 18 | self.last_sector_inside = None 19 | 20 | self.camera_x = 0 21 | self.camera_y = 0 22 | self.zoom = 0 23 | 24 | 25 | self.emulator_path = "" 26 | self.xgmtool_path = "" 27 | self.textures_path = "textures/" 28 | self.music_tracks_path = "music/" 29 | self.sprites_path = "sprites/" 30 | 31 | self.load_config(cur_path) 32 | tex_files = utils.get_texture_files(self) 33 | sprite_files = utils.get_sprite_files(self) 34 | 35 | self.hovered_item = None 36 | 37 | self.default_up_color = 3 38 | self.default_low_color = 3 39 | self.default_mid_color = 0 40 | self.default_floor_color = 4 41 | self.default_ceil_color = 5 42 | self.default_texture_file = tex_files[0] 43 | self.default_sprite_file = sprite_files[0] 44 | self.default_thing_type = 0 45 | 46 | self.map_data: map.Map = map.Map(self.default_sprite_file) 47 | 48 | def load_config(self, cur_path): 49 | conf_file_path = os.path.join(cur_path, "conf.ini") 50 | conf_exists = os.path.exists(conf_file_path) 51 | if not conf_exists: 52 | print("No configuration file, using defaults!") 53 | return 54 | config = configparser.ConfigParser() 55 | config.read(conf_file_path) 56 | self.emulator_path = config.get("Default Settings", "emulator-path") 57 | self.xgmtool_path = config.get("Default Settings", "xgmtool-path") 58 | self.textures_path = config.get("Default Settings", "textures-path") 59 | self.sprites_path = config.get("Default Settings", "sprites-path") 60 | self.music_tracks_path = config.get("Default Settings", "music-tracks-path") 61 | self.megalink_path = config.get("Default Settings", "megalink-path") 62 | -------------------------------------------------------------------------------- /src/python/editor/texture_utils.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | 3 | 4 | def dpix(p): 5 | return (p << 4) | p 6 | 7 | 8 | TRANSPARENT_IDX = 0x0 9 | LIGHT_GREEN_IDX = 0x1 10 | LIGHT_RED_IDX = 0x2 11 | LIGHT_BLUE_IDX = 0x3 12 | LIGHT_BROWN_IDX = 0x4 13 | LIGHT_STEEL_IDX = 0x5 14 | GREEN_IDX = 0x6 15 | RED_IDX = 0x7 16 | BLUE_IDX = 0x8 17 | BROWN_IDX = 0x9 18 | STEEL_IDX = 0xA 19 | DARK_GREEN_IDX = 0xB 20 | DARK_RED_IDX = 0xC 21 | DARK_BLUE_IDX = 0xD 22 | DARK_BROWN_IDX = 0xE 23 | DARK_STEEL_IDX = 0xF 24 | 25 | dark_col_lut = [ 26 | 0, GREEN_IDX, RED_IDX, BLUE_IDX, BROWN_IDX, STEEL_IDX, 27 | DARK_GREEN_IDX, DARK_RED_IDX, DARK_BLUE_IDX, DARK_BROWN_IDX, DARK_STEEL_IDX, 28 | DARK_GREEN_IDX, DARK_RED_IDX, DARK_BLUE_IDX, DARK_BROWN_IDX, DARK_STEEL_IDX 29 | ] 30 | 31 | light_col_lut = [ 32 | 0, LIGHT_GREEN_IDX, LIGHT_RED_IDX, LIGHT_BLUE_IDX, LIGHT_BROWN_IDX, LIGHT_STEEL_IDX, 33 | LIGHT_GREEN_IDX, LIGHT_RED_IDX, LIGHT_BLUE_IDX, LIGHT_BROWN_IDX, LIGHT_STEEL_IDX, 34 | GREEN_IDX, RED_IDX, BLUE_IDX, BROWN_IDX, STEEL_IDX, 35 | ] 36 | 37 | 38 | def mpix(l, r, next_nib): 39 | return ((l << 4 | r) << 8) | (r << 4 | next_nib) 40 | 41 | def gen_textures_from_atlas(name, atlas_file): 42 | conv = [] 43 | dconv = [] 44 | 45 | def add_pix(arr, l, r): 46 | arr.append((dpix(l) << 8) | dpix(r)) 47 | 48 | atlas = Image.open(atlas_file) 49 | all_pix = atlas.load() 50 | 51 | def pix(x, y): 52 | return all_pix[x, y], all_pix[x+64, y] 53 | 54 | TEX_SIZE = 64 55 | 56 | atlas_width, atlas_height = atlas.size 57 | 58 | #assert atlas_width == TEX_SIZE*2 59 | 60 | num_textures = atlas_height//TEX_SIZE 61 | assert num_textures * TEX_SIZE == atlas_height 62 | 63 | for tex in range(num_textures): 64 | base_y = tex*64 65 | 66 | for x in range(0, TEX_SIZE, 2): 67 | lx, rx = x, x + 1 68 | for y in range(TEX_SIZE): 69 | mid_l, dark_l = pix(lx, y+base_y) 70 | mid_r, dark_r = pix(rx, y+base_y) 71 | add_pix(conv, mid_l, mid_r) 72 | add_pix(dconv, dark_l, dark_r) 73 | 74 | names = [] 75 | for (key, tbl) in [('mid', conv), ('dark', dconv), 76 | ]: 77 | 78 | gen_name = "raw_{}_tex_{}_{}".format(name, tex, key) 79 | names.append(gen_name) 80 | print("const u16 {}[{}*{}]".format(gen_name, len(tbl) // 64, 64) + " = {") 81 | for i, col in enumerate(tbl): 82 | print("0x{:02x}, ".format(col), end='') 83 | if i != 0 and i % 64 == 0: 84 | print("") 85 | print("\n};\n\n") 86 | 87 | mname = names[0] 88 | dname = names[1] 89 | for (level,d,m,l) in [("dark", dname, dname, mname), 90 | #("mid_dark", dname, mname), 91 | ("mid", dname, dname, mname), 92 | #("mid_light", mname, mname), 93 | ("light", mname, mname, mname)]: 94 | 95 | print("const lit_texture lit_{}_tex_{}_{} = {}".format(name, tex, level, '{')) 96 | print(" .dark = {}, .mid = {}, .light = {}".format(d, m, l)) 97 | print("};") 98 | 99 | conv = [] 100 | dconv = [] 101 | 102 | 103 | def gen_texture_words_from_file(file): 104 | conv = [] 105 | dconv = [] 106 | 107 | def add_pix(arr, l, r): 108 | arr.append((dpix(l) << 8) | dpix(r)) 109 | 110 | atlas = Image.open(file) 111 | all_pix = atlas.load() 112 | 113 | def pix(x, y): 114 | return all_pix[x, y], all_pix[x+64, y] 115 | 116 | TEX_SIZE = 64 117 | 118 | for x in range(0, TEX_SIZE, 2): 119 | lx, rx = x, x + 1 120 | for y in range(TEX_SIZE): 121 | mid_l, dark_l = pix(lx, y) 122 | mid_r, dark_r = pix(rx, y) 123 | add_pix(conv, mid_l, mid_r) 124 | add_pix(dconv, dark_l, dark_r) 125 | return conv, dconv 126 | 127 | 128 | 129 | def gen_mip_image(name, light_file, mid_file, dark_file): 130 | conv = [] 131 | lconv = [] 132 | dconv = [] 133 | 134 | 135 | def add_pix(arr, l, r): 136 | arr.append((dpix(l) << 8) | dpix(r)) 137 | 138 | light_img = Image.open(light_file) 139 | mid_img = Image.open(mid_file) 140 | dark_img = Image.open(dark_file) 141 | 142 | light_pix = light_img.load() 143 | mid_pix = mid_img.load() 144 | dark_pix = dark_img.load() 145 | 146 | def pix(x, y): 147 | return light_pix[x, y], mid_pix[x, y], dark_pix[x, y] 148 | 149 | mx, my = light_img.size 150 | assert light_img.size == mid_img.size 151 | assert mid_img.size == dark_img.size 152 | 153 | for x in range(0, mx, 2): 154 | lx, rx = x, x + 1 155 | for y in range(my): 156 | light_l, mid_l, dark_l = pix(lx, y) 157 | light_r, mid_r, dark_r = pix(rx, y) 158 | 159 | add_pix(lconv, light_l, light_r) 160 | add_pix(conv, mid_l, mid_r) 161 | add_pix(dconv, dark_l, dark_r) 162 | 163 | 164 | names = [] 165 | for (key, tbl) in [('light', lconv), ('mid', conv), ('dark', dconv), 166 | ]: 167 | gen_name = "raw_{}_{}".format(name, key) 168 | names.append(gen_name) 169 | print("const u16 {}[{}*{}]".format(gen_name, len(tbl) // 64, 64) + " = {") 170 | for i, col in enumerate(tbl): 171 | print("0x{:02x}, ".format(col), end='') 172 | if i != 0 and i % 64 == 0: 173 | print("") 174 | print("\n};\n\n") 175 | 176 | dname = names[2] 177 | mname = names[1] 178 | lname = names[0] 179 | for (level,d,m,l) in [("dark", dname, dname, dname), 180 | ("mid_dark", dname, dname, mname), 181 | ("mid", dname, mname, lname), 182 | ("mid_light", mname, lname, lname), 183 | ("light", lname, lname, lname)]: 184 | 185 | print("const lit_texture {}_{} = {}".format(name, level, '{')) 186 | print(" .dark = {}, .mid = {}, .light = {}".format(d, m, l)) 187 | print("};") 188 | 189 | 190 | 191 | 192 | if __name__ == '__main__': 193 | 194 | 195 | 196 | 197 | #gen_textures_from_atlas("tex_15col", "./res/textures/texture_atlas.png") 198 | 199 | gen_textures_from_atlas("texture_atlas_sprite_palette", "./res/textures/texture_atlas_sprite_palette.png") 200 | 201 | -------------------------------------------------------------------------------- /src/python/editor/things.py: -------------------------------------------------------------------------------- 1 | import imgui 2 | 3 | import utils 4 | import undo 5 | 6 | 7 | KEY_TYPES = ['None', 'Blue key', 'Green key', 'Red key'] 8 | 9 | class ThingDef(object): 10 | def __init__(self, sprite_file, name="", width=64, height=64, anchor_draw_offset=0, init_state=0, speed=3, anchor_top = False, anchor_bottom = True, key_type = 0): 11 | self.name = name 12 | self.sprite_file = sprite_file 13 | self.width = width 14 | self.height = height 15 | self.anchor_draw_offset = anchor_draw_offset 16 | self.init_state = init_state 17 | self.speed = speed 18 | self.anchor_top = anchor_top 19 | self.anchor_bottom = anchor_bottom 20 | self.key_type = key_type 21 | #def draw_things_mode(cur_state): 22 | # for i in range(cur_state.map_data.things): 23 | 24 | class Thing(object): 25 | def __init__(self, typ, sector_num, x, y, z, index): 26 | self.sector_num = sector_num 27 | self.object_type = typ 28 | self.x = x 29 | self.y = y 30 | self.z = z 31 | self.index = index 32 | 33 | def __str__(self): 34 | return "type: {}, sector: {}, x,y: {},{}".format(self.object_type, self.sector_num, self.x, self.y) 35 | 36 | 37 | def point_collides(self,x,y): 38 | radius = 10 39 | x1 = self.x 40 | y1 = self.y 41 | x2 = x 42 | y2 = y 43 | 44 | return utils.point_in_circle(x1,y1,x2,y2,radius) 45 | 46 | def draw_thing_defs_mode(cur_state): 47 | 48 | 49 | sprite_files = utils.get_sprite_files(cur_state) 50 | 51 | 52 | imgui.begin_child("thing defs") 53 | thing_defs = cur_state.map_data.thing_defs 54 | for idx,thing_def in enumerate(thing_defs): 55 | 56 | if thing_def.sprite_file == "": 57 | thing_def.sprite_file = sprite_files[0] 58 | def set_sprite_file(spr): 59 | undo.push_state(cur_state) 60 | thing_defs[idx].sprite_file = spr 61 | 62 | name_changed, new_name = imgui.input_text("name:##obj_name_{} ".format(idx), thing_defs[idx].name, buffer_length=32) 63 | 64 | utils.file_selector("sprite##obj_sprite_{}".format(idx), thing_def.sprite_file, sprite_files, set_sprite_file) 65 | 66 | width_changed, new_width = imgui.input_int("width:##obj_{}_width".format(idx), thing_defs[idx].width) 67 | height_changed, new_height = imgui.input_int("height:##obj_{}_height".format(idx), thing_defs[idx].height) 68 | speed_changed, new_speed = imgui.input_int("speed:##obj_{}_speed".format(idx), thing_defs[idx].speed) 69 | anchor_draw_off_changed, new_anchor_draw_offset = imgui.input_int("anchor draw offset:##obj_floor_draw_off{}".format(idx), thing_defs[idx].anchor_draw_offset) 70 | 71 | #state_options = ["idle","look for player", "follow player", "maybe get picked up"] 72 | state_options = ["static","look for player", "follow player", "maybe get picked up"] 73 | 74 | init_state_changed, new_init_state = imgui.core.combo("init state:##obj_{}_init_state".format(idx), thing_def.init_state, state_options) 75 | 76 | key_type_changed, new_key_type = imgui.core.combo("key type:##obj_{}_key_type".format(idx), thing_def.key_type, KEY_TYPES) 77 | 78 | anchor_top_changed, new_anchor_top = imgui.core.checkbox("anchor top##obj_{}_anchor_top".format(idx), thing_def.anchor_top) 79 | imgui.same_line() 80 | anchor_bot_changed, new_anchor_bot = imgui.core.checkbox("anchor bottom##obj_{}_anchor_bot".format(idx), thing_def.anchor_bottom) 81 | 82 | 83 | if name_changed: 84 | undo.push_state(cur_state) 85 | thing_defs[idx].name = new_name 86 | 87 | if width_changed: 88 | undo.push_state(cur_state) 89 | thing_defs[idx].width = new_width 90 | 91 | if height_changed: 92 | undo.push_state(cur_state) 93 | thing_defs[idx].height = new_height 94 | 95 | if speed_changed: 96 | undo.push_state(cur_state) 97 | thing_defs[idx].speed = new_speed 98 | 99 | if anchor_draw_off_changed: 100 | undo.push_state(cur_state) 101 | thing_defs[idx].anchor_draw_offset = new_anchor_draw_offset 102 | 103 | if init_state_changed: 104 | undo.push_state(cur_state) 105 | thing_defs[idx].init_state = new_init_state 106 | 107 | if anchor_top_changed: 108 | thing_defs[idx].anchor_top = new_anchor_top 109 | 110 | if anchor_bot_changed: 111 | thing_defs[idx].anchor_bottom = new_anchor_bot 112 | 113 | if key_type_changed: 114 | thing_defs[idx].key_type = new_key_type 115 | 116 | imgui.dummy(0, 10) 117 | 118 | imgui.end_child() 119 | 120 | 121 | def draw_things_mode(cur_state): 122 | 123 | if cur_state.cur_thing is not None: 124 | cur_thing = cur_state.cur_thing 125 | type_options = ['player'] + [ thing.name for thing in cur_state.map_data.thing_defs] 126 | type_changed, new_type = imgui.core.combo("type", cur_thing.object_type, type_options) 127 | if type_changed: 128 | cur_thing.object_type = new_type 129 | 130 | x_changed, new_x = imgui.input_int("x:##obj_x", cur_thing.x) 131 | if x_changed: 132 | cur_thing.x = new_x 133 | y_changed, new_y = imgui.input_int("y:##obj_y", cur_thing.y) 134 | if y_changed: 135 | cur_thing.y = new_y 136 | 137 | sector_opts = ["{}".format(idx) for idx in range(len(cur_state.map_data.sectors))] 138 | 139 | sect_changed,new_sect = imgui.core.combo("sector", cur_thing.sector_num, sector_opts) 140 | if sect_changed: 141 | cur_thing.sector_num = new_sect 142 | 143 | 144 | def print_thing(thing): 145 | if thing.object_type == 0: 146 | thing_name = ' player' 147 | else: 148 | thing_name = cur_state.map_data.thing_defs[thing.object_type-1].name 149 | return str(thing) + " name: {}".format(thing_name) 150 | 151 | 152 | def set_cur_thing(idx): 153 | cur_state.cur_thing = cur_state.map_data.things[idx] 154 | 155 | def delete_thing(thing): 156 | if cur_state.cur_thing == thing: 157 | cur_state.cur_thing = None 158 | 159 | for idx,cur_thing in enumerate(cur_state.map_data.things): 160 | if thing == cur_thing: 161 | undo.push_state(cur_state) 162 | del cur_state.map_data.things[idx] 163 | break 164 | 165 | 166 | utils.draw_list(cur_state, "Things", "Thing list", cur_state.map_data.things, 167 | set_cur_thing, 168 | delete_callback=delete_thing, 169 | print_item=print_thing) -------------------------------------------------------------------------------- /src/python/editor/tree.py: -------------------------------------------------------------------------------- 1 | import imgui 2 | import sector 3 | 4 | def split_concave_sector(sect, cur_state): 5 | for wall in sect.walls: 6 | has_adj_wall,adj_wall = wall.find_adjacent_wall(cur_state.map_data) 7 | if has_adj_wall: 8 | print("Portal to this sector in sector {} might need to be split".format(adj_wall.sector_idx)) 9 | 10 | if wall.adj_sector_idx != -1: 11 | print("Portal to sector {} might need to be split".format(wall.adj_sector_idx)) 12 | 13 | # for each vertex in the sector 14 | # try to draw from that vertex to a new vertex, creating two partial sectors 15 | # check if both are convex 16 | # if they are, we're good. 17 | 18 | # if one is still concave, recursively split the extra concave sector 19 | # if 20 | 21 | sector_verts = sect.get_vertexes() 22 | num_verts = len(sector_verts) 23 | floor_height = sect.floor_height 24 | ceil_height = sect.ceil_height 25 | 26 | for i1 in range(1, num_verts): 27 | for i2 in range(num_verts): 28 | if i1 == i2: 29 | continue 30 | if abs(i2-i1) == 1: 31 | continue 32 | 33 | v1 = sector_verts[i1] 34 | v2 = sector_verts[i2] 35 | 36 | fst_sector = sector.Sector(index = sect.index+1, floor_height=floor_height, ceil_height=ceil_height) 37 | snd_sector = sector.Sector(index = sect.index+2, floor_height=floor_height, ceil_height=ceil_height) 38 | # how do we split the sector with these vertexes? 39 | 40 | # first sector starts at i2 and goes up to i1, then reconnects to i2 41 | print("first sector from vert {} to vert {}".format(i2, i1)) 42 | 43 | # second sector starts at i1 and goes up to i2, then reconnects to i1 44 | print("second sector from vert {} to vert {}".format(i1, i2)) 45 | 46 | 47 | fst_sector.add_wall() 48 | 49 | def split_concave_sectors(cur_state): 50 | for sect in cur_state.map_data.sectors: 51 | if sect.is_convex(): 52 | continue 53 | 54 | split_concave_sector(sect, cur_state) 55 | 56 | print("Sector {} is concave, splitting".format(sect.index)) 57 | 58 | 59 | # update cur_sector,wall,vertex? 60 | 61 | 62 | def draw_tree_mode(cur_state): 63 | disabled = all([s.is_convex() for s in cur_state.map_data.sectors]) 64 | 65 | if not disabled: 66 | if imgui.button("Split concave sectors"): 67 | split_concave_sectors(cur_state) 68 | 69 | # any sectors connected to this sector 70 | -------------------------------------------------------------------------------- /src/python/editor/trigger.py: -------------------------------------------------------------------------------- 1 | def draw_trigger_mode(cur_state): 2 | pass 3 | -------------------------------------------------------------------------------- /src/python/editor/undo.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | undo_stack = [] 4 | redo_stack = [] 5 | 6 | def undo(cur_state, set_cur_state): 7 | global undo_stack, redo_stack 8 | if len(undo_stack) > 0: 9 | redo_stack.append(cur_state) 10 | if len(redo_stack) > 100: 11 | redo_stack = redo_stack[1:] # limit to max of 100 redo's 12 | set_cur_state(undo_stack.pop()) 13 | 14 | def redo(cur_state, set_cur_state): 15 | global undo_stack, redo_stack 16 | if len(redo_stack) > 0: 17 | undo_stack.append(cur_state) 18 | if len(undo_stack) > 100: 19 | undo_stack = undo_stack[1:] # limit to max of 100 undo's 20 | set_cur_state(redo_stack.pop()) 21 | 22 | # call before every user action that mutates the map data 23 | def push_state(cur_state): 24 | global undo_stack, redo_stack 25 | undo_stack.append(copy.deepcopy(cur_state)) 26 | redo_stack = [] 27 | 28 | def can_undo(): 29 | return len(undo_stack) > 0 30 | 31 | def can_redo(): 32 | return len(redo_stack) > 0 -------------------------------------------------------------------------------- /src/python/editor/vertex.py: -------------------------------------------------------------------------------- 1 | from utils import point_in_circle, input_int2, draw_list 2 | import imgui 3 | import undo 4 | 5 | 6 | class Vertex(): 7 | def __init__(self, x, y, index): 8 | self.x = x 9 | self.y = y 10 | self.index = index 11 | 12 | def __str__(self): 13 | return "x: {} y: {}".format(self.x, self.y) 14 | 15 | def __eq__(self, other): 16 | if isinstance(other, Vertex): 17 | return self.x == other.x and self.y == other.y 18 | else: 19 | return False 20 | 21 | def point_collides(self,x,y): 22 | radius = 5 23 | x1 = self.x 24 | y1 = self.y 25 | x2 = x 26 | y2 = y 27 | 28 | return point_in_circle(x1,y1,x2,y2,radius) 29 | 30 | 31 | 32 | 33 | def draw_vert_mode(cur_state): 34 | 35 | if cur_state.cur_vertex is not None: 36 | 37 | cur_vertex = cur_state.cur_vertex 38 | imgui.text("Vertex {}".format(cur_vertex.index)) 39 | 40 | def set_xy(xy): 41 | undo.push_state(cur_state) 42 | (x,y) = xy 43 | cur_state.cur_vertex.x = x 44 | cur_state.cur_vertex.y = y 45 | 46 | input_int2("x,y: ", "##vert{}_xy".format(cur_vertex.index), (cur_vertex.x, cur_vertex.y), 47 | set_xy) 48 | 49 | 50 | 51 | def set_cur_vertex(idx): 52 | cur_state.cur_vertex = cur_state.map_data.vertexes[idx] 53 | 54 | def delete_vertex(vert): 55 | if cur_state.cur_vertex == vert: 56 | cur_state.cur_vertex = None 57 | deleted_idx = None 58 | for idx,vertex in enumerate(cur_state.map_data.vertexes): 59 | if vert == vertex: 60 | undo.push_state(cur_state) 61 | del cur_state.map_data.vertexes[idx] 62 | deleted_idx = idx 63 | break 64 | if deleted_idx is not None: 65 | for idx,vertex in enumerate(cur_state.map_data.vertexes): 66 | vertex.index = idx 67 | 68 | # disable deleting vertexes for now 69 | # i'm not sure what it should do if you delete a vertex that's used by a line 70 | # maybe we should disallow it 71 | draw_list(cur_state, "Vertexes", "Vertex list", 72 | cur_state.map_data.vertexes, 73 | set_cur_vertex) #, delete_vertex) 74 | 75 | -------------------------------------------------------------------------------- /src/python/editor/wad.py: -------------------------------------------------------------------------------- 1 | # load and save wad files 2 | 3 | # directory 4 | 5 | # BWAD -> base wad 6 | 7 | from io import BufferedRandom 8 | from typing import NewType 9 | from file_utils import read_u8, read_u16, read_u32 10 | 11 | WadHeader = NewType(dict[str, int]) 12 | WadChunk = NewType(tuple[str, bytes]) 13 | 14 | 15 | Wad = NewType(list[WadChunk]) 16 | 17 | NAME_FIELD_LEN = 32 18 | def read_name(file: BufferedRandom) -> str: 19 | return str(file.read(NAME_FIELD_LEN)) 20 | 21 | 22 | def read_wad_header(wad_file: BufferedRandom) -> WadHeader: 23 | # already read the tag and asserted that it was 'BWAD' 24 | num_directories = read_u16(wad_file) 25 | header = {} 26 | for i in range(num_directories): 27 | # name and offset (length can be in the directory itself?) 28 | # name is up to 32 characters 29 | nm = read_name(wad_file) 30 | off = read_u32(wad_file) 31 | header[nm] = off 32 | return WadHeader(header) 33 | 34 | def read_wad_chunks(wad_file: BufferedRandom, header: WadHeader) -> list[WadChunk]: 35 | chunks: list[WadChunk] = [] 36 | for nm, off in header.items(): 37 | wad_file.seek(off) 38 | chunk_len = read_u32(wad_file) 39 | chunks.append(WadChunk(tuple(nm, wad_file.read(chunk_len)))) 40 | return chunks 41 | 42 | 43 | 44 | def read_wad(wad_file: BufferedRandom) -> Wad: 45 | tag = wad_file.read(4) 46 | 47 | assert tag == 'BWAD' 48 | header = read_wad_header(wad_file) 49 | chunks = read_wad_chunks(wad_file, header) 50 | 51 | return Wad(chunks) 52 | 53 | 54 | def export_to_rom(header, directories, wad_bytes): 55 | pass -------------------------------------------------------------------------------- /src/python/gen_font_shadow_lut.py: -------------------------------------------------------------------------------- 1 | for i in range(257): 2 | lpix = i>>4 3 | rpix = i&0b1111 4 | lshad = rshad = 0 5 | if lpix > 0: 6 | lshad = 0b1110 7 | if rpix > 0: 8 | rshad = 0b1110 9 | print((lshad<<4)|rshad, end=',') 10 | if i != 0 and i % 16 == 0: 11 | print("") -------------------------------------------------------------------------------- /src/python/gen_linedef_rasterizer.py: -------------------------------------------------------------------------------- 1 | def get_y_var_names(num): 2 | return [("y{}".format(i), "dy{}_dx".format(i)) for i in range(num)] 3 | 4 | 5 | def gen_rasterizer(bitmap): 6 | draw_upper_step = (bitmap & 0b1000) >> 3 7 | draw_lower_step = (bitmap & 0b0100) >> 2 8 | draw_ceiling = (bitmap & 0b0010) >> 1 9 | draw_floor = (bitmap & 0b0001) 10 | 11 | trace_upper_line = draw_upper_step | draw_ceiling 12 | trace_lower_line = draw_lower_step | draw_floor 13 | 14 | update_upper_clipping = trace_upper_line 15 | update_lower_clipping = trace_lower_line 16 | 17 | trace_neighbor_upper_line = draw_upper_step 18 | trace_neighbor_lower_line = draw_lower_step 19 | num_y_vars = (trace_upper_line + trace_neighbor_upper_line + trace_lower_line + trace_neighbor_lower_line) 20 | 21 | y_var_names = get_y_var_names(num_y_vars) 22 | 23 | print("for(int x = x0; x < x1; x++){") 24 | for y_var,y_var_dx in y_var_names: 25 | print(" {} += {};".format(y_var, y_var_dx)) 26 | print("}") 27 | 28 | 29 | for bitmap in range(0b0001, 0b1111): 30 | gen_rasterizer(bitmap) 31 | -------------------------------------------------------------------------------- /src/python/gen_postinc_tex_tables.py: -------------------------------------------------------------------------------- 1 | HEADER_TOP_CLIP = """ 2 | | tex_column in a0, col_ptr in a1, top_clip in d0 3 | lsl.l #2, %d0 4 | jmp scale_64_{}_jump(%pc, %d0.w) 5 | """ 6 | 7 | TABLE_HEADER_BOT_CLIP = """ 8 | | tex_column in a0, col_ptr in a1, top_clip in d0, bot_clip in d1 9 | add.w %d1, %d0 | d0 = clip_top+clip_bot 10 | lsl.w #2, %d0 | d0 = (clip_top+clip_bot)<<=2 11 | move.l %a0, -(%sp) | stash tex_column for now 12 | move.l #skip_64_{}, %a0 13 | move.b 0(%a0, %d1.w), %d1 | d1 = texels to skip backwards 14 | | ext.w %d1 | extend d1 to a longword |ext.w %d1 | andi.w #255, %d1 15 | move.l (%sp)+, %a0 16 | sub.w %d1, %a0 17 | jmp scale_64_{}_jump(%pc, %d0.w) 18 | 19 | """ 20 | 21 | 22 | # sadly, this doesnt seem to solve the problem 23 | # becuse there are different sources of error caused by jumping to the right/wrong position in the instruction stream 24 | HEADER_BOT_CLIP = """ 25 | | tex_column in a0, col_ptr in a1, top_clip in d0, bot_clip in d1 26 | lsl.w #2, %d0 | d0 is top_clip * 4 27 | move.l %a0, -(%sp) | stash tex_column for now 28 | move.w scale_64_{}_jump+2(%pc, %d0.l), %a0 | a0 = expected offset 29 | lsl.w #2, %d1 | d1 is bot_clip * 4 30 | add.w %d1, %d0 | d0 = (top_clip*4+bot_clip*4) 31 | move.w scale_64_{}_jump+2(%pc, %d0.l), %d1 | d1 = actual offset 32 | sub.w %a0, %d1 | negative offset to use 33 | ext.w %d1 | extend d1 to a longword 34 | move.l (%sp)+, %a0 35 | sub.l %d1, %a0 36 | jmp scale_64_{}_jump(%pc, %d0.w) 37 | """ 38 | 39 | 40 | 41 | for y in range(513): 42 | print(".globl scale_64_{}_top_clip".format(y)) 43 | print(".globl scale_64_{}_bot_clip".format(y)) 44 | 45 | 46 | print("scale_64_{}_top_clip:".format(y)) 47 | if y == 0: 48 | print("rts") 49 | else: 50 | print(HEADER_TOP_CLIP.format(y)) 51 | 52 | print("scale_64_{}_bot_clip:".format(y)) 53 | if y == 0: 54 | print("rts") 55 | else: 56 | print(TABLE_HEADER_BOT_CLIP.format(y, y, y, y, y, y, y)) 57 | 58 | if y == 0: 59 | continue 60 | 61 | du_per_dy = 64/y 62 | scaled = du_per_dy <= 1 63 | cur_u = 0 64 | 65 | print("scale_64_{}_jump:".format(y)) 66 | if 0: #scaled: 67 | 68 | 69 | for yy in range(y): 70 | int_u = int(cur_u) 71 | cur_u += du_per_dy 72 | next_int_u = int(cur_u) 73 | if next_int_u > int_u+1: 74 | raise Exception("wtf") 75 | elif next_int_u == int_u+1: 76 | print(" move.w (%a0)+, (%a1)+") 77 | else: 78 | print(" move.w 0(%a0), (%a1)+") 79 | else: 80 | 81 | for yy in range(y): 82 | int_u = int(cur_u) 83 | cur_u += du_per_dy 84 | if int_u == 0: 85 | print("dc.w 0b0011001011101000, 0b0000000000000000") 86 | else: 87 | print("move.w {}(%a0), (%a1)+".format(2*int_u)) 88 | print("rts") 89 | 90 | 91 | 92 | #print(".global skip_64_{}".format(y)) 93 | #print("skip_64_{}:".format(y)) 94 | #for yy in range(y): 95 | # int_u = int(cur_u) 96 | # cur_u += du_per_dy 97 | # print("dc.w {}".format(int_u)) 98 | 99 | 100 | """ 101 | for y in range(1,513): 102 | print("const u8 skip_64_{}[{}] = ".format(y, y) + "{") 103 | du_dy = 64/y 104 | for skip in range(y): 105 | print("{}, ".format(2*int(skip*du_dy)), end="") 106 | if skip % 40 == 0: 107 | print("") 108 | print("};") 109 | """ 110 | 111 | #for y in range(1, 513): 112 | # print("skip_64_{}, ".format(y), end="") 113 | # 114 | # if y % 20 == 0: 115 | # print("") 116 | 117 | #for y in range(513): 118 | # print("extern void scale_64_{}_top_clip(void);".format(y)) 119 | # print("extern void scale_64_{}_bot_clip(void);".format(y)) 120 | 121 | 122 | #print("jump_table_top_clip_lut[513] = {") 123 | #for y in range(1, 513): 124 | # print("scale_64_{}_top_clip, ".format(y), end="") 125 | # if y % 8 == 0: 126 | # print("") 127 | #print("}") 128 | 129 | #print("jump_table_bot_clip_lut[513] = {") 130 | #for y in range(1, 513): 131 | # print("scale_64_{}_bot_clip, ".format(y), end="") 132 | # if y % 8 == 0: 133 | # print("") 134 | #print("}") -------------------------------------------------------------------------------- /src/python/gen_recip_table.py: -------------------------------------------------------------------------------- 1 | def gen_table(): 2 | pass 3 | 4 | if __name__ == '__main__': 5 | for z_12_4 in range(65536): 6 | if z_12_4 == 0: 7 | print(0, end=", ") 8 | continue 9 | 10 | val = int(1<<26)//z_12_4 11 | print(val, end=", ") 12 | 13 | 14 | if z_12_4 % 50 == 0: 15 | print("") 16 | 17 | -------------------------------------------------------------------------------- /src/python/gen_sprite_scale_routine_tables.py: -------------------------------------------------------------------------------- 1 | # generate tables to update in-RAM sprite scaling routine 2 | names = [] 3 | MAX_SIZE = 512 4 | for output_size in range(MAX_SIZE+1): #513): 5 | name = "sprite_scale_{}_table".format(output_size) 6 | names.append(name) 7 | 8 | print("const u16 {}[{}] = ".format(name, output_size) + "{", end='') 9 | 10 | scale = output_size/64 11 | if output_size == 0: 12 | texels_per_pixel = 0 13 | else: 14 | texels_per_pixel = 64/output_size 15 | 16 | cur_texel = 0 17 | for i in range(output_size): 18 | if i % 32 == 0: 19 | print("") 20 | print("{},".format(int(cur_texel)), end='') 21 | cur_texel += texels_per_pixel 22 | 23 | print("\n};") 24 | 25 | print("const u16* sprite_scale_routine_pointer_lut[{}] = ".format(len(names)) + "{", end="") 26 | for y in range(MAX_SIZE+1): 27 | if y % 4 == 0: 28 | print("") 29 | print("{}, ".format(names[y]), end='') 30 | print("};") 31 | 32 | -------------------------------------------------------------------------------- /src/python/gen_sprite_scale_tables.py: -------------------------------------------------------------------------------- 1 | base = 64 2 | 3 | cnt = 0 4 | 5 | """ 6 | # size of run lengths of M, for 64-texel bitmap scaled to N pixels 7 | print("{") 8 | for scaled_size in range(0,513): 9 | scale_factor = (scaled_size)/64 10 | for run_length in range(64): 11 | scaled_run_length = int((run_length)*65536*scale_factor) 12 | cnt += 1 13 | print(scaled_run_length, end=", ") 14 | if cnt % 16 == 0: 15 | print("") 16 | print("}") 17 | 18 | #for i in range(512): 19 | # # calculate texels per pixel 20 | """ 21 | 22 | # number of 23 | print("u32 sprite_tex_per_pix[513] = {", end='') 24 | for scale in range(513): 25 | if scale % 32 == 0: 26 | print('') 27 | if scale == 0: 28 | tex_per_pix = 0 29 | else: 30 | tex_per_pix = int((64*65536)/scale) 31 | 32 | print("{},".format(tex_per_pix), end='') 33 | print("};") -------------------------------------------------------------------------------- /src/python/optimizer.py: -------------------------------------------------------------------------------- 1 | import itertools, random 2 | 3 | def optimize_linedef_entries(entries): 4 | linedefs_with_vertexes = {} 5 | for (linedef,v1,v2) in entries: 6 | lst = linedefs_with_vertexes.get(v1, list()) 7 | lst.append(linedef) 8 | linedefs_with_vertexes[v1] = lst 9 | lst2 = linedefs_with_vertexes.get(v2, list()) 10 | lst2.append(linedef) 11 | linedefs_with_vertexes[v2] = lst2 12 | return linedefs_with_vertexes 13 | 14 | def count_vertex_transformations(entries): 15 | prev_vert = None 16 | transformations = [] 17 | for (l,v1,v2) in entries: 18 | if v1 == prev_vert: 19 | # no transformations needed 20 | pass 21 | else: 22 | transformations.append(v1) 23 | transformations.append(v2) 24 | prev_vert = v2 25 | return transformations 26 | 27 | 28 | def get_linedefs_permutations(entries): 29 | linedef_to_verts = {} 30 | for (l,v1,v2) in entries: 31 | linedef_to_verts[l] = (v1,v2) 32 | permutations = itertools.permutations(l for (l,_,_) in entries) 33 | 34 | res = [] 35 | for perm in permutations: 36 | perm2 = [] 37 | for l in perm: 38 | (v1,v2) = linedef_to_verts[l] 39 | perm2.append((l,v1,v2)) 40 | res.append(perm2) 41 | return res 42 | 43 | def swap_entry(entry): 44 | (l,v1,v2) = entry 45 | return (l,v2,v1) 46 | 47 | 48 | def get_vertex_swap_permutations(entries, prefixes=[]): 49 | #print("got {}".format(entries)) 50 | #print("prefixes: {}".format(prefixes)) 51 | if len(entries) == 0: 52 | res = prefixes 53 | elif len(entries) == 1: 54 | fst = entries[0] 55 | res = [] 56 | for prefix in prefixes: 57 | res.append(prefix + [fst]) 58 | res.append(prefix + [swap_entry(fst)]) 59 | 60 | else: 61 | fst = entries[0] 62 | swapped_fst = swap_entry(fst) 63 | new_prefixes_fst = [pfx+[fst] for pfx in prefixes] 64 | new_prefixes_swp = [pfx+[swapped_fst] for pfx in prefixes] 65 | new_prefixes = new_prefixes_fst + new_prefixes_swp 66 | if len(new_prefixes) == 0: 67 | new_prefixes = [[fst], [swapped_fst]] 68 | res = get_vertex_swap_permutations(entries[1:], new_prefixes) 69 | 70 | #print("returning: {}".format(res)) 71 | return res 72 | 73 | 74 | def get_all_possible_permutations(entries): 75 | res = [] 76 | linedef_perms = get_linedefs_permutations(entries) 77 | for linedef_entries_permutation in linedef_perms: 78 | for vertex_perm in get_vertex_swap_permutations(linedef_entries_permutation): 79 | res.append(vertex_perm) 80 | return res 81 | 82 | def find_best_ordering(entries): 83 | best_score = None 84 | best_scoring = None 85 | for perm in get_all_possible_permutations(entries): 86 | transformations = count_vertex_transformations(perm) 87 | score = len(transformations) 88 | if best_scoring is None or score < best_score: 89 | best_scoring = perm 90 | best_score = score 91 | 92 | return best_scoring, best_score 93 | 94 | 95 | 96 | 97 | if __name__ == '__main__': 98 | entries = [(1,'v1','v2'),(2,'v2','v3'), (3,'v0','v1')] 99 | entries2 = [(1,'v1','v2'),(2,'v2','v3')] 100 | 101 | long_list = [(i, range(50)] 102 | #print(get_vertex_swap_permutations(entries)) 103 | #print(get_vertex_swap_permutations(entries2)) 104 | 105 | #print(get_all_possible_permutations(entries)) 106 | 107 | print(find_best_ordering(entries)) 108 | 109 | -------------------------------------------------------------------------------- /src/python/profile.stats: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/python/profile.stats -------------------------------------------------------------------------------- /src/python/span_buffer.py: -------------------------------------------------------------------------------- 1 | #from intervaltree import Interval, IntervalTree 2 | 3 | span_buffer = None 4 | 5 | def clear_span_buffer(): 6 | pass 7 | #global span_buffer 8 | #span_buffer = IntervalTree() 9 | 10 | def does_interval_overlap(x1, x2): 11 | return span_buffer.overlaps(Interval(x1, x2)) 12 | 13 | 14 | def get_clipped_draw_spans(x1, x2): 15 | overlapping_spans = span_buffer.overlap(x1, x2) 16 | 17 | #for span in spans: 18 | 19 | # if 20 | 21 | 22 | def insert_interval(x1, x2): 23 | pass 24 | #span_buffer[ 25 | 26 | 27 | #def reset(max_width): 28 | #global span_buf 29 | #span_buf = [(0, max_width)] 30 | 31 | def __span_intersects_span(span1, span2): 32 | (span1_x1, span1_x2) = span1 33 | (span2_x1, span2_x2) = span2 34 | 35 | 36 | if span1_x2 < span2_x1: 37 | return False 38 | 39 | if span1_x1 > span2_x2: 40 | return False 41 | 42 | return True 43 | 44 | 45 | 46 | def span_intersects(span): 47 | for item in span_buf: 48 | if(__span_intersects_span(span, item)): 49 | return True 50 | return False 51 | 52 | -------------------------------------------------------------------------------- /src/python/util.py: -------------------------------------------------------------------------------- 1 | import random 2 | import draw 3 | 4 | colors = [] 5 | 6 | 7 | def rand_color(): 8 | #while True: 9 | while len(colors) < 16: 10 | 11 | col = (random.randint(0, 255), 12 | random.randint(0, 255), 13 | random.randint(0, 255)) 14 | colors.append(col) 15 | 16 | idx = random.randint(0, 15) 17 | return colors[idx] 18 | #if col != GRAY: 19 | # break 20 | return col 21 | -------------------------------------------------------------------------------- /src/random.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | 5 | const u8 rndtable[256] = { 6 | 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , 7 | 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , 8 | 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , 9 | 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , 10 | 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , 11 | 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , 12 | 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , 13 | 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , 14 | 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , 15 | 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , 16 | 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , 17 | 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , 18 | 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , 19 | 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , 20 | 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , 21 | 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , 22 | 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , 23 | 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , 24 | 120, 163, 236, 249 25 | }; 26 | static u8 fx_idx = 0; 27 | static u8 det_idx = 0; 28 | 29 | // randomness for effects 30 | u8 fxrandom() { 31 | return rndtable[fx_idx++]; 32 | } 33 | 34 | // deterministic random 35 | u8 detrandom() { 36 | return rndtable[det_idx++]; 37 | } -------------------------------------------------------------------------------- /src/random.h: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_H 2 | #define RANDOM_H 3 | #include 4 | 5 | u8 fxrandom(); 6 | 7 | u8 detrandom(); 8 | 9 | #endif -------------------------------------------------------------------------------- /src/real_timer.c: -------------------------------------------------------------------------------- 1 | #include "genesis.h" 2 | #include "my_bmp.h" 3 | #include "real_timer.h" 4 | 5 | 6 | 7 | #define BASE_SHIFT 2 8 | 9 | // 0b0000000000000001 16ms 10 | // 0b0000000000000010 32ms 11 | // 0b0000000000000100 64ms 12 | 13 | //u32 frame_timer_masks[4] = { 14 | // ~0b0000000000000111, // sixteenth second 15 | // ~0b0000000000001111, // eighth second 16 | // ~0b0000000000011111, // quarter second 17 | // ~0b0000000000111111, // half second 18 | // ~0b0000000001111111, // second (1024ms) 19 | //}; 20 | 21 | u32 frames_for_counters[5] = { 22 | 0,0,0,0,0 23 | }; 24 | 25 | static u32 ms; 26 | void update_real_timer() { 27 | u32 vints = end_of_frame_vints; 28 | u32 vints_tmp = vints>>BASE_SHIFT; 29 | u32* frame_cnt_ptr = frames_for_counters; 30 | for(int i = 0; i < 5; i++) { 31 | *frame_cnt_ptr++ = vints_tmp; 32 | vints_tmp >>= 1; 33 | } 34 | 35 | u32 dvints = (end_of_frame_vints - prev_end_of_frame_vints); 36 | 37 | if(SYS_isNTSC()) { 38 | ms = dvints << 4; 39 | } else { 40 | u32 four_dvints = dvints <<2; 41 | u32 sixteen_dvints = dvints << 4; 42 | ms = four_dvints + sixteen_dvints; 43 | } 44 | if(ms > 1000) { ms = 1000; } 45 | 46 | } 47 | 48 | u32 ms_since_last_frame() { 49 | return ms; 50 | } 51 | 52 | u32 total_vints() { 53 | return end_of_frame_vints; 54 | } -------------------------------------------------------------------------------- /src/real_timer.h: -------------------------------------------------------------------------------- 1 | #ifndef REAL_TIMER_H 2 | #define REAL_TIMER_H 3 | 4 | typedef enum { 5 | SIXTEENTH_SECOND, 6 | EIGHTH_SECOND, 7 | QUARTER_SECOND, 8 | HALF_SECOND, 9 | SECOND 10 | } frame_timers; 11 | 12 | extern u32 frames_for_counters[5]; 13 | 14 | void update_real_timer(); 15 | u32 ms_since_last_frame(); 16 | u32 total_vints(); 17 | 18 | #endif -------------------------------------------------------------------------------- /src/sector_group.h: -------------------------------------------------------------------------------- 1 | #ifndef SECTOR_GROUP_H 2 | #define SECTOR_GROUP_H 3 | 4 | #include 5 | #include "portal_map.h" 6 | 7 | #define NUM_SECTOR_GROUP_PARAMS 8 8 | #define NUM_SECTOR_GROUP_PARAMS_SHIFT 3 9 | 10 | typedef struct __attribute__((__packed__)) { 11 | s8 light_level; 12 | u8 floor_ceil_color; 13 | } light_and_floor_ceil_color; 14 | 15 | // these params are actual sector group params 16 | 17 | // light level is only 3 bits, using 16 bits :( 18 | // 00000 light_level:3 ceil_col:4 floor_col:4 19 | 20 | #define SECTOR_GROUP_PARAM_LIGHT_FLOOR_CEIL_COLOR_IDX 0 21 | #define SECTOR_GROUP_PARAM_ORIG_HEIGHT_IDX 1 22 | // state is probably only 8 bits, could possibly be more than that eventually 23 | #define SECTOR_GROUP_PARAM_STATE_IDX 2 24 | #define SECTOR_GROUP_PARAM_TICKS_LEFT_IDX 3 25 | #define SECTOR_GROUP_PARAM_FLOOR_HEIGHT_IDX 4 26 | #define SECTOR_GROUP_PARAM_CEIL_HEIGHT_IDX 5 27 | #define SECTOR_GROUP_PARAM_SCRATCH_ONE_IDX 6 28 | #define SECTOR_GROUP_PARAM_SCRATCH_TWO_IDX 7 29 | 30 | #define NO_TYPE 0 31 | #define FLASHING 1 32 | #define DOOR 2 33 | #define LIFT 3 34 | #define STAIRS 4 35 | #define LOWERING_STAIRS 5 36 | 37 | #define NUM_SECTOR_GROUP_TYPES 6 38 | 39 | #define NO_KEYCARD 0b00000000 40 | #define BLUE_KEYCARD 0b00100000 41 | #define GREEN_KEYCARD 0b01000000 42 | #define RED_KEYCARD 0b01100000 43 | 44 | // use the top three bits for keycard mask 45 | // we can have up to 32 sector types? 46 | #define SECTOR_GROUP_KEYCARD_MASK 0b11100000 47 | #define SECTOR_GROUP_TYPE_MASK 0b00011111 48 | 49 | #define GET_SECTOR_GROUP_TYPE(typ_byte) ((typ_byte) & SECTOR_GROUP_TYPE_MASK) 50 | #define GET_SECTOR_GROUP_KEYCARD(typ_byte) ((typ_byte) & SECTOR_GROUP_KEYCARD_MASK) 51 | 52 | typedef enum { 53 | CLOSED, 54 | GOING_UP, 55 | OPEN, 56 | GOING_DOWN 57 | } door_lift_state; 58 | 59 | typedef enum { 60 | STAIRS_LOWERED = 0, 61 | STAIRS_MOVING = 1, 62 | STAIRS_RAISED = 2 63 | } stair_state; 64 | 65 | extern s16* live_sector_group_parameters; 66 | //extern sector_param* live_sector_parameters; 67 | 68 | 69 | s16* get_sector_group_pointer(u16 sect_group); 70 | 71 | s16 get_sector_group_orig_height(u16 sect_idx); 72 | s8 get_sector_group_light_level(u16 sect_idx); 73 | u16 get_sector_group_ticks_left(u16 sect_idx); 74 | u16 get_sector_group_state(u16 sect_idx); 75 | void set_sector_group_state(u16 sect_group, u16 state); 76 | 77 | s16 get_sector_group_floor_height(u16 sect_idx); 78 | void set_sector_group_floor_height(u16 sector_idx, s16 height); 79 | 80 | s16 get_sector_group_ceil_height(u16 sector_idx); 81 | void set_sector_group_ceil_height(u16 sector_idx, s16 height); 82 | u16 get_sector_group_floor_color(u16 sector_idx); 83 | u16 get_sector_group_ceil_color(u16 sector_idx); 84 | 85 | void run_sector_group_processes(); 86 | 87 | typedef enum { 88 | NO_TRIGGER=0, 89 | SET_SECTOR_DARK=1, 90 | SET_SECTOR_LIGHT=2, 91 | START_STAIRS=3, 92 | LEVEL_END=4, 93 | } trigger_type; 94 | 95 | u16 get_sector_group_trigger_type(u16 sect_group); 96 | s16 get_sector_group_trigger_target(u16 sect_group, u8 tgt_idx); 97 | 98 | 99 | trigger_type activate_sector_group_enter_trigger(u16 sect_group); 100 | 101 | #endif -------------------------------------------------------------------------------- /src/sfx.c: -------------------------------------------------------------------------------- 1 | #include "level.h" 2 | #include "sfx.h" 3 | 4 | int sfx_on = TRUE; 5 | 6 | #define NUM_SFX_CHANNELS 3 7 | 8 | u8 pcm_id[NUM_SFX_CHANNELS] = {0,0,0}; 9 | u8 channels_in_use[NUM_SFX_CHANNELS] = {0,0,0}; // 0 means not in use, 1 means in use priority 0, 2 means 1, etc 10 | u8 sfx_channels[NUM_SFX_CHANNELS] = { SOUND_PCM_CH2, SOUND_PCM_CH3, SOUND_PCM_CH4 }; 11 | void toggle_sfx(int menu_idx) { 12 | sfx_on = ! sfx_on; 13 | } 14 | 15 | void play_sfx(u8 id, u8 priority) { 16 | if(!sfx_on) { return; } 17 | //KLog_U2("playing sfx: ", id, " with priority: ", priority); 18 | 19 | // check for a free channel first 20 | for(int i = 0; i < NUM_SFX_CHANNELS; i++) { 21 | u8 ch_used = channels_in_use[i]; 22 | s8 old_priority = ch_used-1; 23 | s8 pcm_equal = pcm_id[i] - id; 24 | s8 prio_diff = priority - (ch_used-1); // if 0, that means it's actually higher priority 25 | 26 | if(ch_used == 0) { 27 | //if(priority >= channels_in_use[i] && pcm_id[i] != id) { // sadly, we need to check this to make sure a sfx gets played if possible 28 | //KLog_U1("playing on unused channel: ", sfx_channels[i]); 29 | XGM_startPlayPCM(id, priority, sfx_channels[i]); 30 | //KLog_S2("playing id: ", id, " on channel: ", i+1); 31 | channels_in_use[i] = priority+1; 32 | //KLog_S3("ch used: ", ch_used, " prev priority: ", old_priority, " cur priority: ", priority); 33 | pcm_id[i] = id; 34 | return; 35 | } 36 | } 37 | // if no free channels, check for a lower priority channel 38 | for(int i = 0; i < NUM_SFX_CHANNELS; i++) { 39 | u8 ch_used = channels_in_use[i]; 40 | s8 old_priority = ch_used-1; 41 | s8 pcm_equal = pcm_id[i] - id; 42 | s8 prio_diff = priority - (ch_used-1); // if 0, that means it's actually higher priority 43 | 44 | if((priority > old_priority) && (pcm_equal != 0)) { 45 | //if(priority >= channels_in_use[i] && pcm_id[i] != id) { // sadly, we need to check this to make sure a sfx gets played if possible 46 | //KLog_U1("playing on lower priority channel: ", sfx_channels[i]); 47 | XGM_startPlayPCM(id, priority, sfx_channels[i]); 48 | //KLog_S2("playing id: ", id, " on channel: ", i+1); 49 | channels_in_use[i] = priority+1; 50 | //KLog_S3("ch used: ", ch_used, " prev priority: ", old_priority, " cur priority: ", priority); 51 | pcm_id[i] = id; 52 | return; 53 | } 54 | } 55 | // dropped sfx sadly 56 | KLog_U1("dropped sfx :( %i", id); 57 | } 58 | 59 | u8 sound_hearable(u16 sound_src_sector_group, u16 dst_sector) { 60 | portal_map* map = (portal_map*)cur_portal_map; 61 | 62 | 63 | // if src_sector is reachable from the set of rendered sectors via up to one max sector between, play it 64 | for(int sect = 0; sect < cur_portal_map->num_sectors; sect++) { 65 | if(sector_group(sect, map) != sound_src_sector_group) { 66 | continue; 67 | } 68 | if(!sector_in_phs(dst_sector, sect, map)) { 69 | continue; 70 | } 71 | return 1; 72 | } 73 | return 0; 74 | } 75 | 76 | void propagate_sfx_from_sect_group(u8 id, u8 priority, u16 src_sector_group, u16 dst_sector) { 77 | if(sound_hearable(src_sector_group, dst_sector)) { 78 | play_sfx(id, priority); 79 | } 80 | } 81 | 82 | void update_sfx() { 83 | u8 playing_status = (XGM_isPlayingPCM(SOUND_PCM_CH2_MSK | SOUND_PCM_CH3_MSK | SOUND_PCM_CH4_MSK))>>1; 84 | //KLog_U3("bitmap ", playing_status&1, "", playing_status&2, "", playing_status&4); 85 | switch(playing_status) { 86 | case 0b000: 87 | channels_in_use[0] = 0; 88 | channels_in_use[1] = 0; 89 | channels_in_use[2] = 0; 90 | break; 91 | case 0b001: 92 | channels_in_use[1] = 0; 93 | channels_in_use[2] = 0; 94 | break; 95 | case 0b010: 96 | channels_in_use[0] = 0; 97 | channels_in_use[2] = 0; 98 | break; 99 | case 0b011: 100 | channels_in_use[2] = 0; 101 | break; 102 | case 0b100: 103 | channels_in_use[0] = 0; 104 | channels_in_use[1] = 0; 105 | break; 106 | case 0b101: 107 | channels_in_use[1] = 0; 108 | break; 109 | case 0b110: 110 | channels_in_use[0] = 0; 111 | break; 112 | case 0b111: 113 | break; 114 | } 115 | } -------------------------------------------------------------------------------- /src/sfx.h: -------------------------------------------------------------------------------- 1 | #ifndef SFX_H 2 | #define SFX_H 3 | 4 | #include 5 | 6 | extern int sfx_on; 7 | void toggle_sfx(); 8 | void update_sfx(); 9 | void play_sfx(u8 id, u8 priority); 10 | void propagate_sfx_from_sect_group(u8 id, u8 priority, u16 src_sector_group, u16 dst_sector); 11 | 12 | #define SFX_OPEN_DOOR_ID 1 13 | #define SFX_CLOSE_DOOR_ID 2 14 | #define SFX_LIFT_GO_UP_ID 3 15 | #define SFX_JUMP1_ID 4 16 | #define SFX_JUMP2_ID 5 17 | #define SFX_SHOTGUN_ID 6 18 | #define SFX_SELECT_ID 7 19 | #define SFX_ENEMY_A_WAKE_ID 8 20 | 21 | 22 | #endif -------------------------------------------------------------------------------- /src/sprite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehaliewicz/Manifold/b6fe52a52344464de00bde4dc58a40f8b89cfc1a/src/sprite -------------------------------------------------------------------------------- /src/tex_tables_lookup.h: -------------------------------------------------------------------------------- 1 | //extern const void const* jump_table_lut[513]; 2 | extern void end_tables(void); 3 | extern const u8* const skip_table_lut[513]; 4 | extern const void* const jump_table_top_clip_lut[513]; 5 | extern const void* const jump_table_bot_clip_lut[513]; 6 | -------------------------------------------------------------------------------- /src/texture.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "colors.h" 3 | #include "config.h" 4 | #include "div_lut.h" 5 | #include "math3d.h" 6 | #include "texture.h" 7 | #include "textures.h" 8 | #include "tex_tables_lookup.h" 9 | #include "utils.h" 10 | 11 | 12 | // 64 pixels tall 13 | // 64 pixels wide 14 | // which means 64 rasterized columns 15 | 16 | #define Z_PERSP_MIN (100<<4) 17 | 18 | persp_params calc_perspective(u16 z1_12_4, u16 z2_12_4, u16 inv_z1, u16 inv_z2, u32 left_u_16, u32 right_u_16, u16 dx) { 19 | persp_params ret; 20 | //if (abs(z2_12_4 - z1_12_4) < Z_PERSP_MIN) { 21 | // u32 du_16 = right_u_16-left_u_16; 22 | // u32 du_dx_16 = du_16 / dx; 23 | // ret.left_u_16 = left_u_16; 24 | // ret.du_dx_16 = du_dx_16; 25 | // ret.needs_perspective = 0; 26 | // return ret; 27 | //} 28 | u32 one_over_z_26 = z_12_4_to_one_over_z_26[z1_12_4>>4]; 29 | u32 one_over_z_end_26 = z_12_4_to_one_over_z_26[z2_12_4>>4]; 30 | 31 | 32 | s32 d_one_over_z_26 = (one_over_z_end_26 - one_over_z_26); 33 | 34 | //u32 u_over_z_23 = (left_u_16<<(TRANS_Z_FRAC_BITS+7))/z1_12_4; 35 | //u32 u_over_z_end_23 = (right_u_16<<(TRANS_Z_FRAC_BITS+7))/z2_12_4; 36 | 37 | //u16 inv_z1_0_16 = 65536/(z1_12_4>>4); 38 | //u16 inv_z2_0_16 = 65536/(z2_12_4>>4); 39 | //u16 inv_z1_0_16 = z_recip_table_16[z1_12_4>>4]; 40 | //u16 inv_z2_0_16 = z_recip_table_16[z2_12_4>>4]; 41 | u32 u_over_z_23 = ((left_u_16)*(inv_z1))>>9; // 11+12 42 | u32 u_over_z_end_23 = ((right_u_16)*(inv_z2))>>9; 43 | 44 | 45 | 46 | 47 | //u32 u_over_z_end = (right_u_16<<8)/z2_12_4; 48 | //u32 u_over_z_23 = u_over_z<<19; 49 | //u32 u_over_z_end_23 = u_over_z_end<<19; 50 | 51 | s32 d_u_over_z_23 = (u_over_z_end_23 - u_over_z_23); 52 | 53 | 54 | ret.one_over_z_26 = one_over_z_26; 55 | 56 | 57 | ret.d_one_over_z_dx_26 = (d_one_over_z_26/dx); 58 | 59 | ret.u_over_z_23 = u_over_z_23; 60 | ret.d_u_over_z_dx_23 = d_u_over_z_23/dx; 61 | return ret; 62 | } 63 | 64 | typedef u8* (*top_clip_wall_fill_func)(u8* col_ptr, u16* tex_column, u16 clip_top); 65 | typedef u8* (*bot_clip_wall_fill_func)(u8* col_ptr, u16* tex_column, u16 clip_top, u16 clip_bot); 66 | 67 | 68 | void draw_texture_vertical_line(s16 unclipped_y0, u16 y0, s16 unclipped_y1, u8* col_ptr, const u16* tex_column) { 69 | u16 unclipped_dy = unclipped_y1; 70 | __asm volatile( 71 | "sub.w %1, %0" 72 | : "+d" (unclipped_dy) 73 | : "d" (unclipped_y0) 74 | ); 75 | 76 | if(unclipped_dy > 512) { return; } 77 | __asm volatile( 78 | "add.l %1, %0\t\n\ 79 | add.l %1, %0" 80 | : "+a" (col_ptr) 81 | : "d" (y0) 82 | ); 83 | 84 | u16 clip_top = y0;//y0-unclipped_y0; 85 | __asm volatile( 86 | "sub.w %1, %0" 87 | : "+d" (clip_top) 88 | : "d" (unclipped_y0) 89 | ); 90 | u16 f;// = unclipped_dy; 91 | 92 | register const u16* a0 asm ("%a0") = tex_column; 93 | register const u8* a1 asm ("%a1") = col_ptr; 94 | 95 | register const u32 d0 asm ("%d0") = clip_top; 96 | 97 | __asm volatile( 98 | "lsl.l #2, %6\t\n\ 99 | move.l 0(%5, %6.w), %1\t\n\ 100 | jsr (%1)" 101 | : "+a" (col_ptr) 102 | : "a" (f), "a" (a0), "a" (a1), "d" (d0), "a" (jump_table_top_clip_lut), "d" (unclipped_dy) 103 | ); 104 | 105 | } 106 | 107 | 108 | 109 | void draw_bottom_clipped_texture_vertical_line(s16 unclipped_y0, u16 y0, s16 unclipped_y1, u16 y1, u8* col_ptr, const u16* tex_column) { 110 | 111 | u16 unclipped_dy = unclipped_y1; 112 | __asm volatile( 113 | "sub.w %1, %0" 114 | : "+d" (unclipped_dy) 115 | : "d" (unclipped_y0) 116 | ); 117 | 118 | if(unclipped_dy > 512) { return; } 119 | s16 clipped_dy = y1-y0; 120 | if(clipped_dy <= 0) { return; } 121 | __asm volatile( 122 | "add.l %1, %0\t\n\ 123 | add.l %1, %0" 124 | : "+a" (col_ptr) 125 | : "d" (y0) 126 | ); 127 | 128 | u16 clip_top = y0; 129 | u16 clip_bot = unclipped_y1; 130 | __asm volatile( 131 | "sub.w %2, %0\t\n\ 132 | sub.w %3, %1\t\n\ 133 | " 134 | : "+d" (clip_top), "+d" (clip_bot) 135 | : "d" (unclipped_y0), "d" (y1) 136 | ); 137 | u16 f; 138 | register const u32 d0 asm ("%d0") = clip_top; 139 | register const u32 d1 asm ("%d1") = clip_bot; 140 | register const u16* a0 asm ("%a0") = tex_column; 141 | register const u8* a1 asm ("%a1") = col_ptr; 142 | 143 | 144 | __asm volatile( 145 | "lsl.l #2, %7\t\n\ 146 | move.l 0(%6, %7.w), %1\t\n\ 147 | jsr (%1)\t\n\ 148 | " 149 | : "+a" (col_ptr) 150 | : "a" (f), "a" (a0), "a" (a1), "d" (d0), "d" (d1), "a" (jump_table_bot_clip_lut), "d" (unclipped_dy) 151 | ); 152 | 153 | } 154 | 155 | -------------------------------------------------------------------------------- /src/texture.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURE_H 2 | #define TEXTURE_H 3 | 4 | #include 5 | 6 | //#define TEX_HEIGHT 128 7 | //#define TEX_HEIGHT_SHIFT 7 8 | 9 | //#define TEX_WIDTH 64 10 | //#define TEX_WIDTH_SHIFT 6 11 | #define TEX_WIDTH 32 12 | #define TEX_WIDTH_SHIFT 5 13 | 14 | #define TEX_HEIGHT 64 15 | #define TEX_HEIGHT_SHIFT 6 16 | //#define TEX_WIDTH 32 17 | //#define TEX_WIDTH_SHIFT 5 18 | 19 | extern u16* dark_texture; 20 | extern u16* mid_texture; 21 | extern u16* light_texture; 22 | void tick_texture(); 23 | 24 | 25 | typedef struct __attribute__((__packed__)) { 26 | const uint16_t* const dark; 27 | const uint16_t* const mid; 28 | const uint16_t* const light; 29 | } lit_texture; 30 | 31 | typedef struct __attribute__((__packed__)) { 32 | uint8_t num_frames; 33 | uint8_t frequency; 34 | uint8_t frame_0_idx; 35 | } anim_texture; 36 | 37 | typedef struct { 38 | u32 left_u; // 16.16 39 | s16 left_z; 40 | u32 right_u; // 16.16 41 | s16 right_z; 42 | s32 du_over_dz; 43 | u8 repetitions; 44 | lit_texture* tex; 45 | u8 needs_texture; 46 | } texmap_params; 47 | 48 | typedef struct { 49 | u32 one_over_z_26; 50 | s32 d_one_over_z_dx_26; 51 | 52 | u32 u_over_z_23; 53 | u32 d_u_over_z_dx_23; 54 | } persp_params; 55 | 56 | persp_params calc_perspective(u16 z1_12_4, u16 z2_12_4, u16 inv_z1, u16 inv_z2, u32 left_u_16, u32 right_u_16, u16 dx); 57 | 58 | #define FAR_MIP_WIDTH 16 59 | #define FAR_MIP_WIDTH_SHIFT 4 60 | #define FAR_MIP_HEIGHT 64 61 | 62 | #define MID_MIP_WIDTH 32 63 | #define MID_MIP_WIDTH_SHIFT 5 64 | #define MID_MIP_HEIGHT 64 65 | 66 | #define NEAR_MIP_WIDTH 64 67 | #define NEAR_MIP_WIDTH_SHIFT 6 68 | #define NEAR_MIP_HEIGHT 64 69 | 70 | // three mipmap levels 71 | 72 | 73 | //typedef struct { 74 | // lit_texture mip_far; 75 | // lit_texture mip_mid; 76 | // lit_texture mip_near; 77 | //} texture_set; 78 | 79 | 80 | void draw_texture_vertical_line(s16 unclipped_y0, u16 y0, s16 unclipped_y1, u8* col_ptr, const u16* tex_column); 81 | 82 | void draw_bottom_clipped_texture_vertical_line(s16 unclipped_y0, u16 y0, s16 unclipped_y1, u16 y1, u8* col_ptr, const u16* tex_column); 83 | 84 | #endif -------------------------------------------------------------------------------- /src/textures.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURES_H 2 | #define TEXTURES_H 3 | 4 | #include 5 | #include "texture.h" 6 | 7 | //extern const lit_texture wall_texture; 8 | //extern const lit_texture sci_fi_wall_texture; 9 | extern const lit_texture door_mid; 10 | 11 | #define NUM_TEXTURES 5 12 | 13 | //extern lit_texture* const textures[8*5]; 14 | 15 | //const u16 raw_key_mid[2048]; 16 | //const u16 raw_key_32_32_mid[2048]; 17 | 18 | lit_texture* get_texture(u8 tex_idx, s8 light_level); 19 | 20 | #endif -------------------------------------------------------------------------------- /src/tile.h: -------------------------------------------------------------------------------- 1 | #ifndef TILE_H 2 | #define TILE_H 3 | 4 | 5 | typedef union { 6 | u8 bytes[8][4]; 7 | u32 rows[8]; 8 | } tile; 9 | 10 | typedef struct { 11 | u8 bytes[4*8]; // 4 columns 8 rows tall each 12 | } two_pix_col_tile; 13 | 14 | #endif -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "utils.h" 4 | 5 | 6 | #define SUB_16_16(a, b) do { \ 7 | __asm volatile( \ 8 | "sub.w %1, %0" \ 9 | : "+d" (a) \ 10 | : "d" (b) \ 11 | ); \ 12 | } while(0); 13 | 14 | 15 | u16 sub_16_16(u16 a, u16 b) { 16 | __asm volatile( 17 | "sub.w %1, %0" 18 | : "+d" (a) 19 | : "d" (b) 20 | ); 21 | return a; 22 | } 23 | 24 | u32 fastLength(s32 dx, s32 dy) { 25 | u32 adx = abs(dx); 26 | u32 ady = abs(dy); 27 | return (adx > ady) ? (adx + (ady >> 1)) : (ady + (adx >> 1)); 28 | } 29 | 30 | 31 | void die(char* msg) { 32 | while(1) { 33 | VDP_drawTextBG(BG_B, msg, 2, 12); 34 | VDP_waitVInt(); 35 | } 36 | } 37 | 38 | 39 | void assert(int expr, char* msg) { 40 | if(!expr) { 41 | die(msg); 42 | } 43 | } 44 | 45 | static char buf[80]; 46 | void* malloc(u16 size, const char* thing) { 47 | sprintf(buf, "allocating %i bytes for %s", size, thing); 48 | KLog(buf); 49 | void* p = MEM_alloc(size); 50 | KLog_U1("allocated pointer: ", (u32)p); 51 | return p; 52 | } 53 | 54 | void free(void* thing, const char* thing2) { 55 | sprintf(buf, "freeing %s", thing2); 56 | KLog(buf); 57 | MEM_free(thing); 58 | } -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | #include 4 | 5 | 6 | #define SCREEN_WIDTH BMP_PITCH 7 | #define SCREEN_HEIGHT 144 // BMP_HEIGHT // H 8 | 9 | //s16 divs_32_by_16(s32 num, s16 denom); 10 | 11 | //u16 divu_32_by_16(u32 num, u16 denom); 12 | //u32 mulu_16_by_16(u16 a, u16 b); 13 | 14 | //s32 muls_16_by_16(u16 a, u16 b); 15 | u16 sub_16_16(u16 a, u16 b); 16 | 17 | 18 | #define FETCH_INC_BYTE(var, ptr) do {\ 19 | __asm volatile( \ 20 | "move.b (%1)+, %0" \ 21 | : "=d" (var), "+a" (ptr) \ 22 | ); \ 23 | } while(0); 24 | 25 | #define FETCH_INC_WORD(var, ptr) do {\ 26 | __asm volatile( \ 27 | "move.w (%1)+, %0" \ 28 | : "=d" (var), "+a" (ptr) \ 29 | ); \ 30 | } while(0); 31 | 32 | #define WRITE_WORD_INC(var, ptr) do {\ 33 | __asm volatile( \ 34 | "move.w %1, (%0)+" \ 35 | : "+a" (ptr) \ 36 | : "d" (var) \ 37 | ); \ 38 | } while(0); 39 | 40 | #define WRITE_BYTE_INC(var, ptr) do {\ 41 | __asm volatile( \ 42 | "move.b %1, (%0)+" \ 43 | : "+a" (ptr) \ 44 | : "d" (var) \ 45 | ); \ 46 | } while(0); 47 | 48 | #define COPY_BYTE_POSTINC_SRC_QINC_4_DST(src, dst) do { \ 49 | __asm volatile( \ 50 | "move.b (%0)+, (%1)\t\naddq.l #4, %1" \ 51 | : "+a" (src), "+a" (dst) \ 52 | : \ 53 | ); \ 54 | } while(0); 55 | 56 | #define WRITE_BYTE_QINC_4(var, ptr) do { \ 57 | __asm volatile( \ 58 | "move.b %1, (%0)\t\naddq.l #4, %0" \ 59 | : "+a" (ptr) \ 60 | : "d" (var) \ 61 | ); \ 62 | } while(0); 63 | 64 | #define WRITE_BYTE(var, ptr) do {\ 65 | __asm volatile( \ 66 | "move.b %1, (%0)" \ 67 | : "+a" (ptr) \ 68 | : "d" (var) \ 69 | ); \ 70 | } while(0); 71 | 72 | inline u16 divu_32_by_16(u32 num, u16 denom) { 73 | __asm volatile( 74 | "divu.w %1, %0" 75 | : "+d" (num) // output 76 | : "d" (denom) 77 | ); 78 | 79 | s16 res = num; 80 | return res; 81 | } 82 | 83 | 84 | inline u32 mulu_16_by_16(u16 a, u16 b) { 85 | u32 a32 = a; 86 | __asm volatile( 87 | "mulu.w %1, %0" 88 | : "+d" (a32) // output 89 | : "d" (b) 90 | ); 91 | return a32; 92 | } 93 | 94 | inline s16 divs_32_by_16(s32 num, s16 denom) { 95 | __asm volatile( 96 | "divs.w %1, %0" 97 | : "+d" (num) // output 98 | : "d" (denom) 99 | ); 100 | 101 | s16 res = num; 102 | return res; 103 | } 104 | 105 | 106 | inline s32 muls_16_by_16(s16 a, s16 b) { 107 | s32 a32 = a; 108 | __asm volatile( 109 | "muls.w %1, %0" 110 | : "+d" (a32) // output 111 | : "d" (b) 112 | ); 113 | return a32; 114 | } 115 | 116 | 117 | void die(char* s); 118 | 119 | u32 fastLength(s32 dx, s32 dy); 120 | 121 | 122 | 123 | inline u16 fastLength16(s16 dx, s16 dy) { 124 | u16 adx = abs(dx); 125 | u16 ady = abs(dy); 126 | return (adx > ady) ? (adx + (ady >> 1)) : (ady + (adx >> 1)); 127 | /* 128 | 129 | __asm volatile( 130 | "tst.w %0\t\n\ 131 | bpl.b to_there%=\t\n\ 132 | neg.w %0\t\n\ 133 | to_there%=:\n" 134 | : "+d" (dx) : : "cc" 135 | ); 136 | __asm volatile( 137 | "tst.w %0\t\n\ 138 | bpl.b to_there%=\t\n\ 139 | neg.w %0\t\n\ 140 | to_there%=:\n" 141 | : "+d" (dy) : : "cc" 142 | ); 143 | return (dx > dy) ? (dx + (dy >> 1)) : (dy + (dx >> 1)); 144 | */ 145 | } 146 | 147 | void die(char* msg); 148 | void assert(int expr, char* msg); 149 | 150 | #define clamp(a, mi,ma) min(max(a,mi),ma) 151 | 152 | void* malloc(u16 size, const char* thing); 153 | void free(void* thing, const char* thing2); 154 | 155 | #endif -------------------------------------------------------------------------------- /src/vertex.h: -------------------------------------------------------------------------------- 1 | #ifndef VERTEX_H 2 | #define VERTEX_H 3 | 4 | #include 5 | 6 | typedef struct __attribute__((__packed__)) { 7 | s16 x, y; 8 | } vertex; 9 | 10 | 11 | typedef struct __attribute__((__packed__)) { 12 | fix32 x, y; 13 | } vertex_f32; 14 | 15 | #endif -------------------------------------------------------------------------------- /src/vwf.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "vwf.h" 3 | 4 | #define DIVIDE_ROUND_UP(a,b) (((a) + ((b)-1))/(b)) 5 | 6 | 7 | 8 | void vwf_init() { 9 | } 10 | 11 | void vwf_cleanup() { 12 | } 13 | 14 | int vwf_count_tiles(char* string, int len) { 15 | int num_pairs = 0; 16 | for(int i = 0; i < len; i++) { 17 | char c = string[i]; 18 | int cur_num_pairs = charmap[c-32].width; 19 | if(cur_num_pairs == 0 || cur_num_pairs > PAIRS_IN_TILE) { 20 | 21 | char buf[32]; sprintf(buf, "wtf %i/%c %i", c, c, cur_num_pairs); 22 | KLog(buf); 23 | die(buf); } // die("wtf"); } 24 | 25 | 26 | num_pairs += cur_num_pairs; 27 | } 28 | int int_tiles = num_pairs / PAIRS_IN_TILE; 29 | if ((int_tiles * PAIRS_IN_TILE) != num_pairs) { 30 | int_tiles++; 31 | } 32 | return int_tiles; 33 | 34 | //return DIVIDE_ROUND_UP(num_pairs, PAIRS_IN_TILE); 35 | } 36 | 37 | 38 | u8 dummy_col[9] = { 39 | 0b0000000, 40 | 0b0000000, 41 | 0b0000000, 42 | 0b0000000, 43 | 0b0000000, 44 | 0b0000000, 45 | 0b0000000, 46 | 0b0000000, 47 | 0b0000000, 48 | }; 49 | 50 | const u8 multicolor_shadow_lut[256] = { 51 | 0,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,224, 52 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 53 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 54 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 55 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 56 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 57 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 58 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 59 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 60 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 61 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 62 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 63 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 64 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 65 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,224, 66 | 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, 67 | }; 68 | 69 | // render a string into 8x8 tiles, return number of characters processed 70 | static int cnt; 71 | int vwf_render_tiles(char* string, int len, tile* tiles, int num_tiles) { 72 | cnt++; 73 | if(cnt > 3) { cnt = 0; } 74 | 75 | 76 | int tile_num = 0; 77 | int pos_in_tile = 0; 78 | 79 | tile* cur_tile = tiles; 80 | 81 | int i; 82 | for(i = 0; i < len; i++) { 83 | char c = string[i]; 84 | const char_entry* centr = &(charmap[c-32]); 85 | 86 | int char_width = centr->width; 87 | two_pix_col_tile til = centr->bitmap; 88 | 89 | if(tile_num == num_tiles-1 && pos_in_tile + char_width > PAIRS_IN_TILE) { 90 | // can't render this, return number of characters rendered 91 | break; 92 | } 93 | 94 | 95 | // render 1 byte, 2 pixel pair at once 96 | 97 | //this is with y then x ordering 98 | u8* output_ptr = &cur_tile->bytes[0][pos_in_tile]; 99 | u8* input_ptr = til.bytes;//[0][0]; 100 | u16 pix_mask = 0xFF;//0b00010001; 101 | 102 | 103 | u8* above_left_ptr = &dummy_col[0]; 104 | 105 | for(int pair_x = 0; pair_x < char_width; pair_x++) { 106 | u8* col_output_ptr = output_ptr; 107 | u8 above_byte = 0; 108 | u8* col_input_ptr = input_ptr; 109 | u8 filled_pixel_mask; u8 cur_byte; 110 | 111 | // fill in top two pix 112 | FETCH_INC_BYTE(cur_byte, col_input_ptr); 113 | WRITE_BYTE_QINC_4(cur_byte, col_output_ptr); 114 | above_byte = cur_byte; 115 | 116 | 117 | for(int y = 1; y < 8; y++) { 118 | 119 | // clear only pixels with font 120 | 121 | // shadow from above 122 | // the ordering of this is not good. 123 | 124 | 125 | // shadow for single color font 126 | u8 above_left_byte; //*above_left_ptr++; 127 | FETCH_INC_BYTE(above_left_byte, above_left_ptr); 128 | above_left_byte <<= 4; 129 | u8 shadow_pix = above_byte; //(above_byte >> 4) | (above_left_byte << 4); 130 | shadow_pix >>= 4; 131 | shadow_pix |= above_left_byte; 132 | 133 | //shadow_pix = multicolor_shadow_lut[shadow_pix]; 134 | __asm volatile( 135 | "\t\n\ 136 | and.w %2, %0\t\n\ 137 | move.b (%1, %0.w), %0\t\n\ 138 | " 139 | : "+d" (shadow_pix) 140 | : "a" (multicolor_shadow_lut), "d" (pix_mask) 141 | ); 142 | 143 | FETCH_INC_BYTE(cur_byte, col_input_ptr); 144 | 145 | shadow_pix |= cur_byte; 146 | WRITE_BYTE_QINC_4(shadow_pix, col_output_ptr); 147 | 148 | above_byte = cur_byte; 149 | 150 | 151 | } 152 | 153 | output_ptr++; 154 | above_left_ptr = input_ptr; 155 | input_ptr += 8; 156 | 157 | pos_in_tile += 1; 158 | 159 | if (pos_in_tile == PAIRS_IN_TILE) { 160 | tile_num += 1; 161 | cur_tile++; 162 | pos_in_tile = 0; 163 | output_ptr = (u8*)cur_tile; //&cur_tile->bytes[0][0]; 164 | } 165 | } 166 | 167 | 168 | } 169 | 170 | return i; 171 | } 172 | 173 | int vwf_render_to_separate_tiles(char* string, int len, tile* tiles, int num_tiles) { 174 | 175 | // NOTE: this no longer works as ive changed the text to be column based! 176 | int tile_num = 0; 177 | int pos_in_tile = 0; 178 | 179 | tile* cur_tile = tiles; 180 | 181 | for(int i = 0; i < len; i++) { 182 | char c = string[i]; 183 | const char_entry* centr = &(charmap[c-32]); 184 | 185 | int char_width = centr->width; 186 | two_pix_col_tile til = centr->bitmap; 187 | 188 | if(tile_num == num_tiles-1 && pos_in_tile + char_width > PAIRS_IN_TILE) { 189 | // can't render this, return number of characters rendered 190 | return i; 191 | } 192 | 193 | memcpy(cur_tile->bytes, &til.bytes, sizeof(tile)); 194 | tile_num += 1; 195 | cur_tile++; 196 | 197 | } 198 | 199 | return len; 200 | } 201 | -------------------------------------------------------------------------------- /src/vwf.h: -------------------------------------------------------------------------------- 1 | #ifndef FONT_H 2 | #define FONT_H 3 | 4 | #include 5 | #include "tile.h" 6 | 7 | #define NUM_CHARS 96 8 | #define PAIRS_IN_TILE 4 9 | 10 | 11 | typedef struct { 12 | char chr; 13 | int width; // width in 2-pixel pairs (bytes), always a minimum of two, so we can write words at once 14 | two_pix_col_tile bitmap; 15 | } char_entry; 16 | 17 | extern const char_entry charmap[96]; 18 | 19 | void vwf_init(); 20 | int vwf_render_tiles(char* string, int len, tile* tiles, int num_tiles); 21 | int vwf_render_to_separate_tiles(char* string, int len, tile* tiles, int num_tiles); 22 | int vwf_count_tiles(char* string, int len); 23 | void vwf_cleanup(); 24 | 25 | #endif -------------------------------------------------------------------------------- /src/weapon_sprites.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "weapon_sprites.h" 4 | #include "sprites_res.h" 5 | #include "utils.h" 6 | 7 | #define MAX_SPRITE_TILES 97 8 | #define MAX_SPRITES 16 9 | 10 | static u32 start_tile_loc; 11 | static u16 cur_num_sprites; 12 | static u16 sprite_idx_start; 13 | 14 | // frame index 15 | // time til next frame 16 | 17 | typedef enum { 18 | SHOTGUN_SPR=0, 19 | SHOTGUN_RELOAD_SPR=1, 20 | } weapon_sprite; 21 | 22 | typedef struct { 23 | u8 spr; 24 | s16 time; 25 | u16 next_anim; 26 | u8 anim_idx; u8 frame_idx; 27 | } animation_entry; 28 | 29 | 30 | 31 | 32 | animation_entry anim_table[] = { 33 | { 34 | .spr = SHOTGUN_SPR, 35 | .time = -1, 36 | .next_anim = 0, 37 | }, 38 | { 39 | .spr = SHOTGUN_RELOAD_SPR, 40 | .time = 3, // 2 frames 41 | .next_anim = 2, 42 | .anim_idx = 0, .frame_idx = 0, 43 | }, 44 | { 45 | .spr = SHOTGUN_RELOAD_SPR, 46 | .time = 3, // 2 frames 47 | .next_anim = SHOTGUN_IDLE_ANIM, // back to idle 48 | .anim_idx = 0, .frame_idx = 1, 49 | } 50 | }; 51 | 52 | static s16 cur_frame_timer; 53 | static u16 cur_anim_idx; 54 | SpriteDefinition *cur_sprite_def; 55 | 56 | typedef enum { 57 | DONE=0, 58 | TILES_LOADING = 1, 59 | SPRITES_LOADING = 2, 60 | } update_state; 61 | 62 | u8 transfer_pending = 0; 63 | u8 pre_queue_size = 0; 64 | 65 | 66 | void update_weapon_sprites(TransferMethod transfer_type); 67 | 68 | void step_weapon_animation(TransferMethod transfer_type) { 69 | if(cur_frame_timer == -1) { 70 | if(transfer_pending != DONE) { 71 | if(DMA_getQueueSize() == pre_queue_size) { 72 | transfer_pending = DONE; 73 | update_weapon_sprites(transfer_type); 74 | } 75 | } 76 | return; 77 | } 78 | if(cur_frame_timer-- <= 0 && transfer_pending == DONE) { 79 | // move to next frame 80 | //set_weapon_anim 81 | u16 next_anim = anim_table[cur_anim_idx].next_anim; 82 | KLog_U1("going to anim index: ", next_anim); 83 | 84 | pre_queue_size = DMA_getQueueSize(); 85 | set_weapon_anim(next_anim, transfer_type); 86 | transfer_pending = TILES_LOADING; 87 | } 88 | if(transfer_pending != DONE) { 89 | if(DMA_getQueueSize() == pre_queue_size) { 90 | transfer_pending = DONE; 91 | update_weapon_sprites(transfer_type); 92 | } 93 | } 94 | 95 | } 96 | 97 | u32 init_weapon_sprites(u32 tile_loc) { 98 | VDP_resetSprites(); 99 | start_tile_loc = tile_loc; 100 | // set load tile location? 101 | sprite_idx_start = VDP_allocateSprites(MAX_SPRITES); 102 | KLog_U1_("Allocating ", MAX_SPRITES, " sprites for weapons"); 103 | KLog_U1_("Allocating ", (MAX_SPRITE_TILES*2), " tiles for weapons"); 104 | return tile_loc+(MAX_SPRITE_TILES*2); 105 | 106 | } 107 | 108 | 109 | #define BASE_WEAPON_X 128 110 | #define BASE_WEAPON_Y 120 111 | 112 | u32 get_next_tile_loc_and_flip() { 113 | static u8 flag = 0; 114 | 115 | if(flag == 0) { 116 | flag = 1; 117 | return start_tile_loc; 118 | } else { 119 | flag = 0; 120 | return start_tile_loc + MAX_SPRITE_TILES; 121 | } 122 | } 123 | 124 | u32 cur_upload_idx; 125 | void set_weapon_anim(weapon_sprite_anim anim_idx, TransferMethod transfer_type) { 126 | cur_anim_idx = anim_idx; 127 | animation_entry entry = anim_table[cur_anim_idx]; 128 | cur_frame_timer = entry.time; 129 | cur_upload_idx = get_next_tile_loc_and_flip(); 130 | 131 | switch(entry.spr) { 132 | case SHOTGUN_SPR: 133 | cur_sprite_def = &shotgun; 134 | break; 135 | case SHOTGUN_RELOAD_SPR: 136 | cur_sprite_def = &shotgun_reload; 137 | break; 138 | default: 139 | die("Unknown sprite"); 140 | } 141 | u8 animation_idx = entry.anim_idx; 142 | u8 frame_idx = entry.frame_idx; 143 | 144 | 145 | //ASSERT(num_tiles <= MAX_SPRITE_TILES) 146 | KLog_U1("sprites: ", cur_sprite_def->maxNumSprite); 147 | KLog_U1("sprite tiles: ", cur_sprite_def->maxNumTile); 148 | assert(cur_sprite_def->maxNumTile <= MAX_SPRITE_TILES, "Weapon has too many sprite tiles"); 149 | assert(cur_sprite_def->maxNumSprite <= MAX_SPRITES, "Weapon has too many sprites"); 150 | 151 | AnimationFrame* frame = cur_sprite_def->animations[animation_idx]->frames[frame_idx]; 152 | VDP_loadTileSet(frame->tileset, cur_upload_idx, transfer_type); 153 | } 154 | 155 | 156 | void update_weapon_sprites(TransferMethod transfer_type) { 157 | animation_entry entry = anim_table[cur_anim_idx]; 158 | u8 animation_idx = entry.anim_idx; 159 | u8 frame_idx = entry.frame_idx; 160 | AnimationFrame* frame = cur_sprite_def->animations[animation_idx]->frames[frame_idx]; 161 | //VDP_resetSprites(); // should i always reset? What if something else uses sprites? 162 | cur_num_sprites = frame->numSprite; 163 | 164 | u16 tile_idx = cur_upload_idx; 165 | 166 | VDP_clearSprites(); 167 | FrameVDPSprite** f = frame->frameInfos[0].frameVDPSprites; 168 | for(int i = 0; i < cur_num_sprites; i++) { 169 | FrameVDPSprite* spr = f[i]; 170 | 171 | VDP_setSprite( 172 | sprite_idx_start+i, 173 | BASE_WEAPON_X+spr->offsetX, BASE_WEAPON_Y+spr->offsetY, 174 | spr->size, 175 | TILE_ATTR_FULL(PAL0, 0, 0, 0, tile_idx) 176 | ); 177 | tile_idx += spr->numTile; 178 | 179 | } 180 | VDP_linkSprites(sprite_idx_start, cur_num_sprites); 181 | VDP_setSpriteLink(sprite_idx_start+cur_num_sprites, 0); 182 | VDP_updateSprites(cur_num_sprites, transfer_type); 183 | } 184 | 185 | 186 | void set_weapon_sprite_position_offset(s16 offX, s16 offY) { 187 | 188 | 189 | 190 | AnimationFrame *frame1 = cur_sprite_def->animations[0]->frames[0]; 191 | FrameVDPSprite** f = frame1->frameInfos[0].frameVDPSprites; 192 | for(int i = 0; i < cur_num_sprites; i++) { 193 | FrameVDPSprite* spr = f[i]; 194 | VDP_setSpritePosition(i+sprite_idx_start, 195 | BASE_WEAPON_X+spr->offsetX+offX, 196 | BASE_WEAPON_Y+spr->offsetY+offY 197 | ); 198 | } 199 | VDP_updateSprites(cur_num_sprites, DMA_QUEUE); 200 | 201 | 202 | } -------------------------------------------------------------------------------- /src/weapon_sprites.h: -------------------------------------------------------------------------------- 1 | #ifndef WEAPON_SPRITES 2 | 3 | #include 4 | 5 | #define WEAPON_SPRITES 6 | 7 | typedef enum { 8 | SHOTGUN_IDLE_ANIM=0, 9 | SHOTGUN_RELOAD_ANIM=1, 10 | } weapon_sprite_anim; 11 | 12 | u32 init_weapon_sprites(u32 tile_loc); 13 | 14 | void set_weapon_anim(weapon_sprite_anim anim_idx, TransferMethod transfer_type); 15 | void set_weapon_sprite_position_offset(s16 offX, s16 offY); 16 | void step_weapon_anim(TransferMethod transfer_type); 17 | 18 | #endif --------------------------------------------------------------------------------