diff --git a/Makefile.port b/Makefile.port index b8ebadb12..650f02474 100644 --- a/Makefile.port +++ b/Makefile.port @@ -151,9 +151,15 @@ G_DIR := src/generated/$(ROMID) ifneq (,$(findstring windows,$(TARGET_PLATFORM))) TARGET_CFLAGS := $(shell pkg-config sdl2 --cflags-only-I) - TARGET_LDFLAGS := $(shell pkg-config sdl2 --libs) -lz -lopengl32 -static-libstdc++ -ldbghelp + TARGET_LDFLAGS := $(shell pkg-config sdl2 --libs) -lz -lopengl32 -ldbghelp # on windows/mingw we need this to be built with a 32-bit compiler so it finds the correct libs TOOLCHAIN ?= i686-w64-mingw32- + # on GCC link libstdc++ statically + ifeq (,$(findstring clang,$(CC))$(findstring clang,$(CXX))) + TARGET_LDFLAGS += -static-libstdc++ + else + TARGET_LDFLAGS += -lstdc++ + endif else # assume *nix TARGET_CFLAGS := $(shell pkg-config sdl2 --cflags-only-I) TARGET_LDFLAGS := $(shell pkg-config sdl2 --libs) -lGL -lz -ldl @@ -233,8 +239,8 @@ INCLUDES = \ -I port/include TOOLCHAIN ?= -CC := $(TOOLCHAIN)gcc -std=c11 -CXX := $(TOOLCHAIN)g++ -std=c++20 +CC ?= $(TOOLCHAIN)gcc +CXX ?= $(TOOLCHAIN)g++ ifneq (,$(CXX_O_FILES)) LD := $(CXX) else @@ -250,7 +256,6 @@ COMMON_CFLAGS := $(C_DEFINES) -DAVOID_UB=1 $(INCLUDES) $(TARGET_CFLAGS) -g \ -fwrapv \ -Wall \ -Wno-address \ - -Wno-aggressive-loop-optimizations \ -Wno-int-in-bool-context \ -Wno-misleading-indentation \ -Wno-missing-braces \ @@ -261,8 +266,8 @@ COMMON_CFLAGS := $(C_DEFINES) -DAVOID_UB=1 $(INCLUDES) $(TARGET_CFLAGS) -g \ -Wno-unused-variable \ -Wno-format-truncation -CFLAGS := $(COMMON_CFLAGS) -Wno-pointer-sign -CXXFLAGS := $(COMMON_CFLAGS) -Wno-unused-function +CFLAGS := -std=c11 $(COMMON_CFLAGS) -Wno-pointer-sign +CXXFLAGS := -std=c++20 $(COMMON_CFLAGS) -Wno-unused-function LDFLAGS := $(TARGET_LDFLAGS) # Make ROMID available as an environment variable to all tooling. @@ -271,7 +276,7 @@ export ROMID BIN := $(B_DIR)/$(BIN_NAME) -default: $(JSON_HEADERS) $(BIN) +default: $(BIN) ################################################################################ # Asset Manager header generation @@ -309,11 +314,11 @@ ifneq ($(CV2PDB),) $(CV2PDB) $(BIN) endif -$(B_DIR)/%.o: %.c +$(B_DIR)/%.o: %.c $(JSON_HEADERS) @mkdir -p $(dir $@) $(CC) -c $(CFLAGS) $(OPT_LVL) -o $@ $< -$(B_DIR)/%.o: %.cpp +$(B_DIR)/%.o: %.cpp $(JSON_HEADERS) @mkdir -p $(dir $@) $(CXX) -c $(CXXFLAGS) $(OPT_LVL) -o $@ $< diff --git a/README.md b/README.md index 3451264ff..4c4e4913a 100644 --- a/README.md +++ b/README.md @@ -93,24 +93,32 @@ Controls can be rebound in `pd.ini`. Default control scheme is as follows: 1. Install [MSYS2](https://www.msys2.org). 2. Open the `MINGW32` prompt. (**NOTE:** _not_ the `MSYS` prompt or the `MINGW64` prompt) -3. Install dependencies: `pacman -S mingw-w64-i686-toolchain mingw-w64-i686-SDL2 mingw-w64-i686-zlib make git` -4. Run `make -f Makefile.port` in the `perfect_dark` directory. -5. The resulting executable will be at `build/ntsc-final-port/pd.exe`. +3. Install dependencies: + `pacman -S mingw-w64-i686-toolchain mingw-w64-i686-SDL2 mingw-w64-i686-zlib make git` +4. Get the source code: + `git clone --recursive https://github.com/fgsfdsfgs/perfect_dark.git && cd perfect_dark` +5. Run `make -f Makefile.port`. + * Add ` ROMID=pal-final` or ` ROMID=jpn-final` at the end of the command if you want to build a PAL or JPN executable respectively. +6. The resulting executable will be at `build/ntsc-final-port/pd.exe`. +7. If you don't know where you downloaded the source to, you can run `explorer .` to open the current directory. ### Linux 1. Ensure you have gcc, g++ (version 10.0+) and 32-bit versions of SDL2 (version 2.0.12+), libGL and ZLib installed on your system. * On a 64-bit system you also need to have `gcc-multilib` and `g++-multilib` (or your distro's analogues) installed. -2. Run the following command in the `perfect_dark` directory: +2. Get the source code: + `git clone --recursive https://github.com/fgsfdsfgs/perfect_dark.git && cd perfect_dark` +3. Run the following command: * On a 64-bit system: ```make -f Makefile.port TARGET_PLATFORM=i686-linux``` * On a 32-bit system: ```make -f Makefile.port``` -3. The resulting executable will be at `build/ntsc-final-port/pd.exe`. + * Add ` ROMID=pal-final` or ` ROMID=jpn-final` at the end of the command if you want to build a PAL or JPN executable respectively. +4. The resulting executable will be at `build/ntsc-final-port/pd.exe`. Currently only `i686-linux` and `i686-windows` are supported, using `gcc -m32` and `i686-w64-mingw32-gcc` as compilers, respectively. Alternate compilers can be specified by passing `TOOLCHAIN=i686-whatever-` as a command line argument. You can build an executable with PAL or JPN ROM support by adding `ROMID=pal-final` or `ROMID=jpn-final` to the `make` command. -You will need to provide a `jpn-final` or `pal-final` ROM for those, named `pd.jpn-final.z64` or `pd.pal-final.z64`. +You will need to provide a `jpn-final` or `pal-final` ROM to run those, named `pd.jpn-final.z64` or `pd.pal-final.z64`. It might be possible to build a 32-bit ARM executable, but this has not been tested. diff --git a/include/PR/os_libc.h b/include/PR/os_libc.h index 23a13d72e..6ec0f4db8 100644 --- a/include/PR/os_libc.h +++ b/include/PR/os_libc.h @@ -32,6 +32,10 @@ #include "os_pfs.h" +#ifndef PLATFORM_N64 +#include +#endif + #ifdef _LANGUAGE_C_PLUS_PLUS extern "C" { #endif @@ -86,7 +90,9 @@ extern void bzero(void *, size_t); /* Printf */ +#ifdef PLATFORM_N64 extern int sprintf(char *s, const char *fmt, ...); +#endif extern void osSyncPrintf(const char *fmt, ...); diff --git a/port/fast3d/gfx_api.h b/port/fast3d/gfx_api.h index 3bf8427ad..279a684ef 100644 --- a/port/fast3d/gfx_api.h +++ b/port/fast3d/gfx_api.h @@ -20,6 +20,12 @@ struct GfxDimensions { float aspect_ratio; }; +struct GfxInitSettings { + struct GfxWindowManagerAPI *wapi; + struct GfxRenderingAPI *rapi; + struct GfxWindowInitSettings window_settings; +}; + extern struct GfxDimensions gfx_current_window_dimensions; // The dimensions of the window extern struct GfxDimensions gfx_current_dimensions; // The dimensions of the draw area the game draws to, before scaling (if applicable) @@ -29,10 +35,9 @@ extern uint32_t gfx_msaa_level; extern struct XYWidthHeight gfx_current_native_viewport; // The internal/native video mode of the game extern float gfx_current_native_aspect; // The aspect ratio of the above mode extern bool gfx_framebuffers_enabled; +extern bool gfx_detail_textures_enabled; -void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name, - bool start_in_fullscreen, bool start_maximized, uint32_t width, uint32_t height, - uint32_t posX, uint32_t posY); +void gfx_init(const struct GfxInitSettings *settings); void gfx_destroy(void); struct GfxRenderingAPI* gfx_get_current_rendering_api(void); void gfx_start_frame(void); diff --git a/port/fast3d/gfx_cc.cpp b/port/fast3d/gfx_cc.cpp index 066612843..6aaaa655e 100644 --- a/port/fast3d/gfx_cc.cpp +++ b/port/fast3d/gfx_cc.cpp @@ -26,10 +26,6 @@ void gfx_cc_get_features(uint64_t shader_id0, uint32_t shader_id1, struct CCFeat cc_features->used_textures[0] = false; cc_features->used_textures[1] = false; - cc_features->used_masks[0] = false; - cc_features->used_masks[1] = false; - cc_features->used_blend[0] = false; - cc_features->used_blend[1] = false; cc_features->num_inputs = 0; for (int c = 0; c < 2; c++) { @@ -60,18 +56,4 @@ void gfx_cc_get_features(uint64_t shader_id0, uint32_t shader_id1, struct CCFeat cc_features->color_alpha_same[c] = ((shader_id0 >> c * 32) & 0xffff) == ((shader_id0 >> (c * 32 + 16)) & 0xffff); } - - if (cc_features->used_textures[0] && (shader_id1 & SHADER_OPT_TEXEL0_MASK)) { - cc_features->used_masks[0] = true; - } - if (cc_features->used_textures[1] && (shader_id1 & SHADER_OPT_TEXEL1_MASK)) { - cc_features->used_masks[1] = true; - } - - if (cc_features->used_textures[0] && (shader_id1 & SHADER_OPT_TEXEL0_BLEND)) { - cc_features->used_blend[0] = true; - } - if (cc_features->used_textures[1] && (shader_id1 & SHADER_OPT_TEXEL1_BLEND)) { - cc_features->used_blend[1] = true; - } } diff --git a/port/fast3d/gfx_cc.h b/port/fast3d/gfx_cc.h index cfd00c9f6..5fd4797c7 100644 --- a/port/fast3d/gfx_cc.h +++ b/port/fast3d/gfx_cc.h @@ -8,17 +8,6 @@ #include #endif -/*enum { - CC_0, - CC_TEXEL0, - CC_TEXEL1, - CC_PRIM, - CC_SHADE, - CC_ENV, - CC_TEXEL0A, - CC_LOD -};*/ - enum { SHADER_0, SHADER_INPUT_1, @@ -34,7 +23,8 @@ enum { SHADER_TEXEL1A, SHADER_1, SHADER_COMBINED, - SHADER_NOISE + SHADER_NOISE, + SHADER_LOD_FRAC, }; #define SHADER_OPT_ALPHA (1 << 0) @@ -49,11 +39,7 @@ enum { #define SHADER_OPT_TEXEL0_CLAMP_T (1 << 9) #define SHADER_OPT_TEXEL1_CLAMP_S (1 << 10) #define SHADER_OPT_TEXEL1_CLAMP_T (1 << 11) -#define SHADER_OPT_TEXEL0_MASK (1 << 12) -#define SHADER_OPT_TEXEL1_MASK (1 << 13) -#define SHADER_OPT_TEXEL0_BLEND (1 << 14) -#define SHADER_OPT_TEXEL1_BLEND (1 << 15) -#define SHADER_OPT_BLUR (1 << 16) +#define SHADER_OPT_BLUR (1 << 12) struct ColorCombinerKey { uint64_t combine_mode; @@ -61,10 +47,8 @@ struct ColorCombinerKey { auto operator<=>(const ColorCombinerKey&) const = default; }; -#define SHADER_MAX_TEXTURES 6 +#define SHADER_MAX_TEXTURES 2 #define SHADER_FIRST_TEXTURE 0 -#define SHADER_FIRST_MASK_TEXTURE 2 -#define SHADER_FIRST_REPLACEMENT_TEXTURE 4 struct CCFeatures { uint8_t c[2][2][4]; @@ -78,8 +62,6 @@ struct CCFeatures { bool opt_grayscale; bool opt_blur; bool used_textures[2]; - bool used_masks[2]; - bool used_blend[2]; bool clamp[2][2]; int num_inputs; bool do_single[2][2]; diff --git a/port/fast3d/gfx_opengl.cpp b/port/fast3d/gfx_opengl.cpp index 86efd96bc..f486628a5 100644 --- a/port/fast3d/gfx_opengl.cpp +++ b/port/fast3d/gfx_opengl.cpp @@ -373,18 +373,6 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad if (cc_features.used_textures[1]) { append_line(fs_buf, &fs_len, "uniform sampler2D uTex1;"); } - if (cc_features.used_masks[0]) { - append_line(fs_buf, &fs_len, "uniform sampler2D uTexMask0;"); - } - if (cc_features.used_masks[1]) { - append_line(fs_buf, &fs_len, "uniform sampler2D uTexMask1;"); - } - if (cc_features.used_blend[0]) { - append_line(fs_buf, &fs_len, "uniform sampler2D uTexBlend0;"); - } - if (cc_features.used_blend[1]) { - append_line(fs_buf, &fs_len, "uniform sampler2D uTexBlend1;"); - } append_line(fs_buf, &fs_len, "uniform int frame_count;"); append_line(fs_buf, &fs_len, "uniform float noise_scale;"); @@ -473,24 +461,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad } } - fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = hookTexture2D(uTex%d, vTexCoordAdj%d, texSize%d);\n", i, - i, i, i); - if (cc_features.used_masks[i]) { - fs_len += sprintf(fs_buf + fs_len, "vec2 maskSize%d = textureSize(uTexMask%d, 0);\n", i, i); - - fs_len += - sprintf(fs_buf + fs_len, - "vec4 maskVal%d = hookTexture2D(uTexMask%d, vTexCoordAdj%d, maskSize%d);\n", i, i, i, i); - if (cc_features.used_blend[i]) { - fs_len += sprintf(fs_buf + fs_len, - "vec4 blendVal%d = hookTexture2D(uTexBlend%d, vTexCoordAdj%d, texSize%d);\n", i, - i, i, i); - } else { - fs_len += sprintf(fs_buf + fs_len, "vec4 blendVal%d = vec4(0, 0, 0, 0);\n", i); - } - - fs_len += sprintf(fs_buf + fs_len, "texVal%d = mix(texVal%d, blendVal%d, maskVal%d.a);\n", i, i, i, i); - } + fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = hookTexture2D(uTex%d, vTexCoordAdj%d, texSize%d);\n", i, i, i, i); } } @@ -653,10 +624,6 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad prg->num_inputs = cc_features.num_inputs; prg->used_textures[0] = cc_features.used_textures[0]; prg->used_textures[1] = cc_features.used_textures[1]; - prg->used_textures[2] = cc_features.used_masks[0]; - prg->used_textures[3] = cc_features.used_masks[1]; - prg->used_textures[4] = cc_features.used_blend[0]; - prg->used_textures[5] = cc_features.used_blend[1]; prg->num_floats = num_floats; prg->num_attribs = cnt; @@ -670,22 +637,6 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad GLint sampler_location = glGetUniformLocation(shader_program, "uTex1"); glUniform1i(sampler_location, 1); } - if (cc_features.used_masks[0]) { - GLint sampler_location = glGetUniformLocation(shader_program, "uTexMask0"); - glUniform1i(sampler_location, 2); - } - if (cc_features.used_masks[1]) { - GLint sampler_location = glGetUniformLocation(shader_program, "uTexMask1"); - glUniform1i(sampler_location, 3); - } - if (cc_features.used_blend[0]) { - GLint sampler_location = glGetUniformLocation(shader_program, "uTexBlend0"); - glUniform1i(sampler_location, 4); - } - if (cc_features.used_blend[1]) { - GLint sampler_location = glGetUniformLocation(shader_program, "uTexBlend1"); - glUniform1i(sampler_location, 5); - } prg->frame_count_location = glGetUniformLocation(shader_program, "frame_count"); prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale"); diff --git a/port/fast3d/gfx_pc.cpp b/port/fast3d/gfx_pc.cpp index cdd0d3e3c..7938fdd77 100644 --- a/port/fast3d/gfx_pc.cpp +++ b/port/fast3d/gfx_pc.cpp @@ -33,8 +33,6 @@ uintptr_t gfxFramebuffer; -using namespace std; - #define ALIGN(x, a) (((x) + (a - 1)) & ~(a - 1)) #define SUPPORT_CHECK(x) assert(x) @@ -55,8 +53,7 @@ using namespace std; #define RATIO_Y (gfx_current_dimensions.height / (float)SCREEN_HEIGHT) #define MAX_BUFFERED 256 -// #define MAX_LIGHTS 2 -#define MAX_LIGHTS 32 +#define MAX_LIGHTS 4 #define MAX_VERTICES 128 #define MAX_VERTEX_COLORS 64 @@ -86,8 +83,8 @@ struct LoadedVertex { static struct { TextureCacheMap map; - list lru; - vector free_texture_ids; + std::list lru; + std::vector free_texture_ids; } gfx_texture_cache; struct ColorCombiner { @@ -98,8 +95,8 @@ struct ColorCombiner { uint8_t shader_input_mapping[2][7]; }; -static map color_combiner_pool; -static map::iterator prev_combiner = color_combiner_pool.end(); +static std::map color_combiner_pool; +static std::map::iterator prev_combiner = color_combiner_pool.end(); static uint8_t* tex_upload_buffer = nullptr; @@ -154,8 +151,6 @@ struct LoadedTexture { uint32_t line_size_bytes; uint32_t tex_flags; struct RawTexMetadata raw_tex_metadata; - bool masked; - bool blended; }; static struct RDP { @@ -184,6 +179,8 @@ static struct RDP { bool textures_changed[2]; uint8_t first_tile_index; + uint8_t tex_min_lod; + uint8_t tex_max_lod; uint32_t other_mode_l, other_mode_h; uint64_t combine_mode; @@ -219,6 +216,7 @@ struct XYWidthHeight gfx_current_game_window_viewport; struct XYWidthHeight gfx_current_native_viewport; float gfx_current_native_aspect = 4.f / 3.f; bool gfx_framebuffers_enabled = true; +bool gfx_detail_textures_enabled = true; static bool game_renders_to_framebuffer; static int game_framebuffer; @@ -226,8 +224,6 @@ static int game_framebuffer_msaa_resolved; uint32_t gfx_msaa_level = 1; -static bool has_drawn_imgui_menu; - static bool dropped_frame; static float buf_vbo[MAX_BUFFERED * (32 * 3)]; // 3 vertices in a triangle and 32 floats per vtx @@ -237,7 +233,6 @@ static size_t buf_vbo_num_tris; static struct GfxWindowManagerAPI* gfx_wapi; static struct GfxRenderingAPI* gfx_rapi; -static int markerOn; static uintptr_t segmentPointers[16]; struct FBInfo { @@ -247,8 +242,8 @@ struct FBInfo { }; static bool fbActive = 0; -static map::iterator active_fb; -static map framebuffers; +static std::map::iterator active_fb; +static std::map framebuffers; static constexpr float clampf(const float x, const float min, const float max) { return (x < min) ? min : (x > max) ? max : x; @@ -507,7 +502,7 @@ static struct ColorCombiner* gfx_lookup_or_create_color_combiner(const ColorComb return &prev_combiner->second; } gfx_flush(); - prev_combiner = color_combiner_pool.insert(make_pair(key, ColorCombiner())).first; + prev_combiner = color_combiner_pool.insert(std::make_pair(key, ColorCombiner())).first; gfx_generate_cc(&prev_combiner->second, key); return &prev_combiner->second; } @@ -551,7 +546,7 @@ static bool gfx_texture_cache_lookup(int i, const TextureCacheKey& key) { texture_id = gfx_rapi->new_texture(); } - it = gfx_texture_cache.map.insert(make_pair(key, TextureCacheValue())).first; + it = gfx_texture_cache.map.insert(std::make_pair(key, TextureCacheValue())).first; TextureCacheNode* node = &*it; node->second.texture_id = texture_id; node->second.lru_location = gfx_texture_cache.lru.insert(gfx_texture_cache.lru.end(), { it }); @@ -859,7 +854,7 @@ static void import_texture(int i, int tile, bool importReplacement) { const uint32_t tex_flags = loaded_texture.tex_flags; const uint8_t palette_index = rdp.texture_tile[tile].palette; - if (rdp.tex_lod || !loaded_texture.addr) { + if ((rdp.tex_lod && tile >= rdp.first_tile_index + rdp.tex_detail) || !loaded_texture.addr) { // set up miplevel 0; also acts as a catch-all for when .addr is NULL because my texture loader sucks loaded_texture.addr = rdp.texture_to_load.addr; loaded_texture.line_size_bytes = rdp.texture_tile[tile].line_size_bytes; @@ -1191,14 +1186,18 @@ static void gfx_sp_modify_vertex(uint16_t vtx_idx, uint8_t where, uint32_t val) v->v = t; } +static inline int gfx_lod_tile_offset(const int i) { + if (gfx_detail_textures_enabled) + return ((rdp.tex_lod && !rdp.tex_detail) ? 0 : i); + return (rdp.tex_lod ? rdp.tex_detail : i); +} + static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bool is_rect) { struct LoadedVertex* v1 = &rsp.loaded_vertices[vtx1_idx]; struct LoadedVertex* v2 = &rsp.loaded_vertices[vtx2_idx]; struct LoadedVertex* v3 = &rsp.loaded_vertices[vtx3_idx]; struct LoadedVertex* v_arr[3] = { v1, v2, v3 }; - // if (rand()%2) return; - if ((rsp.extra_geometry_mode & G_NO_CLIPPING_EXT) == 0) { if (v1->clip_rej & v2->clip_rej & v3->clip_rej) { // The whole triangle lies outside the visible area @@ -1276,20 +1275,18 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo rdp.viewport_or_scissor_changed = false; } - uint64_t cc_id = rdp.combine_mode; uint64_t cc_options = 0; bool use_alpha = (rdp.other_mode_l & (3 << 20)) == (G_BL_CLR_MEM << 20) && (rdp.other_mode_l & (3 << 16)) == (G_BL_1MA << 16); - bool use_fog = ((rdp.other_mode_l >> 30) == G_BL_CLR_FOG) || ((rdp.other_mode_l >> 26) == G_BL_A_FOG); - bool texture_edge = (rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA; - bool use_noise = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_DITHER; - bool use_2cyc = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_2CYCLE; - bool alpha_threshold = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_THRESHOLD; - bool invisible = - (rdp.other_mode_l & (3 << 24)) == (G_BL_0 << 24) && (rdp.other_mode_l & (3 << 20)) == (G_BL_CLR_MEM << 20); - bool use_grayscale = rdp.grayscale; - bool use_modulate = use_alpha && (rsp.extra_geometry_mode & G_MODULATE_EXT) != 0; - bool use_blur = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) == G_TF_BLUR_EXT; + const bool use_fog = ((rdp.other_mode_l >> 30) == G_BL_CLR_FOG) || ((rdp.other_mode_l >> 26) == G_BL_A_FOG); + const bool texture_edge = (rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA; + const bool use_noise = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_DITHER; + const bool use_2cyc = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_2CYCLE; + const bool alpha_threshold = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_THRESHOLD; + const bool invisible = (rdp.other_mode_l & (3 << 24)) == (G_BL_0 << 24) && (rdp.other_mode_l & (3 << 20)) == (G_BL_CLR_MEM << 20); + const bool use_grayscale = rdp.grayscale; + const bool use_modulate = use_alpha && (rsp.extra_geometry_mode & G_MODULATE_EXT) != 0; + const bool use_blur = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) == G_TF_BLUR_EXT; if (texture_edge) { use_alpha = true; @@ -1339,7 +1336,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo for (int i = 0; i < 2; i++) { // TODO: fix this; for now just ignore smaller mips - const uint32_t tile = rdp.first_tile_index + (rdp.tex_lod ? rdp.tex_detail : i); + const uint32_t tile = rdp.first_tile_index + gfx_lod_tile_offset(i); if (comb->used_textures[i]) { if (rdp.textures_changed[i]) { gfx_flush(); @@ -1385,7 +1382,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo cms &= ~G_TX_CLAMP; } if ((cmt & G_TX_CLAMP) && ((cmt & G_TX_MIRROR) || tex_height1 != tex_height2[i])) { - tm |= 1 << 2 * i + 1; + tm |= 1 << (2 * i + 1); cmt &= ~G_TX_CLAMP; } @@ -1444,7 +1441,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo } // TODO: fix this; for now just ignore smaller mips - const uint32_t tile = (rdp.tex_lod ? rdp.tex_detail : t); + const uint32_t tile = gfx_lod_tile_offset(t); float u = v_arr[i]->u / 32.0f; float v = v_arr[i]->v / 32.0f; @@ -1486,24 +1483,14 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo buf_vbo[buf_vbo_len++] = v / tex_height[t]; bool clampS = tm & (1 << 2 * t); - bool clampT = tm & (1 << 2 * t + 1); + bool clampT = tm & (1 << (2 * t + 1)); if (clampS) { buf_vbo[buf_vbo_len++] = (tex_width2[t] - 0.5f) / tex_width[t]; } -#ifdef __WIIU__ - else { - buf_vbo[buf_vbo_len++] = 0.0f; - } -#endif if (clampT) { buf_vbo[buf_vbo_len++] = (tex_height2[t] - 0.5f) / tex_height[t]; } -#ifdef __WIIU__ - else { - buf_vbo[buf_vbo_len++] = 0.0f; - } -#endif } if (use_fog) { @@ -1557,15 +1544,10 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo } case G_CCMUX_LOD_FRACTION: { if (rdp.other_mode_h & G_TL_LOD) { - // "Hack" that works for Bowser - Peach painting - float distance_frac = (v1->w - 3000.0f) / 3000.0f; - if (distance_frac < 0.0f) { - distance_frac = 0.0f; - } - if (distance_frac > 1.0f) { - distance_frac = 1.0f; - } - tmp.r = tmp.g = tmp.b = tmp.a = distance_frac * 255.0f; + // HACK: very roughly eyeballed based on the carpets in Defection + // this is actually supposed to be calculated per pixel + const float distance_frac = std::max(0.f, std::min(w / 1024.f, 1.f)); + tmp.r = tmp.g = tmp.b = tmp.a = (0.7f + distance_frac * 0.3f) * 255.f; } else { tmp.r = tmp.g = tmp.b = tmp.a = 255; } @@ -1585,27 +1567,14 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo buf_vbo[buf_vbo_len++] = color->r / 255.0f; buf_vbo[buf_vbo_len++] = color->g / 255.0f; buf_vbo[buf_vbo_len++] = color->b / 255.0f; -#ifdef __WIIU__ - // padding - if (!use_alpha) { - buf_vbo[buf_vbo_len++] = 1.0f; - } -#endif } else { buf_vbo[buf_vbo_len++] = color->a / 255.0f; } } } - - // struct RGBA *color = &v_arr[i]->color; - // buf_vbo[buf_vbo_len++] = color->r / 255.0f; - // buf_vbo[buf_vbo_len++] = color->g / 255.0f; - // buf_vbo[buf_vbo_len++] = color->b / 255.0f; - // buf_vbo[buf_vbo_len++] = color->a / 255.0f; } if (++buf_vbo_num_tris == MAX_BUFFERED) { - // if (++buf_vbo_num_tris == 1) { gfx_flush(); } } @@ -1740,38 +1709,21 @@ static void gfx_sp_movemem(uint8_t index, uint8_t offset, const void* data) { rsp.lookat_enabled = (index == 0) || (rsp.lookat[1].dir[0] || rsp.lookat[1].dir[1]); rsp.lights_changed = true; break; -#ifdef F3DEX_GBI_2 - case G_MV_LIGHT: { - int lightidx = offset / 24 - 2; - if (lightidx >= 0 && lightidx <= MAX_LIGHTS) { // skip lookat - // NOTE: reads out of bounds if it is an ambient light - memcpy(rsp.current_lights + lightidx, data, sizeof(Light_t)); - } else if (lightidx < 0) { - memcpy(rsp.lookat + offset / 24, data, sizeof(Light_t)); - } - break; - } -#else case G_MV_L0: case G_MV_L1: case G_MV_L2: // NOTE: reads out of bounds if it is an ambient light memcpy(rsp.current_lights + (index - G_MV_L0) / 2, data, sizeof(Light_t)); break; -#endif } } static void gfx_sp_moveword(uint8_t index, uint16_t offset, uintptr_t data) { switch (index) { case G_MW_NUMLIGHT: -#ifdef F3DEX_GBI_2 - rsp.current_num_lights = data / 24 + 1; // add ambient light -#else // Ambient light is included // The 31th bit is a flag that lights should be recalculated rsp.current_num_lights = (data - 0x80000000U) / 32; -#endif rsp.lights_changed = 1; break; case G_MW_FOG: @@ -1799,12 +1751,12 @@ static void gfx_sp_moveword(uint8_t index, uint16_t offset, uintptr_t data) { static void gfx_sp_texture(uint16_t sc, uint16_t tc, uint8_t level, uint8_t tile, uint8_t on) { rsp.texture_scaling_factor.s = sc; rsp.texture_scaling_factor.t = tc; + rdp.tex_max_lod = level; if (rdp.first_tile_index != tile) { rdp.textures_changed[0] = true; rdp.textures_changed[1] = true; + rdp.first_tile_index = tile; } - - rdp.first_tile_index = tile; } static void gfx_dp_set_scissor(uint32_t mode, uint32_t ulx, uint32_t uly, uint32_t lrx, uint32_t lry) { @@ -1937,8 +1889,6 @@ static void gfx_dp_load_block(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t loaded_texture.tex_flags = rdp.texture_to_load.tex_flags; loaded_texture.raw_tex_metadata = rdp.texture_to_load.raw_tex_metadata; loaded_texture.addr = rdp.texture_to_load.addr; - loaded_texture.masked = false; - loaded_texture.blended = false; rdp.textures_changed[0] = rdp.textures_changed[1] = true; } @@ -1979,8 +1929,6 @@ static void gfx_dp_load_tile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t loaded_texture.tex_flags = rdp.texture_to_load.tex_flags; loaded_texture.raw_tex_metadata = rdp.texture_to_load.raw_tex_metadata; loaded_texture.addr = rdp.texture_to_load.addr + start_offset_bytes; - loaded_texture.masked = false; - loaded_texture.blended = false; rdp.texture_tile[tile].uls = uls; rdp.texture_tile[tile].ult = ult; @@ -1992,38 +1940,6 @@ static void gfx_dp_load_tile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t rdp.textures_changed[0] = rdp.textures_changed[1] = true; } -/*static uint8_t color_comb_component(uint32_t v) { - switch (v) { - case G_CCMUX_TEXEL0: - return CC_TEXEL0; - case G_CCMUX_TEXEL1: - return CC_TEXEL1; - case G_CCMUX_PRIMITIVE: - return CC_PRIM; - case G_CCMUX_SHADE: - return CC_SHADE; - case G_CCMUX_ENVIRONMENT: - return CC_ENV; - case G_CCMUX_TEXEL0_ALPHA: - return CC_TEXEL0A; - case G_CCMUX_LOD_FRACTION: - return CC_LOD; - default: - return CC_0; - } -} - -static inline uint32_t color_comb(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { - return color_comb_component(a) | - (color_comb_component(b) << 3) | - (color_comb_component(c) << 6) | - (color_comb_component(d) << 9); -} - -static void gfx_dp_set_combine_mode(uint32_t rgb, uint32_t alpha) { - rdp.combine_mode = rgb | (alpha << 12); -}*/ - static void gfx_dp_set_combine_mode(uint32_t rgb, uint32_t alpha, uint32_t rgb_cyc2, uint32_t alpha_cyc2) { rdp.combine_mode = rgb | (alpha << 16) | ((uint64_t)rgb_cyc2 << 28) | ((uint64_t)alpha_cyc2 << 44); } @@ -2060,6 +1976,8 @@ static void gfx_dp_set_prim_color(uint8_t m, uint8_t l, uint8_t r, uint8_t g, ui rdp.fill_color.g = g; rdp.fill_color.b = b; rdp.fill_color.a = a; + rdp.tex_min_lod = m; + } static void gfx_dp_set_fog_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { @@ -2153,7 +2071,6 @@ static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lr static void gfx_dp_texture_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry, uint8_t tile, int16_t uls, int16_t ult, int16_t dsdx, int16_t dtdy, bool flip) { - // printf("render %d at %d\n", tile, lrx); uint64_t saved_combine_mode = rdp.combine_mode; if ((rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_COPY) { // Per RDP Command Summary Set Tile's shift s and this dsdx should be set to 4 texels @@ -2363,49 +2280,23 @@ static void gfx_run_dl(Gfx* cmd) { case G_NOOP: break; case G_MTX: { -#ifdef F3DEX_GBI_2 - gfx_sp_matrix(C0(0, 8) ^ G_MTX_PUSH, (const int32_t*)seg_addr(cmd->words.w1)); -#else gfx_sp_matrix(C0(16, 8), (const int32_t*)seg_addr(cmd->words.w1)); -#endif break; } case (uint8_t)G_POPMTX: -#ifdef F3DEX_GBI_2 - gfx_sp_pop_matrix(cmd->words.w1 / 64); -#else gfx_sp_pop_matrix(1); -#endif break; case G_MOVEMEM: -#ifdef F3DEX_GBI_2 - gfx_sp_movemem(C0(0, 8), C0(8, 8) * 8, seg_addr(cmd->words.w1)); -#else gfx_sp_movemem(C0(16, 8), 0, seg_addr(cmd->words.w1)); -#endif break; case (uint8_t)G_MOVEWORD: -#ifdef F3DEX_GBI_2 - gfx_sp_moveword(C0(16, 8), C0(0, 16), cmd->words.w1); -#else gfx_sp_moveword(C0(0, 8), C0(8, 16), cmd->words.w1); -#endif break; case (uint8_t)G_TEXTURE: -#ifdef F3DEX_GBI_2 - gfx_sp_texture(C1(16, 16), C1(0, 16), C0(11, 3), C0(8, 3), C0(1, 7)); -#else gfx_sp_texture(C1(16, 16), C1(0, 16), C0(11, 3), C0(8, 3), C0(0, 8)); -#endif break; case G_VTX: -#ifdef F3DEX_GBI_2 - gfx_sp_vertex(C0(12, 8), C0(1, 7) - C0(12, 8), (const Vtx*)seg_addr(cmd->words.w1)); -#elif defined(F3DEX_GBI) || defined(F3DLP_GBI) - gfx_sp_vertex(C0(10, 6), C0(16, 8) / 2, (const Vtx*)seg_addr(cmd->words.w1)); -#else gfx_sp_vertex(C0(0, 16) / sizeof(Vtx), C0(16, 4), (const Vtx*)seg_addr(cmd->words.w1)); -#endif break; case G_DL: if (C0(16, 1) == 0) { @@ -2421,17 +2312,7 @@ static void gfx_run_dl(Gfx* cmd) { } break; case (uint8_t)G_ENDDL: - - // if (markerOn) - // printf("END DL ON MARKER\n"); - - markerOn = false; return; -#ifdef F3DEX_GBI_2 - case G_GEOMETRYMODE: - gfx_sp_geometry_mode(~C0(0, 24), cmd->words.w1); - break; -#else case (uint8_t)G_SETGEOMETRYMODE: gfx_sp_geometry_mode(0, cmd->words.w1); break; @@ -2441,43 +2322,17 @@ static void gfx_run_dl(Gfx* cmd) { case G_EXTRAGEOMETRYMODE_EXT: gfx_sp_extra_geometry_mode(~C0(0, 24), cmd->words.w1); break; -#endif case (uint8_t)G_TRI1: -#ifdef F3DEX_GBI_2 - gfx_sp_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2, false); -#elif defined(F3DEX_GBI) || defined(F3DLP_GBI) - gfx_sp_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2, false); -#else gfx_sp_tri1(C1(16, 8) / 10, C1(8, 8) / 10, C1(0, 8) / 10, false); -#endif break; -#ifdef F3DEX_GBI_2 - case G_QUAD: { - [[fallthrough]]; - } -#endif -#if defined(F3DEX_GBI) || defined(F3DLP_GBI) - case (uint8_t)G_TRI2: - gfx_sp_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2, false); - gfx_sp_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2, false); - break; -#endif case (uint8_t)G_TRI4: gfx_sp_tri4(cmd); break; case (uint8_t)G_SETOTHERMODE_L: -#ifdef F3DEX_GBI_2 - gfx_sp_set_other_mode(31 - C0(8, 8) - C0(0, 8), C0(0, 8) + 1, cmd->words.w1); -#else gfx_sp_set_other_mode(C0(8, 8), C0(0, 8), cmd->words.w1); -#endif break; case (uint8_t)G_SETOTHERMODE_H: -#ifdef F3DEX_GBI_2 - gfx_sp_set_other_mode(63 - C0(8, 8) - C0(0, 8), C0(0, 8) + 1, (uint64_t)cmd->words.w1 << 32); -#else gfx_sp_set_other_mode(C0(8, 8) + 32, C0(0, 8), (uint64_t)cmd->words.w1 << 32); -#endif break; case G_COL: gfx_sp_set_vertex_colors(C0(0, 16) / 4, (NormalColor *)seg_addr(cmd->words.w1)); @@ -2540,19 +2395,6 @@ static void gfx_run_dl(Gfx* cmd) { case G_TEXRECTFLIP: { int32_t lrx, lry, tile, ulx, uly; uint32_t uls, ult, dsdx, dtdy; -#ifdef F3DEX_GBI_2E - lrx = (int32_t)(C0(0, 24) << 8) >> 8; - lry = (int32_t)(C1(0, 24) << 8) >> 8; - tile = C1(24, 3); - ++cmd; - ulx = (int32_t)(C0(0, 24) << 8) >> 8; - uly = (int32_t)(C1(0, 24) << 8) >> 8; - ++cmd; - uls = C0(16, 16); - ult = C0(0, 16); - dsdx = C1(16, 16); - dtdy = C1(0, 16); -#else lrx = C0(12, 12); lry = C0(0, 12); tile = C1(24, 3); @@ -2564,26 +2406,12 @@ static void gfx_run_dl(Gfx* cmd) { ++cmd; dsdx = C1(16, 16); dtdy = C1(0, 16); -#endif gfx_dp_texture_rectangle(ulx, uly, lrx, lry, tile, uls, ult, dsdx, dtdy, opcode == G_TEXRECTFLIP); break; } case G_FILLRECT: -#ifdef F3DEX_GBI_2E - { - int32_t lrx, lry, ulx, uly; - lrx = (int32_t)(C0(0, 24) << 8) >> 8; - lry = (int32_t)(C1(0, 24) << 8) >> 8; - ++cmd; - ulx = (int32_t)(C0(0, 24) << 8) >> 8; - uly = (int32_t)(C1(0, 24) << 8) >> 8; - gfx_dp_fill_rectangle(ulx, uly, lrx, lry); - break; - } -#else gfx_dp_fill_rectangle(C1(12, 12), C1(0, 12), C0(12, 12), C0(0, 12)); break; -#endif case G_FILLRECT_WIDE_EXT: { int32_t lrx, lry, ulx, uly; lrx = (int32_t)(C0(0, 24) << 8) >> 8; @@ -2669,8 +2497,9 @@ static void gfx_run_dl(Gfx* cmd) { case (uint8_t)G_RDPHALF_1: case (uint8_t)G_RDPHALF_2: case (uint8_t)G_RDPHALF_CONT: - // TODO: skyRender uses these to render some types of skies and skybox water + // on N64 skyRender uses these to render some types of skies and skybox water // by issuing low-level ucode commands G_TRI_FILL and G_TRI_SHADE_TXTR + // the port renders the sky in a different manner break; case G_RDPFLUSH_EXT: gfx_flush(); @@ -2698,16 +2527,15 @@ extern "C" void gfx_get_dimensions(uint32_t* width, uint32_t* height, int32_t* p gfx_wapi->get_dimensions(width, height, posX, posY); } -extern "C" void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name, - bool start_in_fullscreen, bool start_maximized, uint32_t width, uint32_t height, uint32_t posX, uint32_t posY) { - gfx_wapi = wapi; - gfx_rapi = rapi; - gfx_wapi->init(game_name, rapi->get_name(), start_in_fullscreen, start_maximized, width, height, posX, posY); +extern "C" void gfx_init(const GfxInitSettings *settings) { + gfx_wapi = settings->wapi; + gfx_rapi = settings->rapi; + gfx_wapi->init(&settings->window_settings); gfx_rapi->init(); - gfx_rapi->update_framebuffer_parameters(0, width, height, 1, false, true, true, true); + gfx_rapi->update_framebuffer_parameters(0, settings->window_settings.width, settings->window_settings.height, 1, false, true, true, true); gfx_current_dimensions.internal_mul = 1; - gfx_current_game_window_viewport.width = gfx_current_dimensions.width = width; - gfx_current_game_window_viewport.height = gfx_current_dimensions.height = height; + gfx_current_game_window_viewport.width = gfx_current_dimensions.width = settings->window_settings.width; + gfx_current_game_window_viewport.height = gfx_current_dimensions.height = settings->window_settings.height; game_framebuffer = gfx_rapi->create_framebuffer(); game_framebuffer_msaa_resolved = gfx_rapi->create_framebuffer(); @@ -2722,7 +2550,7 @@ extern "C" void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAP if (tex_upload_buffer == nullptr) { // We cap texture max to 8k, because why would you need more? - int max_tex_size = min(8192, gfx_rapi->get_max_texture_size()); + int max_tex_size = std::min(8192, gfx_rapi->get_max_texture_size()); tex_upload_buffer = (uint8_t*)malloc(max_tex_size * max_tex_size * 4); } @@ -2862,7 +2690,6 @@ extern "C" void gfx_run(Gfx* commands) { gfx_rapi->end_frame(); gfx_wapi->swap_buffers_begin(); - has_drawn_imgui_menu = false; } extern "C" void gfx_end_frame(void) { diff --git a/port/fast3d/gfx_sdl2.cpp b/port/fast3d/gfx_sdl2.cpp index bd6e4def7..181038d26 100644 --- a/port/fast3d/gfx_sdl2.cpp +++ b/port/fast3d/gfx_sdl2.cpp @@ -17,6 +17,7 @@ static bool vsync_enabled = true; // OTRTODO: These are redundant. Info can be queried from SDL. static int window_width = DESIRED_SCREEN_WIDTH; static int window_height = DESIRED_SCREEN_HEIGHT; +static uint32_t fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP; static bool fullscreen_state; static bool maximized_state; static bool is_running = true; @@ -34,7 +35,7 @@ static void set_fullscreen(bool on, bool call_callback) { return; } fullscreen_state = on; - SDL_SetWindowFullscreen(wnd, on ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + SDL_SetWindowFullscreen(wnd, on ? fullscreen_flag : 0); if (call_callback && on_fullscreen_changed_callback) { on_fullscreen_changed_callback(on); } @@ -60,10 +61,20 @@ static void gfx_sdl_get_active_window_refresh_rate(uint32_t* refresh_rate) { *refresh_rate = mode.refresh_rate; } -static void gfx_sdl_init(const char* game_name, const char* gfx_api_name, bool start_in_fullscreen, bool start_maximized, uint32_t width, - uint32_t height, int32_t posX, int32_t posY) { - window_width = width; - window_height = height; +static void gfx_sdl_init(const struct GfxWindowInitSettings *set) { + window_width = set->width; + window_height = set->height; + +#ifdef SDL_HINT_VIDEO_HIGHDPI_DISABLED + if (!set->allow_hidpi) { + // HiDPI control, if available + SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1"); +#if defined(PLATFORM_WIN32) && defined(SDL_HINT_WINDOWS_DPI_AWARENESS) + // if HiDPI is disabled, declare ourselves DPI aware to get 1:1 window size on Windows + SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitor"); +#endif + } +#endif if (SDL_Init(SDL_INIT_VIDEO) != 0) { sysFatalError("Could not init SDL:\n%s", SDL_GetError()); @@ -77,18 +88,38 @@ static void gfx_sdl_init(const char* game_name, const char* gfx_api_name, bool s SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); } - char title[512]; - snprintf(title, sizeof(title), "%s (%s)", game_name, gfx_api_name); - + int posX = set->x; + int posY = set->y; int display_in_use = SDL_GetWindowDisplayIndex(wnd); if (display_in_use < 0) { // Fallback to default if out of bounds posX = 100; posY = 100; } - const Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL; + if (set->fullscreen_is_exclusive) { + fullscreen_flag = SDL_WINDOW_FULLSCREEN; + } - wnd = SDL_CreateWindow(title, posX, posY, window_width, window_height, flags); + Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL; + + // if fullscreen was requested, start the window in fullscreen right away + if (set->fullscreen) { + flags |= fullscreen_flag; + fullscreen_state = true; + } + + if (set->maximized) { + flags |= SDL_WINDOW_MAXIMIZED; + maximized_state = true; + } + +#ifdef SDL_WINDOW_ALLOW_HIGHDPI + if (set->allow_hidpi) { + flags |= SDL_WINDOW_ALLOW_HIGHDPI; + } +#endif + + wnd = SDL_CreateWindow(set->title, posX, posY, window_width, window_height, flags); if (!wnd) { sysFatalError("Could not open SDL window:\n%s", SDL_GetError()); } @@ -143,9 +174,6 @@ static void gfx_sdl_init(const char* game_name, const char* gfx_api_name, bool s SDL_GL_SetSwapInterval(1); qpc_freq = SDL_GetPerformanceFrequency(); - - set_maximize_window(start_maximized); - set_fullscreen(start_in_fullscreen, false); } static void gfx_sdl_close(void) { @@ -160,6 +188,18 @@ static void gfx_sdl_set_fullscreen(bool enable) { set_fullscreen(enable, true); } +static void gfx_sdl_set_fullscreen_exclusive(bool enable) { + const uint32_t newflag = enable ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP; + if (fullscreen_flag != newflag) { + fullscreen_flag = newflag; + // reset fullscreen to take new value into account if it already is in fullscreen + if (fullscreen_state) { + fullscreen_state = false; + set_fullscreen(enable, true); + } + } +} + static void gfx_sdl_set_maximize_window(bool enable) { set_maximize_window(enable); } @@ -284,6 +324,7 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_close, gfx_sdl_set_fullscreen_changed_callback, gfx_sdl_set_fullscreen, + gfx_sdl_set_fullscreen_exclusive, gfx_sdl_set_maximize_window, gfx_sdl_get_active_window_refresh_rate, gfx_sdl_set_cursor_visibility, diff --git a/port/fast3d/gfx_window_manager_api.h b/port/fast3d/gfx_window_manager_api.h index 44eda4160..61b05315f 100644 --- a/port/fast3d/gfx_window_manager_api.h +++ b/port/fast3d/gfx_window_manager_api.h @@ -4,12 +4,24 @@ #include #include +struct GfxWindowInitSettings { + const char *title; + uint32_t width; + uint32_t height; + int32_t x; + int32_t y; + bool fullscreen; + bool fullscreen_is_exclusive; + bool maximized; + bool allow_hidpi; +}; + struct GfxWindowManagerAPI { - void (*init)(const char* game_name, const char* gfx_api_name, bool start_in_fullscreen, bool start_maximized, uint32_t width, - uint32_t height, int32_t posX, int32_t posY); + void (*init)(const struct GfxWindowInitSettings *settings); void (*close)(void); void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen)); void (*set_fullscreen)(bool enable); + void (*set_fullscreen_exclusive)(bool exc); void (*set_maximize)(bool enable); void (*get_active_window_refresh_rate)(uint32_t* refresh_rate); void (*set_cursor_visibility)(bool visible); diff --git a/port/include/video.h b/port/include/video.h index 105f78699..d7a888886 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -30,12 +30,14 @@ s32 videoGetFullscreen(void); s32 videoGetMaximizeWindow(void); void videoSetMaximizeWindow(s32 fs); u32 videoGetTextureFilter(void); -u32 videoGetTextureFilter2D(void); +s32 videoGetTextureFilter2D(void); +s32 videoGetDetailTextures(void); void videoSetWindowOffset(s32 x, s32 y); void videoSetFullscreen(s32 fs); void videoSetTextureFilter(u32 filter); -void videoSetTextureFilter2D(u32 filter); +void videoSetTextureFilter2D(s32 filter); +void videoSetDetailTextures(s32 detail); s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize); void videoSetFramebuffer(s32 target); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index e8085b564..b6ac6dab7 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -678,6 +678,19 @@ static MenuItemHandlerResult menuhandlerTexFilter(s32 operation, struct menuitem return 0; } +static MenuItemHandlerResult menuhandlerTexDetail(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return (videoGetDetailTextures() != 0); + case MENUOP_SET: + videoSetDetailTextures(data->checkbox.value); + break; + } + + return 0; +} + static MenuItemHandlerResult menuhandlerTexFilter2D(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { @@ -769,6 +782,14 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerMaximizeWindow, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Detail Textures", + 0, + menuhandlerTexDetail, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/port/src/system.c b/port/src/system.c index e889f75df..16b689c47 100644 --- a/port/src/system.c +++ b/port/src/system.c @@ -1,3 +1,7 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + #include #include #include @@ -22,11 +26,11 @@ static CREATEWAITABLETIMEREXAFN pfnCreateWaitableTimerExA; // winapi also provides a yield macro #define DO_YIELD() YieldProcessor() -#else +// ask system for high performance GPU, if any +__attribute__((dllexport)) u32 NvOptimusEnablement = 1; +__attribute__((dllexport)) u32 AmdPowerXpressRequestHighPerformance = 1; -#ifndef _GNU_SOURCE -#define _GNU_SOURCE 1 -#endif +#else #include diff --git a/port/src/video.c b/port/src/video.c index 18da94fc0..181af3ab5 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -21,13 +21,16 @@ static s32 vidWidth = 640; static s32 vidHeight = 480; static s32 vidFramebuffers = true; static s32 vidFullscreen = false; +static s32 vidFullscreenExclusive = false; static s32 vidMaximize = false; +static s32 vidAllowHiDpi = false; static s32 vidVsync = 1; static s32 vidMSAA = 1; static s32 vidFramerateLimit = 0; static s32 texFilter = FILTER_LINEAR; static s32 texFilter2D = true; +static s32 texDetail = true; static u32 dlcount = 0; static u32 frames = 0; @@ -43,9 +46,26 @@ s32 videoInit(void) gfx_current_native_viewport.height = 220; gfx_current_native_aspect = 320.f / 220.f; gfx_framebuffers_enabled = (bool)vidFramebuffers; + gfx_detail_textures_enabled = (bool)texDetail; gfx_msaa_level = vidMSAA; - gfx_init(wmAPI, renderingAPI, "PD", vidFullscreen, vidMaximize, vidWidth, vidHeight, 100, 100); + struct GfxInitSettings set = { + .wapi = wmAPI, + .rapi = renderingAPI, + .window_settings = { + .title = "Perfect Dark", + .width = vidWidth, + .height = vidHeight, + .x = 100, + .y = 100, + .fullscreen = vidFullscreen, + .fullscreen_is_exclusive = vidFullscreenExclusive, + .maximized = vidMaximize, + .allow_hidpi = vidAllowHiDpi + } + }; + + gfx_init(&set); if (!wmAPI->set_swap_interval(vidVsync)) { vidVsync = 0; @@ -158,7 +178,7 @@ f32 videoGetAspect(void) return gfx_current_dimensions.aspect_ratio; } -u32 videoGetTextureFilter2D(void) +s32 videoGetTextureFilter2D(void) { return texFilter2D; } @@ -168,6 +188,11 @@ u32 videoGetTextureFilter(void) return texFilter; } +s32 videoGetDetailTextures(void) +{ + return texDetail; +} + void videoSetWindowOffset(s32 x, s32 y) { gfx_current_game_window_viewport.x = x; @@ -197,11 +222,17 @@ void videoSetTextureFilter(u32 filter) renderingAPI->set_texture_filter((enum FilteringMode)filter); } -void videoSetTextureFilter2D(u32 filter) +void videoSetTextureFilter2D(s32 filter) { texFilter2D = !!filter; } +void videoSetDetailTextures(s32 detail) +{ + texDetail = !!detail; + gfx_detail_textures_enabled = (bool)texDetail; +} + s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize) { return gfx_create_framebuffer(w, h, upscale, autoresize); @@ -249,10 +280,13 @@ PD_CONSTRUCTOR static void videoConfigInit(void) configRegisterInt("Video.DefaultMaximize", &vidMaximize, 0, 1); configRegisterInt("Video.DefaultWidth", &vidWidth, 0, 32767); configRegisterInt("Video.DefaultHeight", &vidHeight, 0, 32767); + configRegisterInt("Video.ExclusiveFullscreen", &vidFullscreenExclusive, 0, 1); + configRegisterInt("Video.AllowHiDpi", &vidAllowHiDpi, 0, 1); configRegisterInt("Video.VSync", &vidVsync, -1, 10); configRegisterInt("Video.FramebufferEffects", &vidFramebuffers, 0, 1); configRegisterInt("Video.FramerateLimit", &vidFramerateLimit, 0, VIDEO_MAX_FPS); configRegisterInt("Video.MSAA", &vidMSAA, 1, 16); configRegisterInt("Video.TextureFilter", &texFilter, 0, 2); configRegisterInt("Video.TextureFilter2D", &texFilter2D, 0, 1); + configRegisterInt("Video.DetailTextures", &texDetail, 0, 1); } diff --git a/src/game/bg.c b/src/game/bg.c index 0b2c0b03b..c6dddc699 100644 --- a/src/game/bg.c +++ b/src/game/bg.c @@ -984,7 +984,9 @@ Gfx *bgRenderScene(Gfx *gdl) s16 roomorder[60]; RoomNum roomnums[60]; +#ifdef PLATFORM_N64 g_NumRoomsWithGlares = 0; +#endif if (g_Vars.currentplayer->visionmode == VISIONMODE_XRAY) { gdl = bgRenderSceneInXray(gdl); @@ -1206,6 +1208,7 @@ Gfx *bgRenderScene(Gfx *gdl) gdl = propsRender(gdl, thing->roomnum, RENDERPASS_XLU, roomnumsbyprop); } +#ifdef PLATFORM_N64 // on PC we do this earlier, before prop matrices turn into garbage if (!g_Vars.mplayerisrunning) { artifactsCalculateGlaresForRoom(thing->roomnum); @@ -1213,6 +1216,7 @@ Gfx *bgRenderScene(Gfx *gdl) g_GlareRooms[g_NumRoomsWithGlares++] = thing->roomnum; } } +#endif } #if VERSION < VERSION_NTSC_1_0 @@ -6397,3 +6401,25 @@ void bgFindEnteredRooms(struct coord *bbmin, struct coord *bbmax, RoomNum *rooms end: rooms[len] = -1; } + +#ifndef PLATFORM_N64 + +void bgCalculateGlaresForVisibleRooms(void) +{ + s32 i; + + g_NumRoomsWithGlares = 0; + + if (!g_Vars.mplayerisrunning) { + for (i = 1; i < g_Vars.roomcount; i++) { + if (g_Rooms[i].flags & ROOMFLAG_ONSCREEN) { + artifactsCalculateGlaresForRoom(i); + if (g_NumRoomsWithGlares < 100) { + g_GlareRooms[g_NumRoomsWithGlares++] = i; + } + } + } + } +} + +#endif diff --git a/src/game/bondgun.c b/src/game/bondgun.c index 67825c973..8c99e39c1 100644 --- a/src/game/bondgun.c +++ b/src/game/bondgun.c @@ -11735,6 +11735,7 @@ s32 bgunConsiderToggleGunFunction(s32 usedowntime, bool trigpressed, bool fromac { #ifndef PLATFORM_N64 const bool extcontrols = PLAYER_EXTCFG().extcontrols; + bool docontinue; #endif switch (bgunGetWeaponNum(HAND_RIGHT)) { case WEAPON_SNIPERRIFLE: @@ -11759,7 +11760,6 @@ s32 bgunConsiderToggleGunFunction(s32 usedowntime, bool trigpressed, bool fromac } #ifndef PLATFORM_N64 - bool docontinue; if (extcontrols) { docontinue = (ABS(usedowntime) < 0); } else { @@ -11781,6 +11781,18 @@ s32 bgunConsiderToggleGunFunction(s32 usedowntime, bool trigpressed, bool fromac g_Vars.currentplayer->hands[HAND_RIGHT].activatesecondary = true; return (extcontrols ? USETIMER_STOP : USETIMER_REPEAT); case WEAPON_RCP120: +#ifndef PLATFORM_N64 + // very special alt-button handling for RCP-120's cloaking + if (!trigpressed && extcontrols && fromdedicatedbutton) { + if (g_Vars.currentplayer->devicesactive & DEVICE_CLOAKRCP120) { + g_Vars.currentplayer->devicesactive &= ~DEVICE_CLOAKRCP120; + } else { + g_Vars.currentplayer->devicesactive = (g_Vars.currentplayer->devicesactive & ~DEVICE_CLOAKRCP120) | DEVICE_CLOAKRCP120; + } + g_Vars.currentplayer->gunctrl.invertgunfunc = false; + return USETIMER_STOP; + } +#endif case WEAPON_LAPTOPGUN: case WEAPON_DRAGON: case WEAPON_REMOTEMINE: diff --git a/src/game/camdraw.c b/src/game/camdraw.c index 97498ec6e..ee9b25834 100644 --- a/src/game/camdraw.c +++ b/src/game/camdraw.c @@ -1333,7 +1333,7 @@ void func0f14bdbc(s32 index) s32 value; for (i = 0; i < 30; i++) { - thing->unk0dc[i] = NULL; + thing->unk0dc[i] = 0; } thing->unk094 = 0; diff --git a/src/game/cheats.c b/src/game/cheats.c index 71ac21ca8..4567f5e30 100644 --- a/src/game/cheats.c +++ b/src/game/cheats.c @@ -100,6 +100,9 @@ struct cheat g_Cheats[] = { { L_MPWEAPONS_114, WEAPON_DMC, 0, 0, CHEATFLAG_FIRINGRANGE }, // DMC { L_MPWEAPONS_115, WEAPON_AR53, 0, 0, CHEATFLAG_FIRINGRANGE }, // AR53 { L_MPWEAPONS_116, WEAPON_RCP45, 0, 0, CHEATFLAG_FIRINGRANGE }, // RC-P45 +#ifndef PLATFORM_N64 + { L_MPWEAPONS_215, 0, SOLOSTAGEINDEX_EXTRACTION, DIFF_A, CHEATFLAG_COMPLETION }, // Dual wield all guns +#endif }; u32 cheatIsUnlocked(s32 cheat_id) @@ -1090,6 +1093,16 @@ struct menuitem g_CheatsGameplayMenuItems[] = { 0, cheatCheckboxMenuHandler, }, +#ifndef PLATFORM_N64 + { + MENUITEMTYPE_CHECKBOX, + CHEAT_DUALWIELDALLGUNS, + 0, + (uintptr_t)&cheatGetNameIfUnlocked, + 0, + cheatCheckboxMenuHandler, + }, +#endif { MENUITEMTYPE_SEPARATOR, 0, diff --git a/src/game/game_0b0fd0.c b/src/game/game_0b0fd0.c index 719959461..3747c37a4 100644 --- a/src/game/game_0b0fd0.c +++ b/src/game/game_0b0fd0.c @@ -291,6 +291,13 @@ bool weaponHasFlag(s32 itemid, u32 flag) return false; } +#ifndef PLATFORM_N64 + // always dual-wieldable if cheat is enabled + if (cheatIsActive(CHEAT_DUALWIELDALLGUNS) && (flag == WEAPONFLAG_DUALWIELD)) { + return true; + } +#endif + return (weapon->flags & flag) != 0; } diff --git a/src/game/game_13c510.c b/src/game/game_13c510.c index 6258fff51..c2b4b81e9 100644 --- a/src/game/game_13c510.c +++ b/src/game/game_13c510.c @@ -20,7 +20,7 @@ #include "lib/collision.h" #include "lib/lib_17ce0.h" #include "game/player.h" -f32 fabsf(f32 value); +#include "game/prop.h" #endif u8 *var800a41a0; @@ -104,8 +104,10 @@ s32 func0f13c710(f32 arg0) #ifndef PLATFORM_N64 -bool artifactTestLos(struct coord *spec, struct coord *roompos) +bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi) { + s32 i = 0; + if (!g_Vars.currentplayer) { return false; } @@ -115,35 +117,15 @@ bool artifactTestLos(struct coord *spec, struct coord *roompos) endpos.y = roompos->y + spec->y; endpos.z = roompos->z + spec->z; - // get what rooms the LOS goes through - RoomNum outrooms[17], tmprooms[8], srcrooms[2]; - srcrooms[0] = g_Vars.currentplayer->cam_room; - srcrooms[1] = -1; - outrooms[16] = -1; - portal00018148(&g_Vars.currentplayer->cam_pos, &endpos, srcrooms, tmprooms, outrooms, 16); + struct coord gundir2d; + struct coord gunpos2d = {{ 0.f, 0.f, 0.f }}; + struct coord gundir3d; + struct coord gunpos3d = g_Vars.currentplayer->cam_pos; + f32 crosspos[2] = { (f32)xi, (f32)yi }; + cam0f0b4c3c(crosspos, &gundir2d, 1.f); + mtx4RotateVec(camGetProjectionMtxF(), &gundir2d, &gundir3d); - // cheaper LOS test against props first - const u32 cdtype = CDTYPE_OBJS | CDTYPE_DOORS | CDTYPE_PLAYERS | CDTYPE_CHRS; - const u16 geoflags = GEOFLAG_BLOCK_SIGHT | GEOFLAG_BLOCK_SHOOT; - if (!cdTestAToB(&g_Vars.currentplayer->cam_pos, &endpos, outrooms, cdtype, geoflags, true, 1, 0, 0)) { - return false; - } - - // no prop was hit; check for bg hits - struct hitthing hit; - for (s32 i = 0; outrooms[i] != -1; ++i) { - if (bgTestHitInRoom(&g_Vars.currentplayer->cam_pos, &endpos, outrooms[i], &hit)) { - // check if it's far enough away from the end point - if (fabsf(hit.pos.x - endpos.x) >= 0.1f || - fabsf(hit.pos.y - endpos.y) >= 0.1f || - fabsf(hit.pos.z - endpos.z) >= 0.1f) { - return false; - } - } - } - - // nothing hit - return true; + return shotTestLos(&gunpos2d, &gundir2d, &gunpos3d, &gundir3d, &endpos); } #endif @@ -186,11 +168,6 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) struct coord *campos = &g_Vars.currentplayer->cam_pos; struct artifact *artifact; -#ifndef PLATFORM_N64 - const bool prevperim = g_Vars.currentplayer->bondperimenabled; - playerSetPerimEnabled(g_Vars.currentplayer->prop, false); -#endif - if (g_Rooms[roomnum].gfxdata != NULL && g_Rooms[roomnum].loaded240) { numlights = g_Rooms[roomnum].gfxdata->numlights; @@ -380,7 +357,7 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) if (index < MAX_ARTIFACTS) { #ifndef PLATFORM_N64 - artifact->unk02 = artifactTestLos(&spec, &g_BgRooms[roomnum].pos); + artifact->unk02 = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi); #endif artifact->unk04 = func0f13c574(f0) >> 2; artifact->unk08 = &g_ZbufPtr1[viGetWidth() * yi + xi]; @@ -396,10 +373,6 @@ void artifactsCalculateGlaresForRoom(s32 roomnum) } } } - -#ifndef PLATFORM_N64 - playerSetPerimEnabled(g_Vars.currentplayer->prop, prevperim); -#endif } u8 func0f13d3c4(u8 arg0, u8 arg1) diff --git a/src/game/inv.c b/src/game/inv.c index 825e21fe9..04fce4a8a 100644 --- a/src/game/inv.c +++ b/src/game/inv.c @@ -511,6 +511,17 @@ s32 invGiveWeaponsByProp(struct prop *prop) weaponnum = weapon->weaponnum; otherweaponnum; +#ifndef PLATFORM_N64 + // always allow picking up a second gun if dual wield cheat is on + if (!g_Vars.normmplayerisrunning && cheatIsActive(CHEAT_DUALWIELDALLGUNS)) { + if (invHasSingleWeaponExcAllGuns(weaponnum) && !invHasDoubleWeaponExcAllGuns(weaponnum, weaponnum)) { + if (invGiveDoubleWeapon(weaponnum, weaponnum)) { + return 2; + } + } + } +#endif + if (cheatIsActive(CHEAT_PERFECTDARKNESS) && weaponnum == WEAPON_NIGHTVISION) { return 1; } diff --git a/src/game/lv.c b/src/game/lv.c index 1c746b77d..c3273c6c6 100644 --- a/src/game/lv.c +++ b/src/game/lv.c @@ -1127,6 +1127,13 @@ Gfx *lvRender(Gfx *gdl) chr->blurdrugamount = 0; chr->blurnumtimesdied = 0; } + +#ifndef PLATFORM_N64 + // reset the drug blur to 0 if it's disabled in MP settings + if (g_Vars.mplayerisrunning && (g_MpSetup.options & MPOPTION_NODRUGBLUR)) { + bluramount = 0; + } +#endif } } @@ -1194,6 +1201,11 @@ Gfx *lvRender(Gfx *gdl) autoaimTick(); handsTickAttack(); +#ifndef PLATFORM_N64 + // glares calculated earlier on PC, before prop matrices turn into garbage + bgCalculateGlaresForVisibleRooms(); +#endif + // Calculate lookingatprop if (PLAYERCOUNT() == 1 || g_Vars.coopplayernum >= 0 diff --git a/src/game/modeldata/player.c b/src/game/modeldata/player.c index ad49e1c88..76e334f95 100644 --- a/src/game/modeldata/player.c +++ b/src/game/modeldata/player.c @@ -18,7 +18,7 @@ struct modeldef g_PlayerModeldef = { 3, 41.161228179932, 0, - NULL, + 0, }; // rodata parent next prev child diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index e74e8fc49..80b9a4c1f 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -92,7 +92,12 @@ struct mpweapon g_MpWeapons[NUM_MPWEAPONS] = { /*0x1f*/ { WEAPON_PROXIMITYMINE, AMMOTYPE_PROXY_MINE, 5, 0, 0, 0, MPFEATURE_WEAPON_PROXIMITYMINE, MODEL_CHRPROXIMITYMINE, 384 }, /*0x20*/ { WEAPON_REMOTEMINE, AMMOTYPE_REMOTE_MINE, 5, 0, 0, 0, MPFEATURE_WEAPON_REMOTEMINE, MODEL_CHRREMOTEMINE, 384 }, /*0x21*/ { WEAPON_LASER, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_LASER, MODEL_CHRLASER, 512 }, +#ifndef PLATFORM_N64 + // fix X-Ray Scanner model + /*0x22*/ { WEAPON_XRAYSCANNER, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_XRAYSCANNER, MODEL_XRAYSPECS, 256 }, +#else /*0x22*/ { WEAPON_XRAYSCANNER, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_XRAYSCANNER, MODEL_CHRNIGHTSIGHT, 256 }, +#endif /*0x23*/ { WEAPON_CLOAKINGDEVICE, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_CLOAKINGDEVICE, MODEL_CHRCLOAKER, 256 }, /*0x24*/ { WEAPON_COMBATBOOST, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_COMBATBOOST, MODEL_CHRSPEEDPILL, 256 }, #ifndef PLATFORM_N64 @@ -179,6 +184,15 @@ void mpStartMatch(void) s32 numplayers = 0; s32 stagenum; +#ifndef PLATFORM_N64 + if (g_MpSetup.options & MPOPTION_AUTORANDOMWEAPON_START) { + if (g_MpWeaponSetNum == WEAPONSET_RANDOM + || g_MpWeaponSetNum == WEAPONSET_RANDOMFIVE) { + mpApplyWeaponSet(); + } + } +#endif + mpConfigureQuickTeamSimulants(); if (!challengeIsFeatureUnlocked(MPFEATURE_ONEHITKILLS)) { @@ -2457,6 +2471,15 @@ void mpEndMatch(void) challengeConsiderMarkingComplete(); } +#ifndef PLATFORM_N64 + if (g_MpSetup.options & MPOPTION_AUTORANDOMWEAPON_END) { + if (g_MpWeaponSetNum == WEAPONSET_RANDOM + || g_MpWeaponSetNum == WEAPONSET_RANDOMFIVE) { + mpApplyWeaponSet(); + } + } +#endif + func0f0f820c(NULL, -6); } diff --git a/src/game/mplayer/scenarios.c b/src/game/mplayer/scenarios.c index e2c144d21..aa502ed21 100644 --- a/src/game/mplayer/scenarios.c +++ b/src/game/mplayer/scenarios.c @@ -25,6 +25,7 @@ #include "game/mplayer/mplayer.h" #include "game/pad.h" #include "game/propobj.h" +#include "game/options.h" #include "bss.h" #include "lib/vi.h" #include "lib/snd.h" diff --git a/src/game/mplayer/scenarios/capturethecase.inc b/src/game/mplayer/scenarios/capturethecase.inc index e940390e7..192b8923c 100644 --- a/src/game/mplayer/scenarios/capturethecase.inc +++ b/src/game/mplayer/scenarios/capturethecase.inc @@ -65,10 +65,18 @@ struct menuitem g_CtcOptionsMenuItems[] = { MENUITEMTYPE_CHECKBOX, 0, MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Spawn With Weapon", + (uintptr_t)"Spawn With Gun", MPOPTION_SPAWNWITHWEAPON, menuhandlerMpCheckboxOption, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"No Drug Blur", + MPOPTION_NODRUGBLUR, + menuhandlerMpCheckboxOption, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/src/game/mplayer/scenarios/combat.inc b/src/game/mplayer/scenarios/combat.inc index 03834861f..854e38319 100644 --- a/src/game/mplayer/scenarios/combat.inc +++ b/src/game/mplayer/scenarios/combat.inc @@ -60,10 +60,18 @@ struct menuitem g_MpCombatOptionsMenuItems[] = { MENUITEMTYPE_CHECKBOX, 0, MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Spawn With Weapon", + (uintptr_t)"Spawn With Gun", MPOPTION_SPAWNWITHWEAPON, menuhandlerMpCheckboxOption, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"No Drug Blur", + MPOPTION_NODRUGBLUR, + menuhandlerMpCheckboxOption, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/src/game/mplayer/scenarios/hackthatmac.inc b/src/game/mplayer/scenarios/hackthatmac.inc index 2ab930965..9a7cf4b42 100644 --- a/src/game/mplayer/scenarios/hackthatmac.inc +++ b/src/game/mplayer/scenarios/hackthatmac.inc @@ -63,10 +63,18 @@ struct menuitem g_HtmOptionsMenuItems[] = { MENUITEMTYPE_CHECKBOX, 0, MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Spawn With Weapon", + (uintptr_t)"Spawn With Gun", MPOPTION_SPAWNWITHWEAPON, menuhandlerMpCheckboxOption, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"No Drug Blur", + MPOPTION_NODRUGBLUR, + menuhandlerMpCheckboxOption, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/src/game/mplayer/scenarios/holdthebriefcase.inc b/src/game/mplayer/scenarios/holdthebriefcase.inc index f08a33f3c..b4bfedf75 100644 --- a/src/game/mplayer/scenarios/holdthebriefcase.inc +++ b/src/game/mplayer/scenarios/holdthebriefcase.inc @@ -63,10 +63,18 @@ struct menuitem g_HtbOptionsMenuItems[] = { MENUITEMTYPE_CHECKBOX, 0, MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Spawn With Weapon", + (uintptr_t)"Spawn With Gun", MPOPTION_SPAWNWITHWEAPON, menuhandlerMpCheckboxOption, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"No Drug Blur", + MPOPTION_NODRUGBLUR, + menuhandlerMpCheckboxOption, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/src/game/mplayer/scenarios/kingofthehill.inc b/src/game/mplayer/scenarios/kingofthehill.inc index 45684ff4b..ff72ea228 100644 --- a/src/game/mplayer/scenarios/kingofthehill.inc +++ b/src/game/mplayer/scenarios/kingofthehill.inc @@ -84,10 +84,18 @@ struct menuitem g_KohOptionsMenuItems[] = { MENUITEMTYPE_CHECKBOX, 0, MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Spawn With Weapon", + (uintptr_t)"Spawn With Gun", MPOPTION_SPAWNWITHWEAPON, menuhandlerMpCheckboxOption, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"No Drug Blur", + MPOPTION_NODRUGBLUR, + menuhandlerMpCheckboxOption, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/src/game/mplayer/scenarios/popacap.inc b/src/game/mplayer/scenarios/popacap.inc index fa58c551f..7e37939f7 100644 --- a/src/game/mplayer/scenarios/popacap.inc +++ b/src/game/mplayer/scenarios/popacap.inc @@ -61,10 +61,18 @@ struct menuitem g_PacOptionsMenuItems[] = { MENUITEMTYPE_CHECKBOX, 0, MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Spawn With Weapon", + (uintptr_t)"Spawn With Gun", MPOPTION_SPAWNWITHWEAPON, menuhandlerMpCheckboxOption, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"No Drug Blur", + MPOPTION_NODRUGBLUR, + menuhandlerMpCheckboxOption, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/src/game/mplayer/setup.c b/src/game/mplayer/setup.c index 87b99860d..44834bbfb 100644 --- a/src/game/mplayer/setup.c +++ b/src/game/mplayer/setup.c @@ -31,6 +31,10 @@ struct menudialogdef g_MpChangeTeamNameMenuDialog; struct menudialogdef g_MpEditSimulantMenuDialog; struct menudialogdef g_MpSaveSetupNameMenuDialog; +#ifndef PLATFORM_N64 +extern s32 g_MpWeaponSetNum; +#endif + MenuItemHandlerResult menuhandlerMpDropOut(s32 operation, struct menuitem *item, union handlerdata *data) { if (operation == MENUOP_SET) { @@ -1160,6 +1164,52 @@ struct menudialogdef g_MpSaveSetupExistsMenuDialog = { NULL, }; +#ifndef PLATFORM_N64 +MenuItemHandlerResult menuhandlerMpAutoRandomWeapon(s32 operation, struct menuitem *item, union handlerdata *data) +{ + static const char *labels[] = { + "Off", + "Start", + "End", + }; + + switch (operation) { + case MENUOP_CHECKDISABLED: + case MENUOP_CHECKHIDDEN: + if (g_MpWeaponSetNum == WEAPONSET_RANDOM + || g_MpWeaponSetNum == WEAPONSET_RANDOMFIVE) { + return false; + } + return true; + case MENUOP_GETOPTIONCOUNT: + data->dropdown.value = ARRAYCOUNT(labels); + break; + case MENUOP_GETOPTIONTEXT: + return (intptr_t)labels[data->dropdown.value]; + case MENUOP_SET: + g_MpSetup.options &= ~(MPOPTION_AUTORANDOMWEAPON_START | MPOPTION_AUTORANDOMWEAPON_END); + + if (data->dropdown.value == AUTORANDOMWEAPON_START) { + g_MpSetup.options |= MPOPTION_AUTORANDOMWEAPON_START; + } else if (data->dropdown.value == AUTORANDOMWEAPON_END) { + g_MpSetup.options |= MPOPTION_AUTORANDOMWEAPON_END; + } + break; + case MENUOP_GETSELECTEDINDEX: + if (g_MpSetup.options & MPOPTION_AUTORANDOMWEAPON_END) { + data->dropdown.value = AUTORANDOMWEAPON_END; + } else if (g_MpSetup.options & MPOPTION_AUTORANDOMWEAPON_START) { + data->dropdown.value = AUTORANDOMWEAPON_START; + } else { + data->dropdown.value = AUTORANDOMWEAPON_OFF; + } + break; + } + + return 0; +} +#endif + struct menuitem g_MpWeaponsMenuItems[] = { { MENUITEMTYPE_DROPDOWN, @@ -1169,6 +1219,16 @@ struct menuitem g_MpWeaponsMenuItems[] = { 0, menuhandlerMpWeaponSetDropdown, }, +#ifndef PLATFORM_N64 + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Auto Random\n", + 0, + menuhandlerMpAutoRandomWeapon, + }, +#endif { MENUITEMTYPE_SEPARATOR, 0, diff --git a/src/game/player.c b/src/game/player.c index bb26bd28e..eb4e2c891 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -1100,6 +1100,10 @@ void playerSpawn(void) } } else { #ifndef PLATFORM_N64 + if (cheatIsActive(CHEAT_PERFECTDARKNESS)) { + invGiveSingleWeapon(WEAPON_NIGHTVISION); + } + if ((g_MpSetup.options & MPOPTION_SPAWNWITHWEAPON) && g_MpSetup.weapons[0] != MPWEAPON_NONE && g_MpSetup.weapons[0] != MPWEAPON_DISABLED) { @@ -4590,20 +4594,12 @@ Gfx *playerRenderHud(Gfx *gdl) && ((g_Vars.currentplayer->devicesactive & ~g_Vars.currentplayer->devicesinhibit) & DEVICE_NIGHTVISION)) { gdl = bviewDrawNvLens(gdl); gdl = bviewDrawNvBinoculars(gdl); -#ifndef PLATFORM_N64 - // turn off "greenscale" - gDPGrayscaleEXT(gdl++, G_OFF); -#endif } else if (g_Vars.currentplayer->isdead == false && g_InCutscene == 0 && (!g_Vars.currentplayer->eyespy || (g_Vars.currentplayer->eyespy && !g_Vars.currentplayer->eyespy->active)) && ((g_Vars.currentplayer->devicesactive & ~g_Vars.currentplayer->devicesinhibit) & DEVICE_IRSCANNER)) { gdl = bviewDrawIrLens(gdl); gdl = bviewDrawIrBinoculars(gdl); -#ifndef PLATFORM_N64 - // turn off "redscale" - gDPGrayscaleEXT(gdl++, G_OFF); -#endif } if (g_Vars.currentplayer->eyesshutfrac > 0) { diff --git a/src/game/prop.c b/src/game/prop.c index 92505fa3a..ae8da24f2 100644 --- a/src/game/prop.c +++ b/src/game/prop.c @@ -973,6 +973,117 @@ struct prop *shotCalculateHits(s32 handnum, bool isshooting, struct coord *gunpo return result; } +#ifndef PLATFORM_N64 + +/** + * Checks if the specified line of sight (gunpos3d - endpos3d) intersects any props or BG, + * using the same process as shotCalculateHits, except with no side effects and cheap = true. + * Returns true if nothing was hit. + */ +bool shotTestLos(struct coord *gunpos2d, struct coord *gundir2d, struct coord *gunpos3d, struct coord *gundir3d, struct coord *endpos3d) +{ + struct prop **propptr; + struct hitthing sp664; + struct coord delta; + struct shotdata shotdata; + s32 i; + RoomNum rooms[131]; + RoomNum spc8[8]; + RoomNum spb8[8]; + RoomNum *roomsptr; + struct prop *prop; + + shotdata.gunpos3d.x = gunpos3d->x; + shotdata.gunpos3d.y = gunpos3d->y; + shotdata.gunpos3d.z = gunpos3d->z; + + shotdata.gunpos2d.x = gunpos2d->x; + shotdata.gunpos2d.y = gunpos2d->y; + shotdata.gunpos2d.z = gunpos2d->z; + + shotdata.gundir3d.x = gundir3d->x; + shotdata.gundir3d.y = gundir3d->y; + shotdata.gundir3d.z = gundir3d->z; + + shotdata.gundir2d.x = gundir2d->x; + shotdata.gundir2d.y = gundir2d->y; + shotdata.gundir2d.z = gundir2d->z; + + // use falcon2 as a dummy weapon + shotdata.gset.weaponnum = WEAPON_FALCON2; + shotdata.gset.weaponfunc = 0; + shotdata.gset.unk063a = 0; + shotdata.gset.unk0639 = 0; + + shotdata.penetration = 1; + shotdata.distance = 999999999.f; + + for (i = 0; i < ARRAYCOUNT(shotdata.hits); i++) { + shotdata.hits[i].prop = NULL; + shotdata.hits[i].hitpart = 0; + shotdata.hits[i].bboxnode = NULL; + } + + rooms[0] = rooms[130] = -1; + spc8[0] = g_Vars.currentplayer->cam_room; + spc8[1] = -1; + portal00018148(&shotdata.gunpos3d, endpos3d, spc8, spb8, rooms, 30); + + roomsptr = rooms; + + while (*roomsptr != -1) { + roomsptr++; + } + + // Note this is being appended to rooms + bgGetForceOnscreenRooms(roomsptr, 100); + + // Check for BG hits first + for (i = 0; rooms[i] != -1; i++) { + if (bgTestHitInRoom(&shotdata.gunpos3d, endpos3d, rooms[i], &sp664)) { + // check if it's far enough away from the end point + if (fabsf(sp664.pos.x - endpos3d->x) >= 0.1f || + fabsf(sp664.pos.y - endpos3d->y) >= 0.1f || + fabsf(sp664.pos.z - endpos3d->z) >= 0.1f) { + return false; + } + } + } + + // didn't hit any bg, shrink distance to the line size + delta.x = endpos3d->x - gunpos3d->x; + delta.y = endpos3d->y - gunpos3d->y; + delta.z = endpos3d->z - gunpos3d->z; + shotdata.distance = sqrtf(delta.x * delta.x + delta.y * delta.y + delta.z * delta.z); + + // and check props + propptr = g_Vars.endonscreenprops - 1; + while (propptr >= g_Vars.onscreenprops) { + prop = *propptr; + if (prop) { + if (prop->type == PROPTYPE_CHR + || (prop->type == PROPTYPE_PLAYER && prop->chr && (g_Vars.in_cutscene || playermgrGetPlayerNumByProp(prop) != g_Vars.currentplayernum))) { + chrTestHit(prop, &shotdata, false, true); + } else if (prop->type == PROPTYPE_WEAPON || prop->type == PROPTYPE_DOOR + || (prop->type == PROPTYPE_OBJ && prop->obj->type != OBJTYPE_GLASS && prop->obj->type != OBJTYPE_TINTEDGLASS)) { + objTestHit(prop, &shotdata); + } + if (shotdata.hits[0].prop) { + // ignore some glass parts and shields + if (shotdata.hits[0].slowsbullet && shotdata.hits[0].hitthing.texturenum != 10000) { + return false; + } + } + } + propptr--; + } + + // did not hit anything + return true; +} + +#endif + struct prop *propFindAimingAt(s32 handnum, bool isshooting, u32 context) { struct coord gundir2d; diff --git a/src/game/sky.c b/src/game/sky.c index e46110911..e610cad83 100644 --- a/src/game/sky.c +++ b/src/game/sky.c @@ -2518,7 +2518,7 @@ void skyCreateSunArtifact(struct artifact *artifact, s32 x, s32 y) sunpos.x = env->suns[i].pos[0]; sunpos.y = env->suns[i].pos[1]; sunpos.z = env->suns[i].pos[2]; - artifact->unk02 = artifactTestLos(&sunpos, &zero) * 0xfffc; + artifact->unk02 = artifactTestLos(&sunpos, &zero, x, y) * 0xfffc; #endif artifact->unk08 = &g_ZbufPtr1[(s32)camGetScreenWidth() * y + x]; artifact->unk0c.u16_2 = x; diff --git a/src/include/constants.h b/src/include/constants.h index ac39e4c60..f6fea0f11 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -449,6 +449,7 @@ #define CHEAT_DMC 39 #define CHEAT_AR53 40 #define CHEAT_RCP45 41 +#define CHEAT_DUALWIELDALLGUNS 42 #define CHEATFLAG_TIMED 0 #define CHEATFLAG_ALWAYSON 1 @@ -2890,7 +2891,10 @@ #define MPOPTION_PAC_HIGHLIGHTTARGET 0x00080000 #define MPOPTION_PAC_SHOWONRADAR 0x00100000 #define MPOPTION_SPAWNWITHWEAPON 0x00200000 -#define MPOPTION_FRIENDLYFIRE 0x00400000 +#define MPOPTION_NODRUGBLUR 0x00400000 +#define MPOPTION_AUTORANDOMWEAPON_START 0x00800000 +#define MPOPTION_AUTORANDOMWEAPON_END 0x01000000 +#define MPOPTION_FRIENDLYFIRE 0x02000000 #define MPPAUSEMODE_UNPAUSED 0 #define MPPAUSEMODE_PAUSED 1 @@ -3803,6 +3807,10 @@ #define SLOWMOTION_ON 1 #define SLOWMOTION_SMART 2 +#define AUTORANDOMWEAPON_OFF 0 +#define AUTORANDOMWEAPON_START 1 +#define AUTORANDOMWEAPON_END 2 + #define SMOKETYPE_NONE 0 #define SMOKETYPE_ELECTRICAL 1 // Dr Caroll, mainframes in Infiltration bunker #define SMOKETYPE_MINI 2 // Phoenix, Laptop sentry diff --git a/src/include/game/bg.h b/src/include/game/bg.h index 0ac8645ca..89b6c7550 100644 --- a/src/include/game/bg.h +++ b/src/include/game/bg.h @@ -102,5 +102,8 @@ s32 bgFindPortalBetweenPositions(struct coord *pos1, struct coord *pos2); bool bgIsBboxOverlapping(struct coord *arg0, struct coord *arg1, struct coord *arg2, struct coord *arg3); void bgCalculatePortalBbox(s32 portalnum, struct coord *bbmin, struct coord *bbmax); void bgFindEnteredRooms(struct coord *bbmin, struct coord *upper, RoomNum *rooms, s32 maxlen, bool arg4); +#ifndef PLATFORM_N64 +void bgCalculateGlaresForVisibleRooms(void); +#endif #endif diff --git a/src/include/game/game_13c510.h b/src/include/game/game_13c510.h index 187cbc6ce..d8ee2f6dd 100644 --- a/src/include/game/game_13c510.h +++ b/src/include/game/game_13c510.h @@ -14,7 +14,7 @@ Gfx *artifactsConfigureForGlares(Gfx *gdl); Gfx *artifactsUnconfigureForGlares(Gfx *gdl); Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum); #ifndef PLATFORM_N64 -bool artifactTestLos(struct coord *spec, struct coord *roompos); +bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi); #endif #endif diff --git a/src/include/game/prop.h b/src/include/game/prop.h index 2105cc061..cdbab1737 100644 --- a/src/include/game/prop.h +++ b/src/include/game/prop.h @@ -65,4 +65,8 @@ void propsDefragRoomProps(void); void propGetBbox(struct prop *prop, f32 *radius, f32 *ymax, f32 *ymin); bool propUpdateGeometry(struct prop *prop, u8 **start, u8 **end); +#ifndef PLATFORM_N64 +bool shotTestLos(struct coord *gunpos2d, struct coord *gundir2d, struct coord *gunpos3d, struct coord *gundir3d, struct coord *endpos3d); +#endif + #endif diff --git a/src/include/lib/str.h b/src/include/lib/str.h index c80ba0dae..1a8da3de0 100644 --- a/src/include/lib/str.h +++ b/src/include/lib/str.h @@ -16,6 +16,7 @@ s32 isalpha(char c); s32 isspace(char c); s32 strtol(const char *src, const char **endptr, s32 base); #else +#include #include #include #endif