mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
Gameplay and rendering fixes D463-D468; D441 pointer-width sites
Zero-line tile texrect guard and texture-import skip (D463), stage-bank red zone checker (D464), Dam/Caverns water slide (D465, #127), extended draw distance no longer changing gameplay (D466, #125), player matrix pointers and Model/PROMOTE/vtxstore rebase (D457 c/d), AI view clamp for ultrawide (D468), fire-sound path audit (D433, D467 #130), italoarruda's #107 pointer-width sites (two sites, credited). Refs: D463 D464 D465 D466 D467 D468 D457 D433 #125 #127 #130 #107 Co-authored-by: italoarruda <3868006+italoarruda@users.noreply.github.com>
This commit is contained in:
1 parent
00fdd94c8d
commit
9dd98bdb20
27 files changed
+972
-39
No files matched your search
@@ -0,0 +1,43 @@
|
||||
#ifndef _DRAWDISTGAMEPLAY_H_
|
||||
#define _DRAWDISTGAMEPLAY_H_
|
||||
|
||||
/* D466 (#125): Video.DrawDistance must not change GAMEPLAY visibility.
|
||||
* Rendering keeps the extended range; gameplay readers (AI "on screen",
|
||||
* "has been seen", out-of-view spawn checks, shot tests, ...) see exactly
|
||||
* what the N64 would at the level's authored FarFog. Design + reader triage:
|
||||
* docs/dev/findings.md D466. Every hook is a no-op when the draw-distance
|
||||
* multiplier is 1, unless GE_D466_FORCE=1; GE_D466_OFF=1 disables the split
|
||||
* (reproduces the leak, for A/B). */
|
||||
|
||||
struct PropRecord;
|
||||
|
||||
/* prop->flags is a u8 and bit 0x80 is unused by the game. Set (together with
|
||||
* PROPFLAG_ONSCREEN) when a prop is drawn only thanks to the extended range. */
|
||||
#define PORT_PROPFLAG_EXTONLY 0x80
|
||||
|
||||
int portD466Active(void); /* split in effect right now */
|
||||
void portD466SetAuthoredPass(int on); /* bgDetermineVisibleRooms pass 1 */
|
||||
int portD466InAuthoredPass(void);
|
||||
void portD466StoreRoom(int room, int rendered, int neighbor);
|
||||
void portD466FrameLog(void); /* GE_D466LOG, called once per rendered frame */
|
||||
|
||||
int portRoomGameplayVisible(int room); /* = getROOMID_isRendered at mult 1 */
|
||||
int portRoomGameplayNeighbor(int room);
|
||||
int portPropGameplayOnScreen(struct PropRecord *prop);
|
||||
void portPropSetGameplayOnScreen(struct PropRecord *prop, int gameplayVisible);
|
||||
|
||||
/* posIsOnScreen records the authored-distance verdict of its last call here. */
|
||||
void portD466SetLastPosVerdict(int gameplayVisible);
|
||||
int portD466LastPosVerdict(void);
|
||||
|
||||
/* D468: AI view clamp (cartridge-widest view = 16:9 at the game's FOV). */
|
||||
void portD468UpdateClamp(void); /* per player view, bgDetermineVisibleRooms */
|
||||
int portD468ClampActive(void);
|
||||
void portD468ShrinkBox(float *minmax); /* {min.x,min.y,max.x,max.y} */
|
||||
|
||||
/* authored-distance fog (bgfog.c) */
|
||||
float portD466AuthoredFar(void);
|
||||
int portFogPositionVisibleGameplay(float *pos3, float range);
|
||||
float portFogScaledFarFogIntensitySquaredGameplay(void);
|
||||
|
||||
#endif
|
||||
@@ -476,7 +476,7 @@ s16 viGetX();
|
||||
s16 viGetY();
|
||||
void viSetColorMode16Bit();
|
||||
void viSetColorMode32Bit();
|
||||
s32 vtxstore_allocate();
|
||||
void *vtxstore_allocate(); /* D457: was s32 */
|
||||
Gfx* watchRenderController();
|
||||
Gfx * watchRenderControllerOpaque();
|
||||
u32 weaponLoadProjectileModels();
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/* drawdistgameplay.c -- D466 (#125): keep Video.DrawDistance out of gameplay.
|
||||
*
|
||||
* See port/include/drawdistgameplay.h and docs/dev/findings.md D466. This file
|
||||
* only holds port state + helpers; the game-file hooks (all #ifdef PORT) are
|
||||
* in bg.c (two room sets), bgfog.c (authored fog), chr.c/propobj.c (verdict
|
||||
* recording) and the gameplay readers.
|
||||
*
|
||||
* Env (dev probes, see docs/dev/GE-ENV-PROBES.md):
|
||||
* GE_D466_FORCE=1 run the split even at multiplier 1 (identity test)
|
||||
* GE_D466_OFF=1 disable the split entirely (reproduces the #125 leak)
|
||||
* GE_D466LOG=<n> log a line every n rendered frames (room-set + chr counts)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "PR/os.h"
|
||||
#include "bondtypes.h"
|
||||
#include "bondconstants.h"
|
||||
#include "bg.h"
|
||||
#include "chrai.h"
|
||||
#include "chr.h"
|
||||
#include "bondview.h"
|
||||
#include "port_math.h"
|
||||
#include "drawdistgameplay.h"
|
||||
|
||||
extern f32 portDrawDistanceMultiplier(void);
|
||||
|
||||
static u8 s_authRendered[MAXROOMCOUNT];
|
||||
static u8 s_authNeighbor[MAXROOMCOUNT];
|
||||
static int s_authoredPass;
|
||||
static int s_lastVerdict = 1;
|
||||
|
||||
static int d466Env(const char *name, int *cache)
|
||||
{
|
||||
if (*cache < 0) { *cache = getenv(name) != NULL; }
|
||||
return *cache;
|
||||
}
|
||||
|
||||
/* D468: AI view clamp. The cartridge's widest gameplay view is its own Ratio
|
||||
* 16:9 mode at the game's FOV; with a wider window (ultrawide) or
|
||||
* Video.FovScale > 100 the rendered frustum is wider than that. Perspective is
|
||||
* linear in screen space, so the faithful frustum is exactly a centred
|
||||
* sub-rectangle of the current player's screen: fx = tanH_faithful / tanH,
|
||||
* fy = tanV_faithful / tanV. Computed once per player view in
|
||||
* bgDetermineVisibleRooms (portD468UpdateClamp) and used by the authored room
|
||||
* pass (shrunken screen box) and the posIsOnScreen gameplay verdict. Off when
|
||||
* Game.AIWideView=1 (PD-port model) or when the view is already faithful. */
|
||||
extern struct player *g_CurrentPlayer;
|
||||
extern f32 portFovYScaleFactor(void);
|
||||
extern int portAIWideView(void);
|
||||
static int s_clampOn;
|
||||
static float s_clampFx = 1.0f, s_clampFy = 1.0f;
|
||||
|
||||
void portD468UpdateClamp(void)
|
||||
{
|
||||
struct player *p = g_CurrentPlayer;
|
||||
s_clampOn = 0; s_clampFx = s_clampFy = 1.0f;
|
||||
if (portAIWideView() || p == NULL || p->c_halfheight <= 0.0f || p->c_halfwidth <= 0.0f ||
|
||||
p->c_perspfovy <= 0.0f || p->c_perspaspect <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
const float halfRad = 3.14159265f / 360.0f; /* degrees -> half-angle radians */
|
||||
float k = portFovYScaleFactor();
|
||||
float tanV = tanf(p->c_perspfovy * halfRad);
|
||||
float tanVf = tanf((p->c_perspfovy / k) * halfRad);
|
||||
/* Ratio 16:9 mode: aspect = viewport w/h * 0.75 * 16/9 (bondview2.c). */
|
||||
float aspF = (p->c_halfwidth / p->c_halfheight) * 0.75f * (16.0f / 9.0f);
|
||||
float asp = p->c_perspaspect;
|
||||
if (aspF > asp) { aspF = asp; }
|
||||
if (tanV <= 0.0f) { return; }
|
||||
s_clampFy = tanVf / tanV;
|
||||
s_clampFx = (tanVf * aspF) / (tanV * asp);
|
||||
{ /* GE_D468_SCALE=<0.05..1> (dev falsifier, registered): narrow the
|
||||
* faithful box further so a headless run can prove the clamp bites. */
|
||||
static float dbg = -1.0f;
|
||||
if (dbg < 0.0f) {
|
||||
const char *e = getenv("GE_D468_SCALE");
|
||||
dbg = e ? (float)atof(e) : 0.0f;
|
||||
if (dbg != 0.0f && (dbg < 0.05f || dbg > 1.0f)) { dbg = 0.0f; }
|
||||
}
|
||||
if (dbg > 0.0f) { s_clampFx *= dbg; s_clampFy *= dbg; }
|
||||
}
|
||||
if (s_clampFx > 1.0f) { s_clampFx = 1.0f; }
|
||||
if (s_clampFy > 1.0f) { s_clampFy = 1.0f; }
|
||||
s_clampOn = (s_clampFx < 0.999f || s_clampFy < 0.999f);
|
||||
}
|
||||
}
|
||||
|
||||
int portD468ClampActive(void) { return s_clampOn; }
|
||||
|
||||
/* Shrink a screen box (f[0]=min x,y; f[1]=max x,y) to its centred faithful part. */
|
||||
void portD468ShrinkBox(float *minmax)
|
||||
{
|
||||
float cx, cy, hx, hy;
|
||||
if (!s_clampOn) { return; }
|
||||
cx = (minmax[0] + minmax[2]) * 0.5f; hx = (minmax[2] - minmax[0]) * 0.5f * s_clampFx;
|
||||
cy = (minmax[1] + minmax[3]) * 0.5f; hy = (minmax[3] - minmax[1]) * 0.5f * s_clampFy;
|
||||
minmax[0] = cx - hx; minmax[2] = cx + hx;
|
||||
minmax[1] = cy - hy; minmax[3] = cy + hy;
|
||||
}
|
||||
|
||||
int portD466Active(void)
|
||||
{
|
||||
static int force = -1, off = -1;
|
||||
if (d466Env("GE_D466_OFF", &off)) { return 0; }
|
||||
if (portDrawDistanceMultiplier() > 1.0f || s_clampOn) { return 1; }
|
||||
return d466Env("GE_D466_FORCE", &force);
|
||||
}
|
||||
|
||||
void portD466SetAuthoredPass(int on) { s_authoredPass = on; }
|
||||
int portD466InAuthoredPass(void) { return s_authoredPass; }
|
||||
|
||||
void portD466StoreRoom(int room, int rendered, int neighbor)
|
||||
{
|
||||
if (room >= 0 && room < MAXROOMCOUNT) {
|
||||
s_authRendered[room] = (u8)rendered;
|
||||
s_authNeighbor[room] = (u8)neighbor;
|
||||
}
|
||||
}
|
||||
|
||||
int portRoomGameplayVisible(int room)
|
||||
{
|
||||
if (portD466Active() && room >= 0 && room < MAXROOMCOUNT) {
|
||||
return s_authRendered[room];
|
||||
}
|
||||
return getROOMID_isRendered(room);
|
||||
}
|
||||
|
||||
int portRoomGameplayNeighbor(int room)
|
||||
{
|
||||
if (portD466Active() && room >= 0 && room < MAXROOMCOUNT) {
|
||||
return s_authNeighbor[room];
|
||||
}
|
||||
return getROOMID_isNeighborToRendered(room);
|
||||
}
|
||||
|
||||
int portPropGameplayOnScreen(struct PropRecord *prop)
|
||||
{
|
||||
PropRecord *p = (PropRecord *)prop;
|
||||
if (!(p->flags & PROPFLAG_ONSCREEN)) { return 0; }
|
||||
if (portD466Active() && (p->flags & PORT_PROPFLAG_EXTONLY)) { return 0; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
void portPropSetGameplayOnScreen(struct PropRecord *prop, int gameplayVisible)
|
||||
{
|
||||
PropRecord *p = (PropRecord *)prop;
|
||||
if (gameplayVisible || !portD466Active()) {
|
||||
p->flags &= ~PORT_PROPFLAG_EXTONLY;
|
||||
} else {
|
||||
p->flags |= PORT_PROPFLAG_EXTONLY;
|
||||
}
|
||||
}
|
||||
|
||||
void portD466SetLastPosVerdict(int v) { s_lastVerdict = v; }
|
||||
int portD466LastPosVerdict(void) { return s_lastVerdict; }
|
||||
|
||||
/* ---- GE_D466LOG ------------------------------------------------------- */
|
||||
|
||||
extern s32 g_MaxNumRooms;
|
||||
extern s32 getPlayerCount(void);
|
||||
|
||||
void portD466FrameLog(void)
|
||||
{
|
||||
static int period = -1;
|
||||
static unsigned frame;
|
||||
if (period < 0) {
|
||||
const char *e = getenv("GE_D466LOG");
|
||||
period = e ? atoi(e) : 0;
|
||||
if (e && period <= 0) { period = 1; }
|
||||
}
|
||||
if (period == 0) { return; }
|
||||
if ((frame++ % (unsigned)period) != 0) { return; }
|
||||
{
|
||||
int i, ext = 0, auth = 0, sub = 1;
|
||||
unsigned he = 2166136261u, ha = 2166136261u;
|
||||
for (i = 0; i < g_MaxNumRooms && i < MAXROOMCOUNT; i++) {
|
||||
int e = getROOMID_isRendered(i) != 0;
|
||||
int a = s_authRendered[i] != 0;
|
||||
ext += e; auth += a;
|
||||
if (e) { he = (he ^ (unsigned)i) * 16777619u; }
|
||||
if (a) { ha = (ha ^ (unsigned)i) * 16777619u; }
|
||||
if (a && !e) { sub = 0; }
|
||||
}
|
||||
int c, nOn = 0, nGp = 0, nSeen = 0;
|
||||
unsigned hg = 2166136261u;
|
||||
for (c = 0; c < g_NumChrSlots; c++) {
|
||||
ChrRecord *chr = &g_ChrSlots[c];
|
||||
PropRecord *pr = chr->prop;
|
||||
unsigned v;
|
||||
int gp;
|
||||
if (pr == NULL || chr->model == NULL) { continue; }
|
||||
gp = portPropGameplayOnScreen((struct PropRecord *)pr);
|
||||
nOn += (pr->flags & PROPFLAG_ONSCREEN) != 0;
|
||||
nGp += gp;
|
||||
nSeen += (chr->chrflags & CHRFLAG_HAS_BEEN_ON_SCREEN) != 0;
|
||||
v = (unsigned)chr->chrnum;
|
||||
v = v * 31u + (unsigned)chr->actiontype;
|
||||
v = v * 31u + (unsigned)(int)(pr->pos.f[0] + 0.5f);
|
||||
v = v * 31u + (unsigned)(int)(pr->pos.f[1] + 0.5f);
|
||||
v = v * 31u + (unsigned)(int)(pr->pos.f[2] + 0.5f);
|
||||
v = v * 31u + (unsigned)gp;
|
||||
v = v * 31u + (unsigned)((chr->chrflags & CHRFLAG_HAS_BEEN_ON_SCREEN) != 0);
|
||||
hg = (hg ^ v) * 16777619u;
|
||||
}
|
||||
fprintf(stderr, "D466 f=%u active=%d ext=%d auth=%d subset=%d hext=%08x hauth=%08x chrOn=%d chrGp=%d seen=%d hgame=%08x\n",
|
||||
frame - 1, portD466Active(), ext, auth, sub, he, ha, nOn, nGp, nSeen, hg);
|
||||
}
|
||||
}
|
||||
@@ -310,6 +310,9 @@ static struct Row rows[] = {
|
||||
/* D232: the community "no damage flash" toggle (suppresses the red/green
|
||||
* hit-flash overlay in bondview2). */
|
||||
{ .key="Game.NoHitFlash", .label="No hit flash", .kind=ROW_TOGGLE, .step=1, .names=kOnOff },
|
||||
/* D468: off = AI awareness keeps the cartridge-widest view (16:9 at the
|
||||
* game's FOV) under ultrawide or a raised FOV; on = AI sees the full view. */
|
||||
{ .key="Game.AIWideView", .label="AI sees the wider view", .kind=ROW_TOGGLE, .step=1, .names=kOnOff },
|
||||
/* D257: everything-unlocked goodie. The C initializers (port/src/video.c)
|
||||
* start it OFF (0) -- the old "default ON" note was stale and is
|
||||
* corrected by D356. Consumed at startup by main.c -- applies from the
|
||||
|
||||
@@ -263,6 +263,28 @@ f32 portScaleFovY(f32 fovy, s32 isTitleScreen)
|
||||
return fovy;
|
||||
}
|
||||
|
||||
/* D468: the factor portScaleFovY applies to an in-level fovy (clamps
|
||||
* ignored), so gameplay can recover the game's own FOV from the rendered one.
|
||||
* 1.0f at the defaults (native widescreen on, FovScale 100). */
|
||||
f32 portFovYScaleFactor(void)
|
||||
{
|
||||
f32 k = 1.0f;
|
||||
if (cfgWidescreenAuto && !cfgNativeWidescreen &&
|
||||
gfx_current_dimensions.aspect_ratio > 0.01f) {
|
||||
k *= sqrtf(gfx_current_dimensions.aspect_ratio / (4.0f / 3.0f));
|
||||
}
|
||||
if (portFovScale > 0.4f && portFovScale < 2.01f && portFovScale != 1.0f) {
|
||||
k *= portFovScale;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/* D468: Game.AIWideView -- 0 (default) = AI awareness keeps the cartridge's
|
||||
* widest view (16:9 at the game's own FOV) when the window is wider or
|
||||
* FovScale > 100; 1 = AI sees the full rendered view (the PD-port model). */
|
||||
static int cfgAIWideView = 0;
|
||||
int portAIWideView(void) { return cfgAIWideView; }
|
||||
|
||||
/* D443 (D357): horizontal FOV in degrees for a Video.FovScale percent, for the
|
||||
* settings display only. Mirrors portScaleFovY + the gameplay projection:
|
||||
* vertical FOV = FOV_Y_F (60) * [WidescreenAuto stretch-era boost] * pct/100,
|
||||
@@ -446,6 +468,7 @@ 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.AIWideView", &cfgAIWideView, 0, 1); /* D468 */
|
||||
configRegisterInt("Video.CrosshairHide", &portCrosshairHide, 0, 1); /* v0.4.0 M2 (D373) */
|
||||
configRegisterInt("Video.CrosshairColor", &cfgCrosshairColor, 0, 8); /* 8 = custom; old ini values unchanged */
|
||||
configRegisterInt("Video.CrosshairRed", &cfgCrosshairRed, 0, 255);
|
||||
|
||||
Reference in new issue
Block a user