mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
Compare commits
12
Commits
v0.6.0
...
0e767334f7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e767334f7 | ||
|
|
6c183d9434 | ||
|
|
fd7800cf80 | ||
|
|
06856ed973 | ||
|
|
fbc8911238 | ||
|
|
3154efa79f | ||
|
|
af9a3c19e6 | ||
|
|
dd88c8ee28 | ||
|
|
aa8c2754d9 | ||
|
|
5787e3abbc | ||
|
|
342e825c73 | ||
|
|
f3b74e9c7c |
No files matched your search
+9
-7
@@ -23,14 +23,15 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
# declaration (no members) and rejects base-member access. So prefer GCC.
|
||||
# (GCC does hard-error on the one struct that declares a member with the same
|
||||
# name as an inherited one — CCTVRecord's `pad` — see docs/internals.md.)
|
||||
# An explicit -DCMAKE_C_COMPILER=... always wins.
|
||||
if(NOT DEFINED CMAKE_C_COMPILER)
|
||||
# An explicit -DCMAKE_C_COMPILER=... or a CC environment variable always wins.
|
||||
if(NOT DEFINED CMAKE_C_COMPILER AND "$ENV{CC}" STREQUAL "")
|
||||
if(CMAKE_HOST_APPLE)
|
||||
# Homebrew installs real GNU GCC with a version suffix; /usr/bin/gcc is
|
||||
# Apple Clang and does not support the Plan 9 struct extensions required
|
||||
# by the decomp's `inherits` pattern.
|
||||
# Homebrew (gcc-N) and MacPorts (gcc-mp-N) install real GNU GCC with a
|
||||
# version suffix; /usr/bin/gcc is Apple Clang and does not support the
|
||||
# Plan 9 struct extensions required by the decomp's `inherits` pattern.
|
||||
find_program(_GE007_GCC
|
||||
NAMES gcc-16 gcc-15 gcc-14 gcc-13 gcc-12 gcc-11 gcc)
|
||||
NAMES gcc-16 gcc-mp-16 gcc-15 gcc-mp-15 gcc-14 gcc-mp-14 gcc-13 gcc-mp-13
|
||||
gcc-12 gcc-mp-12 gcc-11 gcc-mp-11 gcc)
|
||||
else()
|
||||
find_program(_GE007_GCC NAMES gcc)
|
||||
endif()
|
||||
@@ -39,7 +40,8 @@ if(NOT DEFINED CMAKE_C_COMPILER)
|
||||
get_filename_component(_GE007_GCC_DIR "${_GE007_GCC}" DIRECTORY)
|
||||
if(CMAKE_HOST_APPLE)
|
||||
find_program(_GE007_GXX
|
||||
NAMES g++-16 g++-15 g++-14 g++-13 g++-12 g++-11 g++
|
||||
NAMES g++-16 g++-mp-16 g++-15 g++-mp-15 g++-14 g++-mp-14 g++-13 g++-mp-13
|
||||
g++-12 g++-mp-12 g++-11 g++-mp-11 g++
|
||||
PATHS "${_GE007_GCC_DIR}" NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_program(_GE007_GXX NAMES g++
|
||||
|
||||
Regular → Executable
File mode changed.
+13
-1
@@ -55,7 +55,19 @@ brew install cmake gcc sdl2 zlib python3
|
||||
|
||||
The project requires real GNU GCC for the decomp's Plan 9 struct extensions.
|
||||
Apple's `/usr/bin/gcc` is Clang and is not compatible. CMake automatically
|
||||
selects Homebrew's versioned `gcc-N`/`g++-N` executables.
|
||||
selects Homebrew's versioned `gcc-N`/`g++-N` (or MacPorts' `gcc-mp-N`/`g++-mp-N`)
|
||||
executables; set `CC`/`CXX` to pick a specific one. Delete the build directory
|
||||
after switching compilers.
|
||||
|
||||
If GCC fails with `fatal error: _bounds.h: No such file or directory`, it was
|
||||
built against a newer macOS SDK than the one CMake picked (the `xcode-select`
|
||||
default). Point `SDKROOT` at the SDK GCC was built with (`gcc -v` shows it as
|
||||
`--with-sysroot`), for example with MacPorts GCC 15:
|
||||
|
||||
```sh
|
||||
SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk \
|
||||
CC=/opt/local/bin/gcc-mp-15 CXX=/opt/local/bin/g++-mp-15 ./build-pc.sh ntsc-final
|
||||
```
|
||||
|
||||
### Asset extraction (decompilation toolchain)
|
||||
|
||||
|
||||
+213
-8
@@ -38,6 +38,12 @@ struct ShaderProgram {
|
||||
GLint frame_count_location;
|
||||
GLint noise_scale_location;
|
||||
GLint three_point_filter_locations[2];
|
||||
GLint decal_mode_location;
|
||||
GLint decal_scale_location;
|
||||
int decal_mode;
|
||||
float decal_scale;
|
||||
GLint depth_prepass_location;
|
||||
int depth_prepass;
|
||||
};
|
||||
|
||||
struct Framebuffer {
|
||||
@@ -45,6 +51,8 @@ struct Framebuffer {
|
||||
bool has_depth_buffer;
|
||||
uint32_t msaa_level;
|
||||
bool invert_y;
|
||||
bool depth_copy_checked;
|
||||
bool depth_copy_failed;
|
||||
|
||||
GLuint fbo, clrbuf, clrbuf_msaa, rbo;
|
||||
};
|
||||
@@ -63,6 +71,27 @@ static int current_anisotropy_level = 4; /* B1: sane default (0 was invalid fo
|
||||
static FilteringMode current_filter_mode = FILTER_LINEAR;
|
||||
static MipmapFilteringMode current_mipmap_filter_mode = MIPMAP_LINEAR;
|
||||
static bool current_textures_linear_filter[2] = {false, false};
|
||||
static struct ShaderProgram* current_shader_program;
|
||||
|
||||
// N64 decal depth mode: pixels only pass when close to the stored depth, using a copy of the depth buffer
|
||||
#define DECAL_DEPTH_TEXTURE_UNIT 2
|
||||
// Height of the N64 frame; the RDP's depth slope tolerance is measured in N64 pixels
|
||||
#define DECAL_N64_SCREEN_HEIGHT 240.0f
|
||||
static bool depth_copy_supported;
|
||||
static bool depth_copy_dirty = true;
|
||||
static size_t depth_copy_source;
|
||||
static GLuint depth_copy_fbo, depth_copy_tex;
|
||||
static uint32_t depth_copy_width, depth_copy_height;
|
||||
static bool current_depth_test;
|
||||
static bool current_depth_decal;
|
||||
|
||||
// Translucent surfaces write no depth and are only drawn in rough back-to-front order, so a farther
|
||||
// railing drawn later blended over a nearer one. Their nearly opaque texels (railing bars) first get
|
||||
// a depth-only pass, then the surface is drawn as usual: semi-transparent texels are unchanged.
|
||||
#define XLU_DEPTH_PREPASS_ALPHA "0.98"
|
||||
static bool current_depth_compare;
|
||||
static GLenum current_depth_func = GL_LESS;
|
||||
static bool current_blend;
|
||||
|
||||
static int gl_glsl_version = 130;
|
||||
static char gl_glsl_version_str[16] = "130";
|
||||
@@ -127,6 +156,7 @@ static void gfx_opengl_unload_shader(struct ShaderProgram* old_prg) {
|
||||
|
||||
static void gfx_opengl_load_shader(struct ShaderProgram* new_prg) {
|
||||
// if (!new_prg) return;
|
||||
current_shader_program = new_prg;
|
||||
glUseProgram(new_prg->opengl_program_id);
|
||||
gfx_opengl_vertex_array_set_attribs(new_prg);
|
||||
gfx_opengl_set_uniforms(new_prg);
|
||||
@@ -396,6 +426,10 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
|
||||
append_line(fs_buf, &fs_len, "uniform int frame_count;");
|
||||
append_line(fs_buf, &fs_len, "uniform float noise_scale;");
|
||||
append_line(fs_buf, &fs_len, "uniform int uDecalMode;");
|
||||
append_line(fs_buf, &fs_len, "uniform int uDepthPrepass;");
|
||||
append_line(fs_buf, &fs_len, "uniform highp float uDecalPixelScale;");
|
||||
append_line(fs_buf, &fs_len, "uniform highp sampler2D uDepthTex;");
|
||||
|
||||
append_line(fs_buf, &fs_len, "float random(in vec3 value) {");
|
||||
append_line(fs_buf, &fs_len, " float random = dot(sin(value), vec3(12.9898, 78.233, 37.719));");
|
||||
@@ -455,6 +489,16 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
}
|
||||
|
||||
append_line(fs_buf, &fs_len, "void main() {");
|
||||
|
||||
// RDP decal Z mode: pass only if |z - stored z| <= dz (the per-pixel depth slope), and never over cleared depth.
|
||||
// The slope is computed before anything can discard, since derivatives are undefined after a neighbor discards.
|
||||
append_line(fs_buf, &fs_len, " highp float decalSlope = fwidth(gl_FragCoord.z);");
|
||||
append_line(fs_buf, &fs_len, " if (uDecalMode != 0) {");
|
||||
append_line(fs_buf, &fs_len, " highp float storedZ = texelFetch(uDepthTex, ivec2(gl_FragCoord.xy), 0).r;");
|
||||
append_line(fs_buf, &fs_len, " highp float dz = max(decalSlope * uDecalPixelScale, 1.0 / 262144.0);");
|
||||
append_line(fs_buf, &fs_len, " if (storedZ >= 1.0 || abs(gl_FragCoord.z - storedZ) > dz) discard;");
|
||||
append_line(fs_buf, &fs_len, " }");
|
||||
|
||||
/* D548: combiner inputs are 8-bit on the N64 (shade, prim, env), so never
|
||||
* above 1.0. Under the D540 noperspective shade, GPU clipping of a big
|
||||
* near-camera triangle can extrapolate them past 1.0; the CC wrap
|
||||
@@ -584,6 +628,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
if (cc_features.opt_invisible) {
|
||||
append_line(fs_buf, &fs_len, " texel.a = 0.0;");
|
||||
}
|
||||
append_line(fs_buf, &fs_len, " if (uDepthPrepass != 0 && texel.a < " XLU_DEPTH_PREPASS_ALPHA ") discard;");
|
||||
|
||||
append_line(fs_buf, &fs_len, " OUTPUT_COLOR = texel;");
|
||||
} else {
|
||||
@@ -703,6 +748,19 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale");
|
||||
prg->three_point_filter_locations[0] = glGetUniformLocation(shader_program, "three_point_filter0");
|
||||
prg->three_point_filter_locations[1] = glGetUniformLocation(shader_program, "three_point_filter1");
|
||||
prg->decal_mode_location = glGetUniformLocation(shader_program, "uDecalMode");
|
||||
prg->decal_scale_location = glGetUniformLocation(shader_program, "uDecalPixelScale");
|
||||
prg->decal_mode = 0;
|
||||
prg->decal_scale = 0.0f;
|
||||
prg->depth_prepass_location = glGetUniformLocation(shader_program, "uDepthPrepass");
|
||||
prg->depth_prepass = 0;
|
||||
if (prg->depth_prepass_location >= 0) {
|
||||
glUniform1i(prg->depth_prepass_location, 0);
|
||||
}
|
||||
if (prg->decal_mode_location >= 0) {
|
||||
glUniform1i(prg->decal_mode_location, 0);
|
||||
glUniform1i(glGetUniformLocation(shader_program, "uDepthTex"), DECAL_DEPTH_TEXTURE_UNIT);
|
||||
}
|
||||
|
||||
gfx_opengl_load_shader(prg);
|
||||
|
||||
@@ -722,6 +780,7 @@ static void gfx_opengl_shader_get_info(struct ShaderProgram* prg, uint8_t* num_i
|
||||
|
||||
static void gfx_opengl_clear_shaders(void) {
|
||||
glUseProgram(0);
|
||||
current_shader_program = nullptr;
|
||||
for (auto& pair : shader_program_pool) {
|
||||
glDeleteProgram(pair.second.opengl_program_id);
|
||||
}
|
||||
@@ -830,9 +889,9 @@ static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint
|
||||
}
|
||||
mipmaps = mipmaps && (current_mipmap_filter_mode != MIPMAP_DISABLED);
|
||||
const int mip_idx = mipmaps ? current_mipmap_filter_mode : 0;
|
||||
const GLint min_filter = linear_filter ? min_filters[current_filter_mode][mip_idx] : GL_NEAREST;
|
||||
/* Trilinear option (playtest 2026-10-03): TRILINEAR magnifies LINEAR too. */
|
||||
const GLint max_filter = linear_filter && (current_filter_mode == FILTER_LINEAR || current_filter_mode == FILTER_TRILINEAR) ? GL_LINEAR : GL_NEAREST;
|
||||
const GLint min_filter = linear_filter ? min_filters[current_filter_mode][mip_idx] : GL_LINEAR;
|
||||
// Point-sampled textures (e.g. HUD ammo icons) are filtered too
|
||||
const GLint max_filter = GL_LINEAR;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + tile);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
|
||||
@@ -859,6 +918,10 @@ static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint
|
||||
}
|
||||
|
||||
static void gfx_opengl_set_depth_mode(bool depth_test, bool depth_update, bool depth_compare, bool depth_source_prim, uint16_t zmode) {
|
||||
current_depth_test = depth_test;
|
||||
current_depth_compare = depth_compare;
|
||||
current_depth_decal = depth_test && depth_compare && zmode == ZMODE_DEC;
|
||||
|
||||
if (depth_test) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(depth_update ? GL_TRUE : GL_FALSE);
|
||||
@@ -867,7 +930,7 @@ static void gfx_opengl_set_depth_mode(bool depth_test, bool depth_update, bool d
|
||||
if (depth_compare) {
|
||||
switch (zmode) {
|
||||
case ZMODE_INTER:
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthFunc(current_depth_func = GL_LEQUAL);
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(0, 0);
|
||||
break;
|
||||
@@ -875,22 +938,22 @@ static void gfx_opengl_set_depth_mode(bool depth_test, bool depth_update, bool d
|
||||
case ZMODE_OPA:
|
||||
case ZMODE_XLU:
|
||||
if (depth_source_prim) {
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthFunc(current_depth_func = GL_LEQUAL);
|
||||
} else {
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthFunc(current_depth_func = GL_LESS);
|
||||
}
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(0, 0);
|
||||
break;
|
||||
|
||||
case ZMODE_DEC:
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthFunc(current_depth_func = GL_LEQUAL);
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(-2, -2);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glDepthFunc(current_depth_func = GL_ALWAYS);
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(0, 0);
|
||||
}
|
||||
@@ -916,6 +979,7 @@ static void gfx_opengl_set_scissor(int x, int y, int width, int height) {
|
||||
}
|
||||
|
||||
static void gfx_opengl_set_use_alpha(bool use_alpha, bool modulate) {
|
||||
current_blend = use_alpha && !modulate;
|
||||
if (use_alpha) {
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
@@ -928,10 +992,142 @@ static void gfx_opengl_set_use_alpha(bool use_alpha, bool modulate) {
|
||||
}
|
||||
}
|
||||
|
||||
static GLuint gfx_opengl_framebuffer_name(size_t fb_id) {
|
||||
// fb 0 is the window's default framebuffer (GL name 0)
|
||||
return fb_id == 0 ? 0 : framebuffers[fb_id].fbo;
|
||||
}
|
||||
|
||||
static bool gfx_opengl_update_depth_copy(void) {
|
||||
if (!depth_copy_supported) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Framebuffer& fb = framebuffers[current_framebuffer];
|
||||
if ((current_framebuffer != 0 && !fb.has_depth_buffer) || fb.depth_copy_failed) {
|
||||
return false;
|
||||
}
|
||||
if (!depth_copy_dirty && depth_copy_source == current_framebuffer && depth_copy_width == fb.width &&
|
||||
depth_copy_height == fb.height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (depth_copy_tex == 0 || depth_copy_width != fb.width || depth_copy_height != fb.height) {
|
||||
if (depth_copy_tex == 0) {
|
||||
glGenTextures(1, &depth_copy_tex);
|
||||
glGenFramebuffers(1, &depth_copy_fbo);
|
||||
}
|
||||
|
||||
GLint active_texture;
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
|
||||
glActiveTexture(GL_TEXTURE0 + DECAL_DEPTH_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_copy_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, fb.width, fb.height, 0, GL_DEPTH_STENCIL,
|
||||
GL_UNSIGNED_INT_24_8, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glActiveTexture(active_texture);
|
||||
|
||||
const GLenum no_draw_buffer = GL_NONE;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, depth_copy_fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depth_copy_tex, 0);
|
||||
glDrawBuffers(1, &no_draw_buffer);
|
||||
glReadBuffer(GL_NONE);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gfx_opengl_framebuffer_name(current_framebuffer));
|
||||
|
||||
depth_copy_width = fb.width;
|
||||
depth_copy_height = fb.height;
|
||||
}
|
||||
|
||||
const bool check_errors = !fb.depth_copy_checked;
|
||||
if (check_errors) {
|
||||
for (int i = 0; i < 16 && glGetError() != GL_NO_ERROR; i++) {
|
||||
}
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, gfx_opengl_framebuffer_name(current_framebuffer));
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, depth_copy_fbo);
|
||||
glBlitFramebuffer(0, 0, fb.width, fb.height, 0, 0, fb.width, fb.height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gfx_opengl_framebuffer_name(current_framebuffer));
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
if (check_errors) {
|
||||
fb.depth_copy_checked = true;
|
||||
if (glGetError() != GL_NO_ERROR) {
|
||||
fb.depth_copy_failed = true;
|
||||
sysLogPrintf(LOG_WARNING, "GL: could not copy depth buffer of framebuffer %u, decal edges will not be clipped",
|
||||
(unsigned)current_framebuffer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
depth_copy_dirty = false;
|
||||
depth_copy_source = current_framebuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gfx_opengl_set_decal_uniforms(struct ShaderProgram* prg, bool enabled) {
|
||||
if (prg == nullptr || prg->decal_mode_location < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int mode = enabled ? 1 : 0;
|
||||
if (prg->decal_mode != mode) {
|
||||
glUniform1i(prg->decal_mode_location, mode);
|
||||
prg->decal_mode = mode;
|
||||
}
|
||||
|
||||
const float scale = framebuffers[current_framebuffer].height / DECAL_N64_SCREEN_HEIGHT;
|
||||
if (enabled && prg->decal_scale != scale) {
|
||||
glUniform1f(prg->decal_scale_location, scale);
|
||||
prg->decal_scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_opengl_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) {
|
||||
// printf("flushing %d tris\n", buf_vbo_num_tris);
|
||||
if (current_depth_decal) {
|
||||
// The shader replaces the depth test like the RDP does; fall back to polygon offset if depth can't be copied
|
||||
const bool decal = gfx_opengl_update_depth_copy();
|
||||
glDepthFunc(decal ? GL_ALWAYS : GL_LEQUAL);
|
||||
if (decal) {
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
} else {
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
}
|
||||
gfx_opengl_set_decal_uniforms(current_shader_program, decal);
|
||||
} else {
|
||||
gfx_opengl_set_decal_uniforms(current_shader_program, false);
|
||||
}
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * buf_vbo_len, buf_vbo, GL_STREAM_DRAW);
|
||||
|
||||
struct ShaderProgram* prg = current_shader_program;
|
||||
if (!current_depth_decal && current_depth_test && current_depth_compare && !current_depth_mask && current_blend &&
|
||||
prg != nullptr && prg->depth_prepass_location >= 0) {
|
||||
glUniform1i(prg->depth_prepass_location, 1);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris);
|
||||
glUniform1i(prg->depth_prepass_location, 0);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
// The colour pass must pass where the prepass just stored this surface's own depth
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris);
|
||||
glDepthFunc(current_depth_func);
|
||||
depth_copy_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris);
|
||||
|
||||
if (current_depth_test && current_depth_mask) {
|
||||
depth_copy_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (APIENTRY *DEBUGPROC)(GLenum source,
|
||||
@@ -1122,6 +1318,11 @@ static void gfx_opengl_init(void) {
|
||||
gfx_framebuffers_enabled = false;
|
||||
}
|
||||
|
||||
depth_copy_supported = GLVersion.major >= 3 && gfx_opengl_supports_framebuffers() && glDrawBuffers && glReadBuffer;
|
||||
if (!depth_copy_supported) {
|
||||
sysLogPrintf(LOG_WARNING, "GL: depth buffer copies unsupported, decal edges will not be clipped");
|
||||
}
|
||||
|
||||
if ((GLVersion.major < 4 || GLVersion.minor < 4) && !GLAD_GL_ARB_texture_mirror_clamp_to_edge) {
|
||||
// GL_MIRROR_CLAMP_TO_EDGE unsupported
|
||||
gl_mirror_clamp = GL_MIRRORED_REPEAT;
|
||||
@@ -1455,6 +1656,7 @@ static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width,
|
||||
fb.has_depth_buffer = has_depth_buffer;
|
||||
fb.msaa_level = msaa_level;
|
||||
fb.invert_y = opengl_invert_y;
|
||||
depth_copy_dirty = true;
|
||||
}
|
||||
|
||||
bool gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) {
|
||||
@@ -1469,6 +1671,7 @@ bool gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) {
|
||||
* GL implementation like Mesa/llvmpipe). */
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb_id == 0 ? 0 : fb.fbo);
|
||||
current_framebuffer = fb_id;
|
||||
depth_copy_dirty = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -1486,6 +1689,7 @@ void gfx_opengl_clear_framebuffer(bool clear_color, bool clear_depth) {
|
||||
if (clear_depth) {
|
||||
glDepthMask(GL_TRUE);
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
depth_copy_dirty = true;
|
||||
}
|
||||
glClear(mask);
|
||||
if (clear_depth) {
|
||||
@@ -1509,6 +1713,7 @@ void gfx_opengl_clear_region(bool clear_color, bool clear_depth, int x, int y, i
|
||||
if (clear_depth) {
|
||||
glDepthMask(GL_TRUE);
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
depth_copy_dirty = true;
|
||||
}
|
||||
glScissor(x, y, w, h);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
@@ -112,6 +112,8 @@ s32 portSkipIntro = 0;
|
||||
* hook in src/game/bondview2.c currentPlayerSetFadeColour). 0 (default) =
|
||||
* original damage flash. */
|
||||
s32 portNoHitFlash = 0;
|
||||
s32 portDecalLimit = 100; /* Game.DecalLimit: bullet holes / blood splats kept per stage (N64: 100) */
|
||||
s32 portMaxCorpses = 0; /* Game.MaxCorpses: 0 = bodies fade after dying (N64), -1 = never */
|
||||
|
||||
/* v0.4.0 M2 (D373/D379): in-game crosshair on/off + tint
|
||||
* (gunfire.c gunDrawSight, #ifdef PORT). Default keeps the original
|
||||
@@ -561,6 +563,8 @@ PD_CONSTRUCTOR static void videoConfigInit(void)
|
||||
configRegisterFloat("Game.ScreenShakeIntensity", &portScreenShakeScale, 0.0f, 10.0f);
|
||||
configRegisterInt("Game.SkipIntro", &portSkipIntro, 0, 1);
|
||||
configRegisterInt("Game.NoHitFlash", &portNoHitFlash, 0, 1);
|
||||
configRegisterInt("Game.DecalLimit", &portDecalLimit, 100, 2000);
|
||||
configRegisterInt("Game.MaxCorpses", &portMaxCorpses, -1, 64); /* -1 = never fade */
|
||||
configRegisterInt("Game.AIWideView", &cfgAIWideView, 0, 1); /* D468 */
|
||||
configRegisterInt("Video.CrosshairHide", &portCrosshairHide, 0, 1); /* v0.4.0 M2 (D373) */
|
||||
configRegisterInt("Video.CrosshairPersistent", &cfgCrosshairPersistent, 0, 1); /* D436 */
|
||||
|
||||
@@ -764,6 +764,22 @@ Gfx *sub_GAME_7F0B3C8C(Gfx *gdl)
|
||||
{
|
||||
if (i == dword_CODE_bss_8007FFA0[j].unk1)
|
||||
{
|
||||
#ifdef PORT
|
||||
/* Tinted glass stands in a window opening at the far side of
|
||||
* the room, yet it is a prop and so was drawn after the room's
|
||||
* translucent geometry. Railing balusters in front of it are
|
||||
* such geometry (an XLU quad that writes no depth), so the
|
||||
* glass painted over them (Facility main hall). Draw the room's
|
||||
* tinted glass before its translucent geometry instead. */
|
||||
if (get_debug_do_draw_obj() && sub_GAME_7F0BD8F0())
|
||||
{
|
||||
gSPMatrix(gdl++, osVirtualToPhysical((void*)currentPlayerGetProjectionMatrix()), (G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION));
|
||||
gdl = fogRenderClearFogMode(gdl);
|
||||
g_PortXluGlassPhase = 1;
|
||||
gdl = chrpropsRenderPass(gdl, dword_CODE_bss_8007FFA0[j].roomid, 1);
|
||||
}
|
||||
g_PortXluGlassPhase = 2;
|
||||
#endif
|
||||
gSPMatrix(gdl++, osVirtualToPhysical((void*)get_BONDdata_field_10E0()), (G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION));
|
||||
gdl = fogSetRenderFogColor(
|
||||
bgScissorCurrentPlayerViewF(
|
||||
@@ -792,6 +808,9 @@ Gfx *sub_GAME_7F0B3C8C(Gfx *gdl)
|
||||
gdl = chrpropsRenderPass(gdl, dword_CODE_bss_8007FFA0[j].roomid, 1);
|
||||
}
|
||||
}
|
||||
#ifdef PORT
|
||||
g_PortXluGlassPhase = 0;
|
||||
#endif
|
||||
|
||||
if (1);
|
||||
}
|
||||
@@ -3358,6 +3377,38 @@ bool bgTestRayIntersectsBbox(coord3d *origin, coord3d *dir, s32 *bbox_min, s32 *
|
||||
}
|
||||
|
||||
|
||||
#ifdef PORT
|
||||
#include "image.h"
|
||||
|
||||
/* The image id of a room triangle's texture, read like the N64 does: texLoad
|
||||
* stores the id in the 8 bytes before the texture data, and the room's
|
||||
* G_SETTIMG points at that data (a full host pointer in w1 on PC). Room GDLs
|
||||
* load into the default pool, so anything outside it is not a texture.
|
||||
* The id selects the impact decal, sound and dust puff (HIT_WATER: no decal,
|
||||
* no puff, splash sound). */
|
||||
static s32 bgTexnumFromSetTimg(Gfx *settimg)
|
||||
{
|
||||
struct texpool *pool = &ptr_texture_alloc_start;
|
||||
u8 *data = (u8 *)settimg->words.w1;
|
||||
s32 texnum;
|
||||
|
||||
if (data < pool->start + 8 || data >= (u8 *)pool->end)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
texnum = *(s16 *)(data - 8);
|
||||
|
||||
if (texnum < 0 || texnum >= MAX_TEXTURES)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return texnum;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool bgTestRayIntersectionInRoom(coord3d *from, coord3d *to, coord3d *dir, RoomVtxBatchBounds *point, s32 roomnum, struct HitThing *hitthing)
|
||||
{
|
||||
Vertex *vtxbase;
|
||||
@@ -3555,6 +3606,11 @@ bool bgTestRayIntersectionInRoom(coord3d *from, coord3d *to, coord3d *dir, RoomV
|
||||
#ifdef PORT
|
||||
/* D154: KSEG0 texnum deref invalid for converted GDLs (cf. D135). D431 (#119): light fixtures are the one texture class the game acts on, so recover their image id from the fixture table. */
|
||||
texnum = lightFixtureTexnumForGfx(gdl, roomnum);
|
||||
|
||||
if (texnum < 0)
|
||||
{
|
||||
texnum = bgTexnumFromSetTimg(tcmd);
|
||||
}
|
||||
#else
|
||||
temp.word = ((u32 *) tcmd)[1] - 8;
|
||||
texnum = *((u16 *) (temp.word | 0x80000000));
|
||||
@@ -3737,6 +3793,11 @@ bool bgTestRayIntersectionInRoom(coord3d *from, coord3d *to, coord3d *dir, RoomV
|
||||
#ifdef PORT
|
||||
/* D154: KSEG0 texnum deref invalid for converted GDLs (cf. D135). D431 (#119): light fixtures are the one texture class the game acts on, so recover their image id from the fixture table. */
|
||||
texnum = lightFixtureTexnumForGfx(gdl, roomnum);
|
||||
|
||||
if (texnum < 0)
|
||||
{
|
||||
texnum = bgTexnumFromSetTimg(tcmd);
|
||||
}
|
||||
#else
|
||||
temp.word = ((u32 *) tcmd)[1] - 8;
|
||||
texnum = *((u16 *) (temp.word | 0x80000000));
|
||||
|
||||
@@ -887,6 +887,99 @@ void chrlvActorFadeAway(ChrRecord *self)
|
||||
}
|
||||
|
||||
|
||||
#ifdef PORT
|
||||
/* Game.MaxCorpses: 0 = original (every body fades once its death animation
|
||||
* ends), -1 = bodies never fade unless guard slots run low (spawning needs 3
|
||||
* free). Otherwise bodies stay, and the oldest starts its normal fade once
|
||||
* there are more than this many. A kept body is marked by allowfade ==
|
||||
* CORPSE_KEEP, and reaptimer holds its death order. */
|
||||
#define CORPSE_MIN_FREE_SLOTS 4
|
||||
#define CORPSE_KEEP (-2)
|
||||
extern s32 portMaxCorpses;
|
||||
s32 chrGetNumFree(void);
|
||||
static s32 s_CorpseSeq = 0;
|
||||
|
||||
static void chrlvActorKeepCorpse(ChrRecord *self)
|
||||
{
|
||||
if (self->actiontype != ACT_DEAD)
|
||||
{
|
||||
chrStopFiring(self);
|
||||
self->actiontype = ACT_DEAD;
|
||||
self->act_dead.allowfade = CORPSE_KEEP;
|
||||
self->act_dead.reaptimer = ++s_CorpseSeq;
|
||||
self->sleep = 0;
|
||||
|
||||
/* Scripts see a removed guard once its body would have faded: level
|
||||
* scripts respawn reinforcements on if_chr_does_not_exist(id), and
|
||||
* spawned guards reuse that id via SetMyChrNum. Move the body to a
|
||||
* fresh internal id (as chrAllocate does) so its old one frees up. */
|
||||
self->chrnum = (s16)player1_guardID;
|
||||
player1_guardID += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void chrlvFadeExcessCorpses(void)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
s32 i;
|
||||
s32 count = 0;
|
||||
s32 fading = 0;
|
||||
bool excess;
|
||||
ChrRecord *oldest = NULL;
|
||||
ChrRecord *oldestoffscreen = NULL;
|
||||
|
||||
for (i = 0; i < g_NumChrSlots; i++)
|
||||
{
|
||||
ChrRecord *chr = &g_ChrSlots[i];
|
||||
|
||||
if (chr->model == NULL || chr->prop == NULL || chr->actiontype != ACT_DEAD)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chr->act_dead.allowfade != CORPSE_KEEP)
|
||||
{
|
||||
/* already fading: its slot frees up within 1.5 s */
|
||||
fading++;
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
if (oldest == NULL || chr->act_dead.reaptimer < oldest->act_dead.reaptimer)
|
||||
{
|
||||
oldest = chr;
|
||||
}
|
||||
|
||||
if ((chr->prop->flags & PROPFLAG_ONSCREEN) == 0
|
||||
&& (oldestoffscreen == NULL || chr->act_dead.reaptimer < oldestoffscreen->act_dead.reaptimer))
|
||||
{
|
||||
oldestoffscreen = chr;
|
||||
}
|
||||
}
|
||||
|
||||
if (portMaxCorpses > 0)
|
||||
{
|
||||
excess = count > portMaxCorpses;
|
||||
}
|
||||
else
|
||||
{
|
||||
excess = chrGetNumFree() + fading < CORPSE_MIN_FREE_SLOTS;
|
||||
}
|
||||
|
||||
if (!excess || oldest == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prefer one the player can't see; chrlvTickDead starts the fade */
|
||||
(oldestoffscreen ? oldestoffscreen : oldest)->act_dead.allowfade = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* chrStepToSide
|
||||
@@ -5331,6 +5424,13 @@ void chrlvTickSurrender(ChrRecord *self)
|
||||
*/
|
||||
void chrlvTickDead(ChrRecord *self)
|
||||
{
|
||||
#ifdef PORT
|
||||
if (self->act_dead.allowfade == CORPSE_KEEP)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (self->act_init.padding[0] >= 0)
|
||||
{
|
||||
self->act_init.padding[0] += g_ClockTimer;
|
||||
@@ -5502,6 +5602,13 @@ void chrlvTickDie(ChrRecord *self)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef PORT
|
||||
if (portMaxCorpses != 0)
|
||||
{
|
||||
chrlvActorKeepCorpse(self);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
chrlvActorFadeAway(self);
|
||||
}
|
||||
|
||||
@@ -9618,6 +9725,13 @@ void chrlvAllChrTick(void)
|
||||
chrlvActionTick(&g_ActiveChrs[i]);
|
||||
}
|
||||
|
||||
#ifdef PORT
|
||||
if (portMaxCorpses != 0)
|
||||
{
|
||||
chrlvFadeExcessCorpses();
|
||||
}
|
||||
#endif
|
||||
|
||||
g_SeenBondRecentlyGuardCount = 0;
|
||||
|
||||
for (i=0; i<max; i++)
|
||||
|
||||
@@ -334,6 +334,11 @@ s32 chrpropTestPointInPolygon(coord3d *point, struct rect4f *poly
|
||||
void roomGetProps(s32 *roomids);
|
||||
ObjectRecord *scan_position_data_table_for_normal_object_at_preset(s32 arg0);
|
||||
Gfx *chrpropsRenderPass(Gfx *arg0, s32 roomid, s32 arg2);
|
||||
#ifdef PORT
|
||||
/* Translucent prop pass filter: 0 = all props, 1 = tinted glass only,
|
||||
* 2 = all but tinted glass (see bg.c). */
|
||||
extern s32 g_PortXluGlassPhase;
|
||||
#endif
|
||||
PropRecord *chrpropGetActiveTail(void);
|
||||
void propsDefragRoomProps(void);
|
||||
void chraiGetPropRoomIds(PropRecord *self, s32 *roomids);
|
||||
|
||||
@@ -466,6 +466,15 @@ Gfx *chrpropRender(Gfx * gdl, PropRecord *prop, s32 withalpha)
|
||||
/**
|
||||
* Address: 7F03A6F4
|
||||
*/
|
||||
#ifdef PORT
|
||||
s32 g_PortXluGlassPhase = 0;
|
||||
|
||||
static bool chrpropIsTintedGlass(PropRecord *prop)
|
||||
{
|
||||
return prop->type == PROP_TYPE_OBJ && prop->obj != NULL && prop->obj->type == PROPDEF_TINTED_GLASS;
|
||||
}
|
||||
#endif
|
||||
|
||||
Gfx *chrpropsRenderPass(Gfx *gdl, s32 roomid, s32 renderpass)
|
||||
{
|
||||
s32 flag;
|
||||
@@ -542,6 +551,14 @@ Gfx *chrpropsRenderPass(Gfx *gdl, s32 roomid, s32 renderpass)
|
||||
{
|
||||
prop = *pp;
|
||||
|
||||
#ifdef PORT
|
||||
if (prop != NULL && g_PortXluGlassPhase != 0
|
||||
&& chrpropIsTintedGlass(prop) != (g_PortXluGlassPhase == 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (prop != NULL)
|
||||
{
|
||||
flag = 0;
|
||||
@@ -1176,6 +1193,14 @@ void chraiDefaultWeaponFireHandler(s32 hand)
|
||||
rooms[1] = 255;
|
||||
explosionCreate(0, finalpos, hittile, 1, 0, get_cur_playernum(), rooms, 0);
|
||||
}
|
||||
#ifdef PORT
|
||||
else if (gotbghit && besttexture >= 0 && g_Textures[besttexture].hitTexture == HIT_WATER)
|
||||
{
|
||||
/* a splash instead of the yellow impact flash below */
|
||||
explosionCreateWaterSplash(finalpos, bestroom);
|
||||
createSpark = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
finalpos->x -= 26.0f * shotdata.dir.x;
|
||||
|
||||
@@ -76,6 +76,11 @@ void dynInitMemory(void) {
|
||||
* targets, so g_VtxBuffers is left alone. */
|
||||
{
|
||||
s32 gfxHalf = g_GfxSizesByPlayerCount[getPlayerCount() - 1] * ((s32)sizeof(Gfx) / 8);
|
||||
extern s32 portDecalLimit;
|
||||
/* Game.DecalLimit: each impact above the default 100 needs a few more commands */
|
||||
if (portDecalLimit > 100) {
|
||||
gfxHalf += (portDecalLimit - 100) * 4 * (s32)sizeof(Gfx);
|
||||
}
|
||||
g_GfxBuffers[0] = mempAllocBytesInBank(gfxHalf * 2, MEMPOOL_STAGE);
|
||||
g_GfxBuffers[1] = (g_GfxBuffers[0] + gfxHalf);
|
||||
g_GfxBuffers[2] = (g_GfxBuffers[1] + gfxHalf);
|
||||
|
||||
@@ -74,6 +74,9 @@ struct Scorch *g_ScorchBuffer;
|
||||
* cannonically wallhitlist
|
||||
*/
|
||||
struct BulletImpact *g_BulletImpactBuffer;
|
||||
#ifdef PORT
|
||||
s32 g_BulletImpactBufferLen = BULLET_IMPACT_BUFFER_LEN_DEFAULT;
|
||||
#endif
|
||||
|
||||
// data
|
||||
//D:80040170
|
||||
@@ -1587,6 +1590,9 @@ void explosionInitFlyingParticles(coord3d *spawnpos, f32 spawn_rand_scale, f32 s
|
||||
rand3 = (2.0f * (((f32) randomGetNext()) * (1.0f / (f32)UINT_MAX))) - 1.0f;
|
||||
|
||||
g_FlyingParticlesBuffer[g_NumParticleEntries].unk00 = 1;
|
||||
#ifdef PORT
|
||||
g_FlyingParticlesBuffer[g_NumParticleEntries].is_splash = FALSE;
|
||||
#endif
|
||||
|
||||
g_FlyingParticlesBuffer[g_NumParticleEntries].position.f[0] = spawnpos->f[0] + (spawn_rand_scale * rand1);
|
||||
g_FlyingParticlesBuffer[g_NumParticleEntries].position.f[1] = spawnpos->f[1] + (spawn_rand_scale * rand2);
|
||||
@@ -1736,6 +1742,15 @@ void explosionUpdateFlyingParticles(void)
|
||||
g_FlyingParticlesBuffer[i].unk00 = 0;
|
||||
}
|
||||
|
||||
#ifdef PORT
|
||||
if (g_FlyingParticlesBuffer[i].is_splash
|
||||
&& g_FlyingParticlesBuffer[i].position_drift.f[1] < 0.0f
|
||||
&& g_FlyingParticlesBuffer[i].position.f[1] < g_FlyingParticlesBuffer[i].splash_y)
|
||||
{
|
||||
g_FlyingParticlesBuffer[i].unk00 = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// position-related. deletes particles that are too low or too high.
|
||||
if ((g_FlyingParticlesBuffer[i].position.f[1] < -30000.0f) || (g_FlyingParticlesBuffer[i].position.f[1] > 30000.0f))
|
||||
{
|
||||
@@ -1746,6 +1761,62 @@ void explosionUpdateFlyingParticles(void)
|
||||
}
|
||||
|
||||
|
||||
#ifdef PORT
|
||||
#include "glass.h"
|
||||
|
||||
/* Bullet hitting water (PC): the N64 only drew the yellow impact flash there.
|
||||
* Use the chr hit puffs instead (chrCreateHitPuffs: the small impact puff and
|
||||
* the larger burst), tinted to the water colour, plus droplets (the wall-hit
|
||||
* dust particles, recoloured) thrown up and removed when they fall back
|
||||
* through the surface. */
|
||||
#define WATER_SPLASH_R 0xA0
|
||||
#define WATER_SPLASH_G 0xC8
|
||||
#define WATER_SPLASH_B 0xF0
|
||||
#define WATER_SPLASH_DROPLETS 12
|
||||
|
||||
static void explosionTintSpark(s_bullet_spark *spark)
|
||||
{
|
||||
if (spark != NULL)
|
||||
{
|
||||
spark->unk28 = WATER_SPLASH_R;
|
||||
spark->unk29 = WATER_SPLASH_G;
|
||||
spark->unk2A = WATER_SPLASH_B;
|
||||
spark->unk2B = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void explosionCreateWaterSplash(coord3d *pos, s16 room)
|
||||
{
|
||||
s32 i;
|
||||
s32 j;
|
||||
|
||||
explosionTintSpark(bullet_spark_create(pos, 3, 43.0f, room));
|
||||
explosionTintSpark(bullet_spark_create(pos, 1, 21.0f, room));
|
||||
|
||||
for (i = 0; i < WATER_SPLASH_DROPLETS; i++)
|
||||
{
|
||||
struct FlyingParticles *drop = &g_FlyingParticlesBuffer[g_NumParticleEntries];
|
||||
|
||||
explosionInitFlyingParticles(pos, 2.0f, 1.0f, 6.0f, 3.0f);
|
||||
|
||||
drop->is_splash = TRUE;
|
||||
drop->splash_y = pos->f[1];
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
/* vary the brightness per vertex, keep the water hue */
|
||||
s32 shade = 0xC0 + (randomGetNext() & 0x3F);
|
||||
|
||||
drop->vertex_list[j].v.cn[0] = (WATER_SPLASH_R * shade) / 0xFF;
|
||||
drop->vertex_list[j].v.cn[1] = (WATER_SPLASH_G * shade) / 0xFF;
|
||||
drop->vertex_list[j].v.cn[2] = (WATER_SPLASH_B * shade) / 0xFF;
|
||||
drop->vertex_list[j].v.cn[3] = 0xB0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
extern Gfx globalDL_0xa50;
|
||||
|
||||
/***
|
||||
@@ -2062,6 +2133,21 @@ void explosionCreateBulletImpact(struct coord3d *pos, struct coord3d *arg1, s16
|
||||
impact_type = 0x10;
|
||||
}
|
||||
|
||||
#ifdef PORT
|
||||
/* Glass objects: the bulletproof/unbreakable ones take the texture-based
|
||||
* hole, a decal drawn in the opaque pass. Transparent glass writes no
|
||||
* depth there, so the decal depth test (RDP ZMODE_DEC, emulated exactly)
|
||||
* rejects every pixel. Use the window crack the breakable glass gets: it
|
||||
* is drawn after the glass in the translucent pass, without the decal
|
||||
* test. */
|
||||
if ((prop != NULL) && (impact_type != 0x10)
|
||||
&& ((prop->obj->type == PROPDEF_GLASS) || (prop->obj->type == PROPDEF_TINTED_GLASS))
|
||||
&& ((g_ImpactTypes[impact_type].unk2 >= 2) || (g_ImpactTypes[impact_type].unk1 != 2)))
|
||||
{
|
||||
impact_type = (randomGetNext() % 3) + 0x11;
|
||||
}
|
||||
#endif
|
||||
|
||||
spA0.f[0] = pos->f[0];
|
||||
spA0.f[1] = pos->f[1];
|
||||
spA0.f[2] = pos->f[2];
|
||||
|
||||
+16
-1
@@ -13,7 +13,14 @@
|
||||
#define SMOKE_PARTS_LEN 10
|
||||
#define SCORCH_BUFFER_LEN 20
|
||||
#define IMPACT_TYPE_LEN 20
|
||||
#define BULLET_IMPACT_BUFFER_LEN 100
|
||||
#define BULLET_IMPACT_BUFFER_LEN_DEFAULT 100
|
||||
#ifdef PORT
|
||||
/* Game.DecalLimit, latched when the stage's impact buffer is allocated */
|
||||
extern s32 g_BulletImpactBufferLen;
|
||||
#define BULLET_IMPACT_BUFFER_LEN g_BulletImpactBufferLen
|
||||
#else
|
||||
#define BULLET_IMPACT_BUFFER_LEN BULLET_IMPACT_BUFFER_LEN_DEFAULT
|
||||
#endif
|
||||
#define MAX_FLYING_PARTICLES 200
|
||||
|
||||
typedef struct s_smoketype {
|
||||
@@ -141,6 +148,11 @@ struct FlyingParticles {
|
||||
s32 unk34;
|
||||
|
||||
Vtx vertex_list[4];
|
||||
#ifdef PORT
|
||||
/* water splash droplet: removed once it falls back below splash_y */
|
||||
s32 is_splash;
|
||||
f32 splash_y;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern f32 g_SpExplosionDamageMult;
|
||||
@@ -171,6 +183,9 @@ Gfx *explosionRenderPropSmoke(PropRecord *arg0, Gfx *arg1, s32 withalpha);
|
||||
Gfx *explosionRenderBulletImpactOnProp(Gfx *arg0, PropRecord *arg1, s32 arg2);
|
||||
|
||||
void explosionCreateSmoke(coord3d *pos, StandTile *stan, s16 smoke_type, u8 *rooms, s32 flags);
|
||||
#ifdef PORT
|
||||
void explosionCreateWaterSplash(coord3d *pos, s16 room);
|
||||
#endif
|
||||
void explosionUpdateFlyingParticles(void);
|
||||
u8 explosionChrpropSmokeTick(PropRecord* prop);
|
||||
u8 explosionChrpropExplosionTick(PropRecord* prop);
|
||||
|
||||
@@ -57,6 +57,12 @@ void alloc_explosion_smoke_casing_scorch_impact_buffers(void)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PORT
|
||||
{
|
||||
extern s32 portDecalLimit;
|
||||
g_BulletImpactBufferLen = portDecalLimit;
|
||||
}
|
||||
#endif
|
||||
osSyncPrintf("Allocating %d bytes for wallhit data\n", BULLET_IMPACT_BUFFER_LEN * sizeof(struct BulletImpact));
|
||||
g_BulletImpactBuffer = (struct BulletImpact *)mempAllocBytesInBank(BULLET_IMPACT_BUFFER_LEN * sizeof(struct BulletImpact), MEMPOOL_STAGE);
|
||||
|
||||
|
||||
@@ -28,6 +28,13 @@ void alloc_init_GUARDdata_entries(s32 count)
|
||||
s32 i;
|
||||
|
||||
g_NumChrSlots = count + 10; //special guards?
|
||||
#ifdef PORT
|
||||
{
|
||||
/* Game.MaxCorpses: kept bodies hold their slots; keep room for spawns */
|
||||
extern s32 portMaxCorpses;
|
||||
g_NumChrSlots += portMaxCorpses < 0 ? 64 : portMaxCorpses;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_ChrSlots = mempAllocBytesInBank(align_addr_zero(g_NumChrSlots * sizeof(ChrRecord)), MEMPOOL_STAGE);
|
||||
for(i = 0; g_NumChrSlots > i; i++)
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
#include "bondtypes.h"
|
||||
#include "initunk_005450.h"
|
||||
|
||||
#define MODELHITENTRIES_LEN 600
|
||||
|
||||
extern struct ModelHitEntry *g_ModelHitFreeList; // canonically freedist
|
||||
|
||||
/**
|
||||
* These are linker/BSS labels. g_ModelHitEntries is the start of a
|
||||
* ModelHitEntry[600] pool. g_ModelHitEntriesPenultimate labels entry 598
|
||||
* ModelHitEntry[MODELHITENTRIES_LEN] pool. g_ModelHitEntriesPenultimate labels entry 598
|
||||
* inside that same pool and is kept as a raw char symbol so IDO emits the
|
||||
* original relocation.
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
#define _INITUNK_005450_H_
|
||||
#include <ultra64.h>
|
||||
|
||||
/* Hit-test/draw-order entries shared by every model drawn in a frame (each
|
||||
* chr takes one per drawable node, ~20-30). PC: room for many more drawn
|
||||
* chrs, e.g. bodies kept by Game.MaxCorpses; exhausting the pool left later
|
||||
* chrs without entries and crashed the next shot test. */
|
||||
#if defined(PLATFORM_64BIT)
|
||||
#define MODELHITENTRIES_LEN 8192
|
||||
#else
|
||||
#define MODELHITENTRIES_LEN 600
|
||||
#endif
|
||||
|
||||
void initModelHitEntryFreeList(void);
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <ultra64.h>
|
||||
#include <memp.h>
|
||||
#include "objecthandler.h"
|
||||
#include "initunk_005450.h"
|
||||
#include "model.h"
|
||||
|
||||
// bss
|
||||
@@ -11,7 +12,7 @@
|
||||
// writes 600*40 bytes from g_ModelHitEntries and overruns the N64-sized placeholders
|
||||
// by 12KB into adjacent .bss globals. Size the pool for the real struct on PC; keep
|
||||
// the original placeholder chain verbatim on N64.
|
||||
char g_ModelHitEntries[600 * sizeof(ModelHitEntry)];
|
||||
char g_ModelHitEntries[MODELHITENTRIES_LEN * sizeof(ModelHitEntry)];
|
||||
#else
|
||||
//CODE.bss:80076A50
|
||||
char g_ModelHitEntries[0xC];
|
||||
@@ -1269,6 +1270,16 @@ s32 sub_GAME_7F06C010(ModelHitEntry **entryptr, coord3d *modelRayStart, coord3d
|
||||
{
|
||||
ModelHitEntry *entry = *entryptr;
|
||||
|
||||
#ifdef PORT
|
||||
/* No entries (the shared pool ran out while drawing): nothing to hit */
|
||||
if (entry == NULL)
|
||||
{
|
||||
*outModel = NULL;
|
||||
*outNode = NULL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (entry->next != NULL)
|
||||
{
|
||||
entry = entry->next;
|
||||
|
||||
Reference in new issue
Block a user