diff --git a/Makefile b/Makefile index b3bafd9a..dfcd625f 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,12 @@ TARGET_RPI ?= 0 # Disable better camera by default BETTERCAMERA ?= 0 +# Disable no drawing distance by default +NODRAWINGDISTANCE ?= 0 +# Disable texture fixes by default (helps with them purists) +TEXTURE_FIX ?= 0 +# Enable extended options menu by default +EXT_OPTIONS_MENU ?= 1 # Build for Emscripten/WebGL TARGET_WEB ?= 0 @@ -42,8 +48,6 @@ else endif # Automatic settings for PC port(s) -# WINDOWS_BUILD IS NOT FOR COMPILING A WINDOWS EXECUTABLE UNDER LINUX OR WSL! -# USE THE WIKI GUIDE WITH MSYS2 FOR COMPILING A WINDOWS EXECUTABLE! NON_MATCHING := 1 GRUCODE := f3dex2e @@ -420,9 +424,14 @@ else LD := $(CC) endif -CPP := $(CROSS)cpp -P +ifeq ($(WINDOWS_BUILD),1) # fixes compilation in MXE on Linux and WSL + CPP := cpp -P + OBJCOPY := objcopy +else + CPP := $(CROSS)cpp -P + OBJCOPY := $(CROSS)objcopy +endif OBJDUMP := $(CROSS)objdump -OBJCOPY := $(CROSS)objcopy PYTHON := python3 SDLCONFIG := $(CROSS)sdl2-config @@ -440,33 +449,53 @@ CC_CHECK := $(CC) -fsyntax-only -fsigned-char $(INCLUDE_CFLAGS) -Wall -Wextra -W CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv `$(SDLCONFIG) --cflags` endif -# Check for better camera option +# Check for enhancement options + +# Check for Puppycam option ifeq ($(BETTERCAMERA),1) -CC_CHECK += -DBETTERCAMERA -CFLAGS += -DBETTERCAMERA + CC_CHECK += -DBETTERCAMERA + CFLAGS += -DBETTERCAMERA + EXT_OPTIONS_MENU := 1 +endif + +# Check for no drawing distance option +ifeq ($(NODRAWINGDISTANCE),1) + CC_CHECK += -DNODRAWINGDISTANCE + CFLAGS += -DNODRAWINGDISTANCE +endif + +# Check for texture fix option +ifeq ($(TEXTURE_FIX),1) + CC_CHECK += -DTEXTURE_FIX + CFLAGS += -DTEXTURE_FIX +endif + +# Check for extended options menu option +ifeq ($(EXT_OPTIONS_MENU),1) + CC_CHECK += -DEXT_OPTIONS_MENU + CFLAGS += -DEXT_OPTIONS_MENU endif ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS) ifeq ($(TARGET_WEB),1) LDFLAGS := -lm -lGL -lSDL2 -no-pie -s TOTAL_MEMORY=20MB -g4 --source-map-base http://localhost:8080/ -s "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']" -else - -ifeq ($(WINDOWS_BUILD),1) -LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -lpthread -lglew32 `$(SDLCONFIG) --static-libs` -lm -lglu32 -lsetupapi -ldinput8 -luser32 -lgdi32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion -luuid -lopengl32 -no-pie -static -ifeq ($(WINDOWS_CONSOLE),1) -LDFLAGS += -mconsole -endif -else - +else ifeq ($(WINDOWS_BUILD),1) + LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -lpthread -lglew32 `$(SDLCONFIG) --static-libs` -lm -lglu32 -lsetupapi -ldinput8 -luser32 -lgdi32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion -luuid -lopengl32 -static + ifneq ($(CROSS),i686-w64-mingw32.static-) + ifneq ($(CROSS),x86_64-w64-mingw32.static-) + LDFLAGS += -no-pie + endif + endif + ifeq ($(WINDOWS_CONSOLE),1) + LDFLAGS += -mconsole + endif +else ifeq ($(TARGET_RPI),1) # Linux / Other builds below -ifeq ($(TARGET_RPI),1) LDFLAGS := $(OPT_FLAGS) -lm -lGLESv2 `$(SDLCONFIG) --libs` -no-pie else LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm -lGL `$(SDLCONFIG) --libs` -no-pie -lpthread endif -endif -endif #Added for Pi ifeq # Prevent a crash with -sopt @@ -576,11 +605,13 @@ ifeq ($(VERSION),eu) $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o +$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o O_FILES += $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o else $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h +$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h endif ################################################################ diff --git a/README.md b/README.md index fc8e1320..b1cc3121 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ OpenGL adaptation of [n64decomp/sm64](https://github.com/n64decomp/sm64). Feel free to report bugs and contribute, but remember, there must be **no upload of any copyrighted asset**. -Run `./extract-assets.py --clean && make clean` or `make distclean` to remove ROM-originated content. This port has been made possible thanks to [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) by [Emill](https://github.com/Emill). +Run `./extract_assets.py --clean && make clean` or `make distclean` to remove ROM-originated content. This port has been made possible mostly thanks to [Emill](https://github.com/Emill) and his [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) renderer. + +*Read this in other languages: [Español](README_es_ES.md) [简体中文](README_zh_CN.md).* ## Features @@ -10,6 +12,9 @@ Run `./extract-assets.py --clean && make clean` or `make distclean` to remove RO * Variable aspect ratio and resolution. The game can now correctly render at basically any window size. * Native xinput controller support. On Linux, DualShock 4 has been confirmed to work plug-and-play. * Analog camera control and mouse look. (Activate with `make BETTERCAMERA=1`.) + * An option to disable drawing distances. (Activate with `make NODRAWINGDISTANCE=1`.) + * In-game control binding, currently available on the `testing` branch. + * Skip introductory Peach & Lakitu cutscenes with the `--skip-intro` CLI option ## Building For building instructions, please refer to the [wiki](https://github.com/sm64pc/sm64pc/wiki). diff --git a/README_es_ES.md b/README_es_ES.md new file mode 100644 index 00000000..7426486c --- /dev/null +++ b/README_es_ES.md @@ -0,0 +1,204 @@ +# sm64pc +Adaptación a OpenGL de [n64decomp/sm64](https://github.com/n64decomp/sm64). + +No dudes en contribuir o reportar bugs, pero recuerda: **no se debe subir nada con copyright**. +Ejecuta `./extract_assets.py --clean && make clean` o `make distclean` para borrar todo el contenido proveniente de la ROM. Este port es posible gracias a [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) creado por [Emill](https://github.com/Emill). + +## Funcionalidades + + * Renderizado nativo. Podrás jugar a Super Mario 64 sin necesidad de un emulador. + * Resolución y relación de aspecto variables. Puedes jugar a Super Mario 64 a básicamente cualquier resolución o tamaño de ventana. + * Soporte nativo para mandos XInput. En Linux, se ha confirmado que el DualShock 4 funciona sin más. + * Cámara analógica y cámara controlada con el ratón. (Se activa con `make BETTERCAMERA=1`.) + * Opción para desactivar el límite de distancia de renderizado. (Se activa con `make NODRAWINGDISTANCE=1`.) + * Configurar los controles desde el juego, actualmente solo en la rama `testing`. + * Posibilidad de saltarte la intro con la opción de línea de comandos `--skip-intro`, actualmente solo en las ramas `testing` y `skip-intro`. + +## Compilar en Windows +**No intentes compilar ejecutables para Windows bajo Linux usando `WINDOWS_BUILD=1`. No va a funcionar. Sigue la guía.** +#### 1. Instalación y configuración de MSYS2. + +1. Descarga [msys2-x86_64-latest.exe](http://repo.msys2.org/distrib/msys2-x86_64-latest.exe) y ejecútalo. Si tu sistema operativo es de 32 bits (¿por qué?) descarga [msys2-i686-latest.exe](http://repo.msys2.org/distrib/msys2-i686-latest.exe) en su lugar. Asegúrate de que lo instalas en `C:\dev\msys64` (o `C:\dev\msys32` para 32 bits...). Ejecuta MSYS2. + +2. En la ventana de comandos de MSYS2, ejecuta el siguiente comando: + ``` + pacman -Syuu + ``` +3. Abre "MSYS2 MinGW 64-Bit". Ejecuta este comando __repetidamente__ hasta que MSYS diga que ya no hay más actualizaciones. Es posible que tengas que volver a cerrar y abrir MSYS2. + + ``` + pacman -Syuu + ``` + +5. Ejecuta este comando y cuando te pida confirmación pulsa intro: + + ``` + pacman -S --needed base-devel mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain \ + git subversion mercurial \ + mingw-w64-i686-cmake mingw-w64-x86_64-cmake + ``` +6. Listo. +#### Instala las dependencias +``` +pacman -S mingw-w64-i686-glew mingw-w64-x86_64-glew mingw-w64-i686-SDL2 mingw-w64-x86_64-SDL2 python3 +``` +### Crea el directorio en el que preparar todo +Desde el explorador de Windows, navega a `C:\msys64\home\(nombre de usuario)\` y crea una carpeta con el nombre que te apetezca. Aquí es donde vamos a preparar todo. +### Clona el repositorio +En MSYS2, introduce el siguiente comando: +``` +git clone https://github.com/sm64pc/sm64pc/ +``` +(Si no funciona, prueba a escribirlo manualmente, en lugar de copiar y pegar) +#### Copia la ROM base al directorio correspondiente +El paso anterior tiene que haber creado una carpeta llamada sm64pc. Dentro de esa carpeta, y para cada version de la ROM (jp/us/eu) de la cual quieras compilar un ejecutable, coloca la ROM con el nombre `baserom..z64` para extraer sus assets. Por ejemplo, `baserom.us.z64` para la versión americana, o `baserom.eu.z64` para la versión europea. + +#### En MSYS2, vamos a navegar a la carpeta `./tools/audiofile-0.3.6/` y ejecutar el `autoreconf-i`. Introduce los siguientes comandos, en orden, uno a uno: +``` +cd sm64pc/tools/audiofile-0.3.6/ +autoreconf -i +``` +No te vayas de este directorio hasta el paso 9. + +#### Ejecuta el script `configure` +``` +PATH=/mingw64/bin:/mingw32/bin:$PATH LIBS=-lstdc++ ./configure --disable-docs +``` +#### Ejecuta el script `make` +``` +PATH=/mingw64/bin:/mingw32/bin:$PATH make +``` +#### Crea un directorio `lib` en `tools/` +``` +mkdir ../lib +``` + +#### Acabas de compilar `libaudiofile`. Ahora cópialo a `tools/lib/` +``` +cp libaudiofile/.libs/libaudiofile.a ../lib/ +cp libaudiofile/.libs/libaudiofile.la ../lib/ +``` +#### Ahora toca hacer algo desde Windows. +En el explorador de Windows, ve a sm64pc\tools y edita el archivo Makefile desde un editor de texto (es recomendable usar un editor decente como Notepad++ o Sublime Text, en lugar del bloc de notas, para asegurarte de que no rompes el formato del texto) Busca la línea que contiene esto: + +```tabledesign_CFLAGS := -Wno-uninitialized -laudiofile``` + +Y añade ` -lstdc++` al final, de manera que quede así (¡no olvides el espacio!) + +```tabledesign_CFLAGS := -Wno-uninitialized -laudiofile -lstdc++``` + +Guarda el archivo. +#### Vuelve a la carpeta tools y ejecuta `make` con los siguientes comandos. +``` +cd .. +PATH=/mingw64/bin:/mingw32/bin:$PATH make +``` +#### Vuelve al directorio sm64pc +``` +cd .. +``` +#### Finalmente, ejecuta ```make``` de nuevo. + +(Ten en cuenta que mingw32 y mingw64 han sido intercambiados. Esto es para que puedas compilar la versión de 32 bits si quieres.) + +Aquí pones las opciones que quieras según la versión que quieras compilar. Por ejemplo, si quieres activar la cámara analógica, añade al final BETTERCAMERA=1. Si quieres la opción de distancia de renderizado ilimitada, añade NODRAWINGDISTANCE=1. + +Por ejemplo: +``` +PATH=/mingw32/bin:/mingw64/bin:$PATH make BETTERCAMERA=1 NODRAWINGDISTANCE=1 +``` +Listo. El .exe estará en sm64pc\build\. Disfruta. +## Compilar en Linux + +### Nota para usuarios de Windows +No intentes compilar un ejecutable para Windows desde Linux o WSL. No funciona. Sigue la guía para Windows. + +#### Copia la(s) ROM(s) base para la extracción de assets. + +Por cada versión de la cual quieras compilar un ejecutable, copia la ROM en `./baserom..z64` para extraer los assets. + +#### Instala las dependencias. + +Para compilar necesitas las sigueintes dependencias. + * python3 >= 3.6 + * libsdl2-dev + * [audiofile](https://audiofile.68k.org/) + * libglew-dev + * git + +Puedes instalarlas con este comando: + +##### Debian / Ubuntu - (compilando para 32 bits) +``` +sudo apt install build-essential git python3 libaudiofile-dev libglew-dev:i386 libsdl2-dev:i386 +``` +##### Debian / Ubuntu - (compilando para 64 bits) +``` +sudo apt install build-essential git python3 libaudiofile-dev libglew-dev libsdl2-dev +``` +##### Arch Linux +Hay un paquete AUR (cortesía de @narukeh) disponible bajo el nombre [sm64pc-git](https://aur.archlinux.org/packages/sm64pc-git/). Instálalo con tu gestor de AURs preferido. + +Si quieres compilarlo por tu cuenta: +``` +sudo pacman -S base-devel python audiofile sdl2 glew +``` + +##### Void Linux - (compilando para 64 bits) +``` +sudo xbps-install -S base-devel python3 audiofile-devel SDL2-devel glew-devel +``` + +##### Void Linux - (compilando para 32 bits) +``` +sudo xbps-install -S base-devel python3 audiofile-devel-32bit SDL2-devel-32bit glew-devel-32bit +``` + +#### Compila el ejecutable. + +Ejecuta `make` para compilar (por defecto `VERSION=us`) + +``` +make VERSION=jp -j6 # Compila la versión (J) usando 6 hilos +make VERSION=us MARCH=i686 TARGET_BITS=32 # Compila un ejecutable de la versión (U) de 32 bits +make TARGET_RPI=1 # Compila un ejecutable para Raspberry Pi +``` +## Compilar para la web +Puedes compilar el juego para navegadores que admitan WebGL usando [Emscripten](https://github.com/emscripten-core). Para hacerlo, instala [emsdk](https://github.com/emscripten-core/emsdk) y ejecuta `make TARGET_WEB=1`. + +## Script para compilar para Raspberry Pi + +[Hyenadae](https://github.com/Hyenadae/) ha creado un script que ayuda a compilar el juego para Raspberry Pi. Estos son los pasos que hace el script: + + * Instala las dependencias; + * Cambia VC4_DRM en la RPi de 0 a 3; + * Cambia ajustes en la memoria de las RPis 0 y 1 para que se pueda completar la compilación; + * Permite la instalación de un SDL2 con KMS, lo que elimina la necesidad de usar X11 y garantiza el máximo rendimiento de cualquier RPi que ejecute VC4; + * Clona sm64pc si no encuentra los archivos necesarios; + * Comprueba si existen los assets y la ROM base necesaria (baserom.*.z64); + * Compila sm64pc. + +El script está incluído en la rama master, pero también puede descargarse [aquí](https://raw.githubusercontent.com/sm64pc/sm64pc/master/pisetup.sh). +# Problemas conocidos +### Problemas ya conocidos: + * La versión EU tiene bugs en los textos y no tiene audio. + * El movimiento analógico horizontal de la cámara vuelve al estilo antiguo en el nivel Bowser in the Dark World (#72) + * La cámara con el ratón falla cuando disparas a Mario hacia un árbol o un tubo. (#71) + * "make: Nothing to be done for 'default'" al compilar para web. (#67) + +### Estos problemas están marcados como solucionados. Por favor, contacta si sigues teniendo estos problemas. + * El juego se llena de flags aleatorias en las builds de 64 bits para Windows + * Hazy Maze Cave se cuelga en pantalla completa (#57) + * La pantalla de título no tiene el cursor para manipular a Mario en pantalla completa. (#28) + +## Parches +En la carpeta `./enhancements` hay varios archivos `patch`, que pueden aplicarse de la siguiente manera: + +``` + git apply fps.patch --ignore-whitespace --reject +``` +Si ocurre un rechazo, puedes buscarlo con el comando `find | grep .rej`. +Intenta resolver los rechazos a través de [wiggle](https://github.com/neilbrown/wiggle). +``` +wiggle rejection.rej --replace +``` diff --git a/README_zh_CN.md b/README_zh_CN.md new file mode 100644 index 00000000..b3e90f92 --- /dev/null +++ b/README_zh_CN.md @@ -0,0 +1,21 @@ +# sm64pc +本项目是 [n64decomp/sm64](https://github.com/n64decomp/sm64) 的 OpenGL 移植版本。 + +我们欢迎贡献代码与 bug 报告,但请切记,**不得上传任何被版权保护(来自 ROM 文件)的资源**。 +提交前请运行 `./extract_assets.py --clean && make clean` 或 `make distclean` 来清除所有从 ROM 文件中提取的内容。 +本移植是基于 [Emill](https://github.com/Emill) 的工作 [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) 才得以实现的。 + +## 主要功能 + + * 原生渲染。现在不用任何模拟器就可以运行 马力欧64 了。 + * 长宽比和分辨率可以自由改变。本游戏目前可以在几乎任何窗口尺寸下正确渲染。 + * 原生 xinput 手柄支持。在 Linux 下,已经确认 PS4 手柄可以即插即用。 + * 支持模拟量视点控制、鼠标控制视点。(请使用 `make BETTERCAMERA=1` 编译) + * 可取消可视距离限制。(请使用 `make NODRAWINGDISTANCE=1` 编译) + * 游戏内操作设定功能,目前在 `testing` 分支下可用。 + * 使用 `--skip-intro` 命令行选项跳过碧奇公主与 Lakitu 的片头剧情。目前在 `testing` 及 `skip-intro` 分支下可用。 + +## 编译方法 +关于如何编译,请参考 [wiki](https://github.com/sm64pc/sm64pc/wiki)。 + +**请勿在 Linux 或者 WSL 下使用 `WINDOWS_BUILD=1` 参数尝试编译 Windows 版本,这样无法编译成功。请参考 Wiki。 diff --git a/actors/burn_smoke/model.inc.c b/actors/burn_smoke/model.inc.c index a0f65578..bcf4fd6f 100644 --- a/actors/burn_smoke/model.inc.c +++ b/actors/burn_smoke/model.inc.c @@ -11,6 +11,9 @@ static const Vtx burn_smoke_seg4_vertex_040217C0[] = { // //! Wrong texture format. Called as rgba16, which makes the burn smoke appear // as a transparent black burn smoke. Probably meant to show up as white-ish // burn smoke, but mistakened for being intended as black smoke. +// Due to debate in the Koopa shorts PR surrounding the fix to a similar bug, +// said fix is on a compile-time variable. Use TEXTURE_FIX=1 at compile time +// to fix this. // 0x04021800 ALIGNED8 static const u8 burn_smoke_seg4_texture_04021800[] = { #include "actors/burn_smoke/burn_smoke.ia16.inc.c" @@ -44,7 +47,11 @@ const Gfx burn_smoke_seg4_dl_04022048[] = { // 0x04022070 - 0x040220C8 const Gfx burn_smoke_seg4_dl_04022070[] = { gsSPDisplayList(burn_smoke_seg4_dl_04022000), + #ifdef TEXTURE_FIX + gsDPLoadTextureBlock(burn_smoke_seg4_texture_04021800, G_IM_FMT_IA, G_IM_SIZ_16b, 32, 32, 0, G_TX_CLAMP, G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD), + #else gsDPLoadTextureBlock(burn_smoke_seg4_texture_04021800, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, 0, G_TX_CLAMP, G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD), + #endif gsSPDisplayList(burn_smoke_seg4_dl_04022028), gsSPDisplayList(burn_smoke_seg4_dl_04022048), gsSPEndDisplayList(), diff --git a/actors/koopa/model.inc.c b/actors/koopa/model.inc.c index 04a43a8a..6bf24187 100644 --- a/actors/koopa/model.inc.c +++ b/actors/koopa/model.inc.c @@ -54,6 +54,8 @@ static const Lights1 koopa_seg6_lights_06002630 = gdSPDefLights1( // beneath its shell, despite the fact it was intended to be white like // the rest of its body. This is evident because once the mistake is corrected // it turns back to being white like the other polygons. +// Due to debate in the PR surrounding the fix to this, said fix is on +// a compile-time variable. Use TEXTURE_FIX=1 at compile time to fix this. // 0x06002648 ALIGNED8 static const u8 koopa_seg6_texture_06002648[] = { #include "actors/koopa/koopa_shell_front.rgba16.inc.c" @@ -2077,8 +2079,13 @@ const Gfx koopa_seg6_dl_0600C498[] = { gsSPVertex(koopa_seg6_vertex_0600B560, 9, 0), gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 0x0), gsSP1Triangle( 6, 7, 8, 0x0), + #ifdef TEXTURE_FIX + gsSPLight(&koopa_seg6_lights_06002630.l, 1), + gsSPLight(&koopa_seg6_lights_06002630.a, 2), + #else gsSPLight(koopa_seg6_texture_06002648 + 0x20, 1), // this malformed light results in a gsSPLight(koopa_seg6_texture_06002648 + 0x18, 2), // koopa appearing to wear pink shorts. + #endif gsSPVertex(koopa_seg6_vertex_0600B5F0, 15, 0), gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 0x0), gsSP2Triangles( 6, 7, 0, 0x0, 8, 5, 9, 0x0), diff --git a/c2obj.py b/c2obj.py new file mode 100644 index 00000000..508a51ec --- /dev/null +++ b/c2obj.py @@ -0,0 +1,141 @@ +""" +This module attempts to parse the ``model.inc.c`` files and extract the +3D models within as standard Wavefront OBJ files. + +Example: + Specify the path to the ``.inc.c`` file and a directory where to save + the extracted ``.obj`` files. + + $ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/ + +This is a work in progress and it currently has some serious limitations: + * It only extracts geometry information, so no textures or any other info + * It makes assumptions about the layout of the code in the C source + * It hasn't been properly tested. + +""" + +def parse(filename, output_directory): + from os import path, mkdir + + if not path.isdir(output_directory): + try: + mkdir(output_directory) + except OSError: + print(f'Could not use output directory {output_directory}.') + + vtx_def = 'static const Vtx ' + vtx_data = {} + reading_vtx = False + current_vtx_name = '' + current_vtx_data = [] + current_vtx_vertices = 0 + + gfx_def = 'const Gfx ' + reading_gfx = False + current_gfx_vertices = 0 + current_gfx_faces = 0 + insert_vert_call = 'gsSPVertex(' + insert_1tri_call = 'gsSP1Triangle(' + insert_2tri_call = 'gsSP2Triangles(' + gfx_count = 0 + + end_of_block = '};' + + with open(filename, 'r') as f: + for line in f: + line = line.strip() + + if line.startswith(vtx_def): + vtx_name = line.split(' ')[3][:-2] + current_vtx_name = vtx_name + current_vtx_data = [] + reading_vtx = True + continue + + if line.startswith(gfx_def): + from datetime import datetime + + current_gfx_name = line.split(' ')[2][:-2] + current_gfx_file = open(path.join(output_directory, current_gfx_name + '.obj'), 'w') + current_gfx_file.write("# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n") + current_gfx_file.write('# File Created: {}\n\n'.format(datetime.now())) + reading_gfx = True + continue + + if line == end_of_block: + if reading_vtx: + vtx_data[current_vtx_name] = current_vtx_data + reading_vtx = False + + elif reading_gfx: + current_gfx_file.write(f'# {current_gfx_faces} faces\n\n') + current_gfx_file.close() + current_gfx_vertices = 0 + reading_gfx = False + gfx_count += 1 + + continue + + if reading_vtx: + line = line.replace('{', '[').replace('}', ']') + tri = eval(line[:-1])[0] + current_vtx_data.append(tri) + continue + + if reading_gfx: + if line.startswith(insert_vert_call): + args = line[len(insert_vert_call):].split(',') + current_vtx_name = args[0] + + if current_gfx_vertices > 0: + current_gfx_file.write(f'# {current_gfx_faces} faces\n\n') + + current_gfx_faces = 0 + current_vtx_vertices = len(vtx_data[current_vtx_name]) + current_gfx_vertices += current_vtx_vertices + + current_gfx_file.write(f'#\n# object {current_vtx_name}\n#\n\n') + current_vtx_data = vtx_data[current_vtx_name] + for tri in current_vtx_data: + v = tri[0] + current_gfx_file.write('v {:.3f} {:.3f} {:.3f}\n'.format(*v)) + current_gfx_file.write(f'# {current_vtx_vertices} vertices\n\n') + + for tri in current_vtx_data: + n = [_decode_normal(u) for u in tri[3][:3]] + current_gfx_file.write('vn {:.3f} {:.3f} {:.3f}\n'.format(*n)) + current_gfx_file.write(f'# {current_vtx_vertices} vertex normals\n\n') + + current_gfx_file.write(f'g {current_vtx_name}\n\n') + + elif line.startswith(insert_2tri_call): + args = line[len(insert_2tri_call):].split(',') + correction = current_gfx_vertices - current_vtx_vertices + 1 + indexes = [eval(args[i]) + correction for i in [0, 1, 2, 4, 5, 6]] + current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[:3])) + current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[3:])) + current_gfx_faces += 2 + + elif line.startswith(insert_1tri_call): + args = line[len(insert_1tri_call):].split(',') + correction = current_gfx_vertices - current_vtx_vertices + 1 + indexes = [eval(args[i]) + correction for i in [0, 1, 2]] + current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes)) + current_gfx_faces += 1 + + continue + + print(f'{gfx_count} models extracted.') + +def _decode_normal(x): + y = x if x <= 127 else x - 255 + return y / 127 + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('filename', help = 'filename of the .inc.c source file') + parser.add_argument('output_directory', help = 'directory where to put the extracted .obj files') + args = parser.parse_args() + parse(args.filename, args.output_directory) \ No newline at end of file diff --git a/data/behavior_data.c b/data/behavior_data.c index 13212d3b..3b4af5a0 100644 --- a/data/behavior_data.c +++ b/data/behavior_data.c @@ -114,7 +114,7 @@ // Often used to end behavior scripts that do not contain an infinite loop. #define BREAK() \ BC_B(0x0A) - + // Exits the behavior script, unused. #define BREAK_UNUSED() \ BC_B(0x0B) @@ -175,15 +175,15 @@ #define ADD_INT_RAND_RSHIFT(field, min, rshift) \ BC_BBH(0x17, field, min), \ BC_H(rshift) - + // No operation. Unused. #define CMD_NOP_1(field) \ BC_BB(0x18, field) - + // No operation. Unused. #define CMD_NOP_2(field) \ BC_BB(0x19, field) - + // No operation. Unused. #define CMD_NOP_3(field) \ BC_BB(0x1A, field) @@ -219,6 +219,9 @@ #define BILLBOARD() \ BC_B(0x21) +#define CYLBOARD() \ + BC_B(0x38) + // Hides the current object. #define HIDE() \ BC_B(0x22) @@ -3180,7 +3183,7 @@ const BehaviorScript bhvFloorTrapInCastle[] = { const BehaviorScript bhvTree[] = { BEGIN(OBJ_LIST_POLELIKE), - BILLBOARD(), + CYLBOARD(), OR_INT(oFlags, OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE), SET_INT(oInteractType, INTERACT_POLE), SET_HITBOX(/*Radius*/ 80, /*Height*/ 500), @@ -6105,5 +6108,3 @@ const BehaviorScript bhvIntroScene[] = { CALL_NATIVE(bhv_intro_scene_loop), END_LOOP(), }; - - diff --git a/enhancements/no_draw_distance/README.md b/enhancements/no_draw_distance/README.md deleted file mode 100644 index dd890383..00000000 --- a/enhancements/no_draw_distance/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# No Draw Distances - -This is a work-in-progress by [wabberz](https://github.com/wabberz) that disables the drawing distance for most objects and enemies. - -**This will crash some levels in the 32-bit version**. - -[Related Push Request](https://github.com/sm64pc/sm64pc/pull/75). - -For instructions on how to apply patches, please refer to [the Wiki](https://github.com/sm64pc/sm64pc/wiki/Patches). - diff --git a/enhancements/no_draw_distance/nodrawdistance.patch b/enhancements/no_draw_distance/nodrawdistance.patch deleted file mode 100644 index b986b1a6..00000000 --- a/enhancements/no_draw_distance/nodrawdistance.patch +++ /dev/null @@ -1,406 +0,0 @@ -From c98a263cf40520bf0d131eb2d1a2f90240787c98 Mon Sep 17 00:00:00 2001 -From: uwabami -Date: Tue, 12 May 2020 09:26:16 +0200 -Subject: [PATCH] adding option to disable draw distance - ---- - Makefile | 8 ++++++++ - src/engine/behavior_script.c | 4 ++++ - src/engine/surface_load.c | 4 ++++ - src/game/behaviors/butterfly.inc.c | 3 ++- - src/game/behaviors/chain_chomp.inc.c | 8 ++++++++ - src/game/behaviors/coin.inc.c | 6 ++++++ - src/game/behaviors/fish.inc.c | 4 ++++ - src/game/behaviors/goomba.inc.c | 4 ++++ - src/game/behaviors/heave_ho.inc.c | 4 ++++ - src/game/behaviors/king_bobomb.inc.c | 4 ++++ - src/game/behaviors/pokey.inc.c | 6 ++++++ - src/game/behaviors/snufit.inc.c | 4 ++++ - src/game/behaviors/triplet_butterfly.inc.c | 4 ++++ - src/game/behaviors/water_bomb_cannon.inc.c | 8 ++++++++ - src/game/behaviors/whirlpool.inc.c | 4 ++++ - 15 files changed, 74 insertions(+), 1 deletion(-) - -diff --git a/Makefile b/Makefile -index d4bd284..efeb63e 100644 ---- a/Makefile -+++ b/Makefile -@@ -27,6 +27,8 @@ COMPILER ?= ido - - # Disable better camera by default - BETTERCAMERA ?= 0 -+# Disable no drawing distance by default -+NODRAWINGDISTANCE ?= 0 - - # Build for Emscripten/WebGL - TARGET_WEB ?= 0 -@@ -449,6 +451,12 @@ CC_CHECK += -DBETTERCAMERA - CFLAGS += -DBETTERCAMERA - endif - -+# Check for no drawing distance option -+ifeq ($(NODRAWINGDISTANCE),1) -+CC_CHECK += -DNODRAWINGDISTANCE -+CFLAGS += -DNODRAWINGDISTANCE -+endif -+ - ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS) - - ifeq ($(TARGET_WEB),1) -diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c -index edd5247..feb6fef 100644 ---- a/src/engine/behavior_script.c -+++ b/src/engine/behavior_script.c -@@ -987,11 +987,15 @@ void cur_obj_update(void) { - } else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) { - if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) { - // If the object has a render distance, check if it should be shown. -+#ifndef NODRAWINGDISTANCE - if (distanceFromMario > gCurrentObject->oDrawingDistance) { - // Out of render distance, hide the object. - gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; - } else if (gCurrentObject->oHeldState == HELD_FREE) { -+#else -+ if (distanceFromMario <= gCurrentObject->oDrawingDistance && gCurrentObject->oHeldState == HELD_FREE) { -+#endif - // In render distance (and not being held), show the object. - gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; - gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; -diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c -index 363f9af..498fae0 100644 ---- a/src/engine/surface_load.c -+++ b/src/engine/surface_load.c -@@ -789,9 +789,13 @@ void load_object_collision_model(void) { - } - } - -+#ifndef NODRAWINGDISTANCE - if (marioDist < gCurrentObject->oDrawingDistance) { -+#endif - gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; -+#ifndef NODRAWINGDISTANCE - } else { - gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - } -+#endif - } -diff --git a/src/game/behaviors/butterfly.inc.c b/src/game/behaviors/butterfly.inc.c -index d435d8d..9296ed5 100644 ---- a/src/game/behaviors/butterfly.inc.c -+++ b/src/game/behaviors/butterfly.inc.c -@@ -107,6 +107,7 @@ void bhv_butterfly_loop(void) { - butterfly_act_return_home(); - break; - } -- -+#ifndef NODRAWINGDISTANCE - set_object_visibility(o, 3000); -+#endif - } -diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c -index a77c5d5..9b9c342 100644 ---- a/src/game/behaviors/chain_chomp.inc.c -+++ b/src/game/behaviors/chain_chomp.inc.c -@@ -53,7 +53,9 @@ static void chain_chomp_act_uninitialized(void) { - struct ChainSegment *segments; - s32 i; - -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 3000.0f) { -+#endif - segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); - if (segments != NULL) { - // Each segment represents the offset of a chain part to the pivot. -@@ -81,7 +83,9 @@ static void chain_chomp_act_uninitialized(void) { - cur_obj_unhide(); - } - } -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - /** -@@ -359,10 +363,12 @@ static void chain_chomp_act_move(void) { - f32 maxDistToPivot; - - // Unload chain if mario is far enough -+#ifndef NODRAWINGDISTANCE - if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) { - o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN; - o->oForwardVel = o->oVelY = 0.0f; - } else { -+#endif - cur_obj_update_floor_and_walls(); - - switch (o->oChainChompReleaseStatus) { -@@ -446,7 +452,9 @@ static void chain_chomp_act_move(void) { - o->oGravity = -4.0f; - o->oChainChompTargetPitch = -0x3000; - } -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - /** -diff --git a/src/game/behaviors/coin.inc.c b/src/game/behaviors/coin.inc.c -index 913c583..05619b9 100644 ---- a/src/game/behaviors/coin.inc.c -+++ b/src/game/behaviors/coin.inc.c -@@ -184,17 +184,23 @@ void bhv_coin_formation_loop(void) { - s32 bitIndex; - switch (o->oAction) { - case 0: -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 2000.0f) { -+#endif - for (bitIndex = 0; bitIndex < 8; bitIndex++) { - if (!(o->oCoinUnkF4 & (1 << bitIndex))) - spawn_coin_in_formation(bitIndex, o->oBehParams2ndByte); - } - o->oAction++; -+#ifndef NODRAWINGDISTANCE - } -+#endif - break; - case 1: -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario > 2100.0f) - o->oAction++; -+#endif - break; - case 2: - o->oAction = 0; -diff --git a/src/game/behaviors/fish.inc.c b/src/game/behaviors/fish.inc.c -index 839ab8d..f652ef4 100644 ---- a/src/game/behaviors/fish.inc.c -+++ b/src/game/behaviors/fish.inc.c -@@ -42,7 +42,9 @@ void fish_act_spawn(void) { - * If the current level is Secret Aquarium, ignore this requirement. - * Fish moves at random with a max-range of 700.0f. - */ -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) { -+#endif - for (i = 0; i < schoolQuantity; i++) { - fishObject = spawn_object(o, model, bhvFish); - fishObject->oBehParams2ndByte = o->oBehParams2ndByte; -@@ -50,7 +52,9 @@ void fish_act_spawn(void) { - obj_translate_xyz_random(fishObject, 700.0f); - } - o->oAction = FISH_ACT_ACTIVE; -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - /** -diff --git a/src/game/behaviors/goomba.inc.c b/src/game/behaviors/goomba.inc.c -index 2dab2fe..bf47dda 100644 ---- a/src/game/behaviors/goomba.inc.c -+++ b/src/game/behaviors/goomba.inc.c -@@ -78,7 +78,9 @@ void bhv_goomba_triplet_spawner_update(void) { - // If mario is close enough and the goombas aren't currently loaded, then - // spawn them - if (o->oAction == GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 3000.0f) { -+#endif - // The spawner is capable of spawning more than 3 goombas, but this - // is not used in the game - dAngle = -@@ -98,11 +100,13 @@ void bhv_goomba_triplet_spawner_update(void) { - } - - o->oAction += 1; -+#ifndef NODRAWINGDISTANCE - } - } else if (o->oDistanceToMario > 4000.0f) { - // If mario is too far away, enter the unloaded action. The goombas - // will detect this and unload themselves - o->oAction = GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED; -+#endif - } - } - -diff --git a/src/game/behaviors/heave_ho.inc.c b/src/game/behaviors/heave_ho.inc.c -index 662bb0b..2f9da86 100644 ---- a/src/game/behaviors/heave_ho.inc.c -+++ b/src/game/behaviors/heave_ho.inc.c -@@ -73,14 +73,18 @@ void heave_ho_act_3(void) { - - void heave_ho_act_0(void) { - cur_obj_set_pos_to_home(); -+#ifndef NODRAWINGDISTANCE - if (find_water_level(o->oPosX, o->oPosZ) < o->oPosY && o->oDistanceToMario < 4000.0f) { -+#endif - cur_obj_become_tangible(); - cur_obj_unhide(); - o->oAction = 1; -+#ifndef NODRAWINGDISTANCE - } else { - cur_obj_become_intangible(); - cur_obj_hide(); - } -+#endif - } - - void (*sHeaveHoActions[])(void) = { heave_ho_act_0, heave_ho_act_1, heave_ho_act_2, heave_ho_act_3 }; -diff --git a/src/game/behaviors/king_bobomb.inc.c b/src/game/behaviors/king_bobomb.inc.c -index 63a7575..af1cb0a 100644 ---- a/src/game/behaviors/king_bobomb.inc.c -+++ b/src/game/behaviors/king_bobomb.inc.c -@@ -295,10 +295,14 @@ void king_bobomb_move(void) { - cur_obj_move_using_fvel_and_gravity(); - cur_obj_call_action_function(sKingBobombActions); - exec_anim_sound_state(sKingBobombSoundStates); -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 5000.0f) -+#endif - cur_obj_enable_rendering(); -+#ifndef NODRAWINGDISTANCE - else - cur_obj_disable_rendering(); -+#endif - } - - void bhv_king_bobomb_loop(void) { -diff --git a/src/game/behaviors/pokey.inc.c b/src/game/behaviors/pokey.inc.c -index df5d11f..cfcc92c 100644 ---- a/src/game/behaviors/pokey.inc.c -+++ b/src/game/behaviors/pokey.inc.c -@@ -151,7 +151,9 @@ static void pokey_act_uninitialized(void) { - s32 i; - s16 partModel; - -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 2000.0f) { -+#endif - partModel = MODEL_POKEY_HEAD; - - for (i = 0; i < 5; i++) { -@@ -170,7 +172,9 @@ static void pokey_act_uninitialized(void) { - o->oPokeyNumAliveBodyParts = 5; - o->oPokeyBottomBodyPartSize = 1.0f; - o->oAction = POKEY_ACT_WANDER; -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - /** -@@ -185,9 +189,11 @@ static void pokey_act_wander(void) { - - if (o->oPokeyNumAliveBodyParts == 0) { - obj_mark_for_deletion(o); -+#ifndef NODRAWINGDISTANCE - } else if (o->oDistanceToMario > 2500.0f) { - o->oAction = POKEY_ACT_UNLOAD_PARTS; - o->oForwardVel = 0.0f; -+#endif - } else { - treat_far_home_as_mario(1000.0f); - cur_obj_update_floor_and_walls(); -diff --git a/src/game/behaviors/snufit.inc.c b/src/game/behaviors/snufit.inc.c -index f3a0c9e..76e78c0 100644 ---- a/src/game/behaviors/snufit.inc.c -+++ b/src/game/behaviors/snufit.inc.c -@@ -180,7 +180,11 @@ void bhv_snufit_loop(void) { - void bhv_snufit_balls_loop(void) { - // If far from Mario or in a different room, despawn. - if ((o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) -+#ifndef NODRAWINGDISTANCE - || (o->oTimer != 0 && o->oDistanceToMario > 1500.0f)) { -+#else -+ || (o->oTimer != 0)) { -+#endif - obj_mark_for_deletion(o); - } - -diff --git a/src/game/behaviors/triplet_butterfly.inc.c b/src/game/behaviors/triplet_butterfly.inc.c -index 1c2b926..3d16a9d 100644 ---- a/src/game/behaviors/triplet_butterfly.inc.c -+++ b/src/game/behaviors/triplet_butterfly.inc.c -@@ -54,9 +54,11 @@ static void triplet_butterfly_act_init(void) { - } - - static void triplet_butterfly_act_wander(void) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario > 1500.0f) { - obj_mark_for_deletion(o); - } else { -+#endif - approach_f32_ptr(&o->oTripletButterflySpeed, 8.0f, 0.5f); - if (o->oTimer < 60) { - o->oTripletButterflyTargetYaw = cur_obj_angle_to_home(); -@@ -82,7 +84,9 @@ static void triplet_butterfly_act_wander(void) { - - obj_move_pitch_approach(o->oTripletButterflyTargetPitch, 400); - cur_obj_rotate_yaw_toward(o->oTripletButterflyTargetYaw, random_linear_offset(400, 800)); -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - static void triplet_butterfly_act_activate(void) { -diff --git a/src/game/behaviors/water_bomb_cannon.inc.c b/src/game/behaviors/water_bomb_cannon.inc.c -index 8e9ba33..fb82e43 100644 ---- a/src/game/behaviors/water_bomb_cannon.inc.c -+++ b/src/game/behaviors/water_bomb_cannon.inc.c -@@ -38,19 +38,27 @@ void bhv_bubble_cannon_barrel_loop(void) { - } - - void water_bomb_cannon_act_0(void) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 2000.0f) { -+#endif - spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles); - cur_obj_unhide(); - - o->oAction = 1; - o->oMoveAnglePitch = o->oWaterCannonUnkFC = 0x1C00; -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - void water_bomb_cannon_act_1(void) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario > 2500.0f) { - o->oAction = 2; - } else if (o->oBehParams2ndByte == 0) { -+#else -+ if (o->oBehParams2ndByte == 0) { -+#endif - if (o->oWaterCannonUnkF4 != 0) { - o->oWaterCannonUnkF4 -= 1; - } else { -diff --git a/src/game/behaviors/whirlpool.inc.c b/src/game/behaviors/whirlpool.inc.c -index 405e051..5aebebd 100644 ---- a/src/game/behaviors/whirlpool.inc.c -+++ b/src/game/behaviors/whirlpool.inc.c -@@ -35,7 +35,9 @@ void whirpool_orient_graph(void) { - } - - void bhv_whirlpool_loop(void) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 5000.0f) { -+#endif - o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; - - // not sure if actually an array -@@ -52,10 +54,12 @@ void bhv_whirlpool_loop(void) { - whirpool_orient_graph(); - - o->oFaceAngleYaw += 0x1F40; -+#ifndef NODRAWINGDISTANCE - } else { - o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; - gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT] = 0; - } -+#endif - - cur_obj_play_sound_1(SOUND_ENV_WATER); - diff --git a/include/text_strings.h.in b/include/text_strings.h.in index 030f3959..3246699e 100644 --- a/include/text_strings.h.in +++ b/include/text_strings.h.in @@ -3,20 +3,47 @@ #include "text_menu_strings.h" -#define NC_CAMX _("Camera X Sensitivity") -#define NC_CAMY _("Camera Y Sensitivity") -#define NC_INVERTX _("Invert X Axis") -#define NC_INVERTY _("Invert Y Axis") -#define NC_CAMC _("Camera Centre Aggression") -#define NC_CAMP _("Camera Pan Level") -#define NC_ENABLED _("Enabled") -#define NC_DISABLED _("Disabled") -#define NC_BUTTON _("[R]: Options") -#define NC_BUTTON2 _("[R]: Return") -#define NC_OPTION _("OPTIONS") -#define NC_HIGHLIGHT _("O") -#define NC_ANALOGUE _("Analogue Camera") -#define NC_MOUSE _("Mouse Look") +#define TEXT_OPT_CAMX _("Camera X Sensitivity") +#define TEXT_OPT_CAMY _("Camera Y Sensitivity") +#define TEXT_OPT_INVERTX _("Invert X Axis") +#define TEXT_OPT_INVERTY _("Invert Y Axis") +#define TEXT_OPT_CAMC _("Camera Centre Aggression") +#define TEXT_OPT_CAMP _("Camera Pan Level") +#define TEXT_OPT_CAMD _("Camera Deceleration") +#define TEXT_OPT_ENABLED _("Enabled") +#define TEXT_OPT_DISABLED _("Disabled") +#define TEXT_OPT_BUTTON1 _("[R]: Options") +#define TEXT_OPT_BUTTON2 _("[R]: Return") +#define TEXT_OPT_OPTIONS _("OPTIONS") +#define TEXT_OPT_CAMERA _("CAMERA") +#define TEXT_OPT_CONTROLS _("CONTROLS") +#define TEXT_OPT_VIDEO _("DISPLAY") +#define TEXT_OPT_AUDIO _("SOUND") +#define TEXT_OPT_HIGHLIGHT _("O") +#define TEXT_OPT_ANALOGUE _("Analogue Camera") +#define TEXT_OPT_MOUSE _("Mouse Look") +#define TEXT_OPT_TEXFILTER _("Texture Filtering") +#define TEXT_OPT_FSCREEN _("Fullscreen") +#define TEXT_OPT_NEAREST _("Nearest") +#define TEXT_OPT_LINEAR _("Linear") +#define TEXT_OPT_MVOLUME _("Master Volume") + +#define TEXT_OPT_UNBOUND _("NONE") +#define TEXT_OPT_PRESSKEY _("...") +#define TEXT_BIND_A _("A Button") +#define TEXT_BIND_B _("B Button") +#define TEXT_BIND_START _("Start Button") +#define TEXT_BIND_L _("L Trigger") +#define TEXT_BIND_R _("R Trigger") +#define TEXT_BIND_Z _("Z Trigger") +#define TEXT_BIND_C_UP _("C-Up") +#define TEXT_BIND_C_DOWN _("C-Down") +#define TEXT_BIND_C_LEFT _("C-Left") +#define TEXT_BIND_C_RIGHT _("C-Right") +#define TEXT_BIND_UP _("Stick Up") +#define TEXT_BIND_DOWN _("Stick Down") +#define TEXT_BIND_LEFT _("Stick Left") +#define TEXT_BIND_RIGHT _("Stick Right") /** * Global Symbols diff --git a/obj2c.py b/obj2c.py new file mode 100644 index 00000000..015aa628 --- /dev/null +++ b/obj2c.py @@ -0,0 +1,107 @@ +""" +This module generates a fragment of C code, in the style of that found in +the ``model.inc.c`` files, that encodes the geometry of the model specified +by the Wavefront OBJ file. + +Example: + Specify the path to the ``.obj`` file and pipe the output of the script + into the desired destination ``.c`` file. + + $ python obj2c.py left_hand_closed.obj > left_hand_closed.inc.c + +This is a work in progress and it currently has some serious limitations: + * It only encodes the geometry information of the OBJ file, so no + texture mapping or any other info. + * The generated fragment of C code has to be manually pasted into the + desired source file. Make sure that the name of the Gfx structure + you're pasting matches the one you're replacing. + * It hasn't been properly tested. + +""" + +def parse(filename): + from os.path import basename, splitext + from re import sub + + # WARNIGN! + # `gfx_name` is just a guess. You have to manually check that the name + # of the Gfx structure you're pasting matches the one you're replacing. + clean = lambda fn: sub('\W|^(?=\d)','_', fn) + gfx_name = clean(splitext(basename(filename))[0]) + gfx_vertices = [] + gfx_normals = [] + vertex_to_normal = {} + gfx_v_count = 0 + + vtx_name = '' + vtx_faces = [] + vtx_v_count = 0 + + output_upper = [] + output_lower = [f'const Gfx {gfx_name}[] = {{'] + + with open(filename, 'r') as obj: + for line in obj: + line = line.strip() + + if line.startswith('v '): + coordinates = [eval(x) for x in line.split()[1:4]] + gfx_vertices.append(coordinates) + vtx_v_count += 1 + gfx_v_count += 1 + + if line.startswith('vn '): + coordinates = [eval(x) for x in line.split()[1:4]] + gfx_normals.append([_encode_normal(x) for x in coordinates]) + + if line.startswith('g '): + vtx_name = line.split()[1] + + if line.startswith('f '): + pairs = [pair.split('//') for pair in line.split()[1:4]] + vtx_faces.append([int(pair[0]) for pair in pairs]) + for (x, y) in pairs: + vertex_to_normal[int(x) - 1] = int(y) - 1 + + if line.startswith('# ') and line.endswith('faces'): + output_upper.append(f'static const Vtx {vtx_name}[] = {{') + for i in range(gfx_v_count - vtx_v_count, gfx_v_count): + v_string = '[{}, {}, {}]'.format(*gfx_vertices[i]) + n_string = '[{}, {}, {}, 0x00]'.format(*gfx_normals[vertex_to_normal[i]]) + combined = f' [[{v_string}, 0, [0, 0], {n_string}]],' + output_upper.append(combined.replace('[', '{').replace(']', '}')) + + output_upper.append('};\n') + output_lower.append(f' gsSPVertex({vtx_name}, {vtx_v_count}, 0),') + + n = len(vtx_faces) + correction = vtx_v_count - gfx_v_count - 1 + for i in range(int(n / 2)): + f1 = [vtx_faces[2 * i][j] + correction for j in range(3)] + f2 = [vtx_faces[2 * i + 1][j] + correction for j in range(3)] + output_lower.append(' gsSP2Triangles({}, {}, {}, 0x0, {}, {}, {}, 0x0),'.format(*f1, *f2)) + + if n % 2 != 0: + f3 = [vtx_faces[-1][j] + correction for j in range(3)] + output_lower.append(' gsSP1Triangle({}, {}, {}, 0x0),'.format(*f3)) + + vtx_v_count = 0 + vtx_faces = [] + + output_lower.append(' gsSPEndDisplayList(),') + output_lower.append('};') + + for line in output_upper + output_lower: + print(line) + +def _encode_normal(x): + x *= 127 + if x <= 0: x += 255 + return hex(int(x)) + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('filename', help = 'filename of the .obj file to parse') + args = parser.parse_args() + parse(args.filename) \ No newline at end of file diff --git a/pisetup.sh b/pisetup.sh index cfd97320..5b34c233 100755 --- a/pisetup.sh +++ b/pisetup.sh @@ -1,4 +1,8 @@ #!/bin/bash +# Super Mario 64 PC on Raspberry Pi +# Find latest updates and code on https://www.github.com/sm64pc/sm64pc +# ToDo: Test on more Pi models with fresh Raspbian and allow existing src folders to be updated. +# clear echo "This script will assist with compiling Super Mario 64 on Raspbian 10" echo "Note that accelerated OpenGL (vc4_drm) is required for maximum performance" @@ -65,7 +69,8 @@ clear echo "Super Mario 64 RPi Initial Setup" if [[ $pi != 4 ]] -then #Dumb idea, but quick hack. We CANNOT enable VC4 for Pi4. +then #Dumb idea, but quick hack. + #We CANNOT enable VC4 for Pi4 as it uses VC6 inxinf=$(inxi -Gx) echo "Checking for pre-enabled VC4 acceleration (inxi -Gx)" @@ -119,24 +124,24 @@ fixmem=$(cat /boot/cmdline.txt | grep cma=128M) else echo "" - echo "Warning: VC4 enabled, but your Rasp Pi has 512MB or less RAM" + echo "Warning: VC4 enabled, but your RasPi has 512MB or less RAM" echo "To ensure VC4_DRM and game compilation is succesful, video memory will be reduced" echo "gpu_mem=48M (config.txt) | cma=128M (cmdline.txt) will be written to /boot " echo "" read -p "Fix mem? (Y/N): " fixmem - if [[ $fixmem =~ "Y" ]] - then - sudo sh -c "echo 'gpu_mem=48' >> /boot/config.txt" - sudo sh -c "echo 'cma=128M' >> /boot/cmdline.txt" - sync - echo "Wrote configuration changes to SD card." - sleep 2 - else - echo "" - echo "Warning: Compilation freezes & errors are likely to occur on your Pi" - echo "" - sleep 3 + if [[ $fixmem =~ "Y" ]] + then + sudo sh -c "echo 'gpu_mem=48' >> /boot/config.txt" + sudo sh -c "echo 'cma=128M' >> /boot/cmdline.txt" + sync + echo "Wrote configuration changes to SD card." + sleep 2 + else + echo "" + echo "Warning: Compilation freezes & errors are likely to occur on your Pi" + echo "" + sleep 3 fi fi fi @@ -149,11 +154,12 @@ read -p "Reboot to enable changes? (Y/N): " fixstart if [[ $fixstart =~ "Y" ]] then echo "" - echo "Rebooting RasPi!" + echo "Rebooting RasPi in 4 seconds! Press Control-C to cancel." + sleep 4 sudo reboot fi -fi -fi #This should never run on a Pi 4 + fi +fi # "Should never run on a Pi 4" part ends here #-------------------------------------------------------------------------------- #------------------------------------------------------------------------------- @@ -182,7 +188,7 @@ fi #------------------------------------------------------------------------------------- clear echo "Optional: Compile SDL2 with 'KMSDRM' for enhanced performance?" -echo "KMSDRM allows Super Mario 64 to be run without GUI/Desktop enabled on boot." +echo "KMSDRM allows Super Mario 64 to be run without GUI/Desktop (Xorg) enabled on boot" echo "" echo "Warning: Compile could take up to an hour on older Raspberry Pi models" read -p "Proceed? (Y/N): " sdlcomp @@ -293,7 +299,12 @@ echo "" echo "Step 3. Compiling Super Mario 64 for the Raspberry Pi" echo "" echo "Warning: Super Mario 64 assets are required in order to compile" +if [[ $curdir ==1 ]] +then +echo "Assets will be extracted from "$PWD" " +else echo "Assets will be extracted from $HOME/src/sm64pi/sm64pc/baserom.(us/eu/jp).z64 " +fi if [[ $curdir == 1 ]] then @@ -312,12 +323,15 @@ else echo "" echo "Please satisfy this requirement before continuing." echo "Exiting Super Mario 64 RasPi setup and compilation script." +echo "" echo "Note: Re-run script once baserom(s) are inserted into" if [[ $curdir == 1 ]] then echo $PWD +echo "" else +echo "" echo $HOME/src/sm64pi/sm64pc/ fi diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index edd52478..539f3a32 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -98,10 +98,10 @@ static void cur_obj_bhv_stack_push(uintptr_t bhvAddr) { // Retrieve the last behavior command address from the object's behavior stack. static uintptr_t cur_obj_bhv_stack_pop(void) { uintptr_t bhvAddr; - + gCurrentObject->bhvStackIndex--; bhvAddr = gCurrentObject->bhvStack[gCurrentObject->bhvStackIndex]; - + return bhvAddr; } @@ -115,7 +115,7 @@ static void stub_behavior_script_1(void) { // Usage: HIDE() static s32 bhv_cmd_hide(void) { cur_obj_hide(); - + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -124,7 +124,7 @@ static s32 bhv_cmd_hide(void) { // Usage: DISABLE_RENDERING() static s32 bhv_cmd_disable_rendering(void) { gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -133,7 +133,15 @@ static s32 bhv_cmd_disable_rendering(void) { // Usage: BILLBOARD() static s32 bhv_cmd_billboard(void) { gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD; - + + gCurBhvCommand++; + return BHV_PROC_CONTINUE; +} + +// Command 0x +static s32 bhv_cmd_cylboard(void) { + gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_CYLBOARD; + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -142,9 +150,9 @@ static s32 bhv_cmd_billboard(void) { // Usage: SET_MODEL(modelID) static s32 bhv_cmd_set_model(void) { s32 modelID = BHV_CMD_GET_2ND_S16(0); - + gCurrentObject->header.gfx.sharedChild = gLoadedGraphNodes[modelID]; - + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -216,7 +224,7 @@ static s32 bhv_cmd_break_unused(void) { static s32 bhv_cmd_call(void) { const BehaviorScript *jumpAddress; gCurBhvCommand++; - + cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(1)); // Store address of the next bhv command in the stack. jumpAddress = segmented_to_virtual(BHV_CMD_GET_VPTR(0)); gCurBhvCommand = jumpAddress; // Jump to the new address. @@ -300,7 +308,7 @@ static s32 bhv_cmd_begin_repeat(void) { static s32 bhv_cmd_end_repeat(void) { u32 count = cur_obj_bhv_stack_pop(); // Retrieve loop count from the stack. count--; - + if (count != 0) { gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop // Save address and count to the stack again @@ -320,7 +328,7 @@ static s32 bhv_cmd_end_repeat(void) { static s32 bhv_cmd_end_repeat_continue(void) { u32 count = cur_obj_bhv_stack_pop(); count--; - + if (count != 0) { gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop // Save address and count to the stack again @@ -546,7 +554,7 @@ static s32 bhv_cmd_drop_to_floor(void) { f32 x = gCurrentObject->oPosX; f32 y = gCurrentObject->oPosY; f32 z = gCurrentObject->oPosZ; - + f32 floor = find_floor_height(x, y + 200.0f, z); gCurrentObject->oPosY = floor; gCurrentObject->oMoveFlags |= OBJ_MOVE_ON_GROUND; @@ -665,7 +673,7 @@ static s32 bhv_cmd_nop_4(void) { static s32 bhv_cmd_begin(void) { // These objects were likely very early objects, which is why this code is here // instead of in the respective behavior scripts. - + // Initiate the room if the object is a haunted chair or the mad piano. if (cur_obj_has_behavior(bhvHauntedChair)) { bhv_init_room(); @@ -696,7 +704,7 @@ static void bhv_cmd_set_int_random_from_table(s32 tableSize) { } cur_obj_set_int(field, table[(s32)(tableSize * random_float())]); - + // Does not increment gCurBhvCommand or return a bhv status } @@ -719,7 +727,7 @@ static s32 bhv_cmd_set_int_random_from_table(void) { // Set the field to a random entry of the table. cur_obj_set_int(field, table[(s32)(tableSize * random_float())]); - + gCurBhvCommand += (tableSize / 2) + 1; return BHV_PROC_CONTINUE; } @@ -729,9 +737,9 @@ static s32 bhv_cmd_set_int_random_from_table(void) { // Usage: LOAD_COLLISION_DATA(collisionData) static s32 bhv_cmd_load_collision_data(void) { u32 *collisionData = segmented_to_virtual(BHV_CMD_GET_VPTR(1)); - + gCurrentObject->collisionData = collisionData; - + gCurBhvCommand += 2; return BHV_PROC_CONTINUE; } @@ -742,7 +750,7 @@ static s32 bhv_cmd_set_home(void) { gCurrentObject->oHomeX = gCurrentObject->oPosX; gCurrentObject->oHomeY = gCurrentObject->oPosY; gCurrentObject->oHomeZ = gCurrentObject->oPosZ; - + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -815,9 +823,9 @@ static s32 bhv_cmd_parent_bit_clear(void) { // Usage: SPAWN_WATER_DROPLET(dropletParams) static s32 bhv_cmd_spawn_water_droplet(void) { struct WaterDropletParams *dropletParams = BHV_CMD_GET_VPTR(1); - + spawn_water_droplet(gCurrentObject, dropletParams); - + gCurBhvCommand += 2; return BHV_PROC_CONTINUE; } @@ -842,62 +850,63 @@ void stub_behavior_script_2(void) { typedef s32 (*BhvCommandProc)(void); static BhvCommandProc BehaviorCmdTable[] = { - bhv_cmd_begin, - bhv_cmd_delay, - bhv_cmd_call, - bhv_cmd_return, - bhv_cmd_goto, - bhv_cmd_begin_repeat, - bhv_cmd_end_repeat, - bhv_cmd_end_repeat_continue, - bhv_cmd_begin_loop, - bhv_cmd_end_loop, - bhv_cmd_break, - bhv_cmd_break_unused, - bhv_cmd_call_native, - bhv_cmd_add_float, - bhv_cmd_set_float, - bhv_cmd_add_int, - bhv_cmd_set_int, - bhv_cmd_or_int, - bhv_cmd_bit_clear, - bhv_cmd_set_int_rand_rshift, - bhv_cmd_set_random_float, - bhv_cmd_set_random_int, - bhv_cmd_add_random_float, - bhv_cmd_add_int_rand_rshift, - bhv_cmd_nop_1, - bhv_cmd_nop_2, - bhv_cmd_nop_3, - bhv_cmd_set_model, - bhv_cmd_spawn_child, - bhv_cmd_deactivate, - bhv_cmd_drop_to_floor, - bhv_cmd_sum_float, - bhv_cmd_sum_int, - bhv_cmd_billboard, - bhv_cmd_hide, - bhv_cmd_set_hitbox, - bhv_cmd_nop_4, - bhv_cmd_delay_var, - bhv_cmd_begin_repeat_unused, - bhv_cmd_load_animations, - bhv_cmd_animate, - bhv_cmd_spawn_child_with_param, - bhv_cmd_load_collision_data, - bhv_cmd_set_hitbox_with_offset, - bhv_cmd_spawn_obj, - bhv_cmd_set_home, - bhv_cmd_set_hurtbox, - bhv_cmd_set_interact_type, - bhv_cmd_set_obj_physics, - bhv_cmd_set_interact_subtype, - bhv_cmd_scale, - bhv_cmd_parent_bit_clear, - bhv_cmd_animate_texture, - bhv_cmd_disable_rendering, - bhv_cmd_set_int_unused, - bhv_cmd_spawn_water_droplet, + bhv_cmd_begin, //00 + bhv_cmd_delay, //01 + bhv_cmd_call, //02 + bhv_cmd_return, //03 + bhv_cmd_goto, //04 + bhv_cmd_begin_repeat, //05 + bhv_cmd_end_repeat, //06 + bhv_cmd_end_repeat_continue, //07 + bhv_cmd_begin_loop, //08 + bhv_cmd_end_loop, //09 + bhv_cmd_break, //0A + bhv_cmd_break_unused, //0B + bhv_cmd_call_native, //0C + bhv_cmd_add_float, //0D + bhv_cmd_set_float, //0E + bhv_cmd_add_int, //0F + bhv_cmd_set_int, //10 + bhv_cmd_or_int, //11 + bhv_cmd_bit_clear, //12 + bhv_cmd_set_int_rand_rshift, //13 + bhv_cmd_set_random_float, //14 + bhv_cmd_set_random_int, //15 + bhv_cmd_add_random_float, //16 + bhv_cmd_add_int_rand_rshift, //17 + bhv_cmd_nop_1, //18 + bhv_cmd_nop_2, //19 + bhv_cmd_nop_3, //1A + bhv_cmd_set_model, //1B + bhv_cmd_spawn_child, //1C + bhv_cmd_deactivate, //1D + bhv_cmd_drop_to_floor, //1E + bhv_cmd_sum_float, //1F + bhv_cmd_sum_int, //20 + bhv_cmd_billboard, //21 + bhv_cmd_hide, //22 + bhv_cmd_set_hitbox, //23 + bhv_cmd_nop_4, //24 + bhv_cmd_delay_var, //25 + bhv_cmd_begin_repeat_unused, //26 + bhv_cmd_load_animations, //27 + bhv_cmd_animate, //28 + bhv_cmd_spawn_child_with_param, //29 + bhv_cmd_load_collision_data, //2A + bhv_cmd_set_hitbox_with_offset, //2B + bhv_cmd_spawn_obj, //2C + bhv_cmd_set_home, //2D + bhv_cmd_set_hurtbox, //2E + bhv_cmd_set_interact_type, //2F + bhv_cmd_set_obj_physics, //30 + bhv_cmd_set_interact_subtype, //31 + bhv_cmd_scale, //32 + bhv_cmd_parent_bit_clear, //33 + bhv_cmd_animate_texture, //34 + bhv_cmd_disable_rendering, //35 + bhv_cmd_set_int_unused, //36 + bhv_cmd_spawn_water_droplet, //37 + bhv_cmd_cylboard //38 }; // Execute the behavior script of the current object, process the object flags, and other miscellaneous code for updating objects. @@ -987,11 +996,15 @@ void cur_obj_update(void) { } else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) { if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) { // If the object has a render distance, check if it should be shown. +#ifndef NODRAWINGDISTANCE if (distanceFromMario > gCurrentObject->oDrawingDistance) { // Out of render distance, hide the object. gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; } else if (gCurrentObject->oHeldState == HELD_FREE) { +#else + if (distanceFromMario <= gCurrentObject->oDrawingDistance && gCurrentObject->oHeldState == HELD_FREE) { +#endif // In render distance (and not being held), show the object. gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; diff --git a/src/engine/graph_node.h b/src/engine/graph_node.h index ac05ed14..c99e5f32 100644 --- a/src/engine/graph_node.h +++ b/src/engine/graph_node.h @@ -30,12 +30,13 @@ extern Vec3s gVec3sOne; #define GRAPH_RENDER_Z_BUFFER (1 << 3) #define GRAPH_RENDER_INVISIBLE (1 << 4) #define GRAPH_RENDER_HAS_ANIMATION (1 << 5) +#define GRAPH_RENDER_CYLBOARD (1 << 6) // Whether the node type has a function pointer of type GraphNodeFunc #define GRAPH_NODE_TYPE_FUNCTIONAL 0x100 // Type used for Bowser and an unused geo function in obj_behaviors.c -#define GRAPH_NODE_TYPE_400 0x400 +#define GRAPH_NODE_TYPE_400 0x400 // The discriminant for different types of geo nodes #define GRAPH_NODE_TYPE_ROOT 0x001 diff --git a/src/engine/math_util.c b/src/engine/math_util.c index 8643f5e0..3f027ed5 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -377,6 +377,31 @@ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) { dest[3][3] = 1; } +void mtxf_cylboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) { //straight up mtxf_billboard but minus the dest[1][n] lines. transform for cylindrical billboards + dest[0][0] = coss(angle); + dest[0][1] = sins(angle); + dest[0][2] = 0; + dest[0][3] = 0; + + dest[1][0] = mtx[1][0]; + dest[1][1] = mtx[1][1]; + dest[1][2] = mtx[1][2]; + dest[1][3] = 0; + + dest[2][0] = 0; + dest[2][1] = 0; + dest[2][2] = 1; + dest[2][3] = 0; + + dest[3][0] = + mtx[0][0] * position[0] + mtx[1][0] * position[1] + mtx[2][0] * position[2] + mtx[3][0]; + dest[3][1] = + mtx[0][1] * position[0] + mtx[1][1] * position[1] + mtx[2][1] * position[2] + mtx[3][1]; + dest[3][2] = + mtx[0][2] * position[0] + mtx[1][2] * position[1] + mtx[2][2] * position[2] + mtx[3][2]; + dest[3][3] = 1; +} + /** * Set 'dest' to a transformation matrix that aligns an object with the terrain * based on the normal. Used for enemies. diff --git a/src/engine/math_util.h b/src/engine/math_util.h index 650fe973..b8b7f1b8 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -56,6 +56,7 @@ void mtxf_lookat(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d); void mtxf_rotate_zxy_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c); void mtxf_rotate_xyz_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c); void mtxf_billboard(f32 mtx1[4][4], f32 mtx2[4][4], Vec3f c, s16 d); +void mtxf_cylboard(f32 mtx1[4][4], f32 mtx2[4][4], Vec3f c, s16 d); void mtxf_align_terrain_normal(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d); void mtxf_align_terrain_triangle(f32 mtx[4][4], Vec3f b, s16 c, f32 d); void mtxf_mul(f32 dest[4][4], f32 a[4][4], f32 b[4][4]); diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index da354352..7a7da511 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -786,9 +786,13 @@ void load_object_collision_model(void) { } } +#ifndef NODRAWINGDISTANCE if (marioDist < gCurrentObject->oDrawingDistance) { +#endif gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; +#ifndef NODRAWINGDISTANCE } else { gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; } +#endif } diff --git a/src/game/behaviors/bub.inc.c b/src/game/behaviors/bub.inc.c index e8e63096..7bf71690 100644 --- a/src/game/behaviors/bub.inc.c +++ b/src/game/behaviors/bub.inc.c @@ -8,11 +8,15 @@ void bub_spawner_act_0(void) { s32 i; s32 sp18 = o->oBirdChirpChirpUnkF4; +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 1500.0f) { +#endif for (i = 0; i < sp18; i++) spawn_object(o, MODEL_BUB, bhvBub); o->oAction = 1; +#ifndef NODRAWINGDISTANCE } +#endif } void bub_spawner_act_1(void) { diff --git a/src/game/behaviors/cannon.inc.c b/src/game/behaviors/cannon.inc.c index 5f55ca83..a8cfcab3 100644 --- a/src/game/behaviors/cannon.inc.c +++ b/src/game/behaviors/cannon.inc.c @@ -17,9 +17,11 @@ void opened_cannon_act_0(void) { cur_obj_enable_rendering(); cur_obj_become_tangible(); } + cur_obj_become_tangible(); + cur_obj_enable_rendering(); if (o->oDistanceToMario < 500.0f) { - cur_obj_become_tangible(); - cur_obj_enable_rendering(); + //cur_obj_become_tangible(); + //cur_obj_enable_rendering(); if (o->oInteractStatus & INT_STATUS_INTERACTED && (!(o->oInteractStatus & INT_STATUS_TOUCHED_BOB_OMB))) // bob-omb explodes when it gets into a cannon @@ -30,8 +32,8 @@ void opened_cannon_act_0(void) { } else o->oInteractStatus = 0; } else { - cur_obj_become_intangible(); - cur_obj_disable_rendering(); + //cur_obj_become_intangible(); + //cur_obj_disable_rendering(); o->oCannonUnk10C = 0; } } diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c index a77c5d5f..9b9c3423 100644 --- a/src/game/behaviors/chain_chomp.inc.c +++ b/src/game/behaviors/chain_chomp.inc.c @@ -53,7 +53,9 @@ static void chain_chomp_act_uninitialized(void) { struct ChainSegment *segments; s32 i; +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 3000.0f) { +#endif segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); if (segments != NULL) { // Each segment represents the offset of a chain part to the pivot. @@ -81,7 +83,9 @@ static void chain_chomp_act_uninitialized(void) { cur_obj_unhide(); } } +#ifndef NODRAWINGDISTANCE } +#endif } /** @@ -359,10 +363,12 @@ static void chain_chomp_act_move(void) { f32 maxDistToPivot; // Unload chain if mario is far enough +#ifndef NODRAWINGDISTANCE if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) { o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN; o->oForwardVel = o->oVelY = 0.0f; } else { +#endif cur_obj_update_floor_and_walls(); switch (o->oChainChompReleaseStatus) { @@ -446,7 +452,9 @@ static void chain_chomp_act_move(void) { o->oGravity = -4.0f; o->oChainChompTargetPitch = -0x3000; } +#ifndef NODRAWINGDISTANCE } +#endif } /** diff --git a/src/game/behaviors/cloud.inc.c b/src/game/behaviors/cloud.inc.c index e5cb9bed..fa82e3f4 100644 --- a/src/game/behaviors/cloud.inc.c +++ b/src/game/behaviors/cloud.inc.c @@ -47,10 +47,14 @@ static void cloud_act_spawn_parts(void) { * Wait for mario to approach, then unhide and enter the spawn parts action. */ static void cloud_act_fwoosh_hidden(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif cur_obj_unhide(); o->oAction = CLOUD_ACT_SPAWN_PARTS; +#ifndef NODRAWINGDISTANCE } +#endif } /** @@ -58,9 +62,11 @@ static void cloud_act_fwoosh_hidden(void) { * long enough, blow wind at him. */ static void cloud_fwoosh_update(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 2500.0f) { o->oAction = CLOUD_ACT_UNLOAD; } else { +#endif if (o->oCloudBlowing) { o->header.gfx.scale[0] += o->oCloudGrowSpeed; @@ -95,7 +101,9 @@ static void cloud_fwoosh_update(void) { } cur_obj_scale(o->header.gfx.scale[0]); +#ifndef NODRAWINGDISTANCE } +#endif } /** diff --git a/src/game/behaviors/coin.inc.c b/src/game/behaviors/coin.inc.c index 913c5834..05619b96 100644 --- a/src/game/behaviors/coin.inc.c +++ b/src/game/behaviors/coin.inc.c @@ -184,17 +184,23 @@ void bhv_coin_formation_loop(void) { s32 bitIndex; switch (o->oAction) { case 0: +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif for (bitIndex = 0; bitIndex < 8; bitIndex++) { if (!(o->oCoinUnkF4 & (1 << bitIndex))) spawn_coin_in_formation(bitIndex, o->oBehParams2ndByte); } o->oAction++; +#ifndef NODRAWINGDISTANCE } +#endif break; case 1: +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 2100.0f) o->oAction++; +#endif break; case 2: o->oAction = 0; diff --git a/src/game/behaviors/enemy_lakitu.inc.c b/src/game/behaviors/enemy_lakitu.inc.c index 056c3f13..cacd732f 100644 --- a/src/game/behaviors/enemy_lakitu.inc.c +++ b/src/game/behaviors/enemy_lakitu.inc.c @@ -24,12 +24,16 @@ static struct ObjectHitbox sEnemyLakituHitbox = { * Wait for mario to approach, then spawn the cloud and become visible. */ static void enemy_lakitu_act_uninitialized(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif spawn_object_relative_with_scale(CLOUD_BP_LAKITU_CLOUD, 0, 0, 0, 2.0f, o, MODEL_MIST, bhvCloud); cur_obj_unhide(); o->oAction = ENEMY_LAKITU_ACT_MAIN; +#ifndef NODRAWINGDISTANCE } +#endif } /** diff --git a/src/game/behaviors/fish.inc.c b/src/game/behaviors/fish.inc.c index 839ab8d6..f652ef47 100644 --- a/src/game/behaviors/fish.inc.c +++ b/src/game/behaviors/fish.inc.c @@ -42,7 +42,9 @@ void fish_act_spawn(void) { * If the current level is Secret Aquarium, ignore this requirement. * Fish moves at random with a max-range of 700.0f. */ +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) { +#endif for (i = 0; i < schoolQuantity; i++) { fishObject = spawn_object(o, model, bhvFish); fishObject->oBehParams2ndByte = o->oBehParams2ndByte; @@ -50,7 +52,9 @@ void fish_act_spawn(void) { obj_translate_xyz_random(fishObject, 700.0f); } o->oAction = FISH_ACT_ACTIVE; +#ifndef NODRAWINGDISTANCE } +#endif } /** diff --git a/src/game/behaviors/goomba.inc.c b/src/game/behaviors/goomba.inc.c index 2dab2fec..bf47dda1 100644 --- a/src/game/behaviors/goomba.inc.c +++ b/src/game/behaviors/goomba.inc.c @@ -78,7 +78,9 @@ void bhv_goomba_triplet_spawner_update(void) { // If mario is close enough and the goombas aren't currently loaded, then // spawn them if (o->oAction == GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 3000.0f) { +#endif // The spawner is capable of spawning more than 3 goombas, but this // is not used in the game dAngle = @@ -98,11 +100,13 @@ void bhv_goomba_triplet_spawner_update(void) { } o->oAction += 1; +#ifndef NODRAWINGDISTANCE } } else if (o->oDistanceToMario > 4000.0f) { // If mario is too far away, enter the unloaded action. The goombas // will detect this and unload themselves o->oAction = GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED; +#endif } } diff --git a/src/game/behaviors/heave_ho.inc.c b/src/game/behaviors/heave_ho.inc.c index 662bb0bd..2cbd1f0e 100644 --- a/src/game/behaviors/heave_ho.inc.c +++ b/src/game/behaviors/heave_ho.inc.c @@ -72,8 +72,12 @@ void heave_ho_act_3(void) { } void heave_ho_act_0(void) { - cur_obj_set_pos_to_home(); +#ifndef NODRAWINGDISTANCE if (find_water_level(o->oPosX, o->oPosZ) < o->oPosY && o->oDistanceToMario < 4000.0f) { +#else + if (find_water_level(o->oPosX, o->oPosZ) < (o->oPosY - 50.0f)) { +#endif + cur_obj_set_pos_to_home(); cur_obj_become_tangible(); cur_obj_unhide(); o->oAction = 1; diff --git a/src/game/behaviors/king_bobomb.inc.c b/src/game/behaviors/king_bobomb.inc.c index 63a75755..af1cb0a6 100644 --- a/src/game/behaviors/king_bobomb.inc.c +++ b/src/game/behaviors/king_bobomb.inc.c @@ -295,10 +295,14 @@ void king_bobomb_move(void) { cur_obj_move_using_fvel_and_gravity(); cur_obj_call_action_function(sKingBobombActions); exec_anim_sound_state(sKingBobombSoundStates); +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 5000.0f) +#endif cur_obj_enable_rendering(); +#ifndef NODRAWINGDISTANCE else cur_obj_disable_rendering(); +#endif } void bhv_king_bobomb_loop(void) { diff --git a/src/game/behaviors/lll_floating_wood_piece.inc.c b/src/game/behaviors/lll_floating_wood_piece.inc.c index a484471c..fad31f3f 100644 --- a/src/game/behaviors/lll_floating_wood_piece.inc.c +++ b/src/game/behaviors/lll_floating_wood_piece.inc.c @@ -14,18 +14,24 @@ void bhv_lll_floating_wood_bridge_loop(void) { s32 i; switch (o->oAction) { case 0: +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2500.0f) { +#endif for (i = 1; i < 4; i++) { sp3C = spawn_object_relative(0, (i - 2) * 300, 0, 0, o, MODEL_LLL_WOOD_BRIDGE, bhvLllWoodPiece); sp3C->oLllWoodPieceUnkF4 = i * 4096; } o->oAction = 1; +#ifndef NODRAWINGDISTANCE } +#endif break; case 1: +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 2600.0f) o->oAction = 2; +#endif break; case 2: o->oAction = 0; diff --git a/src/game/behaviors/lll_rotating_hex_flame.inc.c b/src/game/behaviors/lll_rotating_hex_flame.inc.c index efabfca8..fc707330 100644 --- a/src/game/behaviors/lll_rotating_hex_flame.inc.c +++ b/src/game/behaviors/lll_rotating_hex_flame.inc.c @@ -30,7 +30,9 @@ void fire_bar_spawn_flames(s16 a0) { } void fire_bar_act_0(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 3000.0f) +#endif o->oAction = 1; } @@ -45,8 +47,10 @@ void fire_bar_act_1(void) { void fire_bar_act_2(void) { o->oAngleVelYaw = -0x100; o->oMoveAngleYaw += o->oAngleVelYaw; +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 3200.0f) o->oAction = 3; +#endif } void fire_bar_act_3(void) { diff --git a/src/game/behaviors/piranha_plant.inc.c b/src/game/behaviors/piranha_plant.inc.c index e8abe089..328f4518 100644 --- a/src/game/behaviors/piranha_plant.inc.c +++ b/src/game/behaviors/piranha_plant.inc.c @@ -328,7 +328,7 @@ void (*TablePiranhaPlantActions[])(void) = { */ void bhv_piranha_plant_loop(void) { cur_obj_call_action_function(TablePiranhaPlantActions); - + #ifndef NODRAWINGDISTANCE // In WF, hide all Piranha Plants once high enough up. if (gCurrLevelNum == LEVEL_WF) { if (gMarioObject->oPosY > 3400.0f) @@ -336,5 +336,6 @@ void bhv_piranha_plant_loop(void) { else cur_obj_unhide(); } + #endif o->oInteractStatus = 0; } diff --git a/src/game/behaviors/pokey.inc.c b/src/game/behaviors/pokey.inc.c index df5d11f5..cfcc92c2 100644 --- a/src/game/behaviors/pokey.inc.c +++ b/src/game/behaviors/pokey.inc.c @@ -151,7 +151,9 @@ static void pokey_act_uninitialized(void) { s32 i; s16 partModel; +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif partModel = MODEL_POKEY_HEAD; for (i = 0; i < 5; i++) { @@ -170,7 +172,9 @@ static void pokey_act_uninitialized(void) { o->oPokeyNumAliveBodyParts = 5; o->oPokeyBottomBodyPartSize = 1.0f; o->oAction = POKEY_ACT_WANDER; +#ifndef NODRAWINGDISTANCE } +#endif } /** @@ -185,9 +189,11 @@ static void pokey_act_wander(void) { if (o->oPokeyNumAliveBodyParts == 0) { obj_mark_for_deletion(o); +#ifndef NODRAWINGDISTANCE } else if (o->oDistanceToMario > 2500.0f) { o->oAction = POKEY_ACT_UNLOAD_PARTS; o->oForwardVel = 0.0f; +#endif } else { treat_far_home_as_mario(1000.0f); cur_obj_update_floor_and_walls(); diff --git a/src/game/behaviors/sl_walking_penguin.inc.c b/src/game/behaviors/sl_walking_penguin.inc.c index f5b60a8a..59428acb 100644 --- a/src/game/behaviors/sl_walking_penguin.inc.c +++ b/src/game/behaviors/sl_walking_penguin.inc.c @@ -97,7 +97,9 @@ void bhv_sl_walking_penguin_loop(void) { } cur_obj_move_standard(-78); - if (!cur_obj_hide_if_mario_far_away_y(1000.0f)) +#ifndef NODRAWINGDISTANCE + if (!cur_obj_hide_if_mario_far_away_y(1000.0f)) +#endif play_penguin_walking_sound(PENGUIN_WALK_BIG); // Adjust the position to get a point better lined up with the visual model, for stopping the wind. diff --git a/src/game/behaviors/snufit.inc.c b/src/game/behaviors/snufit.inc.c index f3a0c9ef..76e78c09 100644 --- a/src/game/behaviors/snufit.inc.c +++ b/src/game/behaviors/snufit.inc.c @@ -180,7 +180,11 @@ void bhv_snufit_loop(void) { void bhv_snufit_balls_loop(void) { // If far from Mario or in a different room, despawn. if ((o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) +#ifndef NODRAWINGDISTANCE || (o->oTimer != 0 && o->oDistanceToMario > 1500.0f)) { +#else + || (o->oTimer != 0)) { +#endif obj_mark_for_deletion(o); } diff --git a/src/game/behaviors/triplet_butterfly.inc.c b/src/game/behaviors/triplet_butterfly.inc.c index 1c2b9265..3d16a9d2 100644 --- a/src/game/behaviors/triplet_butterfly.inc.c +++ b/src/game/behaviors/triplet_butterfly.inc.c @@ -54,9 +54,11 @@ static void triplet_butterfly_act_init(void) { } static void triplet_butterfly_act_wander(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 1500.0f) { obj_mark_for_deletion(o); } else { +#endif approach_f32_ptr(&o->oTripletButterflySpeed, 8.0f, 0.5f); if (o->oTimer < 60) { o->oTripletButterflyTargetYaw = cur_obj_angle_to_home(); @@ -82,7 +84,9 @@ static void triplet_butterfly_act_wander(void) { obj_move_pitch_approach(o->oTripletButterflyTargetPitch, 400); cur_obj_rotate_yaw_toward(o->oTripletButterflyTargetYaw, random_linear_offset(400, 800)); +#ifndef NODRAWINGDISTANCE } +#endif } static void triplet_butterfly_act_activate(void) { diff --git a/src/game/behaviors/water_bomb_cannon.inc.c b/src/game/behaviors/water_bomb_cannon.inc.c index 8e9ba33b..fb82e43c 100644 --- a/src/game/behaviors/water_bomb_cannon.inc.c +++ b/src/game/behaviors/water_bomb_cannon.inc.c @@ -38,19 +38,27 @@ void bhv_bubble_cannon_barrel_loop(void) { } void water_bomb_cannon_act_0(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles); cur_obj_unhide(); o->oAction = 1; o->oMoveAnglePitch = o->oWaterCannonUnkFC = 0x1C00; +#ifndef NODRAWINGDISTANCE } +#endif } void water_bomb_cannon_act_1(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 2500.0f) { o->oAction = 2; } else if (o->oBehParams2ndByte == 0) { +#else + if (o->oBehParams2ndByte == 0) { +#endif if (o->oWaterCannonUnkF4 != 0) { o->oWaterCannonUnkF4 -= 1; } else { diff --git a/src/game/behaviors/whirlpool.inc.c b/src/game/behaviors/whirlpool.inc.c index 405e0518..5aebebd2 100644 --- a/src/game/behaviors/whirlpool.inc.c +++ b/src/game/behaviors/whirlpool.inc.c @@ -35,7 +35,9 @@ void whirpool_orient_graph(void) { } void bhv_whirlpool_loop(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 5000.0f) { +#endif o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; // not sure if actually an array @@ -52,10 +54,12 @@ void bhv_whirlpool_loop(void) { whirpool_orient_graph(); o->oFaceAngleYaw += 0x1F40; +#ifndef NODRAWINGDISTANCE } else { o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT] = 0; } +#endif cur_obj_play_sound_1(SOUND_ENV_WATER); diff --git a/src/game/behaviors/whomp.inc.c b/src/game/behaviors/whomp.inc.c index c9ebca0a..c7c39241 100644 --- a/src/game/behaviors/whomp.inc.c +++ b/src/game/behaviors/whomp.inc.c @@ -246,10 +246,14 @@ void bhv_whomp_loop(void) { cur_obj_call_action_function(sWhompActions); cur_obj_move_standard(-20); if (o->oAction != 9) { +#ifndef NODRAWINGDISTANCE + // o->oBehParams2ndByte here seems to be a flag + // indicating whether this is a normal or king whomp if (o->oBehParams2ndByte != 0) cur_obj_hide_if_mario_far_away_y(2000.0f); else cur_obj_hide_if_mario_far_away_y(1000.0f); +#endif load_object_collision_model(); } } diff --git a/src/game/bettercamera.h b/src/game/bettercamera.h index b3355ef6..ea814dd7 100644 --- a/src/game/bettercamera.h +++ b/src/game/bettercamera.h @@ -26,14 +26,9 @@ enum newcam_flagvalues }; -extern void newcam_display_options(void); -extern void newcam_check_pause_buttons(void); extern void newcam_init_settings(void); -extern void newcam_save_settings(void); -extern void newcam_render_option_text(void); extern void newcam_diagnostics(void); -extern u8 newcam_option_open; extern u8 newcam_sensitivityX; //How quick the camera works. extern u8 newcam_sensitivityY; diff --git a/src/game/bettercamera.inc.h b/src/game/bettercamera.inc.h index 6c9592b8..0f58b2e8 100644 --- a/src/game/bettercamera.inc.h +++ b/src/game/bettercamera.inc.h @@ -28,8 +28,6 @@ NC_MODE_NOTURN: Disables horizontal and vertical control of the camera. //#define NEWCAM_DEBUG //Some print values for puppycam. Not useful anymore, but never hurts to keep em around. //#define nosound //If for some reason you hate the concept of audio, you can disable it. //#define noaccel //Disables smooth movement of the camera with the C buttons. -#define DEGRADE 0.1f //What percent of the remaining camera movement is degraded. Default is 10% - //!Hardcoded camera angle stuff. They're essentially area boxes that when Mario is inside, will trigger some view changes. ///Don't touch this btw, unless you know what you're doing, this has to be above for religious reasons. @@ -88,6 +86,7 @@ s16 newcam_yaw_target; // The yaw value the camera tries to set itself to when t f32 newcam_turnwait; // The amount of time to wait after landing before allowing the camera to turn again f32 newcam_pan_x; f32 newcam_pan_z; +f32 newcam_degrade = 0.1f; //What percent of the remaining camera movement is degraded. Default is 10% u8 newcam_cstick_down = 0; //Just a value that triggers true when the player 2 stick is moved in 8 direction move to prevent holding it down. u8 newcam_target; @@ -105,17 +104,6 @@ u16 newcam_mode; u16 newcam_intendedmode = 0; // which camera mode the camera's going to try to be in when not forced into another. u16 newcam_modeflags; -u8 newcam_option_open = 0; -s8 newcam_option_selection = 0; -f32 newcam_option_timer = 0; -u8 newcam_option_index = 0; -u8 newcam_option_scroll = 0; -u8 newcam_option_scroll_last = 0; -u8 newcam_total = 8; //How many options there are in newcam_uptions. - -u8 newcam_options[][64] = {{NC_ANALOGUE}, {NC_MOUSE}, {NC_CAMX}, {NC_CAMY}, {NC_INVERTX}, {NC_INVERTY}, {NC_CAMC}, {NC_CAMP}}; -u8 newcam_flags[][64] = {{NC_DISABLED}, {NC_ENABLED}}; -u8 newcam_strings[][64] = {{NC_BUTTON}, {NC_BUTTON2}, {NC_OPTION}, {NC_HIGHLIGHT}}; extern int mouse_x; extern int mouse_y; @@ -166,18 +154,7 @@ void newcam_init_settings(void) newcam_invertY = (u8)configCameraInvertY; newcam_mouse = (u8)configCameraMouse; newcam_analogue = (u8)configEnableCamera; -} - -void newcam_save_settings(void) -{ - configCameraXSens = newcam_sensitivityX; - configCameraYSens = newcam_sensitivityY; - configCameraAggr = newcam_aggression; - configCameraPan = newcam_panlevel; - configCameraInvertX = newcam_invertX != 0; - configCameraInvertY = newcam_invertY != 0; - configEnableCamera = newcam_analogue != 0; - configCameraMouse = newcam_mouse != 0; + newcam_degrade = (f32)configCameraDegrade / 100.0f; } /** Mathematic calculations. This stuffs so basic even *I* understand it lol @@ -291,7 +268,7 @@ static void newcam_rotate_button(void) #ifdef noaccel newcam_yaw_acc = 0; #else - newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); + newcam_yaw_acc -= (newcam_yaw_acc*newcam_degrade); #endif } @@ -303,7 +280,7 @@ static void newcam_rotate_button(void) #ifdef noaccel newcam_tilt_acc = 0; #else - newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); + newcam_tilt_acc -= (newcam_tilt_acc*newcam_degrade); #endif newcam_framessincec[0] += 1; @@ -369,13 +346,13 @@ static void newcam_rotate_button(void) else { newcam_cstick_down = 0; - newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); + newcam_yaw_acc -= (newcam_yaw_acc*newcam_degrade); } if (ABS(gPlayer2Controller->stickY) > 20 && newcam_modeflags & NC_FLAG_YTURN) newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,(-gPlayer2Controller->stickY/4)); else - newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); + newcam_tilt_acc -= (newcam_tilt_acc*newcam_degrade); } if (newcam_mouse == 1) @@ -401,8 +378,8 @@ static void newcam_zoom_button(void) newcam_distance = newcam_distance_target; } - //When you press L and R together, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time. - if (gPlayer1Controller->buttonDown & L_TRIG && gPlayer1Controller->buttonDown & R_TRIG && newcam_modeflags & NC_FLAG_ZOOM) + //When you press L, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time. + if (gPlayer1Controller->buttonDown & L_TRIG && newcam_modeflags & NC_FLAG_ZOOM) { newcam_yaw_target = -gMarioState->faceAngle[1]-0x4000; newcam_centering = 1; @@ -670,234 +647,10 @@ void newcam_loop(struct Camera *c) newcam_position_cam(); newcam_find_fixed(); if (gMarioObject) - newcam_apply_values(c); + newcam_apply_values(c); //Just some visual information on the values of the camera. utilises ifdef because it's better at runtime. #ifdef NEWCAM_DEBUG newcam_diagnostics(); #endif // NEWCAM_DEBUG } - - - -//Displays a box. -void newcam_display_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b) -{ - gDPPipeSync(gDisplayListHead++); - gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); - gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); - gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255)); - gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1); - gDPPipeSync(gDisplayListHead++); - gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); -} - -//I actually took the time to redo this, properly. Lmao. Please don't bully me over this anymore :( -void newcam_change_setting(u8 toggle) -{ - switch (newcam_option_selection) - { - case 0: - newcam_analogue ^= 1; - break; - case 1: - newcam_mouse ^= 1; - break; - case 2: - newcam_sensitivityX = newcam_clamp(newcam_sensitivityX + toggle, 10, 250); - break; - case 3: - newcam_sensitivityY = newcam_clamp(newcam_sensitivityY + toggle, 10, 250); - break; - case 4: - newcam_invertX ^= 1; - break; - case 5: - newcam_invertY ^= 1; - break; - case 6: - newcam_aggression = newcam_clamp(newcam_aggression + toggle, 0, 100); - break; - case 7: - newcam_panlevel = newcam_clamp(newcam_panlevel + toggle, 0, 100); - break; - } -} - -void newcam_text(s16 x, s16 y, u8 str[], u8 col) -{ - u8 textX; - textX = get_str_x_pos_from_center(x,str,10.0f); - gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); - print_generic_string(textX+1,y-1,str); - if (col != 0) - { - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - } - else - { - gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255); - } - print_generic_string(textX,y,str); -} - -//Options menu -void newcam_display_options() -{ - u8 i = 0; - u8 newstring[32]; - s16 scroll; - s16 scrollpos; - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - print_hud_lut_string(HUD_LUT_GLOBAL, 118, 40, newcam_strings[2]); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); - - if (newcam_total>4) - { - newcam_display_box(272,90,280,208,0x80,0x80,0x80); - scrollpos = (54)*((f32)newcam_option_scroll/(newcam_total-4)); - newcam_display_box(272,90+scrollpos,280,154+scrollpos,0xFF,0xFF,0xFF); - } - - - gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); - gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT); - for (i = 0; i < newcam_total; i++) - { - scroll = 140-(32*i)+(newcam_option_scroll*32); - if (scroll <= 140 && scroll > 32) - { - newcam_text(160,scroll,newcam_options[i],newcam_option_selection-i); - switch (i) - { - case 0: - newcam_text(160,scroll-12,newcam_flags[newcam_analogue],newcam_option_selection-i); - break; - case 1: - newcam_text(160,scroll-12,newcam_flags[newcam_mouse],newcam_option_selection-i); - break; - case 2: - int_to_str(newcam_sensitivityX,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 3: - int_to_str(newcam_sensitivityY,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 4: - newcam_text(160,scroll-12,newcam_flags[newcam_invertX],newcam_option_selection-i); - break; - case 5: - newcam_text(160,scroll-12,newcam_flags[newcam_invertY],newcam_option_selection-i); - break; - case 6: - int_to_str(newcam_aggression,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 7: - int_to_str(newcam_panlevel,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - } - } - } - gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - gSPDisplayList(gDisplayListHead++, dl_ia_text_end); - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); - print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]); - print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); -} - -//This has been separated for interesting reasons. Don't question it. -void newcam_render_option_text(void) -{ - gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); - newcam_text(278,212,newcam_strings[newcam_option_open],1); - gSPDisplayList(gDisplayListHead++, dl_ia_text_end); -} - -void newcam_check_pause_buttons() -{ - if (gPlayer1Controller->buttonPressed & R_TRIG) - { - if (newcam_option_open == 0) - { - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - newcam_option_open = 1; - } - else - { - #ifndef nosound - play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs); - #endif - newcam_option_open = 0; - newcam_save_settings(); - } - } - - if (newcam_option_open) - { - if (ABS(gPlayer1Controller->stickY) > 60) - { - newcam_option_timer -= 1; - if (newcam_option_timer <= 0) - { - switch (newcam_option_index) - { - case 0: newcam_option_index++; newcam_option_timer += 10; break; - default: newcam_option_timer += 3; break; - } - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - if (gPlayer1Controller->stickY >= 60) - { - newcam_option_selection--; - if (newcam_option_selection < 0) - newcam_option_selection = newcam_total-1; - } - else - { - newcam_option_selection++; - if (newcam_option_selection >= newcam_total) - newcam_option_selection = 0; - } - } - } - else - if (ABS(gPlayer1Controller->stickX) > 60) - { - newcam_option_timer -= 1; - if (newcam_option_timer <= 0) - { - switch (newcam_option_index) - { - case 0: newcam_option_index++; newcam_option_timer += 10; break; - default: newcam_option_timer += 3; break; - } - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - if (gPlayer1Controller->stickX >= 60) - newcam_change_setting(1); - else - newcam_change_setting(-1); - } - } - else - { - newcam_option_timer = 0; - newcam_option_index = 0; - } - - while (newcam_option_scroll - newcam_option_selection < -3 && newcam_option_selection > newcam_option_scroll) - newcam_option_scroll +=1; - while (newcam_option_scroll + newcam_option_selection > 0 && newcam_option_selection < newcam_option_scroll) - newcam_option_scroll -=1; - } -} diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index f1336559..baaeaf71 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -22,6 +22,9 @@ #ifdef BETTERCAMERA #include "bettercamera.h" #endif +#ifdef EXT_OPTIONS_MENU +#include "options_menu.h" +#endif extern Gfx *gDisplayListHead; extern s16 gCurrCourseNum; @@ -2371,12 +2374,6 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) { { TEXT_EXIT_COURSE_DE } }; - u8 textExitGame[][22] ={ - { TEXT_EXIT_GAME }, - { TEXT_EXIT_GAME_FR }, - { TEXT_EXIT_GAME_DE } - }; - u8 textCameraAngleR[][24] = { { TEXT_CAMERA_ANGLE_R }, { TEXT_CAMERA_ANGLE_R_FR }, @@ -2384,27 +2381,23 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) { }; #define textContinue textContinue[gInGameLanguage] #define textExitCourse textExitCourse[gInGameLanguage] -#define textExitGame textExitGame[gInGameLanguage] #define textCameraAngleR textCameraAngleR[gInGameLanguage] #else u8 textContinue[] = { TEXT_CONTINUE }; u8 textExitCourse[] = { TEXT_EXIT_COURSE }; - u8 textExitGame[] = { TEXT_EXIT_GAME }; u8 textCameraAngleR[] = { TEXT_CAMERA_ANGLE_R }; #endif - handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 4); // Index max raised to 4 from 3 + handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 3); gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); print_generic_string(x + 10, y - 2, textContinue); print_generic_string(x + 10, y - 17, textExitCourse); - print_generic_string(x + 10, y - 33, textExitGame); - - if (index[0] != 4) { - print_generic_string(x + 10, y - 48, textCameraAngleR); + if (index[0] != 3) { + print_generic_string(x + 10, y - 33, textCameraAngleR); gSPDisplayList(gDisplayListHead++, dl_ia_text_end); create_dl_translation_matrix(MENU_MTX_PUSH, x - X_VAL8, (y - ((index[0] - 1) * yIndex)) - Y_VAL8, 0); @@ -2412,10 +2405,8 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) { gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); gSPDisplayList(gDisplayListHead++, dl_draw_triangle); gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); - } - - if (index[0] == 4) { - render_pause_camera_options(x - 42, y - 57, &gDialogCameraAngleIndex, 110); + } else { + render_pause_camera_options(x - 42, y - 42, &gDialogCameraAngleIndex, 110); } } @@ -2612,9 +2603,8 @@ s16 render_pause_courses_and_castle(void) { #ifdef VERSION_EU gInGameLanguage = eu_get_language(); #endif -#ifdef BETTERCAMERA - if (newcam_option_open == 0) - { +#ifdef EXT_OPTIONS_MENU + if (optmenu_open == 0) { #endif switch (gDialogBoxState) { case DIALOG_STATE_OPENING: @@ -2656,7 +2646,7 @@ s16 render_pause_courses_and_castle(void) { gDialogBoxState = DIALOG_STATE_OPENING; gMenuMode = -1; - if (gDialogLineNum == 2 || gDialogLineNum == 3) { + if (gDialogLineNum == 2) { num = gDialogLineNum; } else { num = 1; @@ -2691,15 +2681,13 @@ s16 render_pause_courses_and_castle(void) { if (gDialogTextAlpha < 250) { gDialogTextAlpha += 25; } -#ifdef BETTERCAMERA - } - else - { +#ifdef EXT_OPTIONS_MENU + } else { shade_screen(); - newcam_display_options(); + optmenu_draw(); } - newcam_check_pause_buttons(); - newcam_render_option_text(); + optmenu_check_buttons(); + optmenu_draw_prompt(); #endif return 0; diff --git a/src/game/level_update.c b/src/game/level_update.c index b2450c0b..3fa795f9 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -32,6 +32,8 @@ #include "../pc/configfile.h" #define CONFIG_FILE "sm64config.txt" +#include "pc/cliopts.h" + #define PLAY_MODE_NORMAL 0 #define PLAY_MODE_PAUSED 2 #define PLAY_MODE_CHANGE_AREA 3 @@ -176,7 +178,8 @@ s8 D_8032C9E0 = 0; u8 unused3[4]; u8 unused4[2]; - +// For configfile intro skipping +extern unsigned int configSkipIntro; void basic_update(s16 *arg); @@ -1214,7 +1217,7 @@ s32 init_level(void) { if (gMarioState->action != ACT_UNINITIALIZED) { if (save_file_exists(gCurrSaveFileNum - 1)) { set_mario_action(gMarioState, ACT_IDLE, 0); - } else { + } else if (gCLIOpts.SkipIntro == 0 && configSkipIntro == 0) { set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0); val4 = 1; } @@ -1284,7 +1287,7 @@ s32 lvl_init_from_save_file(UNUSED s16 arg0, s32 levelNum) { #endif sWarpDest.type = WARP_TYPE_NOT_WARPING; sDelayedWarpOp = WARP_OP_NONE; - gShouldNotPlayCastleMusic = !save_file_exists(gCurrSaveFileNum - 1); + gShouldNotPlayCastleMusic = !save_file_exists(gCurrSaveFileNum - 1) && gCLIOpts.SkipIntro == 0 && configSkipIntro == 0; gCurrLevelNum = levelNum; gCurrCourseNum = COURSE_NONE; diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index 0e2c7c86..68cebe5d 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -527,11 +527,15 @@ void set_object_visibility(struct Object *obj, s32 dist) { f32 objY = obj->oPosY; f32 objZ = obj->oPosZ; +#ifndef NODRAWINGDISTANCE if (is_point_within_radius_of_mario(objX, objY, objZ, dist) == TRUE) { +#endif obj->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; +#ifndef NODRAWINGDISTANCE } else { obj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; } +#endif } /** diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 42ad0e57..e05f7743 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -984,7 +984,7 @@ BAD_RETURN(s16) cur_obj_reverse_animation(void) { BAD_RETURN(s32) cur_obj_extend_animation_if_at_end(void) { s32 sp4 = o->header.gfx.unk38.animFrame; s32 sp0 = o->header.gfx.unk38.curAnim->unk08 - 2; - + if (sp4 == sp0) o->header.gfx.unk38.animFrame--; } @@ -1595,6 +1595,10 @@ void obj_set_billboard(struct Object *obj) { obj->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD; } +void obj_set_cylboard(struct Object *obj) { + obj->header.gfx.node.flags |= GRAPH_RENDER_CYLBOARD; +} + void cur_obj_set_hitbox_radius_and_height(f32 radius, f32 height) { o->hitboxRadius = radius; o->hitboxHeight = height; diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 80806bde..577f8f66 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -292,6 +292,7 @@ extern void cur_obj_shake_y(f32); void cur_obj_start_cam_event(struct Object *obj, s32 cameraEvent); // extern ? set_mario_interact_hoot_if_in_range(?); void obj_set_billboard(struct Object *a0); +void obj_set_cylboard(struct Object *a0); void cur_obj_set_hitbox_radius_and_height(f32,f32); void cur_obj_set_hurtbox_radius_and_height(f32,f32); // extern ? obj_spawn_loot_coins(?); diff --git a/src/game/options_menu.c b/src/game/options_menu.c new file mode 100644 index 00000000..37bce584 --- /dev/null +++ b/src/game/options_menu.c @@ -0,0 +1,525 @@ +#ifdef EXT_OPTIONS_MENU + +#include "sm64.h" +#include "include/text_strings.h" +#include "engine/math_util.h" +#include "audio/external.h" +#include "game/camera.h" +#include "game/level_update.h" +#include "game/print.h" +#include "game/segment2.h" +#include "game/save_file.h" +#ifdef BETTERCAMERA +#include "game/bettercamera.h" +#endif +#include "game/mario_misc.h" +#include "game/game_init.h" +#include "game/ingame_menu.h" +#include "game/options_menu.h" +#include "pc/configfile.h" +#include "pc/controller/controller_api.h" + +#include + +u8 optmenu_open = 0; + +static u8 optmenu_binding = 0; +static u8 optmenu_bind_idx = 0; + +// How to add stuff: +// strings: add them to include/text_strings.h.in +// and to menuStr[] / opts*Str[] +// options: add them to the relevant options list +// menus: add a new submenu definition and a new +// option to the optsMain list + +static const u8 toggleStr[][16] = { + { TEXT_OPT_DISABLED }, + { TEXT_OPT_ENABLED }, +}; + +static const u8 menuStr[][32] = { + { TEXT_OPT_HIGHLIGHT }, + { TEXT_OPT_BUTTON1 }, + { TEXT_OPT_BUTTON2 }, + { TEXT_OPT_OPTIONS }, + { TEXT_OPT_CAMERA }, + { TEXT_OPT_CONTROLS }, + { TEXT_OPT_VIDEO }, + { TEXT_OPT_AUDIO }, + { TEXT_EXIT_GAME }, +}; + +static const u8 optsCameraStr[][32] = { + { TEXT_OPT_CAMX }, + { TEXT_OPT_CAMY }, + { TEXT_OPT_INVERTX }, + { TEXT_OPT_INVERTY }, + { TEXT_OPT_CAMC }, + { TEXT_OPT_CAMP }, + { TEXT_OPT_ANALOGUE }, + { TEXT_OPT_MOUSE }, + { TEXT_OPT_CAMD }, +}; + +static const u8 optsVideoStr[][32] = { + { TEXT_OPT_FSCREEN }, + { TEXT_OPT_TEXFILTER }, + { TEXT_OPT_NEAREST }, + { TEXT_OPT_LINEAR }, +}; + +static const u8 optsAudioStr[][32] = { + { TEXT_OPT_MVOLUME }, +}; + +static const u8 bindStr[][32] = { + { TEXT_OPT_UNBOUND }, + { TEXT_OPT_PRESSKEY }, + { TEXT_BIND_A }, + { TEXT_BIND_B }, + { TEXT_BIND_START }, + { TEXT_BIND_L }, + { TEXT_BIND_R }, + { TEXT_BIND_Z }, + { TEXT_BIND_C_UP }, + { TEXT_BIND_C_DOWN }, + { TEXT_BIND_C_LEFT }, + { TEXT_BIND_C_RIGHT }, + { TEXT_BIND_UP }, + { TEXT_BIND_DOWN }, + { TEXT_BIND_LEFT }, + { TEXT_BIND_RIGHT }, +}; + +static const u8 *filterChoices[] = { + optsVideoStr[2], + optsVideoStr[3], +}; + +enum OptType { + OPT_INVALID = 0, + OPT_TOGGLE, + OPT_CHOICE, + OPT_SCROLL, + OPT_SUBMENU, + OPT_BIND, + OPT_BUTTON, +}; + +struct SubMenu; + +struct Option { + enum OptType type; + const u8 *label; + union { + u32 *uval; + bool *bval; + }; + union { + struct { + const u8 **choices; + u32 numChoices; + }; + struct { + u32 scrMin; + u32 scrMax; + u32 scrStep; + }; + struct SubMenu *nextMenu; + void (*actionFn)(struct Option *, s32); + }; +}; + +struct SubMenu { + struct SubMenu *prev; // this is set at runtime to avoid needless complication + const u8 *label; + struct Option *opts; + s32 numOpts; + s32 select; + s32 scroll; +}; + +/* helper macros */ + +#define DEF_OPT_TOGGLE(lbl, bv) \ + { .type = OPT_TOGGLE, .label = lbl, .bval = bv } +#define DEF_OPT_SCROLL(lbl, uv, min, max, st) \ + { .type = OPT_SCROLL, .label = lbl, .uval = uv, .scrMin = min, .scrMax = max, .scrStep = st } +#define DEF_OPT_CHOICE(lbl, uv, ch) \ + { .type = OPT_CHOICE, .label = lbl, .uval = uv, .choices = ch, .numChoices = sizeof(ch) / sizeof(ch[0]) } +#define DEF_OPT_SUBMENU(lbl, nm) \ + { .type = OPT_SUBMENU, .label = lbl, .nextMenu = nm } +#define DEF_OPT_BIND(lbl, uv) \ + { .type = OPT_BIND, .label = lbl, .uval = uv } +#define DEF_OPT_BUTTON(lbl, act) \ + { .type = OPT_BUTTON, .label = lbl, .actionFn = act } +#define DEF_SUBMENU(lbl, opt) \ + { .label = lbl, .opts = opt, .numOpts = sizeof(opt) / sizeof(opt[0]) } + +/* button action functions */ + +static void optmenu_act_exit(UNUSED struct Option *self, s32 arg) { + if (!arg) exit(0); // only exit on A press and not directions +} + +/* submenu option lists */ + +#ifdef BETTERCAMERA +static struct Option optsCamera[] = { + DEF_OPT_TOGGLE( optsCameraStr[6], &configEnableCamera ), + DEF_OPT_TOGGLE( optsCameraStr[7], &configCameraMouse ), + DEF_OPT_TOGGLE( optsCameraStr[2], &configCameraInvertX ), + DEF_OPT_TOGGLE( optsCameraStr[3], &configCameraInvertY ), + DEF_OPT_SCROLL( optsCameraStr[0], &configCameraXSens, 10, 250, 1 ), + DEF_OPT_SCROLL( optsCameraStr[1], &configCameraYSens, 10, 250, 1 ), + DEF_OPT_SCROLL( optsCameraStr[4], &configCameraAggr, 0, 100, 1 ), + DEF_OPT_SCROLL( optsCameraStr[5], &configCameraPan, 0, 100, 1 ), + DEF_OPT_SCROLL( optsCameraStr[8], &configCameraDegrade, 0, 100, 1 ), +}; +#endif + +static struct Option optsControls[] = { + DEF_OPT_BIND( bindStr[ 2], configKeyA ), + DEF_OPT_BIND( bindStr[ 3], configKeyB ), + DEF_OPT_BIND( bindStr[ 4], configKeyStart ), + DEF_OPT_BIND( bindStr[ 5], configKeyL ), + DEF_OPT_BIND( bindStr[ 6], configKeyR ), + DEF_OPT_BIND( bindStr[ 7], configKeyZ ), + DEF_OPT_BIND( bindStr[ 8], configKeyCUp ), + DEF_OPT_BIND( bindStr[ 9], configKeyCDown ), + DEF_OPT_BIND( bindStr[10], configKeyCLeft ), + DEF_OPT_BIND( bindStr[11], configKeyCRight ), + DEF_OPT_BIND( bindStr[12], configKeyStickUp ), + DEF_OPT_BIND( bindStr[13], configKeyStickDown ), + DEF_OPT_BIND( bindStr[14], configKeyStickLeft ), + DEF_OPT_BIND( bindStr[15], configKeyStickRight ), +}; + +static struct Option optsVideo[] = { + DEF_OPT_TOGGLE( optsVideoStr[0], &configFullscreen ), + DEF_OPT_CHOICE( optsVideoStr[1], &configFiltering, filterChoices ), +}; + +static struct Option optsAudio[] = { + DEF_OPT_SCROLL( optsAudioStr[0], &configMasterVolume, 0, MAX_VOLUME, 1 ), +}; + +/* submenu definitions */ + +#ifdef BETTERCAMERA +static struct SubMenu menuCamera = DEF_SUBMENU( menuStr[4], optsCamera ); +#endif +static struct SubMenu menuControls = DEF_SUBMENU( menuStr[5], optsControls ); +static struct SubMenu menuVideo = DEF_SUBMENU( menuStr[6], optsVideo ); +static struct SubMenu menuAudio = DEF_SUBMENU( menuStr[7], optsAudio ); + +/* main options menu definition */ + +static struct Option optsMain[] = { +#ifdef BETTERCAMERA + DEF_OPT_SUBMENU( menuStr[4], &menuCamera ), +#endif + DEF_OPT_SUBMENU( menuStr[5], &menuControls ), + DEF_OPT_SUBMENU( menuStr[6], &menuVideo ), + DEF_OPT_SUBMENU( menuStr[7], &menuAudio ), + DEF_OPT_BUTTON ( menuStr[8], optmenu_act_exit ), +}; + +static struct SubMenu menuMain = DEF_SUBMENU( menuStr[3], optsMain ); + +/* implementation */ + +static s32 optmenu_option_timer = 0; +static u8 optmenu_hold_count = 0; + +static struct SubMenu *currentMenu = &menuMain; + +static inline s32 wrap_add(s32 a, const s32 b, const s32 min, const s32 max) { + a += b; + if (a < min) a = max - (min - a) + 1; + else if (a > max) a = min + (a - max) - 1; + return a; +} + +static void uint_to_hex(u32 num, u8 *dst) { + u8 places = 4; + while (places--) { + const u32 digit = num & 0xF; + dst[places] = digit; + num >>= 4; + } + dst[4] = DIALOG_CHAR_TERMINATOR; +} + +//Displays a box. +static void optmenu_draw_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b) { + gDPPipeSync(gDisplayListHead++); + gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); + gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); + gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255)); + gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1); + gDPPipeSync(gDisplayListHead++); + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); +} + +static void optmenu_draw_text(s16 x, s16 y, const u8 *str, u8 col) { + const u8 textX = get_str_x_pos_from_center(x, (u8*)str, 10.0f); + gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); + print_generic_string(textX+1, y-1, str); + if (col == 0) { + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + } else { + gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255); + } + print_generic_string(textX, y, str); +} + +static void optmenu_draw_opt(const struct Option *opt, s16 x, s16 y, u8 sel) { + u8 buf[32] = { 0 }; + + if (opt->type == OPT_SUBMENU || opt->type == OPT_BUTTON) + y -= 6; + + optmenu_draw_text(x, y, opt->label, sel); + + switch (opt->type) { + case OPT_TOGGLE: + optmenu_draw_text(x, y-13, toggleStr[(int)*opt->bval], sel); + break; + + case OPT_CHOICE: + optmenu_draw_text(x, y-13, opt->choices[*opt->uval], sel); + break; + + case OPT_SCROLL: + int_to_str(*opt->uval, buf); + optmenu_draw_text(x, y-13, buf, sel); + break; + + case OPT_BIND: + x = 112; + for (u8 i = 0; i < MAX_BINDS; ++i, x += 48) { + const u8 white = (sel && (optmenu_bind_idx == i)); + // TODO: button names + if (opt->uval[i] == VK_INVALID) { + if (optmenu_binding && white) + optmenu_draw_text(x, y-13, bindStr[1], 1); + else + optmenu_draw_text(x, y-13, bindStr[0], white); + } else { + uint_to_hex(opt->uval[i], buf); + optmenu_draw_text(x, y-13, buf, white); + } + } + break; + + default: break; + }; +} + +static void optmenu_opt_change(struct Option *opt, s32 val) { + switch (opt->type) { + case OPT_TOGGLE: + *opt->bval = !*opt->bval; + break; + + case OPT_CHOICE: + *opt->uval = wrap_add(*opt->uval, val, 0, opt->numChoices - 1); + break; + + case OPT_SCROLL: + *opt->uval = wrap_add(*opt->uval, opt->scrStep * val, opt->scrMin, opt->scrMax); + break; + + case OPT_SUBMENU: + opt->nextMenu->prev = currentMenu; + currentMenu = opt->nextMenu; + break; + + case OPT_BUTTON: + if (opt->actionFn) + opt->actionFn(opt, val); + break; + + case OPT_BIND: + if (val == 0xFF) { + // clear the bind + opt->uval[optmenu_bind_idx] = VK_INVALID; + } else if (val == 0) { + opt->uval[optmenu_bind_idx] = VK_INVALID; + optmenu_binding = 1; + controller_get_raw_key(); // clear the last key, which is probably A + } else { + optmenu_bind_idx = wrap_add(optmenu_bind_idx, val, 0, MAX_BINDS - 1); + } + break; + + default: break; + } +} + +static inline s16 get_hudstr_centered_x(const s16 sx, const u8 *str) { + const u8 *chr = str; + s16 len = 0; + while (*chr != GLOBAR_CHAR_TERMINATOR) ++chr, ++len; + return sx - len * 6; // stride is 12 +} + +//Options menu +void optmenu_draw(void) { + s16 scroll; + s16 scrollpos; + + const s16 labelX = get_hudstr_centered_x(160, currentMenu->label); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + print_hud_lut_string(HUD_LUT_GLOBAL, labelX, 40, currentMenu->label); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); + + if (currentMenu->numOpts > 4) { + optmenu_draw_box(272, 90, 280, 208, 0x80, 0x80, 0x80); + scrollpos = 54 * ((f32)currentMenu->scroll / (currentMenu->numOpts-4)); + optmenu_draw_box(272, 90+scrollpos, 280, 154+scrollpos, 0xFF, 0xFF, 0xFF); + } + + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT); + for (u8 i = 0; i < currentMenu->numOpts; i++) { + scroll = 140 - 32 * i + currentMenu->scroll * 32; + // FIXME: just start from the first visible option bruh + if (scroll <= 140 && scroll > 32) + optmenu_draw_opt(¤tMenu->opts[i], 160, scroll, (currentMenu->select == i)); + } + + gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); + print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90 + (32 * (currentMenu->select - currentMenu->scroll)), menuStr[0]); + print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90 + (32 * (currentMenu->select - currentMenu->scroll)), menuStr[0]); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); +} + +//This has been separated for interesting reasons. Don't question it. +void optmenu_draw_prompt(void) { + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + optmenu_draw_text(278, 212, menuStr[1 + optmenu_open], 0); + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); +} + +void optmenu_toggle(void) { + if (optmenu_open == 0) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + currentMenu = &menuMain; + optmenu_open = 1; + } else { + #ifndef nosound + play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs); + #endif + optmenu_open = 0; + #ifdef BETTERCAMERA + newcam_init_settings(); // load bettercam settings from config vars + #endif + controller_reconfigure(); // rebind using new config values + configfile_save(CONFIG_FILE); + } +} + +void optmenu_check_buttons(void) { + if (optmenu_binding) { + u32 key = controller_get_raw_key(); + if (key != VK_INVALID) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + currentMenu->opts[currentMenu->select].uval[optmenu_bind_idx] = key; + optmenu_binding = 0; + optmenu_option_timer = 12; + } + return; + } + + if (gPlayer1Controller->buttonPressed & R_TRIG) + optmenu_toggle(); + + if (!optmenu_open) return; + + u8 allowInput = 0; + + optmenu_option_timer--; + if (optmenu_option_timer <= 0) { + if (optmenu_hold_count == 0) { + optmenu_hold_count++; + optmenu_option_timer = 10; + } else { + optmenu_option_timer = 3; + } + allowInput = 1; + } + + if (ABS(gPlayer1Controller->stickY) > 60) { + if (allowInput) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + + if (gPlayer1Controller->stickY >= 60) { + currentMenu->select--; + if (currentMenu->select < 0) + currentMenu->select = currentMenu->numOpts-1; + } else { + currentMenu->select++; + if (currentMenu->select >= currentMenu->numOpts) + currentMenu->select = 0; + } + + if (currentMenu->select < currentMenu->scroll) + currentMenu->scroll = currentMenu->select; + else if (currentMenu->select > currentMenu->scroll + 3) + currentMenu->scroll = currentMenu->select - 3; + } + } else if (ABS(gPlayer1Controller->stickX) > 60) { + if (allowInput) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + if (gPlayer1Controller->stickX >= 60) + optmenu_opt_change(¤tMenu->opts[currentMenu->select], 1); + else + optmenu_opt_change(¤tMenu->opts[currentMenu->select], -1); + } + } else if (gPlayer1Controller->buttonPressed & A_BUTTON) { + if (allowInput) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + optmenu_opt_change(¤tMenu->opts[currentMenu->select], 0); + } + } else if (gPlayer1Controller->buttonPressed & B_BUTTON) { + if (allowInput) { + if (currentMenu->prev) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + currentMenu = currentMenu->prev; + } else { + // can't go back, exit the menu altogether + optmenu_toggle(); + } + } + } else if (gPlayer1Controller->buttonPressed & Z_TRIG) { + // HACK: clear binds with Z + if (allowInput && currentMenu->opts[currentMenu->select].type == OPT_BIND) + optmenu_opt_change(¤tMenu->opts[currentMenu->select], 0xFF); + } else if (gPlayer1Controller->buttonPressed & START_BUTTON) { + if (allowInput) optmenu_toggle(); + } else { + optmenu_hold_count = 0; + optmenu_option_timer = 0; + } +} + +#endif // EXT_OPTIONS_MENU diff --git a/src/game/options_menu.h b/src/game/options_menu.h new file mode 100644 index 00000000..4828ae9d --- /dev/null +++ b/src/game/options_menu.h @@ -0,0 +1,11 @@ +#ifndef OPTIONS_MENU_H +#define OPTIONS_MENU_H + +void optmenu_toggle(void); +void optmenu_draw(void); +void optmenu_draw_prompt(void); +void optmenu_check_buttons(void); + +extern u8 optmenu_open; + +#endif // OPTIONS_MENU_H diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index d83616d5..a96eafbf 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -796,6 +796,9 @@ static void geo_process_object(struct Object *node) { if (node->header.gfx.throwMatrix != NULL) { mtxf_mul(gMatStack[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix, gMatStack[gMatStackIndex]); + } else if (node->header.gfx.node.flags & GRAPH_RENDER_CYLBOARD) { + mtxf_cylboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], + node->header.gfx.pos, gCurGraphNodeCamera->roll); } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) { mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], node->header.gfx.pos, gCurGraphNodeCamera->roll); diff --git a/src/goddard/renderer.c b/src/goddard/renderer.c index 6264bb66..821f9aff 100644 --- a/src/goddard/renderer.c +++ b/src/goddard/renderer.c @@ -22,6 +22,7 @@ #include "gd_math.h" #include "shape_helper.h" +#include "config.h" #include "gfx_dimensions.h" #define MAX_GD_DLS 1000 @@ -2302,7 +2303,7 @@ void start_view_dl(struct ObjView *view) { uly = lry - 1.0f; } - gDPSetScissor(next_gfx(), G_SC_NON_INTERLACE, ulx, uly, lrx, lry); + // gDPSetScissor(next_gfx(), G_SC_NON_INTERLACE, ulx, uly, lrx, lry); // N64 only gSPClearGeometryMode(next_gfx(), 0xFFFFFFFF); gSPSetGeometryMode(next_gfx(), G_LIGHTING | G_CULL_BACK | G_SHADING_SMOOTH | G_SHADE); if (view->flags & VIEW_ALLOC_ZBUF) { @@ -2961,9 +2962,9 @@ void update_cursor(void) { reset_dlnum_indices(sHandShape->gdDls[gGdFrameBuf]); if (gGdCtrl.btnApressed) { - gd_put_sprite((u16 *) gd_texture_hand_closed, sHandView->upperLeft.x, sHandView->upperLeft.y, 0x20, 0x20); + gd_put_sprite((u16 *) gd_texture_hand_closed, sHandView->upperLeft.x, sHandView->upperLeft.y, 32, 32); } else { - gd_put_sprite((u16 *) gd_texture_hand_open, sHandView->upperLeft.x, sHandView->upperLeft.y, 0x20, 0x20); + gd_put_sprite((u16 *) gd_texture_hand_open, sHandView->upperLeft.x, sHandView->upperLeft.y, 32, 32); } gd_enddlsplist_parent(); @@ -3419,7 +3420,8 @@ void Unknown801A5FF8(struct ObjGroup *arg0) { void gd_put_sprite(u16 *sprite, s32 x, s32 y, s32 wx, s32 wy) { s32 c; // 5c s32 r; // 58 - f32 aspect = GFX_DIMENSIONS_ASPECT_RATIO * 0.75; + // Must be game screen aspect ratio, not GFX window aspect ratio + f32 aspect = ((float) SCREEN_WIDTH) / ((float) SCREEN_HEIGHT ) * 0.75; x *= aspect; gSPDisplayList(next_gfx(), osVirtualToPhysical(gd_dl_sprite_start_tex_block)); diff --git a/src/pc/audio/audio_api.h b/src/pc/audio/audio_api.h index 26794dd6..b40130e0 100644 --- a/src/pc/audio/audio_api.h +++ b/src/pc/audio/audio_api.h @@ -10,6 +10,7 @@ struct AudioAPI { int (*buffered)(void); int (*get_desired_buffered)(void); void (*play)(const uint8_t *buf, size_t len); + void (*shutdown)(void); }; #endif diff --git a/src/pc/audio/audio_null.c b/src/pc/audio/audio_null.c index 8a244e4b..4d0dd923 100644 --- a/src/pc/audio/audio_null.c +++ b/src/pc/audio/audio_null.c @@ -15,9 +15,13 @@ static int audio_null_get_desired_buffered(void) { static void audio_null_play(const uint8_t *buf, size_t len) { } +static void audio_null_shutdown(void) { +} + struct AudioAPI audio_null = { audio_null_init, audio_null_buffered, audio_null_get_desired_buffered, - audio_null_play -}; + audio_null_play, + audio_null_shutdown +}; \ No newline at end of file diff --git a/src/pc/audio/audio_sdl.c b/src/pc/audio/audio_sdl.c index 2f13ccde..beb5a1e6 100644 --- a/src/pc/audio/audio_sdl.c +++ b/src/pc/audio/audio_sdl.c @@ -40,9 +40,21 @@ static void audio_sdl_play(const uint8_t *buf, size_t len) { } } +static void audio_sdl_shutdown(void) +{ + if (SDL_WasInit(SDL_INIT_AUDIO)) { + if (dev != 0) { + SDL_CloseAudioDevice(dev); + dev = 0; + } + SDL_QuitSubSystem(SDL_INIT_AUDIO); + } +} + struct AudioAPI audio_sdl = { audio_sdl_init, audio_sdl_buffered, audio_sdl_get_desired_buffered, - audio_sdl_play + audio_sdl_play, + audio_sdl_shutdown }; \ No newline at end of file diff --git a/src/pc/cliopts.c b/src/pc/cliopts.c new file mode 100644 index 00000000..602de098 --- /dev/null +++ b/src/pc/cliopts.c @@ -0,0 +1,25 @@ +#include "cliopts.h" +#include + +struct PCCLIOptions gCLIOpts; + +void parse_cli_opts(int argc, char* argv[]) +{ + // Initialize options with false values. + gCLIOpts.SkipIntro = 0; + gCLIOpts.FullScreen = 0; + + // Scan arguments for options + if (argc > 1) + { + int i; + for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], "--skip-intro") == 0) // Skip Peach Intro + gCLIOpts.SkipIntro = 1; + + if (strcmp(argv[i], "--fullscreen") == 0) // Open game in fullscreen + gCLIOpts.FullScreen = 1; + } + } +} \ No newline at end of file diff --git a/src/pc/cliopts.h b/src/pc/cliopts.h new file mode 100644 index 00000000..b4a0b613 --- /dev/null +++ b/src/pc/cliopts.h @@ -0,0 +1,11 @@ +#include "sm64.h" + +struct PCCLIOptions +{ + u8 SkipIntro; + u8 FullScreen; +}; + +extern struct PCCLIOptions gCLIOpts; + +void parse_cli_opts(int argc, char* argv[]); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 4d38aaaf..3c2735c2 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -7,6 +7,7 @@ #include #include "configfile.h" +#include "controller/controller_api.h" #define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) @@ -14,6 +15,7 @@ enum ConfigOptionType { CONFIG_TYPE_BOOL, CONFIG_TYPE_UINT, CONFIG_TYPE_FLOAT, + CONFIG_TYPE_BIND, }; struct ConfigOption { @@ -29,74 +31,60 @@ struct ConfigOption { /* *Config options and default values */ -bool configFullscreen = false; -// Keyboard mappings (scancode values) -unsigned int configKeyA = 0x26; -unsigned int configKeyB = 0x33; -unsigned int configKeyStart = 0x39; -unsigned int configKeyL = 0x34; -unsigned int configKeyR = 0x36; -unsigned int configKeyZ = 0x25; -unsigned int configKeyCUp = 0x148; -unsigned int configKeyCDown = 0x150; -unsigned int configKeyCLeft = 0x14B; -unsigned int configKeyCRight = 0x14D; -unsigned int configKeyStickUp = 0x11; -unsigned int configKeyStickDown = 0x1F; -unsigned int configKeyStickLeft = 0x1E; -unsigned int configKeyStickRight = 0x20; -// Gamepad mappings (SDL_GameControllerButton values) -unsigned int configJoyA = 0; -unsigned int configJoyB = 2; -unsigned int configJoyStart = 6; -unsigned int configJoyL = 7; -unsigned int configJoyR = 10; -unsigned int configJoyZ = 9; -// Mouse button mappings (0 for none, 1 for left, 2 for middle, 3 for right) -unsigned int configMouseA = 3; -unsigned int configMouseB = 1; -unsigned int configMouseL = 4; -unsigned int configMouseR = 5; -unsigned int configMouseZ = 2; + +// Video/audio stuff +bool configFullscreen = false; +unsigned int configFiltering = 1; // 0=force nearest, 1=linear, (TODO) 2=three-point +unsigned int configMasterVolume = MAX_VOLUME; // 0 - MAX_VOLUME + +// Keyboard mappings (VK_ values, by default keyboard/gamepad/mouse) +unsigned int configKeyA[MAX_BINDS] = { 0x0026, 0x1000, 0x1103 }; +unsigned int configKeyB[MAX_BINDS] = { 0x0033, 0x1002, 0x1101 }; +unsigned int configKeyStart[MAX_BINDS] = { 0x0039, 0x1006, VK_INVALID }; +unsigned int configKeyL[MAX_BINDS] = { 0x0034, 0x1007, 0x1104 }; +unsigned int configKeyR[MAX_BINDS] = { 0x0036, 0x100A, 0x1105 }; +unsigned int configKeyZ[MAX_BINDS] = { 0x0025, 0x1009, 0x1102 }; +unsigned int configKeyCUp[MAX_BINDS] = { 0x0148, VK_INVALID, VK_INVALID }; +unsigned int configKeyCDown[MAX_BINDS] = { 0x0150, VK_INVALID, VK_INVALID }; +unsigned int configKeyCLeft[MAX_BINDS] = { 0x014B, VK_INVALID, VK_INVALID }; +unsigned int configKeyCRight[MAX_BINDS] = { 0x014D, VK_INVALID, VK_INVALID }; +unsigned int configKeyStickUp[MAX_BINDS] = { 0x0011, VK_INVALID, VK_INVALID }; +unsigned int configKeyStickDown[MAX_BINDS] = { 0x001F, VK_INVALID, VK_INVALID }; +unsigned int configKeyStickLeft[MAX_BINDS] = { 0x001E, VK_INVALID, VK_INVALID }; +unsigned int configKeyStickRight[MAX_BINDS] = { 0x0020, VK_INVALID, VK_INVALID }; + #ifdef BETTERCAMERA // BetterCamera settings unsigned int configCameraXSens = 50; unsigned int configCameraYSens = 50; unsigned int configCameraAggr = 0; unsigned int configCameraPan = 0; +unsigned int configCameraDegrade = 10; // 0 - 100% bool configCameraInvertX = false; bool configCameraInvertY = false; bool configEnableCamera = false; bool configCameraMouse = false; #endif +unsigned int configSkipIntro = 0; static const struct ConfigOption options[] = { {.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen}, - {.name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA}, - {.name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB}, - {.name = "key_start", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStart}, - {.name = "key_l", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyL}, - {.name = "key_r", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyR}, - {.name = "key_z", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyZ}, - {.name = "key_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCUp}, - {.name = "key_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCDown}, - {.name = "key_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCLeft}, - {.name = "key_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCRight}, - {.name = "key_stickup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickUp}, - {.name = "key_stickdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickDown}, - {.name = "key_stickleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickLeft}, - {.name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight}, - {.name = "joy_a", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyA}, - {.name = "joy_b", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyB}, - {.name = "joy_start", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyStart}, - {.name = "joy_l", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyL}, - {.name = "joy_r", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyR}, - {.name = "joy_z", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyZ}, - {.name = "mouse_a", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseA}, - {.name = "mouse_b", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseB}, - {.name = "mouse_l", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseL}, - {.name = "mouse_r", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseR}, - {.name = "mouse_z", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseZ}, + {.name = "texture_filtering", .type = CONFIG_TYPE_UINT, .uintValue = &configFiltering}, + {.name = "master_volume", .type = CONFIG_TYPE_UINT, .uintValue = &configMasterVolume}, + {.name = "key_a", .type = CONFIG_TYPE_BIND, .uintValue = configKeyA}, + {.name = "key_b", .type = CONFIG_TYPE_BIND, .uintValue = configKeyB}, + {.name = "key_start", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStart}, + {.name = "key_l", .type = CONFIG_TYPE_BIND, .uintValue = configKeyL}, + {.name = "key_r", .type = CONFIG_TYPE_BIND, .uintValue = configKeyR}, + {.name = "key_z", .type = CONFIG_TYPE_BIND, .uintValue = configKeyZ}, + {.name = "key_cup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCUp}, + {.name = "key_cdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCDown}, + {.name = "key_cleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCLeft}, + {.name = "key_cright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCRight}, + {.name = "key_stickup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickUp}, + {.name = "key_stickdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickDown}, + {.name = "key_stickleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickLeft}, + {.name = "key_stickright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickRight}, #ifdef BETTERCAMERA {.name = "bettercam_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnableCamera}, {.name = "bettercam_mouse_look", .type = CONFIG_TYPE_BOOL, .boolValue = &configCameraMouse}, @@ -106,7 +94,9 @@ static const struct ConfigOption options[] = { {.name = "bettercam_ysens", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraYSens}, {.name = "bettercam_aggression", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraAggr}, {.name = "bettercam_pan_level", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraPan}, + {.name = "bettercam_degrade", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraDegrade}, #endif + {.name = "skip_intro", .type = CONFIG_TYPE_UINT, .uintValue = &configSkipIntro}, // Add this back! }; // Reads an entire line from a file (excluding the newline character) and returns an allocated string @@ -207,9 +197,13 @@ void configfile_load(const char *filename) { while (isspace(*p)) p++; + + if (!*p || *p == '#') // comment or empty line + continue; + numTokens = tokenize_string(p, 2, tokens); if (numTokens != 0) { - if (numTokens == 2) { + if (numTokens >= 2) { const struct ConfigOption *option = NULL; for (unsigned int i = 0; i < ARRAY_LEN(options); i++) { @@ -231,6 +225,10 @@ void configfile_load(const char *filename) { case CONFIG_TYPE_UINT: sscanf(tokens[1], "%u", option->uintValue); break; + case CONFIG_TYPE_BIND: + for (int i = 0; i < MAX_BINDS && i < numTokens - 1; ++i) + sscanf(tokens[i + 1], "%x", option->uintValue + i); + break; case CONFIG_TYPE_FLOAT: sscanf(tokens[1], "%f", option->floatValue); break; @@ -273,6 +271,12 @@ void configfile_save(const char *filename) { case CONFIG_TYPE_FLOAT: fprintf(file, "%s %f\n", option->name, *option->floatValue); break; + case CONFIG_TYPE_BIND: + fprintf(file, "%s ", option->name); + for (int i = 0; i < MAX_BINDS; ++i) + fprintf(file, "%04x ", option->uintValue[i]); + fprintf(file, "\n"); + break; default: assert(0); // unknown type } diff --git a/src/pc/configfile.h b/src/pc/configfile.h index a2044dd7..8dc69e27 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -5,38 +5,33 @@ #define CONFIG_FILE "sm64config.txt" +#define MAX_BINDS 3 +#define MAX_VOLUME 127 +#define VOLUME_SHIFT 7 + extern bool configFullscreen; -extern unsigned int configKeyA; -extern unsigned int configKeyB; -extern unsigned int configKeyStart; -extern unsigned int configKeyL; -extern unsigned int configKeyR; -extern unsigned int configKeyZ; -extern unsigned int configKeyCUp; -extern unsigned int configKeyCDown; -extern unsigned int configKeyCLeft; -extern unsigned int configKeyCRight; -extern unsigned int configKeyStickUp; -extern unsigned int configKeyStickDown; -extern unsigned int configKeyStickLeft; -extern unsigned int configKeyStickRight; -extern unsigned int configJoyA; -extern unsigned int configJoyB; -extern unsigned int configJoyStart; -extern unsigned int configJoyL; -extern unsigned int configJoyR; -extern unsigned int configJoyZ; -extern unsigned int configMouseA; -extern unsigned int configMouseB; -extern unsigned int configMouseStart; -extern unsigned int configMouseL; -extern unsigned int configMouseR; -extern unsigned int configMouseZ; +extern unsigned int configFiltering; +extern unsigned int configMasterVolume; +extern unsigned int configKeyA[]; +extern unsigned int configKeyB[]; +extern unsigned int configKeyStart[]; +extern unsigned int configKeyL[]; +extern unsigned int configKeyR[]; +extern unsigned int configKeyZ[]; +extern unsigned int configKeyCUp[]; +extern unsigned int configKeyCDown[]; +extern unsigned int configKeyCLeft[]; +extern unsigned int configKeyCRight[]; +extern unsigned int configKeyStickUp[]; +extern unsigned int configKeyStickDown[]; +extern unsigned int configKeyStickLeft[]; +extern unsigned int configKeyStickRight[]; #ifdef BETTERCAMERA extern unsigned int configCameraXSens; extern unsigned int configCameraYSens; extern unsigned int configCameraAggr; extern unsigned int configCameraPan; +extern unsigned int configCameraDegrade; extern bool configCameraInvertX; extern bool configCameraInvertY; extern bool configEnableCamera; diff --git a/src/pc/controller/controller_api.h b/src/pc/controller/controller_api.h index e040551a..2f9c1d16 100644 --- a/src/pc/controller/controller_api.h +++ b/src/pc/controller/controller_api.h @@ -2,15 +2,25 @@ #define CONTROLLER_API #define DEADZONE 4960 - -// Analog camera movement by Pathétique (github.com/vrmiguel), y0shin and Mors -// Contribute or communicate bugs at github.com/vrmiguel/sm64-analog-camera +#define VK_INVALID 0xFFFF +#define VK_SIZE 0x1000 #include struct ControllerAPI { - void (*init)(void); - void (*read)(OSContPad *pad); + const u32 vkbase; // base number in the virtual keyspace (e.g. keyboard is 0x0000-0x1000) + void (*init)(void); // call once, also calls reconfig() + void (*read)(OSContPad *pad); // read controller and update N64 pad values + u32 (*rawkey)(void); // returns last pressed virtual key or VK_INVALID if none + void (*reconfig)(void); // (optional) call when bindings have changed + void (*shutdown)(void); // (optional) call in osContReset }; +// used for binding keys +u32 controller_get_raw_key(void); +void controller_reconfigure(void); + +// calls the shutdown() function of all controller subsystems +void controller_shutdown(void); + #endif diff --git a/src/pc/controller/controller_entry_point.c b/src/pc/controller/controller_entry_point.c index 9f3630a0..90c10c1d 100644 --- a/src/pc/controller/controller_entry_point.c +++ b/src/pc/controller/controller_entry_point.c @@ -56,3 +56,25 @@ void osContGetReadData(OSContPad *pad) { controller_implementations[i]->read(pad); } } + +u32 controller_get_raw_key(void) { + for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) { + u32 vk = controller_implementations[i]->rawkey(); + if (vk != VK_INVALID) return vk + controller_implementations[i]->vkbase; + } + return VK_INVALID; +} + +void controller_shutdown(void) { + for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) { + if (controller_implementations[i]->shutdown) + controller_implementations[i]->shutdown(); + } +} + +void controller_reconfigure(void) { + for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) { + if (controller_implementations[i]->reconfig) + controller_implementations[i]->reconfig(); + } +} diff --git a/src/pc/controller/controller_keyboard.c b/src/pc/controller/controller_keyboard.c index 87eaee0d..9fb4b635 100644 --- a/src/pc/controller/controller_keyboard.c +++ b/src/pc/controller/controller_keyboard.c @@ -8,14 +8,19 @@ #endif #include "../configfile.h" +#include "controller_keyboard.h" static int keyboard_buttons_down; -static int keyboard_mapping[14][2]; +#define MAX_KEYBINDS 64 +static int keyboard_mapping[MAX_KEYBINDS][2]; +static int num_keybinds = 0; + +static u32 keyboard_lastkey = VK_INVALID; static int keyboard_map_scancode(int scancode) { int ret = 0; - for (size_t i = 0; i < sizeof(keyboard_mapping) / sizeof(keyboard_mapping[0]); i++) { + for (int i = 0; i < num_keybinds; i++) { if (keyboard_mapping[i][0] == scancode) { ret |= keyboard_mapping[i][1]; } @@ -26,12 +31,15 @@ static int keyboard_map_scancode(int scancode) { bool keyboard_on_key_down(int scancode) { int mapped = keyboard_map_scancode(scancode); keyboard_buttons_down |= mapped; + keyboard_lastkey = scancode; return mapped != 0; } bool keyboard_on_key_up(int scancode) { int mapped = keyboard_map_scancode(scancode); keyboard_buttons_down &= ~mapped; + if (keyboard_lastkey == (u32) scancode) + keyboard_lastkey = VK_INVALID; return mapped != 0; } @@ -39,28 +47,38 @@ void keyboard_on_all_keys_up(void) { keyboard_buttons_down = 0; } -static void set_keyboard_mapping(int index, int mask, int scancode) { - keyboard_mapping[index][0] = scancode; - keyboard_mapping[index][1] = mask; +static void keyboard_add_binds(int mask, unsigned int *scancode) { + for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) { + if (scancode[i] < VK_BASE_KEYBOARD + VK_SIZE) { + keyboard_mapping[num_keybinds][0] = scancode[i]; + keyboard_mapping[num_keybinds][1] = mask; + num_keybinds++; + } + } +} + +static void keyboard_bindkeys(void) { + bzero(keyboard_mapping, sizeof(keyboard_mapping)); + num_keybinds = 0; + + keyboard_add_binds(0x80000, configKeyStickUp); + keyboard_add_binds(0x10000, configKeyStickLeft); + keyboard_add_binds(0x40000, configKeyStickDown); + keyboard_add_binds(0x20000, configKeyStickRight); + keyboard_add_binds(A_BUTTON, configKeyA); + keyboard_add_binds(B_BUTTON, configKeyB); + keyboard_add_binds(Z_TRIG, configKeyZ); + keyboard_add_binds(U_CBUTTONS, configKeyCUp); + keyboard_add_binds(L_CBUTTONS, configKeyCLeft); + keyboard_add_binds(D_CBUTTONS, configKeyCDown); + keyboard_add_binds(R_CBUTTONS, configKeyCRight); + keyboard_add_binds(L_TRIG, configKeyL); + keyboard_add_binds(R_TRIG, configKeyR); + keyboard_add_binds(START_BUTTON, configKeyStart); } static void keyboard_init(void) { - int i = 0; - - set_keyboard_mapping(i++, 0x80000, configKeyStickUp); - set_keyboard_mapping(i++, 0x10000, configKeyStickLeft); - set_keyboard_mapping(i++, 0x40000, configKeyStickDown); - set_keyboard_mapping(i++, 0x20000, configKeyStickRight); - set_keyboard_mapping(i++, A_BUTTON, configKeyA); - set_keyboard_mapping(i++, B_BUTTON, configKeyB); - set_keyboard_mapping(i++, Z_TRIG, configKeyZ); - set_keyboard_mapping(i++, U_CBUTTONS, configKeyCUp); - set_keyboard_mapping(i++, L_CBUTTONS, configKeyCLeft); - set_keyboard_mapping(i++, D_CBUTTONS, configKeyCDown); - set_keyboard_mapping(i++, R_CBUTTONS, configKeyCRight); - set_keyboard_mapping(i++, L_TRIG, configKeyL); - set_keyboard_mapping(i++, R_TRIG, configKeyR); - set_keyboard_mapping(i++, START_BUTTON, configKeyStart); + keyboard_bindkeys(); #ifdef TARGET_WEB controller_emscripten_keyboard_init(); @@ -83,7 +101,20 @@ static void keyboard_read(OSContPad *pad) { } } +static u32 keyboard_rawkey(void) { + const u32 ret = keyboard_lastkey; + keyboard_lastkey = VK_INVALID; + return ret; +} + +static void keyboard_shutdown(void) { +} + struct ControllerAPI controller_keyboard = { + VK_BASE_KEYBOARD, keyboard_init, - keyboard_read + keyboard_read, + keyboard_rawkey, + keyboard_bindkeys, + keyboard_shutdown }; diff --git a/src/pc/controller/controller_keyboard.h b/src/pc/controller/controller_keyboard.h index e2c08586..028d2e85 100644 --- a/src/pc/controller/controller_keyboard.h +++ b/src/pc/controller/controller_keyboard.h @@ -4,6 +4,8 @@ #include #include "controller_api.h" +# define VK_BASE_KEYBOARD 0x0000 + #ifdef __cplusplus extern "C" { #endif diff --git a/src/pc/controller/controller_recorded_tas.c b/src/pc/controller/controller_recorded_tas.c index 4f5c1bb0..2fcd5b54 100644 --- a/src/pc/controller/controller_recorded_tas.c +++ b/src/pc/controller/controller_recorded_tas.c @@ -23,7 +23,22 @@ static void tas_read(OSContPad *pad) { } } +static void tas_shutdown(void) { + if (fp != NULL) { + fclose(fp); + fp = NULL; + } +} + +static u32 tas_rawkey(void) { + return VK_INVALID; +} + struct ControllerAPI controller_recorded_tas = { + VK_INVALID, tas_init, - tas_read + tas_read, + tas_rawkey, + NULL, // no rebinding + tas_shutdown }; diff --git a/src/pc/controller/controller_sdl.c b/src/pc/controller/controller_sdl.c index 48120231..62aa2acf 100644 --- a/src/pc/controller/controller_sdl.c +++ b/src/pc/controller/controller_sdl.c @@ -11,9 +11,15 @@ #include #include "controller_api.h" - +#include "controller_sdl.h" #include "../configfile.h" +// mouse buttons are also in the controller namespace (why), just offset 0x100 +#define VK_OFS_SDL_MOUSE 0x0100 +#define VK_BASE_SDL_MOUSE (VK_BASE_SDL_GAMEPAD + VK_OFS_SDL_MOUSE) +#define MAX_JOYBINDS 32 +#define MAX_MOUSEBUTTONS 8 // arbitrary + extern int16_t rightx; extern int16_t righty; @@ -27,6 +33,51 @@ extern u8 newcam_mouse; static bool init_ok; static SDL_GameController *sdl_cntrl; + +static u32 num_joy_binds = 0; +static u32 num_mouse_binds = 0; +static u32 joy_binds[MAX_JOYBINDS][2]; +static u32 mouse_binds[MAX_JOYBINDS][2]; + +static bool joy_buttons[SDL_CONTROLLER_BUTTON_MAX ] = { false }; +static u32 mouse_buttons = 0; +static u32 last_mouse = VK_INVALID; +static u32 last_joybutton = VK_INVALID; + +static inline void controller_add_binds(const u32 mask, const u32 *btns) { + for (u32 i = 0; i < MAX_BINDS; ++i) { + if (btns[i] >= VK_BASE_SDL_GAMEPAD && btns[i] <= VK_BASE_SDL_GAMEPAD + VK_SIZE) { + if (btns[i] >= VK_BASE_SDL_MOUSE && num_joy_binds < MAX_JOYBINDS) { + mouse_binds[num_mouse_binds][0] = btns[i] - VK_BASE_SDL_MOUSE; + mouse_binds[num_mouse_binds][1] = mask; + ++num_mouse_binds; + } else if (num_mouse_binds < MAX_JOYBINDS) { + joy_binds[num_joy_binds][0] = btns[i] - VK_BASE_SDL_GAMEPAD; + joy_binds[num_joy_binds][1] = mask; + ++num_joy_binds; + } + } + } +} + +static void controller_sdl_bind(void) { + bzero(joy_binds, sizeof(joy_binds)); + bzero(mouse_binds, sizeof(mouse_binds)); + num_joy_binds = 0; + num_mouse_binds = 0; + + controller_add_binds(A_BUTTON, configKeyA); + controller_add_binds(B_BUTTON, configKeyB); + controller_add_binds(Z_TRIG, configKeyZ); + controller_add_binds(U_CBUTTONS, configKeyCUp); + controller_add_binds(L_CBUTTONS, configKeyCLeft); + controller_add_binds(D_CBUTTONS, configKeyCDown); + controller_add_binds(R_CBUTTONS, configKeyCRight); + controller_add_binds(L_TRIG, configKeyL); + controller_add_binds(R_TRIG, configKeyR); + controller_add_binds(START_BUTTON, configKeyStart); +} + static void controller_sdl_init(void) { if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) { fprintf(stderr, "SDL init error: %s\n", SDL_GetError()); @@ -39,6 +90,8 @@ static void controller_sdl_init(void) { SDL_GetRelativeMouseState(&mouse_x, &mouse_y); #endif + controller_sdl_bind(); + init_ok = true; } @@ -53,13 +106,16 @@ static void controller_sdl_read(OSContPad *pad) { else SDL_SetRelativeMouseMode(SDL_FALSE); - const u32 mbuttons = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); + u32 mouse = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); + + for (u32 i = 0; i < num_mouse_binds; ++i) + if (mouse & SDL_BUTTON(mouse_binds[i][0])) + pad->button |= mouse_binds[i][1]; + + // remember buttons that changed from 0 to 1 + last_mouse = (mouse_buttons ^ mouse) & mouse; + mouse_buttons = mouse; - if (configMouseA && (mbuttons & SDL_BUTTON(configMouseA))) pad->button |= A_BUTTON; - if (configMouseB && (mbuttons & SDL_BUTTON(configMouseB))) pad->button |= B_BUTTON; - if (configMouseL && (mbuttons & SDL_BUTTON(configMouseL))) pad->button |= L_TRIG; - if (configMouseR && (mbuttons & SDL_BUTTON(configMouseR))) pad->button |= R_TRIG; - if (configMouseZ && (mbuttons & SDL_BUTTON(configMouseZ))) pad->button |= Z_TRIG; #endif SDL_GameControllerUpdate(); @@ -82,12 +138,16 @@ static void controller_sdl_read(OSContPad *pad) { } } - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyStart)) pad->button |= START_BUTTON; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyZ)) pad->button |= Z_TRIG; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyL)) pad->button |= L_TRIG; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyR)) pad->button |= R_TRIG; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyA)) pad->button |= A_BUTTON; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyB)) pad->button |= B_BUTTON; + for (u32 i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { + const bool new = SDL_GameControllerGetButton(sdl_cntrl, i); + const bool pressed = !joy_buttons[i] && new; + joy_buttons[i] = new; + if (pressed) last_joybutton = i; + } + + for (u32 i = 0; i < num_joy_binds; ++i) + if (joy_buttons[joy_binds[i][0]]) + pad->button |= joy_binds[i][1]; int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX); int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY); @@ -127,7 +187,39 @@ static void controller_sdl_read(OSContPad *pad) { } } +static u32 controller_sdl_rawkey(void) { + if (last_joybutton != VK_INVALID) { + const u32 ret = last_joybutton; + last_joybutton = VK_INVALID; + return ret; + } + + for (int i = 0; i < MAX_MOUSEBUTTONS; ++i) { + if (last_mouse & SDL_BUTTON(i)) { + const u32 ret = VK_OFS_SDL_MOUSE + i; + last_mouse = 0; + return ret; + } + } + return VK_INVALID; +} + +static void controller_sdl_shutdown(void) { + if (SDL_WasInit(SDL_INIT_GAMECONTROLLER)) { + if (sdl_cntrl) { + SDL_GameControllerClose(sdl_cntrl); + sdl_cntrl = NULL; + } + SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); + } + init_ok = false; +} + struct ControllerAPI controller_sdl = { + VK_BASE_SDL_GAMEPAD, controller_sdl_init, - controller_sdl_read + controller_sdl_read, + controller_sdl_rawkey, + controller_sdl_bind, + controller_sdl_shutdown }; diff --git a/src/pc/controller/controller_sdl.h b/src/pc/controller/controller_sdl.h index 02aec8d9..bbe8a62c 100644 --- a/src/pc/controller/controller_sdl.h +++ b/src/pc/controller/controller_sdl.h @@ -3,6 +3,8 @@ #include "controller_api.h" +#define VK_BASE_SDL_GAMEPAD 0x1000 + extern struct ControllerAPI controller_sdl; #endif diff --git a/src/pc/gfx/gfx_opengl.c b/src/pc/gfx/gfx_opengl.c index 65b3099e..b283b7e2 100644 --- a/src/pc/gfx/gfx_opengl.c +++ b/src/pc/gfx/gfx_opengl.c @@ -408,9 +408,10 @@ static uint32_t gfx_cm_to_opengl(uint32_t val) { } static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) { + const GLenum filter = linear_filter ? GL_LINEAR : GL_NEAREST; glActiveTexture(GL_TEXTURE0 + tile); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gfx_cm_to_opengl(cms)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gfx_cm_to_opengl(cmt)); } @@ -480,6 +481,9 @@ static void gfx_opengl_start_frame(void) { glEnable(GL_SCISSOR_TEST); } +static void gfx_opengl_shutdown(void) { +} + struct GfxRenderingAPI gfx_opengl_api = { gfx_opengl_z_is_from_0_to_1, gfx_opengl_unload_shader, @@ -499,5 +503,6 @@ struct GfxRenderingAPI gfx_opengl_api = { gfx_opengl_set_use_alpha, gfx_opengl_draw_triangles, gfx_opengl_init, - gfx_opengl_start_frame + gfx_opengl_start_frame, + gfx_opengl_shutdown }; diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index d674a4d6..3b11a46c 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -18,6 +18,8 @@ #include "gfx_rendering_api.h" #include "gfx_screen_config.h" +#include "../configfile.h" + #define SUPPORT_CHECK(x) assert(x) // SCALE_M_N: upscale/downscale M-bit integer to N-bit @@ -569,10 +571,16 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *verti calculate_normal_dir(&lookat_y, rsp.current_lookat_coeffs[1]); rsp.lights_changed = false; } - - int r = rsp.current_lights[rsp.current_num_lights - 1].col[0]; - int g = rsp.current_lights[rsp.current_num_lights - 1].col[1]; - int b = rsp.current_lights[rsp.current_num_lights - 1].col[2]; + + // Inspired by: + // https://github.com/gonetz/GLideN64/commit/c8cbafff71a81bee5112aaafe6e21d6648ff8125#diff-69d8715ec7f9fd627ec4f5516edd003dL484 + const bool useFirstColor = (dest_index & 1) == 0; + const unsigned char* col = useFirstColor + ? rsp.current_lights[rsp.current_num_lights - 1].col + : rsp.current_lights[rsp.current_num_lights - 1].colc; + int r = col[0]; + int g = col[1]; + int b = col[2]; for (int i = 0; i < rsp.current_num_lights - 1; i++) { float intensity = 0; @@ -581,9 +589,14 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *verti intensity += vn->n[2] * rsp.current_lights_coeffs[i][2]; intensity /= 127.0f; if (intensity > 0.0f) { - r += intensity * rsp.current_lights[i].col[0]; - g += intensity * rsp.current_lights[i].col[1]; - b += intensity * rsp.current_lights[i].col[2]; + // Inspired by: + // https://github.com/gonetz/GLideN64/commit/c8cbafff71a81bee5112aaafe6e21d6648ff8125#diff-69d8715ec7f9fd627ec4f5516edd003dL492 + col = useFirstColor + ? rsp.current_lights[i].col + : rsp.current_lights[i].colc; + r += intensity * col[0]; + g += intensity * col[1]; + b += intensity * col[2]; } } @@ -763,7 +776,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) { import_texture(i); rdp.textures_changed[i] = false; } - bool linear_filter = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT; + bool linear_filter = configFiltering && ((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT); if (linear_filter != rendering_state.textures[i]->linear_filter || rdp.texture_tile.cms != rendering_state.textures[i]->cms || rdp.texture_tile.cmt != rendering_state.textures[i]->cmt) { gfx_flush(); gfx_rapi->set_sampler_parameters(i, linear_filter, rdp.texture_tile.cms, rdp.texture_tile.cmt); @@ -1545,3 +1558,14 @@ void gfx_end_frame(void) { gfx_wapi->swap_buffers_end(); } } + +void gfx_shutdown(void) { + if (gfx_rapi) { + if (gfx_rapi->shutdown) gfx_rapi->shutdown(); + gfx_rapi = NULL; + } + if (gfx_wapi) { + if (gfx_wapi->shutdown) gfx_wapi->shutdown(); + gfx_wapi = NULL; + } +} \ No newline at end of file diff --git a/src/pc/gfx/gfx_pc.h b/src/pc/gfx/gfx_pc.h index 6903597c..8d62e2b7 100644 --- a/src/pc/gfx/gfx_pc.h +++ b/src/pc/gfx/gfx_pc.h @@ -15,5 +15,6 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi); void gfx_start_frame(void); void gfx_run(Gfx *commands); void gfx_end_frame(void); +void gfx_shutdown(void); #endif diff --git a/src/pc/gfx/gfx_rendering_api.h b/src/pc/gfx/gfx_rendering_api.h index 58d79435..75aedef9 100644 --- a/src/pc/gfx/gfx_rendering_api.h +++ b/src/pc/gfx/gfx_rendering_api.h @@ -27,6 +27,7 @@ struct GfxRenderingAPI { void (*draw_triangles)(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris); void (*init)(void); void (*start_frame)(void); + void (*shutdown)(void); }; #endif diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index a794c6d3..57ff486d 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -19,13 +19,16 @@ #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" #include "../configfile.h" +#include "../cliopts.h" #include "src/pc/controller/controller_keyboard.h" static SDL_Window *wnd; +static SDL_GLContext ctx = NULL; static int inverted_scancode_table[512]; -extern bool configFullscreen; +static bool cur_fullscreen; +static uint32_t cur_width, cur_height; const SDL_Scancode windows_scancode_table[] = { @@ -78,20 +81,18 @@ const SDL_Scancode scancode_rmapping_nonextended[][2] = { {SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN} }; -static void gfx_sdl_set_fullscreen(bool fullscreen) -{ - if (fullscreen) - { +static void gfx_sdl_set_fullscreen(bool fullscreen) { + if (fullscreen == cur_fullscreen) return; + + if (fullscreen) { SDL_SetWindowFullscreen(wnd, SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_ShowCursor(SDL_DISABLE); - } - else - { + } else { SDL_SetWindowFullscreen(wnd, 0); SDL_ShowCursor(SDL_ENABLE); } - configFullscreen = fullscreen; + cur_fullscreen = fullscreen; } static void gfx_sdl_init(void) { @@ -113,6 +114,10 @@ static void gfx_sdl_init(void) { window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; + if (gCLIOpts.FullScreen) { + configFullscreen = true; + } + if (configFullscreen) { window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } @@ -185,13 +190,9 @@ static void gfx_sdl_onkeydown(int scancode) { const Uint8 *state = SDL_GetKeyboardState(NULL); if (state[SDL_SCANCODE_LALT] && state[SDL_SCANCODE_RETURN]) - { - gfx_sdl_set_fullscreen(!configFullscreen); - } + configFullscreen = !configFullscreen; else if (state[SDL_SCANCODE_ESCAPE] && configFullscreen) - { - gfx_sdl_set_fullscreen(false); - } + configFullscreen = false; } static void gfx_sdl_onkeyup(int scancode) { @@ -215,6 +216,9 @@ static void gfx_sdl_handle_events(void) { exit(0); } } + // just check if the fullscreen value has changed and toggle fullscreen if it has + if (configFullscreen != cur_fullscreen) + gfx_sdl_set_fullscreen(configFullscreen); } static bool gfx_sdl_start_frame(void) { @@ -232,6 +236,15 @@ static double gfx_sdl_get_time(void) { return 0.0; } + +static void gfx_sdl_shutdown(void) { + if (SDL_WasInit(0)) { + if (ctx) { SDL_GL_DeleteContext(ctx); ctx = NULL; } + if (wnd) { SDL_DestroyWindow(wnd); wnd = NULL; } + SDL_Quit(); + } +} + struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_init, gfx_sdl_main_loop, @@ -240,5 +253,6 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_start_frame, gfx_sdl_swap_buffers_begin, gfx_sdl_swap_buffers_end, - gfx_sdl_get_time + gfx_sdl_get_time, + gfx_sdl_shutdown }; diff --git a/src/pc/gfx/gfx_window_manager_api.h b/src/pc/gfx/gfx_window_manager_api.h index 9c68cd10..45826711 100644 --- a/src/pc/gfx/gfx_window_manager_api.h +++ b/src/pc/gfx/gfx_window_manager_api.h @@ -13,6 +13,7 @@ struct GfxWindowManagerAPI { void (*swap_buffers_begin)(void); void (*swap_buffers_end)(void); double (*get_time)(void); // For debug + void (*shutdown)(void); }; #endif diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 5b4bcd23..93774fe2 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -18,6 +18,7 @@ #include "audio/audio_sdl.h" #include "audio/audio_null.h" +#include "cliopts.h" #include "configfile.h" OSMesg D_80339BEC; @@ -72,11 +73,31 @@ void produce_one_frame(void) { create_next_audio_buffer(audio_buffer + i * (num_audio_samples * 2), num_audio_samples); } //printf("Audio samples before submitting: %d\n", audio_api->buffered()); - audio_api->play(audio_buffer, 2 * num_audio_samples * 4); + + // scale by master volume (0-127) + const s32 mod = (s32)configMasterVolume; + for (u32 i = 0; i < num_audio_samples * 4; ++i) + audio_buffer[i] = ((s32)audio_buffer[i] * mod) >> VOLUME_SHIFT; + + audio_api->play((u8*)audio_buffer, 2 * num_audio_samples * 4); gfx_end_frame(); } +void audio_shutdown(void) { + if (audio_api) { + if (audio_api->shutdown) audio_api->shutdown(); + audio_api = NULL; + } +} + +void game_shutdown(void) { + configfile_save(CONFIG_FILE); + controller_shutdown(); + audio_shutdown(); + gfx_shutdown(); +} + #ifdef TARGET_WEB static void em_main_loop(void) { } @@ -110,17 +131,13 @@ static void on_anim_frame(double time) { } #endif -static void save_config(void) { - configfile_save(CONFIG_FILE); -} - void main_func(void) { static u64 pool[0x165000/8 / 4 * sizeof(void *)]; main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0])); gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT); configfile_load(CONFIG_FILE); - atexit(save_config); + atexit(game_shutdown); #ifdef TARGET_WEB emscripten_set_main_loop(em_main_loop, 0, 0); @@ -155,6 +172,7 @@ void main_func(void) { } int main(int argc, char *argv[]) { + parse_cli_opts(argc, argv); main_func(); return 0; }